root/drivers/net/hp-plus.c

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

DEFINITIONS

This source file includes following definitions.
  1. hp_plus_probe
  2. hpp_probe1
  3. hpp_open
  4. hpp_close
  5. hpp_reset_8390
  6. hpp_io_block_input
  7. hpp_mem_block_input
  8. hpp_io_block_output
  9. hpp_mem_block_output
  10. init_module
  11. cleanup_module

   1 /* hp-plus.c: A HP PCLAN/plus ethernet driver for linux. */
   2 /*
   3         Written 1994 by Donald Becker.
   4 
   5         This driver is for the Hewlett Packard PC LAN (27***) plus ethercards.
   6         These cards are sold under several model numbers, usually 2724*.
   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 
  13         Center of Excellence in Space Data and Information Sciences
  14                 Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
  15 
  16         As is often the case, a great deal of credit is owed to Russ Nelson.
  17         The Crynwr packet driver was my primary source of HP-specific
  18         programming information.
  19 */
  20 
  21 static const char *version =
  22 "hp-plus.c:v1.10 9/24/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
  23 
  24 #ifdef MODULE
  25 #include <linux/module.h>
  26 #include <linux/version.h>
  27 #endif
  28 
  29 #include <linux/string.h>               /* Important -- this inlines word moves. */
  30 #include <linux/kernel.h>
  31 #include <linux/sched.h>
  32 #include <linux/errno.h>
  33 #include <linux/ioport.h>
  34 #include <linux/netdevice.h>
  35 #include <linux/etherdevice.h>
  36 
  37 #include <asm/system.h>
  38 #include <asm/io.h>
  39 
  40 
  41 #include "8390.h"
  42 
  43 /* A zero-terminated list of I/O addresses to be probed. */
  44 static unsigned int hpplus_portlist[] =
  45 {0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0};
  46 
  47 /*
  48    The HP EtherTwist chip implementation is a fairly routine DP8390
  49    implementation.  It allows both shared memory and programmed-I/O buffer
  50    access, using a custom interface for both.  The programmed-I/O mode is
  51    entirely implemented in the HP EtherTwist chip, bypassing the problem
  52    ridden built-in 8390 facilities used on NE2000 designs.  The shared
  53    memory mode is likewise special, with an offset register used to make
  54    packets appear at the shared memory base.  Both modes use a base and bounds
  55    page register to hide the Rx ring buffer wrap -- a packet that spans the
  56    end of physical buffer memory appears continuous to the driver. (c.f. the
  57    3c503 and Cabletron E2100)
  58 
  59    A special note: the internal buffer of the board is only 8 bits wide.
  60    This lays several nasty traps for the unaware:
  61    - the 8390 must be programmed for byte-wide operations
  62    - all I/O and memory operations must work on whole words (the access
  63      latches are serially preloaded and have no byte-swapping ability).
  64 
  65    This board is laid out in I/O space much like the earlier HP boards:
  66    the first 16 locations are for the board registers, and the second 16 are
  67    for the 8390.  The board is easy to identify, with both a dedicated 16 bit
  68    ID register and a constant 0x530* value in the upper bits of the paging
  69    register.
  70 */
  71 
  72 #define HP_ID                   0x00    /* ID register, always 0x4850. */
  73 #define HP_PAGING               0x02    /* Registers visible @ 8-f, see PageName. */ 
  74 #define HPP_OPTION              0x04    /* Bitmapped options, see HP_Option.    */
  75 #define HPP_OUT_ADDR    0x08    /* I/O output location in Perf_Page.    */
  76 #define HPP_IN_ADDR             0x0A    /* I/O input location in Perf_Page.             */
  77 #define HP_DATAPORT             0x0c    /* I/O data transfer in Perf_Page.              */
  78 #define NIC_OFFSET              0x10    /* Offset to the 8390 registers.                */
  79 #define HP_IO_EXTENT    32
  80 
  81 #define HP_START_PG             0x00    /* First page of TX buffer */
  82 #define HP_STOP_PG              0x80    /* Last page +1 of RX ring */
  83 
  84 /* The register set selected in HP_PAGING. */
  85 enum PageName {
  86         Perf_Page = 0,                          /* Normal operation. */
  87         MAC_Page = 1,                           /* The ethernet address (+checksum). */
  88         HW_Page = 2,                            /* EEPROM-loaded hardware parameters. */
  89         LAN_Page = 4,                           /* Transceiver selection, testing, etc. */
  90         ID_Page = 6 }; 
  91 
  92 /* The bit definitions for the HPP_OPTION register. */
  93 enum HP_Option {
  94         NICReset = 1, ChipReset = 2,    /* Active low, really UNreset. */
  95         EnableIRQ = 4, FakeIntr = 8, BootROMEnb = 0x10, IOEnb = 0x20,
  96         MemEnable = 0x40, ZeroWait = 0x80, MemDisable = 0x1000, };
  97 
  98 int hp_plus_probe(struct device *dev);
  99 int hpp_probe1(struct device *dev, int ioaddr);
 100 
 101 static void hpp_reset_8390(struct device *dev);
 102 static int hpp_open(struct device *dev);
 103 static int hpp_close(struct device *dev);
 104 static int hpp_mem_block_input(struct device *dev, int count,
 105                                                   char *buf, int ring_offset);
 106 static void hpp_mem_block_output(struct device *dev, int count,
 107                                                         const unsigned char *buf, const start_page);
 108 static int hpp_io_block_input(struct device *dev, int count,
 109                                                   char *buf, int ring_offset);
 110 static void hpp_io_block_output(struct device *dev, int count,
 111                                                         const unsigned char *buf, const start_page);
 112 
 113 
 114 /*      Probe a list of addresses for an HP LAN+ adaptor.
 115         This routine is almost boilerplate. */
 116 #ifdef HAVE_DEVLIST
 117 /* Support for a alternate probe manager, which will eliminate the
 118    boilerplate below. */
 119 struct netdev_entry hpplus_drv =
 120 {"hpplus", hpp_probe1, HP_IO_EXTENT, hpplus_portlist};
 121 #else
 122 
 123 int hp_plus_probe(struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 124 {
 125         int i;
 126         int base_addr = dev ? dev->base_addr : 0;
 127 
 128         if (base_addr > 0x1ff)          /* Check a single specified location. */
 129                 return hpp_probe1(dev, base_addr);
 130         else if (base_addr != 0)        /* Don't probe at all. */
 131                 return ENXIO;
 132 
 133         for (i = 0; hpplus_portlist[i]; i++) {
 134                 int ioaddr = hpplus_portlist[i];
 135                 if (check_region(ioaddr, HP_IO_EXTENT))
 136                         continue;
 137                 if (hpp_probe1(dev, ioaddr) == 0)
 138                         return 0;
 139         }
 140 
 141         return ENODEV;
 142 }
 143 #endif
 144 
 145 /* Do the interesting part of the probe at a single address. */
 146 int hpp_probe1(struct device *dev, int ioaddr)
     /* [previous][next][first][last][top][bottom][index][help] */
 147 {
 148         int i;
 149         unsigned char checksum = 0;
 150         const char *name = "HP-PC-LAN+";
 151         int mem_start;
 152 
 153         /* Check for the HP+ signature, 50 48 0x 53. */
 154         if (inw(ioaddr + HP_ID) != 0x4850
 155                 || (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300)
 156                 return ENODEV;
 157 
 158     if (dev == NULL)
 159                 dev = init_etherdev(0, sizeof(struct ei_device), 0);
 160 
 161         printk("%s: %s at %#3x,", dev->name, name, ioaddr);
 162 
 163         /* Retrieve and checksum the station address. */
 164         outw(MAC_Page, ioaddr + HP_PAGING);
 165 
 166         for(i = 0; i < ETHER_ADDR_LEN; i++) {
 167                 unsigned char inval = inb(ioaddr + 8 + i);
 168                 dev->dev_addr[i] = inval;
 169                 checksum += inval;
 170                 printk(" %2.2x", inval);
 171         }
 172         checksum += inb(ioaddr + 14);
 173 
 174         if (checksum != 0xff) {
 175                 printk(" bad checksum %2.2x.\n", checksum);
 176                 return ENODEV;
 177         } else {
 178                 /* Point at the Software Configuration Flags. */
 179                 outw(ID_Page, ioaddr + HP_PAGING);
 180                 printk(" ID %4.4x", inw(ioaddr + 12));
 181         }
 182 
 183         /* Grab the region so we can find another board if something fails. */
 184         request_region(ioaddr, HP_IO_EXTENT,"hp-plus");
 185 
 186         /* Read the IRQ line. */
 187         outw(HW_Page, ioaddr + HP_PAGING);
 188         {
 189                 int irq = inb(ioaddr + 13) & 0x0f;
 190                 int option = inw(ioaddr + HPP_OPTION);
 191 
 192                 dev->irq = irq;
 193                 if (option & MemEnable) {
 194                         mem_start = inw(ioaddr + 9) << 8;
 195                         printk(", IRQ %d, memory address %#x.\n", irq, mem_start);
 196                 } else {
 197                         mem_start = 0;
 198                         printk(", IRQ %d, programmed-I/O mode.\n", irq);
 199                 }
 200         }
 201 
 202         printk( "%s%s", KERN_INFO, version);
 203 
 204         /* Set the wrap registers for string I/O reads.   */
 205         outw((HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
 206 
 207         /* Set the base address to point to the NIC, not the "real" base! */
 208         dev->base_addr = ioaddr + NIC_OFFSET;
 209 
 210         ethdev_init(dev);
 211 
 212     dev->open = &hpp_open;
 213     dev->stop = &hpp_close;
 214 
 215         ei_status.name = name;
 216         ei_status.word16 = 0;           /* Agggghhhhh! Debug time: 2 days! */
 217         ei_status.tx_start_page = HP_START_PG;
 218         ei_status.rx_start_page = HP_START_PG + TX_2X_PAGES;
 219         ei_status.stop_page = HP_STOP_PG;
 220 
 221         ei_status.reset_8390 = &hpp_reset_8390;
 222         ei_status.block_input = &hpp_io_block_input;
 223         ei_status.block_output = &hpp_io_block_output;
 224 
 225         /* Check if the memory_enable flag is set in the option register. */
 226         if (mem_start) {
 227                 ei_status.block_input = &hpp_mem_block_input;
 228                 ei_status.block_output = &hpp_mem_block_output;
 229                 dev->mem_start = mem_start;
 230                 dev->rmem_start = dev->mem_start + TX_2X_PAGES*256;
 231                 dev->mem_end = dev->rmem_end
 232                         = dev->mem_start + (HP_STOP_PG - HP_START_PG)*256;
 233         }
 234 
 235         outw(Perf_Page, ioaddr + HP_PAGING);
 236         NS8390_init(dev, 0);
 237         /* Leave the 8390 and HP chip reset. */
 238         outw(inw(ioaddr + HPP_OPTION) & ~EnableIRQ, ioaddr + HPP_OPTION);
 239 
 240         return 0;
 241 }
 242 
 243 static int
 244 hpp_open(struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 245 {
 246         int ioaddr = dev->base_addr - NIC_OFFSET;
 247         int option_reg;
 248 
 249         if (request_irq(dev->irq, &ei_interrupt, 0, "hp-plus")) {
 250             return -EAGAIN;
 251         }
 252 
 253         /* Reset the 8390 and HP chip. */
 254         option_reg = inw(ioaddr + HPP_OPTION);
 255         outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION);
 256         SLOW_DOWN_IO; SLOW_DOWN_IO;
 257         /* Unreset the board and enable interrupts. */
 258         outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION);
 259 
 260         /* Set the wrap registers for programmed-I/O operation.   */
 261         outw(HW_Page, ioaddr + HP_PAGING);
 262         outw((HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
 263 
 264         /* Select the operational page. */
 265         outw(Perf_Page, ioaddr + HP_PAGING);
 266 
 267     return ei_open(dev);
 268 }
 269 
 270 static int
 271 hpp_close(struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 272 {
 273         int ioaddr = dev->base_addr - NIC_OFFSET;
 274         int option_reg = inw(ioaddr + HPP_OPTION);
 275 
 276     free_irq(dev->irq);
 277     irq2dev_map[dev->irq] = NULL;
 278     NS8390_init(dev, 0);
 279         outw((option_reg & ~EnableIRQ) | MemDisable | NICReset | ChipReset,
 280                  ioaddr + HPP_OPTION);
 281 
 282     return 0;
 283 }
 284 
 285 static void
 286 hpp_reset_8390(struct device *dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 287 {
 288         int ioaddr = dev->base_addr - NIC_OFFSET;
 289         int option_reg = inw(ioaddr + HPP_OPTION);
 290 
 291         if (ei_debug > 1) printk("resetting the 8390 time=%ld...", jiffies);
 292 
 293         outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION);
 294         /* Pause a few cycles for the hardware reset to take place. */
 295         SLOW_DOWN_IO;
 296         SLOW_DOWN_IO;
 297         ei_status.txing = 0;
 298         outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION);
 299 
 300         SLOW_DOWN_IO; SLOW_DOWN_IO;
 301 
 302 
 303         if ((inb_p(ioaddr+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
 304                 printk("%s: hp_reset_8390() did not complete.\n", dev->name);
 305 
 306         if (ei_debug > 1) printk("8390 reset done (%ld).", jiffies);
 307         return;
 308 }
 309 
 310 /* Block input and output, similar to the Crynwr packet driver.
 311    Note that transfer with the EtherTwist+ must be on word boundaries. */
 312 
 313 static int
 314 hpp_io_block_input(struct device *dev, int count, char *buf, int ring_offset)
     /* [previous][next][first][last][top][bottom][index][help] */
 315 {
 316         int ioaddr = dev->base_addr - NIC_OFFSET;
 317 
 318         outw(ring_offset, ioaddr + HPP_IN_ADDR);
 319         insw(ioaddr + HP_DATAPORT, buf, count>>1);
 320         if (count & 0x01)
 321         buf[count-1] = inw(ioaddr + HP_DATAPORT);
 322         return ring_offset + count;
 323 }
 324 
 325 static int
 326 hpp_mem_block_input(struct device *dev, int count, char *buf, int ring_offset)
     /* [previous][next][first][last][top][bottom][index][help] */
 327 {
 328         int ioaddr = dev->base_addr - NIC_OFFSET;
 329         int option_reg = inw(ioaddr + HPP_OPTION);
 330 
 331         outw(ring_offset, ioaddr + HPP_IN_ADDR);
 332 
 333         outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
 334         /* Caution: this relies on 8390.c rounding up allocations! */
 335         memcpy(buf, (char*)dev->mem_start,  (count + 3) & ~3);
 336         outw(option_reg, ioaddr + HPP_OPTION);
 337 
 338         return ring_offset + count;
 339 }
 340 
 341 /* A special note: we *must* always transfer >=16 bit words.
 342    It's always safe to round up, so we do. */
 343 static void
 344 hpp_io_block_output(struct device *dev, int count,
     /* [previous][next][first][last][top][bottom][index][help] */
 345                                         const unsigned char *buf, const start_page)
 346 {
 347         int ioaddr = dev->base_addr - NIC_OFFSET;
 348         outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
 349         outsl(ioaddr + HP_DATAPORT, buf, (count+3)>>2);
 350         return;
 351 }
 352 
 353 static void
 354 hpp_mem_block_output(struct device *dev, int count,
     /* [previous][next][first][last][top][bottom][index][help] */
 355                                 const unsigned char *buf, const start_page)
 356 {
 357         int ioaddr = dev->base_addr - NIC_OFFSET;
 358         int option_reg = inw(ioaddr + HPP_OPTION);
 359 
 360         outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
 361         outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
 362         memcpy((char *)dev->mem_start, buf, (count + 3) & ~3);
 363         outw(option_reg, ioaddr + HPP_OPTION);
 364 
 365         return;
 366 }
 367 
 368 #ifdef MODULE
 369 char kernel_version[] = UTS_RELEASE;
 370 static char devicename[9] = { 0, };
 371 static struct device dev_hp = {
 372         devicename, /* device name is inserted by linux/drivers/net/net_init.c */
 373         0, 0, 0, 0,
 374         0, 0,
 375         0, 0, 0, NULL, hp_plus_probe };
 376 
 377 int io = 0x200;
 378 int irq = 0;
 379 
 380 int init_module(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 381 {
 382         if (io == 0)
 383                 printk("HP-plus: You should not use auto-probing with insmod!\n");
 384         dev_hp.base_addr = io;
 385         dev_hp.irq       = irq;
 386         if (register_netdev(&dev_hp) != 0) {
 387                 printk("HP-plus: register_netdev() returned non-zero.\n");
 388                 return -EIO;
 389         }
 390         return 0;
 391 }
 392 
 393 void
 394 cleanup_module(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 395 {
 396         if (MOD_IN_USE)
 397                 printk("HP-plus: device busy, remove delayed\n");
 398         else
 399         {
 400                 int ioaddr = dev_hp.base_addr - NIC_OFFSET;
 401 
 402                 unregister_netdev(&dev_hp);
 403 
 404                 /* If we don't do this, we can't re-insmod it later. */
 405                 release_region(ioaddr, HP_IO_EXTENT);
 406         }
 407 }
 408 #endif /* MODULE */
 409 
 410 /*
 411  * Local variables:
 412  * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp-plus.c"
 413  * version-control: t
 414  * kept-new-versions: 5
 415  * tab-width: 4
 416  * c-indent-level: 4
 417  * End:
 418  */

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