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_block_input
  6. wd_block_output
  7. wd_close_card

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

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