root/net/inet/wd.c

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

DEFINITIONS

This source file includes following definitions.
  1. wdprobe
  2. wdprobe1
  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 by Donald Becker.
   4     Copyright 1993 United States Government as represented by the
   5     Director, National Security Agency.  This software may be used and
   6     distributed according to the terms of the GNU Public License,
   7     incorporated herein by reference.
   8     
   9     This is a driver for the WD8003 and WD8013 ethercards.
  10 
  11     The Author may be reached as becker@super.org or
  12     C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
  13 
  14     Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
  15 */
  16 
  17 static char *version =
  18     "wd.c:v0.99-12 8/12/93 Donald Becker (becker@super.org)\n";
  19 
  20 #include <linux/config.h>
  21 #include <linux/kernel.h>
  22 #include <linux/sched.h>
  23 #include <asm/io.h>
  24 #include <asm/system.h>
  25 #include <memory.h>
  26 
  27 #include "dev.h"
  28 #include "8390.h"
  29 
  30 int wdprobe(int ioaddr, struct device *dev);
  31 int wdprobe1(int ioaddr, struct device *dev);
  32 
  33 static int wd_open(struct device *dev);
  34 static void wd_reset_8390(struct device *dev);
  35 static int wd_block_input(struct device *dev, int count,
  36                           char *buf, int ring_offset);
  37 static void wd_block_output(struct device *dev, int count,
  38                             const unsigned char *buf, const start_page);
  39 static int wd_close_card(struct device *dev);
  40 
  41 
  42 #define WD_START_PG     0x00    /* First page of TX buffer */
  43 #define WD03_STOP_PG    0x20    /* Last page +1 of RX ring */
  44 #define WD13_STOP_PG    0x40    /* Last page +1 of RX ring */
  45 
  46 #define WD_CMDREG       0       /* Offset to ASIC command register. */
  47 #define  WD_RESET       0x80    /* Board reset, in WD_CMDREG. */
  48 #define  WD_MEMENB      0x40    /* Enable the shared memory. */
  49 #define WD_CMDREG5      5       /* Offset to 16-bit-only ASIC register 5. */
  50 #define  ISA16          0x80    /* Enable 16 bit access from the ISA bus. */
  51 #define  NIC16          0x40    /* Enable 16 bit access from the 8390. */
  52 #define WD_NIC_OFFSET   16      /* Offset to the 8390 NIC from the base_addr. */
  53 
  54 /*  Probe for the WD8003 and WD8013.  These cards have the station
  55     address PROM at I/O ports <base>+8 to <base>+13, with a checksum
  56     following. A Soundblaster can have the same checksum as an WDethercard,
  57     so we have an extra exclusionary check for it.
  58 
  59     The wdprobe1() routine initializes the card and fills the
  60     station address field. */
  61 
  62 int wdprobe(int ioaddr,  struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
  63 {
  64     int *port, ports[] = {0x300, 0x280, 0x380, 0x240, 0};
  65 
  66     if (ioaddr > 0x100)
  67         return wdprobe1(ioaddr, dev);
  68 
  69     for (port = &ports[0]; *port; port++)
  70         if (inb(*port + 8) != 0xff
  71             && inb(*port + 9) != 0xff /* Extra check to avoid soundcard. */
  72             && wdprobe1(*port, dev))
  73             return *port;
  74     return 0;
  75 }
  76 
  77 int wdprobe1(int ioaddr, struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
  78 {
  79   int i;
  80   unsigned char *station_addr = dev->dev_addr;
  81   int checksum = 0;
  82   int ancient = 0;              /* An old card without config registers. */
  83   int word16 = 0;               /* 0 = 8 bit, 1 = 16 bit */
  84   char *model_name;
  85 
  86   for (i = 0; i < 8; i++)
  87       checksum += inb(ioaddr + 8 + i);
  88   if ((checksum & 0xff) != 0xFF)
  89       return 0;
  90   
  91   printk("%s: WD80x3 at %#3x, ", dev->name, ioaddr);
  92   for (i = 0; i < 6; i++)
  93       printk(" %2.2X", station_addr[i] = inb(ioaddr + 8 + i));
  94 
  95   /* The following PureData probe code was contributed by
  96      Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata seem to do software
  97      configuration differently from others so we have to check for them.
  98      This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card.
  99      */
 100   if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') {
 101       unsigned char reg5 = inb(ioaddr+5);
 102 
 103       switch (inb(ioaddr+2)) {
 104       case 0x03: word16 = 0; model_name = "PDI8023-8";  break;
 105       case 0x05: word16 = 0; model_name = "PDUC8023";   break;
 106       case 0x0a: word16 = 1; model_name = "PDI8023-16"; break;
 107           /* Either 0x01 (dumb) or they've released a new version. */
 108       default:   word16 = 0; model_name = "PDI8023";    break;
 109       }
 110       dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
 111       dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
 112   } else {                              /* End of PureData probe */
 113       /* This method of checking for a 16-bit board is borrowed from the
 114          we.c driver.  A simpler method is just to look in ASIC reg. 0x03.
 115          I'm comparing the two method in alpha test to make certain they
 116          return the same result. */
 117       /* Check for the old 8 bit board - it has register 0/8 aliasing.
 118          Do NOT check i>=6 here -- it hangs the old 8003 boards! */
 119       for (i = 0; i < 6; i++)
 120           if (inb(ioaddr+i) != inb(ioaddr+8+i))
 121               break;
 122       if (i >= 6) {
 123           ancient = 1;
 124           model_name = "WD8003-old";
 125           word16 = 0;
 126       } else {
 127           int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
 128           outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
 129           if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
 130               && (tmp & 0x01) == 0x01   ) {             /* In a 16 slot. */
 131               int asic_reg5 = inb(ioaddr+WD_CMDREG5);
 132               /* Magic to set ASIC to word-wide mode. */
 133               outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
 134               outb(tmp, ioaddr+1);
 135               model_name = "WD8013";
 136               word16 = 1;       /* We have a 16bit board here! */
 137           } else {
 138               model_name = "WD8003";
 139               word16 = 0;
 140           }
 141           outb(tmp, ioaddr+1);          /* Restore original reg1 value. */
 142       }
 143 #ifndef final_version
 144       if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01))
 145           printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
 146                  word16 ? 16 : 8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
 147 #endif
 148   }
 149 
 150 #if defined(WD_SHMEM) && WD_SHMEM > 0x80000
 151   /* Allow a compile-time override.  */
 152   dev->mem_start = WD_SHMEM;
 153 #else
 154   if (dev->mem_start == 0) {
 155       /* Sanity and old 8003 check */
 156       int reg0 = inb(ioaddr);
 157       if (reg0 == 0xff || reg0 == 0) {
 158           /* Future plan: this could check a few likely locations first. */
 159           dev->mem_start = 0xd0000;
 160           printk(" assigning address %#x", dev->mem_start);
 161       } else {
 162           int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f;
 163           /* Some boards don't have the register 5 -- it returns 0xff. */
 164           if (high_addr_bits == 0x1f || word16 == 0)
 165               high_addr_bits = 0x01;
 166           dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
 167       }
 168   }
 169 #endif
 170 
 171   /* The 8390 isn't at the base address -- the ASIC regs are there! */
 172   dev->base_addr = ioaddr+WD_NIC_OFFSET;
 173 
 174   if (dev->irq < 2) {
 175       int irqmap[] = {9,3,5,7,10,11,15,4};
 176       int reg1 = inb(ioaddr+1);
 177       int reg4 = inb(ioaddr+4);
 178       if (reg1 == 0xff)         /* Ack!! No way to read the IRQ! */
 179           dev->irq = word16 ? 10 : 5;
 180       else
 181           dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
 182   } else if (dev->irq == 2)     /* Fixup bogosity: IRQ2 is really IRQ9 */
 183       dev->irq = 9;
 184 
 185   /* Snarf the interrupt now.  There's no point in waiting since we cannot
 186      share and the board will usually be enabled. */
 187   { int irqval = irqaction (dev->irq, &ei_sigaction);
 188     if (irqval) {
 189         printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
 190         return 0;
 191     }
 192   }
 193 
 194   /* OK, were are certain this is going to work.  Setup the device. */
 195   ethdev_init(dev);
 196 
 197   ei_status.name = model_name;
 198   ei_status.word16 = word16;
 199   ei_status.tx_start_page = WD_START_PG;
 200   ei_status.rx_start_page = WD_START_PG + TX_PAGES;
 201   ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
 202 
 203   /* Don't map in the shared memory until the board is actually opened. */
 204   dev->rmem_start = dev->mem_start + TX_PAGES*256;
 205   dev->mem_end = dev->rmem_end
 206       = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
 207 
 208   printk(" %s, IRQ %d, shared memory at %#x-%#x.\n",
 209          model_name, dev->irq, dev->mem_start, dev->mem_end-1);
 210   if (ei_debug > 0)
 211       printk(version);
 212 
 213   ei_status.reset_8390 = &wd_reset_8390;
 214   ei_status.block_input = &wd_block_input;
 215   ei_status.block_output = &wd_block_output;
 216   dev->open = &wd_open;
 217   dev->stop = &wd_close_card;
 218   NS8390_init(dev, 0);
 219 
 220   return dev->base_addr;
 221 }
 222 
 223 static int
 224 wd_open(struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 225 {
 226   int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
 227 
 228   /* Map in the shared memory. Always set register 0 last to remain
 229      compatible with very old boards. */
 230   ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB;
 231   ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16;
 232 
 233   if (ei_status.word16)
 234       outb(ISA16 | ei_status.reg5, ioaddr+WD_CMDREG5);
 235   outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
 236   return ei_open(dev);
 237 }
 238 
 239 static void
 240 wd_reset_8390(struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 241 {
 242     int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
 243     int reset_start_time = jiffies;
 244 
 245     outb(WD_RESET, wd_cmd_port);
 246     if (ei_debug > 1) printk("resetting the WD80x3 t=%d...", jiffies);
 247     ei_status.txing = 0;
 248 
 249     sti();
 250     /* We shouldn't use the boguscount for timing, but this hasn't been
 251        checked yet, and you could hang your machine if jiffies break... */
 252     {
 253         int boguscount = 150000;
 254         while(jiffies - reset_start_time < 2)
 255             if (boguscount-- < 0) {
 256                 printk("jiffy failure (t=%d)...", jiffies);
 257                 break;
 258             }
 259     }
 260 
 261     /* Set up the ASIC registers, just in case something changed them. */
 262     if (ei_status.word16)
 263         outb(NIC16 | ((dev->mem_start>>19) & 0x1f),
 264              wd_cmd_port+WD_CMDREG5);
 265     outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port);
 266 
 267     while ((inb(dev->base_addr+EN0_ISR) & ENISR_RESET) == 0)
 268         if (jiffies - reset_start_time > 2) {
 269             printk("%s: wd_reset_8390() did not complete.\n", dev->name);
 270             break;
 271         }
 272     return;
 273 }
 274 
 275 /* Block input and output are easy on shared memory ethercards, and trivial
 276    on the Western digital card where there is no choice of how to do it. */
 277 
 278 static int
 279 wd_block_input(struct device *dev, int count, char *buf, int ring_offset)
     /* [previous][next][first][last][top][bottom][index][help] */
 280 {
 281     void *xfer_start = (void *)(dev->mem_start + ring_offset
 282                                 - (WD_START_PG<<8));
 283 
 284     /* This mapout won't be necessary when wd_close_card is called. */
 285 #if !defined(WD_no_mapout)
 286     int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
 287 
 288     if (ei_status.word16)
 289         outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
 290     outb(ei_status.reg0, wd_cmdreg); /* WD_CMDREG */
 291 #endif
 292 
 293     if (xfer_start + count > (void*) dev->rmem_end) {
 294         /* We must wrap the input move. */
 295         int semi_count = (void*)dev->rmem_end - xfer_start;
 296         memcpy(buf, xfer_start, semi_count);
 297         count -= semi_count;
 298         memcpy(buf + semi_count, (char *)dev->rmem_start, count);
 299         return dev->rmem_start + count;
 300     }
 301     memcpy(buf, xfer_start, count);
 302     if (ei_debug > 4) {
 303         unsigned short *board = (unsigned short *) xfer_start;
 304         printk("%s: wd8013 block_input(cnt=%d offset=%3x addr=%#x) = %2x %2x %2x...\n",
 305                dev->name, count, ring_offset, xfer_start,
 306                board[-1], board[0], board[1]);
 307     }
 308 
 309 #if !defined(WD_no_mapout)
 310     /* Turn off 16 bit access so that reboot works. */
 311     if (ei_status.word16)
 312         outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
 313 #endif
 314     return ring_offset + count;
 315 }
 316 
 317 /* This could only be outputting to the transmit buffer.  The
 318    ping-pong transmit setup doesn't work with this yet. */
 319 static void
 320 wd_block_output(struct device *dev, int count, const unsigned char *buf,
     /* [previous][next][first][last][top][bottom][index][help] */
 321                 int start_page)
 322 {
 323     unsigned char *shmem
 324         = (unsigned char *)dev->mem_start + ((start_page - WD_START_PG)<<8);
 325 
 326 #if !defined(WD_no_mapout)
 327     int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
 328 
 329     if (ei_status.word16)
 330         outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
 331     outb(ei_status.reg0, wd_cmdreg); /* WD_CMDREG */
 332 #endif
 333 
 334     memcpy(shmem, buf, count);
 335     if (ei_debug > 4)
 336         printk("%s: wd80*3 block_output(addr=%#x cnt=%d) -> %2x=%2x %2x=%2x %d...\n",
 337                shmem, count, shmem[23], buf[23], shmem[24], buf[24], memcmp(shmem,buf,count));
 338 
 339 #if !defined(WD_no_mapout)
 340     /* Turn off 16 bit access so that reboot works. */
 341     if (ei_status.word16)
 342         outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
 343 #endif
 344 }
 345 
 346 /* This function resets the ethercard if something screws up. */
 347 static int
 348 wd_close_card(struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 349 {
 350     int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
 351 
 352     if (ei_debug > 1)
 353         printk("%s: Shutting down ethercard.\n", dev->name);
 354     NS8390_init(dev, 0);
 355 
 356     /* Change from 16-bit to 8-bit shared memory so reboot works. */
 357     outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
 358 
 359     /* And disable the shared memory. */
 360     outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
 361 
 362     return 0;
 363 }
 364 
 365 
 366 /*
 367  * Local variables:
 368  *  compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c wd.c"
 369  *  version-control: t
 370  *  kept-new-versions: 5
 371  * End:
 372  */

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