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>
This commit is contained in:
David Greenman 1996-08-07 11:18:23 +00:00
parent e3b68a1b89
commit aea6ddfd4f
4 changed files with 1556 additions and 64 deletions

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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