diff options
author | dg <dg@FreeBSD.org> | 1996-08-07 11:18:23 +0000 |
---|---|---|
committer | dg <dg@FreeBSD.org> | 1996-08-07 11:18:23 +0000 |
commit | 93c92bd9f8c79ded0e9233b67afcb798971b6dd6 (patch) | |
tree | 755b9c05a17e56d707b396761d23b1871d9f1c4a | |
parent | 8bd2231427bdab6c232fe2d2702663e0c46db1b8 (diff) | |
download | FreeBSD-src-93c92bd9f8c79ded0e9233b67afcb798971b6dd6.zip FreeBSD-src-93c92bd9f8c79ded0e9233b67afcb798971b6dd6.tar.gz |
This diff adds support for the HP PC Lan+ cards (model numbers: 27247B
and 27252A) in FreeBSD's `ed' driver.
Submitted by: A JOSEPH KOSHY <koshy@india.hp.com>
-rw-r--r-- | sys/dev/ed/if_ed.c | 724 | ||||
-rw-r--r-- | sys/dev/ed/if_edreg.h | 86 | ||||
-rw-r--r-- | sys/i386/isa/if_ed.c | 724 | ||||
-rw-r--r-- | sys/i386/isa/if_edreg.h | 86 |
4 files changed, 1556 insertions, 64 deletions
diff --git a/sys/dev/ed/if_ed.c b/sys/dev/ed/if_ed.c index 9418e76..eb78d0b 100644 --- a/sys/dev/ed/if_ed.c +++ b/sys/dev/ed/if_ed.c @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: if_ed.c,v 1.102 1996/08/04 10:57:29 phk Exp $ + * $Id: if_ed.c,v 1.103 1996/08/06 21:14:02 phk Exp $ */ /* @@ -108,6 +108,14 @@ struct ed_softc { int is790; /* set by the probe code if the card is 790 * based */ +/* + * HP PC LAN PLUS card support. + */ + + u_short hpp_options; /* flags controlling behaviour of the HP card */ + u_short hpp_id; /* software revision and other fields */ + caddr_t hpp_mem_start; /* Memory-mapped IO register address */ + caddr_t mem_start; /* NIC memory start address */ caddr_t mem_end; /* NIC memory end address */ u_long mem_size; /* total NIC memory size */ @@ -146,6 +154,7 @@ static int ed_probe_WD80x3 __P((struct isa_device *)); static int ed_probe_3Com __P((struct isa_device *)); static int ed_probe_Novell __P((struct isa_device *)); static int ed_probe_Novell_generic __P((struct ed_softc *, int, int, int)); +static int ed_probe_HP_pclanp __P((struct isa_device *)); #include "pci.h" #if NPCI > 0 @@ -165,6 +174,11 @@ static void ed_rint __P((struct ed_softc *)); static void ed_xmit __P((struct ed_softc *)); static char * ed_ring_copy __P((struct ed_softc *, char *, char *, /* u_short */ int)); +static void ed_hpp_set_physical_link __P((struct ed_softc *)); +static void ed_hpp_readmem __P((struct ed_softc *, int, unsigned char *, + /* u_short */ int)); +static u_short ed_hpp_write_mbufs __P((struct ed_softc *, struct mbuf *, + int)); static void ed_pio_readmem __P((struct ed_softc *, int, unsigned char *, /* u_short */ int)); @@ -172,8 +186,10 @@ static void ed_pio_writemem __P((struct ed_softc *, char *, /* u_short */ int, /* u_short */ int)); static u_short ed_pio_write_mbufs __P((struct ed_softc *, struct mbuf *, int)); +void edintr_sc __P((struct ed_softc *)); static void ed_setrcr(struct ed_softc *); + static u_long ds_crc(u_char *ep); #if NCRD > 0 @@ -190,8 +206,6 @@ static void edunload(struct pccard_dev *); /* Disable driver */ static void edsuspend(struct pccard_dev *); /* Suspend driver */ static int edinit(struct pccard_dev *, int); /* init device */ -void edintr_sc __P((struct ed_softc *)); - static struct pccard_drv ed_info = { "ed", card_intr, @@ -325,6 +339,29 @@ static unsigned short ed_790_intr_mask[] = { IRQ15 }; +/* + * Interrupt conversion table for the HP PC LAN+ + */ + +static unsigned short ed_hpp_intr_mask[] = { + 0, /* 0 */ + 0, /* 1 */ + 0, /* 2 */ + IRQ3, /* 3 */ + IRQ4, /* 4 */ + IRQ5, /* 5 */ + IRQ6, /* 6 */ + IRQ7, /* 7 */ + 0, /* 8 */ + IRQ9, /* 9 */ + IRQ10, /* 10 */ + IRQ11, /* 11 */ + IRQ12, /* 12 */ + 0, /* 13 */ + 0, /* 14 */ + IRQ15 /* 15 */ +}; + static struct kern_devconf kdc_ed_template = { 0, 0, 0, /* filled in by dev_attach */ "ed", 0, { MDDT_ISA, 0, "net" }, @@ -386,6 +423,10 @@ ed_probe(isa_dev) if (nports) return (nports); + nports = ed_probe_HP_pclanp(isa_dev); + if (nports) + return (nports); + return (0); } @@ -1432,6 +1473,325 @@ ed_probe_pccard(isa_dev, ether) #endif /* NCRD > 0 */ +#define ED_HPP_TEST_SIZE 16 + +/* + * Probe and vendor specific initialization for the HP PC Lan+ Cards. + * (HP Part nos: 27247B and 27252A). + * + * The card has an asic wrapper around a DS8390 core. The asic handles + * host accesses and offers both standard register IO and memory mapped + * IO. Memory mapped I/O allows better performance at the expense of greater + * chance of an incompatibility with existing ISA cards. + * + * The card has a few caveats: it isn't tolerant of byte wide accesses, only + * short (16 bit) or word (32 bit) accesses are allowed. Some card revisions + * don't allow 32 bit accesses; these are indicated by a bit in the software + * ID register (see if_edreg.h). + * + * Other caveats are: we should read the MAC address only when the card + * is inactive. + * + * For more information; please consult the CRYNWR packet driver. + * + * The AUI port is turned on using the "link2" option on the ifconfig + * command line. + */ +static int +ed_probe_HP_pclanp(isa_dev) + struct isa_device *isa_dev; +{ + struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; + int n; /* temp var */ + int memsize; /* mem on board */ + u_char checksum; /* checksum of board address */ + u_char irq; /* board configured IRQ */ + char test_pattern[ED_HPP_TEST_SIZE]; /* read/write areas for */ + char test_buffer[ED_HPP_TEST_SIZE]; /* probing card */ + + + /* Fill in basic information */ + sc->asic_addr = isa_dev->id_iobase + ED_HPP_ASIC_OFFSET; + sc->nic_addr = isa_dev->id_iobase + ED_HPP_NIC_OFFSET; + sc->is790 = 0; + sc->isa16bit = 0; /* the 8390 core needs to be in byte mode */ + + /* + * Look for the HP PCLAN+ signature: "0x50,0x48,0x00,0x53" + */ + + if ((inb(sc->asic_addr + ED_HPP_ID) != 0x50) || + (inb(sc->asic_addr + ED_HPP_ID + 1) != 0x48) || + ((inb(sc->asic_addr + ED_HPP_ID + 2) & 0xF0) != 0) || + (inb(sc->asic_addr + ED_HPP_ID + 3) != 0x53)) + return 0; + + /* + * Read the MAC address and verify checksum on the address. + */ + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_MAC); + for (n = 0, checksum = 0; n < ETHER_ADDR_LEN; n++) + checksum += (sc->arpcom.ac_enaddr[n] = + inb(sc->asic_addr + ED_HPP_MAC_ADDR + n)); + + checksum += inb(sc->asic_addr + ED_HPP_MAC_ADDR + ETHER_ADDR_LEN); + + if (checksum != 0xFF) + return 0; + + /* + * Verify that the software model number is 0. + */ + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_ID); + if (((sc->hpp_id = inw(sc->asic_addr + ED_HPP_PAGE_4)) & + ED_HPP_ID_SOFT_MODEL_MASK) != 0x0000) + return 0; + + /* + * Read in and save the current options configured on card. + */ + + sc->hpp_options = inw(sc->asic_addr + ED_HPP_OPTION); + + sc->hpp_options |= (ED_HPP_OPTION_NIC_RESET | + ED_HPP_OPTION_CHIP_RESET | + ED_HPP_OPTION_ENABLE_IRQ); + + /* + * Reset the chip. This requires writing to the option register + * so take care to preserve the other bits. + */ + + outw(sc->asic_addr + ED_HPP_OPTION, + (sc->hpp_options & ~(ED_HPP_OPTION_NIC_RESET | + ED_HPP_OPTION_CHIP_RESET))); + + DELAY(5000); /* wait for chip reset to complete */ + + outw(sc->asic_addr + ED_HPP_OPTION, + (sc->hpp_options | (ED_HPP_OPTION_NIC_RESET | + ED_HPP_OPTION_CHIP_RESET | + ED_HPP_OPTION_ENABLE_IRQ))); + + DELAY(5000); + + if (!(inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST)) + return 0; /* reset did not complete */ + + /* + * Read out configuration information. + */ + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_HW); + + irq = inb(sc->asic_addr + ED_HPP_HW_IRQ); + + /* + * Check for impossible IRQ. + */ + + if (irq >= (sizeof(ed_hpp_intr_mask) / sizeof(ed_hpp_intr_mask[0]))) + return 0; + + /* + * If the kernel IRQ was specified with a '?' use the cards idea + * of the IRQ. If the kernel IRQ was explicitly specified, it + * should match that of the hardware. + */ + + if (isa_dev->id_irq <= 0) + isa_dev->id_irq = ed_hpp_intr_mask[irq]; + else if (isa_dev->id_irq != ed_hpp_intr_mask[irq]) + return 0; + + /* + * Fill in softconfig info. + */ + + sc->vendor = ED_VENDOR_HP; + sc->type = ED_TYPE_HP_PCLANPLUS; + sc->type_str = "HP-PCLAN+"; + sc->kdc.kdc_description = "Ethernet adapter: HP PCLAN+ (27247B/27252A)"; + + sc->mem_shared = 0; /* we DON'T have dual ported RAM */ + sc->mem_start = 0; /* we use offsets inside the card RAM */ + + sc->hpp_mem_start = NULL;/* no memory mapped I/O by default */ + + /* + * Check if memory mapping of the I/O registers possible. + */ + + if (sc->hpp_options & ED_HPP_OPTION_MEM_ENABLE) + { + u_long mem_addr; + + /* + * determine the memory address from the board. + */ + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_HW); + mem_addr = (inw(sc->asic_addr + ED_HPP_HW_MEM_MAP) << 8); + + /* + * Check that the kernel specified start of memory and + * hardware's idea of it match. + */ + + if (mem_addr != kvtop(isa_dev->id_maddr)) + return 0; + + sc->hpp_mem_start = isa_dev->id_maddr; + } + + /* + * The board has 32KB of memory. Is there a way to determine + * this programmatically? + */ + + memsize = 32768; + + /* + * Fill in the rest of the soft config structure. + */ + + /* + * The transmit page index. + */ + + sc->tx_page_start = ED_HPP_TX_PAGE_OFFSET; + + if (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING) + sc->txb_cnt = 1; + else + sc->txb_cnt = 2; + + /* + * Memory description + */ + + sc->mem_size = memsize; + sc->mem_ring = sc->mem_start + + (sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE); + sc->mem_end = sc->mem_start + sc->mem_size; + + /* + * Receive area starts after the transmit area and + * continues till the end of memory. + */ + + sc->rec_page_start = sc->tx_page_start + + (sc->txb_cnt * ED_TXBUF_SIZE); + sc->rec_page_stop = (sc->mem_size / ED_PAGE_SIZE); + + + sc->cr_proto = 0; /* value works */ + + /* + * Set the wrap registers for string I/O reads. + */ + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_HW); + outw(sc->asic_addr + ED_HPP_HW_WRAP, + ((sc->rec_page_start / ED_PAGE_SIZE) | + (((sc->rec_page_stop / ED_PAGE_SIZE) - 1) << 8))); + + /* + * Reset the register page to normal operation. + */ + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_PERF); + + /* + * Verify that we can read/write from adapter memory. + * Create test pattern. + */ + + for (n = 0; n < ED_HPP_TEST_SIZE; n++) + { + test_pattern[n] = (n*n) ^ ~n; + } + +#undef ED_HPP_TEST_SIZE + + /* + * Check that the memory is accessible thru the I/O ports. + * Write out the contents of "test_pattern", read back + * into "test_buffer" and compare the two for any + * mismatch. + */ + + for (n = 0; n < (32768 / ED_PAGE_SIZE); n ++) { + + ed_pio_writemem(sc, test_pattern, (n * ED_PAGE_SIZE), + sizeof(test_pattern)); + ed_pio_readmem(sc, (n * ED_PAGE_SIZE), + test_buffer, sizeof(test_pattern)); + + if (bcmp(test_pattern, test_buffer, + sizeof(test_pattern))) + return 0; + } + + return (ED_HPP_IO_PORTS); + +} + +/* + * HP PC Lan+ : Set the physical link to use AUI or TP/TL. + */ + +void +ed_hpp_set_physical_link(struct ed_softc *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + int lan_page; + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_LAN); + lan_page = inw(sc->asic_addr + ED_HPP_PAGE_0); + + if (ifp->if_flags & IFF_ALTPHYS) { + + /* + * Use the AUI port. + */ + + lan_page |= ED_HPP_LAN_AUI; + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_LAN); + outw(sc->asic_addr + ED_HPP_PAGE_0, lan_page); + + + } else { + + /* + * Use the ThinLan interface + */ + + lan_page &= ~ED_HPP_LAN_AUI; + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_LAN); + outw(sc->asic_addr + ED_HPP_PAGE_0, lan_page); + + } + + /* + * Wait for the lan card to re-initialize itself + */ + + DELAY(150000); /* wait 150 ms */ + + /* + * Restore normal pages. + */ + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_PERF); + +} + + /* * Install interface into kernel networking data structures */ @@ -1494,10 +1854,16 @@ ed_attach(sc, unit, flags) else printf("type unknown (0x%x) ", sc->type); - printf("%s ", sc->isa16bit ? "(16 bit)" : "(8 bit)"); + if (sc->vendor == ED_VENDOR_HP) + printf("(%s %s IO)", (sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS) ? + "16-bit" : "32-bit", + sc->hpp_mem_start ? "memory mapped" : "regular"); + else + printf("%s ", sc->isa16bit ? "(16 bit)" : "(8 bit)"); - printf("%s\n", ((sc->vendor == ED_VENDOR_3COM) && - (ifp->if_flags & IFF_ALTPHYS)) ? " tranceiver disabled" : ""); + printf("%s\n", (((sc->vendor == ED_VENDOR_3COM) || + (sc->vendor == ED_VENDOR_HP)) && + (ifp->if_flags & IFF_ALTPHYS)) ? " tranceiver disabled" : ""); /* * If BPF is in the kernel, call the attach for it @@ -2378,7 +2744,8 @@ ed_ioctl(ifp, command, data) } else { outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); } - } + } else if (sc->vendor == ED_VENDOR_HP) + ed_hpp_set_physical_link(sc); break; case SIOCADDMULTI: @@ -2556,6 +2923,13 @@ ed_pio_readmem(sc, src, dst, amount) unsigned char *dst; unsigned short amount; { + /* HP cards need special handling */ + if (sc->vendor == ED_VENDOR_HP && sc->type == ED_TYPE_HP_PCLANPLUS) { + ed_hpp_readmem(sc, src, dst, amount); + return; + } + + /* Regular Novell cards */ /* select page 0 registers */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); @@ -2594,36 +2968,84 @@ ed_pio_writemem(sc, src, dst, len) { int maxwait = 200; /* about 240us */ - /* select page 0 registers */ - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); + if (sc->vendor == ED_VENDOR_NOVELL) { - /* reset remote DMA complete flag */ - outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); + /* select page 0 registers */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); - /* set up DMA byte count */ - outb(sc->nic_addr + ED_P0_RBCR0, len); - outb(sc->nic_addr + ED_P0_RBCR1, len >> 8); + /* reset remote DMA complete flag */ + outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); - /* set up destination address in NIC mem */ - outb(sc->nic_addr + ED_P0_RSAR0, dst); - outb(sc->nic_addr + ED_P0_RSAR1, dst >> 8); + /* set up DMA byte count */ + outb(sc->nic_addr + ED_P0_RBCR0, len); + outb(sc->nic_addr + ED_P0_RBCR1, len >> 8); - /* set remote DMA write */ - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); + /* set up destination address in NIC mem */ + outb(sc->nic_addr + ED_P0_RSAR0, dst); + outb(sc->nic_addr + ED_P0_RSAR1, dst >> 8); - if (sc->isa16bit) - outsw(sc->asic_addr + ED_NOVELL_DATA, src, len / 2); - else - outsb(sc->asic_addr + ED_NOVELL_DATA, src, len); + /* set remote DMA write */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); - /* - * Wait for remote DMA complete. This is necessary because on the - * transmit side, data is handled internally by the NIC in bursts and - * we can't start another remote DMA until this one completes. Not - * waiting causes really bad things to happen - like the NIC - * irrecoverably jamming the ISA bus. - */ - while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); + if (sc->isa16bit) + outsw(sc->asic_addr + ED_NOVELL_DATA, src, len / 2); + else + outsb(sc->asic_addr + ED_NOVELL_DATA, src, len); + + /* + * Wait for remote DMA complete. This is necessary because on the + * transmit side, data is handled internally by the NIC in bursts and + * we can't start another remote DMA until this one completes. Not + * waiting causes really bad things to happen - like the NIC + * irrecoverably jamming the ISA bus. + */ + while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); + + } else if ((sc->vendor == ED_VENDOR_HP) && + (sc->type == ED_TYPE_HP_PCLANPLUS)) { + + /* HP PCLAN+ */ + + /* reset remote DMA complete flag */ + outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); + + /* program the write address in RAM */ + outw(sc->asic_addr + ED_HPP_PAGE_0, dst); + + if (sc->hpp_mem_start) { + u_short *s = (u_short *) src; + volatile u_short *d = (u_short *) sc->hpp_mem_start; + u_short *const fence = s + (len >> 1); + + /* + * Enable memory mapped access. + */ + + outw(sc->asic_addr + ED_HPP_OPTION, + sc->hpp_options & + ~(ED_HPP_OPTION_MEM_DISABLE | + ED_HPP_OPTION_BOOT_ROM_ENB)); + + /* + * Copy to NIC memory. + */ + + while (s < fence) + *d = *s++; + + /* + * Restore Boot ROM access. + */ + + outw(sc->asic_addr + ED_HPP_OPTION, + sc->hpp_options); + + } else { + /* write data using I/O writes */ + outsw(sc->asic_addr + ED_HPP_PAGE_4, src, len / 2); + } + + } } /* @@ -2641,6 +3063,12 @@ ed_pio_write_mbufs(sc, m, dst) struct mbuf *mp; int maxwait = 200; /* about 240us */ + /* HP PC Lan+ cards need special handling */ + if ((sc->vendor == ED_VENDOR_HP) && + (sc->type == ED_TYPE_HP_PCLANPLUS)) { + return ed_hpp_write_mbufs(sc, m, dst); + } + /* First, count up the total number of bytes to copy */ for (total_len = 0, mp = m; mp; mp = mp->m_next) total_len += mp->m_len; @@ -2740,6 +3168,240 @@ ed_pio_write_mbufs(sc, m, dst) return (total_len); } +/* + * Support routines to handle the HP PC Lan+ card. + */ + +/* + * HP PC Lan+: Read from NIC memory, using either PIO or memory mapped + * IO. + */ + +static void +ed_hpp_readmem(sc, src, dst, amount) + struct ed_softc *sc; + unsigned short src; + unsigned char *dst; + unsigned short amount; +{ + + int use_32bit_access = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS); + + + /* Program the source address in RAM */ + outw(sc->asic_addr + ED_HPP_PAGE_2, src); + + /* + * The HP PC Lan+ card supports word reads as well as + * a memory mapped i/o port that is aliased to every + * even address on the board. + */ + + if (sc->hpp_mem_start) { + + /* Enable memory mapped access. */ + outw(sc->asic_addr + ED_HPP_OPTION, + sc->hpp_options & + ~(ED_HPP_OPTION_MEM_DISABLE | + ED_HPP_OPTION_BOOT_ROM_ENB)); + + if (use_32bit_access && (amount > 3)) { + u_long *dl = (u_long *) dst; + volatile u_long *const sl = + (u_long *) sc->hpp_mem_start; + u_long *const fence = dl + (amount >> 2); + + /* Copy out NIC data. We could probably write this + as a `movsl'. The currently generated code is lousy. + */ + + while (dl < fence) + *dl++ = *sl; + + dst += (amount & ~3); + amount &= 3; + + } + + /* Finish off any words left, as a series of short reads */ + if (amount > 1) { + u_short *d = (u_short *) dst; + volatile u_short *const s = + (u_short *) sc->hpp_mem_start; + u_short *const fence = d + (amount >> 1); + + /* Copy out NIC data. */ + + while (d < fence) + *d++ = *s; + + dst += (amount & ~1); + amount &= 1; + } + + /* + * read in a byte; however we need to always read 16 bits + * at a time or the hardware gets into a funny state + */ + + if (amount == 1) { + /* need to read in a short and copy LSB */ + volatile u_short *const s = + (volatile u_short *) sc->hpp_mem_start; + + *dst = (*s) & 0xFF; + } + + /* Restore Boot ROM access. */ + + outw(sc->asic_addr + ED_HPP_OPTION, + sc->hpp_options); + + + } else { + /* Read in data using the I/O port */ + if (use_32bit_access && (amount > 3)) { + insl(sc->asic_addr + ED_HPP_PAGE_4, dst, amount >> 2); + dst += (amount & ~3); + amount &= 3; + } + if (amount > 1) { + insw(sc->asic_addr + ED_HPP_PAGE_4, dst, amount >> 1); + dst += (amount & ~1); + amount &= 1; + } + if (amount == 1) { /* read in a short and keep the LSB */ + *dst = inw(sc->asic_addr + ED_HPP_PAGE_4) & 0xFF; + } + } +} + +/* + * Write to HP PC Lan+ NIC memory. Access to the NIC can be by using + * outsw() or via the memory mapped interface to the same register. + * Writes have to be in word units; byte accesses won't work and may cause + * the NIC to behave wierdly. Long word accesses are permitted if the ASIC + * allows it. + */ + +static u_short +ed_hpp_write_mbufs(struct ed_softc *sc, struct mbuf *m, int dst) +{ + int len, wantbyte; + unsigned short total_len; + unsigned char savebyte[2]; + volatile u_short * const d = + (volatile u_short *) sc->hpp_mem_start; + int use_32bit_accesses = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS); + + /* select page 0 registers */ + outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA); + + /* reset remote DMA complete flag */ + outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); + + /* program the write address in RAM */ + outw(sc->asic_addr + ED_HPP_PAGE_0, dst); + + if (sc->hpp_mem_start) /* enable memory mapped I/O */ + outw(sc->asic_addr + ED_HPP_OPTION, sc->hpp_options & + ~(ED_HPP_OPTION_MEM_DISABLE | + ED_HPP_OPTION_BOOT_ROM_ENB)); + + wantbyte = 0; + total_len = 0; + + if (sc->hpp_mem_start) { /* Memory mapped I/O port */ + while (m) { + total_len += (len = m->m_len); + if (len) { + caddr_t data = mtod(m, caddr_t); + /* finish the last word of the previous mbuf */ + if (wantbyte) { + savebyte[1] = *data; + *d = *((ushort *) savebyte); + data++; len--; wantbyte = 0; + } + /* output contiguous words */ + if ((len > 3) && (use_32bit_accesses)) { + volatile u_long *const dl = + (volatile u_long *) d; + u_long *sl = (u_long *) data; + u_long *fence = sl + (len >> 2); + + while (sl < fence) + *dl = *sl++; + + data += (len & ~3); + len &= 3; + } + /* finish off remain 16 bit writes */ + if (len > 1) { + u_short *s = (u_short *) data; + u_short *fence = s + (len >> 1); + + while (s < fence) + *d = *s++; + + data += (len & ~1); + len &= 1; + } + /* save last byte if needed */ + if (wantbyte = (len == 1)) + savebyte[0] = *data; + } + m = m->m_next; /* to next mbuf */ + } + if (wantbyte) /* write last byte */ + *d = *((u_short *) savebyte); + } else { + /* use programmed I/O */ + while (m) { + total_len += (len = m->m_len); + if (len) { + caddr_t data = mtod(m, caddr_t); + /* finish the last word of the previous mbuf */ + if (wantbyte) { + savebyte[1] = *data; + outw(sc->asic_addr + ED_HPP_PAGE_4, + *((u_short *)savebyte)); + data++; + len--; + wantbyte = 0; + } + /* output contiguous words */ + if ((len > 3) && use_32bit_accesses) { + outsl(sc->asic_addr + ED_HPP_PAGE_4, + data, len >> 2); + data += (len & ~3); + len &= 3; + } + /* finish off remaining 16 bit accesses */ + if (len > 1) { + outsw(sc->asic_addr + ED_HPP_PAGE_4, + data, len >> 1); + data += (len & ~1); + len &= 1; + } + if (wantbyte = (len == 1)) + savebyte[0] = *data; + + } /* if len != 0 */ + m = m->m_next; + } + if (wantbyte) /* spit last byte */ + outw(sc->asic_addr + ED_HPP_PAGE_4, + *(u_short *)savebyte); + + } + + if (sc->hpp_mem_start) /* turn off memory mapped i/o */ + outw(sc->asic_addr + ED_HPP_OPTION, + sc->hpp_options); + + return (total_len); +} + static void ed_setrcr(sc) struct ed_softc *sc; diff --git a/sys/dev/ed/if_edreg.h b/sys/dev/ed/if_edreg.h index a03ce65..db7db18 100644 --- a/sys/dev/ed/if_edreg.h +++ b/sys/dev/ed/if_edreg.h @@ -6,7 +6,7 @@ * of this software, nor does the author assume any responsibility * for damages incurred with its use. * - * $Id: if_edreg.h,v 1.19 1995/09/26 08:57:45 phk Exp $ + * $Id: if_edreg.h,v 1.20 1996/01/30 22:55:38 mpp Exp $ */ /* * National Semiconductor DS8390 NIC register definitions @@ -569,6 +569,7 @@ struct ed_ring { #define ED_VENDOR_3COM 0x01 /* 3Com */ #define ED_VENDOR_NOVELL 0x02 /* Novell */ #define ED_VENDOR_PCCARD 0x03 /* PCMCIA/PCCARD */ +#define ED_VENDOR_HP 0x04 /* Hewlett Packard */ /* * Compile-time config flags @@ -1001,3 +1002,86 @@ struct ed_ring { #define ZE_MISC 0x18 #define ZE_RESET 0x1F +/* + * Definitions for HP PC LAN Adapter Plus; based on the CRYNWR packet + * driver for the card. + */ + +#define ED_HPP_ASIC_OFFSET 0x00 /* Offset to ASIC registers */ +#define ED_HPP_NIC_OFFSET 0x10 /* Offset to 8390 registers */ + +#define ED_HPP_ID 0x00 /* ID register, always 0x4850 */ +#define ED_HPP_PAGING 0x02 /* Page select register */ +#define ED_HPP_OPTION 0x04 /* Bitmask of supported options */ +#define ED_HPP_PAGE_0 0x08 /* Page 0 */ +#define ED_HPP_PAGE_2 0x0A /* Page 2 */ +#define ED_HPP_PAGE_4 0x0C /* Page 4 */ +#define ED_HPP_PAGE_6 0x0E /* Page 6 */ + +/* PERF PAGE */ +#define ED_HPP_OUT_ADDR ED_HPP_PAGE_0 /* I/O output location */ +#define ED_HPP_IN_ADDR ED_HPP_PAGE_2 /* I/O input location */ +#define ED_HPP_DATAPORT ED_HPP_PAGE_4 /* I/O data transfer */ +/* MAC PAGE */ +#define ED_HPP_MAC_ADDR 0x08 /* Offset of MAC address in MAC page */ + +#define ED_HPP_IO_PORTS 32 /* Number of IO ports */ + +#define ED_HPP_TX_PAGE_OFFSET 0x00 /* first page of TX buffer */ +#define ED_HPP_RX_PAGE_START 0x06 /* start at page 6 */ +#define ED_HPP_RX_PAGE_STOP 0x80 /* end at page 128 */ + +/* + * Register pages supported. + */ + +#define ED_HPP_PAGE_PERF 0 /* Normal operation */ +#define ED_HPP_PAGE_MAC 1 /* The ethernet address and checksum */ +#define ED_HPP_PAGE_HW 2 /* Hardware parameters in EEPROM */ +#define ED_HPP_PAGE_LAN 4 /* Transciever selection etc */ +#define ED_HPP_PAGE_ID 6 /* ID */ + +/* + * Options supported. + */ + +#define ED_HPP_OPTION_NIC_RESET 0x0001 /* active low */ +#define ED_HPP_OPTION_CHIP_RESET 0x0002 /* active low */ +#define ED_HPP_OPTION_ENABLE_IRQ 0x0004 +#define ED_HPP_OPTION_FAKE_INTR 0x0008 +#define ED_HPP_OPTION_BOOT_ROM_ENB 0x0010 +#define ED_HPP_OPTION_IO_ENB 0x0020 +#define ED_HPP_OPTION_MEM_ENABLE 0x0040 +#define ED_HPP_OPTION_ZERO_WAIT 0x0080 +#define ED_HPP_OPTION_MEM_DISABLE 0x1000 + +/* + * Page ID configuration. + */ + +#define ED_HPP_ID_REVISION_MASK 0x0300 /* revision id */ +#define ED_HPP_ID_SOFT_MODEL_MASK 0xFC00 /* soft model number */ +#define ED_HPP_ID_16_BIT_ACCESS 0x0010 /* if set use 16 bit accesses */ +#define ED_HPP_ID_TWISTED_PAIR 0x0040 + +/* + * Hardware configuration. + */ + +#define ED_HPP_HW_MEM_MAP 0x09 /* low mem map location in HW page */ +#define ED_HPP_HW_ID 0x0C /* revision number, capabilities */ +#define ED_HPP_HW_IRQ 0x0D /* IRQ channel register in HW page */ +#define ED_HPP_HW_WRAP 0x0E /* mem wrap page for rcv */ + +/* + * Lan configuration + */ + +#define ED_HPP_LAN_AUI 0x01 /* Use AUI */ +#define ED_HPP_LAN_TL 0x40 /* Don't use AUI */ + +/* + * Card types. + */ + +#define ED_TYPE_HP_PCLANPLUS 0x00 diff --git a/sys/i386/isa/if_ed.c b/sys/i386/isa/if_ed.c index 9418e76..eb78d0b 100644 --- a/sys/i386/isa/if_ed.c +++ b/sys/i386/isa/if_ed.c @@ -24,7 +24,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: if_ed.c,v 1.102 1996/08/04 10:57:29 phk Exp $ + * $Id: if_ed.c,v 1.103 1996/08/06 21:14:02 phk Exp $ */ /* @@ -108,6 +108,14 @@ struct ed_softc { int is790; /* set by the probe code if the card is 790 * based */ +/* + * HP PC LAN PLUS card support. + */ + + u_short hpp_options; /* flags controlling behaviour of the HP card */ + u_short hpp_id; /* software revision and other fields */ + caddr_t hpp_mem_start; /* Memory-mapped IO register address */ + caddr_t mem_start; /* NIC memory start address */ caddr_t mem_end; /* NIC memory end address */ u_long mem_size; /* total NIC memory size */ @@ -146,6 +154,7 @@ static int ed_probe_WD80x3 __P((struct isa_device *)); static int ed_probe_3Com __P((struct isa_device *)); static int ed_probe_Novell __P((struct isa_device *)); static int ed_probe_Novell_generic __P((struct ed_softc *, int, int, int)); +static int ed_probe_HP_pclanp __P((struct isa_device *)); #include "pci.h" #if NPCI > 0 @@ -165,6 +174,11 @@ static void ed_rint __P((struct ed_softc *)); static void ed_xmit __P((struct ed_softc *)); static char * ed_ring_copy __P((struct ed_softc *, char *, char *, /* u_short */ int)); +static void ed_hpp_set_physical_link __P((struct ed_softc *)); +static void ed_hpp_readmem __P((struct ed_softc *, int, unsigned char *, + /* u_short */ int)); +static u_short ed_hpp_write_mbufs __P((struct ed_softc *, struct mbuf *, + int)); static void ed_pio_readmem __P((struct ed_softc *, int, unsigned char *, /* u_short */ int)); @@ -172,8 +186,10 @@ static void ed_pio_writemem __P((struct ed_softc *, char *, /* u_short */ int, /* u_short */ int)); static u_short ed_pio_write_mbufs __P((struct ed_softc *, struct mbuf *, int)); +void edintr_sc __P((struct ed_softc *)); static void ed_setrcr(struct ed_softc *); + static u_long ds_crc(u_char *ep); #if NCRD > 0 @@ -190,8 +206,6 @@ static void edunload(struct pccard_dev *); /* Disable driver */ static void edsuspend(struct pccard_dev *); /* Suspend driver */ static int edinit(struct pccard_dev *, int); /* init device */ -void edintr_sc __P((struct ed_softc *)); - static struct pccard_drv ed_info = { "ed", card_intr, @@ -325,6 +339,29 @@ static unsigned short ed_790_intr_mask[] = { IRQ15 }; +/* + * Interrupt conversion table for the HP PC LAN+ + */ + +static unsigned short ed_hpp_intr_mask[] = { + 0, /* 0 */ + 0, /* 1 */ + 0, /* 2 */ + IRQ3, /* 3 */ + IRQ4, /* 4 */ + IRQ5, /* 5 */ + IRQ6, /* 6 */ + IRQ7, /* 7 */ + 0, /* 8 */ + IRQ9, /* 9 */ + IRQ10, /* 10 */ + IRQ11, /* 11 */ + IRQ12, /* 12 */ + 0, /* 13 */ + 0, /* 14 */ + IRQ15 /* 15 */ +}; + static struct kern_devconf kdc_ed_template = { 0, 0, 0, /* filled in by dev_attach */ "ed", 0, { MDDT_ISA, 0, "net" }, @@ -386,6 +423,10 @@ ed_probe(isa_dev) if (nports) return (nports); + nports = ed_probe_HP_pclanp(isa_dev); + if (nports) + return (nports); + return (0); } @@ -1432,6 +1473,325 @@ ed_probe_pccard(isa_dev, ether) #endif /* NCRD > 0 */ +#define ED_HPP_TEST_SIZE 16 + +/* + * Probe and vendor specific initialization for the HP PC Lan+ Cards. + * (HP Part nos: 27247B and 27252A). + * + * The card has an asic wrapper around a DS8390 core. The asic handles + * host accesses and offers both standard register IO and memory mapped + * IO. Memory mapped I/O allows better performance at the expense of greater + * chance of an incompatibility with existing ISA cards. + * + * The card has a few caveats: it isn't tolerant of byte wide accesses, only + * short (16 bit) or word (32 bit) accesses are allowed. Some card revisions + * don't allow 32 bit accesses; these are indicated by a bit in the software + * ID register (see if_edreg.h). + * + * Other caveats are: we should read the MAC address only when the card + * is inactive. + * + * For more information; please consult the CRYNWR packet driver. + * + * The AUI port is turned on using the "link2" option on the ifconfig + * command line. + */ +static int +ed_probe_HP_pclanp(isa_dev) + struct isa_device *isa_dev; +{ + struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; + int n; /* temp var */ + int memsize; /* mem on board */ + u_char checksum; /* checksum of board address */ + u_char irq; /* board configured IRQ */ + char test_pattern[ED_HPP_TEST_SIZE]; /* read/write areas for */ + char test_buffer[ED_HPP_TEST_SIZE]; /* probing card */ + + + /* Fill in basic information */ + sc->asic_addr = isa_dev->id_iobase + ED_HPP_ASIC_OFFSET; + sc->nic_addr = isa_dev->id_iobase + ED_HPP_NIC_OFFSET; + sc->is790 = 0; + sc->isa16bit = 0; /* the 8390 core needs to be in byte mode */ + + /* + * Look for the HP PCLAN+ signature: "0x50,0x48,0x00,0x53" + */ + + if ((inb(sc->asic_addr + ED_HPP_ID) != 0x50) || + (inb(sc->asic_addr + ED_HPP_ID + 1) != 0x48) || + ((inb(sc->asic_addr + ED_HPP_ID + 2) & 0xF0) != 0) || + (inb(sc->asic_addr + ED_HPP_ID + 3) != 0x53)) + return 0; + + /* + * Read the MAC address and verify checksum on the address. + */ + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_MAC); + for (n = 0, checksum = 0; n < ETHER_ADDR_LEN; n++) + checksum += (sc->arpcom.ac_enaddr[n] = + inb(sc->asic_addr + ED_HPP_MAC_ADDR + n)); + + checksum += inb(sc->asic_addr + ED_HPP_MAC_ADDR + ETHER_ADDR_LEN); + + if (checksum != 0xFF) + return 0; + + /* + * Verify that the software model number is 0. + */ + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_ID); + if (((sc->hpp_id = inw(sc->asic_addr + ED_HPP_PAGE_4)) & + ED_HPP_ID_SOFT_MODEL_MASK) != 0x0000) + return 0; + + /* + * Read in and save the current options configured on card. + */ + + sc->hpp_options = inw(sc->asic_addr + ED_HPP_OPTION); + + sc->hpp_options |= (ED_HPP_OPTION_NIC_RESET | + ED_HPP_OPTION_CHIP_RESET | + ED_HPP_OPTION_ENABLE_IRQ); + + /* + * Reset the chip. This requires writing to the option register + * so take care to preserve the other bits. + */ + + outw(sc->asic_addr + ED_HPP_OPTION, + (sc->hpp_options & ~(ED_HPP_OPTION_NIC_RESET | + ED_HPP_OPTION_CHIP_RESET))); + + DELAY(5000); /* wait for chip reset to complete */ + + outw(sc->asic_addr + ED_HPP_OPTION, + (sc->hpp_options | (ED_HPP_OPTION_NIC_RESET | + ED_HPP_OPTION_CHIP_RESET | + ED_HPP_OPTION_ENABLE_IRQ))); + + DELAY(5000); + + if (!(inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST)) + return 0; /* reset did not complete */ + + /* + * Read out configuration information. + */ + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_HW); + + irq = inb(sc->asic_addr + ED_HPP_HW_IRQ); + + /* + * Check for impossible IRQ. + */ + + if (irq >= (sizeof(ed_hpp_intr_mask) / sizeof(ed_hpp_intr_mask[0]))) + return 0; + + /* + * If the kernel IRQ was specified with a '?' use the cards idea + * of the IRQ. If the kernel IRQ was explicitly specified, it + * should match that of the hardware. + */ + + if (isa_dev->id_irq <= 0) + isa_dev->id_irq = ed_hpp_intr_mask[irq]; + else if (isa_dev->id_irq != ed_hpp_intr_mask[irq]) + return 0; + + /* + * Fill in softconfig info. + */ + + sc->vendor = ED_VENDOR_HP; + sc->type = ED_TYPE_HP_PCLANPLUS; + sc->type_str = "HP-PCLAN+"; + sc->kdc.kdc_description = "Ethernet adapter: HP PCLAN+ (27247B/27252A)"; + + sc->mem_shared = 0; /* we DON'T have dual ported RAM */ + sc->mem_start = 0; /* we use offsets inside the card RAM */ + + sc->hpp_mem_start = NULL;/* no memory mapped I/O by default */ + + /* + * Check if memory mapping of the I/O registers possible. + */ + + if (sc->hpp_options & ED_HPP_OPTION_MEM_ENABLE) + { + u_long mem_addr; + + /* + * determine the memory address from the board. + */ + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_HW); + mem_addr = (inw(sc->asic_addr + ED_HPP_HW_MEM_MAP) << 8); + + /* + * Check that the kernel specified start of memory and + * hardware's idea of it match. + */ + + if (mem_addr != kvtop(isa_dev->id_maddr)) + return 0; + + sc->hpp_mem_start = isa_dev->id_maddr; + } + + /* + * The board has 32KB of memory. Is there a way to determine + * this programmatically? + */ + + memsize = 32768; + + /* + * Fill in the rest of the soft config structure. + */ + + /* + * The transmit page index. + */ + + sc->tx_page_start = ED_HPP_TX_PAGE_OFFSET; + + if (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING) + sc->txb_cnt = 1; + else + sc->txb_cnt = 2; + + /* + * Memory description + */ + + sc->mem_size = memsize; + sc->mem_ring = sc->mem_start + + (sc->txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE); + sc->mem_end = sc->mem_start + sc->mem_size; + + /* + * Receive area starts after the transmit area and + * continues till the end of memory. + */ + + sc->rec_page_start = sc->tx_page_start + + (sc->txb_cnt * ED_TXBUF_SIZE); + sc->rec_page_stop = (sc->mem_size / ED_PAGE_SIZE); + + + sc->cr_proto = 0; /* value works */ + + /* + * Set the wrap registers for string I/O reads. + */ + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_HW); + outw(sc->asic_addr + ED_HPP_HW_WRAP, + ((sc->rec_page_start / ED_PAGE_SIZE) | + (((sc->rec_page_stop / ED_PAGE_SIZE) - 1) << 8))); + + /* + * Reset the register page to normal operation. + */ + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_PERF); + + /* + * Verify that we can read/write from adapter memory. + * Create test pattern. + */ + + for (n = 0; n < ED_HPP_TEST_SIZE; n++) + { + test_pattern[n] = (n*n) ^ ~n; + } + +#undef ED_HPP_TEST_SIZE + + /* + * Check that the memory is accessible thru the I/O ports. + * Write out the contents of "test_pattern", read back + * into "test_buffer" and compare the two for any + * mismatch. + */ + + for (n = 0; n < (32768 / ED_PAGE_SIZE); n ++) { + + ed_pio_writemem(sc, test_pattern, (n * ED_PAGE_SIZE), + sizeof(test_pattern)); + ed_pio_readmem(sc, (n * ED_PAGE_SIZE), + test_buffer, sizeof(test_pattern)); + + if (bcmp(test_pattern, test_buffer, + sizeof(test_pattern))) + return 0; + } + + return (ED_HPP_IO_PORTS); + +} + +/* + * HP PC Lan+ : Set the physical link to use AUI or TP/TL. + */ + +void +ed_hpp_set_physical_link(struct ed_softc *sc) +{ + struct ifnet *ifp = &sc->arpcom.ac_if; + int lan_page; + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_LAN); + lan_page = inw(sc->asic_addr + ED_HPP_PAGE_0); + + if (ifp->if_flags & IFF_ALTPHYS) { + + /* + * Use the AUI port. + */ + + lan_page |= ED_HPP_LAN_AUI; + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_LAN); + outw(sc->asic_addr + ED_HPP_PAGE_0, lan_page); + + + } else { + + /* + * Use the ThinLan interface + */ + + lan_page &= ~ED_HPP_LAN_AUI; + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_LAN); + outw(sc->asic_addr + ED_HPP_PAGE_0, lan_page); + + } + + /* + * Wait for the lan card to re-initialize itself + */ + + DELAY(150000); /* wait 150 ms */ + + /* + * Restore normal pages. + */ + + outw(sc->asic_addr + ED_HPP_PAGING, ED_HPP_PAGE_PERF); + +} + + /* * Install interface into kernel networking data structures */ @@ -1494,10 +1854,16 @@ ed_attach(sc, unit, flags) else printf("type unknown (0x%x) ", sc->type); - printf("%s ", sc->isa16bit ? "(16 bit)" : "(8 bit)"); + if (sc->vendor == ED_VENDOR_HP) + printf("(%s %s IO)", (sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS) ? + "16-bit" : "32-bit", + sc->hpp_mem_start ? "memory mapped" : "regular"); + else + printf("%s ", sc->isa16bit ? "(16 bit)" : "(8 bit)"); - printf("%s\n", ((sc->vendor == ED_VENDOR_3COM) && - (ifp->if_flags & IFF_ALTPHYS)) ? " tranceiver disabled" : ""); + printf("%s\n", (((sc->vendor == ED_VENDOR_3COM) || + (sc->vendor == ED_VENDOR_HP)) && + (ifp->if_flags & IFF_ALTPHYS)) ? " tranceiver disabled" : ""); /* * If BPF is in the kernel, call the attach for it @@ -2378,7 +2744,8 @@ ed_ioctl(ifp, command, data) } else { outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); } - } + } else if (sc->vendor == ED_VENDOR_HP) + ed_hpp_set_physical_link(sc); break; case SIOCADDMULTI: @@ -2556,6 +2923,13 @@ ed_pio_readmem(sc, src, dst, amount) unsigned char *dst; unsigned short amount; { + /* HP cards need special handling */ + if (sc->vendor == ED_VENDOR_HP && sc->type == ED_TYPE_HP_PCLANPLUS) { + ed_hpp_readmem(sc, src, dst, amount); + return; + } + + /* Regular Novell cards */ /* select page 0 registers */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); @@ -2594,36 +2968,84 @@ ed_pio_writemem(sc, src, dst, len) { int maxwait = 200; /* about 240us */ - /* select page 0 registers */ - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); + if (sc->vendor == ED_VENDOR_NOVELL) { - /* reset remote DMA complete flag */ - outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); + /* select page 0 registers */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); - /* set up DMA byte count */ - outb(sc->nic_addr + ED_P0_RBCR0, len); - outb(sc->nic_addr + ED_P0_RBCR1, len >> 8); + /* reset remote DMA complete flag */ + outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); - /* set up destination address in NIC mem */ - outb(sc->nic_addr + ED_P0_RSAR0, dst); - outb(sc->nic_addr + ED_P0_RSAR1, dst >> 8); + /* set up DMA byte count */ + outb(sc->nic_addr + ED_P0_RBCR0, len); + outb(sc->nic_addr + ED_P0_RBCR1, len >> 8); - /* set remote DMA write */ - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); + /* set up destination address in NIC mem */ + outb(sc->nic_addr + ED_P0_RSAR0, dst); + outb(sc->nic_addr + ED_P0_RSAR1, dst >> 8); - if (sc->isa16bit) - outsw(sc->asic_addr + ED_NOVELL_DATA, src, len / 2); - else - outsb(sc->asic_addr + ED_NOVELL_DATA, src, len); + /* set remote DMA write */ + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); - /* - * Wait for remote DMA complete. This is necessary because on the - * transmit side, data is handled internally by the NIC in bursts and - * we can't start another remote DMA until this one completes. Not - * waiting causes really bad things to happen - like the NIC - * irrecoverably jamming the ISA bus. - */ - while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); + if (sc->isa16bit) + outsw(sc->asic_addr + ED_NOVELL_DATA, src, len / 2); + else + outsb(sc->asic_addr + ED_NOVELL_DATA, src, len); + + /* + * Wait for remote DMA complete. This is necessary because on the + * transmit side, data is handled internally by the NIC in bursts and + * we can't start another remote DMA until this one completes. Not + * waiting causes really bad things to happen - like the NIC + * irrecoverably jamming the ISA bus. + */ + while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); + + } else if ((sc->vendor == ED_VENDOR_HP) && + (sc->type == ED_TYPE_HP_PCLANPLUS)) { + + /* HP PCLAN+ */ + + /* reset remote DMA complete flag */ + outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); + + /* program the write address in RAM */ + outw(sc->asic_addr + ED_HPP_PAGE_0, dst); + + if (sc->hpp_mem_start) { + u_short *s = (u_short *) src; + volatile u_short *d = (u_short *) sc->hpp_mem_start; + u_short *const fence = s + (len >> 1); + + /* + * Enable memory mapped access. + */ + + outw(sc->asic_addr + ED_HPP_OPTION, + sc->hpp_options & + ~(ED_HPP_OPTION_MEM_DISABLE | + ED_HPP_OPTION_BOOT_ROM_ENB)); + + /* + * Copy to NIC memory. + */ + + while (s < fence) + *d = *s++; + + /* + * Restore Boot ROM access. + */ + + outw(sc->asic_addr + ED_HPP_OPTION, + sc->hpp_options); + + } else { + /* write data using I/O writes */ + outsw(sc->asic_addr + ED_HPP_PAGE_4, src, len / 2); + } + + } } /* @@ -2641,6 +3063,12 @@ ed_pio_write_mbufs(sc, m, dst) struct mbuf *mp; int maxwait = 200; /* about 240us */ + /* HP PC Lan+ cards need special handling */ + if ((sc->vendor == ED_VENDOR_HP) && + (sc->type == ED_TYPE_HP_PCLANPLUS)) { + return ed_hpp_write_mbufs(sc, m, dst); + } + /* First, count up the total number of bytes to copy */ for (total_len = 0, mp = m; mp; mp = mp->m_next) total_len += mp->m_len; @@ -2740,6 +3168,240 @@ ed_pio_write_mbufs(sc, m, dst) return (total_len); } +/* + * Support routines to handle the HP PC Lan+ card. + */ + +/* + * HP PC Lan+: Read from NIC memory, using either PIO or memory mapped + * IO. + */ + +static void +ed_hpp_readmem(sc, src, dst, amount) + struct ed_softc *sc; + unsigned short src; + unsigned char *dst; + unsigned short amount; +{ + + int use_32bit_access = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS); + + + /* Program the source address in RAM */ + outw(sc->asic_addr + ED_HPP_PAGE_2, src); + + /* + * The HP PC Lan+ card supports word reads as well as + * a memory mapped i/o port that is aliased to every + * even address on the board. + */ + + if (sc->hpp_mem_start) { + + /* Enable memory mapped access. */ + outw(sc->asic_addr + ED_HPP_OPTION, + sc->hpp_options & + ~(ED_HPP_OPTION_MEM_DISABLE | + ED_HPP_OPTION_BOOT_ROM_ENB)); + + if (use_32bit_access && (amount > 3)) { + u_long *dl = (u_long *) dst; + volatile u_long *const sl = + (u_long *) sc->hpp_mem_start; + u_long *const fence = dl + (amount >> 2); + + /* Copy out NIC data. We could probably write this + as a `movsl'. The currently generated code is lousy. + */ + + while (dl < fence) + *dl++ = *sl; + + dst += (amount & ~3); + amount &= 3; + + } + + /* Finish off any words left, as a series of short reads */ + if (amount > 1) { + u_short *d = (u_short *) dst; + volatile u_short *const s = + (u_short *) sc->hpp_mem_start; + u_short *const fence = d + (amount >> 1); + + /* Copy out NIC data. */ + + while (d < fence) + *d++ = *s; + + dst += (amount & ~1); + amount &= 1; + } + + /* + * read in a byte; however we need to always read 16 bits + * at a time or the hardware gets into a funny state + */ + + if (amount == 1) { + /* need to read in a short and copy LSB */ + volatile u_short *const s = + (volatile u_short *) sc->hpp_mem_start; + + *dst = (*s) & 0xFF; + } + + /* Restore Boot ROM access. */ + + outw(sc->asic_addr + ED_HPP_OPTION, + sc->hpp_options); + + + } else { + /* Read in data using the I/O port */ + if (use_32bit_access && (amount > 3)) { + insl(sc->asic_addr + ED_HPP_PAGE_4, dst, amount >> 2); + dst += (amount & ~3); + amount &= 3; + } + if (amount > 1) { + insw(sc->asic_addr + ED_HPP_PAGE_4, dst, amount >> 1); + dst += (amount & ~1); + amount &= 1; + } + if (amount == 1) { /* read in a short and keep the LSB */ + *dst = inw(sc->asic_addr + ED_HPP_PAGE_4) & 0xFF; + } + } +} + +/* + * Write to HP PC Lan+ NIC memory. Access to the NIC can be by using + * outsw() or via the memory mapped interface to the same register. + * Writes have to be in word units; byte accesses won't work and may cause + * the NIC to behave wierdly. Long word accesses are permitted if the ASIC + * allows it. + */ + +static u_short +ed_hpp_write_mbufs(struct ed_softc *sc, struct mbuf *m, int dst) +{ + int len, wantbyte; + unsigned short total_len; + unsigned char savebyte[2]; + volatile u_short * const d = + (volatile u_short *) sc->hpp_mem_start; + int use_32bit_accesses = !(sc->hpp_id & ED_HPP_ID_16_BIT_ACCESS); + + /* select page 0 registers */ + outb(sc->nic_addr + ED_P0_CR, sc->cr_proto | ED_CR_STA); + + /* reset remote DMA complete flag */ + outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); + + /* program the write address in RAM */ + outw(sc->asic_addr + ED_HPP_PAGE_0, dst); + + if (sc->hpp_mem_start) /* enable memory mapped I/O */ + outw(sc->asic_addr + ED_HPP_OPTION, sc->hpp_options & + ~(ED_HPP_OPTION_MEM_DISABLE | + ED_HPP_OPTION_BOOT_ROM_ENB)); + + wantbyte = 0; + total_len = 0; + + if (sc->hpp_mem_start) { /* Memory mapped I/O port */ + while (m) { + total_len += (len = m->m_len); + if (len) { + caddr_t data = mtod(m, caddr_t); + /* finish the last word of the previous mbuf */ + if (wantbyte) { + savebyte[1] = *data; + *d = *((ushort *) savebyte); + data++; len--; wantbyte = 0; + } + /* output contiguous words */ + if ((len > 3) && (use_32bit_accesses)) { + volatile u_long *const dl = + (volatile u_long *) d; + u_long *sl = (u_long *) data; + u_long *fence = sl + (len >> 2); + + while (sl < fence) + *dl = *sl++; + + data += (len & ~3); + len &= 3; + } + /* finish off remain 16 bit writes */ + if (len > 1) { + u_short *s = (u_short *) data; + u_short *fence = s + (len >> 1); + + while (s < fence) + *d = *s++; + + data += (len & ~1); + len &= 1; + } + /* save last byte if needed */ + if (wantbyte = (len == 1)) + savebyte[0] = *data; + } + m = m->m_next; /* to next mbuf */ + } + if (wantbyte) /* write last byte */ + *d = *((u_short *) savebyte); + } else { + /* use programmed I/O */ + while (m) { + total_len += (len = m->m_len); + if (len) { + caddr_t data = mtod(m, caddr_t); + /* finish the last word of the previous mbuf */ + if (wantbyte) { + savebyte[1] = *data; + outw(sc->asic_addr + ED_HPP_PAGE_4, + *((u_short *)savebyte)); + data++; + len--; + wantbyte = 0; + } + /* output contiguous words */ + if ((len > 3) && use_32bit_accesses) { + outsl(sc->asic_addr + ED_HPP_PAGE_4, + data, len >> 2); + data += (len & ~3); + len &= 3; + } + /* finish off remaining 16 bit accesses */ + if (len > 1) { + outsw(sc->asic_addr + ED_HPP_PAGE_4, + data, len >> 1); + data += (len & ~1); + len &= 1; + } + if (wantbyte = (len == 1)) + savebyte[0] = *data; + + } /* if len != 0 */ + m = m->m_next; + } + if (wantbyte) /* spit last byte */ + outw(sc->asic_addr + ED_HPP_PAGE_4, + *(u_short *)savebyte); + + } + + if (sc->hpp_mem_start) /* turn off memory mapped i/o */ + outw(sc->asic_addr + ED_HPP_OPTION, + sc->hpp_options); + + return (total_len); +} + static void ed_setrcr(sc) struct ed_softc *sc; diff --git a/sys/i386/isa/if_edreg.h b/sys/i386/isa/if_edreg.h index a03ce65..db7db18 100644 --- a/sys/i386/isa/if_edreg.h +++ b/sys/i386/isa/if_edreg.h @@ -6,7 +6,7 @@ * of this software, nor does the author assume any responsibility * for damages incurred with its use. * - * $Id: if_edreg.h,v 1.19 1995/09/26 08:57:45 phk Exp $ + * $Id: if_edreg.h,v 1.20 1996/01/30 22:55:38 mpp Exp $ */ /* * National Semiconductor DS8390 NIC register definitions @@ -569,6 +569,7 @@ struct ed_ring { #define ED_VENDOR_3COM 0x01 /* 3Com */ #define ED_VENDOR_NOVELL 0x02 /* Novell */ #define ED_VENDOR_PCCARD 0x03 /* PCMCIA/PCCARD */ +#define ED_VENDOR_HP 0x04 /* Hewlett Packard */ /* * Compile-time config flags @@ -1001,3 +1002,86 @@ struct ed_ring { #define ZE_MISC 0x18 #define ZE_RESET 0x1F +/* + * Definitions for HP PC LAN Adapter Plus; based on the CRYNWR packet + * driver for the card. + */ + +#define ED_HPP_ASIC_OFFSET 0x00 /* Offset to ASIC registers */ +#define ED_HPP_NIC_OFFSET 0x10 /* Offset to 8390 registers */ + +#define ED_HPP_ID 0x00 /* ID register, always 0x4850 */ +#define ED_HPP_PAGING 0x02 /* Page select register */ +#define ED_HPP_OPTION 0x04 /* Bitmask of supported options */ +#define ED_HPP_PAGE_0 0x08 /* Page 0 */ +#define ED_HPP_PAGE_2 0x0A /* Page 2 */ +#define ED_HPP_PAGE_4 0x0C /* Page 4 */ +#define ED_HPP_PAGE_6 0x0E /* Page 6 */ + +/* PERF PAGE */ +#define ED_HPP_OUT_ADDR ED_HPP_PAGE_0 /* I/O output location */ +#define ED_HPP_IN_ADDR ED_HPP_PAGE_2 /* I/O input location */ +#define ED_HPP_DATAPORT ED_HPP_PAGE_4 /* I/O data transfer */ +/* MAC PAGE */ +#define ED_HPP_MAC_ADDR 0x08 /* Offset of MAC address in MAC page */ + +#define ED_HPP_IO_PORTS 32 /* Number of IO ports */ + +#define ED_HPP_TX_PAGE_OFFSET 0x00 /* first page of TX buffer */ +#define ED_HPP_RX_PAGE_START 0x06 /* start at page 6 */ +#define ED_HPP_RX_PAGE_STOP 0x80 /* end at page 128 */ + +/* + * Register pages supported. + */ + +#define ED_HPP_PAGE_PERF 0 /* Normal operation */ +#define ED_HPP_PAGE_MAC 1 /* The ethernet address and checksum */ +#define ED_HPP_PAGE_HW 2 /* Hardware parameters in EEPROM */ +#define ED_HPP_PAGE_LAN 4 /* Transciever selection etc */ +#define ED_HPP_PAGE_ID 6 /* ID */ + +/* + * Options supported. + */ + +#define ED_HPP_OPTION_NIC_RESET 0x0001 /* active low */ +#define ED_HPP_OPTION_CHIP_RESET 0x0002 /* active low */ +#define ED_HPP_OPTION_ENABLE_IRQ 0x0004 +#define ED_HPP_OPTION_FAKE_INTR 0x0008 +#define ED_HPP_OPTION_BOOT_ROM_ENB 0x0010 +#define ED_HPP_OPTION_IO_ENB 0x0020 +#define ED_HPP_OPTION_MEM_ENABLE 0x0040 +#define ED_HPP_OPTION_ZERO_WAIT 0x0080 +#define ED_HPP_OPTION_MEM_DISABLE 0x1000 + +/* + * Page ID configuration. + */ + +#define ED_HPP_ID_REVISION_MASK 0x0300 /* revision id */ +#define ED_HPP_ID_SOFT_MODEL_MASK 0xFC00 /* soft model number */ +#define ED_HPP_ID_16_BIT_ACCESS 0x0010 /* if set use 16 bit accesses */ +#define ED_HPP_ID_TWISTED_PAIR 0x0040 + +/* + * Hardware configuration. + */ + +#define ED_HPP_HW_MEM_MAP 0x09 /* low mem map location in HW page */ +#define ED_HPP_HW_ID 0x0C /* revision number, capabilities */ +#define ED_HPP_HW_IRQ 0x0D /* IRQ channel register in HW page */ +#define ED_HPP_HW_WRAP 0x0E /* mem wrap page for rcv */ + +/* + * Lan configuration + */ + +#define ED_HPP_LAN_AUI 0x01 /* Use AUI */ +#define ED_HPP_LAN_TL 0x40 /* Don't use AUI */ + +/* + * Card types. + */ + +#define ED_TYPE_HP_PCLANPLUS 0x00 |