root/net/inet/wd.c

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

DEFINITIONS

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

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