root/drivers/net/wd.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. wd_probe
  2. wd_probe1
  3. wd_open
  4. wd_reset_8390
  5. wd_get_8390_hdr
  6. wd_block_input
  7. wd_block_output
  8. wd_close_card
  9. init_module
  10. cleanup_module

   1 /* wd.c: A WD80x3 ethernet driver for linux. */
   2 /*
   3         Written 1993-94 by Donald Becker.
   4 
   5         Copyright 1993 United States Government as represented by the
   6         Director, National Security Agency.
   7 
   8         This software may be used and distributed according to the terms
   9         of the GNU Public License, incorporated herein by reference.
  10 
  11         The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
  12         Center of Excellence in Space Data and Information Sciences
  13            Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
  14 
  15         This is a driver for WD8003 and WD8013 "compatible" ethercards.
  16 
  17         Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
  18 */
  19 
  20 static const char *version =
  21         "wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
  22 
  23 #ifdef MODULE
  24 #include <linux/module.h>
  25 #include <linux/version.h>
  26 #endif
  27 
  28 #include <linux/kernel.h>
  29 #include <linux/sched.h>
  30 #include <linux/errno.h>
  31 #include <linux/string.h>
  32 #include <asm/io.h>
  33 #include <asm/system.h>
  34 
  35 #include <linux/netdevice.h>
  36 #include <linux/etherdevice.h>
  37 #include "8390.h"
  38 
  39 /* A zero-terminated list of I/O addresses to be probed. */
  40 static unsigned int wd_portlist[] =
  41 {0x300, 0x280, 0x380, 0x240, 0};
  42 
  43 int wd_probe(struct device *dev);
  44 int wd_probe1(struct device *dev, int ioaddr);
  45 
  46 static int wd_open(struct device *dev);
  47 static void wd_reset_8390(struct device *dev);
  48 static void wd_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
  49                                                 int ring_page);
  50 static void wd_block_input(struct device *dev, int count,
  51                                                   struct sk_buff *skb, int ring_offset);
  52 static void wd_block_output(struct device *dev, int count,
  53                                                         const unsigned char *buf, const start_page);
  54 static int wd_close_card(struct device *dev);
  55 
  56 
  57 #define WD_START_PG             0x00    /* First page of TX buffer */
  58 #define WD03_STOP_PG    0x20    /* Last page +1 of RX ring */
  59 #define WD13_STOP_PG    0x40    /* Last page +1 of RX ring */
  60 
  61 #define WD_CMDREG               0               /* Offset to ASIC command register. */
  62 #define  WD_RESET               0x80    /* Board reset, in WD_CMDREG. */
  63 #define  WD_MEMENB              0x40    /* Enable the shared memory. */
  64 #define WD_CMDREG5              5               /* Offset to 16-bit-only ASIC register 5. */
  65 #define  ISA16                  0x80    /* Enable 16 bit access from the ISA bus. */
  66 #define  NIC16                  0x40    /* Enable 16 bit access from the 8390. */
  67 #define WD_NIC_OFFSET   16              /* Offset to the 8390 from the base_addr. */
  68 #define WD_IO_EXTENT    32
  69 
  70 
  71 /*      Probe for the WD8003 and WD8013.  These cards have the station
  72         address PROM at I/O ports <base>+8 to <base>+13, with a checksum
  73         following. A Soundblaster can have the same checksum as an WDethercard,
  74         so we have an extra exclusionary check for it.
  75 
  76         The wd_probe1() routine initializes the card and fills the
  77         station address field. */
  78 
  79 #ifdef HAVE_DEVLIST
  80 struct netdev_entry wd_drv =
  81 {"wd", wd_probe1, WD_IO_EXTENT, wd_portlist};
  82 #else
  83 
  84 int wd_probe(struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
  85 {
  86         int i;
  87         int base_addr = dev ? dev->base_addr : 0;
  88 
  89         if (base_addr > 0x1ff)          /* Check a single specified location. */
  90                 return wd_probe1(dev, base_addr);
  91         else if (base_addr != 0)        /* Don't probe at all. */
  92                 return ENXIO;
  93 
  94         for (i = 0; wd_portlist[i]; i++) {
  95                 int ioaddr = wd_portlist[i];
  96                 if (check_region(ioaddr, WD_IO_EXTENT))
  97                         continue;
  98                 if (wd_probe1(dev, ioaddr) == 0)
  99                         return 0;
 100         }
 101 
 102         return ENODEV;
 103 }
 104 #endif
 105 
 106 int wd_probe1(struct device *dev, int ioaddr)
     /* [previous][next][first][last][top][bottom][index][help] */
 107 {
 108         int i;
 109         int checksum = 0;
 110         int ancient = 0;                        /* An old card without config registers. */
 111         int word16 = 0;                         /* 0 = 8 bit, 1 = 16 bit */
 112         const char *model_name;
 113 
 114         for (i = 0; i < 8; i++)
 115                 checksum += inb(ioaddr + 8 + i);
 116         if (inb(ioaddr + 8) == 0xff     /* Extra check to avoid soundcard. */
 117                 || inb(ioaddr + 9) == 0xff
 118                 || (checksum & 0xff) != 0xFF)
 119                 return ENODEV;
 120 
 121         if (dev == NULL)
 122                 dev = init_etherdev(0, sizeof(struct ei_device));
 123 
 124         printk("%s: WD80x3 at %#3x, ", dev->name, ioaddr);
 125         for (i = 0; i < 6; i++)
 126                 printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
 127 
 128         /* The following PureData probe code was contributed by
 129            Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software
 130            configuration differently from others so we have to check for them.
 131            This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card.
 132            */
 133         if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') {
 134                 unsigned char reg5 = inb(ioaddr+5);
 135 
 136                 switch (inb(ioaddr+2)) {
 137                 case 0x03: word16 = 0; model_name = "PDI8023-8";        break;
 138                 case 0x05: word16 = 0; model_name = "PDUC8023"; break;
 139                 case 0x0a: word16 = 1; model_name = "PDI8023-16"; break;
 140                         /* Either 0x01 (dumb) or they've released a new version. */
 141                 default:         word16 = 0; model_name = "PDI8023";    break;
 142                 }
 143                 dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
 144                 dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
 145         } else {                                                                /* End of PureData probe */
 146                 /* This method of checking for a 16-bit board is borrowed from the
 147                    we.c driver.  A simpler method is just to look in ASIC reg. 0x03.
 148                    I'm comparing the two method in alpha test to make certain they
 149                    return the same result. */
 150                 /* Check for the old 8 bit board - it has register 0/8 aliasing.
 151                    Do NOT check i>=6 here -- it hangs the old 8003 boards! */
 152                 for (i = 0; i < 6; i++)
 153                         if (inb(ioaddr+i) != inb(ioaddr+8+i))
 154                                 break;
 155                 if (i >= 6) {
 156                         ancient = 1;
 157                         model_name = "WD8003-old";
 158                         word16 = 0;
 159                 } else {
 160                         int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
 161                         outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
 162                         if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
 163                                 && (tmp & 0x01) == 0x01 ) {                             /* In a 16 slot. */
 164                                 int asic_reg5 = inb(ioaddr+WD_CMDREG5);
 165                                 /* Magic to set ASIC to word-wide mode. */
 166                                 outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
 167                                 outb(tmp, ioaddr+1);
 168                                 model_name = "WD8013";
 169                                 word16 = 1;             /* We have a 16bit board here! */
 170                         } else {
 171                                 model_name = "WD8003";
 172                                 word16 = 0;
 173                         }
 174                         outb(tmp, ioaddr+1);                    /* Restore original reg1 value. */
 175                 }
 176 #ifndef final_version
 177                 if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01))
 178                         printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
 179                                    word16 ? 16 : 8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
 180 #endif
 181         }
 182 
 183 #if defined(WD_SHMEM) && WD_SHMEM > 0x80000
 184         /* Allow a compile-time override.        */
 185         dev->mem_start = WD_SHMEM;
 186 #else
 187         if (dev->mem_start == 0) {
 188                 /* Sanity and old 8003 check */
 189                 int reg0 = inb(ioaddr);
 190                 if (reg0 == 0xff || reg0 == 0) {
 191                         /* Future plan: this could check a few likely locations first. */
 192                         dev->mem_start = 0xd0000;
 193                         printk(" assigning address %#lx", dev->mem_start);
 194                 } else {
 195                         int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f;
 196                         /* Some boards don't have the register 5 -- it returns 0xff. */
 197                         if (high_addr_bits == 0x1f || word16 == 0)
 198                                 high_addr_bits = 0x01;
 199                         dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
 200                 }
 201         }
 202 #endif
 203 
 204         /* The 8390 isn't at the base address -- the ASIC regs are there! */
 205         dev->base_addr = ioaddr+WD_NIC_OFFSET;
 206 
 207         if (dev->irq < 2) {
 208                 int irqmap[] = {9,3,5,7,10,11,15,4};
 209                 int reg1 = inb(ioaddr+1);
 210                 int reg4 = inb(ioaddr+4);
 211                 if (ancient || reg1 == 0xff) {  /* Ack!! No way to read the IRQ! */
 212                         short nic_addr = ioaddr+WD_NIC_OFFSET;
 213 
 214                         /* We have an old-style ethercard that doesn't report its IRQ
 215                            line.  Do autoirq to find the IRQ line. Note that this IS NOT
 216                            a reliable way to trigger an interrupt. */
 217                         outb_p(E8390_NODMA + E8390_STOP, nic_addr);
 218                         outb(0x00, nic_addr+EN0_IMR);   /* Disable all intrs. */
 219                         autoirq_setup(0);
 220                         outb_p(0xff, nic_addr + EN0_IMR);       /* Enable all interrupts. */
 221                         outb_p(0x00, nic_addr + EN0_RCNTLO);
 222                         outb_p(0x00, nic_addr + EN0_RCNTHI);
 223                         outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */
 224                         dev->irq = autoirq_report(2);
 225                         outb_p(0x00, nic_addr+EN0_IMR); /* Mask all intrs. again. */
 226 
 227                         if (ei_debug > 2)
 228                                 printk(" autoirq is %d", dev->irq);
 229                         if (dev->irq < 2)
 230                                 dev->irq = word16 ? 10 : 5;
 231                 } else
 232                         dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
 233         } else if (dev->irq == 2)               /* Fixup bogosity: IRQ2 is really IRQ9 */
 234                 dev->irq = 9;
 235 
 236         /* Snarf the interrupt now.  There's no point in waiting since we cannot
 237            share and the board will usually be enabled. */
 238         if (request_irq(dev->irq, ei_interrupt, 0, "wd")) {
 239                 printk (" unable to get IRQ %d.\n", dev->irq);
 240                 return EAGAIN;
 241         }
 242 
 243         /* OK, were are certain this is going to work.  Setup the device. */
 244         request_region(ioaddr, WD_IO_EXTENT,"wd");
 245         ethdev_init(dev);
 246 
 247         ei_status.name = model_name;
 248         ei_status.word16 = word16;
 249         ei_status.tx_start_page = WD_START_PG;
 250         ei_status.rx_start_page = WD_START_PG + TX_PAGES;
 251         ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
 252 
 253         /* Don't map in the shared memory until the board is actually opened. */
 254         dev->rmem_start = dev->mem_start + TX_PAGES*256;
 255         dev->mem_end = dev->rmem_end
 256                 = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
 257 
 258         printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
 259                    model_name, dev->irq, dev->mem_start, dev->mem_end-1);
 260         if (ei_debug > 0)
 261                 printk(version);
 262 
 263         ei_status.reset_8390 = &wd_reset_8390;
 264         ei_status.block_input = &wd_block_input;
 265         ei_status.block_output = &wd_block_output;
 266         ei_status.get_8390_hdr = &wd_get_8390_hdr;
 267         dev->open = &wd_open;
 268         dev->stop = &wd_close_card;
 269         NS8390_init(dev, 0);
 270 
 271 #if 1
 272         /* Enable interrupt generation on softconfig cards -- M.U */
 273         /* .. but possibly potentially unsafe - Donald */
 274         if (inb(ioaddr+14) & 0x20)
 275                 outb(inb(ioaddr+4)|0x80, ioaddr+4);
 276 #endif
 277 
 278         return 0;
 279 }
 280 
 281 static int
 282 wd_open(struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 283 {
 284   int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
 285   int rc;
 286 
 287   /* Map in the shared memory. Always set register 0 last to remain
 288          compatible with very old boards. */
 289   ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB;
 290   ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16;
 291 
 292   if (ei_status.word16)
 293           outb(ei_status.reg5, ioaddr+WD_CMDREG5);
 294   outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
 295 
 296   rc = ei_open(dev);
 297   if (rc != 0) return rc;
 298 #ifdef MODULE
 299   MOD_INC_USE_COUNT;
 300 #endif
 301   return 0;
 302 }
 303 
 304 static void
 305 wd_reset_8390(struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 306 {
 307         int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
 308 
 309         outb(WD_RESET, wd_cmd_port);
 310         if (ei_debug > 1) printk("resetting the WD80x3 t=%lu...", jiffies);
 311         ei_status.txing = 0;
 312 
 313         /* Set up the ASIC registers, just in case something changed them. */
 314         outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port);
 315         if (ei_status.word16)
 316                 outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
 317 
 318         if (ei_debug > 1) printk("reset done\n");
 319         return;
 320 }
 321 
 322 /* Grab the 8390 specific header. Similar to the block_input routine, but
 323    we don't need to be concerned with ring wrap as the header will be at
 324    the start of a page, so we optimize accordingly. */
 325 
 326 static void
 327 wd_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
     /* [previous][next][first][last][top][bottom][index][help] */
 328 {
 329 
 330         int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
 331         unsigned long hdr_start = dev->mem_start + ((ring_page - WD_START_PG)<<8);
 332 
 333         /* We'll always get a 4 byte header read followed by a packet read, so
 334            we enable 16 bit mode before the header, and disable after the body. */
 335         if (ei_status.word16)
 336                 outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
 337 
 338         memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
 339 }
 340 
 341 /* Block input and output are easy on shared memory ethercards, and trivial
 342    on the Western digital card where there is no choice of how to do it.
 343    The only complications are that the ring buffer wraps, and need to map
 344    switch between 8- and 16-bit modes. */
 345 
 346 static void
 347 wd_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
     /* [previous][next][first][last][top][bottom][index][help] */
 348 {
 349         int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
 350         unsigned long xfer_start = dev->mem_start + ring_offset - (WD_START_PG<<8);
 351 
 352         if (xfer_start + count > dev->rmem_end) {
 353                 /* We must wrap the input move. */
 354                 int semi_count = dev->rmem_end - xfer_start;
 355                 memcpy_fromio(skb->data, xfer_start, semi_count);
 356                 count -= semi_count;
 357                 memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
 358         } else {
 359                 /* Packet is in one chunk -- we can copy + cksum. */
 360                 eth_io_copy_and_sum(skb, xfer_start, count, 0);
 361         }
 362 
 363         /* Turn off 16 bit access so that reboot works.  ISA brain-damage */
 364         if (ei_status.word16)
 365                 outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
 366 }
 367 
 368 static void
 369 wd_block_output(struct device *dev, int count, const unsigned char *buf,
     /* [previous][next][first][last][top][bottom][index][help] */
 370                                 int start_page)
 371 {
 372         int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
 373         long shmem = dev->mem_start + ((start_page - WD_START_PG)<<8);
 374 
 375 
 376         if (ei_status.word16) {
 377                 /* Turn on and off 16 bit access so that reboot works. */
 378                 outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
 379                 memcpy_toio(shmem, buf, count);
 380                 outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
 381         } else
 382                 memcpy_toio(shmem, buf, count);
 383 }
 384 
 385 
 386 static int
 387 wd_close_card(struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 388 {
 389         int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
 390 
 391         if (ei_debug > 1)
 392                 printk("%s: Shutting down ethercard.\n", dev->name);
 393         NS8390_init(dev, 0);
 394         dev->start = 0;
 395 
 396         /* Change from 16-bit to 8-bit shared memory so reboot works. */
 397         outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
 398 
 399         /* And disable the shared memory. */
 400         outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
 401 
 402 #ifdef MODULE
 403         MOD_DEC_USE_COUNT;
 404 #endif
 405 
 406         return 0;
 407 }
 408 
 409 
 410 #ifdef MODULE
 411 char kernel_version[] = UTS_RELEASE;
 412 static char devicename[9] = { 0, };
 413 static struct device dev_wd80x3 = {
 414         devicename, /* device name is inserted by linux/drivers/net/net_init.c */
 415         0, 0, 0, 0,
 416         0, 0,
 417         0, 0, 0, NULL, wd_probe };
 418 
 419 int io = 0x300;
 420 int irq = 0;
 421 int mem = 0;
 422 
 423 int init_module(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 424 {
 425         if (io == 0)
 426                 printk("wd: You should not use auto-probing with insmod!\n");
 427         dev_wd80x3.base_addr = io;
 428         dev_wd80x3.irq       = irq;
 429         dev_wd80x3.mem_start = mem;
 430         if (register_netdev(&dev_wd80x3) != 0)
 431                 return -EIO;
 432         return 0;
 433 }
 434 
 435 void
 436 cleanup_module(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 437 {
 438         if (MOD_IN_USE)
 439                 printk("wd80x3: device busy, remove delayed\n");
 440         else
 441         {
 442                 int ioaddr = dev_wd80x3.base_addr - WD_NIC_OFFSET;
 443 
 444                 unregister_netdev(&dev_wd80x3);
 445 
 446                 /* If we don't do this, we can't re-insmod it later. */
 447                 free_irq(dev_wd80x3.irq);
 448                 release_region(ioaddr, WD_IO_EXTENT);
 449         }
 450 }
 451 #endif /* MODULE */
 452 
 453 
 454 /*
 455  * Local variables:
 456  *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c wd.c"
 457  *  version-control: t
 458  *  tab-width: 4
 459  *  kept-new-versions: 5
 460  * End:
 461  */

/* [previous][next][first][last][top][bottom][index][help] */