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