linux/drivers/net/hp-plus.c
<<
>>
Prefs
   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 General Public License, incorporated herein by reference.
  10
  11        The author may be reached as becker@scyld.com, or C/O
  12        Scyld Computing Corporation
  13        410 Severn Ave., Suite 210
  14        Annapolis MD 21403
  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
  21static const char version[] =
  22"hp-plus.c:v1.10 9/24/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
  23
  24#include <linux/module.h>
  25
  26#include <linux/string.h>               /* Important -- this inlines word moves. */
  27#include <linux/kernel.h>
  28#include <linux/errno.h>
  29#include <linux/ioport.h>
  30#include <linux/netdevice.h>
  31#include <linux/etherdevice.h>
  32#include <linux/init.h>
  33#include <linux/delay.h>
  34
  35#include <asm/system.h>
  36#include <asm/io.h>
  37
  38#include "8390.h"
  39
  40#define DRV_NAME "hp-plus"
  41
  42/* A zero-terminated list of I/O addresses to be probed. */
  43static unsigned int hpplus_portlist[] __initdata =
  44{0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0};
  45
  46/*
  47   The HP EtherTwist chip implementation is a fairly routine DP8390
  48   implementation.  It allows both shared memory and programmed-I/O buffer
  49   access, using a custom interface for both.  The programmed-I/O mode is
  50   entirely implemented in the HP EtherTwist chip, bypassing the problem
  51   ridden built-in 8390 facilities used on NE2000 designs.  The shared
  52   memory mode is likewise special, with an offset register used to make
  53   packets appear at the shared memory base.  Both modes use a base and bounds
  54   page register to hide the Rx ring buffer wrap -- a packet that spans the
  55   end of physical buffer memory appears continuous to the driver. (c.f. the
  56   3c503 and Cabletron E2100)
  57
  58   A special note: the internal buffer of the board is only 8 bits wide.
  59   This lays several nasty traps for the unaware:
  60   - the 8390 must be programmed for byte-wide operations
  61   - all I/O and memory operations must work on whole words (the access
  62     latches are serially preloaded and have no byte-swapping ability).
  63
  64   This board is laid out in I/O space much like the earlier HP boards:
  65   the first 16 locations are for the board registers, and the second 16 are
  66   for the 8390.  The board is easy to identify, with both a dedicated 16 bit
  67   ID register and a constant 0x530* value in the upper bits of the paging
  68   register.
  69*/
  70
  71#define HP_ID                   0x00    /* ID register, always 0x4850. */
  72#define HP_PAGING               0x02    /* Registers visible @ 8-f, see PageName. */
  73#define HPP_OPTION              0x04    /* Bitmapped options, see HP_Option.    */
  74#define HPP_OUT_ADDR    0x08    /* I/O output location in Perf_Page.    */
  75#define HPP_IN_ADDR             0x0A    /* I/O input location in Perf_Page.             */
  76#define HP_DATAPORT             0x0c    /* I/O data transfer in Perf_Page.              */
  77#define NIC_OFFSET              0x10    /* Offset to the 8390 registers.                */
  78#define HP_IO_EXTENT    32
  79
  80#define HP_START_PG             0x00    /* First page of TX buffer */
  81#define HP_STOP_PG              0x80    /* Last page +1 of RX ring */
  82
  83/* The register set selected in HP_PAGING. */
  84enum PageName {
  85        Perf_Page = 0,                          /* Normal operation. */
  86        MAC_Page = 1,                           /* The ethernet address (+checksum). */
  87        HW_Page = 2,                            /* EEPROM-loaded hardware parameters. */
  88        LAN_Page = 4,                           /* Transceiver selection, testing, etc. */
  89        ID_Page = 6 };
  90
  91/* The bit definitions for the HPP_OPTION register. */
  92enum HP_Option {
  93        NICReset = 1, ChipReset = 2,    /* Active low, really UNreset. */
  94        EnableIRQ = 4, FakeIntr = 8, BootROMEnb = 0x10, IOEnb = 0x20,
  95        MemEnable = 0x40, ZeroWait = 0x80, MemDisable = 0x1000, };
  96
  97static int hpp_probe1(struct net_device *dev, int ioaddr);
  98
  99static void hpp_reset_8390(struct net_device *dev);
 100static int hpp_open(struct net_device *dev);
 101static int hpp_close(struct net_device *dev);
 102static void hpp_mem_block_input(struct net_device *dev, int count,
 103                                                  struct sk_buff *skb, int ring_offset);
 104static void hpp_mem_block_output(struct net_device *dev, int count,
 105                                                        const unsigned char *buf, int start_page);
 106static void hpp_mem_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
 107                                                  int ring_page);
 108static void hpp_io_block_input(struct net_device *dev, int count,
 109                                                  struct sk_buff *skb, int ring_offset);
 110static void hpp_io_block_output(struct net_device *dev, int count,
 111                                                        const unsigned char *buf, int start_page);
 112static void hpp_io_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
 113                                                  int ring_page);
 114
 115
 116/*      Probe a list of addresses for an HP LAN+ adaptor.
 117        This routine is almost boilerplate. */
 118
 119static int __init do_hpp_probe(struct net_device *dev)
 120{
 121        int i;
 122        int base_addr = dev->base_addr;
 123        int irq = dev->irq;
 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                if (hpp_probe1(dev, hpplus_portlist[i]) == 0)
 132                        return 0;
 133                dev->irq = irq;
 134        }
 135
 136        return -ENODEV;
 137}
 138
 139#ifndef MODULE
 140struct net_device * __init hp_plus_probe(int unit)
 141{
 142        struct net_device *dev = alloc_eip_netdev();
 143        int err;
 144
 145        if (!dev)
 146                return ERR_PTR(-ENOMEM);
 147
 148        sprintf(dev->name, "eth%d", unit);
 149        netdev_boot_setup_check(dev);
 150
 151        err = do_hpp_probe(dev);
 152        if (err)
 153                goto out;
 154        return dev;
 155out:
 156        free_netdev(dev);
 157        return ERR_PTR(err);
 158}
 159#endif
 160
 161/* Do the interesting part of the probe at a single address. */
 162static int __init hpp_probe1(struct net_device *dev, int ioaddr)
 163{
 164        int i, retval;
 165        unsigned char checksum = 0;
 166        const char name[] = "HP-PC-LAN+";
 167        int mem_start;
 168        static unsigned version_printed;
 169        DECLARE_MAC_BUF(mac);
 170
 171        if (!request_region(ioaddr, HP_IO_EXTENT, DRV_NAME))
 172                return -EBUSY;
 173
 174        /* Check for the HP+ signature, 50 48 0x 53. */
 175        if (inw(ioaddr + HP_ID) != 0x4850
 176                || (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300) {
 177                retval = -ENODEV;
 178                goto out;
 179        }
 180
 181        if (ei_debug  &&  version_printed++ == 0)
 182                printk(version);
 183
 184        printk("%s: %s at %#3x, ", dev->name, name, ioaddr);
 185
 186        /* Retrieve and checksum the station address. */
 187        outw(MAC_Page, ioaddr + HP_PAGING);
 188
 189        for(i = 0; i < ETHER_ADDR_LEN; i++) {
 190                unsigned char inval = inb(ioaddr + 8 + i);
 191                dev->dev_addr[i] = inval;
 192                checksum += inval;
 193        }
 194        checksum += inb(ioaddr + 14);
 195
 196        printk("%s", print_mac(mac, dev->dev_addr));
 197
 198        if (checksum != 0xff) {
 199                printk(" bad checksum %2.2x.\n", checksum);
 200                retval = -ENODEV;
 201                goto out;
 202        } else {
 203                /* Point at the Software Configuration Flags. */
 204                outw(ID_Page, ioaddr + HP_PAGING);
 205                printk(" ID %4.4x", inw(ioaddr + 12));
 206        }
 207
 208        /* Read the IRQ line. */
 209        outw(HW_Page, ioaddr + HP_PAGING);
 210        {
 211                int irq = inb(ioaddr + 13) & 0x0f;
 212                int option = inw(ioaddr + HPP_OPTION);
 213
 214                dev->irq = irq;
 215                if (option & MemEnable) {
 216                        mem_start = inw(ioaddr + 9) << 8;
 217                        printk(", IRQ %d, memory address %#x.\n", irq, mem_start);
 218                } else {
 219                        mem_start = 0;
 220                        printk(", IRQ %d, programmed-I/O mode.\n", irq);
 221                }
 222        }
 223
 224        /* Set the wrap registers for string I/O reads.   */
 225        outw((HP_START_PG + TX_PAGES/2) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
 226
 227        /* Set the base address to point to the NIC, not the "real" base! */
 228        dev->base_addr = ioaddr + NIC_OFFSET;
 229
 230        dev->open = &hpp_open;
 231        dev->stop = &hpp_close;
 232#ifdef CONFIG_NET_POLL_CONTROLLER
 233        dev->poll_controller = eip_poll;
 234#endif
 235
 236        ei_status.name = name;
 237        ei_status.word16 = 0;           /* Agggghhhhh! Debug time: 2 days! */
 238        ei_status.tx_start_page = HP_START_PG;
 239        ei_status.rx_start_page = HP_START_PG + TX_PAGES/2;
 240        ei_status.stop_page = HP_STOP_PG;
 241
 242        ei_status.reset_8390 = &hpp_reset_8390;
 243        ei_status.block_input = &hpp_io_block_input;
 244        ei_status.block_output = &hpp_io_block_output;
 245        ei_status.get_8390_hdr = &hpp_io_get_8390_hdr;
 246
 247        /* Check if the memory_enable flag is set in the option register. */
 248        if (mem_start) {
 249                ei_status.block_input = &hpp_mem_block_input;
 250                ei_status.block_output = &hpp_mem_block_output;
 251                ei_status.get_8390_hdr = &hpp_mem_get_8390_hdr;
 252                dev->mem_start = mem_start;
 253                ei_status.mem = ioremap(mem_start,
 254                                        (HP_STOP_PG - HP_START_PG)*256);
 255                if (!ei_status.mem) {
 256                        retval = -ENOMEM;
 257                        goto out;
 258                }
 259                ei_status.rmem_start = dev->mem_start + TX_PAGES/2*256;
 260                dev->mem_end = ei_status.rmem_end
 261                        = dev->mem_start + (HP_STOP_PG - HP_START_PG)*256;
 262        }
 263
 264        outw(Perf_Page, ioaddr + HP_PAGING);
 265        NS8390p_init(dev, 0);
 266        /* Leave the 8390 and HP chip reset. */
 267        outw(inw(ioaddr + HPP_OPTION) & ~EnableIRQ, ioaddr + HPP_OPTION);
 268
 269        retval = register_netdev(dev);
 270        if (retval)
 271                goto out1;
 272        return 0;
 273out1:
 274        iounmap(ei_status.mem);
 275out:
 276        release_region(ioaddr, HP_IO_EXTENT);
 277        return retval;
 278}
 279
 280static int
 281hpp_open(struct net_device *dev)
 282{
 283        int ioaddr = dev->base_addr - NIC_OFFSET;
 284        int option_reg;
 285        int retval;
 286
 287        if ((retval = request_irq(dev->irq, eip_interrupt, 0, dev->name, dev))) {
 288            return retval;
 289        }
 290
 291        /* Reset the 8390 and HP chip. */
 292        option_reg = inw(ioaddr + HPP_OPTION);
 293        outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION);
 294        udelay(5);
 295        /* Unreset the board and enable interrupts. */
 296        outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION);
 297
 298        /* Set the wrap registers for programmed-I/O operation.   */
 299        outw(HW_Page, ioaddr + HP_PAGING);
 300        outw((HP_START_PG + TX_PAGES/2) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
 301
 302        /* Select the operational page. */
 303        outw(Perf_Page, ioaddr + HP_PAGING);
 304
 305        eip_open(dev);
 306        return 0;
 307}
 308
 309static int
 310hpp_close(struct net_device *dev)
 311{
 312        int ioaddr = dev->base_addr - NIC_OFFSET;
 313        int option_reg = inw(ioaddr + HPP_OPTION);
 314
 315        free_irq(dev->irq, dev);
 316        eip_close(dev);
 317        outw((option_reg & ~EnableIRQ) | MemDisable | NICReset | ChipReset,
 318                 ioaddr + HPP_OPTION);
 319
 320        return 0;
 321}
 322
 323static void
 324hpp_reset_8390(struct net_device *dev)
 325{
 326        int ioaddr = dev->base_addr - NIC_OFFSET;
 327        int option_reg = inw(ioaddr + HPP_OPTION);
 328
 329        if (ei_debug > 1) printk("resetting the 8390 time=%ld...", jiffies);
 330
 331        outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION);
 332        /* Pause a few cycles for the hardware reset to take place. */
 333        udelay(5);
 334        ei_status.txing = 0;
 335        outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION);
 336
 337        udelay(5);
 338
 339
 340        if ((inb_p(ioaddr+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
 341                printk("%s: hp_reset_8390() did not complete.\n", dev->name);
 342
 343        if (ei_debug > 1) printk("8390 reset done (%ld).", jiffies);
 344        return;
 345}
 346
 347/* The programmed-I/O version of reading the 4 byte 8390 specific header.
 348   Note that transfer with the EtherTwist+ must be on word boundaries. */
 349
 350static void
 351hpp_io_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
 352{
 353        int ioaddr = dev->base_addr - NIC_OFFSET;
 354
 355        outw((ring_page<<8), ioaddr + HPP_IN_ADDR);
 356        insw(ioaddr + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
 357}
 358
 359/* Block input and output, similar to the Crynwr packet driver. */
 360
 361static void
 362hpp_io_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
 363{
 364        int ioaddr = dev->base_addr - NIC_OFFSET;
 365        char *buf = skb->data;
 366
 367        outw(ring_offset, ioaddr + HPP_IN_ADDR);
 368        insw(ioaddr + HP_DATAPORT, buf, count>>1);
 369        if (count & 0x01)
 370        buf[count-1] = inw(ioaddr + HP_DATAPORT);
 371}
 372
 373/* The corresponding shared memory versions of the above 2 functions. */
 374
 375static void
 376hpp_mem_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
 377{
 378        int ioaddr = dev->base_addr - NIC_OFFSET;
 379        int option_reg = inw(ioaddr + HPP_OPTION);
 380
 381        outw((ring_page<<8), ioaddr + HPP_IN_ADDR);
 382        outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
 383        memcpy_fromio(hdr, ei_status.mem, sizeof(struct e8390_pkt_hdr));
 384        outw(option_reg, ioaddr + HPP_OPTION);
 385        hdr->count = (le16_to_cpu(hdr->count) + 3) & ~3;        /* Round up allocation. */
 386}
 387
 388static void
 389hpp_mem_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
 390{
 391        int ioaddr = dev->base_addr - NIC_OFFSET;
 392        int option_reg = inw(ioaddr + HPP_OPTION);
 393
 394        outw(ring_offset, ioaddr + HPP_IN_ADDR);
 395
 396        outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
 397
 398        /* Caution: this relies on get_8390_hdr() rounding up count!
 399           Also note that we *can't* use eth_io_copy_and_sum() because
 400           it will not always copy "count" bytes (e.g. padded IP).  */
 401
 402        memcpy_fromio(skb->data, ei_status.mem, count);
 403        outw(option_reg, ioaddr + HPP_OPTION);
 404}
 405
 406/* A special note: we *must* always transfer >=16 bit words.
 407   It's always safe to round up, so we do. */
 408static void
 409hpp_io_block_output(struct net_device *dev, int count,
 410                                        const unsigned char *buf, int start_page)
 411{
 412        int ioaddr = dev->base_addr - NIC_OFFSET;
 413        outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
 414        outsl(ioaddr + HP_DATAPORT, buf, (count+3)>>2);
 415        return;
 416}
 417
 418static void
 419hpp_mem_block_output(struct net_device *dev, int count,
 420                                const unsigned char *buf, int start_page)
 421{
 422        int ioaddr = dev->base_addr - NIC_OFFSET;
 423        int option_reg = inw(ioaddr + HPP_OPTION);
 424
 425        outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
 426        outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
 427        memcpy_toio(ei_status.mem, buf, (count + 3) & ~3);
 428        outw(option_reg, ioaddr + HPP_OPTION);
 429
 430        return;
 431}
 432
 433
 434#ifdef MODULE
 435#define MAX_HPP_CARDS   4       /* Max number of HPP cards per module */
 436static struct net_device *dev_hpp[MAX_HPP_CARDS];
 437static int io[MAX_HPP_CARDS];
 438static int irq[MAX_HPP_CARDS];
 439
 440module_param_array(io, int, NULL, 0);
 441module_param_array(irq, int, NULL, 0);
 442MODULE_PARM_DESC(io, "I/O port address(es)");
 443MODULE_PARM_DESC(irq, "IRQ number(s); ignored if properly detected");
 444MODULE_DESCRIPTION("HP PC-LAN+ ISA ethernet driver");
 445MODULE_LICENSE("GPL");
 446
 447/* This is set up so that only a single autoprobe takes place per call.
 448ISA device autoprobes on a running machine are not recommended. */
 449int __init
 450init_module(void)
 451{
 452        struct net_device *dev;
 453        int this_dev, found = 0;
 454
 455        for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) {
 456                if (io[this_dev] == 0)  {
 457                        if (this_dev != 0) break; /* only autoprobe 1st one */
 458                        printk(KERN_NOTICE "hp-plus.c: Presently autoprobing (not recommended) for a single card.\n");
 459                }
 460                dev = alloc_ei_netdev();
 461                if (!dev)
 462                        break;
 463                dev->irq = irq[this_dev];
 464                dev->base_addr = io[this_dev];
 465                if (do_hpp_probe(dev) == 0) {
 466                        dev_hpp[found++] = dev;
 467                        continue;
 468                }
 469                free_netdev(dev);
 470                printk(KERN_WARNING "hp-plus.c: No HP-Plus card found (i/o = 0x%x).\n", io[this_dev]);
 471                break;
 472        }
 473        if (found)
 474                return 0;
 475        return -ENXIO;
 476}
 477
 478static void cleanup_card(struct net_device *dev)
 479{
 480        /* NB: hpp_close() handles free_irq */
 481        iounmap(ei_status.mem);
 482        release_region(dev->base_addr - NIC_OFFSET, HP_IO_EXTENT);
 483}
 484
 485void __exit
 486cleanup_module(void)
 487{
 488        int this_dev;
 489
 490        for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) {
 491                struct net_device *dev = dev_hpp[this_dev];
 492                if (dev) {
 493                        unregister_netdev(dev);
 494                        cleanup_card(dev);
 495                        free_netdev(dev);
 496                }
 497        }
 498}
 499#endif /* MODULE */
 500
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.