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

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