428 lines · c
1/*2 * Linux ARCnet driver - COM90xx chipset (IO-mapped buffers)3 *4 * Written 1997 by David Woodhouse.5 * Written 1994-1999 by Avery Pennarun.6 * Written 1999-2000 by Martin Mares <mj@ucw.cz>.7 * Derived from skeleton.c by Donald Becker.8 *9 * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)10 * for sponsoring the further development of this driver.11 *12 * **********************13 *14 * The original copyright of skeleton.c was as follows:15 *16 * skeleton.c Written 1993 by Donald Becker.17 * Copyright 1993 United States Government as represented by the18 * Director, National Security Agency. This software may only be used19 * and distributed according to the terms of the GNU General Public License as20 * modified by SRC, incorporated herein by reference.21 *22 * **********************23 *24 * For more details, see drivers/net/arcnet.c25 *26 * **********************27 */28 29#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt30 31#include <linux/kernel.h>32#include <linux/module.h>33#include <linux/moduleparam.h>34#include <linux/ioport.h>35#include <linux/delay.h>36#include <linux/netdevice.h>37#include <linux/memblock.h>38#include <linux/init.h>39#include <linux/interrupt.h>40#include <linux/io.h>41 42#include "arcdevice.h"43#include "com9026.h"44 45/* Internal function declarations */46 47static int com90io_found(struct net_device *dev);48static void com90io_command(struct net_device *dev, int command);49static int com90io_status(struct net_device *dev);50static void com90io_setmask(struct net_device *dev, int mask);51static int com90io_reset(struct net_device *dev, int really_reset);52static void com90io_copy_to_card(struct net_device *dev, int bufnum, int offset,53 void *buf, int count);54static void com90io_copy_from_card(struct net_device *dev, int bufnum,55 int offset, void *buf, int count);56 57/* Handy defines for ARCnet specific stuff */58 59/* The number of low I/O ports used by the card. */60#define ARCNET_TOTAL_SIZE 1661 62/****************************************************************************63 * *64 * IO-mapped operation routines *65 * *66 ****************************************************************************/67 68#undef ONE_AT_A_TIME_TX69#undef ONE_AT_A_TIME_RX70 71static u_char get_buffer_byte(struct net_device *dev, unsigned offset)72{73 int ioaddr = dev->base_addr;74 75 arcnet_outb(offset >> 8, ioaddr, COM9026_REG_W_ADDR_HI);76 arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO);77 78 return arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA);79}80 81#ifdef ONE_AT_A_TIME_TX82static void put_buffer_byte(struct net_device *dev, unsigned offset,83 u_char datum)84{85 int ioaddr = dev->base_addr;86 87 arcnet_outb(offset >> 8, ioaddr, COM9026_REG_W_ADDR_HI);88 arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO);89 90 arcnet_outb(datum, ioaddr, COM9026_REG_RW_MEMDATA);91}92 93#endif94 95static void get_whole_buffer(struct net_device *dev, unsigned offset,96 unsigned length, char *dest)97{98 int ioaddr = dev->base_addr;99 100 arcnet_outb((offset >> 8) | AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI);101 arcnet_outb(offset & 0xff, ioaddr, COM9026_REG_W_ADDR_LO);102 103 while (length--)104#ifdef ONE_AT_A_TIME_RX105 *(dest++) = get_buffer_byte(dev, offset++);106#else107 *(dest++) = arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA);108#endif109}110 111static void put_whole_buffer(struct net_device *dev, unsigned offset,112 unsigned length, char *dest)113{114 int ioaddr = dev->base_addr;115 116 arcnet_outb((offset >> 8) | AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI);117 arcnet_outb(offset & 0xff, ioaddr,COM9026_REG_W_ADDR_LO);118 119 while (length--)120#ifdef ONE_AT_A_TIME_TX121 put_buffer_byte(dev, offset++, *(dest++));122#else123 arcnet_outb(*(dest++), ioaddr, COM9026_REG_RW_MEMDATA);124#endif125}126 127/* We cannot probe for an IO mapped card either, although we can check that128 * it's where we were told it was, and even autoirq129 */130static int __init com90io_probe(struct net_device *dev)131{132 int ioaddr = dev->base_addr, status;133 unsigned long airqmask;134 135 if (BUGLVL(D_NORMAL)) {136 pr_info("%s\n", "COM90xx IO-mapped mode support (by David Woodhouse et el.)");137 pr_info("E-mail me if you actually test this driver, please!\n");138 }139 140 if (!ioaddr) {141 arc_printk(D_NORMAL, dev, "No autoprobe for IO mapped cards; you must specify the base address!\n");142 return -ENODEV;143 }144 if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "com90io probe")) {145 arc_printk(D_INIT_REASONS, dev, "IO request_region %x-%x failed\n",146 ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1);147 return -ENXIO;148 }149 if (arcnet_inb(ioaddr, COM9026_REG_R_STATUS) == 0xFF) {150 arc_printk(D_INIT_REASONS, dev, "IO address %x empty\n",151 ioaddr);152 goto err_out;153 }154 arcnet_inb(ioaddr, COM9026_REG_R_RESET);155 mdelay(RESETtime);156 157 status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);158 159 if ((status & 0x9D) != (NORXflag | RECONflag | TXFREEflag | RESETflag)) {160 arc_printk(D_INIT_REASONS, dev, "Status invalid (%Xh)\n",161 status);162 goto err_out;163 }164 arc_printk(D_INIT_REASONS, dev, "Status after reset: %X\n", status);165 166 arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear,167 ioaddr, COM9026_REG_W_COMMAND);168 169 arc_printk(D_INIT_REASONS, dev, "Status after reset acknowledged: %X\n",170 status);171 172 status = arcnet_inb(ioaddr, COM9026_REG_R_STATUS);173 174 if (status & RESETflag) {175 arc_printk(D_INIT_REASONS, dev, "Eternal reset (status=%Xh)\n",176 status);177 goto err_out;178 }179 arcnet_outb((0x16 | IOMAPflag) & ~ENABLE16flag,180 ioaddr, COM9026_REG_RW_CONFIG);181 182 /* Read first loc'n of memory */183 184 arcnet_outb(AUTOINCflag, ioaddr, COM9026_REG_W_ADDR_HI);185 arcnet_outb(0, ioaddr, COM9026_REG_W_ADDR_LO);186 187 status = arcnet_inb(ioaddr, COM9026_REG_RW_MEMDATA);188 if (status != 0xd1) {189 arc_printk(D_INIT_REASONS, dev, "Signature byte not found (%Xh instead).\n",190 status);191 goto err_out;192 }193 if (!dev->irq) {194 /* if we do this, we're sure to get an IRQ since the195 * card has just reset and the NORXflag is on until196 * we tell it to start receiving.197 */198 199 airqmask = probe_irq_on();200 arcnet_outb(NORXflag, ioaddr, COM9026_REG_W_INTMASK);201 udelay(1);202 arcnet_outb(0, ioaddr, COM9026_REG_W_INTMASK);203 dev->irq = probe_irq_off(airqmask);204 205 if ((int)dev->irq <= 0) {206 arc_printk(D_INIT_REASONS, dev, "Autoprobe IRQ failed\n");207 goto err_out;208 }209 }210 release_region(ioaddr, ARCNET_TOTAL_SIZE); /* end of probing */211 return com90io_found(dev);212 213err_out:214 release_region(ioaddr, ARCNET_TOTAL_SIZE);215 return -ENODEV;216}217 218/* Set up the struct net_device associated with this card. Called after219 * probing succeeds.220 */221static int __init com90io_found(struct net_device *dev)222{223 struct arcnet_local *lp;224 int ioaddr = dev->base_addr;225 int err;226 227 /* Reserve the irq */228 if (request_irq(dev->irq, arcnet_interrupt, 0,229 "arcnet (COM90xx-IO)", dev)) {230 arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", dev->irq);231 return -ENODEV;232 }233 /* Reserve the I/O region */234 if (!request_region(dev->base_addr, ARCNET_TOTAL_SIZE,235 "arcnet (COM90xx-IO)")) {236 free_irq(dev->irq, dev);237 return -EBUSY;238 }239 240 lp = netdev_priv(dev);241 lp->card_name = "COM90xx I/O";242 lp->hw.command = com90io_command;243 lp->hw.status = com90io_status;244 lp->hw.intmask = com90io_setmask;245 lp->hw.reset = com90io_reset;246 lp->hw.owner = THIS_MODULE;247 lp->hw.copy_to_card = com90io_copy_to_card;248 lp->hw.copy_from_card = com90io_copy_from_card;249 250 lp->config = (0x16 | IOMAPflag) & ~ENABLE16flag;251 arcnet_outb(lp->config, ioaddr, COM9026_REG_RW_CONFIG);252 253 /* get and check the station ID from offset 1 in shmem */254 255 arcnet_set_addr(dev, get_buffer_byte(dev, 1));256 257 err = register_netdev(dev);258 if (err) {259 arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) & ~IOMAPflag,260 ioaddr, COM9026_REG_RW_CONFIG);261 free_irq(dev->irq, dev);262 release_region(dev->base_addr, ARCNET_TOTAL_SIZE);263 return err;264 }265 266 arc_printk(D_NORMAL, dev, "COM90IO: station %02Xh found at %03lXh, IRQ %d.\n",267 dev->dev_addr[0], dev->base_addr, dev->irq);268 269 return 0;270}271 272/* Do a hardware reset on the card, and set up necessary registers.273 *274 * This should be called as little as possible, because it disrupts the275 * token on the network (causes a RECON) and requires a significant delay.276 *277 * However, it does make sure the card is in a defined state.278 */279static int com90io_reset(struct net_device *dev, int really_reset)280{281 struct arcnet_local *lp = netdev_priv(dev);282 short ioaddr = dev->base_addr;283 284 arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n",285 dev->name, arcnet_inb(ioaddr, COM9026_REG_R_STATUS));286 287 if (really_reset) {288 /* reset the card */289 arcnet_inb(ioaddr, COM9026_REG_R_RESET);290 mdelay(RESETtime);291 }292 /* Set the thing to IO-mapped, 8-bit mode */293 lp->config = (0x1C | IOMAPflag) & ~ENABLE16flag;294 arcnet_outb(lp->config, ioaddr, COM9026_REG_RW_CONFIG);295 296 arcnet_outb(CFLAGScmd | RESETclear, ioaddr, COM9026_REG_W_COMMAND);297 /* clear flags & end reset */298 arcnet_outb(CFLAGScmd | CONFIGclear, ioaddr, COM9026_REG_W_COMMAND);299 300 /* verify that the ARCnet signature byte is present */301 if (get_buffer_byte(dev, 0) != TESTvalue) {302 arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n");303 return 1;304 }305 /* enable extended (512-byte) packets */306 arcnet_outb(CONFIGcmd | EXTconf, ioaddr, COM9026_REG_W_COMMAND);307 /* done! return success. */308 return 0;309}310 311static void com90io_command(struct net_device *dev, int cmd)312{313 short ioaddr = dev->base_addr;314 315 arcnet_outb(cmd, ioaddr, COM9026_REG_W_COMMAND);316}317 318static int com90io_status(struct net_device *dev)319{320 short ioaddr = dev->base_addr;321 322 return arcnet_inb(ioaddr, COM9026_REG_R_STATUS);323}324 325static void com90io_setmask(struct net_device *dev, int mask)326{327 short ioaddr = dev->base_addr;328 329 arcnet_outb(mask, ioaddr, COM9026_REG_W_INTMASK);330}331 332static void com90io_copy_to_card(struct net_device *dev, int bufnum,333 int offset, void *buf, int count)334{335 TIME(dev, "put_whole_buffer", count,336 put_whole_buffer(dev, bufnum * 512 + offset, count, buf));337}338 339static void com90io_copy_from_card(struct net_device *dev, int bufnum,340 int offset, void *buf, int count)341{342 TIME(dev, "get_whole_buffer", count,343 get_whole_buffer(dev, bufnum * 512 + offset, count, buf));344}345 346static int io; /* use the insmod io= irq= shmem= options */347static int irq;348static char device[9]; /* use eg. device=arc1 to change name */349 350module_param_hw(io, int, ioport, 0);351module_param_hw(irq, int, irq, 0);352module_param_string(device, device, sizeof(device), 0);353MODULE_DESCRIPTION("ARCnet COM90xx IO mapped chipset driver");354MODULE_LICENSE("GPL");355 356#ifndef MODULE357static int __init com90io_setup(char *s)358{359 int ints[4];360 361 s = get_options(s, 4, ints);362 if (!ints[0])363 return 0;364 switch (ints[0]) {365 default: /* ERROR */366 pr_err("Too many arguments\n");367 fallthrough;368 case 2: /* IRQ */369 irq = ints[2];370 fallthrough;371 case 1: /* IO address */372 io = ints[1];373 }374 if (*s)375 snprintf(device, sizeof(device), "%s", s);376 return 1;377}378__setup("com90io=", com90io_setup);379#endif380 381static struct net_device *my_dev;382 383static int __init com90io_init(void)384{385 struct net_device *dev;386 int err;387 388 dev = alloc_arcdev(device);389 if (!dev)390 return -ENOMEM;391 392 dev->base_addr = io;393 dev->irq = irq;394 if (dev->irq == 2)395 dev->irq = 9;396 397 err = com90io_probe(dev);398 399 if (err) {400 free_arcdev(dev);401 return err;402 }403 404 my_dev = dev;405 return 0;406}407 408static void __exit com90io_exit(void)409{410 struct net_device *dev = my_dev;411 int ioaddr = dev->base_addr;412 413 unregister_netdev(dev);414 415 /* In case the old driver is loaded later,416 * set the thing back to MMAP mode417 */418 arcnet_outb(arcnet_inb(ioaddr, COM9026_REG_RW_CONFIG) & ~IOMAPflag,419 ioaddr, COM9026_REG_RW_CONFIG);420 421 free_irq(dev->irq, dev);422 release_region(dev->base_addr, ARCNET_TOTAL_SIZE);423 free_arcdev(dev);424}425 426module_init(com90io_init)427module_exit(com90io_exit)428