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

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

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