diff options
Diffstat (limited to 'sys/arch/i386/netboot/wd80x3.c')
-rw-r--r-- | sys/arch/i386/netboot/wd80x3.c | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/sys/arch/i386/netboot/wd80x3.c b/sys/arch/i386/netboot/wd80x3.c new file mode 100644 index 00000000000..3c4fe8b6b66 --- /dev/null +++ b/sys/arch/i386/netboot/wd80x3.c @@ -0,0 +1,432 @@ +/* $NetBSD: wd80x3.c,v 1.4 1994/10/27 04:21:28 cgd Exp $ */ + +/* + * source in this file came from + * the Mach ethernet boot written by Leendert van Doorn. + * + * A very simple network driver for WD80x3 boards that polls. + * + * Copyright (c) 1992 by Leendert van Doorn + */ + +#include "proto.h" +#include "assert.h" +#include "packet.h" +#include "ether.h" +#include "dp8390.h" + +/* configurable parameters */ +#define WD_BASEREG 0x280 /* base register */ +/* the base address doesn't have to be particularly accurate - the + board seems to pick up on addresses in the range a0000..effff. + */ +#define WD_BASEMEM 0xd0000 /* base ram */ + +/* bit definitions for board features */ +#define INTERFACE_CHIP 01 /* has an WD83C583 interface chip */ +#define BOARD_16BIT 02 /* 16 bit board */ +#define SLOT_16BIT 04 /* 16 bit slot */ + +/* register offset definitions */ +#define WD_MSR 0x00 /* control (w) and status (r) */ +#define WD_REG0 0x00 /* generic register definitions */ +#define WD_REG1 0x01 +#define WD_REG2 0x02 +#define WD_REG3 0x03 +#define WD_REG4 0x04 +#define WD_REG5 0x05 +#define WD_REG6 0x06 +#define WD_REG7 0x07 +#define WD_EA0 0x08 /* most significant addr byte */ +#define WD_EA1 0x09 +#define WD_EA2 0x0A +#define WD_EA3 0x0B +#define WD_EA4 0x0C +#define WD_EA5 0x0D /* least significant addr byte */ +#define WD_LTB 0x0E /* LAN type byte */ +#define WD_CHKSUM 0x0F /* sum from WD_EA0 upto here is 0xFF */ +#define WD_DP8390 0x10 /* natsemi chip */ + +/* bits in control register */ +#define WD_MSR_MEMMASK 0x3F /* memory enable bits mask */ +#define WD_MSR_MENABLE 0x40 /* memory enable */ +#define WD_MSR_RESET 0x80 /* software reset */ + +/* bits in bus size register */ +#define WD_BSR_16BIT 0x01 /* 16 bit bus */ + +/* bits in LA address register */ +#define WD_LAAR_A19 0x01 /* address lines for above 1Mb ram */ +#define WD_LAAR_LAN16E 0x40 /* enables 16bit shrd RAM for LAN */ +#define WD_LAAR_MEM16E 0x80 /* enables 16bit shrd RAM for host */ + +u_char eth_myaddr[ETH_ADDRSIZE]; + +static int boardid; +static dpconf_t dpc; + +/* + * Determine whether wd8003 hardware performs register aliasing + * (i.e. whether it is an old WD8003E board). + */ +static int +Aliasing(void) { + if (inb(WD_BASEREG + WD_REG1) != inb(WD_BASEREG + WD_EA1)) + return 0; + if (inb(WD_BASEREG + WD_REG2) != inb(WD_BASEREG + WD_EA2)) + return 0; + if (inb(WD_BASEREG + WD_REG3) != inb(WD_BASEREG + WD_EA3)) + return 0; + if (inb(WD_BASEREG + WD_REG4) != inb(WD_BASEREG + WD_EA4)) + return 0; + if (inb(WD_BASEREG + WD_REG7) != inb(WD_BASEREG + WD_CHKSUM)) + return 0; + return 1; +} + +/* + * This trick is stolen from the clarkson packet driver +TBD - this is _ugly_ bogus! should use system timer + */ +static void +LongPause(void) { + short i; + for (i = 1600; i > 0; i++) + (void) inb(0x61); +} + +/* + * Determine whether this board has 16-bit capabilities + */ +static int +BoardIs16Bit(void) { + u_char bsreg = inb(WD_BASEREG + WD_REG1); + + outb(WD_BASEREG + WD_REG1, bsreg ^ WD_BSR_16BIT); + LongPause(); + if (inb(WD_BASEREG + WD_REG1) == bsreg) { + /* + * Pure magic: LTB is 0x05 indicates that this is a WD8013EB board, + * 0x27 indicates that this is an WD8013 Elite board, and 0x29 + * indicates an SMC Elite 16 board. + */ + u_char tlb = inb(WD_BASEREG + WD_LTB); + return tlb == 0x05 || tlb == 0x27 || tlb == 0x29; + } + outb(WD_BASEREG + WD_REG1, bsreg); + return 1; +} + +/* + * Determine whether the 16 bit capable board is plugged + * into a 16 bit slot. + */ +static int +SlotIs16Bit(void) { + return inb(WD_BASEREG + WD_REG1) & WD_BSR_16BIT; +} + +/* + * Reset ethernet board after a timeout + */ +void +EtherReset(void) { + int dpreg = dpc.dc_reg; + /* initialize the board */ + outb(WD_BASEREG + WD_MSR, + WD_MSR_MENABLE | (((u_long)WD_BASEMEM >> 13) & WD_MSR_MEMMASK)); + + /* reset dp8390 ethernet chip */ + outb(dpreg + DP_CR, CR_STP|CR_DM_ABORT); + + /* initialize first register set */ + outb(dpreg + DP_IMR, 0); + outb(dpreg + DP_CR, CR_PS_P0|CR_STP|CR_DM_ABORT); + outb(dpreg + DP_TPSR, dpc.dc_tpsr); + outb(dpreg + DP_PSTART, dpc.dc_pstart); + outb(dpreg + DP_PSTOP, dpc.dc_pstop); + outb(dpreg + DP_BNRY, dpc.dc_pstart); + outb(dpreg + DP_RCR, RCR_MON); + outb(dpreg + DP_TCR, TCR_NORMAL|TCR_OFST); + if (boardid & SLOT_16BIT) + outb(dpreg + DP_DCR, DCR_WORDWIDE|DCR_8BYTES); + else + outb(dpreg + DP_DCR, DCR_BYTEWIDE|DCR_8BYTES); + outb(dpreg + DP_RBCR0, 0); + outb(dpreg + DP_RBCR1, 0); + outb(dpreg + DP_ISR, 0xFF); + + /* initialize second register set */ + outb(dpreg + DP_CR, CR_PS_P1|CR_DM_ABORT); + outb(dpreg + DP_PAR0, eth_myaddr[0]); + outb(dpreg + DP_PAR1, eth_myaddr[1]); + outb(dpreg + DP_PAR2, eth_myaddr[2]); + outb(dpreg + DP_PAR3, eth_myaddr[3]); + outb(dpreg + DP_PAR4, eth_myaddr[4]); + outb(dpreg + DP_PAR5, eth_myaddr[5]); + outb(dpreg + DP_CURR, dpc.dc_pstart+1); + + /* and back to first register set */ + outb(dpreg + DP_CR, CR_PS_P0|CR_DM_ABORT); + outb(dpreg + DP_RCR, RCR_AB); + + /* flush counters */ + (void) inb(dpreg + DP_CNTR0); + (void) inb(dpreg + DP_CNTR1); + (void) inb(dpreg + DP_CNTR2); + + /* and go ... */ + outb(dpreg + DP_CR, CR_STA|CR_DM_ABORT); +} + +/* + * Initialize the WD80X3 board + */ +int +EtherInit(void) { + unsigned sum; + int memsize; + /* reset the ethernet card */ + outb(WD_BASEREG + WD_MSR, WD_MSR_RESET); + LongPause(); + outb(WD_BASEREG + WD_MSR, 0); + + /* determine whether the controller is there */ + sum = inb(WD_BASEREG + WD_EA0) + inb(WD_BASEREG + WD_EA1) + + inb(WD_BASEREG + WD_EA2) + inb(WD_BASEREG + WD_EA3) + + inb(WD_BASEREG + WD_EA4) + inb(WD_BASEREG + WD_EA5) + + inb(WD_BASEREG + WD_LTB) + inb(WD_BASEREG + WD_CHKSUM); + if ((sum & 0xFF) != 0xFF) + return 0; + + /* + * Determine the type of board + */ + boardid = 0; + if (!Aliasing()) { + if (BoardIs16Bit()) { + boardid |= BOARD_16BIT; + if (SlotIs16Bit()) + boardid |= SLOT_16BIT; + } + } + memsize = (boardid & BOARD_16BIT) ? 0x4000 : 0x2000; /* 16 or 8 Kb */ + + /* special setup needed for WD8013 boards */ + if (boardid & SLOT_16BIT) + outb(WD_BASEREG + WD_REG5, WD_LAAR_A19|WD_LAAR_LAN16E); + + /* get ethernet address */ + eth_myaddr[0] = inb(WD_BASEREG + WD_EA0); + eth_myaddr[1] = inb(WD_BASEREG + WD_EA1); + eth_myaddr[2] = inb(WD_BASEREG + WD_EA2); + eth_myaddr[3] = inb(WD_BASEREG + WD_EA3); + eth_myaddr[4] = inb(WD_BASEREG + WD_EA4); + eth_myaddr[5] = inb(WD_BASEREG + WD_EA5); + + /* save settings for future use */ + dpc.dc_reg = WD_BASEREG + WD_DP8390; + dpc.dc_mem = WD_BASEMEM; + dpc.dc_tpsr = 0; + dpc.dc_pstart = 6; + dpc.dc_pstop = (memsize >> 8) & 0xFF; + + printf("Using wd80x3 board, port 0x%x, iomem 0x%x, iosiz %d\n", WD_BASEREG, WD_BASEMEM, memsize); + + EtherReset(); + return 1; +} + +/* + * Stop ethernet board + */ +void +EtherStop(void) { + /* stop dp8390, followed by a board reset */ + outb(dpc.dc_reg + DP_CR, CR_STP|CR_DM_ABORT); + outb(WD_BASEREG + WD_MSR, WD_MSR_RESET); + outb(WD_BASEREG + WD_MSR, 0); +} + +/* TBD - all users must take care to use the current "data seg" value +when moving data from/to the controller */ +static void +WdCopy(u_long src, u_long dst, u_long count) { +#if TRACE > 0 +printf("WdCopy from %x to %x for %d\n", src, dst, count); +#endif + assert(count <= 1514); + if (boardid & SLOT_16BIT) + outb(WD_BASEREG + WD_REG5, + WD_LAAR_MEM16E|WD_LAAR_LAN16E|WD_LAAR_A19); + PhysBcopy(src, dst, count); + if (boardid & SLOT_16BIT) + outb(WD_BASEREG + WD_REG5, WD_LAAR_LAN16E|WD_LAAR_A19); +} + +/* + * Send an ethernet packet to destination 'dest' + */ +void +EtherSend(packet_t *pkt, u_short proto, u_char *dest) { + ethhdr_t *ep; + + pkt->pkt_len += sizeof(ethhdr_t); + pkt->pkt_offset -= sizeof(ethhdr_t); + ep = (ethhdr_t *) pkt->pkt_offset; + ep->eth_proto = htons(proto); + bcopy((char *)dest, (char *)ep->eth_dst, ETH_ADDRSIZE); + bcopy((char *)eth_myaddr, (char *)ep->eth_src, ETH_ADDRSIZE); +#if 0 +DUMP_STRUCT("ethhdr_t", ep, sizeof(ethhdr_t)); +#endif + if (pkt->pkt_len < 60) + pkt->pkt_len = 60; + +#if TRACE > 0 + { + int i; +DUMP_STRUCT("EtherSend: pkt", pkt->pkt_offset, pkt->pkt_len); +#if 0 + for(i=0; i<(pkt->pkt_len<MDUMP?pkt->pkt_len:MDUMP); i++) printe("%x ", *((u_char*)(pkt->pkt_offset)+i)); + printe("\n"); +#endif + } +#endif + +#if 0 +printe("EtherSend: WdCopy from %x to %x for %d\n", LA(pkt->pkt_offset), dpc.dc_mem + (dpc.dc_tpsr << 8), (u_long)pkt->pkt_len); +#endif + WdCopy(LA(pkt->pkt_offset), + dpc.dc_mem + (dpc.dc_tpsr << 8), (u_long)pkt->pkt_len); + outb(dpc.dc_reg + DP_TPSR, dpc.dc_tpsr); + outb(dpc.dc_reg + DP_TBCR0, (pkt->pkt_len & 0xFF)); + outb(dpc.dc_reg + DP_TBCR1, (pkt->pkt_len >> 8) & 0xFF); + outb(dpc.dc_reg + DP_CR, CR_TXP); + +#if 0 + printe("Ethersend: outb(%x, %x)\n", dpc.dc_reg + DP_TPSR, dpc.dc_tpsr); + printe("Ethersend: outb(%x, %x)\n", dpc.dc_reg + DP_TBCR0, (pkt->pkt_len & 0xFF)); + printe("Ethersend: outb(%x, %x)\n", dpc.dc_reg + DP_TBCR1, (pkt->pkt_len >> 8) & 0xFF); + printe("Ethersend: outb(%x, %x)\n", dpc.dc_reg + DP_CR, CR_TXP); +#endif +} + +/* + * Copy dp8390 packet header for observation + */ +static void +GetHeader(u_long haddr, dphdr_t *dph) { +#if TRACE > 0 +printe("GetHeader: WdCopy from %x to %x for %d\n", haddr, LA(dph), sizeof(dphdr_t)); +#endif + WdCopy(haddr, LA(dph), sizeof(dphdr_t)); +#if 0 +DUMP_STRUCT("GetHeader: dphdr_t", dph, sizeof(dphdr_t)); +#endif +} + +/* + * Poll the dp8390 just see if there's an Ethernet packet + * available. If there is, its contents is returned in a + * pkt structure, otherwise a nil pointer is returned. + */ +packet_t * +EtherReceive(void) { + u_char pageno, curpage, nextpage; + int dpreg = dpc.dc_reg; + packet_t *pkt; + dphdr_t dph; + u_long addr; + + pkt = (packet_t *)0; + if (inb(dpreg + DP_RSR) & RSR_PRX) { + /* get current page numbers */ + pageno = inb(dpreg + DP_BNRY) + 1; + if (pageno == dpc.dc_pstop) + pageno = dpc.dc_pstart; + outb(dpreg + DP_CR, CR_PS_P1); + curpage = inb(dpreg + DP_CURR); + outb(dpreg + DP_CR, CR_PS_P0); + if (pageno == curpage) + return (packet_t *) 0; + + /* get packet header */ + addr = dpc.dc_mem + (pageno << 8); + GetHeader(addr, &dph); + nextpage = dph.dh_next; + + /* allocate packet */ + pkt = PktAlloc(0); +#if 0 +printe("EtherReceive: allocated pkt %x\n", pkt); +#endif + pkt->pkt_len = ((dph.dh_rbch & 0xFF) << 8) | (dph.dh_rbcl & 0xFF); + pkt->pkt_len -= sizeof(dphdr_t); + if (pkt->pkt_len > 1514) /* bug in dp8390 */ + pkt->pkt_len = 1514; + +#if TRACE > 0 + { + int i; + printe("EtherReceive %d bytes: ", pkt->pkt_len); +#if 0 + for(i=0; i<(pkt->pkt_len<MDUMP?pkt->pkt_len:MDUMP); i++) printe("%x ", *((u_char*)pkt+i)); +#else + DUMP_STRUCT("", pkt, pkt->pkt_len); +#endif + printe("\n"); + } +#endif + + /* + * The dp8390 maintains a circular buffer of pages (256 bytes) + * in which incomming ethernet packets are stored. The following + * if detects wrap arounds, and copies the ethernet packet to + * our local buffer in two chunks if necesarry. + */ + assert(pkt->pkt_offset); + assert(pkt->pkt_len <= (6 << 8)); + if (nextpage < pageno && nextpage > dpc.dc_pstart) { + u_long nbytes = ((dpc.dc_pstop - pageno) << 8) - sizeof(dphdr_t); + + assert(nbytes <= (6 << 8)); +#if TRACE > 0 +printe("EtherReceive1: WdCopy from %x to %x for %x\n", addr + sizeof(dphdr_t), LA(pkt->pkt_offset), nbytes); +#endif + WdCopy(addr + sizeof(dphdr_t), + LA(pkt->pkt_offset), nbytes); + if ((pkt->pkt_len - nbytes) > 0) + /* TBD - this OK? */ +#if TRACE > 0 +printe("EtherReceive2: WdCopy from %x to %x for %x\n",dpc.dc_mem + (dpc.dc_pstart << 8), LA(pkt->pkt_offset) + nbytes, pkt->pkt_len - nbytes); +#endif + WdCopy(dpc.dc_mem + (dpc.dc_pstart << 8), + LA(pkt->pkt_offset) + nbytes, + pkt->pkt_len - nbytes); + } else { +#if TRACE > 0 +printe("EtherReceive3: WdCopy from %x to %x for %x\n", addr + sizeof(dphdr_t), LA(pkt->pkt_offset), (u_long)pkt->pkt_len); +#endif + WdCopy(addr + sizeof(dphdr_t), + LA(pkt->pkt_offset), (u_long)pkt->pkt_len); + } + + /* release occupied pages */ + if (nextpage == dpc.dc_pstart) + nextpage = dpc.dc_pstop; + outb(dpreg + DP_BNRY, nextpage - 1); + } + + return pkt; +} + +/* + * Print an ethernet address in human readable form + */ +void +EtherPrintAddr(u_char *addr) { + printf("%x:%x:%x:%x:%x:%x", + addr[0] & 0xFF, addr[1] & 0xFF, addr[2] & 0xFF, + addr[3] & 0xFF, addr[4] & 0xFF, addr[5] & 0xFF); +} |