summaryrefslogtreecommitdiffstats
path: root/src/drivers/net
diff options
context:
space:
mode:
authorMichael Brown2005-03-08 19:53:11 +0100
committerMichael Brown2005-03-08 19:53:11 +0100
commit3d6123e69ab879c72ff489afc5bf93ef0b7a94ce (patch)
tree9f3277569153a550fa8d81ebd61bd88f266eb8da /src/drivers/net
downloadipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.tar.gz
ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.tar.xz
ipxe-3d6123e69ab879c72ff489afc5bf93ef0b7a94ce.zip
Initial revision
Diffstat (limited to 'src/drivers/net')
-rw-r--r--src/drivers/net/3c509.c671
-rw-r--r--src/drivers/net/3c509.h399
-rw-r--r--src/drivers/net/3c515.c814
-rw-r--r--src/drivers/net/3c515.txt31
-rw-r--r--src/drivers/net/3c595.c550
-rw-r--r--src/drivers/net/3c595.h435
-rw-r--r--src/drivers/net/3c90x.c996
-rw-r--r--src/drivers/net/3c90x.txt307
-rw-r--r--src/drivers/net/cs89x0.c720
-rw-r--r--src/drivers/net/cs89x0.h461
-rw-r--r--src/drivers/net/cs89x0.txt26
-rw-r--r--src/drivers/net/davicom.c720
-rw-r--r--src/drivers/net/depca.c794
-rw-r--r--src/drivers/net/dmfe.c1230
-rw-r--r--src/drivers/net/e1000.c3713
-rw-r--r--src/drivers/net/e1000_hw.h2058
-rw-r--r--src/drivers/net/eepro.c625
-rw-r--r--src/drivers/net/eepro100.c841
-rw-r--r--src/drivers/net/epic100.c520
-rw-r--r--src/drivers/net/epic100.h188
-rw-r--r--src/drivers/net/forcedeth.c1039
-rw-r--r--src/drivers/net/hfa384x.h2744
-rw-r--r--src/drivers/net/mtd80x.c1096
-rw-r--r--src/drivers/net/natsemi.c780
-rwxr-xr-xsrc/drivers/net/ns83820.c1020
-rw-r--r--src/drivers/net/ns8390.c1016
-rw-r--r--src/drivers/net/ns8390.h238
-rw-r--r--src/drivers/net/p80211hdr.h262
-rw-r--r--src/drivers/net/pcnet32.c1004
-rw-r--r--src/drivers/net/pnic.c267
-rw-r--r--src/drivers/net/pnic_api.h59
-rw-r--r--src/drivers/net/prism2.c948
-rw-r--r--src/drivers/net/prism2_pci.c32
-rw-r--r--src/drivers/net/prism2_plx.c42
-rw-r--r--src/drivers/net/r8169.c854
-rw-r--r--src/drivers/net/rtl8139.c551
-rw-r--r--src/drivers/net/sis900.c1271
-rw-r--r--src/drivers/net/sis900.h380
-rw-r--r--src/drivers/net/sis900.txt91
-rw-r--r--src/drivers/net/sk_g16.c1189
-rw-r--r--src/drivers/net/sk_g16.h171
-rw-r--r--src/drivers/net/skel.c200
-rw-r--r--src/drivers/net/smc9000.c544
-rw-r--r--src/drivers/net/smc9000.h205
-rw-r--r--src/drivers/net/sundance.c891
-rw-r--r--src/drivers/net/tg3.c3394
-rw-r--r--src/drivers/net/tg3.h2211
-rw-r--r--src/drivers/net/tlan.c1722
-rw-r--r--src/drivers/net/tlan.h536
-rw-r--r--src/drivers/net/tulip.c2082
-rw-r--r--src/drivers/net/tulip.txt53
-rw-r--r--src/drivers/net/via-rhine.c1426
-rw-r--r--src/drivers/net/w89c840.c958
-rw-r--r--src/drivers/net/wlan_compat.h575
54 files changed, 45950 insertions, 0 deletions
diff --git a/src/drivers/net/3c509.c b/src/drivers/net/3c509.c
new file mode 100644
index 00000000..0a7187ed
--- /dev/null
+++ b/src/drivers/net/3c509.c
@@ -0,0 +1,671 @@
+/**************************************************************************
+ETHERBOOT - BOOTP/TFTP Bootstrap Program
+
+Author: Martin Renters.
+ Date: Mar 22 1995
+
+ This code is based heavily on David Greenman's if_ed.c driver and
+ Andres Vega Garcia's if_ep.c driver.
+
+ Copyright (C) 1993-1994, David Greenman, Martin Renters.
+ Copyright (C) 1993-1995, Andres Vega Garcia.
+ Copyright (C) 1995, Serge Babkin.
+ This software may be used, modified, copied, distributed, and sold, in
+ both source and binary form provided that the above copyright and these
+ terms are retained. Under no circumstances are the authors responsible for
+ the proper functioning of this software, nor do the authors assume any
+ responsibility for damages incurred with its use.
+
+3c509 support added by Serge Babkin (babkin@hq.icb.chel.su)
+
+$Id$
+
+***************************************************************************/
+
+/* #define EDEBUG */
+
+#include "etherboot.h"
+#include "nic.h"
+#include "isa.h"
+#include "timer.h"
+#include "3c509.h"
+
+static unsigned short eth_nic_base;
+static enum { none, bnc, utp } connector = none; /* for 3C509 */
+
+#ifdef INCLUDE_3C529
+/*
+ * This table and several other pieces of the MCA support
+ * code were shamelessly borrowed from the Linux kernel source.
+ *
+ * MCA support added by Adam Fritzler (mid@auk.cx)
+ *
+ */
+struct el3_mca_adapters_struct {
+ const char *name;
+ int id;
+};
+static struct el3_mca_adapters_struct el3_mca_adapters[] = {
+ { "3Com 3c529 EtherLink III (10base2)", 0x627c },
+ { "3Com 3c529 EtherLink III (10baseT)", 0x627d },
+ { "3Com 3c529 EtherLink III (test mode)", 0x62db },
+ { "3Com 3c529 EtherLink III (TP or coax)", 0x62f6 },
+ { "3Com 3c529 EtherLink III (TP)", 0x62f7 },
+ { NULL, 0 },
+};
+#endif
+
+/**************************************************************************
+ETH_RESET - Reset adapter
+***************************************************************************/
+static void t509_reset(struct nic *nic)
+{
+ int i;
+
+ /***********************************************************
+ Reset 3Com 509 card
+ *************************************************************/
+
+ /* stop card */
+ outw(RX_DISABLE, BASE + EP_COMMAND);
+ outw(RX_DISCARD_TOP_PACK, BASE + EP_COMMAND);
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ ;
+ outw(TX_DISABLE, BASE + EP_COMMAND);
+ outw(STOP_TRANSCEIVER, BASE + EP_COMMAND);
+ udelay(1000);
+ outw(RX_RESET, BASE + EP_COMMAND);
+ outw(TX_RESET, BASE + EP_COMMAND);
+ outw(C_INTR_LATCH, BASE + EP_COMMAND);
+ outw(SET_RD_0_MASK, BASE + EP_COMMAND);
+ outw(SET_INTR_MASK, BASE + EP_COMMAND);
+ outw(SET_RX_FILTER, BASE + EP_COMMAND);
+
+ /*
+ * initialize card
+ */
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ ;
+
+ GO_WINDOW(0);
+
+ /* Disable the card */
+ outw(0, BASE + EP_W0_CONFIG_CTRL);
+
+ /* Configure IRQ to none */
+ outw(SET_IRQ(0), BASE + EP_W0_RESOURCE_CFG);
+
+ /* Enable the card */
+ outw(ENABLE_DRQ_IRQ, BASE + EP_W0_CONFIG_CTRL);
+
+ GO_WINDOW(2);
+
+ /* Reload the ether_addr. */
+ for (i = 0; i < ETH_ALEN; i++)
+ outb(nic->node_addr[i], BASE + EP_W2_ADDR_0 + i);
+
+ outw(RX_RESET, BASE + EP_COMMAND);
+ outw(TX_RESET, BASE + EP_COMMAND);
+
+ /* Window 1 is operating window */
+ GO_WINDOW(1);
+ for (i = 0; i < 31; i++)
+ inb(BASE + EP_W1_TX_STATUS);
+
+ /* get rid of stray intr's */
+ outw(ACK_INTR | 0xff, BASE + EP_COMMAND);
+
+ outw(SET_RD_0_MASK | S_5_INTS, BASE + EP_COMMAND);
+
+ outw(SET_INTR_MASK, BASE + EP_COMMAND);
+
+ outw(SET_RX_FILTER | FIL_GROUP | FIL_INDIVIDUAL | FIL_BRDCST, BASE + EP_COMMAND);
+
+ /* configure BNC */
+ if (connector == bnc) {
+ outw(START_TRANSCEIVER, BASE + EP_COMMAND);
+ udelay(1000);
+ }
+ /* configure UTP */
+ else if (connector == utp) {
+ GO_WINDOW(4);
+ outw(ENABLE_UTP, BASE + EP_W4_MEDIA_TYPE);
+ sleep(2); /* Give time for media to negotiate */
+ GO_WINDOW(1);
+ }
+
+ /* start transceiver and receiver */
+ outw(RX_ENABLE, BASE + EP_COMMAND);
+ outw(TX_ENABLE, BASE + EP_COMMAND);
+
+ /* set early threshold for minimal packet length */
+ outw(SET_RX_EARLY_THRESH | ETH_ZLEN, BASE + EP_COMMAND);
+ outw(SET_TX_START_THRESH | 16, BASE + EP_COMMAND);
+}
+
+/**************************************************************************
+ETH_TRANSMIT - Transmit a frame
+***************************************************************************/
+static char padmap[] = {
+ 0, 3, 2, 1};
+
+static void t509_transmit(
+struct nic *nic,
+const char *d, /* Destination */
+unsigned int t, /* Type */
+unsigned int s, /* size */
+const char *p) /* Packet */
+{
+ register unsigned int len;
+ int pad;
+ int status;
+
+#ifdef EDEBUG
+ printf("{l=%d,t=%hX}",s+ETH_HLEN,t);
+#endif
+
+ /* swap bytes of type */
+ t= htons(t);
+
+ len=s+ETH_HLEN; /* actual length of packet */
+ pad = padmap[len & 3];
+
+ /*
+ * The 3c509 automatically pads short packets to minimum ethernet length,
+ * but we drop packets that are too large. Perhaps we should truncate
+ * them instead?
+ */
+ if (len + pad > ETH_FRAME_LEN) {
+ return;
+ }
+
+ /* drop acknowledgements */
+ while ((status=inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE ) {
+ if (status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) {
+ outw(TX_RESET, BASE + EP_COMMAND);
+ outw(TX_ENABLE, BASE + EP_COMMAND);
+ }
+ outb(0x0, BASE + EP_W1_TX_STATUS);
+ }
+
+ while (inw(BASE + EP_W1_FREE_TX) < (unsigned short)len + pad + 4)
+ ; /* no room in FIFO */
+
+ outw(len, BASE + EP_W1_TX_PIO_WR_1);
+ outw(0x0, BASE + EP_W1_TX_PIO_WR_1); /* Second dword meaningless */
+
+ /* write packet */
+ outsw(BASE + EP_W1_TX_PIO_WR_1, d, ETH_ALEN/2);
+ outsw(BASE + EP_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2);
+ outw(t, BASE + EP_W1_TX_PIO_WR_1);
+ outsw(BASE + EP_W1_TX_PIO_WR_1, p, s / 2);
+ if (s & 1)
+ outb(*(p+s - 1), BASE + EP_W1_TX_PIO_WR_1);
+
+ while (pad--)
+ outb(0, BASE + EP_W1_TX_PIO_WR_1); /* Padding */
+
+ /* wait for Tx complete */
+ while((inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) != 0)
+ ;
+}
+
+/**************************************************************************
+ETH_POLL - Wait for a frame
+***************************************************************************/
+static int t509_poll(struct nic *nic, int retrieve)
+{
+ /* common variables */
+ /* variables for 3C509 */
+ short status, cst;
+ register short rx_fifo;
+
+ cst=inw(BASE + EP_STATUS);
+
+#ifdef EDEBUG
+ if(cst & 0x1FFF)
+ printf("-%hX-",cst);
+#endif
+
+ if( (cst & S_RX_COMPLETE)==0 ) {
+ /* acknowledge everything */
+ outw(ACK_INTR| (cst & S_5_INTS), BASE + EP_COMMAND);
+ outw(C_INTR_LATCH, BASE + EP_COMMAND);
+
+ return 0;
+ }
+
+ status = inw(BASE + EP_W1_RX_STATUS);
+#ifdef EDEBUG
+ printf("*%hX*",status);
+#endif
+
+ if (status & ERR_RX) {
+ outw(RX_DISCARD_TOP_PACK, BASE + EP_COMMAND);
+ return 0;
+ }
+
+ rx_fifo = status & RX_BYTES_MASK;
+ if (rx_fifo==0)
+ return 0;
+
+ if ( ! retrieve ) return 1;
+
+ /* read packet */
+#ifdef EDEBUG
+ printf("[l=%d",rx_fifo);
+#endif
+ insw(BASE + EP_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2);
+ if(rx_fifo & 1)
+ nic->packet[rx_fifo-1]=inb(BASE + EP_W1_RX_PIO_RD_1);
+ nic->packetlen=rx_fifo;
+
+ while(1) {
+ status = inw(BASE + EP_W1_RX_STATUS);
+#ifdef EDEBUG
+ printf("*%hX*",status);
+#endif
+ rx_fifo = status & RX_BYTES_MASK;
+ if(rx_fifo>0) {
+ insw(BASE + EP_W1_RX_PIO_RD_1, nic->packet+nic->packetlen, rx_fifo / 2);
+ if(rx_fifo & 1)
+ nic->packet[nic->packetlen+rx_fifo-1]=inb(BASE + EP_W1_RX_PIO_RD_1);
+ nic->packetlen+=rx_fifo;
+#ifdef EDEBUG
+ printf("+%d",rx_fifo);
+#endif
+ }
+ if(( status & RX_INCOMPLETE )==0) {
+#ifdef EDEBUG
+ printf("=%d",nic->packetlen);
+#endif
+ break;
+ }
+ udelay(1000); /* if incomplete wait 1 ms */
+ }
+ /* acknowledge reception of packet */
+ outw(RX_DISCARD_TOP_PACK, BASE + EP_COMMAND);
+ while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+ ;
+#ifdef EDEBUG
+{
+ unsigned short type = 0; /* used by EDEBUG */
+ type = (nic->packet[12]<<8) | nic->packet[13];
+ if(nic->packet[0]+nic->packet[1]+nic->packet[2]+nic->packet[3]+nic->packet[4]+
+ nic->packet[5] == 0xFF*ETH_ALEN)
+ printf(",t=%hX,b]",type);
+ else
+ printf(",t=%hX]",type);
+}
+#endif
+ return (1);
+}
+
+/*************************************************************************
+ 3Com 509 - specific routines
+**************************************************************************/
+
+static int
+eeprom_rdy(void)
+{
+ int i;
+
+ for (i = 0; is_eeprom_busy(IS_BASE) && i < MAX_EEPROMBUSY; i++);
+ if (i >= MAX_EEPROMBUSY) {
+ /* printf("3c509: eeprom failed to come ready.\n"); */
+ /* memory in EPROM is tight */
+ /* printf("3c509: eeprom busy.\n"); */
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * get_e: gets a 16 bits word from the EEPROM. we must have set the window
+ * before
+ */
+static int
+get_e(int offset)
+{
+ if (!eeprom_rdy())
+ return (0xffff);
+ outw(EEPROM_CMD_RD | offset, IS_BASE + EP_W0_EEPROM_COMMAND);
+ if (!eeprom_rdy())
+ return (0xffff);
+ return (inw(IS_BASE + EP_W0_EEPROM_DATA));
+}
+
+static int
+send_ID_sequence(int port)
+{
+ int cx, al;
+
+ for (al = 0xff, cx = 0; cx < 255; cx++) {
+ outb(al, port);
+ al <<= 1;
+ if (al & 0x100)
+ al ^= 0xcf;
+ }
+ return (1);
+}
+
+
+/*
+ * We get eeprom data from the id_port given an offset into the eeprom.
+ * Basically; after the ID_sequence is sent to all of the cards; they enter
+ * the ID_CMD state where they will accept command requests. 0x80-0xbf loads
+ * the eeprom data. We then read the port 16 times and with every read; the
+ * cards check for contention (ie: if one card writes a 0 bit and another
+ * writes a 1 bit then the host sees a 0. At the end of the cycle; each card
+ * compares the data on the bus; if there is a difference then that card goes
+ * into ID_WAIT state again). In the meantime; one bit of data is returned in
+ * the AX register which is conveniently returned to us by inb(). Hence; we
+ * read 16 times getting one bit of data with each read.
+ */
+static int
+get_eeprom_data(int id_port, int offset)
+{
+ int i, data = 0;
+ outb(0x80 + offset, id_port);
+ /* Do we really need this wait? Won't be noticeable anyway */
+ udelay(10000);
+ for (i = 0; i < 16; i++)
+ data = (data << 1) | (inw(id_port) & 1);
+ return (data);
+}
+
+static void __t509_disable(void)
+{
+ outb(0xc0, EP_ID_PORT);
+}
+
+static void t509_disable(struct dev *dev)
+{
+ struct nic *nic = (struct nic *)dev;
+ /* reset and disable merge */
+ t509_reset(nic);
+ __t509_disable();
+}
+
+static void t509_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/**************************************************************************
+ETH_PROBE - Look for an adapter
+***************************************************************************/
+#ifdef INCLUDE_3C529
+static int t529_probe(struct dev *dev, unsigned short *probe_addrs __unused)
+#else
+static int t509_probe(struct dev *dev, unsigned short *probe_addrs __unused)
+#endif
+{
+ struct nic *nic = (struct nic *)dev;
+ /* common variables */
+ int i;
+ int failcount;
+
+#ifdef INCLUDE_3C529
+ struct el3_mca_adapters_struct *mcafound = NULL;
+ int mca_pos4 = 0, mca_pos5 = 0, mca_irq = 0;
+#endif
+
+ __t509_disable(); /* in case board was active */
+
+ for (failcount = 0; failcount < 100; failcount++) {
+ int data, j, io_base, id_port;
+ unsigned short k;
+ int ep_current_tag;
+ short *p;
+#ifdef INCLUDE_3C529
+ int curboard;
+#endif
+
+ id_port = EP_ID_PORT;
+ ep_current_tag = EP_LAST_TAG + 1;
+
+ /*********************************************************
+ Search for 3Com 509 card
+ ***********************************************************/
+#ifdef INCLUDE_3C529
+ /*
+ * XXX: We should really check to make sure we have an MCA
+ * bus controller before going ahead with this...
+ *
+ * For now, we avoid any hassle by making it a compile
+ * time option.
+ *
+ */
+ /* printf("\nWarning: Assuming presence of MCA bus\n"); */
+
+ /* Make sure motherboard setup is off */
+ outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
+
+ /* Cycle through slots */
+ for(curboard=0; curboard<MCA_MAX_SLOT_NR; curboard++) {
+ int boardid;
+ int curcard;
+
+ outb_p(0x8|(curboard&0xf), MCA_ADAPTER_SETUP_REG);
+
+ boardid = inb_p(MCA_POS_REG(0));
+ boardid += inb_p(MCA_POS_REG(1)) << 8;
+
+ curcard = 0;
+ while (el3_mca_adapters[curcard].name) {
+ if (el3_mca_adapters[curcard].id == boardid) {
+ mcafound = &el3_mca_adapters[curcard];
+
+ mca_pos4 = inb_p(MCA_POS_REG(4));
+ mca_pos5 = inb_p(MCA_POS_REG(5));
+
+ goto donewithdetect;
+ }
+ else
+ curcard++;
+ }
+
+ }
+ donewithdetect:
+ /* Kill all setup modes */
+ outb_p(0, MCA_ADAPTER_SETUP_REG);
+
+ if (mcafound) {
+ eth_nic_base = ((short)((mca_pos4&0xfc)|0x02)) << 8;
+ mca_irq = mca_pos5 & 0x0f;
+ ep_current_tag--;
+ }
+ else
+ /*printf("MCA Card not found\n")*/;
+#endif
+ /* Look for the EISA boards, leave them activated */
+ /* search for the first card, ignore all others */
+ for(j = 1; j < 16; j++) {
+ io_base = (j * EP_EISA_START) | EP_EISA_W0;
+ if (inw(io_base + EP_W0_MFG_ID) != MFG_ID)
+ continue;
+
+ /* we must have found 0x1f if the board is EISA configurated */
+ if ((inw(io_base + EP_W0_ADDRESS_CFG) & 0x1f) != 0x1f)
+ continue;
+
+ /* Reset and Enable the card */
+ outb(W0_P4_CMD_RESET_ADAPTER, io_base + EP_W0_CONFIG_CTRL);
+ udelay(1000); /* Must wait 800 µs, be conservative */
+ outb(W0_P4_CMD_ENABLE_ADAPTER, io_base + EP_W0_CONFIG_CTRL);
+
+ /*
+ * Once activated, all the registers are mapped in the range
+ * x000 - x00F, where x is the slot number.
+ */
+ eth_nic_base = j * EP_EISA_START;
+ break;
+ }
+ ep_current_tag--;
+
+ /* Look for the ISA boards. Init and leave them actived */
+ /* search for the first card, ignore all others */
+ outb(0xc0, id_port); /* Global reset */
+ udelay(1000); /* wait 1 ms */
+ for (i = 0; i < EP_MAX_BOARDS; i++) {
+ outb(0, id_port);
+ outb(0, id_port);
+ send_ID_sequence(id_port);
+
+ data = get_eeprom_data(id_port, EEPROM_MFG_ID);
+ if (data != MFG_ID)
+ break;
+
+ /* resolve contention using the Ethernet address */
+ for (j = 0; j < 3; j++)
+ data = get_eeprom_data(id_port, j);
+
+ eth_nic_base =
+ (get_eeprom_data(id_port, EEPROM_ADDR_CFG) & 0x1f) * 0x10 + 0x200;
+ outb(ep_current_tag, id_port); /* tags board */
+ outb(ACTIVATE_ADAPTER_TO_CONFIG, id_port);
+ ep_current_tag--;
+ break;
+ }
+
+ if (i >= EP_MAX_BOARDS)
+ goto no3c509;
+
+ /*
+ * The iobase was found and MFG_ID was 0x6d50. PROD_ID should be
+ * 0x9[0-f]50
+ */
+ GO_WINDOW(0);
+ k = get_e(EEPROM_PROD_ID);
+#ifdef INCLUDE_3C529
+ /*
+ * On MCA, the PROD_ID matches the MCA card ID (POS0+POS1)
+ */
+ if (mcafound) {
+ if (mcafound->id != k) {
+ printf("MCA: PROD_ID in EEPROM does not match MCA card ID! (%hX != %hX)\n", k, mcafound->id);
+ goto no3c509;
+ }
+ } else { /* for ISA/EISA */
+ if ((k & 0xf0ff) != (PROD_ID & 0xf0ff))
+ goto no3c509;
+ }
+#else
+ if ((k & 0xf0ff) != (PROD_ID & 0xf0ff))
+ goto no3c509;
+#endif
+
+#ifdef INCLUDE_3C529
+ if (mcafound) {
+ printf("%s board found on MCA at %#hx IRQ %d -",
+ mcafound->name, eth_nic_base, mca_irq);
+ } else {
+#endif
+ if(eth_nic_base >= EP_EISA_START)
+ printf("3C5x9 board on EISA at %#hx - ",eth_nic_base);
+ else
+ printf("3C5x9 board on ISA at %#hx - ",eth_nic_base);
+#ifdef INCLUDE_3C529
+ }
+#endif
+
+ /* test for presence of connectors */
+ i = inw(IS_BASE + EP_W0_CONFIG_CTRL);
+ j = (inw(IS_BASE + EP_W0_ADDRESS_CFG) >> 14) & 0x3;
+
+ switch(j) {
+ case 0:
+ if (i & IS_UTP) {
+ printf("10baseT\n");
+ connector = utp;
+ }
+ else {
+ printf("10baseT not present\n");
+ goto no3c509;
+ }
+ break;
+ case 1:
+ if (i & IS_AUI)
+ printf("10base5\n");
+ else {
+ printf("10base5 not present\n");
+ goto no3c509;
+ }
+ break;
+ case 3:
+ if (i & IS_BNC) {
+ printf("10base2\n");
+ connector = bnc;
+ }
+ else {
+ printf("10base2 not present\n");
+ goto no3c509;
+ }
+ break;
+ default:
+ printf("unknown connector\n");
+ goto no3c509;
+ }
+ /*
+ * Read the station address from the eeprom
+ */
+ p = (unsigned short *) nic->node_addr;
+ for (i = 0; i < ETH_ALEN / 2; i++) {
+ GO_WINDOW(0);
+ p[i] = htons(get_e(i));
+ GO_WINDOW(2);
+ outw(ntohs(p[i]), BASE + EP_W2_ADDR_0 + (i * 2));
+ }
+ printf("Ethernet address: %!\n", nic->node_addr);
+ t509_reset(nic);
+
+ nic->irqno = 0;
+ nic->ioaddr = eth_nic_base;
+
+ dev->disable = t509_disable;
+ nic->poll = t509_poll;
+ nic->transmit = t509_transmit;
+ nic->irq = t509_irq;
+
+ /* Based on PnP ISA map */
+ dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
+ dev->devid.device_id = htons(0x80f7);
+ return 1;
+no3c509:
+ continue;
+ /* printf("(probe fail)"); */
+ }
+ return 0;
+}
+
+#ifdef INCLUDE_3C509
+static struct isa_driver t509_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "3C509",
+ .probe = t509_probe,
+ .ioaddrs = 0,
+};
+#endif
+
+#ifdef INCLUDE_3C529
+static struct isa_driver t529_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "3C529",
+ .probe = t529_probe,
+ .ioaddrs = 0,
+};
+#endif
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/src/drivers/net/3c509.h b/src/drivers/net/3c509.h
new file mode 100644
index 00000000..243ba898
--- /dev/null
+++ b/src/drivers/net/3c509.h
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2. The name
+ * of the author may not be used to endorse or promote products derived from
+ * this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * if_epreg.h,v 1.4 1994/11/13 10:12:37 gibbs Exp Modified by:
+ *
+ October 2, 1994
+
+ Modified by: Andres Vega Garcia
+
+ INRIA - Sophia Antipolis, France
+ e-mail: avega@sophia.inria.fr
+ finger: avega@pax.inria.fr
+
+ */
+
+/*
+ * Ethernet software status per interface.
+ */
+/*
+ * Some global constants
+ */
+
+#define TX_INIT_RATE 16
+#define TX_INIT_MAX_RATE 64
+#define RX_INIT_LATENCY 64
+#define RX_INIT_EARLY_THRESH 64
+#define MIN_RX_EARLY_THRESHF 16 /* not less than ether_header */
+#define MIN_RX_EARLY_THRESHL 4
+
+#define EEPROMSIZE 0x40
+#define MAX_EEPROMBUSY 1000
+#define EP_LAST_TAG 0xd7
+#define EP_MAX_BOARDS 16
+#ifndef EP_ID_PORT
+#define EP_ID_PORT 0x100
+#endif
+
+/*
+ * some macros to acces long named fields
+ */
+#define IS_BASE (eth_nic_base)
+#define BASE (eth_nic_base)
+
+/*
+ * Commands to read/write EEPROM trough EEPROM command register (Window 0,
+ * Offset 0xa)
+ */
+#define EEPROM_CMD_RD 0x0080 /* Read: Address required (5 bits) */
+#define EEPROM_CMD_WR 0x0040 /* Write: Address required (5 bits) */
+#define EEPROM_CMD_ERASE 0x00c0 /* Erase: Address required (5 bits) */
+#define EEPROM_CMD_EWEN 0x0030 /* Erase/Write Enable: No data required */
+
+#define EEPROM_BUSY (1<<15)
+#define EEPROM_TST_MODE (1<<14)
+
+/*
+ * Some short functions, worth to let them be a macro
+ */
+#define is_eeprom_busy(b) (inw((b)+EP_W0_EEPROM_COMMAND)&EEPROM_BUSY)
+#define GO_WINDOW(x) outw(WINDOW_SELECT|(x), BASE+EP_COMMAND)
+
+/**************************************************************************
+ *
+ * These define the EEPROM data structure. They are used in the probe
+ * function to verify the existance of the adapter after having sent
+ * the ID_Sequence.
+ *
+ * There are others but only the ones we use are defined here.
+ *
+ **************************************************************************/
+
+#define EEPROM_NODE_ADDR_0 0x0 /* Word */
+#define EEPROM_NODE_ADDR_1 0x1 /* Word */
+#define EEPROM_NODE_ADDR_2 0x2 /* Word */
+#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */
+#define EEPROM_MFG_ID 0x7 /* 0x6d50 */
+#define EEPROM_ADDR_CFG 0x8 /* Base addr */
+#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */
+
+/**************************************************************************
+ *
+ * These are the registers for the 3Com 3c509 and their bit patterns when
+ * applicable. They have been taken out the the "EtherLink III Parallel
+ * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual
+ * from 3com.
+ *
+ **************************************************************************/
+
+#define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a
+ * command reg. */
+#define EP_STATUS 0x0e /* Read. BASE+0x0e is always status
+ * reg. */
+#define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window
+ * reg. */
+/*
+ * Window 0 registers. Setup.
+ */
+/* Write */
+#define EP_W0_EEPROM_DATA 0x0c
+#define EP_W0_EEPROM_COMMAND 0x0a
+#define EP_W0_RESOURCE_CFG 0x08
+#define EP_W0_ADDRESS_CFG 0x06
+#define EP_W0_CONFIG_CTRL 0x04
+/* Read */
+#define EP_W0_PRODUCT_ID 0x02
+#define EP_W0_MFG_ID 0x00
+
+/*
+ * Window 1 registers. Operating Set.
+ */
+/* Write */
+#define EP_W1_TX_PIO_WR_2 0x02
+#define EP_W1_TX_PIO_WR_1 0x00
+/* Read */
+#define EP_W1_FREE_TX 0x0c
+#define EP_W1_TX_STATUS 0x0b /* byte */
+#define EP_W1_TIMER 0x0a /* byte */
+#define EP_W1_RX_STATUS 0x08
+#define EP_W1_RX_PIO_RD_2 0x02
+#define EP_W1_RX_PIO_RD_1 0x00
+
+/*
+ * Window 2 registers. Station Address Setup/Read
+ */
+/* Read/Write */
+#define EP_W2_ADDR_5 0x05
+#define EP_W2_ADDR_4 0x04
+#define EP_W2_ADDR_3 0x03
+#define EP_W2_ADDR_2 0x02
+#define EP_W2_ADDR_1 0x01
+#define EP_W2_ADDR_0 0x00
+
+/*
+ * Window 3 registers. FIFO Management.
+ */
+/* Read */
+#define EP_W3_FREE_TX 0x0c
+#define EP_W3_FREE_RX 0x0a
+
+/*
+ * Window 4 registers. Diagnostics.
+ */
+/* Read/Write */
+#define EP_W4_MEDIA_TYPE 0x0a
+#define EP_W4_CTRLR_STATUS 0x08
+#define EP_W4_NET_DIAG 0x06
+#define EP_W4_FIFO_DIAG 0x04
+#define EP_W4_HOST_DIAG 0x02
+#define EP_W4_TX_DIAG 0x00
+
+/*
+ * Window 5 Registers. Results and Internal status.
+ */
+/* Read */
+#define EP_W5_READ_0_MASK 0x0c
+#define EP_W5_INTR_MASK 0x0a
+#define EP_W5_RX_FILTER 0x08
+#define EP_W5_RX_EARLY_THRESH 0x06
+#define EP_W5_TX_AVAIL_THRESH 0x02
+#define EP_W5_TX_START_THRESH 0x00
+
+/*
+ * Window 6 registers. Statistics.
+ */
+/* Read/Write */
+#define TX_TOTAL_OK 0x0c
+#define RX_TOTAL_OK 0x0a
+#define TX_DEFERRALS 0x08
+#define RX_FRAMES_OK 0x07
+#define TX_FRAMES_OK 0x06
+#define RX_OVERRUNS 0x05
+#define TX_COLLISIONS 0x04
+#define TX_AFTER_1_COLLISION 0x03
+#define TX_AFTER_X_COLLISIONS 0x02
+#define TX_NO_SQE 0x01
+#define TX_CD_LOST 0x00
+
+/****************************************
+ *
+ * Register definitions.
+ *
+ ****************************************/
+
+/*
+ * Command register. All windows.
+ *
+ * 16 bit register.
+ * 15-11: 5-bit code for command to be executed.
+ * 10-0: 11-bit arg if any. For commands with no args;
+ * this can be set to anything.
+ */
+#define GLOBAL_RESET (unsigned short) 0x0000 /* Wait at least 1ms
+ * after issuing */
+#define WINDOW_SELECT (unsigned short) (0x1<<11)
+#define START_TRANSCEIVER (unsigned short) (0x2<<11) /* Read ADDR_CFG reg to
+ * determine whether
+ * this is needed. If
+ * so; wait 800 uSec
+ * before using trans-
+ * ceiver. */
+#define RX_DISABLE (unsigned short) (0x3<<11) /* state disabled on
+ * power-up */
+#define RX_ENABLE (unsigned short) (0x4<<11)
+#define RX_RESET (unsigned short) (0x5<<11)
+#define RX_DISCARD_TOP_PACK (unsigned short) (0x8<<11)
+#define TX_ENABLE (unsigned short) (0x9<<11)
+#define TX_DISABLE (unsigned short) (0xa<<11)
+#define TX_RESET (unsigned short) (0xb<<11)
+#define REQ_INTR (unsigned short) (0xc<<11)
+#define SET_INTR_MASK (unsigned short) (0xe<<11)
+#define SET_RD_0_MASK (unsigned short) (0xf<<11)
+#define SET_RX_FILTER (unsigned short) (0x10<<11)
+#define FIL_INDIVIDUAL (unsigned short) (0x1)
+#define FIL_GROUP (unsigned short) (0x2)
+#define FIL_BRDCST (unsigned short) (0x4)
+#define FIL_ALL (unsigned short) (0x8)
+#define SET_RX_EARLY_THRESH (unsigned short) (0x11<<11)
+#define SET_TX_AVAIL_THRESH (unsigned short) (0x12<<11)
+#define SET_TX_START_THRESH (unsigned short) (0x13<<11)
+#define STATS_ENABLE (unsigned short) (0x15<<11)
+#define STATS_DISABLE (unsigned short) (0x16<<11)
+#define STOP_TRANSCEIVER (unsigned short) (0x17<<11)
+/*
+ * The following C_* acknowledge the various interrupts. Some of them don't
+ * do anything. See the manual.
+ */
+#define ACK_INTR (unsigned short) (0x6800)
+#define C_INTR_LATCH (unsigned short) (ACK_INTR|0x1)
+#define C_CARD_FAILURE (unsigned short) (ACK_INTR|0x2)
+#define C_TX_COMPLETE (unsigned short) (ACK_INTR|0x4)
+#define C_TX_AVAIL (unsigned short) (ACK_INTR|0x8)
+#define C_RX_COMPLETE (unsigned short) (ACK_INTR|0x10)
+#define C_RX_EARLY (unsigned short) (ACK_INTR|0x20)
+#define C_INT_RQD (unsigned short) (ACK_INTR|0x40)
+#define C_UPD_STATS (unsigned short) (ACK_INTR|0x80)
+
+/*
+ * Status register. All windows.
+ *
+ * 15-13: Window number(0-7).
+ * 12: Command_in_progress.
+ * 11: reserved.
+ * 10: reserved.
+ * 9: reserved.
+ * 8: reserved.
+ * 7: Update Statistics.
+ * 6: Interrupt Requested.
+ * 5: RX Early.
+ * 4: RX Complete.
+ * 3: TX Available.
+ * 2: TX Complete.
+ * 1: Adapter Failure.
+ * 0: Interrupt Latch.
+ */
+#define S_INTR_LATCH (unsigned short) (0x1)
+#define S_CARD_FAILURE (unsigned short) (0x2)
+#define S_TX_COMPLETE (unsigned short) (0x4)
+#define S_TX_AVAIL (unsigned short) (0x8)
+#define S_RX_COMPLETE (unsigned short) (0x10)
+#define S_RX_EARLY (unsigned short) (0x20)
+#define S_INT_RQD (unsigned short) (0x40)
+#define S_UPD_STATS (unsigned short) (0x80)
+#define S_5_INTS (S_CARD_FAILURE|S_TX_COMPLETE|\
+ S_TX_AVAIL|S_RX_COMPLETE|S_RX_EARLY)
+#define S_COMMAND_IN_PROGRESS (unsigned short) (0x1000)
+
+/*
+ * FIFO Registers.
+ * RX Status. Window 1/Port 08
+ *
+ * 15: Incomplete or FIFO empty.
+ * 14: 1: Error in RX Packet 0: Incomplete or no error.
+ * 13-11: Type of error.
+ * 1000 = Overrun.
+ * 1011 = Run Packet Error.
+ * 1100 = Alignment Error.
+ * 1101 = CRC Error.
+ * 1001 = Oversize Packet Error (>1514 bytes)
+ * 0010 = Dribble Bits.
+ * (all other error codes, no errors.)
+ *
+ * 10-0: RX Bytes (0-1514)
+ */
+#define ERR_RX_INCOMPLETE (unsigned short) (0x1<<15)
+#define ERR_RX (unsigned short) (0x1<<14)
+#define ERR_RX_OVERRUN (unsigned short) (0x8<<11)
+#define ERR_RX_RUN_PKT (unsigned short) (0xb<<11)
+#define ERR_RX_ALIGN (unsigned short) (0xc<<11)
+#define ERR_RX_CRC (unsigned short) (0xd<<11)
+#define ERR_RX_OVERSIZE (unsigned short) (0x9<<11)
+#define ERR_RX_DRIBBLE (unsigned short) (0x2<<11)
+
+/*
+ * FIFO Registers.
+ * TX Status. Window 1/Port 0B
+ *
+ * Reports the transmit status of a completed transmission. Writing this
+ * register pops the transmit completion stack.
+ *
+ * Window 1/Port 0x0b.
+ *
+ * 7: Complete
+ * 6: Interrupt on successful transmission requested.
+ * 5: Jabber Error (TP Only, TX Reset required. )
+ * 4: Underrun (TX Reset required. )
+ * 3: Maximum Collisions.
+ * 2: TX Status Overflow.
+ * 1-0: Undefined.
+ *
+ */
+#define TXS_COMPLETE 0x80
+#define TXS_SUCCES_INTR_REQ 0x40
+#define TXS_JABBER 0x20
+#define TXS_UNDERRUN 0x10
+#define TXS_MAX_COLLISION 0x8
+#define TXS_STATUS_OVERFLOW 0x4
+
+/*
+ * Configuration control register.
+ * Window 0/Port 04
+ */
+/* Read */
+#define IS_AUI (1<<13)
+#define IS_BNC (1<<12)
+#define IS_UTP (1<<9)
+/* Write */
+#define ENABLE_DRQ_IRQ 0x0001
+#define W0_P4_CMD_RESET_ADAPTER 0x4
+#define W0_P4_CMD_ENABLE_ADAPTER 0x1
+/*
+ * Media type and status.
+ * Window 4/Port 0A
+ */
+#define ENABLE_UTP 0xc0
+#define DISABLE_UTP 0x0
+
+/*
+ * Resource control register
+ */
+
+#define SET_IRQ(i) ( ((i)<<12) | 0xF00) /* set IRQ i */
+
+/*
+ * Receive status register
+ */
+
+#define RX_BYTES_MASK (unsigned short) (0x07ff)
+#define RX_ERROR 0x4000
+#define RX_INCOMPLETE 0x8000
+
+
+/*
+ * Misc defines for various things.
+ */
+#define ACTIVATE_ADAPTER_TO_CONFIG 0xff /* to the id_port */
+#define MFG_ID 0x6d50 /* in EEPROM and W0 ADDR_CONFIG */
+#define PROD_ID 0x9150
+
+#define AUI 0x1
+#define BNC 0x2
+#define UTP 0x4
+
+#define RX_BYTES_MASK (unsigned short) (0x07ff)
+
+ /* EISA support */
+#define EP_EISA_START 0x1000
+#define EP_EISA_W0 0x0c80
+
+#ifdef INCLUDE_3C529
+ /* MCA support */
+#define MCA_MOTHERBOARD_SETUP_REG 0x94
+#define MCA_ADAPTER_SETUP_REG 0x96
+#define MCA_MAX_SLOT_NR 8
+#define MCA_POS_REG(n) (0x100+(n))
+#endif
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/src/drivers/net/3c515.c b/src/drivers/net/3c515.c
new file mode 100644
index 00000000..f185acef
--- /dev/null
+++ b/src/drivers/net/3c515.c
@@ -0,0 +1,814 @@
+/*
+* 3c515.c -- 3COM 3C515 Fast Etherlink ISA 10/100BASE-TX driver for etherboot
+* Copyright (C) 2002 Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code:
+* Copyright (C) 1997-2002 Donald Becker 3c515.c: A 3Com ISA EtherLink XL "Corkscrew" ethernet driver for linux.
+* Copyright (C) 2001 P.J.H.Fox (fox@roestock.demon.co.uk) ISAPNP Tools
+* Copyright (c) 2002 Jaroslav Kysela <perex@suse.cz> ISA Plug & Play support Linux Kernel
+* Copyright (C) 2000 Shusuke Nisiyama <shu@athena.qe.eng.hokudai.ac.jp> etherboot-5.0.5 3c595.c
+* Coptright (C) 1995 Martin Renters etherboot-5.0.5 3c509.c
+* Copyright (C) 1999 LightSys Technology Services, Inc. etherboot-5.0.5 3c90x.c
+* Portions Copyright (C) 1999 Steve Smith etherboot-5.0.5 3c90x.c
+*
+* The probe and reset functions and defines are direct copies from the
+* Becker code modified where necessary to make it work for etherboot
+*
+* The poll and transmit functions either contain code from or were written by referencing
+* the above referenced etherboot drivers. This driver would not have been
+* possible without this prior work
+*
+* REVISION HISTORY:
+* ================
+* v0.10 4-17-2002 TJL Initial implementation.
+* v0.11 4-17-2002 TJL Cleanup of the code
+* v0.12 4-26-2002 TJL Added ISA Plug and Play for Non-PNP Bioses
+* v0.13 6-10-2002 TJL Fixed ISA_PNP MAC Address problem
+* v0.14 9-23-2003 TJL Replaced delay with currticks
+*
+* Indent Options: indent -kr -i8
+* *********************************************************/
+
+
+#define ISA_PNP
+/*#define EDEBUG1*/
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+#include "isa.h"
+#include "timer.h"
+
+#ifdef ISA_PNP
+
+static void t3c515_wait(unsigned int nticks)
+{
+ unsigned int to = currticks() + nticks;
+ while (currticks() < to)
+ /* wait */ ;
+}
+#endif
+/* TJL definations */
+#define HZ 100
+#define u16 unsigned short
+#define u32 unsigned long
+#define s16 signed short
+#define s32 signed long
+static unsigned short eth_nic_base;
+#define BASE (eth_nic_base)
+static int if_port;
+struct corkscrew_private *vp;
+/* Brought directly from 3c515.c by Becker */
+#define CORKSCREW 1
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt.
+static int max_interrupt_work = 20;
+*/
+
+/* Enable the automatic media selection code -- usually set. */
+#define AUTOMEDIA 1
+
+/* Allow the use of fragment bus master transfers instead of only
+ programmed-I/O for Vortex cards. Full-bus-master transfers are always
+ enabled by default on Boomerang cards. If VORTEX_BUS_MASTER is defined,
+ the feature may be turned on using 'options'. */
+#define VORTEX_BUS_MASTER
+
+/* A few values that may be tweaked. */
+/* Keep the ring sizes a power of two for efficiency. */
+#define TX_RING_SIZE 16
+#define RX_RING_SIZE 16
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */
+
+/* "Knobs" for adjusting internal parameters. */
+/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
+#define DRIVER_DEBUG 1
+/* Some values here only for performance evaluation and path-coverage
+ debugging.
+static int rx_nocopy, rx_copy, queued_packet;
+*/
+
+#ifdef DRIVER_DEBUG
+static int corkscrew_debug = DRIVER_DEBUG;
+#else
+static int corkscrew_debug = 1;
+#endif
+
+#define CORKSCREW_ID 10
+
+#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
+#define EL3_CMD 0x0e
+#define EL3_STATUS 0x0e
+#define RX_BYTES_MASK (unsigned short) (0x07ff)
+
+enum corkscrew_cmd {
+ TotalReset = 0 << 11, SelectWindow = 1 << 11, StartCoax = 2 << 11,
+ RxDisable = 3 << 11, RxEnable = 4 << 11, RxReset = 5 << 11,
+ UpStall = 6 << 11, UpUnstall = (6 << 11) + 1,
+ DownStall = (6 << 11) + 2, DownUnstall = (6 << 11) + 3,
+ RxDiscard = 8 << 11, TxEnable = 9 << 11, TxDisable =
+ 10 << 11, TxReset = 11 << 11,
+ FakeIntr = 12 << 11, AckIntr = 13 << 11, SetIntrEnb = 14 << 11,
+ SetStatusEnb = 15 << 11, SetRxFilter = 16 << 11, SetRxThreshold =
+ 17 << 11,
+ SetTxThreshold = 18 << 11, SetTxStart = 19 << 11,
+ StartDMAUp = 20 << 11, StartDMADown = (20 << 11) + 1, StatsEnable =
+ 21 << 11,
+ StatsDisable = 22 << 11, StopCoax = 23 << 11,
+};
+
+/* The SetRxFilter command accepts the following classes: */
+enum RxFilter {
+ RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8
+};
+
+/* Bits in the general status register. */
+enum corkscrew_status {
+ IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
+ TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
+ IntReq = 0x0040, StatsFull = 0x0080,
+ DMADone = 1 << 8, DownComplete = 1 << 9, UpComplete = 1 << 10,
+ DMAInProgress = 1 << 11, /* DMA controller is still busy. */
+ CmdInProgress = 1 << 12, /* EL3_CMD is still busy. */
+};
+
+/* Register window 1 offsets, the window used in normal operation.
+ On the Corkscrew this window is always mapped at offsets 0x10-0x1f. */
+enum Window1 {
+ TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14,
+ RxStatus = 0x18, Timer = 0x1A, TxStatus = 0x1B,
+ TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */
+};
+enum Window0 {
+ Wn0IRQ = 0x08,
+#if defined(CORKSCREW)
+ Wn0EepromCmd = 0x200A, /* Corkscrew EEPROM command register. */
+ Wn0EepromData = 0x200C, /* Corkscrew EEPROM results register. */
+#else
+ Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */
+ Wn0EepromData = 12, /* Window 0: EEPROM results register. */
+#endif
+};
+enum Win0_EEPROM_bits {
+ EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
+ EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */
+ EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */
+};
+
+enum Window3 { /* Window 3: MAC/config bits. */
+ Wn3_Config = 0, Wn3_MAC_Ctrl = 6, Wn3_Options = 8,
+};
+union wn3_config {
+ int i;
+ struct w3_config_fields {
+ unsigned int ram_size:3, ram_width:1, ram_speed:2,
+ rom_size:2;
+ int pad8:8;
+ unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1,
+ autoselect:1;
+ int pad24:7;
+ } u;
+};
+
+enum Window4 {
+ Wn4_NetDiag = 6, Wn4_Media = 10, /* Window 4: Xcvr/media bits. */
+};
+enum Win4_Media_bits {
+ Media_SQE = 0x0008, /* Enable SQE error counting for AUI. */
+ Media_10TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */
+ Media_Lnk = 0x0080, /* Enable just link beat for 100TX/100FX. */
+ Media_LnkBeat = 0x0800,
+};
+enum Window7 { /* Window 7: Bus Master control. */
+ Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
+};
+
+/* Boomerang-style bus master control registers. Note ISA aliases! */
+enum MasterCtrl {
+ PktStatus = 0x400, DownListPtr = 0x404, FragAddr = 0x408, FragLen =
+ 0x40c,
+ TxFreeThreshold = 0x40f, UpPktStatus = 0x410, UpListPtr = 0x418,
+};
+
+/* The Rx and Tx descriptor lists.
+ Caution Alpha hackers: these types are 32 bits! Note also the 8 byte
+ alignment contraint on tx_ring[] and rx_ring[]. */
+struct boom_rx_desc {
+ u32 next;
+ s32 status;
+ u32 addr;
+ s32 length;
+};
+
+/* Values for the Rx status entry. */
+enum rx_desc_status {
+ RxDComplete = 0x00008000, RxDError = 0x4000,
+ /* See boomerang_rx() for actual error bits */
+};
+
+struct boom_tx_desc {
+ u32 next;
+ s32 status;
+ u32 addr;
+ s32 length;
+};
+
+struct corkscrew_private {
+ const char *product_name;
+ struct net_device *next_module;
+ /* The Rx and Tx rings are here to keep them quad-word-aligned. */
+ struct boom_rx_desc rx_ring[RX_RING_SIZE];
+ struct boom_tx_desc tx_ring[TX_RING_SIZE];
+ /* The addresses of transmit- and receive-in-place skbuffs. */
+ struct sk_buff *rx_skbuff[RX_RING_SIZE];
+ struct sk_buff *tx_skbuff[TX_RING_SIZE];
+ unsigned int cur_rx, cur_tx; /* The next free ring entry */
+ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
+ struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */
+ int capabilities; /* Adapter capabilities word. */
+ int options; /* User-settable misc. driver options. */
+ int last_rx_packets; /* For media autoselection. */
+ unsigned int available_media:8, /* From Wn3_Options */
+ media_override:3, /* Passed-in media type. */
+ default_media:3, /* Read from the EEPROM. */
+ full_duplex:1, autoselect:1, bus_master:1, /* Vortex can only do a fragment bus-m. */
+ full_bus_master_tx:1, full_bus_master_rx:1, /* Boomerang */
+ tx_full:1;
+};
+
+/* The action to take with a media selection timer tick.
+ Note that we deviate from the 3Com order by checking 10base2 before AUI.
+ */
+enum xcvr_types {
+ XCVR_10baseT =
+ 0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
+ XCVR_100baseFx, XCVR_MII = 6, XCVR_Default = 8,
+};
+
+static struct media_table {
+ char *name;
+ unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */
+ mask:8, /* The transceiver-present bit in Wn3_Config. */
+ next:8; /* The media type to try next. */
+ short wait; /* Time before we check media status. */
+} media_tbl[] = {
+ {
+ "10baseT", Media_10TP, 0x08, XCVR_10base2, (14 * HZ) / 10}
+ , {
+ "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1 * HZ) / 10}
+ , {
+ "undefined", 0, 0x80, XCVR_10baseT, 10000}
+ , {
+ "10base2", 0, 0x10, XCVR_AUI, (1 * HZ) / 10}
+ , {
+ "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx,
+ (14 * HZ) / 10}
+ , {
+ "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14 * HZ) / 10}
+ , {
+ "MII", 0, 0x40, XCVR_10baseT, 3 * HZ}
+ , {
+ "undefined", 0, 0x01, XCVR_10baseT, 10000}
+ , {
+ "Default", 0, 0xFF, XCVR_10baseT, 10000}
+,};
+
+/* TILEG Modified to remove reference to dev */
+static int corkscrew_found_device(int ioaddr, int irq, int product_index,
+ int options, struct nic *nic);
+static int corkscrew_probe1(int ioaddr, int irq, int product_index,
+ struct nic *nic);
+
+/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
+/* Note: this is the only limit on the number of cards supported!! */
+static int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1, };
+
+/* End Brought directly from 3c515.c by Becker */
+
+/**************************************************************************
+RESET - Reset adapter
+***************************************************************************/
+static void t515_reset(struct nic *nic)
+{
+ int ioaddr = BASE;
+ union wn3_config config;
+ int i;
+
+ /* Before initializing select the active media port. */
+ EL3WINDOW(3);
+ if (vp->full_duplex)
+ outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */
+ config.i = inl(ioaddr + Wn3_Config);
+
+ if (vp->media_override != 7) {
+ if (corkscrew_debug > 1)
+ printf("Media override to transceiver %d (%s).\n",
+ vp->media_override,
+ media_tbl[vp->media_override].name);
+ if_port = vp->media_override;
+ } else if (vp->autoselect) {
+ /* Find first available media type, starting with 100baseTx. */
+ if_port = 4;
+ while (!(vp->available_media & media_tbl[if_port].mask))
+ if_port = media_tbl[if_port].next;
+
+ if (corkscrew_debug > 1)
+ printf("Initial media type %s.\n",
+ media_tbl[if_port].name);
+ } else
+ if_port = vp->default_media;
+
+ config.u.xcvr = if_port;
+ outl(config.i, ioaddr + Wn3_Config);
+
+ if (corkscrew_debug > 1) {
+ printf("corkscrew_open() InternalConfig 0x%hX.\n",
+ config.i);
+ }
+
+ outw(TxReset, ioaddr + EL3_CMD);
+ for (i = 20; i >= 0; i--)
+ if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
+ break;
+
+ outw(RxReset, ioaddr + EL3_CMD);
+ /* Wait a few ticks for the RxReset command to complete. */
+ for (i = 20; i >= 0; i--)
+ if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
+ break;
+
+ outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
+
+ if (corkscrew_debug > 1) {
+ EL3WINDOW(4);
+ printf("FIXME: fix print for irq, not 9");
+ printf("corkscrew_open() irq %d media status 0x%hX.\n",
+ 9, inw(ioaddr + Wn4_Media));
+ }
+
+ /* Set the station address and mask in window 2 each time opened. */
+ EL3WINDOW(2);
+ for (i = 0; i < 6; i++)
+ outb(nic->node_addr[i], ioaddr + i);
+ for (; i < 12; i += 2)
+ outw(0, ioaddr + i);
+
+ if (if_port == 3)
+ /* Start the thinnet transceiver. We should really wait 50ms... */
+ outw(StartCoax, ioaddr + EL3_CMD);
+ EL3WINDOW(4);
+ outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP | Media_SQE)) |
+ media_tbl[if_port].media_bits, ioaddr + Wn4_Media);
+
+ /* Switch to the stats window, and clear all stats by reading. */
+/* outw(StatsDisable, ioaddr + EL3_CMD);*/
+ EL3WINDOW(6);
+ for (i = 0; i < 10; i++)
+ inb(ioaddr + i);
+ inw(ioaddr + 10);
+ inw(ioaddr + 12);
+ /* New: On the Vortex we must also clear the BadSSD counter. */
+ EL3WINDOW(4);
+ inb(ioaddr + 12);
+ /* ..and on the Boomerang we enable the extra statistics bits. */
+ outw(0x0040, ioaddr + Wn4_NetDiag);
+
+ /* Switch to register set 7 for normal use. */
+ EL3WINDOW(7);
+
+ /* Temporarily left in place. If these FIXMEs are printed
+ it meand that special logic for that card may need to be added
+ see Becker's 3c515.c driver */
+ if (vp->full_bus_master_rx) { /* Boomerang bus master. */
+ printf("FIXME: Is this if necessary");
+ vp->cur_rx = vp->dirty_rx = 0;
+ if (corkscrew_debug > 2)
+ printf(" Filling in the Rx ring.\n");
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ printf("FIXME: Is this if necessary");
+ }
+ }
+ if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */
+ vp->cur_tx = vp->dirty_tx = 0;
+ outb(PKT_BUF_SZ >> 8, ioaddr + TxFreeThreshold); /* Room for a packet. */
+ /* Clear the Tx ring. */
+ for (i = 0; i < TX_RING_SIZE; i++)
+ vp->tx_skbuff[i] = 0;
+ outl(0, ioaddr + DownListPtr);
+ }
+ /* Set receiver mode: presumably accept b-case and phys addr only. */
+ outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
+ ioaddr + EL3_CMD);
+
+ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
+ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
+ /* Allow status bits to be seen. */
+ outw(SetStatusEnb | AdapterFailure | IntReq | StatsFull |
+ (vp->full_bus_master_tx ? DownComplete : TxAvailable) |
+ (vp->full_bus_master_rx ? UpComplete : RxComplete) |
+ (vp->bus_master ? DMADone : 0), ioaddr + EL3_CMD);
+ /* Ack all pending events, and set active indicator mask. */
+ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+ ioaddr + EL3_CMD);
+ outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
+ | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete,
+ ioaddr + EL3_CMD);
+
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int t515_poll(struct nic *nic, int retrieve)
+{
+ short status, cst;
+ register short rx_fifo;
+
+ cst = inw(BASE + EL3_STATUS);
+
+ if ((cst & RxComplete) == 0) {
+ /* Ack all pending events, and set active indicator mask. */
+ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+ BASE + EL3_CMD);
+ outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete |
+ StatsFull | (vp->
+ bus_master ? DMADone : 0) | UpComplete |
+ DownComplete, BASE + EL3_CMD);
+ return 0;
+ }
+ status = inw(BASE + RxStatus);
+
+ if (status & RxDError) {
+ printf("RxDError\n");
+ outw(RxDiscard, BASE + EL3_CMD);
+ return 0;
+ }
+
+ rx_fifo = status & RX_BYTES_MASK;
+ if (rx_fifo == 0)
+ return 0;
+
+ if ( ! retrieve ) return 1;
+
+#ifdef EDEBUG
+ printf("[l=%d", rx_fifo);
+#endif
+ insw(BASE + RX_FIFO, nic->packet, rx_fifo / 2);
+ if (rx_fifo & 1)
+ nic->packet[rx_fifo - 1] = inb(BASE + RX_FIFO);
+ nic->packetlen = rx_fifo;
+
+ while (1) {
+ status = inw(BASE + RxStatus);
+#ifdef EDEBUG
+ printf("0x%hX*", status);
+#endif
+ rx_fifo = status & RX_BYTES_MASK;
+
+ if (rx_fifo > 0) {
+ insw(BASE + RX_FIFO, nic->packet + nic->packetlen,
+ rx_fifo / 2);
+ if (rx_fifo & 1)
+ nic->packet[nic->packetlen + rx_fifo - 1] =
+ inb(BASE + RX_FIFO);
+ nic->packetlen += rx_fifo;
+#ifdef EDEBUG
+ printf("+%d", rx_fifo);
+#endif
+ }
+ if ((status & RxComplete) == 0) {
+#ifdef EDEBUG
+ printf("=%d", nic->packetlen);
+#endif
+ break;
+ }
+ udelay(1000);
+ }
+
+ /* acknowledge reception of packet */
+ outw(RxDiscard, BASE + EL3_CMD);
+ while (inw(BASE + EL3_STATUS) & CmdInProgress);
+#ifdef EDEBUG
+ {
+ unsigned short type = 0;
+ type = (nic->packet[12] << 8) | nic->packet[13];
+ if (nic->packet[0] + nic->packet[1] + nic->packet[2] +
+ nic->packet[3] + nic->packet[4] + nic->packet[5] ==
+ 0xFF * ETH_ALEN)
+ printf(",t=0x%hX,b]", type);
+ else
+ printf(",t=0x%hX]", type);
+ }
+#endif
+
+ return 1;
+}
+
+/*************************************************************************
+ 3Com 515 - specific routines
+**************************************************************************/
+static char padmap[] = {
+ 0, 3, 2, 1
+};
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void t515_transmit(struct nic *nic, const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p)
+{ /* Packet */
+ register int len;
+ int pad;
+ int status;
+
+#ifdef EDEBUG
+ printf("{l=%d,t=0x%hX}", s + ETH_HLEN, t);
+#endif
+
+ /* swap bytes of type */
+ t = htons(t);
+
+ len = s + ETH_HLEN; /* actual length of packet */
+ pad = padmap[len & 3];
+
+ /*
+ * The 3c515 automatically pads short packets to minimum ethernet length,
+ * but we drop packets that are too large. Perhaps we should truncate
+ * them instead?
+ Copied from 3c595. Is this true for the 3c515?
+ */
+ if (len + pad > ETH_FRAME_LEN) {
+ return;
+ }
+ /* drop acknowledgements */
+ while ((status = inb(BASE + TxStatus)) & TxComplete) {
+ /*if(status & (TXS_UNDERRUN|0x88|TXS_STATUS_OVERFLOW)) { */
+ outw(TxReset, BASE + EL3_CMD);
+ outw(TxEnable, BASE + EL3_CMD);
+/* } */
+
+ outb(0x0, BASE + TxStatus);
+ }
+
+ while (inw(BASE + TxFree) < len + pad + 4) {
+ /* no room in FIFO */
+ }
+
+ outw(len, BASE + TX_FIFO);
+ outw(0x0, BASE + TX_FIFO); /* Second dword meaningless */
+
+ /* write packet */
+ outsw(BASE + TX_FIFO, d, ETH_ALEN / 2);
+ outsw(BASE + TX_FIFO, nic->node_addr, ETH_ALEN / 2);
+ outw(t, BASE + TX_FIFO);
+ outsw(BASE + TX_FIFO, p, s / 2);
+
+ if (s & 1)
+ outb(*(p + s - 1), BASE + TX_FIFO);
+
+ while (pad--)
+ outb(0, BASE + TX_FIFO); /* Padding */
+
+ /* wait for Tx complete */
+ while ((inw(BASE + EL3_STATUS) & CmdInProgress) != 0);
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void t515_disable(struct dev *dev)
+{
+ struct nic *nic = (struct nic *) dev;
+
+ /* merge reset an disable */
+ t515_reset(nic);
+
+ /* This is a hack. Since ltsp worked on my
+ system without any disable functionality I
+ have no way to determine if this works */
+
+ /* Disable the receiver and transmitter. */
+ outw(RxDisable, BASE + EL3_CMD);
+ outw(TxDisable, BASE + EL3_CMD);
+
+ if (if_port == XCVR_10base2)
+ /* Turn off thinnet power. Green! */
+ outw(StopCoax, BASE + EL3_CMD);
+
+
+ outw(SetIntrEnb | 0x0000, BASE + EL3_CMD);
+#ifdef ISA_PNP
+ /*Deactivate */
+/* ACTIVATE;
+ WRITE_DATA(0);
+ */
+#endif
+ return;
+}
+
+static void t515_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+You should omit the last argument struct pci_device * for a non-PCI NIC
+***************************************************************************/
+void config_pnp_device(void);
+
+static int t515_probe(struct dev *dev,
+ unsigned short *probe_addrs __unused)
+{
+ struct nic *nic = (struct nic *) dev;
+ /* Direct copy from Beckers 3c515.c removing any ISAPNP sections */
+ int cards_found = 0;
+ static int ioaddr;
+#ifdef ISA_PNP
+ config_pnp_device();
+#endif
+ /* Check all locations on the ISA bus -- evil! */
+ for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) {
+ int irq;
+
+ /* Check the resource configuration for a matching ioaddr. */
+ if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0))
+ continue;
+ /* Verify by reading the device ID from the EEPROM. */
+ {
+ int timer;
+ outw(EEPROM_Read + 7, ioaddr + Wn0EepromCmd);
+ /* Pause for at least 162 us. for the read to take place. */
+ for (timer = 4; timer >= 0; timer--) {
+ t3c515_wait(1);
+ if ((inw(ioaddr + Wn0EepromCmd) & 0x0200)
+ == 0)
+ break;
+ }
+ if (inw(ioaddr + Wn0EepromData) != 0x6d50)
+ continue;
+ }
+ printf
+ ("3c515 Resource configuration register 0x%hX, DCR 0x%hX.\n",
+ inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));
+ irq = inw(ioaddr + 0x2002) & 15;
+ BASE = ioaddr;
+ corkscrew_found_device(BASE, irq, CORKSCREW_ID,
+ options[cards_found], nic);
+ cards_found++;
+ }
+ if (corkscrew_debug)
+ printf("%d 3c515 cards found.\n", cards_found);
+
+ if (cards_found > 0) {
+ t515_reset(nic);
+
+ nic->irqno = 0;
+ nic->ioaddr = BASE;
+
+ dev->disable = t515_disable;
+ nic->poll = t515_poll;
+ nic->transmit = t515_transmit;
+ nic->irq = t515_irq;
+
+ /* Based on PnP ISA map */
+ dev->devid.vendor_id = htons(ISAPNP_VENDOR('T', 'C', 'M'));
+ dev->devid.device_id = htons(0x5051);
+ return 1;
+ } else
+ return 0;
+
+}
+
+static int
+corkscrew_found_device(int ioaddr, int irq,
+ int product_index, int options, struct nic *nic)
+{
+ /* Direct copy from Becker 3c515.c with unecessary parts removed */
+ vp->product_name = "3c515";
+ vp->options = options;
+ if (options >= 0) {
+ vp->media_override =
+ ((options & 7) == 2) ? 0 : options & 7;
+ vp->full_duplex = (options & 8) ? 1 : 0;
+ vp->bus_master = (options & 16) ? 1 : 0;
+ } else {
+ vp->media_override = 7;
+ vp->full_duplex = 0;
+ vp->bus_master = 0;
+ }
+
+ corkscrew_probe1(ioaddr, irq, product_index, nic);
+ return 0;
+}
+
+static int
+corkscrew_probe1(int ioaddr, int irq, int product_index __unused,
+ struct nic *nic)
+{
+ unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */
+ int i;
+ ioaddr = BASE;
+
+ printf("3Com %s at 0x%hX, ", vp->product_name, ioaddr);
+
+ /* Read the station address from the EEPROM. */
+ EL3WINDOW(0);
+ for (i = 0; i < 0x18; i++) {
+ short *phys_addr = (short *) nic->node_addr;
+ int timer;
+ outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);
+ /* Pause for at least 162 us. for the read to take place. */
+ for (timer = 4; timer >= 0; timer--) {
+ t3c515_wait(1);
+ if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0)
+ break;
+ }
+ eeprom[i] = inw(ioaddr + Wn0EepromData);
+#ifdef EDEBUG1
+ printf("Value %d: %hX ", i, eeprom[i]);
+#endif
+ checksum ^= eeprom[i];
+ if (i < 3)
+ phys_addr[i] = htons(eeprom[i]);
+ }
+ checksum = (checksum ^ (checksum >> 8)) & 0xff;
+ if (checksum != 0x00)
+ printf(" ***INVALID CHECKSUM 0x%hX*** ", checksum);
+
+ printf("%!", nic->node_addr);
+ if (eeprom[16] == 0x11c7) { /* Corkscrew */
+
+ }
+ printf(", IRQ %d\n", irq);
+ /* Tell them about an invalid IRQ. */
+ if (corkscrew_debug && (irq <= 0 || irq > 15))
+ printf
+ (" *** Warning: this IRQ is unlikely to work! ***\n");
+
+ {
+ char *ram_split[] = { "5:3", "3:1", "1:1", "3:5" };
+ union wn3_config config;
+ EL3WINDOW(3);
+ vp->available_media = inw(ioaddr + Wn3_Options);
+ config.i = inl(ioaddr + Wn3_Config);
+ if (corkscrew_debug > 1)
+ printf
+ (" Internal config register is %4.4x, transceivers 0x%hX.\n",
+ config.i, inw(ioaddr + Wn3_Options));
+ printf
+ (" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
+ 8 << config.u.ram_size,
+ config.u.ram_width ? "word" : "byte",
+ ram_split[config.u.ram_split],
+ config.u.autoselect ? "autoselect/" : "",
+ media_tbl[config.u.xcvr].name);
+ if_port = config.u.xcvr;
+ vp->default_media = config.u.xcvr;
+ vp->autoselect = config.u.autoselect;
+ }
+ if (vp->media_override != 7) {
+ printf(" Media override to transceiver type %d (%s).\n",
+ vp->media_override,
+ media_tbl[vp->media_override].name);
+ if_port = vp->media_override;
+ }
+
+ vp->capabilities = eeprom[16];
+ vp->full_bus_master_tx = (vp->capabilities & 0x20) ? 1 : 0;
+ /* Rx is broken at 10mbps, so we always disable it. */
+ /* vp->full_bus_master_rx = 0; */
+ vp->full_bus_master_rx = (vp->capabilities & 0x20) ? 1 : 0;
+
+ return 0;
+}
+
+static struct isa_driver t515_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "3C515",
+ .probe = t515_probe,
+ .ioaddrs = 0,
+};
diff --git a/src/drivers/net/3c515.txt b/src/drivers/net/3c515.txt
new file mode 100644
index 00000000..8f7b3a72
--- /dev/null
+++ b/src/drivers/net/3c515.txt
@@ -0,0 +1,31 @@
+3c515.c -- 3COM 3C515 Fast Etherlink ISA 10/100BASE-TX driver for etherboot
+Copyright (C) 2002 Timothy Legge <tlegge@rogers.com>
+
+This driver is for the 3COM 3C515 Fast Etherlink ISA 10/100BASE-TX
+
+REVISION HISTORY:
+================
+v0.10 4-17-2002 TJL Initial implementation.
+v0.11 4-17-2002 TJL Cleanup of the code
+v0.12 4-26-2002 TJL Added ISA Plug and Play for Non-PNP Bioses
+v0.13 3-31-2003 TJL Fixed issue 1 and 2 below
+
+The driver is heavily based on the work of others are referenced in the 3c515.c file.
+
+ISA Plug and Play (ISAPNP) support has been added for Non-PNP Bioses. The ISAPNP code requires the defination of ISA_PNP as:
+
+#define ISA_PNP
+
+Issues:
+=======
+1) RESOLVED - When ISAPNP is defined, the etherboot probe is unable to find the card during the first probe. This is true even though the ISA PNP code actually found and activated the driver.
+
+2) RESOLVED - When ISA_PNP is defined, the etherboot probe finds the incorrect MAC address for the card. However, when the linux kernel boots and loads the linux 3c515 driver the correct MAC address is found. This means that with ISA_PNP defined, you require both MAC addresses defined in the /etc/dhcpd.conf file. The first MAC address allows the driver to load the LTSP Linux kernel. The second allows the Linux dhclient to resolve its IP address.
+
+3) Although the ISA PNP docs specify that the IRQ, DMA and IO Address needs to be assigned to the card before it is activated, Etherboot does not seem to care. Therefore the code does not assign the card with these values.
+
+If you can help address any of thse issues, please feel free.
+
+Timothy Legge
+timlegge@users.sourceforge.net
+April 9, 2003
diff --git a/src/drivers/net/3c595.c b/src/drivers/net/3c595.c
new file mode 100644
index 00000000..e8fc377d
--- /dev/null
+++ b/src/drivers/net/3c595.c
@@ -0,0 +1,550 @@
+/*
+* 3c595.c -- 3COM 3C595 Fast Etherlink III PCI driver for etherboot
+*
+* Copyright (C) 2000 Shusuke Nisiyama <shu@athena.qe.eng.hokudai.ac.jp>
+* All rights reserved.
+* Mar. 14, 2000
+*
+* This software may be used, modified, copied, distributed, and sold, in
+* both source and binary form provided that the above copyright and these
+* terms are retained. Under no circumstances are the authors responsible for
+* the proper functioning of this software, nor do the authors assume any
+* responsibility for damages incurred with its use.
+*
+* This code is based on Martin Renters' etherboot-4.4.3 3c509.c and
+* Herb Peyerl's FreeBSD 3.4-RELEASE if_vx.c driver.
+*
+* Copyright (C) 1993-1994, David Greenman, Martin Renters.
+* Copyright (C) 1993-1995, Andres Vega Garcia.
+* Copyright (C) 1995, Serge Babkin.
+*
+* Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca>
+*
+* timlegge 08-24-2003 Add Multicast Support
+*/
+
+/* #define EDEBUG */
+
+#include "etherboot.h"
+#include "nic.h"
+#include "pci.h"
+#include "3c595.h"
+#include "timer.h"
+
+static unsigned short eth_nic_base;
+static unsigned short vx_connector, vx_connectors;
+
+static struct connector_entry {
+ int bit;
+ char *name;
+} conn_tab[VX_CONNECTORS] = {
+#define CONNECTOR_UTP 0
+ { 0x08, "utp"},
+#define CONNECTOR_AUI 1
+ { 0x20, "aui"},
+/* dummy */
+ { 0, "???"},
+#define CONNECTOR_BNC 3
+ { 0x10, "bnc"},
+#define CONNECTOR_TX 4
+ { 0x02, "tx"},
+#define CONNECTOR_FX 5
+ { 0x04, "fx"},
+#define CONNECTOR_MII 6
+ { 0x40, "mii"},
+ { 0, "???"}
+};
+
+static void vxgetlink(void);
+static void vxsetlink(void);
+
+/**************************************************************************
+ETH_RESET - Reset adapter
+***************************************************************************/
+static void t595_reset(struct nic *nic)
+{
+ int i;
+
+ /***********************************************************
+ Reset 3Com 595 card
+ *************************************************************/
+
+ /* stop card */
+ outw(RX_DISABLE, BASE + VX_COMMAND);
+ outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
+ VX_BUSY_WAIT;
+ outw(TX_DISABLE, BASE + VX_COMMAND);
+ outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
+ udelay(8000);
+ outw(RX_RESET, BASE + VX_COMMAND);
+ VX_BUSY_WAIT;
+ outw(TX_RESET, BASE + VX_COMMAND);
+ VX_BUSY_WAIT;
+ outw(C_INTR_LATCH, BASE + VX_COMMAND);
+ outw(SET_RD_0_MASK, BASE + VX_COMMAND);
+ outw(SET_INTR_MASK, BASE + VX_COMMAND);
+ outw(SET_RX_FILTER, BASE + VX_COMMAND);
+
+ /*
+ * initialize card
+ */
+ VX_BUSY_WAIT;
+
+ GO_WINDOW(0);
+
+ /* Disable the card */
+/* outw(0, BASE + VX_W0_CONFIG_CTRL); */
+
+ /* Configure IRQ to none */
+/* outw(SET_IRQ(0), BASE + VX_W0_RESOURCE_CFG); */
+
+ /* Enable the card */
+/* outw(ENABLE_DRQ_IRQ, BASE + VX_W0_CONFIG_CTRL); */
+
+ GO_WINDOW(2);
+
+ /* Reload the ether_addr. */
+ for (i = 0; i < ETH_ALEN; i++)
+ outb(nic->node_addr[i], BASE + VX_W2_ADDR_0 + i);
+
+ outw(RX_RESET, BASE + VX_COMMAND);
+ VX_BUSY_WAIT;
+ outw(TX_RESET, BASE + VX_COMMAND);
+ VX_BUSY_WAIT;
+
+ /* Window 1 is operating window */
+ GO_WINDOW(1);
+ for (i = 0; i < 31; i++)
+ inb(BASE + VX_W1_TX_STATUS);
+
+ outw(SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
+ S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND);
+ outw(SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE |
+ S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND);
+
+/*
+ * Attempt to get rid of any stray interrupts that occured during
+ * configuration. On the i386 this isn't possible because one may
+ * already be queued. However, a single stray interrupt is
+ * unimportant.
+ */
+
+ outw(ACK_INTR | 0xff, BASE + VX_COMMAND);
+
+ outw(SET_RX_FILTER | FIL_INDIVIDUAL |
+ FIL_BRDCST|FIL_MULTICAST, BASE + VX_COMMAND);
+
+ vxsetlink();
+/*{
+ int i,j;
+ i = CONNECTOR_TX;
+ GO_WINDOW(3);
+ j = inl(BASE + VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK;
+ outl(BASE + VX_W3_INTERNAL_CFG, j | (i <<INTERNAL_CONNECTOR_BITS));
+ GO_WINDOW(4);
+ outw(LINKBEAT_ENABLE, BASE + VX_W4_MEDIA_TYPE);
+ GO_WINDOW(1);
+}*/
+
+ /* start tranciever and receiver */
+ outw(RX_ENABLE, BASE + VX_COMMAND);
+ outw(TX_ENABLE, BASE + VX_COMMAND);
+
+}
+
+/**************************************************************************
+ETH_TRANSMIT - Transmit a frame
+***************************************************************************/
+static char padmap[] = {
+ 0, 3, 2, 1};
+
+static void t595_transmit(
+struct nic *nic,
+const char *d, /* Destination */
+unsigned int t, /* Type */
+unsigned int s, /* size */
+const char *p) /* Packet */
+{
+ register int len;
+ int pad;
+ int status;
+
+#ifdef EDEBUG
+ printf("{l=%d,t=%hX}",s+ETH_HLEN,t);
+#endif
+
+ /* swap bytes of type */
+ t= htons(t);
+
+ len=s+ETH_HLEN; /* actual length of packet */
+ pad = padmap[len & 3];
+
+ /*
+ * The 3c595 automatically pads short packets to minimum ethernet length,
+ * but we drop packets that are too large. Perhaps we should truncate
+ * them instead?
+ */
+ if (len + pad > ETH_FRAME_LEN) {
+ return;
+ }
+
+ /* drop acknowledgements */
+ while(( status=inb(BASE + VX_W1_TX_STATUS) )& TXS_COMPLETE ) {
+ if(status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) {
+ outw(TX_RESET, BASE + VX_COMMAND);
+ outw(TX_ENABLE, BASE + VX_COMMAND);
+ }
+
+ outb(0x0, BASE + VX_W1_TX_STATUS);
+ }
+
+ while (inw(BASE + VX_W1_FREE_TX) < len + pad + 4) {
+ /* no room in FIFO */
+ }
+
+ outw(len, BASE + VX_W1_TX_PIO_WR_1);
+ outw(0x0, BASE + VX_W1_TX_PIO_WR_1); /* Second dword meaningless */
+
+ /* write packet */
+ outsw(BASE + VX_W1_TX_PIO_WR_1, d, ETH_ALEN/2);
+ outsw(BASE + VX_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2);
+ outw(t, BASE + VX_W1_TX_PIO_WR_1);
+ outsw(BASE + VX_W1_TX_PIO_WR_1, p, s / 2);
+ if (s & 1)
+ outb(*(p+s - 1), BASE + VX_W1_TX_PIO_WR_1);
+
+ while (pad--)
+ outb(0, BASE + VX_W1_TX_PIO_WR_1); /* Padding */
+
+ /* wait for Tx complete */
+ while((inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS) != 0)
+ ;
+}
+
+/**************************************************************************
+ETH_POLL - Wait for a frame
+***************************************************************************/
+static int t595_poll(struct nic *nic, int retrieve)
+{
+ /* common variables */
+ /* variables for 3C595 */
+ short status, cst;
+ register short rx_fifo;
+
+ cst=inw(BASE + VX_STATUS);
+
+#ifdef EDEBUG
+ if(cst & 0x1FFF)
+ printf("-%hX-",cst);
+#endif
+
+ if( (cst & S_RX_COMPLETE)==0 ) {
+ /* acknowledge everything */
+ outw(ACK_INTR | cst, BASE + VX_COMMAND);
+ outw(C_INTR_LATCH, BASE + VX_COMMAND);
+
+ return 0;
+ }
+
+ status = inw(BASE + VX_W1_RX_STATUS);
+#ifdef EDEBUG
+ printf("*%hX*",status);
+#endif
+
+ if (status & ERR_RX) {
+ outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
+ return 0;
+ }
+
+ rx_fifo = status & RX_BYTES_MASK;
+ if (rx_fifo==0)
+ return 0;
+
+ if ( ! retrieve ) return 1;
+
+ /* read packet */
+#ifdef EDEBUG
+ printf("[l=%d",rx_fifo);
+#endif
+ insw(BASE + VX_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2);
+ if(rx_fifo & 1)
+ nic->packet[rx_fifo-1]=inb(BASE + VX_W1_RX_PIO_RD_1);
+ nic->packetlen=rx_fifo;
+
+ while(1) {
+ status = inw(BASE + VX_W1_RX_STATUS);
+#ifdef EDEBUG
+ printf("*%hX*",status);
+#endif
+ rx_fifo = status & RX_BYTES_MASK;
+
+ if(rx_fifo>0) {
+ insw(BASE + VX_W1_RX_PIO_RD_1, nic->packet+nic->packetlen, rx_fifo / 2);
+ if(rx_fifo & 1)
+ nic->packet[nic->packetlen+rx_fifo-1]=inb(BASE + VX_W1_RX_PIO_RD_1);
+ nic->packetlen+=rx_fifo;
+#ifdef EDEBUG
+ printf("+%d",rx_fifo);
+#endif
+ }
+ if(( status & RX_INCOMPLETE )==0) {
+#ifdef EDEBUG
+ printf("=%d",nic->packetlen);
+#endif
+ break;
+ }
+ udelay(1000);
+ }
+
+ /* acknowledge reception of packet */
+ outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND);
+ while (inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS);
+#ifdef EDEBUG
+{
+ unsigned short type = 0; /* used by EDEBUG */
+ type = (nic->packet[12]<<8) | nic->packet[13];
+ if(nic->packet[0]+nic->packet[1]+nic->packet[2]+nic->packet[3]+nic->packet[4]+
+ nic->packet[5] == 0xFF*ETH_ALEN)
+ printf(",t=%hX,b]",type);
+ else
+ printf(",t=%hX]",type);
+}
+#endif
+ return 1;
+}
+
+
+/*************************************************************************
+ 3Com 595 - specific routines
+**************************************************************************/
+
+static int
+eeprom_rdy()
+{
+ int i;
+
+ for (i = 0; is_eeprom_busy(BASE) && i < MAX_EEPROMBUSY; i++)
+ udelay(1000);
+ if (i >= MAX_EEPROMBUSY) {
+ /* printf("3c595: eeprom failed to come ready.\n"); */
+ printf("3c595: eeprom is busy.\n"); /* memory in EPROM is tight */
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * get_e: gets a 16 bits word from the EEPROM. we must have set the window
+ * before
+ */
+static int
+get_e(offset)
+int offset;
+{
+ if (!eeprom_rdy())
+ return (0xffff);
+ outw(EEPROM_CMD_RD | offset, BASE + VX_W0_EEPROM_COMMAND);
+ if (!eeprom_rdy())
+ return (0xffff);
+ return (inw(BASE + VX_W0_EEPROM_DATA));
+}
+
+static void
+vxgetlink(void)
+{
+ int n, k;
+
+ GO_WINDOW(3);
+ vx_connectors = inw(BASE + VX_W3_RESET_OPT) & 0x7f;
+ for (n = 0, k = 0; k < VX_CONNECTORS; k++) {
+ if (vx_connectors & conn_tab[k].bit) {
+ if (n > 0) {
+ printf("/");
+ }
+ printf(conn_tab[k].name);
+ n++;
+ }
+ }
+ if (vx_connectors == 0) {
+ printf("no connectors!");
+ return;
+ }
+ GO_WINDOW(3);
+ vx_connector = (inl(BASE + VX_W3_INTERNAL_CFG)
+ & INTERNAL_CONNECTOR_MASK)
+ >> INTERNAL_CONNECTOR_BITS;
+ if (vx_connector & 0x10) {
+ vx_connector &= 0x0f;
+ printf("[*%s*]", conn_tab[vx_connector].name);
+ printf(": disable 'auto select' with DOS util!");
+ } else {
+ printf("[*%s*]", conn_tab[vx_connector].name);
+ }
+}
+
+static void
+vxsetlink(void)
+{
+ int i, j;
+ char *reason, *warning;
+ static char prev_conn = -1;
+
+ if (prev_conn == -1) {
+ prev_conn = vx_connector;
+ }
+
+ i = vx_connector; /* default in EEPROM */
+ reason = "default";
+ warning = 0;
+
+ if ((vx_connectors & conn_tab[vx_connector].bit) == 0) {
+ warning = "strange connector type in EEPROM.";
+ reason = "forced";
+ i = CONNECTOR_UTP;
+ }
+
+ if (warning != 0) {
+ printf("warning: %s\n", warning);
+ }
+ printf("selected %s. (%s)\n", conn_tab[i].name, reason);
+
+ /* Set the selected connector. */
+ GO_WINDOW(3);
+ j = inl(BASE + VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK;
+ outl(j | (i <<INTERNAL_CONNECTOR_BITS), BASE + VX_W3_INTERNAL_CFG);
+
+ /* First, disable all. */
+ outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
+ udelay(8000);
+ GO_WINDOW(4);
+ outw(0, BASE + VX_W4_MEDIA_TYPE);
+
+ /* Second, enable the selected one. */
+ switch(i) {
+ case CONNECTOR_UTP:
+ GO_WINDOW(4);
+ outw(ENABLE_UTP, BASE + VX_W4_MEDIA_TYPE);
+ break;
+ case CONNECTOR_BNC:
+ outw(START_TRANSCEIVER,BASE + VX_COMMAND);
+ udelay(8000);
+ break;
+ case CONNECTOR_TX:
+ case CONNECTOR_FX:
+ GO_WINDOW(4);
+ outw(LINKBEAT_ENABLE, BASE + VX_W4_MEDIA_TYPE);
+ break;
+ default: /* AUI and MII fall here */
+ break;
+ }
+ GO_WINDOW(1);
+}
+
+static void t595_disable(struct dev *dev)
+{
+ struct nic *nic = (struct nic *)dev;
+ t595_reset(nic);
+
+ outw(STOP_TRANSCEIVER, BASE + VX_COMMAND);
+ udelay(8000);
+ GO_WINDOW(4);
+ outw(0, BASE + VX_W4_MEDIA_TYPE);
+ GO_WINDOW(1);
+}
+
+static void t595_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/**************************************************************************
+ETH_PROBE - Look for an adapter
+***************************************************************************/
+static int t595_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *)dev;
+ int i;
+ unsigned short *p;
+
+ if (pci->ioaddr == 0)
+ return 0;
+/* eth_nic_base = probeaddrs[0] & ~3; */
+ eth_nic_base = pci->ioaddr;
+
+ nic->irqno = 0;
+ nic->ioaddr = pci->ioaddr & ~3;
+
+ GO_WINDOW(0);
+ outw(GLOBAL_RESET, BASE + VX_COMMAND);
+ VX_BUSY_WAIT;
+
+ vxgetlink();
+
+/*
+ printf("\nEEPROM:");
+ for (i = 0; i < (EEPROMSIZE/2); i++) {
+ printf("%hX:", get_e(i));
+ }
+ printf("\n");
+*/
+ /*
+ * Read the station address from the eeprom
+ */
+ p = (unsigned short *) nic->node_addr;
+ for (i = 0; i < 3; i++) {
+ GO_WINDOW(0);
+ p[i] = htons(get_e(EEPROM_OEM_ADDR_0 + i));
+ GO_WINDOW(2);
+ outw(ntohs(p[i]), BASE + VX_W2_ADDR_0 + (i * 2));
+ }
+
+ printf("Ethernet address: %!\n", nic->node_addr);
+
+ t595_reset(nic);
+ dev->disable = t595_disable;
+ nic->poll = t595_poll;
+ nic->transmit = t595_transmit;
+ nic->irq = t595_irq;
+ return 1;
+
+}
+
+static struct pci_id t595_nics[] = {
+PCI_ROM(0x10b7, 0x5900, "3c590", "3Com590"), /* Vortex 10Mbps */
+PCI_ROM(0x10b7, 0x5950, "3c595", "3Com595"), /* Vortex 100baseTx */
+PCI_ROM(0x10b7, 0x5951, "3c595-1", "3Com595"), /* Vortex 100baseT4 */
+PCI_ROM(0x10b7, 0x5952, "3c595-2", "3Com595"), /* Vortex 100base-MII */
+PCI_ROM(0x10b7, 0x9000, "3c900-tpo", "3Com900-TPO"), /* 10 Base TPO */
+PCI_ROM(0x10b7, 0x9001, "3c900-t4", "3Com900-Combo"), /* 10/100 T4 */
+PCI_ROM(0x10b7, 0x9004, "3c900b-tpo", "3Com900B-TPO"), /* 10 Base TPO */
+PCI_ROM(0x10b7, 0x9005, "3c900b-combo", "3Com900B-Combo"), /* 10 Base Combo */
+PCI_ROM(0x10b7, 0x9006, "3c900b-tpb2", "3Com900B-2/T"), /* 10 Base TP and Base2 */
+PCI_ROM(0x10b7, 0x900a, "3c900b-fl", "3Com900B-FL"), /* 10 Base F */
+PCI_ROM(0x10b7, 0x9800, "3c980-cyclone-1", "3Com980-Cyclone"), /* Cyclone */
+PCI_ROM(0x10b7, 0x9805, "3c9805-1", "3Com9805"), /* Dual Port Server Cyclone */
+PCI_ROM(0x10b7, 0x7646, "3csoho100-tx-1", "3CSOHO100-TX"), /* Hurricane */
+PCI_ROM(0x10b7, 0x4500, "3c450-1", "3Com450 HomePNA Tornado"),
+};
+
+static struct pci_driver t595_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "3C595",
+ .probe = t595_probe,
+ .ids = t595_nics,
+ .id_count = sizeof(t595_nics)/sizeof(t595_nics[0]),
+ .class = 0,
+};
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/src/drivers/net/3c595.h b/src/drivers/net/3c595.h
new file mode 100644
index 00000000..49d8d9b0
--- /dev/null
+++ b/src/drivers/net/3c595.h
@@ -0,0 +1,435 @@
+/*
+ * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2. The name
+ * of the author may not be used to endorse or promote products derived from
+ * this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ October 2, 1994
+
+ Modified by: Andres Vega Garcia
+
+ INRIA - Sophia Antipolis, France
+ e-mail: avega@sophia.inria.fr
+ finger: avega@pax.inria.fr
+
+ */
+
+/*
+ * Created from if_epreg.h by Fred Gray (fgray@rice.edu) to support the
+ * 3c590 family.
+ */
+
+/*
+ * Modified by Shusuke Nisiyama <shu@athena.qe.eng.hokudai.ac.jp>
+ * for etherboot
+ * Mar. 14, 2000
+*/
+
+/*
+ * Ethernet software status per interface.
+ */
+
+/*
+ * Some global constants
+ */
+
+#define TX_INIT_RATE 16
+#define TX_INIT_MAX_RATE 64
+#define RX_INIT_LATENCY 64
+#define RX_INIT_EARLY_THRESH 64
+#define MIN_RX_EARLY_THRESHF 16 /* not less than ether_header */
+#define MIN_RX_EARLY_THRESHL 4
+
+#define EEPROMSIZE 0x40
+#define MAX_EEPROMBUSY 1000
+#define VX_LAST_TAG 0xd7
+#define VX_MAX_BOARDS 16
+#define VX_ID_PORT 0x100
+
+/*
+ * some macros to acces long named fields
+ */
+#define BASE (eth_nic_base)
+
+/*
+ * Commands to read/write EEPROM trough EEPROM command register (Window 0,
+ * Offset 0xa)
+ */
+#define EEPROM_CMD_RD 0x0080 /* Read: Address required (5 bits) */
+#define EEPROM_CMD_WR 0x0040 /* Write: Address required (5 bits) */
+#define EEPROM_CMD_ERASE 0x00c0 /* Erase: Address required (5 bits) */
+#define EEPROM_CMD_EWEN 0x0030 /* Erase/Write Enable: No data required */
+
+#define EEPROM_BUSY (1<<15)
+
+/*
+ * Some short functions, worth to let them be a macro
+ */
+
+/**************************************************************************
+ * *
+ * These define the EEPROM data structure. They are used in the probe
+ * function to verify the existence of the adapter after having sent
+ * the ID_Sequence.
+ *
+ * There are others but only the ones we use are defined here.
+ *
+ **************************************************************************/
+
+#define EEPROM_NODE_ADDR_0 0x0 /* Word */
+#define EEPROM_NODE_ADDR_1 0x1 /* Word */
+#define EEPROM_NODE_ADDR_2 0x2 /* Word */
+#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */
+#define EEPROM_MFG_ID 0x7 /* 0x6d50 */
+#define EEPROM_ADDR_CFG 0x8 /* Base addr */
+#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */
+#define EEPROM_OEM_ADDR_0 0xa /* Word */
+#define EEPROM_OEM_ADDR_1 0xb /* Word */
+#define EEPROM_OEM_ADDR_2 0xc /* Word */
+#define EEPROM_SOFT_INFO_2 0xf /* Software information 2 */
+
+#define NO_RX_OVN_ANOMALY (1<<5)
+
+/**************************************************************************
+ * *
+ * These are the registers for the 3Com 3c509 and their bit patterns when *
+ * applicable. They have been taken out the the "EtherLink III Parallel *
+ * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual *
+ * from 3com. *
+ * *
+ **************************************************************************/
+
+#define VX_COMMAND 0x0e /* Write. BASE+0x0e is always a
+ * command reg. */
+#define VX_STATUS 0x0e /* Read. BASE+0x0e is always status
+ * reg. */
+#define VX_WINDOW 0x0f /* Read. BASE+0x0f is always window
+ * reg. */
+/*
+ * Window 0 registers. Setup.
+ */
+/* Write */
+#define VX_W0_EEPROM_DATA 0x0c
+#define VX_W0_EEPROM_COMMAND 0x0a
+#define VX_W0_RESOURCE_CFG 0x08
+#define VX_W0_ADDRESS_CFG 0x06
+#define VX_W0_CONFIG_CTRL 0x04
+ /* Read */
+#define VX_W0_PRODUCT_ID 0x02
+#define VX_W0_MFG_ID 0x00
+
+
+/*
+ * Window 1 registers. Operating Set.
+ */
+/* Write */
+#define VX_W1_TX_PIO_WR_2 0x02
+#define VX_W1_TX_PIO_WR_1 0x00
+/* Read */
+#define VX_W1_FREE_TX 0x0c
+#define VX_W1_TX_STATUS 0x0b /* byte */
+#define VX_W1_TIMER 0x0a /* byte */
+#define VX_W1_RX_STATUS 0x08
+#define VX_W1_RX_PIO_RD_2 0x02
+#define VX_W1_RX_PIO_RD_1 0x00
+
+/*
+ * Window 2 registers. Station Address Setup/Read
+ */
+/* Read/Write */
+#define VX_W2_ADDR_5 0x05
+#define VX_W2_ADDR_4 0x04
+#define VX_W2_ADDR_3 0x03
+#define VX_W2_ADDR_2 0x02
+#define VX_W2_ADDR_1 0x01
+#define VX_W2_ADDR_0 0x00
+
+/*
+ * Window 3 registers. FIFO Management.
+ */
+/* Read */
+#define VX_W3_INTERNAL_CFG 0x00
+#define VX_W3_RESET_OPT 0x08
+#define VX_W3_FREE_TX 0x0c
+#define VX_W3_FREE_RX 0x0a
+
+/*
+ * Window 4 registers. Diagnostics.
+ */
+/* Read/Write */
+#define VX_W4_MEDIA_TYPE 0x0a
+#define VX_W4_CTRLR_STATUS 0x08
+#define VX_W4_NET_DIAG 0x06
+#define VX_W4_FIFO_DIAG 0x04
+#define VX_W4_HOST_DIAG 0x02
+#define VX_W4_TX_DIAG 0x00
+
+/*
+ * Window 5 Registers. Results and Internal status.
+ */
+/* Read */
+#define VX_W5_READ_0_MASK 0x0c
+#define VX_W5_INTR_MASK 0x0a
+#define VX_W5_RX_FILTER 0x08
+#define VX_W5_RX_EARLY_THRESH 0x06
+#define VX_W5_TX_AVAIL_THRESH 0x02
+#define VX_W5_TX_START_THRESH 0x00
+
+/*
+ * Window 6 registers. Statistics.
+ */
+/* Read/Write */
+#define TX_TOTAL_OK 0x0c
+#define RX_TOTAL_OK 0x0a
+#define TX_DEFERRALS 0x08
+#define RX_FRAMES_OK 0x07
+#define TX_FRAMES_OK 0x06
+#define RX_OVERRUNS 0x05
+#define TX_COLLISIONS 0x04
+#define TX_AFTER_1_COLLISION 0x03
+#define TX_AFTER_X_COLLISIONS 0x02
+#define TX_NO_SQE 0x01
+#define TX_CD_LOST 0x00
+
+/****************************************
+ *
+ * Register definitions.
+ *
+ ****************************************/
+
+/*
+ * Command register. All windows.
+ *
+ * 16 bit register.
+ * 15-11: 5-bit code for command to be executed.
+ * 10-0: 11-bit arg if any. For commands with no args;
+ * this can be set to anything.
+ */
+#define GLOBAL_RESET (unsigned short) 0x0000 /* Wait at least 1ms
+ * after issuing */
+#define WINDOW_SELECT (unsigned short) (0x1<<11)
+#define START_TRANSCEIVER (unsigned short) (0x2<<11) /* Read ADDR_CFG reg to
+ * determine whether
+ * this is needed. If
+ * so; wait 800 uSec
+ * before using trans-
+ * ceiver. */
+#define RX_DISABLE (unsigned short) (0x3<<11) /* state disabled on
+ * power-up */
+#define RX_ENABLE (unsigned short) (0x4<<11)
+#define RX_RESET (unsigned short) (0x5<<11)
+#define RX_DISCARD_TOP_PACK (unsigned short) (0x8<<11)
+#define TX_ENABLE (unsigned short) (0x9<<11)
+#define TX_DISABLE (unsigned short) (0xa<<11)
+#define TX_RESET (unsigned short) (0xb<<11)
+#define REQ_INTR (unsigned short) (0xc<<11)
+/*
+ * The following C_* acknowledge the various interrupts. Some of them don't
+ * do anything. See the manual.
+ */
+#define ACK_INTR (unsigned short) (0x6800)
+# define C_INTR_LATCH (unsigned short) (ACK_INTR|0x1)
+# define C_CARD_FAILURE (unsigned short) (ACK_INTR|0x2)
+# define C_TX_COMPLETE (unsigned short) (ACK_INTR|0x4)
+# define C_TX_AVAIL (unsigned short) (ACK_INTR|0x8)
+# define C_RX_COMPLETE (unsigned short) (ACK_INTR|0x10)
+# define C_RX_EARLY (unsigned short) (ACK_INTR|0x20)
+# define C_INT_RQD (unsigned short) (ACK_INTR|0x40)
+# define C_UPD_STATS (unsigned short) (ACK_INTR|0x80)
+#define SET_INTR_MASK (unsigned short) (0xe<<11)
+#define SET_RD_0_MASK (unsigned short) (0xf<<11)
+#define SET_RX_FILTER (unsigned short) (0x10<<11)
+# define FIL_INDIVIDUAL (unsigned short) (0x1)
+# define FIL_MULTICAST (unsigned short) (0x02)
+# define FIL_BRDCST (unsigned short) (0x04)
+# define FIL_PROMISC (unsigned short) (0x08)
+#define SET_RX_EARLY_THRESH (unsigned short) (0x11<<11)
+#define SET_TX_AVAIL_THRESH (unsigned short) (0x12<<11)
+#define SET_TX_START_THRESH (unsigned short) (0x13<<11)
+#define STATS_ENABLE (unsigned short) (0x15<<11)
+#define STATS_DISABLE (unsigned short) (0x16<<11)
+#define STOP_TRANSCEIVER (unsigned short) (0x17<<11)
+
+/*
+ * Status register. All windows.
+ *
+ * 15-13: Window number(0-7).
+ * 12: Command_in_progress.
+ * 11: reserved.
+ * 10: reserved.
+ * 9: reserved.
+ * 8: reserved.
+ * 7: Update Statistics.
+ * 6: Interrupt Requested.
+ * 5: RX Early.
+ * 4: RX Complete.
+ * 3: TX Available.
+ * 2: TX Complete.
+ * 1: Adapter Failure.
+ * 0: Interrupt Latch.
+ */
+#define S_INTR_LATCH (unsigned short) (0x1)
+#define S_CARD_FAILURE (unsigned short) (0x2)
+#define S_TX_COMPLETE (unsigned short) (0x4)
+#define S_TX_AVAIL (unsigned short) (0x8)
+#define S_RX_COMPLETE (unsigned short) (0x10)
+#define S_RX_EARLY (unsigned short) (0x20)
+#define S_INT_RQD (unsigned short) (0x40)
+#define S_UPD_STATS (unsigned short) (0x80)
+#define S_COMMAND_IN_PROGRESS (unsigned short) (0x1000)
+
+#define VX_BUSY_WAIT while (inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS)
+
+/* Address Config. Register.
+ * Window 0/Port 06
+ */
+
+#define ACF_CONNECTOR_BITS 14
+#define ACF_CONNECTOR_UTP 0
+#define ACF_CONNECTOR_AUI 1
+#define ACF_CONNECTOR_BNC 3
+
+#define INTERNAL_CONNECTOR_BITS 20
+#define INTERNAL_CONNECTOR_MASK 0x01700000
+
+/*
+ * FIFO Registers. RX Status.
+ *
+ * 15: Incomplete or FIFO empty.
+ * 14: 1: Error in RX Packet 0: Incomplete or no error.
+ * 13-11: Type of error.
+ * 1000 = Overrun.
+ * 1011 = Run Packet Error.
+ * 1100 = Alignment Error.
+ * 1101 = CRC Error.
+ * 1001 = Oversize Packet Error (>1514 bytes)
+ * 0010 = Dribble Bits.
+ * (all other error codes, no errors.)
+ *
+ * 10-0: RX Bytes (0-1514)
+ */
+#define ERR_INCOMPLETE (unsigned short) (0x8000)
+#define ERR_RX (unsigned short) (0x4000)
+#define ERR_MASK (unsigned short) (0x7800)
+#define ERR_OVERRUN (unsigned short) (0x4000)
+#define ERR_RUNT (unsigned short) (0x5800)
+#define ERR_ALIGNMENT (unsigned short) (0x6000)
+#define ERR_CRC (unsigned short) (0x6800)
+#define ERR_OVERSIZE (unsigned short) (0x4800)
+#define ERR_DRIBBLE (unsigned short) (0x1000)
+
+/*
+ * TX Status.
+ *
+ * Reports the transmit status of a completed transmission. Writing this
+ * register pops the transmit completion stack.
+ *
+ * Window 1/Port 0x0b.
+ *
+ * 7: Complete
+ * 6: Interrupt on successful transmission requested.
+ * 5: Jabber Error (TP Only, TX Reset required. )
+ * 4: Underrun (TX Reset required. )
+ * 3: Maximum Collisions.
+ * 2: TX Status Overflow.
+ * 1-0: Undefined.
+ *
+ */
+#define TXS_COMPLETE 0x80
+#define TXS_INTR_REQ 0x40
+#define TXS_JABBER 0x20
+#define TXS_UNDERRUN 0x10
+#define TXS_MAX_COLLISION 0x8
+#define TXS_STATUS_OVERFLOW 0x4
+
+#define RS_AUI (1<<5)
+#define RS_BNC (1<<4)
+#define RS_UTP (1<<3)
+#define RS_T4 (1<<0)
+#define RS_TX (1<<1)
+#define RS_FX (1<<2)
+#define RS_MII (1<<6)
+
+
+/*
+ * FIFO Status (Window 4)
+ *
+ * Supports FIFO diagnostics
+ *
+ * Window 4/Port 0x04.1
+ *
+ * 15: 1=RX receiving (RO). Set when a packet is being received
+ * into the RX FIFO.
+ * 14: Reserved
+ * 13: 1=RX underrun (RO). Generates Adapter Failure interrupt.
+ * Requires RX Reset or Global Reset command to recover.
+ * It is generated when you read past the end of a packet -
+ * reading past what has been received so far will give bad
+ * data.
+ * 12: 1=RX status overrun (RO). Set when there are already 8
+ * packets in the RX FIFO. While this bit is set, no additional
+ * packets are received. Requires no action on the part of
+ * the host. The condition is cleared once a packet has been
+ * read out of the RX FIFO.
+ * 11: 1=RX overrun (RO). Set when the RX FIFO is full (there
+ * may not be an overrun packet yet). While this bit is set,
+ * no additional packets will be received (some additional
+ * bytes can still be pending between the wire and the RX
+ * FIFO). Requires no action on the part of the host. The
+ * condition is cleared once a few bytes have been read out
+ * from the RX FIFO.
+ * 10: 1=TX overrun (RO). Generates adapter failure interrupt.
+ * Requires TX Reset or Global Reset command to recover.
+ * Disables Transmitter.
+ * 9-8: Unassigned.
+ * 7-0: Built in self test bits for the RX and TX FIFO's.
+ */
+#define FIFOS_RX_RECEIVING (unsigned short) 0x8000
+#define FIFOS_RX_UNDERRUN (unsigned short) 0x2000
+#define FIFOS_RX_STATUS_OVERRUN (unsigned short) 0x1000
+#define FIFOS_RX_OVERRUN (unsigned short) 0x0800
+#define FIFOS_TX_OVERRUN (unsigned short) 0x0400
+
+/*
+ * Misc defines for various things.
+ */
+#define TAG_ADAPTER 0xd0
+#define ACTIVATE_ADAPTER_TO_CONFIG 0xff
+#define ENABLE_DRQ_IRQ 0x0001
+#define MFG_ID 0x506d /* `TCM' */
+#define PROD_ID 0x5090
+#define GO_WINDOW(x) outw(WINDOW_SELECT|(x),BASE+VX_COMMAND)
+#define JABBER_GUARD_ENABLE 0x40
+#define LINKBEAT_ENABLE 0x80
+#define ENABLE_UTP (JABBER_GUARD_ENABLE | LINKBEAT_ENABLE)
+#define DISABLE_UTP 0x0
+#define RX_BYTES_MASK (unsigned short) (0x07ff)
+#define RX_ERROR 0x4000
+#define RX_INCOMPLETE 0x8000
+#define TX_INDICATE 1<<15
+#define is_eeprom_busy(b) (inw((b)+VX_W0_EEPROM_COMMAND)&EEPROM_BUSY)
+
+#define VX_IOSIZE 0x20
+
+#define VX_CONNECTORS 8
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/src/drivers/net/3c90x.c b/src/drivers/net/3c90x.c
new file mode 100644
index 00000000..1c8b7e44
--- /dev/null
+++ b/src/drivers/net/3c90x.c
@@ -0,0 +1,996 @@
+/*
+ * 3c90x.c -- This file implements the 3c90x driver for etherboot. Written
+ * by Greg Beeley, Greg.Beeley@LightSys.org. Modified by Steve Smith,
+ * Steve.Smith@Juno.Com. Alignment bug fix Neil Newell (nn@icenoir.net).
+ *
+ * This program Copyright (C) 1999 LightSys Technology Services, Inc.
+ * Portions Copyright (C) 1999 Steve Smith
+ *
+ * This program may be re-distributed in source or binary form, modified,
+ * sold, or copied for any purpose, provided that the above copyright message
+ * and this text are included with all source copies or derivative works, and
+ * provided that the above copyright message and this text are included in the
+ * documentation of any binary-only distributions. This program is distributed
+ * WITHOUT ANY WARRANTY, without even the warranty of FITNESS FOR A PARTICULAR
+ * PURPOSE or MERCHANTABILITY. Please read the associated documentation
+ * "3c90x.txt" before compiling and using this driver.
+ *
+ * --------
+ *
+ * Program written with the assistance of the 3com documentation for
+ * the 3c905B-TX card, as well as with some assistance from the 3c59x
+ * driver Donald Becker wrote for the Linux kernel, and with some assistance
+ * from the remainder of the Etherboot distribution.
+ *
+ * REVISION HISTORY:
+ *
+ * v0.10 1-26-1998 GRB Initial implementation.
+ * v0.90 1-27-1998 GRB System works.
+ * v1.00pre1 2-11-1998 GRB Got prom boot issue fixed.
+ * v2.0 9-24-1999 SCS Modified for 3c905 (from 3c905b code)
+ * Re-wrote poll and transmit for
+ * better error recovery and heavy
+ * network traffic operation
+ * v2.01 5-26-2003 NN Fixed driver alignment issue which
+ * caused system lockups if driver structures
+ * not 8-byte aligned.
+ *
+ */
+
+#include "etherboot.h"
+#include "nic.h"
+#include "pci.h"
+#include "timer.h"
+
+#define XCVR_MAGIC (0x5A00)
+/** any single transmission fails after 16 collisions or other errors
+ ** this is the number of times to retry the transmission -- this should
+ ** be plenty
+ **/
+#define XMIT_RETRIES 250
+
+/*** Register definitions for the 3c905 ***/
+enum Registers
+ {
+ regPowerMgmtCtrl_w = 0x7c, /** 905B Revision Only **/
+ regUpMaxBurst_w = 0x7a, /** 905B Revision Only **/
+ regDnMaxBurst_w = 0x78, /** 905B Revision Only **/
+ regDebugControl_w = 0x74, /** 905B Revision Only **/
+ regDebugData_l = 0x70, /** 905B Revision Only **/
+ regRealTimeCnt_l = 0x40, /** Universal **/
+ regUpBurstThresh_b = 0x3e, /** 905B Revision Only **/
+ regUpPoll_b = 0x3d, /** 905B Revision Only **/
+ regUpPriorityThresh_b = 0x3c, /** 905B Revision Only **/
+ regUpListPtr_l = 0x38, /** Universal **/
+ regCountdown_w = 0x36, /** Universal **/
+ regFreeTimer_w = 0x34, /** Universal **/
+ regUpPktStatus_l = 0x30, /** Universal with Exception, pg 130 **/
+ regTxFreeThresh_b = 0x2f, /** 90X Revision Only **/
+ regDnPoll_b = 0x2d, /** 905B Revision Only **/
+ regDnPriorityThresh_b = 0x2c, /** 905B Revision Only **/
+ regDnBurstThresh_b = 0x2a, /** 905B Revision Only **/
+ regDnListPtr_l = 0x24, /** Universal with Exception, pg 107 **/
+ regDmaCtrl_l = 0x20, /** Universal with Exception, pg 106 **/
+ /** **/
+ regIntStatusAuto_w = 0x1e, /** 905B Revision Only **/
+ regTxStatus_b = 0x1b, /** Universal with Exception, pg 113 **/
+ regTimer_b = 0x1a, /** Universal **/
+ regTxPktId_b = 0x18, /** 905B Revision Only **/
+ regCommandIntStatus_w = 0x0e, /** Universal (Command Variations) **/
+ };
+
+/** following are windowed registers **/
+enum Registers7
+ {
+ regPowerMgmtEvent_7_w = 0x0c, /** 905B Revision Only **/
+ regVlanEtherType_7_w = 0x04, /** 905B Revision Only **/
+ regVlanMask_7_w = 0x00, /** 905B Revision Only **/
+ };
+
+enum Registers6
+ {
+ regBytesXmittedOk_6_w = 0x0c, /** Universal **/
+ regBytesRcvdOk_6_w = 0x0a, /** Universal **/
+ regUpperFramesOk_6_b = 0x09, /** Universal **/
+ regFramesDeferred_6_b = 0x08, /** Universal **/
+ regFramesRecdOk_6_b = 0x07, /** Universal with Exceptions, pg 142 **/
+ regFramesXmittedOk_6_b = 0x06, /** Universal **/
+ regRxOverruns_6_b = 0x05, /** Universal **/
+ regLateCollisions_6_b = 0x04, /** Universal **/
+ regSingleCollisions_6_b = 0x03, /** Universal **/
+ regMultipleCollisions_6_b = 0x02, /** Universal **/
+ regSqeErrors_6_b = 0x01, /** Universal **/
+ regCarrierLost_6_b = 0x00, /** Universal **/
+ };
+
+enum Registers5
+ {
+ regIndicationEnable_5_w = 0x0c, /** Universal **/
+ regInterruptEnable_5_w = 0x0a, /** Universal **/
+ regTxReclaimThresh_5_b = 0x09, /** 905B Revision Only **/
+ regRxFilter_5_b = 0x08, /** Universal **/
+ regRxEarlyThresh_5_w = 0x06, /** Universal **/
+ regTxStartThresh_5_w = 0x00, /** Universal **/
+ };
+
+enum Registers4
+ {
+ regUpperBytesOk_4_b = 0x0d, /** Universal **/
+ regBadSSD_4_b = 0x0c, /** Universal **/
+ regMediaStatus_4_w = 0x0a, /** Universal with Exceptions, pg 201 **/
+ regPhysicalMgmt_4_w = 0x08, /** Universal **/
+ regNetworkDiagnostic_4_w = 0x06, /** Universal with Exceptions, pg 203 **/
+ regFifoDiagnostic_4_w = 0x04, /** Universal with Exceptions, pg 196 **/
+ regVcoDiagnostic_4_w = 0x02, /** Undocumented? **/
+ };
+
+enum Registers3
+ {
+ regTxFree_3_w = 0x0c, /** Universal **/
+ regRxFree_3_w = 0x0a, /** Universal with Exceptions, pg 125 **/
+ regResetMediaOptions_3_w = 0x08, /** Media Options on B Revision, **/
+ /** Reset Options on Non-B Revision **/
+ regMacControl_3_w = 0x06, /** Universal with Exceptions, pg 199 **/
+ regMaxPktSize_3_w = 0x04, /** 905B Revision Only **/
+ regInternalConfig_3_l = 0x00, /** Universal, different bit **/
+ /** definitions, pg 59 **/
+ };
+
+enum Registers2
+ {
+ regResetOptions_2_w = 0x0c, /** 905B Revision Only **/
+ regStationMask_2_3w = 0x06, /** Universal with Exceptions, pg 127 **/
+ regStationAddress_2_3w = 0x00, /** Universal with Exceptions, pg 127 **/
+ };
+
+enum Registers1
+ {
+ regRxStatus_1_w = 0x0a, /** 90X Revision Only, Pg 126 **/
+ };
+
+enum Registers0
+ {
+ regEepromData_0_w = 0x0c, /** Universal **/
+ regEepromCommand_0_w = 0x0a, /** Universal **/
+ regBiosRomData_0_b = 0x08, /** 905B Revision Only **/
+ regBiosRomAddr_0_l = 0x04, /** 905B Revision Only **/
+ };
+
+
+/*** The names for the eight register windows ***/
+enum Windows
+ {
+ winPowerVlan7 = 0x07,
+ winStatistics6 = 0x06,
+ winTxRxControl5 = 0x05,
+ winDiagnostics4 = 0x04,
+ winTxRxOptions3 = 0x03,
+ winAddressing2 = 0x02,
+ winUnused1 = 0x01,
+ winEepromBios0 = 0x00,
+ };
+
+
+/*** Command definitions for the 3c90X ***/
+enum Commands
+ {
+ cmdGlobalReset = 0x00, /** Universal with Exceptions, pg 151 **/
+ cmdSelectRegisterWindow = 0x01, /** Universal **/
+ cmdEnableDcConverter = 0x02, /** **/
+ cmdRxDisable = 0x03, /** **/
+ cmdRxEnable = 0x04, /** Universal **/
+ cmdRxReset = 0x05, /** Universal **/
+ cmdStallCtl = 0x06, /** Universal **/
+ cmdTxEnable = 0x09, /** Universal **/
+ cmdTxDisable = 0x0A, /** **/
+ cmdTxReset = 0x0B, /** Universal **/
+ cmdRequestInterrupt = 0x0C, /** **/
+ cmdAcknowledgeInterrupt = 0x0D, /** Universal **/
+ cmdSetInterruptEnable = 0x0E, /** Universal **/
+ cmdSetIndicationEnable = 0x0F, /** Universal **/
+ cmdSetRxFilter = 0x10, /** Universal **/
+ cmdSetRxEarlyThresh = 0x11, /** **/
+ cmdSetTxStartThresh = 0x13, /** **/
+ cmdStatisticsEnable = 0x15, /** **/
+ cmdStatisticsDisable = 0x16, /** **/
+ cmdDisableDcConverter = 0x17, /** **/
+ cmdSetTxReclaimThresh = 0x18, /** **/
+ cmdSetHashFilterBit = 0x19, /** **/
+ };
+
+
+/*** Values for int status register bitmask **/
+#define INT_INTERRUPTLATCH (1<<0)
+#define INT_HOSTERROR (1<<1)
+#define INT_TXCOMPLETE (1<<2)
+#define INT_RXCOMPLETE (1<<4)
+#define INT_RXEARLY (1<<5)
+#define INT_INTREQUESTED (1<<6)
+#define INT_UPDATESTATS (1<<7)
+#define INT_LINKEVENT (1<<8)
+#define INT_DNCOMPLETE (1<<9)
+#define INT_UPCOMPLETE (1<<10)
+#define INT_CMDINPROGRESS (1<<12)
+#define INT_WINDOWNUMBER (7<<13)
+
+
+/*** TX descriptor ***/
+typedef struct
+ {
+ unsigned int DnNextPtr;
+ unsigned int FrameStartHeader;
+ unsigned int HdrAddr;
+ unsigned int HdrLength;
+ unsigned int DataAddr;
+ unsigned int DataLength;
+ }
+ TXD __attribute__ ((aligned(8))); /* 64-bit aligned for bus mastering */
+
+/*** RX descriptor ***/
+typedef struct
+ {
+ unsigned int UpNextPtr;
+ unsigned int UpPktStatus;
+ unsigned int DataAddr;
+ unsigned int DataLength;
+ }
+ RXD __attribute__ ((aligned(8))); /* 64-bit aligned for bus mastering */
+
+/*** Global variables ***/
+static struct
+ {
+ unsigned char isBrev;
+ unsigned char CurrentWindow;
+ unsigned int IOAddr;
+ unsigned char HWAddr[ETH_ALEN];
+ TXD TransmitDPD;
+ RXD ReceiveUPD;
+ }
+ INF_3C90X;
+
+
+/*** a3c90x_internal_IssueCommand: sends a command to the 3c90x card
+ ***/
+static int
+a3c90x_internal_IssueCommand(int ioaddr, int cmd, int param)
+ {
+ unsigned int val;
+
+ /** Build the cmd. **/
+ val = cmd;
+ val <<= 11;
+ val |= param;
+
+ /** Send the cmd to the cmd register **/
+ outw(val, ioaddr + regCommandIntStatus_w);
+
+ /** Wait for the cmd to complete, if necessary **/
+ while (inw(ioaddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
+
+ return 0;
+ }
+
+
+/*** a3c90x_internal_SetWindow: selects a register window set.
+ ***/
+static int
+a3c90x_internal_SetWindow(int ioaddr, int window)
+ {
+
+ /** Window already as set? **/
+ if (INF_3C90X.CurrentWindow == window) return 0;
+
+ /** Issue the window command. **/
+ a3c90x_internal_IssueCommand(ioaddr, cmdSelectRegisterWindow, window);
+ INF_3C90X.CurrentWindow = window;
+
+ return 0;
+ }
+
+
+/*** a3c90x_internal_ReadEeprom - read data from the serial eeprom.
+ ***/
+static unsigned short
+a3c90x_internal_ReadEeprom(int ioaddr, int address)
+ {
+ unsigned short val;
+
+ /** Select correct window **/
+ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winEepromBios0);
+
+ /** Make sure the eeprom isn't busy **/
+ while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
+
+ /** Read the value. **/
+ outw(address + ((0x02)<<6), ioaddr + regEepromCommand_0_w);
+ while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
+ val = inw(ioaddr + regEepromData_0_w);
+
+ return val;
+ }
+
+
+#if 0
+/*** a3c90x_internal_WriteEepromWord - write a physical word of
+ *** data to the onboard serial eeprom (not the BIOS prom, but the
+ *** nvram in the card that stores, among other things, the MAC
+ *** address).
+ ***/
+static int
+a3c90x_internal_WriteEepromWord(int ioaddr, int address, unsigned short value)
+ {
+ /** Select register window **/
+ a3c90x_internal_SetWindow(ioaddr, winEepromBios0);
+
+ /** Verify Eeprom not busy **/
+ while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
+
+ /** Issue WriteEnable, and wait for completion. **/
+ outw(0x30, ioaddr + regEepromCommand_0_w);
+ while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
+
+ /** Issue EraseRegister, and wait for completion. **/
+ outw(address + ((0x03)<<6), ioaddr + regEepromCommand_0_w);
+ while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
+
+ /** Send the new data to the eeprom, and wait for completion. **/
+ outw(value, ioaddr + regEepromData_0_w);
+ outw(0x30, ioaddr + regEepromCommand_0_w);
+ while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
+
+ /** Burn the new data into the eeprom, and wait for completion. **/
+ outw(address + ((0x01)<<6), ioaddr + regEepromCommand_0_w);
+ while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
+
+ return 0;
+ }
+#endif
+
+#if 0
+/*** a3c90x_internal_WriteEeprom - write data to the serial eeprom,
+ *** and re-compute the eeprom checksum.
+ ***/
+static int
+a3c90x_internal_WriteEeprom(int ioaddr, int address, unsigned short value)
+ {
+ int cksum = 0,v;
+ int i;
+ int maxAddress, cksumAddress;
+
+ if (INF_3C90X.isBrev)
+ {
+ maxAddress=0x1f;
+ cksumAddress=0x20;
+ }
+ else
+ {
+ maxAddress=0x16;
+ cksumAddress=0x17;
+ }
+
+ /** Write the value. **/
+ if (a3c90x_internal_WriteEepromWord(ioaddr, address, value) == -1)
+ return -1;
+
+ /** Recompute the checksum. **/
+ for(i=0;i<=maxAddress;i++)
+ {
+ v = a3c90x_internal_ReadEeprom(ioaddr, i);
+ cksum ^= (v & 0xFF);
+ cksum ^= ((v>>8) & 0xFF);
+ }
+ /** Write the checksum to the location in the eeprom **/
+ if (a3c90x_internal_WriteEepromWord(ioaddr, cksumAddress, cksum) == -1)
+ return -1;
+
+ return 0;
+ }
+#endif
+
+/*** a3c90x_reset: exported function that resets the card to its default
+ *** state. This is so the Linux driver can re-set the card up the way
+ *** it wants to. If CFG_3C90X_PRESERVE_XCVR is defined, then the reset will
+ *** not alter the selected transceiver that we used to download the boot
+ *** image.
+ ***/
+static void a3c90x_reset(void)
+ {
+#ifdef CFG_3C90X_PRESERVE_XCVR
+ int cfg;
+ /** Read the current InternalConfig value. **/
+ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
+ cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l);
+#endif
+
+ /** Send the reset command to the card **/
+ printf("Issuing RESET:\n");
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdGlobalReset, 0);
+
+ /** wait for reset command to complete **/
+ while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
+
+ /** global reset command resets station mask, non-B revision cards
+ ** require explicit reset of values
+ **/
+ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2);
+ outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0);
+ outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2);
+ outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4);
+
+#ifdef CFG_3C90X_PRESERVE_XCVR
+ /** Re-set the original InternalConfig value from before reset **/
+ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
+ outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l);
+
+ /** enable DC converter for 10-Base-T **/
+ if ((cfg&0x0300) == 0x0300)
+ {
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0);
+ }
+#endif
+
+ /** Issue transmit reset, wait for command completion **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0);
+ while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
+ ;
+ if (! INF_3C90X.isBrev)
+ outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b);
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
+
+ /**
+ ** reset of the receiver on B-revision cards re-negotiates the link
+ ** takes several seconds (a computer eternity)
+ **/
+ if (INF_3C90X.isBrev)
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04);
+ else
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00);
+ while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
+ ;
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0);
+
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
+ cmdSetInterruptEnable, 0);
+ /** enable rxComplete and txComplete **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
+ cmdSetIndicationEnable, 0x0014);
+ /** acknowledge any pending status flags **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
+ cmdAcknowledgeInterrupt, 0x661);
+
+ return;
+ }
+
+
+
+/*** a3c90x_transmit: exported function that transmits a packet. Does not
+ *** return any particular status. Parameters are:
+ *** d[6] - destination address, ethernet;
+ *** t - protocol type (ARP, IP, etc);
+ *** s - size of the non-header part of the packet that needs transmitted;
+ *** p - the pointer to the packet data itself.
+ ***/
+static void
+a3c90x_transmit(struct nic *nic __unused, const char *d, unsigned int t,
+ unsigned int s, const char *p)
+ {
+
+ struct eth_hdr
+ {
+ unsigned char dst_addr[ETH_ALEN];
+ unsigned char src_addr[ETH_ALEN];
+ unsigned short type;
+ } hdr;
+
+ unsigned char status;
+ unsigned i, retries;
+
+ for (retries=0; retries < XMIT_RETRIES ; retries++)
+ {
+ /** Stall the download engine **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 2);
+
+ /** Make sure the card is not waiting on us **/
+ inw(INF_3C90X.IOAddr + regCommandIntStatus_w);
+ inw(INF_3C90X.IOAddr + regCommandIntStatus_w);
+
+ while (inw(INF_3C90X.IOAddr+regCommandIntStatus_w) &
+ INT_CMDINPROGRESS)
+ ;
+
+ /** Set the ethernet packet type **/
+ hdr.type = htons(t);
+
+ /** Copy the destination address **/
+ memcpy(hdr.dst_addr, d, ETH_ALEN);
+
+ /** Copy our MAC address **/
+ memcpy(hdr.src_addr, INF_3C90X.HWAddr, ETH_ALEN);
+
+ /** Setup the DPD (download descriptor) **/
+ INF_3C90X.TransmitDPD.DnNextPtr = 0;
+ /** set notification for transmission completion (bit 15) **/
+ INF_3C90X.TransmitDPD.FrameStartHeader = (s + sizeof(hdr)) | 0x8000;
+ INF_3C90X.TransmitDPD.HdrAddr = virt_to_bus(&hdr);
+ INF_3C90X.TransmitDPD.HdrLength = sizeof(hdr);
+ INF_3C90X.TransmitDPD.DataAddr = virt_to_bus(p);
+ INF_3C90X.TransmitDPD.DataLength = s + (1<<31);
+
+ /** Send the packet **/
+ outl(virt_to_bus(&(INF_3C90X.TransmitDPD)),
+ INF_3C90X.IOAddr + regDnListPtr_l);
+
+ /** End Stall and Wait for upload to complete. **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 3);
+ while(inl(INF_3C90X.IOAddr + regDnListPtr_l) != 0)
+ ;
+
+ /** Wait for NIC Transmit to Complete **/
+ load_timer2(10*TICKS_PER_MS); /* Give it 10 ms */
+ while (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004) &&
+ timer2_running())
+ ;
+
+ if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004))
+ {
+ printf("3C90X: Tx Timeout\n");
+ continue;
+ }
+
+ status = inb(INF_3C90X.IOAddr + regTxStatus_b);
+
+ /** acknowledge transmit interrupt by writing status **/
+ outb(0x00, INF_3C90X.IOAddr + regTxStatus_b);
+
+ /** successful completion (sans "interrupt Requested" bit) **/
+ if ((status & 0xbf) == 0x80)
+ return;
+
+ printf("3C90X: Status (%hhX)\n", status);
+ /** check error codes **/
+ if (status & 0x02)
+ {
+ printf("3C90X: Tx Reclaim Error (%hhX)\n", status);
+ a3c90x_reset();
+ }
+ else if (status & 0x04)
+ {
+ printf("3C90X: Tx Status Overflow (%hhX)\n", status);
+ for (i=0; i<32; i++)
+ outb(0x00, INF_3C90X.IOAddr + regTxStatus_b);
+ /** must re-enable after max collisions before re-issuing tx **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
+ }
+ else if (status & 0x08)
+ {
+ printf("3C90X: Tx Max Collisions (%hhX)\n", status);
+ /** must re-enable after max collisions before re-issuing tx **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
+ }
+ else if (status & 0x10)
+ {
+ printf("3C90X: Tx Underrun (%hhX)\n", status);
+ a3c90x_reset();
+ }
+ else if (status & 0x20)
+ {
+ printf("3C90X: Tx Jabber (%hhX)\n", status);
+ a3c90x_reset();
+ }
+ else if ((status & 0x80) != 0x80)
+ {
+ printf("3C90X: Internal Error - Incomplete Transmission (%hhX)\n",
+ status);
+ a3c90x_reset();
+ }
+ }
+
+ /** failed after RETRY attempts **/
+ printf("Failed to send after %d retries\n", retries);
+ return;
+
+ }
+
+
+
+/*** a3c90x_poll: exported routine that waits for a certain length of time
+ *** for a packet, and if it sees none, returns 0. This routine should
+ *** copy the packet to nic->packet if it gets a packet and set the size
+ *** in nic->packetlen. Return 1 if a packet was found.
+ ***/
+static int
+a3c90x_poll(struct nic *nic, int retrieve)
+ {
+ int i, errcode;
+
+ if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0010))
+ {
+ return 0;
+ }
+
+ if ( ! retrieve ) return 1;
+
+ /** we don't need to acknowledge rxComplete -- the upload engine
+ ** does it for us.
+ **/
+
+ /** Build the up-load descriptor **/
+ INF_3C90X.ReceiveUPD.UpNextPtr = 0;
+ INF_3C90X.ReceiveUPD.UpPktStatus = 0;
+ INF_3C90X.ReceiveUPD.DataAddr = virt_to_bus(nic->packet);
+ INF_3C90X.ReceiveUPD.DataLength = 1536 + (1<<31);
+
+ /** Submit the upload descriptor to the NIC **/
+ outl(virt_to_bus(&(INF_3C90X.ReceiveUPD)),
+ INF_3C90X.IOAddr + regUpListPtr_l);
+
+ /** Wait for upload completion (upComplete(15) or upError (14)) **/
+ for(i=0;i<40000;i++);
+ while((INF_3C90X.ReceiveUPD.UpPktStatus & ((1<<14) | (1<<15))) == 0)
+ for(i=0;i<40000;i++);
+
+ /** Check for Error (else we have good packet) **/
+ if (INF_3C90X.ReceiveUPD.UpPktStatus & (1<<14))
+ {
+ errcode = INF_3C90X.ReceiveUPD.UpPktStatus;
+ if (errcode & (1<<16))
+ printf("3C90X: Rx Overrun (%hX)\n",errcode>>16);
+ else if (errcode & (1<<17))
+ printf("3C90X: Runt Frame (%hX)\n",errcode>>16);
+ else if (errcode & (1<<18))
+ printf("3C90X: Alignment Error (%hX)\n",errcode>>16);
+ else if (errcode & (1<<19))
+ printf("3C90X: CRC Error (%hX)\n",errcode>>16);
+ else if (errcode & (1<<20))
+ printf("3C90X: Oversized Frame (%hX)\n",errcode>>16);
+ else
+ printf("3C90X: Packet error (%hX)\n",errcode>>16);
+ return 0;
+ }
+
+ /** Ok, got packet. Set length in nic->packetlen. **/
+ nic->packetlen = (INF_3C90X.ReceiveUPD.UpPktStatus & 0x1FFF);
+
+ return 1;
+ }
+
+
+
+/*** a3c90x_disable: exported routine to disable the card. What's this for?
+ *** the eepro100.c driver didn't have one, so I just left this one empty too.
+ *** Ideas anyone?
+ *** Must turn off receiver at least so stray packets will not corrupt memory
+ *** [Ken]
+ ***/
+static void
+a3c90x_disable(struct dev *dev __unused)
+{
+ /* reset and disable merge */
+ a3c90x_reset();
+ /* Disable the receiver and transmitter. */
+ outw(cmdRxDisable, INF_3C90X.IOAddr + regCommandIntStatus_w);
+ outw(cmdTxDisable, INF_3C90X.IOAddr + regCommandIntStatus_w);
+}
+
+static void a3c90x_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/*** a3c90x_probe: exported routine to probe for the 3c905 card and perform
+ *** initialization. If this routine is called, the pci functions did find the
+ *** card. We just have to init it here.
+ ***/
+static int a3c90x_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *)dev;
+ int i, c;
+ unsigned short eeprom[0x21];
+ unsigned int cfg;
+ unsigned int mopt;
+ unsigned int mstat;
+ unsigned short linktype;
+#define HWADDR_OFFSET 10
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ adjust_pci_device(pci);
+
+ nic->ioaddr = pci->ioaddr & ~3;
+ nic->irqno = 0;
+
+ INF_3C90X.IOAddr = pci->ioaddr & ~3;
+ INF_3C90X.CurrentWindow = 255;
+ switch (a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, 0x03))
+ {
+ case 0x9000: /** 10 Base TPO **/
+ case 0x9001: /** 10/100 T4 **/
+ case 0x9050: /** 10/100 TPO **/
+ case 0x9051: /** 10 Base Combo **/
+ INF_3C90X.isBrev = 0;
+ break;
+
+ case 0x9004: /** 10 Base TPO **/
+ case 0x9005: /** 10 Base Combo **/
+ case 0x9006: /** 10 Base TPO and Base2 **/
+ case 0x900A: /** 10 Base FL **/
+ case 0x9055: /** 10/100 TPO **/
+ case 0x9056: /** 10/100 T4 **/
+ case 0x905A: /** 10 Base FX **/
+ default:
+ INF_3C90X.isBrev = 1;
+ break;
+ }
+
+ /** Load the EEPROM contents **/
+ if (INF_3C90X.isBrev)
+ {
+ for(i=0;i<=0x20;i++)
+ {
+ eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i);
+ }
+
+#ifdef CFG_3C90X_BOOTROM_FIX
+ /** Set xcvrSelect in InternalConfig in eeprom. **/
+ /* only necessary for 3c905b revision cards with boot PROM bug!!! */
+ a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x13, 0x0160);
+#endif
+
+#ifdef CFG_3C90X_XCVR
+ if (CFG_3C90X_XCVR == 255)
+ {
+ /** Clear the LanWorks register **/
+ a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16, 0);
+ }
+ else
+ {
+ /** Set the selected permanent-xcvrSelect in the
+ ** LanWorks register
+ **/
+ a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16,
+ XCVR_MAGIC + ((CFG_3C90X_XCVR) & 0x000F));
+ }
+#endif
+ }
+ else
+ {
+ for(i=0;i<=0x17;i++)
+ {
+ eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i);
+ }
+ }
+
+ /** Print identification message **/
+ printf("\n\n3C90X Driver 2.00 "
+ "Copyright 1999 LightSys Technology Services, Inc.\n"
+ "Portions Copyright 1999 Steve Smith\n");
+ printf("Provided with ABSOLUTELY NO WARRANTY.\n");
+#ifdef CFG_3C90X_BOOTROM_FIX
+ if (INF_3C90X.isBrev)
+ {
+ printf("NOTE: 3c905b bootrom fix enabled; has side "
+ "effects. See 3c90x.txt for info.\n");
+ }
+#endif
+ printf("-------------------------------------------------------"
+ "------------------------\n");
+
+ /** Retrieve the Hardware address and print it on the screen. **/
+ INF_3C90X.HWAddr[0] = eeprom[HWADDR_OFFSET + 0]>>8;
+ INF_3C90X.HWAddr[1] = eeprom[HWADDR_OFFSET + 0]&0xFF;
+ INF_3C90X.HWAddr[2] = eeprom[HWADDR_OFFSET + 1]>>8;
+ INF_3C90X.HWAddr[3] = eeprom[HWADDR_OFFSET + 1]&0xFF;
+ INF_3C90X.HWAddr[4] = eeprom[HWADDR_OFFSET + 2]>>8;
+ INF_3C90X.HWAddr[5] = eeprom[HWADDR_OFFSET + 2]&0xFF;
+ printf("MAC Address = %!\n", INF_3C90X.HWAddr);
+
+ /* Test if the link is good, if not continue */
+ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winDiagnostics4);
+ mstat = inw(INF_3C90X.IOAddr + regMediaStatus_4_w);
+ if((mstat & (1<<11)) == 0) {
+ printf("Valid link not established\n");
+ return 0;
+ }
+
+ /** Program the MAC address into the station address registers **/
+ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2);
+ outw(htons(eeprom[HWADDR_OFFSET + 0]), INF_3C90X.IOAddr + regStationAddress_2_3w);
+ outw(htons(eeprom[HWADDR_OFFSET + 1]), INF_3C90X.IOAddr + regStationAddress_2_3w+2);
+ outw(htons(eeprom[HWADDR_OFFSET + 2]), INF_3C90X.IOAddr + regStationAddress_2_3w+4);
+ outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0);
+ outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2);
+ outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4);
+
+ /** Fill in our entry in the etherboot arp table **/
+ for(i=0;i<ETH_ALEN;i++)
+ nic->node_addr[i] = (eeprom[HWADDR_OFFSET + i/2] >> (8*((i&1)^1))) & 0xff;
+
+ /** Read the media options register, print a message and set default
+ ** xcvr.
+ **
+ ** Uses Media Option command on B revision, Reset Option on non-B
+ ** revision cards -- same register address
+ **/
+ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
+ mopt = inw(INF_3C90X.IOAddr + regResetMediaOptions_3_w);
+
+ /** mask out VCO bit that is defined as 10baseFL bit on B-rev cards **/
+ if (! INF_3C90X.isBrev)
+ {
+ mopt &= 0x7F;
+ }
+
+ printf("Connectors present: ");
+ c = 0;
+ linktype = 0x0008;
+ if (mopt & 0x01)
+ {
+ printf("%s100Base-T4",(c++)?", ":"");
+ linktype = 0x0006;
+ }
+ if (mopt & 0x04)
+ {
+ printf("%s100Base-FX",(c++)?", ":"");
+ linktype = 0x0005;
+ }
+ if (mopt & 0x10)
+ {
+ printf("%s10Base-2",(c++)?", ":"");
+ linktype = 0x0003;
+ }
+ if (mopt & 0x20)
+ {
+ printf("%sAUI",(c++)?", ":"");
+ linktype = 0x0001;
+ }
+ if (mopt & 0x40)
+ {
+ printf("%sMII",(c++)?", ":"");
+ linktype = 0x0006;
+ }
+ if ((mopt & 0xA) == 0xA)
+ {
+ printf("%s10Base-T / 100Base-TX",(c++)?", ":"");
+ linktype = 0x0008;
+ }
+ else if ((mopt & 0xA) == 0x2)
+ {
+ printf("%s100Base-TX",(c++)?", ":"");
+ linktype = 0x0008;
+ }
+ else if ((mopt & 0xA) == 0x8)
+ {
+ printf("%s10Base-T",(c++)?", ":"");
+ linktype = 0x0008;
+ }
+ printf(".\n");
+
+ /** Determine transceiver type to use, depending on value stored in
+ ** eeprom 0x16
+ **/
+ if (INF_3C90X.isBrev)
+ {
+ if ((eeprom[0x16] & 0xFF00) == XCVR_MAGIC)
+ {
+ /** User-defined **/
+ linktype = eeprom[0x16] & 0x000F;
+ }
+ }
+ else
+ {
+#ifdef CFG_3C90X_XCVR
+ if (CFG_3C90X_XCVR != 255)
+ linktype = CFG_3C90X_XCVR;
+#endif /* CFG_3C90X_XCVR */
+
+ /** I don't know what MII MAC only mode is!!! **/
+ if (linktype == 0x0009)
+ {
+ if (INF_3C90X.isBrev)
+ printf("WARNING: MII External MAC Mode only supported on B-revision "
+ "cards!!!!\nFalling Back to MII Mode\n");
+ linktype = 0x0006;
+ }
+ }
+
+ /** enable DC converter for 10-Base-T **/
+ if (linktype == 0x0003)
+ {
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0);
+ }
+
+ /** Set the link to the type we just determined. **/
+ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
+ cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l);
+ cfg &= ~(0xF<<20);
+ cfg |= (linktype<<20);
+ outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l);
+
+ /** Now that we set the xcvr type, reset the Tx and Rx, re-enable. **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0x00);
+ while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
+ ;
+
+ if (!INF_3C90X.isBrev)
+ outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b);
+
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
+
+ /**
+ ** reset of the receiver on B-revision cards re-negotiates the link
+ ** takes several seconds (a computer eternity)
+ **/
+ if (INF_3C90X.isBrev)
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04);
+ else
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00);
+ while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
+ ;
+
+ /** Set the RX filter = receive only individual pkts & multicast & bcast. **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetRxFilter, 0x01 + 0x02 + 0x04);
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0);
+
+
+ /**
+ ** set Indication and Interrupt flags , acknowledge any IRQ's
+ **/
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetInterruptEnable, 0);
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
+ cmdSetIndicationEnable, 0x0014);
+ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
+ cmdAcknowledgeInterrupt, 0x661);
+
+ /** Set our exported functions **/
+ dev->disable = a3c90x_disable;
+ nic->poll = a3c90x_poll;
+ nic->transmit = a3c90x_transmit;
+ nic->irq = a3c90x_irq;
+
+ return 1;
+}
+
+
+static struct pci_id a3c90x_nics[] = {
+/* Original 90x revisions: */
+PCI_ROM(0x10b7, 0x9000, "3c905-tpo", "3Com900-TPO"), /* 10 Base TPO */
+PCI_ROM(0x10b7, 0x9001, "3c905-t4", "3Com900-Combo"), /* 10/100 T4 */
+PCI_ROM(0x10b7, 0x9050, "3c905-tpo100", "3Com905-TX"), /* 100 Base TX / 10/100 TPO */
+PCI_ROM(0x10b7, 0x9051, "3c905-combo", "3Com905-T4"), /* 100 Base T4 / 10 Base Combo */
+/* Newer 90xB revisions: */
+PCI_ROM(0x10b7, 0x9004, "3c905b-tpo", "3Com900B-TPO"), /* 10 Base TPO */
+PCI_ROM(0x10b7, 0x9005, "3c905b-combo", "3Com900B-Combo"), /* 10 Base Combo */
+PCI_ROM(0x10b7, 0x9006, "3c905b-tpb2", "3Com900B-2/T"), /* 10 Base TP and Base2 */
+PCI_ROM(0x10b7, 0x900a, "3c905b-fl", "3Com900B-FL"), /* 10 Base FL */
+PCI_ROM(0x10b7, 0x9055, "3c905b-tpo100", "3Com905B-TX"), /* 10/100 TPO */
+PCI_ROM(0x10b7, 0x9056, "3c905b-t4", "3Com905B-T4"), /* 10/100 T4 */
+PCI_ROM(0x10b7, 0x9058, "3c905b-9058", "3Com905B-9058"), /* Cyclone 10/100/BNC */
+PCI_ROM(0x10b7, 0x905a, "3c905b-fx", "3Com905B-FL"), /* 100 Base FX / 10 Base FX */
+/* Newer 90xC revision: */
+PCI_ROM(0x10b7, 0x9200, "3c905c-tpo", "3Com905C-TXM"), /* 10/100 TPO (3C905C-TXM) */
+PCI_ROM(0x10b7, 0x9202, "3c920b-emb-ati", "3c920B-EMB-WNM (ATI Radeon 9100 IGP)"), /* 3c920B-EMB-WNM (ATI Radeon 9100 IGP) */
+PCI_ROM(0x10b7, 0x9210, "3c920b-emb-wnm","3Com20B-EMB WNM"),
+PCI_ROM(0x10b7, 0x9800, "3c980", "3Com980-Cyclone"), /* Cyclone */
+PCI_ROM(0x10b7, 0x9805, "3c9805", "3Com9805"), /* Dual Port Server Cyclone */
+PCI_ROM(0x10b7, 0x7646, "3csoho100-tx", "3CSOHO100-TX"), /* Hurricane */
+PCI_ROM(0x10b7, 0x4500, "3c450", "3Com450 HomePNA Tornado"),
+PCI_ROM(0x10b7, 0x1201, "3c982a", "3Com982A"),
+PCI_ROM(0x10b7, 0x1202, "3c982b", "3Com982B"),
+};
+
+static struct pci_driver a3c90x_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "3C90X",
+ .probe = a3c90x_probe,
+ .ids = a3c90x_nics,
+ .id_count = sizeof(a3c90x_nics)/sizeof(a3c90x_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/3c90x.txt b/src/drivers/net/3c90x.txt
new file mode 100644
index 00000000..3d6746c5
--- /dev/null
+++ b/src/drivers/net/3c90x.txt
@@ -0,0 +1,307 @@
+
+ Instructions for use of the 3C90X driver for EtherBoot
+
+ Original 3C905B support by:
+ Greg Beeley (Greg.Beeley@LightSys.org),
+ LightSys Technology Services, Inc.
+ February 11, 1999
+
+ Updates for 3C90X family by:
+ Steve Smith (steve.smith@juno.com)
+ October 1, 1999
+
+ Minor documentation updates by
+ Greg Beeley (Greg.Beeley@LightSys.org)
+ March 29, 2000
+
+-------------------------------------------------------------------------------
+
+I OVERVIEW
+
+ The 3c90X series ethernet cards are a group of high-performance busmaster
+ DMA cards from 3Com. This particular driver supports both the 3c90x and
+ the 3c90xB revision cards. 3C90xC family support has been tested to some
+ degree but not extensively.
+
+ Here's the licensing information:
+
+ This program Copyright (C) 1999 LightSys Technology Services, Inc.
+ Portions Copyright (C) 1999 Steve Smith.
+
+ This program may be re-distributed in source or binary form, modified,
+ sold, or copied for any purpose, provided that the above copyright message
+ and this text are included with all source copies or derivative works, and
+ provided that the above copyright message and this text are included in the
+ documentation of any binary-only distributions. This program is
+ distributed WITHOUT ANY WARRANTY, without even the warranty of FITNESS FOR
+ A PARTICULAR PURPOSE or MERCHANTABILITY. Please read the associated
+ documentation "3c90x.txt" before compiling and using this driver.
+
+
+II FLASH PROMS
+
+ The 3c90xB cards, according to the 3Com documentation, only accept the
+ following flash memory chips:
+
+ Atmel AT29C512 (64 kilobyte)
+ Atmel AT29C010 (128 kilobyte)
+
+ The 3c90x cards, according to the 3Com documentation, accept the
+ following flash memory chips capacities:
+
+ 64 kb (8 kB)
+ 128 kb (16 kB)
+ 256 kb (32 kB) and
+ 512 kb (64 kB)
+
+ Atmel AT29C512 (64 kilobyte) chips are specifically listed for both
+ adapters, but flashing on the 3c905b cards would only be supported
+ through the Atmel parts. Any device, of the supported size, should
+ be supported when programmed by a dedicated PROM programmer (e.g.
+ not the card).
+
+ To use this driver in such a PROM, visit Atmel's web site and download
+ their .PDF file containing a list of their distributors. Contact the
+ distributors for pricing information. The prices are quite reasonable
+ (about $3 US each for the 64 kB part), and are comparable to what one would
+ expect for similarly sized standard EPROMs. And, the flash chips are much
+ easier to work with, as they don't need to be UV-erased to be reprogrammed.
+ The 3C905B card actually provides a method to program the flash memory
+ while it is resident on board the card itself; if someone would like to
+ write a small DOS program to do the programming, I can provide the
+ information about the registers and so forth.
+
+ A utility program, 3c90xutil, is provided with Etherboot in the 'contrib'
+ directory that allows for the on-board flashing of the ROM while Linux
+ is running. The program has been successfully used under Linux, but I
+ have heard problem reports of its use under FreeBSD. Anyone willing to
+ make it work under FreeBSD is more than welcome to do so!
+
+ You also have the option of using EPROM chips - the 3C905B-TX-NM has been
+ successfully tested with 27C256 (32kB) and 27C512 (64kB) chips with a
+ specified access time of 100ns and faster.
+
+
+III GENERAL USE
+
+ Normally, the basic procedure for using this driver is as follows:
+
+ 1. Run the 3c90xcfg program on the driver diskette to enable the
+ boot PROM and set it to 64k or 128k, as appropriate.
+ 2. Build the appropriate 3c90x.fd0 or 3c90x.fd0 floppy image with
+ possibly the value CFG_3C90X_XCVR defined to the transceiver type that
+ you want to use (i.e., 10/100 rj45, AUI, coax, MII).
+ 3. Run the floppy image on the PC to be network booted, to get
+ it configured, and to verify that it will boot properly.
+ 4. Build the 3c90x.rom or 3c90x.lzrom PROM image and program
+ it into the flash or EPROM memory chip.
+ 5. Put the PROM in the ethernet card, boot and enable 'boot from
+ network first' in the system BIOS, save and reboot.
+
+ Here are some issues to be aware of:
+
+ 1. If you experience crashes or different behaviour when using the
+ boot PROM, add the setting CFG_3C90X_BOOTROM_FIX and go through the
+ steps 2-5 above. This works around a bug in some 3c905B cards (see
+ below), but has some side-effects which may not be desirable.
+ Please note that you have to boot off a floppy (not PROM!) once for
+ this fix to take effect.
+ 2. The possible need to manually set the CFG_3C90X_XCVR value to
+ configure the transceiver type. Values are listed below.
+ 3. The possible need to define CFG_3C90X_PRESERVE_XCVR for use in
+ operating systems that don't intelligently determine the
+ transceiver type.
+
+ Some things that are on the 'To-Do' list, perhaps for me, but perhaps
+ for any other volunteers out there:
+
+ 1. Extend the driver to fully implement the auto-select
+ algorithm if the card has multiple media ports.
+ 2. Fix any bugs in the code <grin>....
+ 3. Extend the driver to support the 3c905c revision cards
+ "officially". Right now, the support has been primarily empirical
+ and not based on 3c905C documentation.
+
+ Now for the details....
+
+ This driver has been tested on roughly 300 systems. The main two
+ configuration issues to contend with are:
+
+ 1. Ensure that PCI Busmastering is enabled for the adapter (configured
+ in the CMOS setup)
+ 2. Some systems don't work properly with the adapter when plug and
+ play OS is enabled; I always set it to "No" or "Disabled" -- this makes
+ it easier and really doesn't adversely affect anything.
+
+ Roughly 95% of the systems worked when configured properly. A few
+ have issues with booting locally once the boot PROM has been installed
+ (this number has been less than 2%). Other configuration issues that
+ to check:
+
+ 1. Newer BIOS's actually work correctly with the network boot order.
+ Set the network adapter first. Most older BIOS's automatically go to
+ the network boot PROM first.
+ 2. For systems where the adapter was already installed and is just
+ having the PROM installed, try setting the "reset configuration data"
+ to yes in the CMOS setup if the BIOS isn't seen at first. If your BIOS
+ doesn't have this option, remove the card, start the system, shut down,
+ install the card and restart (or switch to a different PCI slot).
+ 3. Make sure the CMOS security settings aren't preventing a boot.
+
+ The 3c905B cards have a significant 'bug' that relates to the flash prom:
+ unless the card is set internally to the MII transceiver, it will only
+ read the first 8k of the PROM image. Don't ask why -- it seems really
+ obscure, but it has to do with the way they mux'd the address lines
+ from the PCI bus to the ROM. Unfortunately, most of us are not using
+ MII transceivers, and even the .lzrom image ends up being just a little
+ bit larger than 8k. Note that the workaround for this is disabled by
+ default, because the Windows NT 4.0 driver does not like it (no packets
+ are transmitted).
+
+ So, the solution that I've used is to internally set the card's nvram
+ configuration to use MII when it boots. The 3c905b driver does this
+ automatically. This way, the 16k prom image can be loaded into memory,
+ and then the 3c905b driver can set the temporary configuration of the
+ card to an appropriate value, either configurable by the user or chosen
+ by the driver.
+
+ To enable the 3c905B bugfix, which is necessary for these cards when
+ booting from the Flash ROM, define -DCFG_3C90X_BOOTROM_FIX when building,
+ create a floppy image and boot it once.
+ Thereafter, the card should accept the larger prom image.
+
+ The driver should choose an appropriate transceiver on the card. However,
+ if it doesn't on your card or if you need to, for instance, set your
+ card to 10mbps when connected to an unmanaged 10/100 hub, you can specify
+ which transceiver you want to use. To do this, build the 3c905b.fd0
+ image with -DCFG_3C90X_XCVR=x, where 'x' is one of the following
+ values:
+
+ 0 10Base-T
+ 1 10mbps AUI
+ 3 10Base-2 (thinnet/coax)
+ 4 100Base-TX
+ 5 100Base-FX
+ 6 MII
+ 8 Auto-negotiation 10Base-T / 100Base-TX (usually the default)
+ 9 MII External MAC Mode
+ 255 Allow driver to choose an 'appropriate' media port.
+
+ Then proceed from step 2 in the above 'general use' instructions. The
+ .rom image can be built with CFG_3C90X_XCVR set to a value, but you
+ normally don't want to do this, since it is easier to change the
+ transceiver type by rebuilding a new floppy, changing the BIOS to floppy
+ boot, booting, and then changing the BIOS back to network boot. If
+ CFG_3C90X_XCVR is not set in a particular build, it just uses the
+ current configuration (either its 'best guess' or whatever the stored
+ CFG_3C90X_XCVR value was from the last time it was set).
+
+ [[ Note for the more technically inclined: The CFG_3C90X_XCVR value is
+ programmed into a register in the card's NVRAM that was reserved for
+ LanWorks PROM images to use. When the driver boots, the card comes
+ up in MII mode, and the driver checks the LanWorks register to find
+ out if the user specified a transceiver type. If it finds that
+ information, it uses that, otherwise it picks a transceiver that the
+ card has based on the 3c905b's MediaOptions register. This driver isn't
+ quite smart enough to always determine which media port is actually
+ _connected_; maybe someone else would like to take on that task (it
+ actually involves sending a self-directed packet and seeing if it
+ comes back. IF it does, that port is connected). ]]
+
+ Another issue to keep in mind is that it is possible that some OS'es
+ might not be happy with the way I've handled the PROM-image hack with
+ setting MII mode on bootup. Linux 2.0.35 does not have this problem.
+ Behavior of other systems may vary. The 3com documentation specifically
+ says that, at least with the card that I have, the device driver in the
+ OS should auto-select the media port, so other drivers should work fine
+ with this 'hack'. However, if yours doesn't seem to, you can try defining
+ CFG_3C90X_PRESERVE_XCVR when building to cause Etherboot to keep the
+ working setting (that allowed the bootp/tftp process) across the eth_reset
+ operation.
+
+
+IV FOR DEVELOPERS....
+
+ If you would like to fix/extend/etc. this driver, feel free to do so; just
+ be sure you can test the modified version on the 3c905B-TX cards that the
+ driver was originally designed for. This section of this document gives
+ some information that might be relevant to a programmer.
+
+ A. Main Entry Point
+
+ a3c90x_probe is the main entry point for this driver. It is referred
+ to in an array in 'config.c'.
+
+ B. Other Important Functions
+
+ The functions a3c90x_transmit, a3c90x_poll, a3c90x_reset, and
+ a3c90x_disable are static functions that EtherBoot finds out about
+ as a result of a3c90x_probe setting entries in the nic structure
+ for them. The EtherBoot framework does not use interrupts. It is
+ polled. All transmit and receive operations are initiated by the
+ etherboot framework, not by an interrupt or by the driver.
+
+ C. Internal Functions
+
+ The following functions are internal to the driver:
+
+ a3c90x_internal_IssueCommand - sends a command to the 3c905b card.
+ a3c90x_internal_SetWindow - shifts between one of eight register
+ windows onboard the 3c90x. The bottom 16 bytes of the card's
+ I/O space are multiplexed among 128 bytes, only 16 of which are
+ visible at any one time. This SetWindow function selects one of
+ the eight sets.
+ a3c90x_internal_ReadEeprom - reads a word (16 bits) from the
+ card's onboard nvram. This is NOT the BIOS boot rom. This is
+ where the card stores such things as its hardware address.
+ a3c90x_internal_WriteEeprom - writes a word (16 bits) to the
+ card's nvram, and recomputes the eeprom checksum.
+ a3c90x_internal_WriteEepromWord - writes a word (16 bits) to the
+ card's nvram. Used by the above routine.
+ a3c90x_internal_WriteEepromWord - writes a word (16 bits) to the
+ card's nvram. Used by the above routine.
+
+ D. Globals
+
+ All global variables are inside a global structure named INF_3C90X.
+ So, wherever you see that structure referenced, you know the variable
+ is a global. Just keeps things a little neater.
+
+ E. Enumerations
+
+ There are quite a few enumerated type definitions for registers and
+ so forth, many for registers that I didn't even touch in the driver.
+ Register types start with 'reg', window numbers (for SetWindow)
+ start with 'win', and commands (for IssueCommand) start with 'cmd'.
+ Register offsets also include an indication in the name as to the
+ size of the register (_b = byte, _w = word, _l = long), and which
+ window the register is in, if it is windowed (0-7).
+
+ F. Why the 'a3c90x' name?
+
+ I had to come up with a letter at the beginning of all of the
+ identifiers, since 3com so conveniently had their name start with a
+ number. Another driver used 't' (for 'three'?); I chose 'a' for
+ no reason at all.
+
+Addendum by Jorge L. deLyra <delyra@latt.if.usp.br>, 22Nov2000 re
+working around the 3C905 hardware bug mentioned above:
+
+Use this floppy to fix any 3COM model 3C905B PCI 10/100 Ethernet cards
+that fail to load and run the boot program the first time around. If
+they have a "Lucent" rather than a "Broadcom" chipset these cards have
+a configuration bug that causes a hang when trying to load the boot
+program from the PROM, if you try to use them right out of the box.
+
+The boot program in this floppy is the file named 3c905b-tpo100.rom
+from Etherboot version 4.6.10, compiled with the bugfix parameter
+
+ CFG_3C90X_BOOTROM_FIX
+
+You have to take the chip off the card and boot the system once using
+this floppy. Once loaded from the floppy, the boot program will access
+the card and change some setting in it, correcting the problem. After
+that you may use either this boot program or the normal one, compiled
+without this bugfix parameter, to boot the machine from the PROM chip.
+
+[Any recent Etherboot version should do, not just 4.6.10 - Ed.]
diff --git a/src/drivers/net/cs89x0.c b/src/drivers/net/cs89x0.c
new file mode 100644
index 00000000..42de1f2a
--- /dev/null
+++ b/src/drivers/net/cs89x0.c
@@ -0,0 +1,720 @@
+#ifdef ALLMULTI
+#error multicast support is not yet implemented
+#endif
+/* cs89x0.c: A Crystal Semiconductor CS89[02]0 driver for etherboot. */
+/*
+ Permission is granted to distribute the enclosed cs89x0.[ch] driver
+ only in conjunction with the Etherboot package. The code is
+ ordinarily distributed under the GPL.
+
+ Russ Nelson, January 2000
+
+ ChangeLog:
+
+ Thu Dec 6 22:40:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
+
+ * disabled all "advanced" features; this should make the code more reliable
+
+ * reorganized the reset function
+
+ * always reset the address port, so that autoprobing will continue working
+
+ * some cosmetic changes
+
+ * 2.5
+
+ Thu Dec 5 21:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
+
+ * tested the code against a CS8900 card
+
+ * lots of minor bug fixes and adjustments
+
+ * this is the first release, that actually works! it still requires some
+ changes in order to be more tolerant to different environments
+
+ * 4
+
+ Fri Nov 22 23:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
+
+ * read the manuals for the CS89x0 chipsets and took note of all the
+ changes that will be neccessary in order to adapt Russel Nelson's code
+ to the requirements of a BOOT-Prom
+
+ * 6
+
+ Thu Nov 19 22:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
+
+ * Synched with Russel Nelson's current code (v1.00)
+
+ * 2
+
+ Thu Nov 12 18:00:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
+
+ * Cleaned up some of the code and tried to optimize the code size.
+
+ * 1.5
+
+ Sun Nov 10 16:30:00 1996 Markus Gutschke <gutschk@math.uni-muenster.de>
+
+ * First experimental release. This code compiles fine, but I
+ have no way of testing whether it actually works.
+
+ * I did not (yet) bother to make the code 16bit aware, so for
+ the time being, it will only work for Etherboot/32.
+
+ * 12
+
+ */
+
+#include "etherboot.h"
+#include "nic.h"
+#include "isa.h"
+#include "cs89x0.h"
+
+#ifndef EMBEDDED
+static unsigned short eth_nic_base;
+#else
+static unsigned long eth_nic_base;
+#endif
+static unsigned long eth_mem_start;
+static unsigned short eth_irqno;
+static unsigned short eth_cs_type; /* one of: CS8900, CS8920, CS8920M */
+static unsigned short eth_auto_neg_cnf;
+static unsigned short eth_adapter_cnf;
+static unsigned short eth_linectl;
+
+/*************************************************************************
+ CS89x0 - specific routines
+**************************************************************************/
+
+static inline int readreg(int portno)
+{
+ outw(portno, eth_nic_base + ADD_PORT);
+ return inw(eth_nic_base + DATA_PORT);
+}
+
+static inline void writereg(int portno, int value)
+{
+ outw(portno, eth_nic_base + ADD_PORT);
+ outw(value, eth_nic_base + DATA_PORT);
+ return;
+}
+
+/*************************************************************************
+EEPROM access
+**************************************************************************/
+
+static int wait_eeprom_ready(void)
+{
+ unsigned long tmo = currticks() + 4*TICKS_PER_SEC;
+
+ /* check to see if the EEPROM is ready, a timeout is used -
+ just in case EEPROM is ready when SI_BUSY in the
+ PP_SelfST is clear */
+ while(readreg(PP_SelfST) & SI_BUSY) {
+ if (currticks() >= tmo)
+ return -1; }
+ return 0;
+}
+
+static int get_eeprom_data(int off, int len, unsigned short *buffer)
+{
+ int i;
+
+#ifdef EDEBUG
+ printf("\ncs: EEPROM data from %hX for %hX:",off,len);
+#endif
+ for (i = 0; i < len; i++) {
+ if (wait_eeprom_ready() < 0)
+ return -1;
+ /* Now send the EEPROM read command and EEPROM location
+ to read */
+ writereg(PP_EECMD, (off + i) | EEPROM_READ_CMD);
+ if (wait_eeprom_ready() < 0)
+ return -1;
+ buffer[i] = readreg(PP_EEData);
+#ifdef EDEBUG
+ if (!(i%10))
+ printf("\ncs: ");
+ printf("%hX ", buffer[i]);
+#endif
+ }
+#ifdef EDEBUG
+ putchar('\n');
+#endif
+
+ return(0);
+}
+
+static int get_eeprom_chksum(int off __unused, int len, unsigned short *buffer)
+{
+ int i, cksum;
+
+ cksum = 0;
+ for (i = 0; i < len; i++)
+ cksum += buffer[i];
+ cksum &= 0xffff;
+ if (cksum == 0)
+ return 0;
+ return -1;
+}
+
+/*************************************************************************
+Activate all of the available media and probe for network
+**************************************************************************/
+
+static void clrline(void)
+{
+ int i;
+
+ putchar('\r');
+ for (i = 79; i--; ) putchar(' ');
+ printf("\rcs: ");
+ return;
+}
+
+static void control_dc_dc(int on_not_off)
+{
+ unsigned int selfcontrol;
+ unsigned long tmo = currticks() + TICKS_PER_SEC;
+
+ /* control the DC to DC convertor in the SelfControl register. */
+ selfcontrol = HCB1_ENBL; /* Enable the HCB1 bit as an output */
+ if (((eth_adapter_cnf & A_CNF_DC_DC_POLARITY) != 0) ^ on_not_off)
+ selfcontrol |= HCB1;
+ else
+ selfcontrol &= ~HCB1;
+ writereg(PP_SelfCTL, selfcontrol);
+
+ /* Wait for the DC/DC converter to power up - 1000ms */
+ while (currticks() < tmo);
+
+ return;
+}
+
+static int detect_tp(void)
+{
+ unsigned long tmo;
+
+ /* Turn on the chip auto detection of 10BT/ AUI */
+
+ clrline(); printf("attempting %s:","TP");
+
+ /* If connected to another full duplex capable 10-Base-T card
+ the link pulses seem to be lost when the auto detect bit in
+ the LineCTL is set. To overcome this the auto detect bit
+ will be cleared whilst testing the 10-Base-T interface.
+ This would not be necessary for the sparrow chip but is
+ simpler to do it anyway. */
+ writereg(PP_LineCTL, eth_linectl &~ AUI_ONLY);
+ control_dc_dc(0);
+
+ /* Delay for the hardware to work out if the TP cable is
+ present - 150ms */
+ for (tmo = currticks() + 4; currticks() < tmo; );
+
+ if ((readreg(PP_LineST) & LINK_OK) == 0)
+ return 0;
+
+ if (eth_cs_type != CS8900) {
+
+ writereg(PP_AutoNegCTL, eth_auto_neg_cnf & AUTO_NEG_MASK);
+
+ if ((eth_auto_neg_cnf & AUTO_NEG_BITS) == AUTO_NEG_ENABLE) {
+ printf(" negotiating duplex... ");
+ while (readreg(PP_AutoNegST) & AUTO_NEG_BUSY) {
+ if (currticks() - tmo > 40*TICKS_PER_SEC) {
+ printf("time out ");
+ break;
+ }
+ }
+ }
+ if (readreg(PP_AutoNegST) & FDX_ACTIVE)
+ printf("using full duplex");
+ else
+ printf("using half duplex");
+ }
+
+ return A_CNF_MEDIA_10B_T;
+}
+
+/* send a test packet - return true if carrier bits are ok */
+static int send_test_pkt(struct nic *nic)
+{
+ static unsigned char testpacket[] = { 0,0,0,0,0,0, 0,0,0,0,0,0,
+ 0, 46, /*A 46 in network order */
+ 0, 0, /*DSAP=0 & SSAP=0 fields */
+ 0xf3,0 /*Control (Test Req+P bit set)*/ };
+ unsigned long tmo;
+
+ writereg(PP_LineCTL, readreg(PP_LineCTL) | SERIAL_TX_ON);
+
+ memcpy(testpacket, nic->node_addr, ETH_ALEN);
+ memcpy(testpacket+ETH_ALEN, nic->node_addr, ETH_ALEN);
+
+ outw(TX_AFTER_ALL, eth_nic_base + TX_CMD_PORT);
+ outw(ETH_ZLEN, eth_nic_base + TX_LEN_PORT);
+
+ /* Test to see if the chip has allocated memory for the packet */
+ for (tmo = currticks() + 2;
+ (readreg(PP_BusST) & READY_FOR_TX_NOW) == 0; )
+ if (currticks() >= tmo)
+ return(0);
+
+ /* Write the contents of the packet */
+ outsw(eth_nic_base + TX_FRAME_PORT, testpacket,
+ (ETH_ZLEN+1)>>1);
+
+ printf(" sending test packet ");
+ /* wait a couple of timer ticks for packet to be received */
+ for (tmo = currticks() + 2; currticks() < tmo; );
+
+ if ((readreg(PP_TxEvent) & TX_SEND_OK_BITS) == TX_OK) {
+ printf("succeeded");
+ return 1;
+ }
+ printf("failed");
+ return 0;
+}
+
+
+static int detect_aui(struct nic *nic)
+{
+ clrline(); printf("attempting %s:","AUI");
+ control_dc_dc(0);
+
+ writereg(PP_LineCTL, (eth_linectl & ~AUTO_AUI_10BASET) | AUI_ONLY);
+
+ if (send_test_pkt(nic)) {
+ return A_CNF_MEDIA_AUI; }
+ else
+ return 0;
+}
+
+static int detect_bnc(struct nic *nic)
+{
+ clrline(); printf("attempting %s:","BNC");
+ control_dc_dc(1);
+
+ writereg(PP_LineCTL, (eth_linectl & ~AUTO_AUI_10BASET) | AUI_ONLY);
+
+ if (send_test_pkt(nic)) {
+ return A_CNF_MEDIA_10B_2; }
+ else
+ return 0;
+}
+
+/**************************************************************************
+ETH_RESET - Reset adapter
+***************************************************************************/
+
+static void cs89x0_reset(struct nic *nic)
+{
+ int i;
+ unsigned long reset_tmo;
+
+ writereg(PP_SelfCTL, readreg(PP_SelfCTL) | POWER_ON_RESET);
+
+ /* wait for two ticks; that is 2*55ms */
+ for (reset_tmo = currticks() + 2; currticks() < reset_tmo; );
+
+ if (eth_cs_type != CS8900) {
+ /* Hardware problem requires PNP registers to be reconfigured
+ after a reset */
+ if (eth_irqno != 0xFFFF) {
+ outw(PP_CS8920_ISAINT, eth_nic_base + ADD_PORT);
+ outb(eth_irqno, eth_nic_base + DATA_PORT);
+ outb(0, eth_nic_base + DATA_PORT + 1); }
+
+ if (eth_mem_start) {
+ outw(PP_CS8920_ISAMemB, eth_nic_base + ADD_PORT);
+ outb((eth_mem_start >> 8) & 0xff, eth_nic_base + DATA_PORT);
+ outb((eth_mem_start >> 24) & 0xff, eth_nic_base + DATA_PORT + 1); } }
+
+ /* Wait until the chip is reset */
+ for (reset_tmo = currticks() + 2;
+ (readreg(PP_SelfST) & INIT_DONE) == 0 &&
+ currticks() < reset_tmo; );
+
+ /* disable interrupts and memory accesses */
+ writereg(PP_BusCTL, 0);
+
+ /* set the ethernet address */
+ for (i=0; i < ETH_ALEN/2; i++)
+ writereg(PP_IA+i*2,
+ nic->node_addr[i*2] |
+ (nic->node_addr[i*2+1] << 8));
+
+ /* receive only error free packets addressed to this card */
+ writereg(PP_RxCTL, DEF_RX_ACCEPT);
+
+ /* do not generate any interrupts on receive operations */
+ writereg(PP_RxCFG, 0);
+
+ /* do not generate any interrupts on transmit operations */
+ writereg(PP_TxCFG, 0);
+
+ /* do not generate any interrupts on buffer operations */
+ writereg(PP_BufCFG, 0);
+
+ /* reset address port, so that autoprobing will keep working */
+ outw(PP_ChipID, eth_nic_base + ADD_PORT);
+
+ return;
+}
+
+/**************************************************************************
+ETH_TRANSMIT - Transmit a frame
+***************************************************************************/
+
+static void cs89x0_transmit(
+ struct nic *nic,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+ unsigned long tmo;
+ int sr;
+
+ /* does this size have to be rounded??? please,
+ somebody have a look in the specs */
+ if ((sr = ((s + ETH_HLEN + 1)&~1)) < ETH_ZLEN)
+ sr = ETH_ZLEN;
+
+retry:
+ /* initiate a transmit sequence */
+ outw(TX_AFTER_ALL, eth_nic_base + TX_CMD_PORT);
+ outw(sr, eth_nic_base + TX_LEN_PORT);
+
+ /* Test to see if the chip has allocated memory for the packet */
+ if ((readreg(PP_BusST) & READY_FOR_TX_NOW) == 0) {
+ /* Oops... this should not happen! */
+ printf("cs: unable to send packet; retrying...\n");
+ for (tmo = currticks() + 5*TICKS_PER_SEC; currticks() < tmo; );
+ cs89x0_reset(nic);
+ goto retry; }
+
+ /* Write the contents of the packet */
+ outsw(eth_nic_base + TX_FRAME_PORT, d, ETH_ALEN/2);
+ outsw(eth_nic_base + TX_FRAME_PORT, nic->node_addr,
+ ETH_ALEN/2);
+ outw(((t >> 8)&0xFF)|(t << 8), eth_nic_base + TX_FRAME_PORT);
+ outsw(eth_nic_base + TX_FRAME_PORT, p, (s+1)/2);
+ for (sr = sr/2 - (s+1)/2 - ETH_ALEN - 1; sr-- > 0;
+ outw(0, eth_nic_base + TX_FRAME_PORT));
+
+ /* wait for transfer to succeed */
+ for (tmo = currticks()+5*TICKS_PER_SEC;
+ (s = readreg(PP_TxEvent)&~0x1F) == 0 && currticks() < tmo;)
+ /* nothing */ ;
+ if ((s & TX_SEND_OK_BITS) != TX_OK) {
+ printf("\ntransmission error %#hX\n", s);
+ }
+
+ return;
+}
+
+/**************************************************************************
+ETH_POLL - Wait for a frame
+***************************************************************************/
+
+static int cs89x0_poll(struct nic *nic, int retrieve)
+{
+ int status;
+
+ status = readreg(PP_RxEvent);
+
+ if ((status & RX_OK) == 0)
+ return(0);
+
+ if ( ! retrieve ) return 1;
+
+ status = inw(eth_nic_base + RX_FRAME_PORT);
+ nic->packetlen = inw(eth_nic_base + RX_FRAME_PORT);
+ insw(eth_nic_base + RX_FRAME_PORT, nic->packet, nic->packetlen >> 1);
+ if (nic->packetlen & 1)
+ nic->packet[nic->packetlen-1] = inw(eth_nic_base + RX_FRAME_PORT);
+ return 1;
+}
+
+static void cs89x0_disable(struct dev *dev)
+{
+ struct nic *nic = (struct nic *)dev;
+ cs89x0_reset(nic);
+}
+
+static void cs89x0_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/**************************************************************************
+ETH_PROBE - Look for an adapter
+***************************************************************************/
+
+static int cs89x0_probe(struct dev *dev, unsigned short *probe_addrs __unused)
+{
+ struct nic *nic = (struct nic *)dev;
+ static const unsigned int netcard_portlist[] = {
+#ifdef CS_SCAN
+ CS_SCAN,
+#else /* use "conservative" default values for autoprobing */
+#ifndef EMBEDDED
+ 0x300,0x320,0x340,0x200,0x220,0x240,
+ 0x260,0x280,0x2a0,0x2c0,0x2e0,
+ /* if that did not work, then be more aggressive */
+ 0x301,0x321,0x341,0x201,0x221,0x241,
+ 0x261,0x281,0x2a1,0x2c1,0x2e1,
+#else
+ 0x01000300,
+#endif
+#endif
+ 0};
+
+ int i, result = -1;
+ unsigned rev_type = 0, ioaddr, ioidx, isa_cnf, cs_revision;
+ unsigned short eeprom_buff[CHKSUM_LEN];
+
+
+ for (ioidx = 0; (ioaddr=netcard_portlist[ioidx++]) != 0; ) {
+ /* if they give us an odd I/O address, then do ONE write to
+ the address port, to get it back to address zero, where we
+ expect to find the EISA signature word. */
+ if (ioaddr & 1) {
+ ioaddr &= ~1;
+ if ((inw(ioaddr + ADD_PORT) & ADD_MASK) != ADD_SIG)
+ continue;
+ outw(PP_ChipID, ioaddr + ADD_PORT);
+ }
+
+ if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG)
+ continue;
+ eth_nic_base = ioaddr;
+
+ /* get the chip type */
+ rev_type = readreg(PRODUCT_ID_ADD);
+ eth_cs_type = rev_type &~ REVISON_BITS;
+ cs_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
+
+ printf("\ncs: cs89%c0%s rev %c, base %#hX",
+ eth_cs_type==CS8900?'0':'2',
+ eth_cs_type==CS8920M?"M":"",
+ cs_revision,
+ eth_nic_base);
+#ifndef EMBEDDED
+ /* First check to see if an EEPROM is attached*/
+ if ((readreg(PP_SelfST) & EEPROM_PRESENT) == 0) {
+ printf("\ncs: no EEPROM...\n");
+ outw(PP_ChipID, eth_nic_base + ADD_PORT);
+ continue; }
+ else if (get_eeprom_data(START_EEPROM_DATA,CHKSUM_LEN,
+ eeprom_buff) < 0) {
+ printf("\ncs: EEPROM read failed...\n");
+ outw(PP_ChipID, eth_nic_base + ADD_PORT);
+ continue; }
+ else if (get_eeprom_chksum(START_EEPROM_DATA,CHKSUM_LEN,
+ eeprom_buff) < 0) {
+ printf("\ncs: EEPROM checksum bad...\n");
+ outw(PP_ChipID, eth_nic_base + ADD_PORT);
+ continue; }
+
+ /* get transmission control word but keep the
+ autonegotiation bits */
+ eth_auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET/2];
+ /* Store adapter configuration */
+ eth_adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET/2];
+ /* Store ISA configuration */
+ isa_cnf = eeprom_buff[ISA_CNF_OFFSET/2];
+
+ /* store the initial memory base address */
+ eth_mem_start = eeprom_buff[PACKET_PAGE_OFFSET/2] << 8;
+
+ printf("%s%s%s, addr ",
+ (eth_adapter_cnf & A_CNF_10B_T)?", RJ-45":"",
+ (eth_adapter_cnf & A_CNF_AUI)?", AUI":"",
+ (eth_adapter_cnf & A_CNF_10B_2)?", BNC":"");
+
+ /* If this is a CS8900 then no pnp soft */
+ if (eth_cs_type != CS8900 &&
+ /* Check if the ISA IRQ has been set */
+ (i = readreg(PP_CS8920_ISAINT) & 0xff,
+ (i != 0 && i < CS8920_NO_INTS)))
+ eth_irqno = i;
+ else {
+ i = isa_cnf & INT_NO_MASK;
+ if (eth_cs_type == CS8900) {
+ /* the table that follows is dependent
+ upon how you wired up your cs8900
+ in your system. The table is the
+ same as the cs8900 engineering demo
+ board. irq_map also depends on the
+ contents of the table. Also see
+ write_irq, which is the reverse
+ mapping of the table below. */
+ if (i < 4) i = "\012\013\014\005"[i];
+ else printf("\ncs: BUG: isa_config is %d\n", i); }
+ eth_irqno = i; }
+
+ /* Retrieve and print the ethernet address. */
+ for (i=0; i<ETH_ALEN; i++) {
+ nic->node_addr[i] = ((unsigned char *)eeprom_buff)[i];
+ }
+ printf("%!\n", nic->node_addr);
+#endif
+#ifdef EMBEDDED
+ /* Retrieve and print the ethernet address. */
+ {
+ unsigned char MAC_HW_ADDR[6]={MAC_HW_ADDR_DRV};
+ memcpy(nic->node_addr, MAC_HW_ADDR, 6);
+ }
+ printf("\n%!\n", nic->node_addr);
+
+ eth_adapter_cnf = A_CNF_10B_T | A_CNF_MEDIA_10B_T;
+ eth_auto_neg_cnf = EE_AUTO_NEG_ENABLE | IMM_BIT;
+#endif
+#ifndef EMBEDDED
+ /* Set the LineCTL quintuplet based on adapter
+ configuration read from EEPROM */
+ if ((eth_adapter_cnf & A_CNF_EXTND_10B_2) &&
+ (eth_adapter_cnf & A_CNF_LOW_RX_SQUELCH))
+ eth_linectl = LOW_RX_SQUELCH;
+ else
+ eth_linectl = 0;
+
+ /* check to make sure that they have the "right"
+ hardware available */
+ switch(eth_adapter_cnf & A_CNF_MEDIA_TYPE) {
+ case A_CNF_MEDIA_10B_T: result = eth_adapter_cnf & A_CNF_10B_T;
+ break;
+ case A_CNF_MEDIA_AUI: result = eth_adapter_cnf & A_CNF_AUI;
+ break;
+ case A_CNF_MEDIA_10B_2: result = eth_adapter_cnf & A_CNF_10B_2;
+ break;
+ default: result = eth_adapter_cnf & (A_CNF_10B_T | A_CNF_AUI |
+ A_CNF_10B_2);
+ }
+ if (!result) {
+ printf("cs: EEPROM is configured for unavailable media\n");
+ error:
+ writereg(PP_LineCTL, readreg(PP_LineCTL) &
+ ~(SERIAL_TX_ON | SERIAL_RX_ON));
+ outw(PP_ChipID, eth_nic_base + ADD_PORT);
+ continue;
+ }
+#endif
+ /* Initialize the card for probing of the attached media */
+ cs89x0_reset(nic);
+
+ /* set the hardware to the configured choice */
+ switch(eth_adapter_cnf & A_CNF_MEDIA_TYPE) {
+ case A_CNF_MEDIA_10B_T:
+ result = detect_tp();
+ if (!result) {
+ clrline();
+ printf("10Base-T (RJ-45%s",
+ ") has no cable\n"); }
+ /* check "ignore missing media" bit */
+ if (eth_auto_neg_cnf & IMM_BIT)
+ /* Yes! I don't care if I see a link pulse */
+ result = A_CNF_MEDIA_10B_T;
+ break;
+ case A_CNF_MEDIA_AUI:
+ result = detect_aui(nic);
+ if (!result) {
+ clrline();
+ printf("10Base-5 (AUI%s",
+ ") has no cable\n"); }
+ /* check "ignore missing media" bit */
+ if (eth_auto_neg_cnf & IMM_BIT)
+ /* Yes! I don't care if I see a carrrier */
+ result = A_CNF_MEDIA_AUI;
+ break;
+ case A_CNF_MEDIA_10B_2:
+ result = detect_bnc(nic);
+ if (!result) {
+ clrline();
+ printf("10Base-2 (BNC%s",
+ ") has no cable\n"); }
+ /* check "ignore missing media" bit */
+ if (eth_auto_neg_cnf & IMM_BIT)
+ /* Yes! I don't care if I can xmit a packet */
+ result = A_CNF_MEDIA_10B_2;
+ break;
+ case A_CNF_MEDIA_AUTO:
+ writereg(PP_LineCTL, eth_linectl | AUTO_AUI_10BASET);
+ if (eth_adapter_cnf & A_CNF_10B_T)
+ if ((result = detect_tp()) != 0)
+ break;
+ if (eth_adapter_cnf & A_CNF_AUI)
+ if ((result = detect_aui(nic)) != 0)
+ break;
+ if (eth_adapter_cnf & A_CNF_10B_2)
+ if ((result = detect_bnc(nic)) != 0)
+ break;
+ clrline(); printf("no media detected\n");
+ goto error;
+ }
+ clrline();
+ switch(result) {
+ case 0: printf("no network cable attached to configured media\n");
+ goto error;
+ case A_CNF_MEDIA_10B_T: printf("using 10Base-T (RJ-45)\n");
+ break;
+ case A_CNF_MEDIA_AUI: printf("using 10Base-5 (AUI)\n");
+ break;
+ case A_CNF_MEDIA_10B_2: printf("using 10Base-2 (BNC)\n");
+ break;
+ }
+
+ /* Turn on both receive and transmit operations */
+ writereg(PP_LineCTL, readreg(PP_LineCTL) | SERIAL_RX_ON |
+ SERIAL_TX_ON);
+
+ break;
+#ifdef EMBEDDED
+ error:
+ writereg(PP_LineCTL, readreg(PP_LineCTL) &
+ ~(SERIAL_TX_ON | SERIAL_RX_ON));
+ outw(PP_ChipID, eth_nic_base + ADD_PORT);
+ continue;
+#endif
+ }
+
+ if (ioaddr == 0)
+ return (0);
+
+ nic->irqno = 0;
+ nic->ioaddr = ioaddr;
+
+ dev->disable = cs89x0_disable;
+ nic->poll = cs89x0_poll;
+ nic->transmit = cs89x0_transmit;
+ nic->irq = cs89x0_irq;
+
+ /* Based on PnP ISA map */
+ dev->devid.vendor_id = htons(ISAPNP_VENDOR('C','S','C'));
+ dev->devid.device_id = htons(0x0007);
+ return 1;
+}
+
+static struct isa_driver cs89x0_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "CS89x0",
+ .probe = cs89x0_probe,
+ .ioaddrs = 0,
+};
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/src/drivers/net/cs89x0.h b/src/drivers/net/cs89x0.h
new file mode 100644
index 00000000..b9b36251
--- /dev/null
+++ b/src/drivers/net/cs89x0.h
@@ -0,0 +1,461 @@
+/* Copyright, 1988-1992, Russell Nelson, Crynwr Software
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, version 1.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define PP_ChipID 0x0000 /* offset 0h -> Corp -ID */
+ /* offset 2h -> Model/Product Number */
+ /* offset 3h -> Chip Revision Number */
+
+#define PP_ISAIOB 0x0020 /* IO base address */
+#define PP_CS8900_ISAINT 0x0022 /* ISA interrupt select */
+#define PP_CS8920_ISAINT 0x0370 /* ISA interrupt select */
+#define PP_CS8900_ISADMA 0x0024 /* ISA Rec DMA channel */
+#define PP_CS8920_ISADMA 0x0374 /* ISA Rec DMA channel */
+#define PP_ISASOF 0x0026 /* ISA DMA offset */
+#define PP_DmaFrameCnt 0x0028 /* ISA DMA Frame count */
+#define PP_DmaByteCnt 0x002A /* ISA DMA Byte count */
+#define PP_CS8900_ISAMemB 0x002C /* Memory base */
+#define PP_CS8920_ISAMemB 0x0348 /* */
+
+#define PP_ISABootBase 0x0030 /* Boot Prom base */
+#define PP_ISABootMask 0x0034 /* Boot Prom Mask */
+
+/* EEPROM data and command registers */
+#define PP_EECMD 0x0040 /* NVR Interface Command register */
+#define PP_EEData 0x0042 /* NVR Interface Data Register */
+#define PP_DebugReg 0x0044 /* Debug Register */
+
+#define PP_RxCFG 0x0102 /* Rx Bus config */
+#define PP_RxCTL 0x0104 /* Receive Control Register */
+#define PP_TxCFG 0x0106 /* Transmit Config Register */
+#define PP_TxCMD 0x0108 /* Transmit Command Register */
+#define PP_BufCFG 0x010A /* Bus configuration Register */
+#define PP_LineCTL 0x0112 /* Line Config Register */
+#define PP_SelfCTL 0x0114 /* Self Command Register */
+#define PP_BusCTL 0x0116 /* ISA bus control Register */
+#define PP_TestCTL 0x0118 /* Test Register */
+#define PP_AutoNegCTL 0x011C /* Auto Negotiation Ctrl */
+
+#define PP_ISQ 0x0120 /* Interrupt Status */
+#define PP_RxEvent 0x0124 /* Rx Event Register */
+#define PP_TxEvent 0x0128 /* Tx Event Register */
+#define PP_BufEvent 0x012C /* Bus Event Register */
+#define PP_RxMiss 0x0130 /* Receive Miss Count */
+#define PP_TxCol 0x0132 /* Transmit Collision Count */
+#define PP_LineST 0x0134 /* Line State Register */
+#define PP_SelfST 0x0136 /* Self State register */
+#define PP_BusST 0x0138 /* Bus Status */
+#define PP_TDR 0x013C /* Time Domain Reflectometry */
+#define PP_AutoNegST 0x013E /* Auto Neg Status */
+#define PP_TxCommand 0x0144 /* Tx Command */
+#define PP_TxLength 0x0146 /* Tx Length */
+#define PP_LAF 0x0150 /* Hash Table */
+#define PP_IA 0x0158 /* Physical Address Register */
+
+#define PP_RxStatus 0x0400 /* Receive start of frame */
+#define PP_RxLength 0x0402 /* Receive Length of frame */
+#define PP_RxFrame 0x0404 /* Receive frame pointer */
+#define PP_TxFrame 0x0A00 /* Transmit frame pointer */
+
+/* Primary I/O Base Address. If no I/O base is supplied by the user, then this */
+/* can be used as the default I/O base to access the PacketPage Area. */
+#define DEFAULTIOBASE 0x0300
+#define FIRST_IO 0x020C /* First I/O port to check */
+#define LAST_IO 0x037C /* Last I/O port to check (+10h) */
+#define ADD_MASK 0x3000 /* Mask it use of the ADD_PORT register */
+#define ADD_SIG 0x3000 /* Expected ID signature */
+
+#define CHIP_EISA_ID_SIG 0x630E /* Product ID Code for Crystal Chip (CS8900 spec 4.3) */
+
+#ifdef IBMEIPKT
+#define EISA_ID_SIG 0x4D24 /* IBM */
+#define PART_NO_SIG 0x1010 /* IBM */
+#define MONGOOSE_BIT 0x0000 /* IBM */
+#else
+#define EISA_ID_SIG 0x630E /* PnP Vendor ID (same as chip id for Crystal board) */
+#define PART_NO_SIG 0x4000 /* ID code CS8920 board (PnP Vendor Product code) */
+#define MONGOOSE_BIT 0x2000 /* PART_NO_SIG + MONGOOSE_BUT => ID of mongoose */
+#endif
+
+#define PRODUCT_ID_ADD 0x0002 /* Address of product ID */
+
+/* Mask to find out the types of registers */
+#define REG_TYPE_MASK 0x001F
+
+/* Eeprom Commands */
+#define ERSE_WR_ENBL 0x00F0
+#define ERSE_WR_DISABLE 0x0000
+
+/* Defines Control/Config register quintuplet numbers */
+#define RX_BUF_CFG 0x0003
+#define RX_CONTROL 0x0005
+#define TX_CFG 0x0007
+#define TX_COMMAND 0x0009
+#define BUF_CFG 0x000B
+#define LINE_CONTROL 0x0013
+#define SELF_CONTROL 0x0015
+#define BUS_CONTROL 0x0017
+#define TEST_CONTROL 0x0019
+
+/* Defines Status/Count registers quintuplet numbers */
+#define RX_EVENT 0x0004
+#define TX_EVENT 0x0008
+#define BUF_EVENT 0x000C
+#define RX_MISS_COUNT 0x0010
+#define TX_COL_COUNT 0x0012
+#define LINE_STATUS 0x0014
+#define SELF_STATUS 0x0016
+#define BUS_STATUS 0x0018
+#define TDR 0x001C
+
+/* PP_RxCFG - Receive Configuration and Interrupt Mask bit definition - Read/write */
+#define SKIP_1 0x0040
+#define RX_STREAM_ENBL 0x0080
+#define RX_OK_ENBL 0x0100
+#define RX_DMA_ONLY 0x0200
+#define AUTO_RX_DMA 0x0400
+#define BUFFER_CRC 0x0800
+#define RX_CRC_ERROR_ENBL 0x1000
+#define RX_RUNT_ENBL 0x2000
+#define RX_EXTRA_DATA_ENBL 0x4000
+
+/* PP_RxCTL - Receive Control bit definition - Read/write */
+#define RX_IA_HASH_ACCEPT 0x0040
+#define RX_PROM_ACCEPT 0x0080
+#define RX_OK_ACCEPT 0x0100
+#define RX_MULTCAST_ACCEPT 0x0200
+#define RX_IA_ACCEPT 0x0400
+#define RX_BROADCAST_ACCEPT 0x0800
+#define RX_BAD_CRC_ACCEPT 0x1000
+#define RX_RUNT_ACCEPT 0x2000
+#define RX_EXTRA_DATA_ACCEPT 0x4000
+#define RX_ALL_ACCEPT (RX_PROM_ACCEPT|RX_BAD_CRC_ACCEPT|RX_RUNT_ACCEPT|RX_EXTRA_DATA_ACCEPT)
+/* Default receive mode - individually addressed, broadcast, and error free */
+#define DEF_RX_ACCEPT (RX_IA_ACCEPT | RX_BROADCAST_ACCEPT | RX_OK_ACCEPT)
+
+/* PP_TxCFG - Transmit Configuration Interrupt Mask bit definition - Read/write */
+#define TX_LOST_CRS_ENBL 0x0040
+#define TX_SQE_ERROR_ENBL 0x0080
+#define TX_OK_ENBL 0x0100
+#define TX_LATE_COL_ENBL 0x0200
+#define TX_JBR_ENBL 0x0400
+#define TX_ANY_COL_ENBL 0x0800
+#define TX_16_COL_ENBL 0x8000
+
+/* PP_TxCMD - Transmit Command bit definition - Read-only */
+#define TX_START_4_BYTES 0x0000
+#define TX_START_64_BYTES 0x0040
+#define TX_START_128_BYTES 0x0080
+#define TX_START_ALL_BYTES 0x00C0
+#define TX_FORCE 0x0100
+#define TX_ONE_COL 0x0200
+#define TX_TWO_PART_DEFF_DISABLE 0x0400
+#define TX_NO_CRC 0x1000
+#define TX_RUNT 0x2000
+
+/* PP_BufCFG - Buffer Configuration Interrupt Mask bit definition - Read/write */
+#define GENERATE_SW_INTERRUPT 0x0040
+#define RX_DMA_ENBL 0x0080
+#define READY_FOR_TX_ENBL 0x0100
+#define TX_UNDERRUN_ENBL 0x0200
+#define RX_MISS_ENBL 0x0400
+#define RX_128_BYTE_ENBL 0x0800
+#define TX_COL_COUNT_OVRFLOW_ENBL 0x1000
+#define RX_MISS_COUNT_OVRFLOW_ENBL 0x2000
+#define RX_DEST_MATCH_ENBL 0x8000
+
+/* PP_LineCTL - Line Control bit definition - Read/write */
+#define SERIAL_RX_ON 0x0040
+#define SERIAL_TX_ON 0x0080
+#define AUI_ONLY 0x0100
+#define AUTO_AUI_10BASET 0x0200
+#define MODIFIED_BACKOFF 0x0800
+#define NO_AUTO_POLARITY 0x1000
+#define TWO_PART_DEFDIS 0x2000
+#define LOW_RX_SQUELCH 0x4000
+
+/* PP_SelfCTL - Software Self Control bit definition - Read/write */
+#define POWER_ON_RESET 0x0040
+#define SW_STOP 0x0100
+#define SLEEP_ON 0x0200
+#define AUTO_WAKEUP 0x0400
+#define HCB0_ENBL 0x1000
+#define HCB1_ENBL 0x2000
+#define HCB0 0x4000
+#define HCB1 0x8000
+
+/* PP_BusCTL - ISA Bus Control bit definition - Read/write */
+#define RESET_RX_DMA 0x0040
+#define MEMORY_ON 0x0400
+#define DMA_BURST_MODE 0x0800
+#define IO_CHANNEL_READY_ON 0x1000
+#define RX_DMA_SIZE_64K 0x2000
+#define ENABLE_IRQ 0x8000
+
+/* PP_TestCTL - Test Control bit definition - Read/write */
+#define LINK_OFF 0x0080
+#define ENDEC_LOOPBACK 0x0200
+#define AUI_LOOPBACK 0x0400
+#define BACKOFF_OFF 0x0800
+#define FAST_TEST 0x8000
+
+/* PP_RxEvent - Receive Event Bit definition - Read-only */
+#define RX_IA_HASHED 0x0040
+#define RX_DRIBBLE 0x0080
+#define RX_OK 0x0100
+#define RX_HASHED 0x0200
+#define RX_IA 0x0400
+#define RX_BROADCAST 0x0800
+#define RX_CRC_ERROR 0x1000
+#define RX_RUNT 0x2000
+#define RX_EXTRA_DATA 0x4000
+
+#define HASH_INDEX_MASK 0x0FC00
+
+/* PP_TxEvent - Transmit Event Bit definition - Read-only */
+#define TX_LOST_CRS 0x0040
+#define TX_SQE_ERROR 0x0080
+#define TX_OK 0x0100
+#define TX_LATE_COL 0x0200
+#define TX_JBR 0x0400
+#define TX_16_COL 0x8000
+#define TX_SEND_OK_BITS (TX_OK|TX_LOST_CRS)
+#define TX_COL_COUNT_MASK 0x7800
+
+/* PP_BufEvent - Buffer Event Bit definition - Read-only */
+#define SW_INTERRUPT 0x0040
+#define RX_DMA 0x0080
+#define READY_FOR_TX 0x0100
+#define TX_UNDERRUN 0x0200
+#define RX_MISS 0x0400
+#define RX_128_BYTE 0x0800
+#define TX_COL_OVRFLW 0x1000
+#define RX_MISS_OVRFLW 0x2000
+#define RX_DEST_MATCH 0x8000
+
+/* PP_LineST - Ethernet Line Status bit definition - Read-only */
+#define LINK_OK 0x0080
+#define AUI_ON 0x0100
+#define TENBASET_ON 0x0200
+#define POLARITY_OK 0x1000
+#define CRS_OK 0x4000
+
+/* PP_SelfST - Chip Software Status bit definition */
+#define ACTIVE_33V 0x0040
+#define INIT_DONE 0x0080
+#define SI_BUSY 0x0100
+#define EEPROM_PRESENT 0x0200
+#define EEPROM_OK 0x0400
+#define EL_PRESENT 0x0800
+#define EE_SIZE_64 0x1000
+
+/* PP_BusST - ISA Bus Status bit definition */
+#define TX_BID_ERROR 0x0080
+#define READY_FOR_TX_NOW 0x0100
+
+/* PP_AutoNegCTL - Auto Negotiation Control bit definition */
+#define RE_NEG_NOW 0x0040
+#define ALLOW_FDX 0x0080
+#define AUTO_NEG_ENABLE 0x0100
+#define NLP_ENABLE 0x0200
+#define FORCE_FDX 0x8000
+#define AUTO_NEG_BITS (FORCE_FDX|NLP_ENABLE|AUTO_NEG_ENABLE)
+#define AUTO_NEG_MASK (FORCE_FDX|NLP_ENABLE|AUTO_NEG_ENABLE|ALLOW_FDX|RE_NEG_NOW)
+
+/* PP_AutoNegST - Auto Negotiation Status bit definition */
+#define AUTO_NEG_BUSY 0x0080
+#define FLP_LINK 0x0100
+#define FLP_LINK_GOOD 0x0800
+#define LINK_FAULT 0x1000
+#define HDX_ACTIVE 0x4000
+#define FDX_ACTIVE 0x8000
+
+/* The following block defines the ISQ event types */
+#define ISQ_RECEIVER_EVENT 0x04
+#define ISQ_TRANSMITTER_EVENT 0x08
+#define ISQ_BUFFER_EVENT 0x0c
+#define ISQ_RX_MISS_EVENT 0x10
+#define ISQ_TX_COL_EVENT 0x12
+
+#define ISQ_EVENT_MASK 0x003F /* ISQ mask to find out type of event */
+#define ISQ_HIST 16 /* small history buffer */
+#define AUTOINCREMENT 0x8000 /* Bit mask to set bit-15 for autoincrement */
+
+#define TXRXBUFSIZE 0x0600
+#define RXDMABUFSIZE 0x8000
+#define RXDMASIZE 0x4000
+#define TXRX_LENGTH_MASK 0x07FF
+
+/* rx options bits */
+#define RCV_WITH_RXON 1 /* Set SerRx ON */
+#define RCV_COUNTS 2 /* Use Framecnt1 */
+#define RCV_PONG 4 /* Pong respondent */
+#define RCV_DONG 8 /* Dong operation */
+#define RCV_POLLING 0x10 /* Poll RxEvent */
+#define RCV_ISQ 0x20 /* Use ISQ, int */
+#define RCV_AUTO_DMA 0x100 /* Set AutoRxDMAE */
+#define RCV_DMA 0x200 /* Set RxDMA only */
+#define RCV_DMA_ALL 0x400 /* Copy all DMA'ed */
+#define RCV_FIXED_DATA 0x800 /* Every frame same */
+#define RCV_IO 0x1000 /* Use ISA IO only */
+#define RCV_MEMORY 0x2000 /* Use ISA Memory */
+
+#define RAM_SIZE 0x1000 /* The card has 4k bytes or RAM */
+#define PKT_START PP_TxFrame /* Start of packet RAM */
+
+#define RX_FRAME_PORT 0x0000
+#define TX_FRAME_PORT RX_FRAME_PORT
+#define TX_CMD_PORT 0x0004
+#define TX_NOW 0x0000 /* Tx packet after 5 bytes copied */
+#define TX_AFTER_381 0x0020 /* Tx packet after 381 bytes copied */
+#define TX_AFTER_ALL 0x00C0 /* Tx packet after all bytes copied */
+#define TX_LEN_PORT 0x0006
+#define ISQ_PORT 0x0008
+#define ADD_PORT 0x000A
+#define DATA_PORT 0x000C
+
+#define EEPROM_WRITE_EN 0x00F0
+#define EEPROM_WRITE_DIS 0x0000
+#define EEPROM_WRITE_CMD 0x0100
+#define EEPROM_READ_CMD 0x0200
+
+/* Receive Header */
+/* Description of header of each packet in receive area of memory */
+#define RBUF_EVENT_LOW 0 /* Low byte of RxEvent - status of received frame */
+#define RBUF_EVENT_HIGH 1 /* High byte of RxEvent - status of received frame */
+#define RBUF_LEN_LOW 2 /* Length of received data - low byte */
+#define RBUF_LEN_HI 3 /* Length of received data - high byte */
+#define RBUF_HEAD_LEN 4 /* Length of this header */
+
+#define CHIP_READ 0x1 /* Used to mark state of the repins code (chip or dma) */
+#define DMA_READ 0x2 /* Used to mark state of the repins code (chip or dma) */
+
+/* for bios scan */
+/* */
+#ifdef CSDEBUG
+/* use these values for debugging bios scan */
+#define BIOS_START_SEG 0x00000
+#define BIOS_OFFSET_INC 0x0010
+#else
+#define BIOS_START_SEG 0x0c000
+#define BIOS_OFFSET_INC 0x0200
+#endif
+
+#define BIOS_LAST_OFFSET 0x0fc00
+
+/* Byte offsets into the EEPROM configuration buffer */
+#define ISA_CNF_OFFSET 0x6
+#define TX_CTL_OFFSET (ISA_CNF_OFFSET + 8) /* 8900 eeprom */
+#define AUTO_NEG_CNF_OFFSET (ISA_CNF_OFFSET + 8) /* 8920 eeprom */
+
+ /* the assumption here is that the bits in the eeprom are generally */
+ /* in the same position as those in the autonegctl register. */
+ /* Of course the IMM bit is not in that register so it must be */
+ /* masked out */
+#define EE_FORCE_FDX 0x8000
+#define EE_NLP_ENABLE 0x0200
+#define EE_AUTO_NEG_ENABLE 0x0100
+#define EE_ALLOW_FDX 0x0080
+#define EE_AUTO_NEG_CNF_MASK (EE_FORCE_FDX|EE_NLP_ENABLE|EE_AUTO_NEG_ENABLE|EE_ALLOW_FDX)
+
+#define IMM_BIT 0x0040 /* ignore missing media */
+
+#define ADAPTER_CNF_OFFSET (AUTO_NEG_CNF_OFFSET + 2)
+#define A_CNF_10B_T 0x0001
+#define A_CNF_AUI 0x0002
+#define A_CNF_10B_2 0x0004
+#define A_CNF_MEDIA_TYPE 0x0060
+#define A_CNF_MEDIA_AUTO 0x0000
+#define A_CNF_MEDIA_10B_T 0x0020
+#define A_CNF_MEDIA_AUI 0x0040
+#define A_CNF_MEDIA_10B_2 0x0060
+#define A_CNF_DC_DC_POLARITY 0x0080
+#define A_CNF_NO_AUTO_POLARITY 0x2000
+#define A_CNF_LOW_RX_SQUELCH 0x4000
+#define A_CNF_EXTND_10B_2 0x8000
+
+#define PACKET_PAGE_OFFSET 0x8
+
+/* Bit definitions for the ISA configuration word from the EEPROM */
+#define INT_NO_MASK 0x000F
+#define DMA_NO_MASK 0x0070
+#define ISA_DMA_SIZE 0x0200
+#define ISA_AUTO_RxDMA 0x0400
+#define ISA_RxDMA 0x0800
+#define DMA_BURST 0x1000
+#define STREAM_TRANSFER 0x2000
+#define ANY_ISA_DMA (ISA_AUTO_RxDMA | ISA_RxDMA)
+
+/* DMA controller registers */
+#define DMA_BASE 0x00 /* DMA controller base */
+#define DMA_BASE_2 0x0C0 /* DMA controller base */
+
+#define DMA_STAT 0x0D0 /* DMA controller status register */
+#define DMA_MASK 0x0D4 /* DMA controller mask register */
+#define DMA_MODE 0x0D6 /* DMA controller mode register */
+#define DMA_RESETFF 0x0D8 /* DMA controller first/last flip flop */
+
+/* DMA data */
+#define DMA_DISABLE 0x04 /* Disable channel n */
+#define DMA_ENABLE 0x00 /* Enable channel n */
+/* Demand transfers, incr. address, auto init, writes, ch. n */
+#define DMA_RX_MODE 0x14
+/* Demand transfers, incr. address, auto init, reads, ch. n */
+#define DMA_TX_MODE 0x18
+
+#define DMA_SIZE (16*1024) /* Size of dma buffer - 16k */
+
+#define CS8900 0x0000
+#define CS8920 0x4000
+#define CS8920M 0x6000
+#define REVISON_BITS 0x1F00
+#define EEVER_NUMBER 0x12
+#define CHKSUM_LEN 0x14
+#define CHKSUM_VAL 0x0000
+#define START_EEPROM_DATA 0x001c /* Offset into eeprom for start of data */
+#define IRQ_MAP_EEPROM_DATA 0x0046 /* Offset into eeprom for the IRQ map */
+#define IRQ_MAP_LEN 0x0004 /* No of bytes to read for the IRQ map */
+#define PNP_IRQ_FRMT 0x0022 /* PNP small item IRQ format */
+#define CS8900_IRQ_MAP 0x1c20 /* This IRQ map is fixed */
+
+#define CS8920_NO_INTS 0x0F /* Max CS8920 interrupt select # */
+
+#define PNP_ADD_PORT 0x0279
+#define PNP_WRITE_PORT 0x0A79
+
+#define GET_PNP_ISA_STRUCT 0x40
+#define PNP_ISA_STRUCT_LEN 0x06
+#define PNP_CSN_CNT_OFF 0x01
+#define PNP_RD_PORT_OFF 0x02
+#define PNP_FUNCTION_OK 0x00
+#define PNP_WAKE 0x03
+#define PNP_RSRC_DATA 0x04
+#define PNP_RSRC_READY 0x01
+#define PNP_STATUS 0x05
+#define PNP_ACTIVATE 0x30
+#define PNP_CNF_IO_H 0x60
+#define PNP_CNF_IO_L 0x61
+#define PNP_CNF_INT 0x70
+#define PNP_CNF_DMA 0x74
+#define PNP_CNF_MEM 0x48
+
+#define BIT0 1
+#define BIT15 0x8000
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/src/drivers/net/cs89x0.txt b/src/drivers/net/cs89x0.txt
new file mode 100644
index 00000000..4927641d
--- /dev/null
+++ b/src/drivers/net/cs89x0.txt
@@ -0,0 +1,26 @@
+Permission is granted to distribute the enclosed cs89x0.[ch] driver
+only in conjunction with the Etherboot package. The code is
+ordinarily distributed under the GPL.
+
+Russ Nelson, January 2000
+
+CREDITS
+
+I want to thank
+
+ Mike Cruse <mcruse@cti-ltd.com>
+ for providing an evaluation NIC and for sponsoring the
+ development of this driver.
+
+ Randall Sears <sears@crystal.cirrus.com>
+ Deva Bodas <bodas@crystal.cirrus.com>
+ Andreas Kraemer <akraemer@crystal.cirrus.com>
+ Wolfgang Krause <100303.2673@compuserve.com>
+ for excellent technical support and for providing the required
+ programming information. I appreciate Crystal Semiconductor's
+ commitment towards free software.
+
+ Russell Nelson <nelson@crynwr.com>
+ for writing the Linux device driver for the CS89x0
+ chipset. Russel's code is very well designed and simplified my
+ job a lot.
diff --git a/src/drivers/net/davicom.c b/src/drivers/net/davicom.c
new file mode 100644
index 00000000..1d0235f4
--- /dev/null
+++ b/src/drivers/net/davicom.c
@@ -0,0 +1,720 @@
+#ifdef ALLMULTI
+#error multicast support is not yet implemented
+#endif
+/*
+ DAVICOM DM9009/DM9102/DM9102A Etherboot Driver V1.00
+
+ This driver was ported from Marty Connor's Tulip Etherboot driver.
+ Thanks Marty Connor (mdc@etherboot.org)
+
+ This davicom etherboot driver supports DM9009/DM9102/DM9102A/
+ DM9102A+DM9801/DM9102A+DM9802 NICs.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+*/
+
+/*********************************************************************/
+/* Revision History */
+/*********************************************************************/
+
+/*
+ 19 OCT 2000 Sten 1.00
+ Different half and full duplex mode
+ Do the different programming for DM9801/DM9802
+
+ 12 OCT 2000 Sten 0.90
+ This driver was ported from tulip driver and it
+ has the following difference.
+ Changed symbol tulip/TULIP to davicom/DAVICOM
+ Deleted some code that did not use in this driver.
+ Used chain-strcture to replace ring structure
+ for both TX/RX descriptor.
+ Allocated two tx descriptor.
+ According current media mode to set operating
+ register(CR6)
+*/
+
+
+/*********************************************************************/
+/* Declarations */
+/*********************************************************************/
+
+#include "etherboot.h"
+#include "nic.h"
+#include "pci.h"
+
+#undef DAVICOM_DEBUG
+#undef DAVICOM_DEBUG_WHERE
+
+#define TX_TIME_OUT 2*TICKS_PER_SEC
+
+typedef unsigned char u8;
+typedef signed char s8;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned int u32;
+typedef signed int s32;
+
+/* Register offsets for davicom device */
+enum davicom_offsets {
+ CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
+ CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
+ CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78, CSR16=0x80, CSR20=0xA0
+};
+
+/* EEPROM Address width definitions */
+#define EEPROM_ADDRLEN 6
+#define EEPROM_SIZE 32 /* 1 << EEPROM_ADDRLEN */
+/* Used to be 128, but we only need to read enough to get the MAC
+ address at bytes 20..25 */
+
+/* Data Read from the EEPROM */
+static unsigned char ee_data[EEPROM_SIZE];
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5 << addr_len)
+#define EE_READ_CMD (6 << addr_len)
+#define EE_ERASE_CMD (7 << addr_len)
+
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */
+#define EE_CS 0x01 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
+#define EE_WRITE_0 0x01
+#define EE_WRITE_1 0x05
+#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
+#define EE_ENB (0x4800 | EE_CS)
+
+/* Sten 10/11 for phyxcer */
+#define PHY_DATA_0 0x0
+#define PHY_DATA_1 0x20000
+#define MDCLKH 0x10000
+
+/* Delay between EEPROM clock transitions. Even at 33Mhz current PCI
+ implementations don't overrun the EEPROM clock. We add a bus
+ turn-around to insure that this remains true. */
+#define eeprom_delay() inl(ee_addr)
+
+/* helpful macro if on a big_endian machine for changing byte order.
+ not strictly needed on Intel
+ Already defined in Etherboot includes
+#define le16_to_cpu(val) (val)
+*/
+
+/* transmit and receive descriptor format */
+struct txdesc {
+ volatile unsigned long status; /* owner, status */
+ unsigned long buf1sz:11, /* size of buffer 1 */
+ buf2sz:11, /* size of buffer 2 */
+ control:10; /* control bits */
+ const unsigned char *buf1addr; /* buffer 1 address */
+ const unsigned char *buf2addr; /* buffer 2 address */
+};
+
+struct rxdesc {
+ volatile unsigned long status; /* owner, status */
+ unsigned long buf1sz:11, /* size of buffer 1 */
+ buf2sz:11, /* size of buffer 2 */
+ control:10; /* control bits */
+ unsigned char *buf1addr; /* buffer 1 address */
+ unsigned char *buf2addr; /* buffer 2 address */
+};
+
+/* Size of transmit and receive buffers */
+#define BUFLEN 1536
+
+/*********************************************************************/
+/* Global Storage */
+/*********************************************************************/
+
+/* PCI Bus parameters */
+static unsigned short vendor, dev_id;
+static unsigned long ioaddr;
+
+/* Note: transmit and receive buffers must be longword aligned and
+ longword divisable */
+
+/* transmit descriptor and buffer */
+#define NTXD 2
+static struct txdesc txd[NTXD] __attribute__ ((aligned(4)));
+static unsigned char txb[BUFLEN] __attribute__ ((aligned(4)));
+
+/* receive descriptor(s) and buffer(s) */
+#define NRXD 4
+static struct rxdesc rxd[NRXD] __attribute__ ((aligned(4)));
+static unsigned char rxb[NRXD * BUFLEN] __attribute__ ((aligned(4)));
+static int rxd_tail;
+static int TxPtr;
+
+
+/*********************************************************************/
+/* Function Prototypes */
+/*********************************************************************/
+static void whereami(const char *str);
+static int read_eeprom(unsigned long ioaddr, int location, int addr_len);
+static int davicom_probe(struct dev *dev, struct pci_device *pci);
+static void davicom_init_chain(struct nic *nic); /* Sten 10/9 */
+static void davicom_reset(struct nic *nic);
+static void davicom_transmit(struct nic *nic, const char *d, unsigned int t,
+ unsigned int s, const char *p);
+static int davicom_poll(struct nic *nic, int retrieve);
+static void davicom_disable(struct dev *dev);
+#ifdef DAVICOM_DEBUG
+static void davicom_more(void);
+#endif /* DAVICOM_DEBUG */
+static void davicom_wait(unsigned int nticks);
+static int phy_read(int);
+static void phy_write(int, u16);
+static void phy_write_1bit(u32, u32);
+static int phy_read_1bit(u32);
+static void davicom_media_chk(struct nic *);
+
+
+/*********************************************************************/
+/* Utility Routines */
+/*********************************************************************/
+static inline void whereami(const char *str)
+{
+ printf("%s\n", str);
+ /* sleep(2); */
+}
+
+#ifdef DAVICOM_DEBUG
+static void davicom_more()
+{
+ printf("\n\n-- more --");
+ while (!iskey())
+ /* wait */;
+ getchar();
+ printf("\n\n");
+}
+#endif /* DAVICOM_DEBUG */
+
+static void davicom_wait(unsigned int nticks)
+{
+ unsigned int to = currticks() + nticks;
+ while (currticks() < to)
+ /* wait */ ;
+}
+
+
+/*********************************************************************/
+/* For DAVICOM phyxcer register by MII interface */
+/*********************************************************************/
+/*
+ Read a word data from phy register
+*/
+static int phy_read(int location)
+{
+ int i, phy_addr=1;
+ u16 phy_data;
+ u32 io_dcr9;
+
+ whereami("phy_read\n");
+
+ io_dcr9 = ioaddr + CSR9;
+
+ /* Send 33 synchronization clock to Phy controller */
+ for (i=0; i<34; i++)
+ phy_write_1bit(io_dcr9, PHY_DATA_1);
+
+ /* Send start command(01) to Phy */
+ phy_write_1bit(io_dcr9, PHY_DATA_0);
+ phy_write_1bit(io_dcr9, PHY_DATA_1);
+
+ /* Send read command(10) to Phy */
+ phy_write_1bit(io_dcr9, PHY_DATA_1);
+ phy_write_1bit(io_dcr9, PHY_DATA_0);
+
+ /* Send Phy addres */
+ for (i=0x10; i>0; i=i>>1)
+ phy_write_1bit(io_dcr9, phy_addr&i ? PHY_DATA_1: PHY_DATA_0);
+
+ /* Send register addres */
+ for (i=0x10; i>0; i=i>>1)
+ phy_write_1bit(io_dcr9, location&i ? PHY_DATA_1: PHY_DATA_0);
+
+ /* Skip transition state */
+ phy_read_1bit(io_dcr9);
+
+ /* read 16bit data */
+ for (phy_data=0, i=0; i<16; i++) {
+ phy_data<<=1;
+ phy_data|=phy_read_1bit(io_dcr9);
+ }
+
+ return phy_data;
+}
+
+/*
+ Write a word to Phy register
+*/
+static void phy_write(int location, u16 phy_data)
+{
+ u16 i, phy_addr=1;
+ u32 io_dcr9;
+
+ whereami("phy_write\n");
+
+ io_dcr9 = ioaddr + CSR9;
+
+ /* Send 33 synchronization clock to Phy controller */
+ for (i=0; i<34; i++)
+ phy_write_1bit(io_dcr9, PHY_DATA_1);
+
+ /* Send start command(01) to Phy */
+ phy_write_1bit(io_dcr9, PHY_DATA_0);
+ phy_write_1bit(io_dcr9, PHY_DATA_1);
+
+ /* Send write command(01) to Phy */
+ phy_write_1bit(io_dcr9, PHY_DATA_0);
+ phy_write_1bit(io_dcr9, PHY_DATA_1);
+
+ /* Send Phy addres */
+ for (i=0x10; i>0; i=i>>1)
+ phy_write_1bit(io_dcr9, phy_addr&i ? PHY_DATA_1: PHY_DATA_0);
+
+ /* Send register addres */
+ for (i=0x10; i>0; i=i>>1)
+ phy_write_1bit(io_dcr9, location&i ? PHY_DATA_1: PHY_DATA_0);
+
+ /* written trasnition */
+ phy_write_1bit(io_dcr9, PHY_DATA_1);
+ phy_write_1bit(io_dcr9, PHY_DATA_0);
+
+ /* Write a word data to PHY controller */
+ for (i=0x8000; i>0; i>>=1)
+ phy_write_1bit(io_dcr9, phy_data&i ? PHY_DATA_1: PHY_DATA_0);
+}
+
+/*
+ Write one bit data to Phy Controller
+*/
+static void phy_write_1bit(u32 ee_addr, u32 phy_data)
+{
+ whereami("phy_write_1bit\n");
+ outl(phy_data, ee_addr); /* MII Clock Low */
+ eeprom_delay();
+ outl(phy_data|MDCLKH, ee_addr); /* MII Clock High */
+ eeprom_delay();
+ outl(phy_data, ee_addr); /* MII Clock Low */
+ eeprom_delay();
+}
+
+/*
+ Read one bit phy data from PHY controller
+*/
+static int phy_read_1bit(u32 ee_addr)
+{
+ int phy_data;
+
+ whereami("phy_read_1bit\n");
+
+ outl(0x50000, ee_addr);
+ eeprom_delay();
+
+ phy_data=(inl(ee_addr)>>19) & 0x1;
+
+ outl(0x40000, ee_addr);
+ eeprom_delay();
+
+ return phy_data;
+}
+
+/*
+ DM9801/DM9802 present check and program
+*/
+static void HPNA_process(void)
+{
+
+ if ( (phy_read(3) & 0xfff0) == 0xb900 ) {
+ if ( phy_read(31) == 0x4404 ) {
+ /* DM9801 present */
+ if (phy_read(3) == 0xb901)
+ phy_write(16, 0x5); /* DM9801 E4 */
+ else
+ phy_write(16, 0x1005); /* DM9801 E3 and others */
+ phy_write(25, ((phy_read(24) + 3) & 0xff) | 0xf000);
+ } else {
+ /* DM9802 present */
+ phy_write(16, 0x5);
+ phy_write(25, (phy_read(25) & 0xff00) + 2);
+ }
+ }
+}
+
+/*
+ Sense media mode and set CR6
+*/
+static void davicom_media_chk(struct nic * nic __unused)
+{
+ unsigned long to, csr6;
+
+ csr6 = 0x00200000; /* SF */
+ outl(csr6, ioaddr + CSR6);
+
+#define PCI_DEVICE_ID_DM9009 0x9009
+ if (vendor == PCI_VENDOR_ID_DAVICOM && dev_id == PCI_DEVICE_ID_DM9009) {
+ /* Set to 10BaseT mode for DM9009 */
+ phy_write(0, 0);
+ } else {
+ /* For DM9102/DM9102A */
+ to = currticks() + 2 * TICKS_PER_SEC;
+ while ( ((phy_read(1) & 0x24)!=0x24) && (currticks() < to))
+ /* wait */ ;
+
+ if ( (phy_read(1) & 0x24) == 0x24 ) {
+ if (phy_read(17) & 0xa000)
+ csr6 |= 0x00000200; /* Full Duplex mode */
+ } else
+ csr6 |= 0x00040000; /* Select DM9801/DM9802 when Ethernet link failed */
+ }
+
+ /* set the chip's operating mode */
+ outl(csr6, ioaddr + CSR6);
+
+ /* DM9801/DM9802 present check & program */
+ if (csr6 & 0x40000)
+ HPNA_process();
+}
+
+
+/*********************************************************************/
+/* EEPROM Reading Code */
+/*********************************************************************/
+/* EEPROM routines adapted from the Linux Tulip Code */
+/* Reading a serial EEPROM is a "bit" grungy, but we work our way
+ through:->.
+*/
+static int read_eeprom(unsigned long ioaddr, int location, int addr_len)
+{
+ int i;
+ unsigned short retval = 0;
+ long ee_addr = ioaddr + CSR9;
+ int read_cmd = location | EE_READ_CMD;
+
+ whereami("read_eeprom\n");
+
+ outl(EE_ENB & ~EE_CS, ee_addr);
+ outl(EE_ENB, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 4 + addr_len; i >= 0; i--) {
+ short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+ outl(EE_ENB | dataval, ee_addr);
+ eeprom_delay();
+ outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ }
+ outl(EE_ENB, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
+ outl(EE_ENB, ee_addr);
+ eeprom_delay();
+ }
+
+ /* Terminate the EEPROM access. */
+ outl(EE_ENB & ~EE_CS, ee_addr);
+ return retval;
+}
+
+/*********************************************************************/
+/* davicom_init_chain - setup the tx and rx descriptors */
+/* Sten 10/9 */
+/*********************************************************************/
+static void davicom_init_chain(struct nic *nic)
+{
+ int i;
+
+ /* setup the transmit descriptor */
+ /* Sten: Set 2 TX descriptor but use one TX buffer because
+ it transmit a packet and wait complete every time. */
+ for (i=0; i<NTXD; i++) {
+ txd[i].buf1addr = (void *)virt_to_bus(&txb[0]); /* Used same TX buffer */
+ txd[i].buf2addr = (void *)virt_to_bus(&txd[i+1]); /* Point to Next TX desc */
+ txd[i].buf1sz = 0;
+ txd[i].buf2sz = 0;
+ txd[i].control = 0x184; /* Begin/End/Chain */
+ txd[i].status = 0x00000000; /* give ownership to Host */
+ }
+
+ /* construct perfect filter frame with mac address as first match
+ and broadcast address for all others */
+ for (i=0; i<192; i++) txb[i] = 0xFF;
+ txb[0] = nic->node_addr[0];
+ txb[1] = nic->node_addr[1];
+ txb[4] = nic->node_addr[2];
+ txb[5] = nic->node_addr[3];
+ txb[8] = nic->node_addr[4];
+ txb[9] = nic->node_addr[5];
+
+ /* setup receive descriptor */
+ for (i=0; i<NRXD; i++) {
+ rxd[i].buf1addr = (void *)virt_to_bus(&rxb[i * BUFLEN]);
+ rxd[i].buf2addr = (void *)virt_to_bus(&rxd[i+1]); /* Point to Next RX desc */
+ rxd[i].buf1sz = BUFLEN;
+ rxd[i].buf2sz = 0; /* not used */
+ rxd[i].control = 0x4; /* Chain Structure */
+ rxd[i].status = 0x80000000; /* give ownership to device */
+ }
+
+ /* Chain the last descriptor to first */
+ txd[NTXD - 1].buf2addr = (void *)virt_to_bus(&txd[0]);
+ rxd[NRXD - 1].buf2addr = (void *)virt_to_bus(&rxd[0]);
+ TxPtr = 0;
+ rxd_tail = 0;
+}
+
+
+/*********************************************************************/
+/* davicom_reset - Reset adapter */
+/*********************************************************************/
+static void davicom_reset(struct nic *nic)
+{
+ unsigned long to;
+
+ whereami("davicom_reset\n");
+
+ /* Stop Tx and RX */
+ outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
+
+ /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
+ outl(0x00000001, ioaddr + CSR0);
+
+ davicom_wait(TICKS_PER_SEC);
+
+ /* TX/RX descriptor burst */
+ outl(0x0C00000, ioaddr + CSR0); /* Sten 10/9 */
+
+ /* set up transmit and receive descriptors */
+ davicom_init_chain(nic); /* Sten 10/9 */
+
+ /* Point to receive descriptor */
+ outl(virt_to_bus(&rxd[0]), ioaddr + CSR3);
+ outl(virt_to_bus(&txd[0]), ioaddr + CSR4); /* Sten 10/9 */
+
+ /* According phyxcer media mode to set CR6,
+ DM9102/A phyxcer can auto-detect media mode */
+ davicom_media_chk(nic);
+
+ /* Prepare Setup Frame Sten 10/9 */
+ txd[TxPtr].buf1sz = 192;
+ txd[TxPtr].control = 0x024; /* SF/CE */
+ txd[TxPtr].status = 0x80000000; /* Give ownership to device */
+
+ /* Start Tx */
+ outl(inl(ioaddr + CSR6) | 0x00002000, ioaddr + CSR6);
+ /* immediate transmit demand */
+ outl(0, ioaddr + CSR1);
+
+ to = currticks() + TX_TIME_OUT;
+ while ((txd[TxPtr].status & 0x80000000) && (currticks() < to)) /* Sten 10/9 */
+ /* wait */ ;
+
+ if (currticks() >= to) {
+ printf ("TX Setup Timeout!\n");
+ }
+ /* Point to next TX descriptor */
+ TxPtr = (++TxPtr >= NTXD) ? 0:TxPtr; /* Sten 10/9 */
+
+#ifdef DAVICOM_DEBUG
+ printf("txd.status = %X\n", txd.status);
+ printf("ticks = %d\n", currticks() - (to - TX_TIME_OUT));
+ davicom_more();
+#endif
+
+ /* enable RX */
+ outl(inl(ioaddr + CSR6) | 0x00000002, ioaddr + CSR6);
+ /* immediate poll demand */
+ outl(0, ioaddr + CSR2);
+}
+
+
+/*********************************************************************/
+/* eth_transmit - Transmit a frame */
+/*********************************************************************/
+static void davicom_transmit(struct nic *nic, const char *d, unsigned int t,
+ unsigned int s, const char *p)
+{
+ unsigned long to;
+
+ whereami("davicom_transmit\n");
+
+ /* Stop Tx */
+ /* outl(inl(ioaddr + CSR6) & ~0x00002000, ioaddr + CSR6); */
+
+ /* setup ethernet header */
+ memcpy(&txb[0], d, ETH_ALEN); /* DA 6byte */
+ memcpy(&txb[ETH_ALEN], nic->node_addr, ETH_ALEN); /* SA 6byte*/
+ txb[ETH_ALEN*2] = (t >> 8) & 0xFF; /* Frame type: 2byte */
+ txb[ETH_ALEN*2+1] = t & 0xFF;
+ memcpy(&txb[ETH_HLEN], p, s); /* Frame data */
+
+ /* setup the transmit descriptor */
+ txd[TxPtr].buf1sz = ETH_HLEN+s;
+ txd[TxPtr].control = 0x00000184; /* LS+FS+CE */
+ txd[TxPtr].status = 0x80000000; /* give ownership to device */
+
+ /* immediate transmit demand */
+ outl(0, ioaddr + CSR1);
+
+ to = currticks() + TX_TIME_OUT;
+ while ((txd[TxPtr].status & 0x80000000) && (currticks() < to))
+ /* wait */ ;
+
+ if (currticks() >= to) {
+ printf ("TX Timeout!\n");
+ }
+
+ /* Point to next TX descriptor */
+ TxPtr = (++TxPtr >= NTXD) ? 0:TxPtr; /* Sten 10/9 */
+
+}
+
+/*********************************************************************/
+/* eth_poll - Wait for a frame */
+/*********************************************************************/
+static int davicom_poll(struct nic *nic, int retrieve)
+{
+ whereami("davicom_poll\n");
+
+ if (rxd[rxd_tail].status & 0x80000000)
+ return 0;
+
+ if ( ! retrieve ) return 1;
+
+ whereami("davicom_poll got one\n");
+
+ nic->packetlen = (rxd[rxd_tail].status & 0x3FFF0000) >> 16;
+
+ if( rxd[rxd_tail].status & 0x00008000){
+ rxd[rxd_tail].status = 0x80000000;
+ rxd_tail++;
+ if (rxd_tail == NRXD) rxd_tail = 0;
+ return 0;
+ }
+
+ /* copy packet to working buffer */
+ /* XXX - this copy could be avoided with a little more work
+ but for now we are content with it because the optimised
+ memcpy is quite fast */
+
+ memcpy(nic->packet, rxb + rxd_tail * BUFLEN, nic->packetlen);
+
+ /* return the descriptor and buffer to receive ring */
+ rxd[rxd_tail].status = 0x80000000;
+ rxd_tail++;
+ if (rxd_tail == NRXD) rxd_tail = 0;
+
+ return 1;
+}
+
+/*********************************************************************/
+/* eth_disable - Disable the interface */
+/*********************************************************************/
+static void davicom_disable(struct dev *dev)
+{
+ struct nic *nic = (struct nic *)dev;
+ whereami("davicom_disable\n");
+
+ davicom_reset(nic);
+
+ /* disable interrupts */
+ outl(0x00000000, ioaddr + CSR7);
+
+ /* Stop the chip's Tx and Rx processes. */
+ outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
+
+ /* Clear the missed-packet counter. */
+ (volatile unsigned long)inl(ioaddr + CSR8);
+}
+
+
+/*********************************************************************/
+/* eth_irq - enable, disable and force interrupts */
+/*********************************************************************/
+static void davicom_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+
+/*********************************************************************/
+/* eth_probe - Look for an adapter */
+/*********************************************************************/
+static int davicom_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *)dev;
+ unsigned int i;
+
+ whereami("davicom_probe\n");
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ vendor = pci->vendor;
+ dev_id = pci->dev_id;
+ ioaddr = pci->ioaddr & ~3;
+
+ nic->irqno = 0;
+ nic->ioaddr = pci->ioaddr & ~3;
+
+ /* wakeup chip */
+ pcibios_write_config_dword(pci->bus, pci->devfn, 0x40, 0x00000000);
+
+ /* Stop the chip's Tx and Rx processes. */
+ outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
+
+ /* Clear the missed-packet counter. */
+ (volatile unsigned long)inl(ioaddr + CSR8);
+
+ /* Get MAC Address */
+ /* read EEPROM data */
+ for (i = 0; i < sizeof(ee_data)/2; i++)
+ ((unsigned short *)ee_data)[i] =
+ le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN));
+
+ /* extract MAC address from EEPROM buffer */
+ for (i=0; i<ETH_ALEN; i++)
+ nic->node_addr[i] = ee_data[20+i];
+
+ printf("Davicom %! at ioaddr %#hX\n", nic->node_addr, ioaddr);
+
+ /* initialize device */
+ davicom_reset(nic);
+
+ dev->disable = davicom_disable;
+ nic->poll = davicom_poll;
+ nic->transmit = davicom_transmit;
+ nic->irq = davicom_irq;
+
+ return 1;
+}
+
+static struct pci_id davicom_nics[] = {
+PCI_ROM(0x1282, 0x9100, "davicom9100", "Davicom 9100"),
+PCI_ROM(0x1282, 0x9102, "davicom9102", "Davicom 9102"),
+PCI_ROM(0x1282, 0x9009, "davicom9009", "Davicom 9009"),
+PCI_ROM(0x1282, 0x9132, "davicom9132", "Davicom 9132"), /* Needs probably some fixing */
+};
+
+static struct pci_driver davicom_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "DAVICOM",
+ .probe = davicom_probe,
+ .ids = davicom_nics,
+ .id_count = sizeof(davicom_nics)/sizeof(davicom_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/depca.c b/src/drivers/net/depca.c
new file mode 100644
index 00000000..29266b10
--- /dev/null
+++ b/src/drivers/net/depca.c
@@ -0,0 +1,794 @@
+/* Not fixed for relocation yet. Probably won't work relocated above 16MB */
+#ifdef ALLMULTI
+#error multicast support is not yet implemented
+#endif
+/* Etherboot: depca.h merged, comments from Linux driver retained */
+/* depca.c: A DIGITAL DEPCA & EtherWORKS ethernet driver for linux.
+
+ Written 1994, 1995 by David C. Davies.
+
+
+ Copyright 1994 David C. Davies
+ and
+ United States Government
+ (as represented by the Director, National Security Agency).
+
+ Copyright 1995 Digital Equipment Corporation.
+
+
+ This software may be used and distributed according to the terms of
+ the GNU Public License, incorporated herein by reference.
+
+ This driver is written for the Digital Equipment Corporation series
+ of DEPCA and EtherWORKS ethernet cards:
+
+ DEPCA (the original)
+ DE100
+ DE101
+ DE200 Turbo
+ DE201 Turbo
+ DE202 Turbo (TP BNC)
+ DE210
+ DE422 (EISA)
+
+ The driver has been tested on DE100, DE200 and DE202 cards in a
+ relatively busy network. The DE422 has been tested a little.
+
+ This driver will NOT work for the DE203, DE204 and DE205 series of
+ cards, since they have a new custom ASIC in place of the AMD LANCE
+ chip. See the 'ewrk3.c' driver in the Linux source tree for running
+ those cards.
+
+ I have benchmarked the driver with a DE100 at 595kB/s to (542kB/s from)
+ a DECstation 5000/200.
+
+ The author may be reached at davies@maniac.ultranet.com
+
+ =========================================================================
+
+ The driver was originally based on the 'lance.c' driver from Donald
+ Becker which is included with the standard driver distribution for
+ linux. V0.4 is a complete re-write with only the kernel interface
+ remaining from the original code.
+
+ 1) Lance.c code in /linux/drivers/net/
+ 2) "Ethernet/IEEE 802.3 Family. 1992 World Network Data Book/Handbook",
+ AMD, 1992 [(800) 222-9323].
+ 3) "Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE)",
+ AMD, Pub. #17881, May 1993.
+ 4) "Am79C960 PCnet-ISA(tm), Single-Chip Ethernet Controller for ISA",
+ AMD, Pub. #16907, May 1992
+ 5) "DEC EtherWORKS LC Ethernet Controller Owners Manual",
+ Digital Equipment corporation, 1990, Pub. #EK-DE100-OM.003
+ 6) "DEC EtherWORKS Turbo Ethernet Controller Owners Manual",
+ Digital Equipment corporation, 1990, Pub. #EK-DE200-OM.003
+ 7) "DEPCA Hardware Reference Manual", Pub. #EK-DEPCA-PR
+ Digital Equipment Corporation, 1989
+ 8) "DEC EtherWORKS Turbo_(TP BNC) Ethernet Controller Owners Manual",
+ Digital Equipment corporation, 1991, Pub. #EK-DE202-OM.001
+
+
+ Peter Bauer's depca.c (V0.5) was referred to when debugging V0.1 of this
+ driver.
+
+ The original DEPCA card requires that the ethernet ROM address counter
+ be enabled to count and has an 8 bit NICSR. The ROM counter enabling is
+ only done when a 0x08 is read as the first address octet (to minimise
+ the chances of writing over some other hardware's I/O register). The
+ NICSR accesses have been changed to byte accesses for all the cards
+ supported by this driver, since there is only one useful bit in the MSB
+ (remote boot timeout) and it is not used. Also, there is a maximum of
+ only 48kB network RAM for this card. My thanks to Torbjorn Lindh for
+ help debugging all this (and holding my feet to the fire until I got it
+ right).
+
+ The DE200 series boards have on-board 64kB RAM for use as a shared
+ memory network buffer. Only the DE100 cards make use of a 2kB buffer
+ mode which has not been implemented in this driver (only the 32kB and
+ 64kB modes are supported [16kB/48kB for the original DEPCA]).
+
+ At the most only 2 DEPCA cards can be supported on the ISA bus because
+ there is only provision for two I/O base addresses on each card (0x300
+ and 0x200). The I/O address is detected by searching for a byte sequence
+ in the Ethernet station address PROM at the expected I/O address for the
+ Ethernet PROM. The shared memory base address is 'autoprobed' by
+ looking for the self test PROM and detecting the card name. When a
+ second DEPCA is detected, information is placed in the base_addr
+ variable of the next device structure (which is created if necessary),
+ thus enabling ethif_probe initialization for the device. More than 2
+ EISA cards can be supported, but care will be needed assigning the
+ shared memory to ensure that each slot has the correct IRQ, I/O address
+ and shared memory address assigned.
+
+ ************************************************************************
+
+ NOTE: If you are using two ISA DEPCAs, it is important that you assign
+ the base memory addresses correctly. The driver autoprobes I/O 0x300
+ then 0x200. The base memory address for the first device must be less
+ than that of the second so that the auto probe will correctly assign the
+ I/O and memory addresses on the same card. I can't think of a way to do
+ this unambiguously at the moment, since there is nothing on the cards to
+ tie I/O and memory information together.
+
+ I am unable to test 2 cards together for now, so this code is
+ unchecked. All reports, good or bad, are welcome.
+
+ ************************************************************************
+
+ The board IRQ setting must be at an unused IRQ which is auto-probed
+ using Donald Becker's autoprobe routines. DEPCA and DE100 board IRQs are
+ {2,3,4,5,7}, whereas the DE200 is at {5,9,10,11,15}. Note that IRQ2 is
+ really IRQ9 in machines with 16 IRQ lines.
+
+ No 16MB memory limitation should exist with this driver as DMA is not
+ used and the common memory area is in low memory on the network card (my
+ current system has 20MB and I've not had problems yet).
+
+ The ability to load this driver as a loadable module has been added. To
+ utilise this ability, you have to do <8 things:
+
+ 0) have a copy of the loadable modules code installed on your system.
+ 1) copy depca.c from the /linux/drivers/net directory to your favourite
+ temporary directory.
+ 2) if you wish, edit the source code near line 1530 to reflect the I/O
+ address and IRQ you're using (see also 5).
+ 3) compile depca.c, but include -DMODULE in the command line to ensure
+ that the correct bits are compiled (see end of source code).
+ 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
+ kernel with the depca configuration turned off and reboot.
+ 5) insmod depca.o [irq=7] [io=0x200] [mem=0xd0000] [adapter_name=DE100]
+ [Alan Cox: Changed the code to allow command line irq/io assignments]
+ [Dave Davies: Changed the code to allow command line mem/name
+ assignments]
+ 6) run the net startup bits for your eth?? interface manually
+ (usually /etc/rc.inet[12] at boot time).
+ 7) enjoy!
+
+ Note that autoprobing is not allowed in loadable modules - the system is
+ already up and running and you're messing with interrupts.
+
+ To unload a module, turn off the associated interface
+ 'ifconfig eth?? down' then 'rmmod depca'.
+
+ To assign a base memory address for the shared memory when running as a
+ loadable module, see 5 above. To include the adapter name (if you have
+ no PROM but know the card name) also see 5 above. Note that this last
+ option will not work with kernel built-in depca's.
+
+ The shared memory assignment for a loadable module makes sense to avoid
+ the 'memory autoprobe' picking the wrong shared memory (for the case of
+ 2 depca's in a PC).
+
+ ************************************************************************
+ Support for MCA EtherWORKS cards added 11-3-98.
+ Verified to work with up to 2 DE212 cards in a system (although not
+ fully stress-tested).
+
+ Currently known bugs/limitations:
+
+ Note: with the MCA stuff as a module, it trusts the MCA configuration,
+ not the command line for IRQ and memory address. You can
+ specify them if you want, but it will throw your values out.
+ You still have to pass the IO address it was configured as
+ though.
+
+ ************************************************************************
+ TO DO:
+ ------
+
+
+ Revision History
+ ----------------
+
+ Version Date Description
+
+ 0.1 25-jan-94 Initial writing.
+ 0.2 27-jan-94 Added LANCE TX hardware buffer chaining.
+ 0.3 1-feb-94 Added multiple DEPCA support.
+ 0.31 4-feb-94 Added DE202 recognition.
+ 0.32 19-feb-94 Tidy up. Improve multi-DEPCA support.
+ 0.33 25-feb-94 Fix DEPCA ethernet ROM counter enable.
+ Add jabber packet fix from murf@perftech.com
+ and becker@super.org
+ 0.34 7-mar-94 Fix DEPCA max network memory RAM & NICSR access.
+ 0.35 8-mar-94 Added DE201 recognition. Tidied up.
+ 0.351 30-apr-94 Added EISA support. Added DE422 recognition.
+ 0.36 16-may-94 DE422 fix released.
+ 0.37 22-jul-94 Added MODULE support
+ 0.38 15-aug-94 Added DBR ROM switch in depca_close().
+ Multi DEPCA bug fix.
+ 0.38axp 15-sep-94 Special version for Alpha AXP Linux V1.0.
+ 0.381 12-dec-94 Added DE101 recognition, fix multicast bug.
+ 0.382 9-feb-95 Fix recognition bug reported by <bkm@star.rl.ac.uk>.
+ 0.383 22-feb-95 Fix for conflict with VESA SCSI reported by
+ <stromain@alf.dec.com>
+ 0.384 17-mar-95 Fix a ring full bug reported by <bkm@star.rl.ac.uk>
+ 0.385 3-apr-95 Fix a recognition bug reported by
+ <ryan.niemi@lastfrontier.com>
+ 0.386 21-apr-95 Fix the last fix...sorry, must be galloping senility
+ 0.40 25-May-95 Rewrite for portability & updated.
+ ALPHA support from <jestabro@amt.tay1.dec.com>
+ 0.41 26-Jun-95 Added verify_area() calls in depca_ioctl() from
+ suggestion by <heiko@colossus.escape.de>
+ 0.42 27-Dec-95 Add 'mem' shared memory assignment for loadable
+ modules.
+ Add 'adapter_name' for loadable modules when no PROM.
+ Both above from a suggestion by
+ <pchen@woodruffs121.residence.gatech.edu>.
+ Add new multicasting code.
+ 0.421 22-Apr-96 Fix alloc_device() bug <jari@markkus2.fimr.fi>
+ 0.422 29-Apr-96 Fix depca_hw_init() bug <jari@markkus2.fimr.fi>
+ 0.423 7-Jun-96 Fix module load bug <kmg@barco.be>
+ 0.43 16-Aug-96 Update alloc_device() to conform to de4x5.c
+ 0.44 1-Sep-97 Fix *_probe() to test check_region() first - bug
+ reported by <mmogilvi@elbert.uccs.edu>
+ 0.45 3-Nov-98 Added support for MCA EtherWORKS (DE210/DE212) cards
+ by <tymm@computer.org>
+ 0.451 5-Nov-98 Fixed mca stuff cuz I'm a dummy. <tymm@computer.org>
+ 0.5 14-Nov-98 Re-spin for 2.1.x kernels.
+ 0.51 27-Jun-99 Correct received packet length for CRC from
+ report by <worm@dkik.dk>
+
+ =========================================================================
+*/
+
+#include "etherboot.h"
+#include "nic.h"
+#include "isa.h"
+
+/*
+** I/O addresses. Note that the 2k buffer option is not supported in
+** this driver.
+*/
+#define DEPCA_NICSR ioaddr+0x00 /* Network interface CSR */
+#define DEPCA_RBI ioaddr+0x02 /* RAM buffer index (2k buffer mode) */
+#define DEPCA_DATA ioaddr+0x04 /* LANCE registers' data port */
+#define DEPCA_ADDR ioaddr+0x06 /* LANCE registers' address port */
+#define DEPCA_HBASE ioaddr+0x08 /* EISA high memory base address reg. */
+#define DEPCA_PROM ioaddr+0x0c /* Ethernet address ROM data port */
+#define DEPCA_CNFG ioaddr+0x0c /* EISA Configuration port */
+#define DEPCA_RBSA ioaddr+0x0e /* RAM buffer starting address (2k buff.) */
+
+/*
+** These are LANCE registers addressable through DEPCA_ADDR
+*/
+#define CSR0 0
+#define CSR1 1
+#define CSR2 2
+#define CSR3 3
+
+/*
+** NETWORK INTERFACE CSR (NI_CSR) bit definitions
+*/
+
+#define TO 0x0100 /* Time Out for remote boot */
+#define SHE 0x0080 /* SHadow memory Enable */
+#define BS 0x0040 /* Bank Select */
+#define BUF 0x0020 /* BUFfer size (1->32k, 0->64k) */
+#define RBE 0x0010 /* Remote Boot Enable (1->net boot) */
+#define AAC 0x0008 /* Address ROM Address Counter (1->enable) */
+#define _128KB 0x0008 /* 128kB Network RAM (1->enable) */
+#define IM 0x0004 /* Interrupt Mask (1->mask) */
+#define IEN 0x0002 /* Interrupt tristate ENable (1->enable) */
+#define LED 0x0001 /* LED control */
+
+/*
+** Control and Status Register 0 (CSR0) bit definitions
+*/
+
+#define ERR 0x8000 /* Error summary */
+#define BABL 0x4000 /* Babble transmitter timeout error */
+#define CERR 0x2000 /* Collision Error */
+#define MISS 0x1000 /* Missed packet */
+#define MERR 0x0800 /* Memory Error */
+#define RINT 0x0400 /* Receiver Interrupt */
+#define TINT 0x0200 /* Transmit Interrupt */
+#define IDON 0x0100 /* Initialization Done */
+#define INTR 0x0080 /* Interrupt Flag */
+#define INEA 0x0040 /* Interrupt Enable */
+#define RXON 0x0020 /* Receiver on */
+#define TXON 0x0010 /* Transmitter on */
+#define TDMD 0x0008 /* Transmit Demand */
+#define STOP 0x0004 /* Stop */
+#define STRT 0x0002 /* Start */
+#define INIT 0x0001 /* Initialize */
+#define INTM 0xff00 /* Interrupt Mask */
+#define INTE 0xfff0 /* Interrupt Enable */
+
+/*
+** CONTROL AND STATUS REGISTER 3 (CSR3)
+*/
+
+#define BSWP 0x0004 /* Byte SWaP */
+#define ACON 0x0002 /* ALE control */
+#define BCON 0x0001 /* Byte CONtrol */
+
+/*
+** Initialization Block Mode Register
+*/
+
+#define PROM 0x8000 /* Promiscuous Mode */
+#define EMBA 0x0080 /* Enable Modified Back-off Algorithm */
+#define INTL 0x0040 /* Internal Loopback */
+#define DRTY 0x0020 /* Disable Retry */
+#define COLL 0x0010 /* Force Collision */
+#define DTCR 0x0008 /* Disable Transmit CRC */
+#define LOOP 0x0004 /* Loopback */
+#define DTX 0x0002 /* Disable the Transmitter */
+#define DRX 0x0001 /* Disable the Receiver */
+
+/*
+** Receive Message Descriptor 1 (RMD1) bit definitions.
+*/
+
+#define R_OWN 0x80000000 /* Owner bit 0 = host, 1 = lance */
+#define R_ERR 0x4000 /* Error Summary */
+#define R_FRAM 0x2000 /* Framing Error */
+#define R_OFLO 0x1000 /* Overflow Error */
+#define R_CRC 0x0800 /* CRC Error */
+#define R_BUFF 0x0400 /* Buffer Error */
+#define R_STP 0x0200 /* Start of Packet */
+#define R_ENP 0x0100 /* End of Packet */
+
+/*
+** Transmit Message Descriptor 1 (TMD1) bit definitions.
+*/
+
+#define T_OWN 0x80000000 /* Owner bit 0 = host, 1 = lance */
+#define T_ERR 0x4000 /* Error Summary */
+#define T_ADD_FCS 0x2000 /* More the 1 retry needed to Xmit */
+#define T_MORE 0x1000 /* >1 retry to transmit packet */
+#define T_ONE 0x0800 /* 1 try needed to transmit the packet */
+#define T_DEF 0x0400 /* Deferred */
+#define T_STP 0x02000000 /* Start of Packet */
+#define T_ENP 0x01000000 /* End of Packet */
+#define T_FLAGS 0xff000000 /* TX Flags Field */
+
+/*
+** Transmit Message Descriptor 3 (TMD3) bit definitions.
+*/
+
+#define TMD3_BUFF 0x8000 /* BUFFer error */
+#define TMD3_UFLO 0x4000 /* UnderFLOw error */
+#define TMD3_RES 0x2000 /* REServed */
+#define TMD3_LCOL 0x1000 /* Late COLlision */
+#define TMD3_LCAR 0x0800 /* Loss of CARrier */
+#define TMD3_RTRY 0x0400 /* ReTRY error */
+
+/*
+** Ethernet PROM defines
+*/
+#define PROBE_LENGTH 32
+
+/*
+** Set the number of Tx and Rx buffers. Ensure that the memory requested
+** here is <= to the amount of shared memory set up by the board switches.
+** The number of descriptors MUST BE A POWER OF 2.
+**
+** total_memory = NUM_RX_DESC*(8+RX_BUFF_SZ) + NUM_TX_DESC*(8+TX_BUFF_SZ)
+*/
+#define NUM_RX_DESC 2 /* Number of RX descriptors */
+#define NUM_TX_DESC 2 /* Number of TX descriptors */
+#define RX_BUFF_SZ 1536 /* Buffer size for each Rx buffer */
+#define TX_BUFF_SZ 1536 /* Buffer size for each Tx buffer */
+
+/*
+** ISA Bus defines
+*/
+#define DEPCA_IO_PORTS {0x300, 0x200, 0}
+
+#ifndef DEPCA_MODEL
+#define DEPCA_MODEL DEPCA
+#endif
+
+static enum {
+ DEPCA, DE100, DE101, DE200, DE201, DE202, DE210, DE212, DE422, unknown
+} adapter = DEPCA_MODEL;
+
+/*
+** Name <-> Adapter mapping
+*/
+
+static char *adapter_name[] = {
+ "DEPCA",
+ "DE100","DE101",
+ "DE200","DE201","DE202",
+ "DE210","DE212",
+ "DE422",
+ ""
+};
+
+#ifndef DEPCA_RAM_BASE
+#define DEPCA_RAM_BASE 0xd0000
+#endif
+
+/*
+** Memory Alignment. Each descriptor is 4 longwords long. To force a
+** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and
+** DESC_ALIGN. ALIGN aligns the start address of the private memory area
+** and hence the RX descriptor ring's first entry.
+*/
+#define ALIGN4 ((u32)4 - 1) /* 1 longword align */
+#define ALIGN8 ((u32)8 - 1) /* 2 longword (quadword) align */
+#define ALIGN ALIGN8 /* Keep the LANCE happy... */
+
+typedef long s32;
+typedef unsigned long u32;
+typedef short s16;
+typedef unsigned short u16;
+typedef char s8;
+typedef unsigned char u8;
+
+/*
+** The DEPCA Rx and Tx ring descriptors.
+*/
+struct depca_rx_desc {
+ volatile s32 base;
+ s16 buf_length; /* This length is negative 2's complement! */
+ s16 msg_length; /* This length is "normal". */
+};
+
+struct depca_tx_desc {
+ volatile s32 base;
+ s16 length; /* This length is negative 2's complement! */
+ s16 misc; /* Errors and TDR info */
+};
+
+#define LA_MASK 0x0000ffff /* LANCE address mask for mapping network RAM
+ to LANCE memory address space */
+
+/*
+** The Lance initialization block, described in databook, in common memory.
+*/
+struct depca_init {
+ u16 mode; /* Mode register */
+ u8 phys_addr[ETH_ALEN]; /* Physical ethernet address */
+ u8 mcast_table[8]; /* Multicast Hash Table. */
+ u32 rx_ring; /* Rx ring base pointer & ring length */
+ u32 tx_ring; /* Tx ring base pointer & ring length */
+};
+
+struct depca_private {
+ struct depca_rx_desc *rx_ring;
+ struct depca_tx_desc *tx_ring;
+ struct depca_init init_block; /* Shadow init block */
+ char *rx_memcpy[NUM_RX_DESC];
+ char *tx_memcpy[NUM_TX_DESC];
+ u32 bus_offset; /* ISA bus address offset */
+ u32 sh_mem; /* address of shared mem */
+ u32 dma_buffs; /* Rx & Tx buffer start */
+ int rx_cur, tx_cur; /* Next free ring entry */
+ int txRingMask, rxRingMask;
+ s32 rx_rlen, tx_rlen;
+ /* log2([rt]xRingMask+1) for the descriptors */
+};
+
+static Address mem_start = DEPCA_RAM_BASE;
+static Address mem_len, offset;
+static unsigned short ioaddr = 0;
+static struct depca_private lp;
+
+/*
+** Miscellaneous defines...
+*/
+#define STOP_DEPCA \
+ outw(CSR0, DEPCA_ADDR);\
+ outw(STOP, DEPCA_DATA)
+
+/* Initialize the lance Rx and Tx descriptor rings. */
+static void depca_init_ring(struct nic *nic)
+{
+ int i;
+ u32 p;
+
+ lp.rx_cur = lp.tx_cur = 0;
+ /* Initialize the base addresses and length of each buffer in the ring */
+ for (i = 0; i <= lp.rxRingMask; i++) {
+ writel((p = lp.dma_buffs + i * RX_BUFF_SZ) | R_OWN, &lp.rx_ring[i].base);
+ writew(-RX_BUFF_SZ, &lp.rx_ring[i].buf_length);
+ lp.rx_memcpy[i] = (char *) (p + lp.bus_offset);
+ }
+ for (i = 0; i <= lp.txRingMask; i++) {
+ writel((p = lp.dma_buffs + (i + lp.txRingMask + 1) * TX_BUFF_SZ) & 0x00ffffff, &lp.tx_ring[i].base);
+ lp.tx_memcpy[i] = (char *) (p + lp.bus_offset);
+ }
+
+ /* Set up the initialization block */
+ lp.init_block.rx_ring = ((u32) ((u32) lp.rx_ring) & LA_MASK) | lp.rx_rlen;
+ lp.init_block.tx_ring = ((u32) ((u32) lp.tx_ring) & LA_MASK) | lp.tx_rlen;
+ for (i = 0; i < ETH_ALEN; i++)
+ lp.init_block.phys_addr[i] = nic->node_addr[i];
+ lp.init_block.mode = 0x0000; /* Enable the Tx and Rx */
+ memset(lp.init_block.mcast_table, 0, sizeof(lp.init_block.mcast_table));
+}
+
+static void LoadCSRs(void)
+{
+ outw(CSR1, DEPCA_ADDR); /* initialisation block address LSW */
+ outw((u16) (lp.sh_mem & LA_MASK), DEPCA_DATA);
+ outw(CSR2, DEPCA_ADDR); /* initialisation block address MSW */
+ outw((u16) ((lp.sh_mem & LA_MASK) >> 16), DEPCA_DATA);
+ outw(CSR3, DEPCA_ADDR); /* ALE control */
+ outw(ACON, DEPCA_DATA);
+ outw(CSR0, DEPCA_ADDR); /* Point back to CSR0 */
+}
+
+static int InitRestartDepca(void)
+{
+ int i;
+
+ /* Copy the shadow init_block to shared memory */
+ memcpy_toio((char *)lp.sh_mem, &lp.init_block, sizeof(struct depca_init));
+ outw(CSR0, DEPCA_ADDR); /* point back to CSR0 */
+ outw(INIT, DEPCA_DATA); /* initialise DEPCA */
+
+ for (i = 0; i < 100 && !(inw(DEPCA_DATA) & IDON); i++)
+ ;
+ if (i < 100) {
+ /* clear IDON by writing a 1, and start LANCE */
+ outw(IDON | STRT, DEPCA_DATA);
+ } else {
+ printf("DEPCA not initialised\n");
+ return (1);
+ }
+ return (0);
+}
+
+/**************************************************************************
+RESET - Reset adapter
+***************************************************************************/
+static void depca_reset(struct nic *nic)
+{
+ s16 nicsr;
+ int i, j;
+
+ STOP_DEPCA;
+ nicsr = inb(DEPCA_NICSR);
+ nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM);
+ outb(nicsr, DEPCA_NICSR);
+ if (inw(DEPCA_DATA) != STOP)
+ {
+ printf("depca: Cannot stop NIC\n");
+ return;
+ }
+
+ /* Initialisation block */
+ lp.sh_mem = mem_start;
+ mem_start += sizeof(struct depca_init);
+ /* Tx & Rx descriptors (aligned to a quadword boundary) */
+ mem_start = (mem_start + ALIGN) & ~ALIGN;
+ lp.rx_ring = (struct depca_rx_desc *) mem_start;
+ mem_start += (sizeof(struct depca_rx_desc) * NUM_RX_DESC);
+ lp.tx_ring = (struct depca_tx_desc *) mem_start;
+ mem_start += (sizeof(struct depca_tx_desc) * NUM_TX_DESC);
+
+ lp.bus_offset = mem_start & 0x00ff0000;
+ /* LANCE re-mapped start address */
+ lp.dma_buffs = mem_start & LA_MASK;
+
+ /* Finish initialising the ring information. */
+ lp.rxRingMask = NUM_RX_DESC - 1;
+ lp.txRingMask = NUM_TX_DESC - 1;
+
+ /* Calculate Tx/Rx RLEN size for the descriptors. */
+ for (i = 0, j = lp.rxRingMask; j > 0; i++) {
+ j >>= 1;
+ }
+ lp.rx_rlen = (s32) (i << 29);
+ for (i = 0, j = lp.txRingMask; j > 0; i++) {
+ j >>= 1;
+ }
+ lp.tx_rlen = (s32) (i << 29);
+
+ /* Load the initialisation block */
+ depca_init_ring(nic);
+ LoadCSRs();
+ InitRestartDepca();
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int depca_poll(struct nic *nic, int retrieve)
+{
+ int entry;
+ u32 status;
+
+ entry = lp.rx_cur;
+ if ((status = readl(&lp.rx_ring[entry].base) & R_OWN))
+ return (0);
+
+ if ( ! retrieve ) return 1;
+
+ memcpy(nic->packet, lp.rx_memcpy[entry], nic->packetlen = lp.rx_ring[entry].msg_length);
+ lp.rx_ring[entry].base |= R_OWN;
+ lp.rx_cur = (++lp.rx_cur) & lp.rxRingMask;
+ return (1);
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void depca_transmit(
+ struct nic *nic,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+ int entry, len;
+ char *mem;
+
+ /* send the packet to destination */
+ /*
+ ** Caution: the right order is important here... dont
+ ** setup the ownership rights until all the other
+ ** information is in place
+ */
+ mem = lp.tx_memcpy[entry = lp.tx_cur];
+ memcpy_toio(mem, d, ETH_ALEN);
+ memcpy_toio(mem + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ mem[ETH_ALEN * 2] = t >> 8;
+ mem[ETH_ALEN * 2 + 1] = t;
+ memcpy_toio(mem + ETH_HLEN, p, s);
+ s += ETH_HLEN;
+ len = (s < ETH_ZLEN ? ETH_ZLEN : s);
+ /* clean out flags */
+ writel(readl(&lp.tx_ring[entry].base) & ~T_FLAGS, &lp.tx_ring[entry].base);
+ /* clears other error flags */
+ writew(0x0000, &lp.tx_ring[entry].misc);
+ /* packet length in buffer */
+ writew(-len, &lp.tx_ring[entry].length);
+ /* start and end of packet, ownership */
+ writel(readl(&lp.tx_ring[entry].base) | (T_STP|T_ENP|T_OWN), &lp.tx_ring[entry].base);
+ /* update current pointers */
+ lp.tx_cur = (++lp.tx_cur) & lp.txRingMask;
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void depca_disable(struct dev *dev)
+{
+ struct nic *nic = (struct nic *)dev;
+ /* reset and disable merge */
+ depca_reset(nic);
+
+ STOP_DEPCA;
+}
+
+/**************************************************************************
+IRQ - Interrupt Control
+***************************************************************************/
+static void depca_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/*
+** Look for a special sequence in the Ethernet station address PROM that
+** is common across all DEPCA products. Note that the original DEPCA needs
+** its ROM address counter to be initialized and enabled. Only enable
+** if the first address octet is a 0x08 - this minimises the chances of
+** messing around with some other hardware, but it assumes that this DEPCA
+** card initialized itself correctly.
+**
+** Search the Ethernet address ROM for the signature. Since the ROM address
+** counter can start at an arbitrary point, the search must include the entire
+** probe sequence length plus the (length_of_the_signature - 1).
+** Stop the search IMMEDIATELY after the signature is found so that the
+** PROM address counter is correctly positioned at the start of the
+** ethernet address for later read out.
+*/
+static int depca_probe1(struct nic *nic)
+{
+ u8 data, nicsr;
+ /* This is only correct for little endian machines, but then
+ Etherboot doesn't work on anything but a PC */
+ u8 sig[] = { 0xFF, 0x00, 0x55, 0xAA, 0xFF, 0x00, 0x55, 0xAA };
+ int i, j;
+ long sum, chksum;
+
+ data = inb(DEPCA_PROM); /* clear counter on DEPCA */
+ data = inb(DEPCA_PROM); /* read data */
+ if (data == 0x8) {
+ nicsr = inb(DEPCA_NICSR);
+ nicsr |= AAC;
+ outb(nicsr, DEPCA_NICSR);
+ }
+ for (i = 0, j = 0; j < (int)sizeof(sig) && i < PROBE_LENGTH+((int)sizeof(sig))-1; ++i) {
+ data = inb(DEPCA_PROM);
+ if (data == sig[j]) /* track signature */
+ ++j;
+ else
+ j = (data == sig[0]) ? 1 : 0;
+ }
+ if (j != sizeof(sig))
+ return (0);
+ /* put the card in its initial state */
+ STOP_DEPCA;
+ nicsr = ((inb(DEPCA_NICSR) & ~SHE & ~RBE & ~IEN) | IM);
+ outb(nicsr, DEPCA_NICSR);
+ if (inw(DEPCA_DATA) != STOP)
+ return (0);
+ memcpy((char *)mem_start, sig, sizeof(sig));
+ if (memcmp((char *)mem_start, sig, sizeof(sig)) != 0)
+ return (0);
+ for (i = 0, j = 0, sum = 0; j < 3; j++) {
+ sum <<= 1;
+ if (sum > 0xFFFF)
+ sum -= 0xFFFF;
+ sum += (u8)(nic->node_addr[i++] = inb(DEPCA_PROM));
+ sum += (u16)((nic->node_addr[i++] = inb(DEPCA_PROM)) << 8);
+ if (sum > 0xFFFF)
+ sum -= 0xFFFF;
+ }
+ if (sum == 0xFFFF)
+ sum = 0;
+ chksum = (u8)inb(DEPCA_PROM);
+ chksum |= (u16)(inb(DEPCA_PROM) << 8);
+ mem_len = (adapter == DEPCA) ? (48 << 10) : (64 << 10);
+ offset = 0;
+ if (nicsr & BUF) {
+ offset = 0x8000;
+ nicsr &= ~BS;
+ mem_len -= (32 << 10);
+ }
+ if (adapter != DEPCA) /* enable shadow RAM */
+ outb(nicsr |= SHE, DEPCA_NICSR);
+ printf("%s base %#hX, memory [%#hX-%#hX], addr %!",
+ adapter_name[adapter], ioaddr, mem_start, mem_start + mem_len,
+ nic->node_addr);
+ if (sum != chksum)
+ printf(" (bad checksum)");
+ putchar('\n');
+ return (1);
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+static int depca_probe(struct dev *dev, unsigned short *probe_addrs)
+{
+ struct nic *nic = (struct nic *)dev;
+ static unsigned short base[] = DEPCA_IO_PORTS;
+ int i;
+
+ if (probe_addrs == 0 || probe_addrs[0] == 0)
+ probe_addrs = base; /* Use defaults */
+ for (i = 0; (ioaddr = base[i]) != 0; ++i) {
+ if (depca_probe1(nic))
+ break;
+ }
+ if (ioaddr == 0)
+ return (0);
+
+ nic->irqno = 0;
+ nic->ioaddr = ioaddr & ~3;
+
+ depca_reset(nic);
+ /* point to NIC specific routines */
+ dev->disable = depca_disable;
+ nic->poll = depca_poll;
+ nic->transmit = depca_transmit;
+ nic->irq = depca_irq;
+
+ /* Based on PnP ISA map */
+ dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
+ dev->devid.device_id = htons(0x80f7);
+ return 1;
+}
+
+static struct isa_driver depca_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "DEPCA",
+ .probe = depca_probe,
+ .ioaddrs = 0,
+};
diff --git a/src/drivers/net/dmfe.c b/src/drivers/net/dmfe.c
new file mode 100644
index 00000000..10c47773
--- /dev/null
+++ b/src/drivers/net/dmfe.c
@@ -0,0 +1,1230 @@
+/**************************************************************************
+*
+* dmfe.c -- Etherboot device driver for the Davicom
+* DM9102/DM9102A/DM9102A+DM9801/DM9102A+DM9802 NIC fast ethernet card
+*
+* Written 2003-2003 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code based on:
+*
+* dmfe.c: A Davicom DM9102/DM9102A/DM9102A+DM9801/DM9102A+DM9802
+* NIC fast ethernet driver for Linux.
+* Copyright (C) 1997 Sten Wang
+* (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved.
+*
+*
+* REVISION HISTORY:
+* ================
+* v1.0 10-02-2004 timlegge Boots ltsp needs cleanup
+*
+* Indent Options: indent -kr -i8
+*
+*
+***************************************************************************/
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include "pci.h"
+#include "timer.h"
+
+/* #define EDEBUG 1 */
+#ifdef EDEBUG
+#define dprintf(x) printf x
+#else
+#define dprintf(x)
+#endif
+
+typedef unsigned char u8;
+typedef signed char s8;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned int u32;
+typedef signed int s32;
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+/* Board/System/Debug information/definition ---------------- */
+#define PCI_DM9132_ID 0x91321282 /* Davicom DM9132 ID */
+#define PCI_DM9102_ID 0x91021282 /* Davicom DM9102 ID */
+#define PCI_DM9100_ID 0x91001282 /* Davicom DM9100 ID */
+#define PCI_DM9009_ID 0x90091282 /* Davicom DM9009 ID */
+
+#define DM9102_IO_SIZE 0x80
+#define DM9102A_IO_SIZE 0x100
+#define TX_MAX_SEND_CNT 0x1 /* Maximum tx packet per time */
+#define TX_DESC_CNT 0x10 /* Allocated Tx descriptors */
+#define RX_DESC_CNT 0x20 /* Allocated Rx descriptors */
+#define TX_FREE_DESC_CNT (TX_DESC_CNT - 2) /* Max TX packet count */
+#define TX_WAKE_DESC_CNT (TX_DESC_CNT - 3) /* TX wakeup count */
+#define DESC_ALL_CNT (TX_DESC_CNT + RX_DESC_CNT)
+#define TX_BUF_ALLOC 0x600
+#define RX_ALLOC_SIZE 0x620
+#define DM910X_RESET 1
+#define CR0_DEFAULT 0x00E00000 /* TX & RX burst mode */
+#define CR6_DEFAULT 0x00080000 /* HD */
+#define CR7_DEFAULT 0x180c1
+#define CR15_DEFAULT 0x06 /* TxJabber RxWatchdog */
+#define TDES0_ERR_MASK 0x4302 /* TXJT, LC, EC, FUE */
+#define MAX_PACKET_SIZE 1514
+#define DMFE_MAX_MULTICAST 14
+#define RX_COPY_SIZE 100
+#define MAX_CHECK_PACKET 0x8000
+#define DM9801_NOISE_FLOOR 8
+#define DM9802_NOISE_FLOOR 5
+
+#define DMFE_10MHF 0
+#define DMFE_100MHF 1
+#define DMFE_10MFD 4
+#define DMFE_100MFD 5
+#define DMFE_AUTO 8
+#define DMFE_1M_HPNA 0x10
+
+#define DMFE_TXTH_72 0x400000 /* TX TH 72 byte */
+#define DMFE_TXTH_96 0x404000 /* TX TH 96 byte */
+#define DMFE_TXTH_128 0x0000 /* TX TH 128 byte */
+#define DMFE_TXTH_256 0x4000 /* TX TH 256 byte */
+#define DMFE_TXTH_512 0x8000 /* TX TH 512 byte */
+#define DMFE_TXTH_1K 0xC000 /* TX TH 1K byte */
+
+#define DMFE_TIMER_WUT (jiffies + HZ * 1) /* timer wakeup time : 1 second */
+#define DMFE_TX_TIMEOUT ((3*HZ)/2) /* tx packet time-out time 1.5 s" */
+#define DMFE_TX_KICK (HZ/2) /* tx packet Kick-out time 0.5 s" */
+
+#define DMFE_DBUG(dbug_now, msg, value) if (dmfe_debug || (dbug_now)) printk(KERN_ERR DRV_NAME ": %s %lx\n", (msg), (long) (value))
+
+#define SHOW_MEDIA_TYPE(mode) printk(KERN_ERR DRV_NAME ": Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half");
+
+
+/* CR9 definition: SROM/MII */
+#define CR9_SROM_READ 0x4800
+#define CR9_SRCS 0x1
+#define CR9_SRCLK 0x2
+#define CR9_CRDOUT 0x8
+#define SROM_DATA_0 0x0
+#define SROM_DATA_1 0x4
+#define PHY_DATA_1 0x20000
+#define PHY_DATA_0 0x00000
+#define MDCLKH 0x10000
+
+#define PHY_POWER_DOWN 0x800
+
+#define SROM_V41_CODE 0x14
+
+#define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);udelay(5);outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);udelay(5);outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);udelay(5);
+
+#define __CHK_IO_SIZE(pci_id, dev_rev) ( ((pci_id)==PCI_DM9132_ID) || ((dev_rev) >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE
+#define CHK_IO_SIZE(pci_dev, dev_rev) __CHK_IO_SIZE(((pci_dev)->device << 16) | (pci_dev)->vendor, dev_rev)
+
+/* Sten Check */
+#define DEVICE net_device
+
+/* Structure/enum declaration ------------------------------- */
+struct tx_desc {
+ u32 tdes0, tdes1, tdes2, tdes3; /* Data for the card */
+ u32 tx_buf_ptr; /* Data for us */
+ u32 /* struct tx_desc * */ next_tx_desc;
+} __attribute__ ((aligned(32)));
+
+struct rx_desc {
+ u32 rdes0, rdes1, rdes2, rdes3; /* Data for the card */
+ u32 rx_skb_ptr; /* Data for us */
+ u32 /* struct rx_desc * */ next_rx_desc;
+} __attribute__ ((aligned(32)));
+
+struct dmfe_private {
+ u32 chip_id; /* Chip vendor/Device ID */
+ u32 chip_revision; /* Chip revision */
+ u32 cr0_data;
+// u32 cr5_data;
+ u32 cr6_data;
+ u32 cr7_data;
+ u32 cr15_data;
+
+ u16 HPNA_command; /* For HPNA register 16 */
+ u16 HPNA_timer; /* For HPNA remote device check */
+ u16 NIC_capability; /* NIC media capability */
+ u16 PHY_reg4; /* Saved Phyxcer register 4 value */
+
+ u8 HPNA_present; /* 0:none, 1:DM9801, 2:DM9802 */
+ u8 chip_type; /* Keep DM9102A chip type */
+ u8 media_mode; /* user specify media mode */
+ u8 op_mode; /* real work media mode */
+ u8 phy_addr;
+ u8 dm910x_chk_mode; /* Operating mode check */
+
+ /* NIC SROM data */
+ unsigned char srom[128];
+ /* Etherboot Only */
+ u8 cur_tx;
+ u8 cur_rx;
+} dfx;
+
+static struct dmfe_private *db;
+
+enum dmfe_offsets {
+ DCR0 = 0x00, DCR1 = 0x08, DCR2 = 0x10, DCR3 = 0x18, DCR4 = 0x20,
+ DCR5 = 0x28, DCR6 = 0x30, DCR7 = 0x38, DCR8 = 0x40, DCR9 = 0x48,
+ DCR10 = 0x50, DCR11 = 0x58, DCR12 = 0x60, DCR13 = 0x68, DCR14 =
+ 0x70,
+ DCR15 = 0x78
+};
+
+enum dmfe_CR6_bits {
+ CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80,
+ CR6_FDM = 0x200, CR6_TXSC = 0x2000, CR6_STI = 0x100000,
+ CR6_SFT = 0x200000, CR6_RXA = 0x40000000, CR6_NO_PURGE = 0x20000000
+};
+
+/* Global variable declaration ----------------------------- */
+static int dmfe_debug;
+static unsigned char dmfe_media_mode = DMFE_AUTO;
+static u32 dmfe_cr6_user_set;
+
+/* For module input parameter */
+static int debug;
+static u8 chkmode = 1;
+static u8 HPNA_mode; /* Default: Low Power/High Speed */
+static u8 HPNA_rx_cmd; /* Default: Disable Rx remote command */
+static u8 HPNA_tx_cmd; /* Default: Don't issue remote command */
+static u8 HPNA_NoiseFloor; /* Default: HPNA NoiseFloor */
+static u8 SF_mode; /* Special Function: 1:VLAN, 2:RX Flow Control
+ 4: TX pause packet */
+
+
+/**********************************************
+* Descriptor Ring and Buffer defination
+***********************************************/
+/* Define the TX Descriptor */
+static struct tx_desc txd[TX_DESC_CNT]
+ __attribute__ ((aligned(32)));
+
+/* Create a static buffer of size PKT_BUF_SZ for each TX Descriptor.
+ All descriptors point to a part of this buffer */
+static unsigned char txb[TX_BUF_ALLOC * TX_DESC_CNT]
+ __attribute__ ((aligned(32)));
+
+/* Define the RX Descriptor */
+static struct rx_desc rxd[RX_DESC_CNT]
+__attribute__ ((aligned(32)));
+
+/* Create a static buffer of size PKT_BUF_SZ for each RX Descriptor.
+ All descriptors point to a part of this buffer */
+static unsigned char rxb[RX_ALLOC_SIZE * RX_DESC_CNT]
+__attribute__ ((aligned(32)));
+
+/* NIC specific static variables go here */
+long int BASE;
+
+static u16 read_srom_word(long ioaddr, int offset);
+static void dmfe_init_dm910x(struct nic *nic);
+static void dmfe_descriptor_init(struct nic *, unsigned long ioaddr);
+static void update_cr6(u32, unsigned long);
+static void send_filter_frame(struct nic *nic);
+static void dm9132_id_table(struct nic *nic);
+
+static u16 phy_read(unsigned long, u8, u8, u32);
+static void phy_write(unsigned long, u8, u8, u16, u32);
+static void phy_write_1bit(unsigned long, u32);
+static u16 phy_read_1bit(unsigned long);
+static u8 dmfe_sense_speed(struct nic *nic);
+static void dmfe_process_mode(struct nic *nic);
+static void dmfe_set_phyxcer(struct nic *nic);
+
+static void dmfe_parse_srom(struct nic *nic);
+static void dmfe_program_DM9801(struct nic *nic, int);
+static void dmfe_program_DM9802(struct nic *nic);
+
+static void dmfe_reset(struct nic *nic)
+{
+ /* system variable init */
+ db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set;
+
+ db->NIC_capability = 0xf; /* All capability */
+ db->PHY_reg4 = 0x1e0;
+
+ /* CR6 operation mode decision */
+ if (!chkmode || (db->chip_id == PCI_DM9132_ID) ||
+ (db->chip_revision >= 0x02000030)) {
+ db->cr6_data |= DMFE_TXTH_256;
+ db->cr0_data = CR0_DEFAULT;
+ db->dm910x_chk_mode = 4; /* Enter the normal mode */
+ } else {
+ db->cr6_data |= CR6_SFT; /* Store & Forward mode */
+ db->cr0_data = 0;
+ db->dm910x_chk_mode = 1; /* Enter the check mode */
+ }
+ /* Initilize DM910X board */
+ dmfe_init_dm910x(nic);
+
+ return;
+}
+
+/* Initilize DM910X board
+ * Reset DM910X board
+ * Initilize TX/Rx descriptor chain structure
+ * Send the set-up frame
+ * Enable Tx/Rx machine
+ */
+
+static void dmfe_init_dm910x(struct nic *nic)
+{
+ unsigned long ioaddr = BASE;
+
+ /* Reset DM910x MAC controller */
+ outl(DM910X_RESET, ioaddr + DCR0); /* RESET MAC */
+ udelay(100);
+ outl(db->cr0_data, ioaddr + DCR0);
+ udelay(5);
+
+ /* Phy addr : DM910(A)2/DM9132/9801, phy address = 1 */
+ db->phy_addr = 1;
+
+ /* Parser SROM and media mode */
+ dmfe_parse_srom(nic);
+ db->media_mode = dmfe_media_mode;
+
+ /* RESET Phyxcer Chip by GPR port bit 7 */
+ outl(0x180, ioaddr + DCR12); /* Let bit 7 output port */
+ if (db->chip_id == PCI_DM9009_ID) {
+ outl(0x80, ioaddr + DCR12); /* Issue RESET signal */
+ mdelay(300); /* Delay 300 ms */
+ }
+ outl(0x0, ioaddr + DCR12); /* Clear RESET signal */
+
+ /* Process Phyxcer Media Mode */
+ if (!(db->media_mode & 0x10)) /* Force 1M mode */
+ dmfe_set_phyxcer(nic);
+
+ /* Media Mode Process */
+ if (!(db->media_mode & DMFE_AUTO))
+ db->op_mode = db->media_mode; /* Force Mode */
+
+ /* Initiliaze Transmit/Receive decriptor and CR3/4 */
+ dmfe_descriptor_init(nic, ioaddr);
+
+ /* tx descriptor start pointer */
+ outl(virt_to_le32desc(&txd[0]), ioaddr + DCR4); /* TX DESC address */
+
+ /* rx descriptor start pointer */
+ outl(virt_to_le32desc(&rxd[0]), ioaddr + DCR3); /* RX DESC address */
+
+ /* Init CR6 to program DM910x operation */
+ update_cr6(db->cr6_data, ioaddr);
+
+ /* Send setup frame */
+ if (db->chip_id == PCI_DM9132_ID) {
+ dm9132_id_table(nic); /* DM9132 */
+ } else {
+ send_filter_frame(nic); /* DM9102/DM9102A */
+ }
+
+ /* Init CR7, interrupt active bit */
+ db->cr7_data = CR7_DEFAULT;
+ outl(db->cr7_data, ioaddr + DCR7);
+ /* Init CR15, Tx jabber and Rx watchdog timer */
+ outl(db->cr15_data, ioaddr + DCR15);
+ /* Enable DM910X Tx/Rx function */
+ db->cr6_data |= CR6_RXSC | CR6_TXSC | 0x40000;
+ update_cr6(db->cr6_data, ioaddr);
+}
+#ifdef EDEBUG
+void hex_dump(const char *data, const unsigned int len);
+#endif
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int dmfe_poll(struct nic *nic, int retrieve)
+{
+ u32 rdes0;
+ int entry = db->cur_rx % RX_DESC_CNT;
+ int rxlen;
+ rdes0 = le32_to_cpu(rxd[entry].rdes0);
+ if (rdes0 & 0x80000000)
+ return 0;
+
+ if (!retrieve)
+ return 1;
+
+ if ((rdes0 & 0x300) != 0x300) {
+ /* A packet without First/Last flag */
+ printf("strange Packet\n");
+ rxd[entry].rdes0 = cpu_to_le32(0x80000000);
+ return 0;
+ } else {
+ /* A packet with First/Last flag */
+ rxlen = ((rdes0 >> 16) & 0x3fff) - 4;
+ /* error summary bit check */
+ if (rdes0 & 0x8000) {
+ printf("Error\n");
+ return 0;
+ }
+ if (!(rdes0 & 0x8000) ||
+ ((db->cr6_data & CR6_PM) && (rxlen > 6))) {
+ if (db->dm910x_chk_mode & 1)
+ printf("Silly check mode\n");
+
+ nic->packetlen = rxlen;
+ memcpy(nic->packet, rxb + (entry * RX_ALLOC_SIZE),
+ nic->packetlen);
+ }
+ }
+ rxd[entry].rdes0 = cpu_to_le32(0x80000000);
+ db->cur_rx++;
+ return 1;
+}
+
+static void dmfe_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void dmfe_transmit(struct nic *nic,
+ const char *dest, /* Destination */
+ unsigned int type, /* Type */
+ unsigned int size, /* size */
+ const char *packet) /* Packet */
+{
+ u16 nstype;
+ u8 *ptxb;
+
+ ptxb = &txb[db->cur_tx];
+
+ /* Stop Tx */
+ outl(0, BASE + DCR7);
+ memcpy(ptxb, dest, ETH_ALEN);
+ memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ nstype = htons((u16) type);
+ memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2);
+ memcpy(ptxb + ETH_HLEN, packet, size);
+
+ size += ETH_HLEN;
+ while (size < ETH_ZLEN)
+ ptxb[size++] = '\0';
+
+ /* setup the transmit descriptor */
+ txd[db->cur_tx].tdes1 = cpu_to_le32(0xe1000000 | size);
+ txd[db->cur_tx].tdes0 = cpu_to_le32(0x80000000); /* give ownership to device */
+
+ /* immediate transmit demand */
+ outl(0x1, BASE + DCR1);
+ outl(db->cr7_data, BASE + DCR7);
+
+ /* Point to next TX descriptor */
+ db->cur_tx++;
+ db->cur_tx = db->cur_tx % TX_DESC_CNT;
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void dmfe_disable(struct dev *dev __unused)
+{
+ /* Reset & stop DM910X board */
+ outl(DM910X_RESET, BASE + DCR0);
+ udelay(5);
+ phy_write(BASE, db->phy_addr, 0, 0x8000, db->chip_id);
+
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+
+#define board_found 1
+#define valid_link 0
+static int dmfe_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *) dev;
+ uint32_t dev_rev, pci_pmr;
+ int i;
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ BASE = pci->ioaddr;
+ printf("dmfe.c: Found %s Vendor=0x%hX Device=0x%hX\n",
+ pci->name, pci->vendor, pci->dev_id);
+
+ /* Read Chip revision */
+ pci_read_config_dword(pci, PCI_REVISION_ID, &dev_rev);
+ dprintf(("Revision %lX\n", dev_rev));
+
+ /* point to private storage */
+ db = &dfx;
+
+ db->chip_id = ((u32) pci->dev_id << 16) | pci->vendor;
+ BASE = pci_bar_start(pci, PCI_BASE_ADDRESS_0);
+ db->chip_revision = dev_rev;
+
+ pci_read_config_dword(pci, 0x50, &pci_pmr);
+ pci_pmr &= 0x70000;
+ if ((pci_pmr == 0x10000) && (dev_rev == 0x02000031))
+ db->chip_type = 1; /* DM9102A E3 */
+ else
+ db->chip_type = 0;
+
+ dprintf(("Chip type : %d\n", db->chip_type));
+
+ /* read 64 word srom data */
+ for (i = 0; i < 64; i++)
+ ((u16 *) db->srom)[i] = cpu_to_le16(read_srom_word(BASE, i));
+
+ /* Set Node address */
+ for (i = 0; i < 6; i++)
+ nic->node_addr[i] = db->srom[20 + i];
+
+ /* Print out some hardware info */
+ printf("%s: %! at ioaddr %hX\n", pci->name, nic->node_addr, BASE);
+
+ /* Set the card as PCI Bus Master */
+ adjust_pci_device(pci);
+
+ dmfe_reset(nic);
+
+ nic->irqno = 0;
+ nic->ioaddr = pci->ioaddr;
+
+ /* point to NIC specific routines */
+ dev->disable = dmfe_disable;
+ nic->poll = dmfe_poll;
+ nic->transmit = dmfe_transmit;
+ nic->irq = dmfe_irq;
+
+ return 1;
+}
+
+/*
+ * Initialize transmit/Receive descriptor
+ * Using Chain structure, and allocate Tx/Rx buffer
+ */
+
+static void dmfe_descriptor_init(struct nic *nic __unused, unsigned long ioaddr)
+{
+ int i;
+ db->cur_tx = 0;
+ db->cur_rx = 0;
+
+ /* tx descriptor start pointer */
+ outl(virt_to_le32desc(&txd[0]), ioaddr + DCR4); /* TX DESC address */
+
+ /* rx descriptor start pointer */
+ outl(virt_to_le32desc(&rxd[0]), ioaddr + DCR3); /* RX DESC address */
+
+ /* Init Transmit chain */
+ for (i = 0; i < TX_DESC_CNT; i++) {
+ txd[i].tx_buf_ptr = (u32) & txb[i];
+ txd[i].tdes0 = cpu_to_le32(0);
+ txd[i].tdes1 = cpu_to_le32(0x81000000); /* IC, chain */
+ txd[i].tdes2 = cpu_to_le32(virt_to_bus(&txb[i]));
+ txd[i].tdes3 = cpu_to_le32(virt_to_bus(&txd[i + 1]));
+ txd[i].next_tx_desc = cpu_to_le32(&txd[i + 1]);
+ }
+ /* Mark the last entry as wrapping the ring */
+ txd[i - 1].tdes3 = virt_to_le32desc(&txd[0]);
+ txd[i - 1].next_tx_desc = (u32) & txd[0];
+
+ /* receive descriptor chain */
+ for (i = 0; i < RX_DESC_CNT; i++) {
+ rxd[i].rx_skb_ptr = (u32) & rxb[i * RX_ALLOC_SIZE];
+ rxd[i].rdes0 = cpu_to_le32(0x80000000);
+ rxd[i].rdes1 = cpu_to_le32(0x01000600);
+ rxd[i].rdes2 =
+ cpu_to_le32(virt_to_bus(&rxb[i * RX_ALLOC_SIZE]));
+ rxd[i].rdes3 = cpu_to_le32(virt_to_bus(&rxd[i + 1]));
+ rxd[i].next_rx_desc = cpu_to_le32(&rxd[i + 1]);
+ }
+ /* Mark the last entry as wrapping the ring */
+ rxd[i - 1].rdes3 = cpu_to_le32(virt_to_bus(&rxd[0]));
+ rxd[i - 1].next_rx_desc = virt_to_le32desc(&rxd[0]);
+
+}
+
+/*
+ * Update CR6 value
+ * Firstly stop DM910X , then written value and start
+ */
+
+static void update_cr6(u32 cr6_data, unsigned long ioaddr)
+{
+ u32 cr6_tmp;
+
+ cr6_tmp = cr6_data & ~0x2002; /* stop Tx/Rx */
+ outl(cr6_tmp, ioaddr + DCR6);
+ udelay(5);
+ outl(cr6_data, ioaddr + DCR6);
+ udelay(5);
+}
+
+
+/*
+ * Send a setup frame for DM9132
+ * This setup frame initilize DM910X addres filter mode
+*/
+
+static void dm9132_id_table(struct nic *nic __unused)
+{
+#ifdef LINUX
+ u16 *addrptr;
+ u8 dmi_addr[8];
+ unsigned long ioaddr = BASE + 0xc0; /* ID Table */
+ u32 hash_val;
+ u16 i, hash_table[4];
+#endif
+ dprintf(("dm9132_id_table\n"));
+
+ printf("FIXME: This function is broken. If you have this card contact "
+ "Timothy Legge at the etherboot-user list\n");
+
+#ifdef LINUX
+ //DMFE_DBUG(0, "dm9132_id_table()", 0);
+
+ /* Node address */
+ addrptr = (u16 *) nic->node_addr;
+ outw(addrptr[0], ioaddr);
+ ioaddr += 4;
+ outw(addrptr[1], ioaddr);
+ ioaddr += 4;
+ outw(addrptr[2], ioaddr);
+ ioaddr += 4;
+
+ /* Clear Hash Table */
+ for (i = 0; i < 4; i++)
+ hash_table[i] = 0x0;
+
+ /* broadcast address */
+ hash_table[3] = 0x8000;
+
+ /* the multicast address in Hash Table : 64 bits */
+ for (mcptr = mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) {
+ hash_val = cal_CRC((char *) mcptr->dmi_addr, 6, 0) & 0x3f;
+ hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);
+ }
+
+ /* Write the hash table to MAC MD table */
+ for (i = 0; i < 4; i++, ioaddr += 4)
+ outw(hash_table[i], ioaddr);
+#endif
+}
+
+
+/*
+ * Send a setup frame for DM9102/DM9102A
+ * This setup frame initilize DM910X addres filter mode
+ */
+
+static void send_filter_frame(struct nic *nic)
+{
+
+ u8 *ptxb;
+ int i;
+
+ dprintf(("send_filter_frame\n"));
+ /* point to the current txb incase multiple tx_rings are used */
+ ptxb = &txb[db->cur_tx];
+
+ /* construct perfect filter frame with mac address as first match
+ and broadcast address for all others */
+ for (i = 0; i < 192; i++)
+ ptxb[i] = 0xFF;
+ ptxb[0] = nic->node_addr[0];
+ ptxb[1] = nic->node_addr[1];
+ ptxb[4] = nic->node_addr[2];
+ ptxb[5] = nic->node_addr[3];
+ ptxb[8] = nic->node_addr[4];
+ ptxb[9] = nic->node_addr[5];
+
+ /* prepare the setup frame */
+ txd[db->cur_tx].tdes1 = cpu_to_le32(0x890000c0);
+ txd[db->cur_tx].tdes0 = cpu_to_le32(0x80000000);
+ update_cr6(db->cr6_data | 0x2000, BASE);
+ outl(0x1, BASE + DCR1); /* Issue Tx polling */
+ update_cr6(db->cr6_data, BASE);
+ db->cur_tx++;
+}
+
+/*
+ * Read one word data from the serial ROM
+ */
+
+static u16 read_srom_word(long ioaddr, int offset)
+{
+ int i;
+ u16 srom_data = 0;
+ long cr9_ioaddr = ioaddr + DCR9;
+
+ outl(CR9_SROM_READ, cr9_ioaddr);
+ outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr);
+
+ /* Send the Read Command 110b */
+ SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr);
+ SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr);
+ SROM_CLK_WRITE(SROM_DATA_0, cr9_ioaddr);
+
+ /* Send the offset */
+ for (i = 5; i >= 0; i--) {
+ srom_data =
+ (offset & (1 << i)) ? SROM_DATA_1 : SROM_DATA_0;
+ SROM_CLK_WRITE(srom_data, cr9_ioaddr);
+ }
+
+ outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr);
+
+ for (i = 16; i > 0; i--) {
+ outl(CR9_SROM_READ | CR9_SRCS | CR9_SRCLK, cr9_ioaddr);
+ udelay(5);
+ srom_data =
+ (srom_data << 1) | ((inl(cr9_ioaddr) & CR9_CRDOUT) ? 1
+ : 0);
+ outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr);
+ udelay(5);
+ }
+
+ outl(CR9_SROM_READ, cr9_ioaddr);
+ return srom_data;
+}
+
+
+/*
+ * Auto sense the media mode
+ */
+
+static u8 dmfe_sense_speed(struct nic *nic __unused)
+{
+ u8 ErrFlag = 0;
+ u16 phy_mode;
+
+ /* CR6 bit18=0, select 10/100M */
+ update_cr6((db->cr6_data & ~0x40000), BASE);
+
+ phy_mode = phy_read(BASE, db->phy_addr, 1, db->chip_id);
+ phy_mode = phy_read(BASE, db->phy_addr, 1, db->chip_id);
+
+ if ((phy_mode & 0x24) == 0x24) {
+ if (db->chip_id == PCI_DM9132_ID) /* DM9132 */
+ phy_mode =
+ phy_read(BASE, db->phy_addr, 7,
+ db->chip_id) & 0xf000;
+ else /* DM9102/DM9102A */
+ phy_mode =
+ phy_read(BASE, db->phy_addr, 17,
+ db->chip_id) & 0xf000;
+ /* printk(DRV_NAME ": Phy_mode %x ",phy_mode); */
+ switch (phy_mode) {
+ case 0x1000:
+ db->op_mode = DMFE_10MHF;
+ break;
+ case 0x2000:
+ db->op_mode = DMFE_10MFD;
+ break;
+ case 0x4000:
+ db->op_mode = DMFE_100MHF;
+ break;
+ case 0x8000:
+ db->op_mode = DMFE_100MFD;
+ break;
+ default:
+ db->op_mode = DMFE_10MHF;
+ ErrFlag = 1;
+ break;
+ }
+ } else {
+ db->op_mode = DMFE_10MHF;
+ //DMFE_DBUG(0, "Link Failed :", phy_mode);
+ ErrFlag = 1;
+ }
+
+ return ErrFlag;
+}
+
+
+/*
+ * Set 10/100 phyxcer capability
+ * AUTO mode : phyxcer register4 is NIC capability
+ * Force mode: phyxcer register4 is the force media
+ */
+
+static void dmfe_set_phyxcer(struct nic *nic __unused)
+{
+ u16 phy_reg;
+
+ /* Select 10/100M phyxcer */
+ db->cr6_data &= ~0x40000;
+ update_cr6(db->cr6_data, BASE);
+
+ /* DM9009 Chip: Phyxcer reg18 bit12=0 */
+ if (db->chip_id == PCI_DM9009_ID) {
+ phy_reg =
+ phy_read(BASE, db->phy_addr, 18,
+ db->chip_id) & ~0x1000;
+ phy_write(BASE, db->phy_addr, 18, phy_reg, db->chip_id);
+ }
+
+ /* Phyxcer capability setting */
+ phy_reg = phy_read(BASE, db->phy_addr, 4, db->chip_id) & ~0x01e0;
+
+ if (db->media_mode & DMFE_AUTO) {
+ /* AUTO Mode */
+ phy_reg |= db->PHY_reg4;
+ } else {
+ /* Force Mode */
+ switch (db->media_mode) {
+ case DMFE_10MHF:
+ phy_reg |= 0x20;
+ break;
+ case DMFE_10MFD:
+ phy_reg |= 0x40;
+ break;
+ case DMFE_100MHF:
+ phy_reg |= 0x80;
+ break;
+ case DMFE_100MFD:
+ phy_reg |= 0x100;
+ break;
+ }
+ if (db->chip_id == PCI_DM9009_ID)
+ phy_reg &= 0x61;
+ }
+
+ /* Write new capability to Phyxcer Reg4 */
+ if (!(phy_reg & 0x01e0)) {
+ phy_reg |= db->PHY_reg4;
+ db->media_mode |= DMFE_AUTO;
+ }
+ phy_write(BASE, db->phy_addr, 4, phy_reg, db->chip_id);
+
+ /* Restart Auto-Negotiation */
+ if (db->chip_type && (db->chip_id == PCI_DM9102_ID))
+ phy_write(BASE, db->phy_addr, 0, 0x1800, db->chip_id);
+ if (!db->chip_type)
+ phy_write(BASE, db->phy_addr, 0, 0x1200, db->chip_id);
+}
+
+
+/*
+ * Process op-mode
+ * AUTO mode : PHY controller in Auto-negotiation Mode
+ * Force mode: PHY controller in force mode with HUB
+ * N-way force capability with SWITCH
+ */
+
+static void dmfe_process_mode(struct nic *nic __unused)
+{
+ u16 phy_reg;
+
+ /* Full Duplex Mode Check */
+ if (db->op_mode & 0x4)
+ db->cr6_data |= CR6_FDM; /* Set Full Duplex Bit */
+ else
+ db->cr6_data &= ~CR6_FDM; /* Clear Full Duplex Bit */
+
+ /* Transciver Selection */
+ if (db->op_mode & 0x10) /* 1M HomePNA */
+ db->cr6_data |= 0x40000; /* External MII select */
+ else
+ db->cr6_data &= ~0x40000; /* Internal 10/100 transciver */
+
+ update_cr6(db->cr6_data, BASE);
+
+ /* 10/100M phyxcer force mode need */
+ if (!(db->media_mode & 0x18)) {
+ /* Forece Mode */
+ phy_reg = phy_read(BASE, db->phy_addr, 6, db->chip_id);
+ if (!(phy_reg & 0x1)) {
+ /* parter without N-Way capability */
+ phy_reg = 0x0;
+ switch (db->op_mode) {
+ case DMFE_10MHF:
+ phy_reg = 0x0;
+ break;
+ case DMFE_10MFD:
+ phy_reg = 0x100;
+ break;
+ case DMFE_100MHF:
+ phy_reg = 0x2000;
+ break;
+ case DMFE_100MFD:
+ phy_reg = 0x2100;
+ break;
+ }
+ phy_write(BASE, db->phy_addr, 0, phy_reg,
+ db->chip_id);
+ if (db->chip_type
+ && (db->chip_id == PCI_DM9102_ID))
+ mdelay(20);
+ phy_write(BASE, db->phy_addr, 0, phy_reg,
+ db->chip_id);
+ }
+ }
+}
+
+
+/*
+ * Write a word to Phy register
+ */
+
+static void phy_write(unsigned long iobase, u8 phy_addr, u8 offset,
+ u16 phy_data, u32 chip_id)
+{
+ u16 i;
+ unsigned long ioaddr;
+
+ if (chip_id == PCI_DM9132_ID) {
+ ioaddr = iobase + 0x80 + offset * 4;
+ outw(phy_data, ioaddr);
+ } else {
+ /* DM9102/DM9102A Chip */
+ ioaddr = iobase + DCR9;
+
+ /* Send 33 synchronization clock to Phy controller */
+ for (i = 0; i < 35; i++)
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+
+ /* Send start command(01) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+
+ /* Send write command(01) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+
+ /* Send Phy addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr,
+ phy_addr & i ? PHY_DATA_1 :
+ PHY_DATA_0);
+
+ /* Send register addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr,
+ offset & i ? PHY_DATA_1 :
+ PHY_DATA_0);
+
+ /* written trasnition */
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+
+ /* Write a word data to PHY controller */
+ for (i = 0x8000; i > 0; i >>= 1)
+ phy_write_1bit(ioaddr,
+ phy_data & i ? PHY_DATA_1 :
+ PHY_DATA_0);
+ }
+}
+
+
+/*
+ * Read a word data from phy register
+ */
+
+static u16 phy_read(unsigned long iobase, u8 phy_addr, u8 offset,
+ u32 chip_id)
+{
+ int i;
+ u16 phy_data;
+ unsigned long ioaddr;
+
+ if (chip_id == PCI_DM9132_ID) {
+ /* DM9132 Chip */
+ ioaddr = iobase + 0x80 + offset * 4;
+ phy_data = inw(ioaddr);
+ } else {
+ /* DM9102/DM9102A Chip */
+ ioaddr = iobase + DCR9;
+
+ /* Send 33 synchronization clock to Phy controller */
+ for (i = 0; i < 35; i++)
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+
+ /* Send start command(01) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+
+ /* Send read command(10) to Phy */
+ phy_write_1bit(ioaddr, PHY_DATA_1);
+ phy_write_1bit(ioaddr, PHY_DATA_0);
+
+ /* Send Phy addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr,
+ phy_addr & i ? PHY_DATA_1 :
+ PHY_DATA_0);
+
+ /* Send register addres */
+ for (i = 0x10; i > 0; i = i >> 1)
+ phy_write_1bit(ioaddr,
+ offset & i ? PHY_DATA_1 :
+ PHY_DATA_0);
+
+ /* Skip transition state */
+ phy_read_1bit(ioaddr);
+
+ /* read 16bit data */
+ for (phy_data = 0, i = 0; i < 16; i++) {
+ phy_data <<= 1;
+ phy_data |= phy_read_1bit(ioaddr);
+ }
+ }
+
+ return phy_data;
+}
+
+
+/*
+ * Write one bit data to Phy Controller
+ */
+
+static void phy_write_1bit(unsigned long ioaddr, u32 phy_data)
+{
+ outl(phy_data, ioaddr); /* MII Clock Low */
+ udelay(1);
+ outl(phy_data | MDCLKH, ioaddr); /* MII Clock High */
+ udelay(1);
+ outl(phy_data, ioaddr); /* MII Clock Low */
+ udelay(1);
+}
+
+
+/*
+ * Read one bit phy data from PHY controller
+ */
+
+static u16 phy_read_1bit(unsigned long ioaddr)
+{
+ u16 phy_data;
+
+ outl(0x50000, ioaddr);
+ udelay(1);
+ phy_data = (inl(ioaddr) >> 19) & 0x1;
+ outl(0x40000, ioaddr);
+ udelay(1);
+
+ return phy_data;
+}
+
+
+/*
+ * Parser SROM and media mode
+ */
+
+static void dmfe_parse_srom(struct nic *nic)
+{
+ char *srom = db->srom;
+ int dmfe_mode, tmp_reg;
+
+ /* Init CR15 */
+ db->cr15_data = CR15_DEFAULT;
+
+ /* Check SROM Version */
+ if (((int) srom[18] & 0xff) == SROM_V41_CODE) {
+ /* SROM V4.01 */
+ /* Get NIC support media mode */
+ db->NIC_capability = *(u16 *) (srom + 34);
+ db->PHY_reg4 = 0;
+ for (tmp_reg = 1; tmp_reg < 0x10; tmp_reg <<= 1) {
+ switch (db->NIC_capability & tmp_reg) {
+ case 0x1:
+ db->PHY_reg4 |= 0x0020;
+ break;
+ case 0x2:
+ db->PHY_reg4 |= 0x0040;
+ break;
+ case 0x4:
+ db->PHY_reg4 |= 0x0080;
+ break;
+ case 0x8:
+ db->PHY_reg4 |= 0x0100;
+ break;
+ }
+ }
+
+ /* Media Mode Force or not check */
+ dmfe_mode = *((int *) srom + 34) & *((int *) srom + 36);
+ switch (dmfe_mode) {
+ case 0x4:
+ dmfe_media_mode = DMFE_100MHF;
+ break; /* 100MHF */
+ case 0x2:
+ dmfe_media_mode = DMFE_10MFD;
+ break; /* 10MFD */
+ case 0x8:
+ dmfe_media_mode = DMFE_100MFD;
+ break; /* 100MFD */
+ case 0x100:
+ case 0x200:
+ dmfe_media_mode = DMFE_1M_HPNA;
+ break; /* HomePNA */
+ }
+
+ /* Special Function setting */
+ /* VLAN function */
+ if ((SF_mode & 0x1) || (srom[43] & 0x80))
+ db->cr15_data |= 0x40;
+
+ /* Flow Control */
+ if ((SF_mode & 0x2) || (srom[40] & 0x1))
+ db->cr15_data |= 0x400;
+
+ /* TX pause packet */
+ if ((SF_mode & 0x4) || (srom[40] & 0xe))
+ db->cr15_data |= 0x9800;
+ }
+
+ /* Parse HPNA parameter */
+ db->HPNA_command = 1;
+
+ /* Accept remote command or not */
+ if (HPNA_rx_cmd == 0)
+ db->HPNA_command |= 0x8000;
+
+ /* Issue remote command & operation mode */
+ if (HPNA_tx_cmd == 1)
+ switch (HPNA_mode) { /* Issue Remote Command */
+ case 0:
+ db->HPNA_command |= 0x0904;
+ break;
+ case 1:
+ db->HPNA_command |= 0x0a00;
+ break;
+ case 2:
+ db->HPNA_command |= 0x0506;
+ break;
+ case 3:
+ db->HPNA_command |= 0x0602;
+ break;
+ } else
+ switch (HPNA_mode) { /* Don't Issue */
+ case 0:
+ db->HPNA_command |= 0x0004;
+ break;
+ case 1:
+ db->HPNA_command |= 0x0000;
+ break;
+ case 2:
+ db->HPNA_command |= 0x0006;
+ break;
+ case 3:
+ db->HPNA_command |= 0x0002;
+ break;
+ }
+
+ /* Check DM9801 or DM9802 present or not */
+ db->HPNA_present = 0;
+ update_cr6(db->cr6_data | 0x40000, BASE);
+ tmp_reg = phy_read(BASE, db->phy_addr, 3, db->chip_id);
+ if ((tmp_reg & 0xfff0) == 0xb900) {
+ /* DM9801 or DM9802 present */
+ db->HPNA_timer = 8;
+ if (phy_read(BASE, db->phy_addr, 31, db->chip_id) ==
+ 0x4404) {
+ /* DM9801 HomeRun */
+ db->HPNA_present = 1;
+ dmfe_program_DM9801(nic, tmp_reg);
+ } else {
+ /* DM9802 LongRun */
+ db->HPNA_present = 2;
+ dmfe_program_DM9802(nic);
+ }
+ }
+
+}
+
+/*
+ * Init HomeRun DM9801
+ */
+
+static void dmfe_program_DM9801(struct nic *nic __unused, int HPNA_rev)
+{
+ u32 reg17, reg25;
+
+ if (!HPNA_NoiseFloor)
+ HPNA_NoiseFloor = DM9801_NOISE_FLOOR;
+ switch (HPNA_rev) {
+ case 0xb900: /* DM9801 E3 */
+ db->HPNA_command |= 0x1000;
+ reg25 = phy_read(BASE, db->phy_addr, 24, db->chip_id);
+ reg25 = ((reg25 + HPNA_NoiseFloor) & 0xff) | 0xf000;
+ reg17 = phy_read(BASE, db->phy_addr, 17, db->chip_id);
+ break;
+ case 0xb901: /* DM9801 E4 */
+ reg25 = phy_read(BASE, db->phy_addr, 25, db->chip_id);
+ reg25 = (reg25 & 0xff00) + HPNA_NoiseFloor;
+ reg17 = phy_read(BASE, db->phy_addr, 17, db->chip_id);
+ reg17 = (reg17 & 0xfff0) + HPNA_NoiseFloor + 3;
+ break;
+ case 0xb902: /* DM9801 E5 */
+ case 0xb903: /* DM9801 E6 */
+ default:
+ db->HPNA_command |= 0x1000;
+ reg25 = phy_read(BASE, db->phy_addr, 25, db->chip_id);
+ reg25 = (reg25 & 0xff00) + HPNA_NoiseFloor - 5;
+ reg17 = phy_read(BASE, db->phy_addr, 17, db->chip_id);
+ reg17 = (reg17 & 0xfff0) + HPNA_NoiseFloor;
+ break;
+ }
+ phy_write(BASE, db->phy_addr, 16, db->HPNA_command, db->chip_id);
+ phy_write(BASE, db->phy_addr, 17, reg17, db->chip_id);
+ phy_write(BASE, db->phy_addr, 25, reg25, db->chip_id);
+}
+
+
+/*
+ * Init HomeRun DM9802
+ */
+
+static void dmfe_program_DM9802(struct nic *nic __unused)
+{
+ u32 phy_reg;
+
+ if (!HPNA_NoiseFloor)
+ HPNA_NoiseFloor = DM9802_NOISE_FLOOR;
+ phy_write(BASE, db->phy_addr, 16, db->HPNA_command, db->chip_id);
+ phy_reg = phy_read(BASE, db->phy_addr, 25, db->chip_id);
+ phy_reg = (phy_reg & 0xff00) + HPNA_NoiseFloor;
+ phy_write(BASE, db->phy_addr, 25, phy_reg, db->chip_id);
+}
+
+
+static struct pci_id dmfe_nics[] = {
+ PCI_ROM(0x1282, 0x9100, "dmfe9100", "Davicom 9100"),
+ PCI_ROM(0x1282, 0x9102, "dmfe9102", "Davicom 9102"),
+ PCI_ROM(0x1282, 0x9009, "dmfe9009", "Davicom 9009"),
+ PCI_ROM(0x1282, 0x9132, "dmfe9132", "Davicom 9132"), /* Needs probably some fixing */
+};
+
+static struct pci_driver dmfe_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "DMFE/PCI",
+ .probe = dmfe_probe,
+ .ids = dmfe_nics,
+ .id_count = sizeof(dmfe_nics) / sizeof(dmfe_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/e1000.c b/src/drivers/net/e1000.c
new file mode 100644
index 00000000..7c572656
--- /dev/null
+++ b/src/drivers/net/e1000.c
@@ -0,0 +1,3713 @@
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+Inter Pro 1000 for Etherboot
+Drivers are port from Intel's Linux driver e1000-4.3.15
+
+***************************************************************************/
+/*******************************************************************************
+
+
+ Copyright(c) 1999 - 2003 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+/*
+ * Copyright (C) Archway Digital Solutions.
+ *
+ * written by Chrsitopher Li <cli at arcyway dot com> or <chrisl at gnuchina dot org>
+ * 2/9/2002
+ *
+ * Copyright (C) Linux Networx.
+ * Massive upgrade to work with the new intel gigabit NICs.
+ * <ebiederman at lnxi dot com>
+ *
+ * Support for 82541ei & 82547ei chips from Intel's Linux driver 5.1.13 added by
+ * Georg Baum <gbaum@users.sf.net>, sponsored by PetaMem GmbH and linkLINE Communications, Inc.
+ *
+ * 01/2004: Updated to Linux driver 5.2.22 by Georg Baum <gbaum@users.sf.net>
+ */
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include "pci.h"
+#include "timer.h"
+
+typedef unsigned char *dma_addr_t;
+
+typedef enum {
+ FALSE = 0,
+ TRUE = 1
+} boolean_t;
+
+#define DEBUG 0
+
+
+/* Some pieces of code are disabled with #if 0 ... #endif.
+ * They are not deleted to show where the etherboot driver differs
+ * from the linux driver below the function level.
+ * Some member variables of the hw struct have been eliminated
+ * and the corresponding inplace checks inserted instead.
+ * Pieces such as LED handling that we definitely don't need are deleted.
+ *
+ * The following defines should not be needed normally,
+ * but may be helpful for debugging purposes. */
+
+/* Define this if you want to program the transmission control register
+ * the way the Linux driver does it. */
+#undef LINUX_DRIVER_TCTL
+
+/* Define this to behave more like the Linux driver. */
+#undef LINUX_DRIVER
+
+#include "e1000_hw.h"
+
+/* NIC specific static variables go here */
+static struct e1000_hw hw;
+static char tx_pool[128 + 16];
+static char rx_pool[128 + 16];
+static char packet[2096];
+
+static struct e1000_tx_desc *tx_base;
+static struct e1000_rx_desc *rx_base;
+
+static int tx_tail;
+static int rx_tail, rx_last;
+
+/* Function forward declarations */
+static int e1000_setup_link(struct e1000_hw *hw);
+static int e1000_setup_fiber_serdes_link(struct e1000_hw *hw);
+static int e1000_setup_copper_link(struct e1000_hw *hw);
+static int e1000_phy_setup_autoneg(struct e1000_hw *hw);
+static void e1000_config_collision_dist(struct e1000_hw *hw);
+static int e1000_config_mac_to_phy(struct e1000_hw *hw);
+static int e1000_config_fc_after_link_up(struct e1000_hw *hw);
+static int e1000_check_for_link(struct e1000_hw *hw);
+static int e1000_wait_autoneg(struct e1000_hw *hw);
+static void e1000_get_speed_and_duplex(struct e1000_hw *hw, uint16_t *speed, uint16_t *duplex);
+static int e1000_read_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *phy_data);
+static int e1000_read_phy_reg_ex(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *phy_data);
+static int e1000_write_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t phy_data);
+static int e1000_write_phy_reg_ex(struct e1000_hw *hw, uint32_t reg_addr, uint16_t phy_data);
+static void e1000_phy_hw_reset(struct e1000_hw *hw);
+static int e1000_phy_reset(struct e1000_hw *hw);
+static int e1000_detect_gig_phy(struct e1000_hw *hw);
+static void e1000_irq(struct nic *nic, irq_action_t action);
+
+/* Printing macros... */
+
+#define E1000_ERR(args...) printf("e1000: " args)
+
+#if DEBUG >= 3
+#define E1000_DBG(args...) printf("e1000: " args)
+#else
+#define E1000_DBG(args...)
+#endif
+
+#define MSGOUT(S, A, B) printk(S "\n", A, B)
+#if DEBUG >= 2
+#define DEBUGFUNC(F) DEBUGOUT(F "\n");
+#else
+#define DEBUGFUNC(F)
+#endif
+#if DEBUG >= 1
+#define DEBUGOUT(S) printf(S)
+#define DEBUGOUT1(S,A) printf(S,A)
+#define DEBUGOUT2(S,A,B) printf(S,A,B)
+#define DEBUGOUT3(S,A,B,C) printf(S,A,B,C)
+#define DEBUGOUT7(S,A,B,C,D,E,F,G) printf(S,A,B,C,D,E,F,G)
+#else
+#define DEBUGOUT(S)
+#define DEBUGOUT1(S,A)
+#define DEBUGOUT2(S,A,B)
+#define DEBUGOUT3(S,A,B,C)
+#define DEBUGOUT7(S,A,B,C,D,E,F,G)
+#endif
+
+#define E1000_WRITE_REG(a, reg, value) ( \
+ ((a)->mac_type >= e1000_82543) ? \
+ (writel((value), ((a)->hw_addr + E1000_##reg))) : \
+ (writel((value), ((a)->hw_addr + E1000_82542_##reg))))
+
+#define E1000_READ_REG(a, reg) ( \
+ ((a)->mac_type >= e1000_82543) ? \
+ readl((a)->hw_addr + E1000_##reg) : \
+ readl((a)->hw_addr + E1000_82542_##reg))
+
+#define E1000_WRITE_REG_ARRAY(a, reg, offset, value) ( \
+ ((a)->mac_type >= e1000_82543) ? \
+ writel((value), ((a)->hw_addr + E1000_##reg + ((offset) << 2))) : \
+ writel((value), ((a)->hw_addr + E1000_82542_##reg + ((offset) << 2))))
+
+#define E1000_READ_REG_ARRAY(a, reg, offset) ( \
+ ((a)->mac_type >= e1000_82543) ? \
+ readl((a)->hw_addr + E1000_##reg + ((offset) << 2)) : \
+ readl((a)->hw_addr + E1000_82542_##reg + ((offset) << 2)))
+
+#define E1000_WRITE_FLUSH(a) {uint32_t x; x = E1000_READ_REG(a, STATUS);}
+
+uint32_t
+e1000_io_read(struct e1000_hw *hw __unused, uint32_t port)
+{
+ return inl(port);
+}
+
+void
+e1000_io_write(struct e1000_hw *hw __unused, uint32_t port, uint32_t value)
+{
+ outl(value, port);
+}
+
+static inline void e1000_pci_set_mwi(struct e1000_hw *hw)
+{
+ pci_write_config_word(hw->pdev, PCI_COMMAND, hw->pci_cmd_word);
+}
+
+static inline void e1000_pci_clear_mwi(struct e1000_hw *hw)
+{
+ pci_write_config_word(hw->pdev, PCI_COMMAND,
+ hw->pci_cmd_word & ~PCI_COMMAND_INVALIDATE);
+}
+
+/******************************************************************************
+ * Raises the EEPROM's clock input.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * eecd - EECD's current value
+ *****************************************************************************/
+static void
+e1000_raise_ee_clk(struct e1000_hw *hw,
+ uint32_t *eecd)
+{
+ /* Raise the clock input to the EEPROM (by setting the SK bit), and then
+ * wait <delay> microseconds.
+ */
+ *eecd = *eecd | E1000_EECD_SK;
+ E1000_WRITE_REG(hw, EECD, *eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(hw->eeprom.delay_usec);
+}
+
+/******************************************************************************
+ * Lowers the EEPROM's clock input.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * eecd - EECD's current value
+ *****************************************************************************/
+static void
+e1000_lower_ee_clk(struct e1000_hw *hw,
+ uint32_t *eecd)
+{
+ /* Lower the clock input to the EEPROM (by clearing the SK bit), and then
+ * wait 50 microseconds.
+ */
+ *eecd = *eecd & ~E1000_EECD_SK;
+ E1000_WRITE_REG(hw, EECD, *eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(hw->eeprom.delay_usec);
+}
+
+/******************************************************************************
+ * Shift data bits out to the EEPROM.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * data - data to send to the EEPROM
+ * count - number of bits to shift out
+ *****************************************************************************/
+static void
+e1000_shift_out_ee_bits(struct e1000_hw *hw,
+ uint16_t data,
+ uint16_t count)
+{
+ struct e1000_eeprom_info *eeprom = &hw->eeprom;
+ uint32_t eecd;
+ uint32_t mask;
+
+ /* We need to shift "count" bits out to the EEPROM. So, value in the
+ * "data" parameter will be shifted out to the EEPROM one bit at a time.
+ * In order to do this, "data" must be broken down into bits.
+ */
+ mask = 0x01 << (count - 1);
+ eecd = E1000_READ_REG(hw, EECD);
+ if (eeprom->type == e1000_eeprom_microwire) {
+ eecd &= ~E1000_EECD_DO;
+ } else if (eeprom->type == e1000_eeprom_spi) {
+ eecd |= E1000_EECD_DO;
+ }
+ do {
+ /* A "1" is shifted out to the EEPROM by setting bit "DI" to a "1",
+ * and then raising and then lowering the clock (the SK bit controls
+ * the clock input to the EEPROM). A "0" is shifted out to the EEPROM
+ * by setting "DI" to "0" and then raising and then lowering the clock.
+ */
+ eecd &= ~E1000_EECD_DI;
+
+ if(data & mask)
+ eecd |= E1000_EECD_DI;
+
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+
+ udelay(eeprom->delay_usec);
+
+ e1000_raise_ee_clk(hw, &eecd);
+ e1000_lower_ee_clk(hw, &eecd);
+
+ mask = mask >> 1;
+
+ } while(mask);
+
+ /* We leave the "DI" bit set to "0" when we leave this routine. */
+ eecd &= ~E1000_EECD_DI;
+ E1000_WRITE_REG(hw, EECD, eecd);
+}
+
+/******************************************************************************
+ * Shift data bits in from the EEPROM
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static uint16_t
+e1000_shift_in_ee_bits(struct e1000_hw *hw,
+ uint16_t count)
+{
+ uint32_t eecd;
+ uint32_t i;
+ uint16_t data;
+
+ /* In order to read a register from the EEPROM, we need to shift 'count'
+ * bits in from the EEPROM. Bits are "shifted in" by raising the clock
+ * input to the EEPROM (setting the SK bit), and then reading the value of
+ * the "DO" bit. During this "shifting in" process the "DI" bit should
+ * always be clear.
+ */
+
+ eecd = E1000_READ_REG(hw, EECD);
+
+ eecd &= ~(E1000_EECD_DO | E1000_EECD_DI);
+ data = 0;
+
+ for(i = 0; i < count; i++) {
+ data = data << 1;
+ e1000_raise_ee_clk(hw, &eecd);
+
+ eecd = E1000_READ_REG(hw, EECD);
+
+ eecd &= ~(E1000_EECD_DI);
+ if(eecd & E1000_EECD_DO)
+ data |= 1;
+
+ e1000_lower_ee_clk(hw, &eecd);
+ }
+
+ return data;
+}
+
+/******************************************************************************
+ * Prepares EEPROM for access
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Lowers EEPROM clock. Clears input pin. Sets the chip select pin. This
+ * function should be called before issuing a command to the EEPROM.
+ *****************************************************************************/
+static int32_t
+e1000_acquire_eeprom(struct e1000_hw *hw)
+{
+ struct e1000_eeprom_info *eeprom = &hw->eeprom;
+ uint32_t eecd, i=0;
+
+ eecd = E1000_READ_REG(hw, EECD);
+
+ /* Request EEPROM Access */
+ if(hw->mac_type > e1000_82544) {
+ eecd |= E1000_EECD_REQ;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ eecd = E1000_READ_REG(hw, EECD);
+ while((!(eecd & E1000_EECD_GNT)) &&
+ (i < E1000_EEPROM_GRANT_ATTEMPTS)) {
+ i++;
+ udelay(5);
+ eecd = E1000_READ_REG(hw, EECD);
+ }
+ if(!(eecd & E1000_EECD_GNT)) {
+ eecd &= ~E1000_EECD_REQ;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ DEBUGOUT("Could not acquire EEPROM grant\n");
+ return -E1000_ERR_EEPROM;
+ }
+ }
+
+ /* Setup EEPROM for Read/Write */
+
+ if (eeprom->type == e1000_eeprom_microwire) {
+ /* Clear SK and DI */
+ eecd &= ~(E1000_EECD_DI | E1000_EECD_SK);
+ E1000_WRITE_REG(hw, EECD, eecd);
+
+ /* Set CS */
+ eecd |= E1000_EECD_CS;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ } else if (eeprom->type == e1000_eeprom_spi) {
+ /* Clear SK and CS */
+ eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
+ E1000_WRITE_REG(hw, EECD, eecd);
+ udelay(1);
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Returns EEPROM to a "standby" state
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static void
+e1000_standby_eeprom(struct e1000_hw *hw)
+{
+ struct e1000_eeprom_info *eeprom = &hw->eeprom;
+ uint32_t eecd;
+
+ eecd = E1000_READ_REG(hw, EECD);
+
+ if(eeprom->type == e1000_eeprom_microwire) {
+
+ /* Deselect EEPROM */
+ eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(eeprom->delay_usec);
+
+ /* Clock high */
+ eecd |= E1000_EECD_SK;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(eeprom->delay_usec);
+
+ /* Select EEPROM */
+ eecd |= E1000_EECD_CS;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(eeprom->delay_usec);
+
+ /* Clock low */
+ eecd &= ~E1000_EECD_SK;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(eeprom->delay_usec);
+ } else if(eeprom->type == e1000_eeprom_spi) {
+ /* Toggle CS to flush commands */
+ eecd |= E1000_EECD_CS;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(eeprom->delay_usec);
+ eecd &= ~E1000_EECD_CS;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(eeprom->delay_usec);
+ }
+}
+
+/******************************************************************************
+ * Terminates a command by inverting the EEPROM's chip select pin
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static void
+e1000_release_eeprom(struct e1000_hw *hw)
+{
+ uint32_t eecd;
+
+ eecd = E1000_READ_REG(hw, EECD);
+
+ if (hw->eeprom.type == e1000_eeprom_spi) {
+ eecd |= E1000_EECD_CS; /* Pull CS high */
+ eecd &= ~E1000_EECD_SK; /* Lower SCK */
+
+ E1000_WRITE_REG(hw, EECD, eecd);
+
+ udelay(hw->eeprom.delay_usec);
+ } else if(hw->eeprom.type == e1000_eeprom_microwire) {
+ /* cleanup eeprom */
+
+ /* CS on Microwire is active-high */
+ eecd &= ~(E1000_EECD_CS | E1000_EECD_DI);
+
+ E1000_WRITE_REG(hw, EECD, eecd);
+
+ /* Rising edge of clock */
+ eecd |= E1000_EECD_SK;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(hw->eeprom.delay_usec);
+
+ /* Falling edge of clock */
+ eecd &= ~E1000_EECD_SK;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ E1000_WRITE_FLUSH(hw);
+ udelay(hw->eeprom.delay_usec);
+ }
+
+ /* Stop requesting EEPROM access */
+ if(hw->mac_type > e1000_82544) {
+ eecd &= ~E1000_EECD_REQ;
+ E1000_WRITE_REG(hw, EECD, eecd);
+ }
+}
+
+/******************************************************************************
+ * Reads a 16 bit word from the EEPROM.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static int32_t
+e1000_spi_eeprom_ready(struct e1000_hw *hw)
+{
+ uint16_t retry_count = 0;
+ uint8_t spi_stat_reg;
+
+ /* Read "Status Register" repeatedly until the LSB is cleared. The
+ * EEPROM will signal that the command has been completed by clearing
+ * bit 0 of the internal status register. If it's not cleared within
+ * 5 milliseconds, then error out.
+ */
+ retry_count = 0;
+ do {
+ e1000_shift_out_ee_bits(hw, EEPROM_RDSR_OPCODE_SPI,
+ hw->eeprom.opcode_bits);
+ spi_stat_reg = (uint8_t)e1000_shift_in_ee_bits(hw, 8);
+ if (!(spi_stat_reg & EEPROM_STATUS_RDY_SPI))
+ break;
+
+ udelay(5);
+ retry_count += 5;
+
+ } while(retry_count < EEPROM_MAX_RETRY_SPI);
+
+ /* ATMEL SPI write time could vary from 0-20mSec on 3.3V devices (and
+ * only 0-5mSec on 5V devices)
+ */
+ if(retry_count >= EEPROM_MAX_RETRY_SPI) {
+ DEBUGOUT("SPI EEPROM Status error\n");
+ return -E1000_ERR_EEPROM;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Reads a 16 bit word from the EEPROM.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * offset - offset of word in the EEPROM to read
+ * data - word read from the EEPROM
+ * words - number of words to read
+ *****************************************************************************/
+static int
+e1000_read_eeprom(struct e1000_hw *hw,
+ uint16_t offset,
+ uint16_t words,
+ uint16_t *data)
+{
+ struct e1000_eeprom_info *eeprom = &hw->eeprom;
+ uint32_t i = 0;
+
+ DEBUGFUNC("e1000_read_eeprom");
+
+ /* A check for invalid values: offset too large, too many words, and not
+ * enough words.
+ */
+ if((offset > eeprom->word_size) || (words > eeprom->word_size - offset) ||
+ (words == 0)) {
+ DEBUGOUT("\"words\" parameter out of bounds\n");
+ return -E1000_ERR_EEPROM;
+ }
+
+ /* Prepare the EEPROM for reading */
+ if(e1000_acquire_eeprom(hw) != E1000_SUCCESS)
+ return -E1000_ERR_EEPROM;
+
+ if(eeprom->type == e1000_eeprom_spi) {
+ uint16_t word_in;
+ uint8_t read_opcode = EEPROM_READ_OPCODE_SPI;
+
+ if(e1000_spi_eeprom_ready(hw)) {
+ e1000_release_eeprom(hw);
+ return -E1000_ERR_EEPROM;
+ }
+
+ e1000_standby_eeprom(hw);
+
+ /* Some SPI eeproms use the 8th address bit embedded in the opcode */
+ if((eeprom->address_bits == 8) && (offset >= 128))
+ read_opcode |= EEPROM_A8_OPCODE_SPI;
+
+ /* Send the READ command (opcode + addr) */
+ e1000_shift_out_ee_bits(hw, read_opcode, eeprom->opcode_bits);
+ e1000_shift_out_ee_bits(hw, (uint16_t)(offset*2), eeprom->address_bits);
+
+ /* Read the data. The address of the eeprom internally increments with
+ * each byte (spi) being read, saving on the overhead of eeprom setup
+ * and tear-down. The address counter will roll over if reading beyond
+ * the size of the eeprom, thus allowing the entire memory to be read
+ * starting from any offset. */
+ for (i = 0; i < words; i++) {
+ word_in = e1000_shift_in_ee_bits(hw, 16);
+ data[i] = (word_in >> 8) | (word_in << 8);
+ }
+ } else if(eeprom->type == e1000_eeprom_microwire) {
+ for (i = 0; i < words; i++) {
+ /* Send the READ command (opcode + addr) */
+ e1000_shift_out_ee_bits(hw, EEPROM_READ_OPCODE_MICROWIRE,
+ eeprom->opcode_bits);
+ e1000_shift_out_ee_bits(hw, (uint16_t)(offset + i),
+ eeprom->address_bits);
+
+ /* Read the data. For microwire, each word requires the overhead
+ * of eeprom setup and tear-down. */
+ data[i] = e1000_shift_in_ee_bits(hw, 16);
+ e1000_standby_eeprom(hw);
+ }
+ }
+
+ /* End this read operation */
+ e1000_release_eeprom(hw);
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Verifies that the EEPROM has a valid checksum
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Reads the first 64 16 bit words of the EEPROM and sums the values read.
+ * If the the sum of the 64 16 bit words is 0xBABA, the EEPROM's checksum is
+ * valid.
+ *****************************************************************************/
+static int
+e1000_validate_eeprom_checksum(struct e1000_hw *hw)
+{
+ uint16_t checksum = 0;
+ uint16_t i, eeprom_data;
+
+ DEBUGFUNC("e1000_validate_eeprom_checksum");
+
+ for(i = 0; i < (EEPROM_CHECKSUM_REG + 1); i++) {
+ if(e1000_read_eeprom(hw, i, 1, &eeprom_data) < 0) {
+ DEBUGOUT("EEPROM Read Error\n");
+ return -E1000_ERR_EEPROM;
+ }
+ checksum += eeprom_data;
+ }
+
+ if(checksum == (uint16_t) EEPROM_SUM)
+ return E1000_SUCCESS;
+ else {
+ DEBUGOUT("EEPROM Checksum Invalid\n");
+ return -E1000_ERR_EEPROM;
+ }
+}
+
+/******************************************************************************
+ * Reads the adapter's MAC address from the EEPROM and inverts the LSB for the
+ * second function of dual function devices
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static int
+e1000_read_mac_addr(struct e1000_hw *hw)
+{
+ uint16_t offset;
+ uint16_t eeprom_data;
+ int i;
+
+ DEBUGFUNC("e1000_read_mac_addr");
+
+ for(i = 0; i < NODE_ADDRESS_SIZE; i += 2) {
+ offset = i >> 1;
+ if(e1000_read_eeprom(hw, offset, 1, &eeprom_data) < 0) {
+ DEBUGOUT("EEPROM Read Error\n");
+ return -E1000_ERR_EEPROM;
+ }
+ hw->mac_addr[i] = eeprom_data & 0xff;
+ hw->mac_addr[i+1] = (eeprom_data >> 8) & 0xff;
+ }
+ if(((hw->mac_type == e1000_82546) || (hw->mac_type == e1000_82546_rev_3)) &&
+ (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1))
+ /* Invert the last bit if this is the second device */
+ hw->mac_addr[5] ^= 1;
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Initializes receive address filters.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Places the MAC address in receive address register 0 and clears the rest
+ * of the receive addresss registers. Clears the multicast table. Assumes
+ * the receiver is in reset when the routine is called.
+ *****************************************************************************/
+static void
+e1000_init_rx_addrs(struct e1000_hw *hw)
+{
+ uint32_t i;
+ uint32_t addr_low;
+ uint32_t addr_high;
+
+ DEBUGFUNC("e1000_init_rx_addrs");
+
+ /* Setup the receive address. */
+ DEBUGOUT("Programming MAC Address into RAR[0]\n");
+ addr_low = (hw->mac_addr[0] |
+ (hw->mac_addr[1] << 8) |
+ (hw->mac_addr[2] << 16) | (hw->mac_addr[3] << 24));
+
+ addr_high = (hw->mac_addr[4] |
+ (hw->mac_addr[5] << 8) | E1000_RAH_AV);
+
+ E1000_WRITE_REG_ARRAY(hw, RA, 0, addr_low);
+ E1000_WRITE_REG_ARRAY(hw, RA, 1, addr_high);
+
+ /* Zero out the other 15 receive addresses. */
+ DEBUGOUT("Clearing RAR[1-15]\n");
+ for(i = 1; i < E1000_RAR_ENTRIES; i++) {
+ E1000_WRITE_REG_ARRAY(hw, RA, (i << 1), 0);
+ E1000_WRITE_REG_ARRAY(hw, RA, ((i << 1) + 1), 0);
+ }
+}
+
+/******************************************************************************
+ * Clears the VLAN filer table
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static void
+e1000_clear_vfta(struct e1000_hw *hw)
+{
+ uint32_t offset;
+
+ for(offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++)
+ E1000_WRITE_REG_ARRAY(hw, VFTA, offset, 0);
+}
+
+/******************************************************************************
+* Writes a value to one of the devices registers using port I/O (as opposed to
+* memory mapped I/O). Only 82544 and newer devices support port I/O. *
+* hw - Struct containing variables accessed by shared code
+* offset - offset to write to * value - value to write
+*****************************************************************************/
+void e1000_write_reg_io(struct e1000_hw *hw, uint32_t offset, uint32_t value){
+ uint32_t io_addr = hw->io_base;
+ uint32_t io_data = hw->io_base + 4;
+ e1000_io_write(hw, io_addr, offset);
+ e1000_io_write(hw, io_data, value);
+}
+
+/******************************************************************************
+ * Set the phy type member in the hw struct.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static int32_t
+e1000_set_phy_type(struct e1000_hw *hw)
+{
+ DEBUGFUNC("e1000_set_phy_type");
+
+ switch(hw->phy_id) {
+ case M88E1000_E_PHY_ID:
+ case M88E1000_I_PHY_ID:
+ case M88E1011_I_PHY_ID:
+ hw->phy_type = e1000_phy_m88;
+ break;
+ case IGP01E1000_I_PHY_ID:
+ hw->phy_type = e1000_phy_igp;
+ break;
+ default:
+ /* Should never have loaded on this device */
+ hw->phy_type = e1000_phy_undefined;
+ return -E1000_ERR_PHY_TYPE;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * IGP phy init script - initializes the GbE PHY
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static void
+e1000_phy_init_script(struct e1000_hw *hw)
+{
+ DEBUGFUNC("e1000_phy_init_script");
+
+#if 0
+ /* See e1000_sw_init() of the Linux driver */
+ if(hw->phy_init_script) {
+#else
+ if((hw->mac_type == e1000_82541) ||
+ (hw->mac_type == e1000_82547) ||
+ (hw->mac_type == e1000_82541_rev_2) ||
+ (hw->mac_type == e1000_82547_rev_2)) {
+#endif
+ mdelay(20);
+
+ e1000_write_phy_reg(hw,0x0000,0x0140);
+
+ mdelay(5);
+
+ if(hw->mac_type == e1000_82541 || hw->mac_type == e1000_82547) {
+ e1000_write_phy_reg(hw, 0x1F95, 0x0001);
+
+ e1000_write_phy_reg(hw, 0x1F71, 0xBD21);
+
+ e1000_write_phy_reg(hw, 0x1F79, 0x0018);
+
+ e1000_write_phy_reg(hw, 0x1F30, 0x1600);
+
+ e1000_write_phy_reg(hw, 0x1F31, 0x0014);
+
+ e1000_write_phy_reg(hw, 0x1F32, 0x161C);
+
+ e1000_write_phy_reg(hw, 0x1F94, 0x0003);
+
+ e1000_write_phy_reg(hw, 0x1F96, 0x003F);
+
+ e1000_write_phy_reg(hw, 0x2010, 0x0008);
+ } else {
+ e1000_write_phy_reg(hw, 0x1F73, 0x0099);
+ }
+
+ e1000_write_phy_reg(hw, 0x0000, 0x3300);
+
+
+ if(hw->mac_type == e1000_82547) {
+ uint16_t fused, fine, coarse;
+
+ /* Move to analog registers page */
+ e1000_read_phy_reg(hw, IGP01E1000_ANALOG_SPARE_FUSE_STATUS, &fused);
+
+ if(!(fused & IGP01E1000_ANALOG_SPARE_FUSE_ENABLED)) {
+ e1000_read_phy_reg(hw, IGP01E1000_ANALOG_FUSE_STATUS, &fused);
+
+ fine = fused & IGP01E1000_ANALOG_FUSE_FINE_MASK;
+ coarse = fused & IGP01E1000_ANALOG_FUSE_COARSE_MASK;
+
+ if(coarse > IGP01E1000_ANALOG_FUSE_COARSE_THRESH) {
+ coarse -= IGP01E1000_ANALOG_FUSE_COARSE_10;
+ fine -= IGP01E1000_ANALOG_FUSE_FINE_1;
+ } else if(coarse == IGP01E1000_ANALOG_FUSE_COARSE_THRESH)
+ fine -= IGP01E1000_ANALOG_FUSE_FINE_10;
+
+ fused = (fused & IGP01E1000_ANALOG_FUSE_POLY_MASK) |
+ (fine & IGP01E1000_ANALOG_FUSE_FINE_MASK) |
+ (coarse & IGP01E1000_ANALOG_FUSE_COARSE_MASK);
+
+ e1000_write_phy_reg(hw, IGP01E1000_ANALOG_FUSE_CONTROL, fused);
+ e1000_write_phy_reg(hw, IGP01E1000_ANALOG_FUSE_BYPASS,
+ IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL);
+ }
+ }
+ }
+}
+
+/******************************************************************************
+ * Set the mac type member in the hw struct.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static int
+e1000_set_mac_type(struct e1000_hw *hw)
+{
+ DEBUGFUNC("e1000_set_mac_type");
+
+ switch (hw->device_id) {
+ case E1000_DEV_ID_82542:
+ switch (hw->revision_id) {
+ case E1000_82542_2_0_REV_ID:
+ hw->mac_type = e1000_82542_rev2_0;
+ break;
+ case E1000_82542_2_1_REV_ID:
+ hw->mac_type = e1000_82542_rev2_1;
+ break;
+ default:
+ /* Invalid 82542 revision ID */
+ return -E1000_ERR_MAC_TYPE;
+ }
+ break;
+ case E1000_DEV_ID_82543GC_FIBER:
+ case E1000_DEV_ID_82543GC_COPPER:
+ hw->mac_type = e1000_82543;
+ break;
+ case E1000_DEV_ID_82544EI_COPPER:
+ case E1000_DEV_ID_82544EI_FIBER:
+ case E1000_DEV_ID_82544GC_COPPER:
+ case E1000_DEV_ID_82544GC_LOM:
+ hw->mac_type = e1000_82544;
+ break;
+ case E1000_DEV_ID_82540EM:
+ case E1000_DEV_ID_82540EM_LOM:
+ case E1000_DEV_ID_82540EP:
+ case E1000_DEV_ID_82540EP_LOM:
+ case E1000_DEV_ID_82540EP_LP:
+ hw->mac_type = e1000_82540;
+ break;
+ case E1000_DEV_ID_82545EM_COPPER:
+ case E1000_DEV_ID_82545EM_FIBER:
+ hw->mac_type = e1000_82545;
+ break;
+ case E1000_DEV_ID_82545GM_COPPER:
+ case E1000_DEV_ID_82545GM_FIBER:
+ case E1000_DEV_ID_82545GM_SERDES:
+ hw->mac_type = e1000_82545_rev_3;
+ break;
+ case E1000_DEV_ID_82546EB_COPPER:
+ case E1000_DEV_ID_82546EB_FIBER:
+ case E1000_DEV_ID_82546EB_QUAD_COPPER:
+ hw->mac_type = e1000_82546;
+ break;
+ case E1000_DEV_ID_82546GB_COPPER:
+ case E1000_DEV_ID_82546GB_FIBER:
+ case E1000_DEV_ID_82546GB_SERDES:
+ hw->mac_type = e1000_82546_rev_3;
+ break;
+ case E1000_DEV_ID_82541EI:
+ case E1000_DEV_ID_82541EI_MOBILE:
+ hw->mac_type = e1000_82541;
+ break;
+ case E1000_DEV_ID_82541ER:
+ case E1000_DEV_ID_82541GI:
+ case E1000_DEV_ID_82541GI_MOBILE:
+ hw->mac_type = e1000_82541_rev_2;
+ break;
+ case E1000_DEV_ID_82547EI:
+ hw->mac_type = e1000_82547;
+ break;
+ case E1000_DEV_ID_82547GI:
+ hw->mac_type = e1000_82547_rev_2;
+ break;
+ default:
+ /* Should never have loaded on this device */
+ return -E1000_ERR_MAC_TYPE;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/*****************************************************************************
+ * Set media type and TBI compatibility.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * **************************************************************************/
+static void
+e1000_set_media_type(struct e1000_hw *hw)
+{
+ uint32_t status;
+
+ DEBUGFUNC("e1000_set_media_type");
+
+ if(hw->mac_type != e1000_82543) {
+ /* tbi_compatibility is only valid on 82543 */
+ hw->tbi_compatibility_en = FALSE;
+ }
+
+ switch (hw->device_id) {
+ case E1000_DEV_ID_82545GM_SERDES:
+ case E1000_DEV_ID_82546GB_SERDES:
+ hw->media_type = e1000_media_type_internal_serdes;
+ break;
+ default:
+ if(hw->mac_type >= e1000_82543) {
+ status = E1000_READ_REG(hw, STATUS);
+ if(status & E1000_STATUS_TBIMODE) {
+ hw->media_type = e1000_media_type_fiber;
+ /* tbi_compatibility not valid on fiber */
+ hw->tbi_compatibility_en = FALSE;
+ } else {
+ hw->media_type = e1000_media_type_copper;
+ }
+ } else {
+ /* This is an 82542 (fiber only) */
+ hw->media_type = e1000_media_type_fiber;
+ }
+ }
+}
+
+/******************************************************************************
+ * Reset the transmit and receive units; mask and clear all interrupts.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static void
+e1000_reset_hw(struct e1000_hw *hw)
+{
+ uint32_t ctrl;
+ uint32_t ctrl_ext;
+ uint32_t icr;
+ uint32_t manc;
+
+ DEBUGFUNC("e1000_reset_hw");
+
+ /* For 82542 (rev 2.0), disable MWI before issuing a device reset */
+ if(hw->mac_type == e1000_82542_rev2_0) {
+ DEBUGOUT("Disabling MWI on 82542 rev 2.0\n");
+ e1000_pci_clear_mwi(hw);
+ }
+
+ /* Clear interrupt mask to stop board from generating interrupts */
+ DEBUGOUT("Masking off all interrupts\n");
+ E1000_WRITE_REG(hw, IMC, 0xffffffff);
+
+ /* Disable the Transmit and Receive units. Then delay to allow
+ * any pending transactions to complete before we hit the MAC with
+ * the global reset.
+ */
+ E1000_WRITE_REG(hw, RCTL, 0);
+ E1000_WRITE_REG(hw, TCTL, E1000_TCTL_PSP);
+ E1000_WRITE_FLUSH(hw);
+
+ /* The tbi_compatibility_on Flag must be cleared when Rctl is cleared. */
+ hw->tbi_compatibility_on = FALSE;
+
+ /* Delay to allow any outstanding PCI transactions to complete before
+ * resetting the device
+ */
+ mdelay(10);
+
+ ctrl = E1000_READ_REG(hw, CTRL);
+
+ /* Must reset the PHY before resetting the MAC */
+ if((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) {
+ E1000_WRITE_REG_IO(hw, CTRL, (ctrl | E1000_CTRL_PHY_RST));
+ mdelay(5);
+ }
+
+ /* Issue a global reset to the MAC. This will reset the chip's
+ * transmit, receive, DMA, and link units. It will not effect
+ * the current PCI configuration. The global reset bit is self-
+ * clearing, and should clear within a microsecond.
+ */
+ DEBUGOUT("Issuing a global reset to MAC\n");
+
+ switch(hw->mac_type) {
+ case e1000_82544:
+ case e1000_82540:
+ case e1000_82545:
+ case e1000_82546:
+ case e1000_82541:
+ case e1000_82541_rev_2:
+ /* These controllers can't ack the 64-bit write when issuing the
+ * reset, so use IO-mapping as a workaround to issue the reset */
+ E1000_WRITE_REG_IO(hw, CTRL, (ctrl | E1000_CTRL_RST));
+ break;
+ case e1000_82545_rev_3:
+ case e1000_82546_rev_3:
+ /* Reset is performed on a shadow of the control register */
+ E1000_WRITE_REG(hw, CTRL_DUP, (ctrl | E1000_CTRL_RST));
+ break;
+ default:
+ E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_RST));
+ break;
+ }
+
+ /* After MAC reset, force reload of EEPROM to restore power-on settings to
+ * device. Later controllers reload the EEPROM automatically, so just wait
+ * for reload to complete.
+ */
+ switch(hw->mac_type) {
+ case e1000_82542_rev2_0:
+ case e1000_82542_rev2_1:
+ case e1000_82543:
+ case e1000_82544:
+ /* Wait for reset to complete */
+ udelay(10);
+ ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
+ ctrl_ext |= E1000_CTRL_EXT_EE_RST;
+ E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
+ E1000_WRITE_FLUSH(hw);
+ /* Wait for EEPROM reload */
+ mdelay(2);
+ break;
+ case e1000_82541:
+ case e1000_82541_rev_2:
+ case e1000_82547:
+ case e1000_82547_rev_2:
+ /* Wait for EEPROM reload */
+ mdelay(20);
+ break;
+ default:
+ /* Wait for EEPROM reload (it happens automatically) */
+ mdelay(5);
+ break;
+ }
+
+ /* Disable HW ARPs on ASF enabled adapters */
+ if(hw->mac_type >= e1000_82540) {
+ manc = E1000_READ_REG(hw, MANC);
+ manc &= ~(E1000_MANC_ARP_EN);
+ E1000_WRITE_REG(hw, MANC, manc);
+ }
+
+ if((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) {
+ e1000_phy_init_script(hw);
+ }
+
+ /* Clear interrupt mask to stop board from generating interrupts */
+ DEBUGOUT("Masking off all interrupts\n");
+ E1000_WRITE_REG(hw, IMC, 0xffffffff);
+
+ /* Clear any pending interrupt events. */
+ icr = E1000_READ_REG(hw, ICR);
+
+ /* If MWI was previously enabled, reenable it. */
+ if(hw->mac_type == e1000_82542_rev2_0) {
+#ifdef LINUX_DRIVER
+ if(hw->pci_cmd_word & CMD_MEM_WRT_INVALIDATE)
+#endif
+ e1000_pci_set_mwi(hw);
+ }
+}
+
+/******************************************************************************
+ * Performs basic configuration of the adapter.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Assumes that the controller has previously been reset and is in a
+ * post-reset uninitialized state. Initializes the receive address registers,
+ * multicast table, and VLAN filter table. Calls routines to setup link
+ * configuration and flow control settings. Clears all on-chip counters. Leaves
+ * the transmit and receive units disabled and uninitialized.
+ *****************************************************************************/
+static int
+e1000_init_hw(struct e1000_hw *hw)
+{
+ uint32_t ctrl, status;
+ uint32_t i;
+ int32_t ret_val;
+ uint16_t pcix_cmd_word;
+ uint16_t pcix_stat_hi_word;
+ uint16_t cmd_mmrbc;
+ uint16_t stat_mmrbc;
+ e1000_bus_type bus_type = e1000_bus_type_unknown;
+
+ DEBUGFUNC("e1000_init_hw");
+
+ /* Set the media type and TBI compatibility */
+ e1000_set_media_type(hw);
+
+ /* Disabling VLAN filtering. */
+ DEBUGOUT("Initializing the IEEE VLAN\n");
+ E1000_WRITE_REG(hw, VET, 0);
+
+ e1000_clear_vfta(hw);
+
+ /* For 82542 (rev 2.0), disable MWI and put the receiver into reset */
+ if(hw->mac_type == e1000_82542_rev2_0) {
+ DEBUGOUT("Disabling MWI on 82542 rev 2.0\n");
+ e1000_pci_clear_mwi(hw);
+ E1000_WRITE_REG(hw, RCTL, E1000_RCTL_RST);
+ E1000_WRITE_FLUSH(hw);
+ mdelay(5);
+ }
+
+ /* Setup the receive address. This involves initializing all of the Receive
+ * Address Registers (RARs 0 - 15).
+ */
+ e1000_init_rx_addrs(hw);
+
+ /* For 82542 (rev 2.0), take the receiver out of reset and enable MWI */
+ if(hw->mac_type == e1000_82542_rev2_0) {
+ E1000_WRITE_REG(hw, RCTL, 0);
+ E1000_WRITE_FLUSH(hw);
+ mdelay(1);
+#ifdef LINUX_DRIVER
+ if(hw->pci_cmd_word & CMD_MEM_WRT_INVALIDATE)
+#endif
+ e1000_pci_set_mwi(hw);
+ }
+
+ /* Zero out the Multicast HASH table */
+ DEBUGOUT("Zeroing the MTA\n");
+ for(i = 0; i < E1000_MC_TBL_SIZE; i++)
+ E1000_WRITE_REG_ARRAY(hw, MTA, i, 0);
+
+#if 0
+ /* Set the PCI priority bit correctly in the CTRL register. This
+ * determines if the adapter gives priority to receives, or if it
+ * gives equal priority to transmits and receives.
+ */
+ if(hw->dma_fairness) {
+ ctrl = E1000_READ_REG(hw, CTRL);
+ E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PRIOR);
+ }
+#endif
+
+ switch(hw->mac_type) {
+ case e1000_82545_rev_3:
+ case e1000_82546_rev_3:
+ break;
+ default:
+ if (hw->mac_type >= e1000_82543) {
+ /* See e1000_get_bus_info() of the Linux driver */
+ status = E1000_READ_REG(hw, STATUS);
+ bus_type = (status & E1000_STATUS_PCIX_MODE) ?
+ e1000_bus_type_pcix : e1000_bus_type_pci;
+ }
+
+ /* Workaround for PCI-X problem when BIOS sets MMRBC incorrectly. */
+ if(bus_type == e1000_bus_type_pcix) {
+ pci_read_config_word(hw->pdev, PCIX_COMMAND_REGISTER, &pcix_cmd_word);
+ pci_read_config_word(hw->pdev, PCIX_STATUS_REGISTER_HI, &pcix_stat_hi_word);
+ cmd_mmrbc = (pcix_cmd_word & PCIX_COMMAND_MMRBC_MASK) >>
+ PCIX_COMMAND_MMRBC_SHIFT;
+ stat_mmrbc = (pcix_stat_hi_word & PCIX_STATUS_HI_MMRBC_MASK) >>
+ PCIX_STATUS_HI_MMRBC_SHIFT;
+ if(stat_mmrbc == PCIX_STATUS_HI_MMRBC_4K)
+ stat_mmrbc = PCIX_STATUS_HI_MMRBC_2K;
+ if(cmd_mmrbc > stat_mmrbc) {
+ pcix_cmd_word &= ~PCIX_COMMAND_MMRBC_MASK;
+ pcix_cmd_word |= stat_mmrbc << PCIX_COMMAND_MMRBC_SHIFT;
+ pci_write_config_word(hw->pdev, PCIX_COMMAND_REGISTER, pcix_cmd_word);
+ }
+ }
+ break;
+ }
+
+ /* Call a subroutine to configure the link and setup flow control. */
+ ret_val = e1000_setup_link(hw);
+
+ /* Set the transmit descriptor write-back policy */
+ if(hw->mac_type > e1000_82544) {
+ ctrl = E1000_READ_REG(hw, TXDCTL);
+ ctrl = (ctrl & ~E1000_TXDCTL_WTHRESH) | E1000_TXDCTL_FULL_TX_DESC_WB;
+ E1000_WRITE_REG(hw, TXDCTL, ctrl);
+ }
+
+#if 0
+ /* Clear all of the statistics registers (clear on read). It is
+ * important that we do this after we have tried to establish link
+ * because the symbol error count will increment wildly if there
+ * is no link.
+ */
+ e1000_clear_hw_cntrs(hw);
+#endif
+
+ return ret_val;
+}
+
+/******************************************************************************
+ * Adjust SERDES output amplitude based on EEPROM setting.
+ *
+ * hw - Struct containing variables accessed by shared code.
+ *****************************************************************************/
+static int32_t
+e1000_adjust_serdes_amplitude(struct e1000_hw *hw)
+{
+ uint16_t eeprom_data;
+ int32_t ret_val;
+
+ DEBUGFUNC("e1000_adjust_serdes_amplitude");
+
+ if(hw->media_type != e1000_media_type_internal_serdes)
+ return E1000_SUCCESS;
+
+ switch(hw->mac_type) {
+ case e1000_82545_rev_3:
+ case e1000_82546_rev_3:
+ break;
+ default:
+ return E1000_SUCCESS;
+ }
+
+ if ((ret_val = e1000_read_eeprom(hw, EEPROM_SERDES_AMPLITUDE, 1,
+ &eeprom_data))) {
+ return ret_val;
+ }
+
+ if(eeprom_data != EEPROM_RESERVED_WORD) {
+ /* Adjust SERDES output amplitude only. */
+ eeprom_data &= EEPROM_SERDES_AMPLITUDE_MASK;
+ if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_EXT_CTRL,
+ eeprom_data)))
+ return ret_val;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Configures flow control and link settings.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Determines which flow control settings to use. Calls the apropriate media-
+ * specific link configuration function. Configures the flow control settings.
+ * Assuming the adapter has a valid link partner, a valid link should be
+ * established. Assumes the hardware has previously been reset and the
+ * transmitter and receiver are not enabled.
+ *****************************************************************************/
+static int
+e1000_setup_link(struct e1000_hw *hw)
+{
+ uint32_t ctrl_ext;
+ int32_t ret_val;
+ uint16_t eeprom_data;
+
+ DEBUGFUNC("e1000_setup_link");
+
+ /* Read and store word 0x0F of the EEPROM. This word contains bits
+ * that determine the hardware's default PAUSE (flow control) mode,
+ * a bit that determines whether the HW defaults to enabling or
+ * disabling auto-negotiation, and the direction of the
+ * SW defined pins. If there is no SW over-ride of the flow
+ * control setting, then the variable hw->fc will
+ * be initialized based on a value in the EEPROM.
+ */
+ if(e1000_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG, 1, &eeprom_data) < 0) {
+ DEBUGOUT("EEPROM Read Error\n");
+ return -E1000_ERR_EEPROM;
+ }
+
+ if(hw->fc == e1000_fc_default) {
+ if((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) == 0)
+ hw->fc = e1000_fc_none;
+ else if((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) ==
+ EEPROM_WORD0F_ASM_DIR)
+ hw->fc = e1000_fc_tx_pause;
+ else
+ hw->fc = e1000_fc_full;
+ }
+
+ /* We want to save off the original Flow Control configuration just
+ * in case we get disconnected and then reconnected into a different
+ * hub or switch with different Flow Control capabilities.
+ */
+ if(hw->mac_type == e1000_82542_rev2_0)
+ hw->fc &= (~e1000_fc_tx_pause);
+
+#if 0
+ /* See e1000_sw_init() of the Linux driver */
+ if((hw->mac_type < e1000_82543) && (hw->report_tx_early == 1))
+#else
+ if((hw->mac_type < e1000_82543) && (hw->mac_type >= e1000_82543))
+#endif
+ hw->fc &= (~e1000_fc_rx_pause);
+
+#if 0
+ hw->original_fc = hw->fc;
+#endif
+
+ DEBUGOUT1("After fix-ups FlowControl is now = %x\n", hw->fc);
+
+ /* Take the 4 bits from EEPROM word 0x0F that determine the initial
+ * polarity value for the SW controlled pins, and setup the
+ * Extended Device Control reg with that info.
+ * This is needed because one of the SW controlled pins is used for
+ * signal detection. So this should be done before e1000_setup_pcs_link()
+ * or e1000_phy_setup() is called.
+ */
+ if(hw->mac_type == e1000_82543) {
+ ctrl_ext = ((eeprom_data & EEPROM_WORD0F_SWPDIO_EXT) <<
+ SWDPIO__EXT_SHIFT);
+ E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
+ }
+
+ /* Call the necessary subroutine to configure the link. */
+ ret_val = (hw->media_type == e1000_media_type_copper) ?
+ e1000_setup_copper_link(hw) :
+ e1000_setup_fiber_serdes_link(hw);
+ if (ret_val < 0) {
+ return ret_val;
+ }
+
+ /* Initialize the flow control address, type, and PAUSE timer
+ * registers to their default values. This is done even if flow
+ * control is disabled, because it does not hurt anything to
+ * initialize these registers.
+ */
+ DEBUGOUT("Initializing the Flow Control address, type and timer regs\n");
+
+ E1000_WRITE_REG(hw, FCAL, FLOW_CONTROL_ADDRESS_LOW);
+ E1000_WRITE_REG(hw, FCAH, FLOW_CONTROL_ADDRESS_HIGH);
+ E1000_WRITE_REG(hw, FCT, FLOW_CONTROL_TYPE);
+#if 0
+ E1000_WRITE_REG(hw, FCTTV, hw->fc_pause_time);
+#else
+ E1000_WRITE_REG(hw, FCTTV, FC_DEFAULT_TX_TIMER);
+#endif
+
+ /* Set the flow control receive threshold registers. Normally,
+ * these registers will be set to a default threshold that may be
+ * adjusted later by the driver's runtime code. However, if the
+ * ability to transmit pause frames in not enabled, then these
+ * registers will be set to 0.
+ */
+ if(!(hw->fc & e1000_fc_tx_pause)) {
+ E1000_WRITE_REG(hw, FCRTL, 0);
+ E1000_WRITE_REG(hw, FCRTH, 0);
+ } else {
+ /* We need to set up the Receive Threshold high and low water marks
+ * as well as (optionally) enabling the transmission of XON frames.
+ */
+#if 0
+ if(hw->fc_send_xon) {
+ E1000_WRITE_REG(hw, FCRTL, (hw->fc_low_water | E1000_FCRTL_XONE));
+ E1000_WRITE_REG(hw, FCRTH, hw->fc_high_water);
+ } else {
+ E1000_WRITE_REG(hw, FCRTL, hw->fc_low_water);
+ E1000_WRITE_REG(hw, FCRTH, hw->fc_high_water);
+ }
+#else
+ E1000_WRITE_REG(hw, FCRTL, (FC_DEFAULT_LO_THRESH | E1000_FCRTL_XONE));
+ E1000_WRITE_REG(hw, FCRTH, FC_DEFAULT_HI_THRESH);
+#endif
+ }
+ return ret_val;
+}
+
+/******************************************************************************
+ * Sets up link for a fiber based or serdes based adapter
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Manipulates Physical Coding Sublayer functions in order to configure
+ * link. Assumes the hardware has been previously reset and the transmitter
+ * and receiver are not enabled.
+ *****************************************************************************/
+static int
+e1000_setup_fiber_serdes_link(struct e1000_hw *hw)
+{
+ uint32_t ctrl;
+ uint32_t status;
+ uint32_t txcw = 0;
+ uint32_t i;
+ uint32_t signal = 0;
+ int32_t ret_val;
+
+ DEBUGFUNC("e1000_setup_fiber_serdes_link");
+
+ /* On adapters with a MAC newer than 82544, SW Defineable pin 1 will be
+ * set when the optics detect a signal. On older adapters, it will be
+ * cleared when there is a signal. This applies to fiber media only.
+ * If we're on serdes media, adjust the output amplitude to value set in
+ * the EEPROM.
+ */
+ ctrl = E1000_READ_REG(hw, CTRL);
+ if(hw->media_type == e1000_media_type_fiber)
+ signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0;
+
+ if((ret_val = e1000_adjust_serdes_amplitude(hw)))
+ return ret_val;
+
+ /* Take the link out of reset */
+ ctrl &= ~(E1000_CTRL_LRST);
+
+#if 0
+ /* Adjust VCO speed to improve BER performance */
+ if((ret_val = e1000_set_vco_speed(hw)))
+ return ret_val;
+#endif
+
+ e1000_config_collision_dist(hw);
+
+ /* Check for a software override of the flow control settings, and setup
+ * the device accordingly. If auto-negotiation is enabled, then software
+ * will have to set the "PAUSE" bits to the correct value in the Tranmsit
+ * Config Word Register (TXCW) and re-start auto-negotiation. However, if
+ * auto-negotiation is disabled, then software will have to manually
+ * configure the two flow control enable bits in the CTRL register.
+ *
+ * The possible values of the "fc" parameter are:
+ * 0: Flow control is completely disabled
+ * 1: Rx flow control is enabled (we can receive pause frames, but
+ * not send pause frames).
+ * 2: Tx flow control is enabled (we can send pause frames but we do
+ * not support receiving pause frames).
+ * 3: Both Rx and TX flow control (symmetric) are enabled.
+ */
+ switch (hw->fc) {
+ case e1000_fc_none:
+ /* Flow control is completely disabled by a software over-ride. */
+ txcw = (E1000_TXCW_ANE | E1000_TXCW_FD);
+ break;
+ case e1000_fc_rx_pause:
+ /* RX Flow control is enabled and TX Flow control is disabled by a
+ * software over-ride. Since there really isn't a way to advertise
+ * that we are capable of RX Pause ONLY, we will advertise that we
+ * support both symmetric and asymmetric RX PAUSE. Later, we will
+ * disable the adapter's ability to send PAUSE frames.
+ */
+ txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK);
+ break;
+ case e1000_fc_tx_pause:
+ /* TX Flow control is enabled, and RX Flow control is disabled, by a
+ * software over-ride.
+ */
+ txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_ASM_DIR);
+ break;
+ case e1000_fc_full:
+ /* Flow control (both RX and TX) is enabled by a software over-ride. */
+ txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK);
+ break;
+ default:
+ DEBUGOUT("Flow control param set incorrectly\n");
+ return -E1000_ERR_CONFIG;
+ break;
+ }
+
+ /* Since auto-negotiation is enabled, take the link out of reset (the link
+ * will be in reset, because we previously reset the chip). This will
+ * restart auto-negotiation. If auto-neogtiation is successful then the
+ * link-up status bit will be set and the flow control enable bits (RFCE
+ * and TFCE) will be set according to their negotiated value.
+ */
+ DEBUGOUT("Auto-negotiation enabled\n");
+
+ E1000_WRITE_REG(hw, TXCW, txcw);
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+ E1000_WRITE_FLUSH(hw);
+
+ hw->txcw = txcw;
+ mdelay(1);
+
+ /* If we have a signal (the cable is plugged in) then poll for a "Link-Up"
+ * indication in the Device Status Register. Time-out if a link isn't
+ * seen in 500 milliseconds seconds (Auto-negotiation should complete in
+ * less than 500 milliseconds even if the other end is doing it in SW).
+ * For internal serdes, we just assume a signal is present, then poll.
+ */
+ if(hw->media_type == e1000_media_type_internal_serdes ||
+ (E1000_READ_REG(hw, CTRL) & E1000_CTRL_SWDPIN1) == signal) {
+ DEBUGOUT("Looking for Link\n");
+ for(i = 0; i < (LINK_UP_TIMEOUT / 10); i++) {
+ mdelay(10);
+ status = E1000_READ_REG(hw, STATUS);
+ if(status & E1000_STATUS_LU) break;
+ }
+ if(i == (LINK_UP_TIMEOUT / 10)) {
+ DEBUGOUT("Never got a valid link from auto-neg!!!\n");
+ hw->autoneg_failed = 1;
+ /* AutoNeg failed to achieve a link, so we'll call
+ * e1000_check_for_link. This routine will force the link up if
+ * we detect a signal. This will allow us to communicate with
+ * non-autonegotiating link partners.
+ */
+ if((ret_val = e1000_check_for_link(hw))) {
+ DEBUGOUT("Error while checking for link\n");
+ return ret_val;
+ }
+ hw->autoneg_failed = 0;
+ } else {
+ hw->autoneg_failed = 0;
+ DEBUGOUT("Valid Link Found\n");
+ }
+ } else {
+ DEBUGOUT("No Signal Detected\n");
+ }
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Detects which PHY is present and the speed and duplex
+*
+* hw - Struct containing variables accessed by shared code
+******************************************************************************/
+static int
+e1000_setup_copper_link(struct e1000_hw *hw)
+{
+ uint32_t ctrl;
+ int32_t ret_val;
+ uint16_t i;
+ uint16_t phy_data;
+
+ DEBUGFUNC("e1000_setup_copper_link");
+
+ ctrl = E1000_READ_REG(hw, CTRL);
+ /* With 82543, we need to force speed and duplex on the MAC equal to what
+ * the PHY speed and duplex configuration is. In addition, we need to
+ * perform a hardware reset on the PHY to take it out of reset.
+ */
+ if(hw->mac_type > e1000_82543) {
+ ctrl |= E1000_CTRL_SLU;
+ ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX);
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+ } else {
+ ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX | E1000_CTRL_SLU);
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+ e1000_phy_hw_reset(hw);
+ }
+
+ /* Make sure we have a valid PHY */
+ if((ret_val = e1000_detect_gig_phy(hw))) {
+ DEBUGOUT("Error, did not detect valid phy.\n");
+ return ret_val;
+ }
+ DEBUGOUT1("Phy ID = %x \n", hw->phy_id);
+
+ if(hw->mac_type <= e1000_82543 ||
+ hw->mac_type == e1000_82541 || hw->mac_type == e1000_82547 ||
+#if 0
+ hw->mac_type == e1000_82541_rev_2 || hw->mac_type == e1000_82547_rev_2)
+ hw->phy_reset_disable = FALSE;
+
+ if(!hw->phy_reset_disable) {
+#else
+ hw->mac_type == e1000_82541_rev_2 || hw->mac_type == e1000_82547_rev_2) {
+#endif
+ if (hw->phy_type == e1000_phy_igp) {
+
+ if((ret_val = e1000_phy_reset(hw))) {
+ DEBUGOUT("Error Resetting the PHY\n");
+ return ret_val;
+ }
+
+ /* Wait 10ms for MAC to configure PHY from eeprom settings */
+ mdelay(15);
+
+#if 0
+ /* disable lplu d3 during driver init */
+ if((ret_val = e1000_set_d3_lplu_state(hw, FALSE))) {
+ DEBUGOUT("Error Disabling LPLU D3\n");
+ return ret_val;
+ }
+
+ /* Configure mdi-mdix settings */
+ if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL,
+ &phy_data)))
+ return ret_val;
+
+ if((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) {
+ hw->dsp_config_state = e1000_dsp_config_disabled;
+ /* Force MDI for IGP B-0 PHY */
+ phy_data &= ~(IGP01E1000_PSCR_AUTO_MDIX |
+ IGP01E1000_PSCR_FORCE_MDI_MDIX);
+ hw->mdix = 1;
+
+ } else {
+ hw->dsp_config_state = e1000_dsp_config_enabled;
+ phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX;
+
+ switch (hw->mdix) {
+ case 1:
+ phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX;
+ break;
+ case 2:
+ phy_data |= IGP01E1000_PSCR_FORCE_MDI_MDIX;
+ break;
+ case 0:
+ default:
+ phy_data |= IGP01E1000_PSCR_AUTO_MDIX;
+ break;
+ }
+ }
+ if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL,
+ phy_data)))
+ return ret_val;
+
+ /* set auto-master slave resolution settings */
+ e1000_ms_type phy_ms_setting = hw->master_slave;
+
+ if(hw->ffe_config_state == e1000_ffe_config_active)
+ hw->ffe_config_state = e1000_ffe_config_enabled;
+
+ if(hw->dsp_config_state == e1000_dsp_config_activated)
+ hw->dsp_config_state = e1000_dsp_config_enabled;
+#endif
+
+ /* when autonegotiation advertisment is only 1000Mbps then we
+ * should disable SmartSpeed and enable Auto MasterSlave
+ * resolution as hardware default. */
+ if(hw->autoneg_advertised == ADVERTISE_1000_FULL) {
+ /* Disable SmartSpeed */
+ if((ret_val = e1000_read_phy_reg(hw,
+ IGP01E1000_PHY_PORT_CONFIG,
+ &phy_data)))
+ return ret_val;
+ phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED;
+ if((ret_val = e1000_write_phy_reg(hw,
+ IGP01E1000_PHY_PORT_CONFIG,
+ phy_data)))
+ return ret_val;
+ /* Set auto Master/Slave resolution process */
+ if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL,
+ &phy_data)))
+ return ret_val;
+ phy_data &= ~CR_1000T_MS_ENABLE;
+ if((ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL,
+ phy_data)))
+ return ret_val;
+ }
+
+ if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL,
+ &phy_data)))
+ return ret_val;
+
+#if 0
+ /* load defaults for future use */
+ hw->original_master_slave = (phy_data & CR_1000T_MS_ENABLE) ?
+ ((phy_data & CR_1000T_MS_VALUE) ?
+ e1000_ms_force_master :
+ e1000_ms_force_slave) :
+ e1000_ms_auto;
+
+ switch (phy_ms_setting) {
+ case e1000_ms_force_master:
+ phy_data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE);
+ break;
+ case e1000_ms_force_slave:
+ phy_data |= CR_1000T_MS_ENABLE;
+ phy_data &= ~(CR_1000T_MS_VALUE);
+ break;
+ case e1000_ms_auto:
+ phy_data &= ~CR_1000T_MS_ENABLE;
+ default:
+ break;
+ }
+#endif
+
+ if((ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL,
+ phy_data)))
+ return ret_val;
+ } else {
+ /* Enable CRS on TX. This must be set for half-duplex operation. */
+ if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL,
+ &phy_data)))
+ return ret_val;
+
+ phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
+
+ /* Options:
+ * MDI/MDI-X = 0 (default)
+ * 0 - Auto for all speeds
+ * 1 - MDI mode
+ * 2 - MDI-X mode
+ * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes)
+ */
+#if 0
+ phy_data &= ~M88E1000_PSCR_AUTO_X_MODE;
+
+ switch (hw->mdix) {
+ case 1:
+ phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE;
+ break;
+ case 2:
+ phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE;
+ break;
+ case 3:
+ phy_data |= M88E1000_PSCR_AUTO_X_1000T;
+ break;
+ case 0:
+ default:
+#endif
+ phy_data |= M88E1000_PSCR_AUTO_X_MODE;
+#if 0
+ break;
+ }
+#endif
+
+ /* Options:
+ * disable_polarity_correction = 0 (default)
+ * Automatic Correction for Reversed Cable Polarity
+ * 0 - Disabled
+ * 1 - Enabled
+ */
+ phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL;
+ if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL,
+ phy_data)))
+ return ret_val;
+
+ /* Force TX_CLK in the Extended PHY Specific Control Register
+ * to 25MHz clock.
+ */
+ if((ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
+ &phy_data)))
+ return ret_val;
+
+ phy_data |= M88E1000_EPSCR_TX_CLK_25;
+
+#ifdef LINUX_DRIVER
+ if (hw->phy_revision < M88E1011_I_REV_4) {
+#endif
+ /* Configure Master and Slave downshift values */
+ phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK |
+ M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK);
+ phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X |
+ M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X);
+ if((ret_val = e1000_write_phy_reg(hw,
+ M88E1000_EXT_PHY_SPEC_CTRL,
+ phy_data)))
+ return ret_val;
+ }
+
+ /* SW Reset the PHY so all changes take effect */
+ if((ret_val = e1000_phy_reset(hw))) {
+ DEBUGOUT("Error Resetting the PHY\n");
+ return ret_val;
+#ifdef LINUX_DRIVER
+ }
+#endif
+ }
+
+ /* Options:
+ * autoneg = 1 (default)
+ * PHY will advertise value(s) parsed from
+ * autoneg_advertised and fc
+ * autoneg = 0
+ * PHY will be set to 10H, 10F, 100H, or 100F
+ * depending on value parsed from forced_speed_duplex.
+ */
+
+ /* Is autoneg enabled? This is enabled by default or by software
+ * override. If so, call e1000_phy_setup_autoneg routine to parse the
+ * autoneg_advertised and fc options. If autoneg is NOT enabled, then
+ * the user should have provided a speed/duplex override. If so, then
+ * call e1000_phy_force_speed_duplex to parse and set this up.
+ */
+ /* Perform some bounds checking on the hw->autoneg_advertised
+ * parameter. If this variable is zero, then set it to the default.
+ */
+ hw->autoneg_advertised &= AUTONEG_ADVERTISE_SPEED_DEFAULT;
+
+ /* If autoneg_advertised is zero, we assume it was not defaulted
+ * by the calling code so we set to advertise full capability.
+ */
+ if(hw->autoneg_advertised == 0)
+ hw->autoneg_advertised = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+
+ DEBUGOUT("Reconfiguring auto-neg advertisement params\n");
+ if((ret_val = e1000_phy_setup_autoneg(hw))) {
+ DEBUGOUT("Error Setting up Auto-Negotiation\n");
+ return ret_val;
+ }
+ DEBUGOUT("Restarting Auto-Neg\n");
+
+ /* Restart auto-negotiation by setting the Auto Neg Enable bit and
+ * the Auto Neg Restart bit in the PHY control register.
+ */
+ if((ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data)))
+ return ret_val;
+
+ phy_data |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG);
+ if((ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data)))
+ return ret_val;
+
+#if 0
+ /* Does the user want to wait for Auto-Neg to complete here, or
+ * check at a later time (for example, callback routine).
+ */
+ if(hw->wait_autoneg_complete) {
+ if((ret_val = e1000_wait_autoneg(hw))) {
+ DEBUGOUT("Error while waiting for autoneg to complete\n");
+ return ret_val;
+ }
+ }
+#else
+ /* If we do not wait for autonegotiation to complete I
+ * do not see a valid link status.
+ */
+ if((ret_val = e1000_wait_autoneg(hw))) {
+ DEBUGOUT("Error while waiting for autoneg to complete\n");
+ return ret_val;
+ }
+#endif
+ } /* !hw->phy_reset_disable */
+
+ /* Check link status. Wait up to 100 microseconds for link to become
+ * valid.
+ */
+ for(i = 0; i < 10; i++) {
+ if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+ return ret_val;
+ if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+ return ret_val;
+
+ if(phy_data & MII_SR_LINK_STATUS) {
+ /* We have link, so we need to finish the config process:
+ * 1) Set up the MAC to the current PHY speed/duplex
+ * if we are on 82543. If we
+ * are on newer silicon, we only need to configure
+ * collision distance in the Transmit Control Register.
+ * 2) Set up flow control on the MAC to that established with
+ * the link partner.
+ */
+ if(hw->mac_type >= e1000_82544) {
+ e1000_config_collision_dist(hw);
+ } else {
+ if((ret_val = e1000_config_mac_to_phy(hw))) {
+ DEBUGOUT("Error configuring MAC to PHY settings\n");
+ return ret_val;
+ }
+ }
+ if((ret_val = e1000_config_fc_after_link_up(hw))) {
+ DEBUGOUT("Error Configuring Flow Control\n");
+ return ret_val;
+ }
+#if 0
+ if(hw->phy_type == e1000_phy_igp) {
+ if((ret_val = e1000_config_dsp_after_link_change(hw, TRUE))) {
+ DEBUGOUT("Error Configuring DSP after link up\n");
+ return ret_val;
+ }
+ }
+#endif
+ DEBUGOUT("Valid link established!!!\n");
+ return E1000_SUCCESS;
+ }
+ udelay(10);
+ }
+
+ DEBUGOUT("Unable to establish link!!!\n");
+ return -E1000_ERR_NOLINK;
+}
+
+/******************************************************************************
+* Configures PHY autoneg and flow control advertisement settings
+*
+* hw - Struct containing variables accessed by shared code
+******************************************************************************/
+static int
+e1000_phy_setup_autoneg(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ uint16_t mii_autoneg_adv_reg;
+ uint16_t mii_1000t_ctrl_reg;
+
+ DEBUGFUNC("e1000_phy_setup_autoneg");
+
+ /* Read the MII Auto-Neg Advertisement Register (Address 4). */
+ if((ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV,
+ &mii_autoneg_adv_reg)))
+ return ret_val;
+
+ /* Read the MII 1000Base-T Control Register (Address 9). */
+ if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &mii_1000t_ctrl_reg)))
+ return ret_val;
+
+ /* Need to parse both autoneg_advertised and fc and set up
+ * the appropriate PHY registers. First we will parse for
+ * autoneg_advertised software override. Since we can advertise
+ * a plethora of combinations, we need to check each bit
+ * individually.
+ */
+
+ /* First we clear all the 10/100 mb speed bits in the Auto-Neg
+ * Advertisement Register (Address 4) and the 1000 mb speed bits in
+ * the 1000Base-T Control Register (Address 9).
+ */
+ mii_autoneg_adv_reg &= ~REG4_SPEED_MASK;
+ mii_1000t_ctrl_reg &= ~REG9_SPEED_MASK;
+
+ DEBUGOUT1("autoneg_advertised %x\n", hw->autoneg_advertised);
+
+ /* Do we want to advertise 10 Mb Half Duplex? */
+ if(hw->autoneg_advertised & ADVERTISE_10_HALF) {
+ DEBUGOUT("Advertise 10mb Half duplex\n");
+ mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS;
+ }
+
+ /* Do we want to advertise 10 Mb Full Duplex? */
+ if(hw->autoneg_advertised & ADVERTISE_10_FULL) {
+ DEBUGOUT("Advertise 10mb Full duplex\n");
+ mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS;
+ }
+
+ /* Do we want to advertise 100 Mb Half Duplex? */
+ if(hw->autoneg_advertised & ADVERTISE_100_HALF) {
+ DEBUGOUT("Advertise 100mb Half duplex\n");
+ mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS;
+ }
+
+ /* Do we want to advertise 100 Mb Full Duplex? */
+ if(hw->autoneg_advertised & ADVERTISE_100_FULL) {
+ DEBUGOUT("Advertise 100mb Full duplex\n");
+ mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS;
+ }
+
+ /* We do not allow the Phy to advertise 1000 Mb Half Duplex */
+ if(hw->autoneg_advertised & ADVERTISE_1000_HALF) {
+ DEBUGOUT("Advertise 1000mb Half duplex requested, request denied!\n");
+ }
+
+ /* Do we want to advertise 1000 Mb Full Duplex? */
+ if(hw->autoneg_advertised & ADVERTISE_1000_FULL) {
+ DEBUGOUT("Advertise 1000mb Full duplex\n");
+ mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS;
+ }
+
+ /* Check for a software override of the flow control settings, and
+ * setup the PHY advertisement registers accordingly. If
+ * auto-negotiation is enabled, then software will have to set the
+ * "PAUSE" bits to the correct value in the Auto-Negotiation
+ * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto-negotiation.
+ *
+ * The possible values of the "fc" parameter are:
+ * 0: Flow control is completely disabled
+ * 1: Rx flow control is enabled (we can receive pause frames
+ * but not send pause frames).
+ * 2: Tx flow control is enabled (we can send pause frames
+ * but we do not support receiving pause frames).
+ * 3: Both Rx and TX flow control (symmetric) are enabled.
+ * other: No software override. The flow control configuration
+ * in the EEPROM is used.
+ */
+ switch (hw->fc) {
+ case e1000_fc_none: /* 0 */
+ /* Flow control (RX & TX) is completely disabled by a
+ * software over-ride.
+ */
+ mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
+ break;
+ case e1000_fc_rx_pause: /* 1 */
+ /* RX Flow control is enabled, and TX Flow control is
+ * disabled, by a software over-ride.
+ */
+ /* Since there really isn't a way to advertise that we are
+ * capable of RX Pause ONLY, we will advertise that we
+ * support both symmetric and asymmetric RX PAUSE. Later
+ * (in e1000_config_fc_after_link_up) we will disable the
+ *hw's ability to send PAUSE frames.
+ */
+ mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
+ break;
+ case e1000_fc_tx_pause: /* 2 */
+ /* TX Flow control is enabled, and RX Flow control is
+ * disabled, by a software over-ride.
+ */
+ mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR;
+ mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE;
+ break;
+ case e1000_fc_full: /* 3 */
+ /* Flow control (both RX and TX) is enabled by a software
+ * over-ride.
+ */
+ mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
+ break;
+ default:
+ DEBUGOUT("Flow control param set incorrectly\n");
+ return -E1000_ERR_CONFIG;
+ }
+
+ if((ret_val = e1000_write_phy_reg(hw, PHY_AUTONEG_ADV,
+ mii_autoneg_adv_reg)))
+ return ret_val;
+
+ DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg);
+
+ if((ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, mii_1000t_ctrl_reg)))
+ return ret_val;
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Sets the collision distance in the Transmit Control register
+*
+* hw - Struct containing variables accessed by shared code
+*
+* Link should have been established previously. Reads the speed and duplex
+* information from the Device Status register.
+******************************************************************************/
+static void
+e1000_config_collision_dist(struct e1000_hw *hw)
+{
+ uint32_t tctl;
+
+ tctl = E1000_READ_REG(hw, TCTL);
+
+ tctl &= ~E1000_TCTL_COLD;
+ tctl |= E1000_COLLISION_DISTANCE << E1000_COLD_SHIFT;
+
+ E1000_WRITE_REG(hw, TCTL, tctl);
+ E1000_WRITE_FLUSH(hw);
+}
+
+/******************************************************************************
+* Sets MAC speed and duplex settings to reflect the those in the PHY
+*
+* hw - Struct containing variables accessed by shared code
+* mii_reg - data to write to the MII control register
+*
+* The contents of the PHY register containing the needed information need to
+* be passed in.
+******************************************************************************/
+static int
+e1000_config_mac_to_phy(struct e1000_hw *hw)
+{
+ uint32_t ctrl;
+ int32_t ret_val;
+ uint16_t phy_data;
+
+ DEBUGFUNC("e1000_config_mac_to_phy");
+
+ /* Read the Device Control Register and set the bits to Force Speed
+ * and Duplex.
+ */
+ ctrl = E1000_READ_REG(hw, CTRL);
+ ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX);
+ ctrl &= ~(E1000_CTRL_SPD_SEL | E1000_CTRL_ILOS);
+
+ /* Set up duplex in the Device Control and Transmit Control
+ * registers depending on negotiated values.
+ */
+ if (hw->phy_type == e1000_phy_igp) {
+ if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS,
+ &phy_data)))
+ return ret_val;
+
+ if(phy_data & IGP01E1000_PSSR_FULL_DUPLEX) ctrl |= E1000_CTRL_FD;
+ else ctrl &= ~E1000_CTRL_FD;
+
+ e1000_config_collision_dist(hw);
+
+ /* Set up speed in the Device Control register depending on
+ * negotiated values.
+ */
+ if((phy_data & IGP01E1000_PSSR_SPEED_MASK) ==
+ IGP01E1000_PSSR_SPEED_1000MBPS)
+ ctrl |= E1000_CTRL_SPD_1000;
+ else if((phy_data & IGP01E1000_PSSR_SPEED_MASK) ==
+ IGP01E1000_PSSR_SPEED_100MBPS)
+ ctrl |= E1000_CTRL_SPD_100;
+ } else {
+ if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS,
+ &phy_data)))
+ return ret_val;
+
+ if(phy_data & M88E1000_PSSR_DPLX) ctrl |= E1000_CTRL_FD;
+ else ctrl &= ~E1000_CTRL_FD;
+
+ e1000_config_collision_dist(hw);
+
+ /* Set up speed in the Device Control register depending on
+ * negotiated values.
+ */
+ if((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS)
+ ctrl |= E1000_CTRL_SPD_1000;
+ else if((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_100MBS)
+ ctrl |= E1000_CTRL_SPD_100;
+ }
+ /* Write the configured values back to the Device Control Reg. */
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Forces the MAC's flow control settings.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Sets the TFCE and RFCE bits in the device control register to reflect
+ * the adapter settings. TFCE and RFCE need to be explicitly set by
+ * software when a Copper PHY is used because autonegotiation is managed
+ * by the PHY rather than the MAC. Software must also configure these
+ * bits when link is forced on a fiber connection.
+ *****************************************************************************/
+static int
+e1000_force_mac_fc(struct e1000_hw *hw)
+{
+ uint32_t ctrl;
+
+ DEBUGFUNC("e1000_force_mac_fc");
+
+ /* Get the current configuration of the Device Control Register */
+ ctrl = E1000_READ_REG(hw, CTRL);
+
+ /* Because we didn't get link via the internal auto-negotiation
+ * mechanism (we either forced link or we got link via PHY
+ * auto-neg), we have to manually enable/disable transmit an
+ * receive flow control.
+ *
+ * The "Case" statement below enables/disable flow control
+ * according to the "hw->fc" parameter.
+ *
+ * The possible values of the "fc" parameter are:
+ * 0: Flow control is completely disabled
+ * 1: Rx flow control is enabled (we can receive pause
+ * frames but not send pause frames).
+ * 2: Tx flow control is enabled (we can send pause frames
+ * frames but we do not receive pause frames).
+ * 3: Both Rx and TX flow control (symmetric) is enabled.
+ * other: No other values should be possible at this point.
+ */
+
+ switch (hw->fc) {
+ case e1000_fc_none:
+ ctrl &= (~(E1000_CTRL_TFCE | E1000_CTRL_RFCE));
+ break;
+ case e1000_fc_rx_pause:
+ ctrl &= (~E1000_CTRL_TFCE);
+ ctrl |= E1000_CTRL_RFCE;
+ break;
+ case e1000_fc_tx_pause:
+ ctrl &= (~E1000_CTRL_RFCE);
+ ctrl |= E1000_CTRL_TFCE;
+ break;
+ case e1000_fc_full:
+ ctrl |= (E1000_CTRL_TFCE | E1000_CTRL_RFCE);
+ break;
+ default:
+ DEBUGOUT("Flow control param set incorrectly\n");
+ return -E1000_ERR_CONFIG;
+ }
+
+ /* Disable TX Flow Control for 82542 (rev 2.0) */
+ if(hw->mac_type == e1000_82542_rev2_0)
+ ctrl &= (~E1000_CTRL_TFCE);
+
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Configures flow control settings after link is established
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Should be called immediately after a valid link has been established.
+ * Forces MAC flow control settings if link was forced. When in MII/GMII mode
+ * and autonegotiation is enabled, the MAC flow control settings will be set
+ * based on the flow control negotiated by the PHY. In TBI mode, the TFCE
+ * and RFCE bits will be automaticaly set to the negotiated flow control mode.
+ *****************************************************************************/
+static int
+e1000_config_fc_after_link_up(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ uint16_t mii_status_reg;
+ uint16_t mii_nway_adv_reg;
+ uint16_t mii_nway_lp_ability_reg;
+ uint16_t speed;
+ uint16_t duplex;
+
+ DEBUGFUNC("e1000_config_fc_after_link_up");
+
+ /* Check for the case where we have fiber media and auto-neg failed
+ * so we had to force link. In this case, we need to force the
+ * configuration of the MAC to match the "fc" parameter.
+ */
+ if(((hw->media_type == e1000_media_type_fiber) && (hw->autoneg_failed)) ||
+ ((hw->media_type == e1000_media_type_internal_serdes) && (hw->autoneg_failed))) {
+ if((ret_val = e1000_force_mac_fc(hw))) {
+ DEBUGOUT("Error forcing flow control settings\n");
+ return ret_val;
+ }
+ }
+
+ /* Check for the case where we have copper media and auto-neg is
+ * enabled. In this case, we need to check and see if Auto-Neg
+ * has completed, and if so, how the PHY and link partner has
+ * flow control configured.
+ */
+ if(hw->media_type == e1000_media_type_copper) {
+ /* Read the MII Status Register and check to see if AutoNeg
+ * has completed. We read this twice because this reg has
+ * some "sticky" (latched) bits.
+ */
+ if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg)))
+ return ret_val;
+ if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg)))
+ return ret_val;
+
+ if(mii_status_reg & MII_SR_AUTONEG_COMPLETE) {
+ /* The AutoNeg process has completed, so we now need to
+ * read both the Auto Negotiation Advertisement Register
+ * (Address 4) and the Auto_Negotiation Base Page Ability
+ * Register (Address 5) to determine how flow control was
+ * negotiated.
+ */
+ if((ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV,
+ &mii_nway_adv_reg)))
+ return ret_val;
+ if((ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY,
+ &mii_nway_lp_ability_reg)))
+ return ret_val;
+
+ /* Two bits in the Auto Negotiation Advertisement Register
+ * (Address 4) and two bits in the Auto Negotiation Base
+ * Page Ability Register (Address 5) determine flow control
+ * for both the PHY and the link partner. The following
+ * table, taken out of the IEEE 802.3ab/D6.0 dated March 25,
+ * 1999, describes these PAUSE resolution bits and how flow
+ * control is determined based upon these settings.
+ * NOTE: DC = Don't Care
+ *
+ * LOCAL DEVICE | LINK PARTNER
+ * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution
+ *-------|---------|-------|---------|--------------------
+ * 0 | 0 | DC | DC | e1000_fc_none
+ * 0 | 1 | 0 | DC | e1000_fc_none
+ * 0 | 1 | 1 | 0 | e1000_fc_none
+ * 0 | 1 | 1 | 1 | e1000_fc_tx_pause
+ * 1 | 0 | 0 | DC | e1000_fc_none
+ * 1 | DC | 1 | DC | e1000_fc_full
+ * 1 | 1 | 0 | 0 | e1000_fc_none
+ * 1 | 1 | 0 | 1 | e1000_fc_rx_pause
+ *
+ */
+ /* Are both PAUSE bits set to 1? If so, this implies
+ * Symmetric Flow Control is enabled at both ends. The
+ * ASM_DIR bits are irrelevant per the spec.
+ *
+ * For Symmetric Flow Control:
+ *
+ * LOCAL DEVICE | LINK PARTNER
+ * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
+ *-------|---------|-------|---------|--------------------
+ * 1 | DC | 1 | DC | e1000_fc_full
+ *
+ */
+ if((mii_nway_adv_reg & NWAY_AR_PAUSE) &&
+ (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE)) {
+ /* Now we need to check if the user selected RX ONLY
+ * of pause frames. In this case, we had to advertise
+ * FULL flow control because we could not advertise RX
+ * ONLY. Hence, we must now check to see if we need to
+ * turn OFF the TRANSMISSION of PAUSE frames.
+ */
+#if 0
+ if(hw->original_fc == e1000_fc_full) {
+ hw->fc = e1000_fc_full;
+#else
+ if(hw->fc == e1000_fc_full) {
+#endif
+ DEBUGOUT("Flow Control = FULL.\r\n");
+ } else {
+ hw->fc = e1000_fc_rx_pause;
+ DEBUGOUT("Flow Control = RX PAUSE frames only.\r\n");
+ }
+ }
+ /* For receiving PAUSE frames ONLY.
+ *
+ * LOCAL DEVICE | LINK PARTNER
+ * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
+ *-------|---------|-------|---------|--------------------
+ * 0 | 1 | 1 | 1 | e1000_fc_tx_pause
+ *
+ */
+ else if(!(mii_nway_adv_reg & NWAY_AR_PAUSE) &&
+ (mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
+ (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
+ (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
+ hw->fc = e1000_fc_tx_pause;
+ DEBUGOUT("Flow Control = TX PAUSE frames only.\r\n");
+ }
+ /* For transmitting PAUSE frames ONLY.
+ *
+ * LOCAL DEVICE | LINK PARTNER
+ * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
+ *-------|---------|-------|---------|--------------------
+ * 1 | 1 | 0 | 1 | e1000_fc_rx_pause
+ *
+ */
+ else if((mii_nway_adv_reg & NWAY_AR_PAUSE) &&
+ (mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
+ !(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
+ (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
+ hw->fc = e1000_fc_rx_pause;
+ DEBUGOUT("Flow Control = RX PAUSE frames only.\r\n");
+ }
+ /* Per the IEEE spec, at this point flow control should be
+ * disabled. However, we want to consider that we could
+ * be connected to a legacy switch that doesn't advertise
+ * desired flow control, but can be forced on the link
+ * partner. So if we advertised no flow control, that is
+ * what we will resolve to. If we advertised some kind of
+ * receive capability (Rx Pause Only or Full Flow Control)
+ * and the link partner advertised none, we will configure
+ * ourselves to enable Rx Flow Control only. We can do
+ * this safely for two reasons: If the link partner really
+ * didn't want flow control enabled, and we enable Rx, no
+ * harm done since we won't be receiving any PAUSE frames
+ * anyway. If the intent on the link partner was to have
+ * flow control enabled, then by us enabling RX only, we
+ * can at least receive pause frames and process them.
+ * This is a good idea because in most cases, since we are
+ * predominantly a server NIC, more times than not we will
+ * be asked to delay transmission of packets than asking
+ * our link partner to pause transmission of frames.
+ */
+#if 0
+ else if(hw->original_fc == e1000_fc_none ||
+ hw->original_fc == e1000_fc_tx_pause) {
+#else
+ else if(hw->fc == e1000_fc_none)
+ DEBUGOUT("Flow Control = NONE.\r\n");
+ else if(hw->fc == e1000_fc_tx_pause) {
+#endif
+ hw->fc = e1000_fc_none;
+ DEBUGOUT("Flow Control = NONE.\r\n");
+ } else {
+ hw->fc = e1000_fc_rx_pause;
+ DEBUGOUT("Flow Control = RX PAUSE frames only.\r\n");
+ }
+
+ /* Now we need to do one last check... If we auto-
+ * negotiated to HALF DUPLEX, flow control should not be
+ * enabled per IEEE 802.3 spec.
+ */
+ e1000_get_speed_and_duplex(hw, &speed, &duplex);
+
+ if(duplex == HALF_DUPLEX)
+ hw->fc = e1000_fc_none;
+
+ /* Now we call a subroutine to actually force the MAC
+ * controller to use the correct flow control settings.
+ */
+ if((ret_val = e1000_force_mac_fc(hw))) {
+ DEBUGOUT("Error forcing flow control settings\n");
+ return ret_val;
+ }
+ } else {
+ DEBUGOUT("Copper PHY and Auto Neg has not completed.\r\n");
+ }
+ }
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Checks to see if the link status of the hardware has changed.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Called by any function that needs to check the link status of the adapter.
+ *****************************************************************************/
+static int
+e1000_check_for_link(struct e1000_hw *hw)
+{
+ uint32_t rxcw;
+ uint32_t ctrl;
+ uint32_t status;
+ uint32_t rctl;
+ uint32_t signal = 0;
+ int32_t ret_val;
+ uint16_t phy_data;
+ uint16_t lp_capability;
+
+ DEBUGFUNC("e1000_check_for_link");
+
+ /* On adapters with a MAC newer than 82544, SW Defineable pin 1 will be
+ * set when the optics detect a signal. On older adapters, it will be
+ * cleared when there is a signal. This applies to fiber media only.
+ */
+ if(hw->media_type == e1000_media_type_fiber)
+ signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0;
+
+ ctrl = E1000_READ_REG(hw, CTRL);
+ status = E1000_READ_REG(hw, STATUS);
+ rxcw = E1000_READ_REG(hw, RXCW);
+
+ /* If we have a copper PHY then we only want to go out to the PHY
+ * registers to see if Auto-Neg has completed and/or if our link
+ * status has changed. The get_link_status flag will be set if we
+ * receive a Link Status Change interrupt or we have Rx Sequence
+ * Errors.
+ */
+#if 0
+ if((hw->media_type == e1000_media_type_copper) && hw->get_link_status) {
+#else
+ if(hw->media_type == e1000_media_type_copper) {
+#endif
+ /* First we want to see if the MII Status Register reports
+ * link. If so, then we want to get the current speed/duplex
+ * of the PHY.
+ * Read the register twice since the link bit is sticky.
+ */
+ if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+ return ret_val;
+ if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+ return ret_val;
+
+ if(phy_data & MII_SR_LINK_STATUS) {
+#if 0
+ hw->get_link_status = FALSE;
+#endif
+ } else {
+ /* No link detected */
+ return -E1000_ERR_NOLINK;
+ }
+
+ /* We have a M88E1000 PHY and Auto-Neg is enabled. If we
+ * have Si on board that is 82544 or newer, Auto
+ * Speed Detection takes care of MAC speed/duplex
+ * configuration. So we only need to configure Collision
+ * Distance in the MAC. Otherwise, we need to force
+ * speed/duplex on the MAC to the current PHY speed/duplex
+ * settings.
+ */
+ if(hw->mac_type >= e1000_82544)
+ e1000_config_collision_dist(hw);
+ else {
+ if((ret_val = e1000_config_mac_to_phy(hw))) {
+ DEBUGOUT("Error configuring MAC to PHY settings\n");
+ return ret_val;
+ }
+ }
+
+ /* Configure Flow Control now that Auto-Neg has completed. First, we
+ * need to restore the desired flow control settings because we may
+ * have had to re-autoneg with a different link partner.
+ */
+ if((ret_val = e1000_config_fc_after_link_up(hw))) {
+ DEBUGOUT("Error configuring flow control\n");
+ return ret_val;
+ }
+
+ /* At this point we know that we are on copper and we have
+ * auto-negotiated link. These are conditions for checking the link
+ * parter capability register. We use the link partner capability to
+ * determine if TBI Compatibility needs to be turned on or off. If
+ * the link partner advertises any speed in addition to Gigabit, then
+ * we assume that they are GMII-based, and TBI compatibility is not
+ * needed. If no other speeds are advertised, we assume the link
+ * partner is TBI-based, and we turn on TBI Compatibility.
+ */
+ if(hw->tbi_compatibility_en) {
+ if((ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY,
+ &lp_capability)))
+ return ret_val;
+ if(lp_capability & (NWAY_LPAR_10T_HD_CAPS |
+ NWAY_LPAR_10T_FD_CAPS |
+ NWAY_LPAR_100TX_HD_CAPS |
+ NWAY_LPAR_100TX_FD_CAPS |
+ NWAY_LPAR_100T4_CAPS)) {
+ /* If our link partner advertises anything in addition to
+ * gigabit, we do not need to enable TBI compatibility.
+ */
+ if(hw->tbi_compatibility_on) {
+ /* If we previously were in the mode, turn it off. */
+ rctl = E1000_READ_REG(hw, RCTL);
+ rctl &= ~E1000_RCTL_SBP;
+ E1000_WRITE_REG(hw, RCTL, rctl);
+ hw->tbi_compatibility_on = FALSE;
+ }
+ } else {
+ /* If TBI compatibility is was previously off, turn it on. For
+ * compatibility with a TBI link partner, we will store bad
+ * packets. Some frames have an additional byte on the end and
+ * will look like CRC errors to to the hardware.
+ */
+ if(!hw->tbi_compatibility_on) {
+ hw->tbi_compatibility_on = TRUE;
+ rctl = E1000_READ_REG(hw, RCTL);
+ rctl |= E1000_RCTL_SBP;
+ E1000_WRITE_REG(hw, RCTL, rctl);
+ }
+ }
+ }
+ }
+ /* If we don't have link (auto-negotiation failed or link partner cannot
+ * auto-negotiate), the cable is plugged in (we have signal), and our
+ * link partner is not trying to auto-negotiate with us (we are receiving
+ * idles or data), we need to force link up. We also need to give
+ * auto-negotiation time to complete, in case the cable was just plugged
+ * in. The autoneg_failed flag does this.
+ */
+ else if((((hw->media_type == e1000_media_type_fiber) &&
+ ((ctrl & E1000_CTRL_SWDPIN1) == signal)) ||
+ (hw->media_type == e1000_media_type_internal_serdes)) &&
+ (!(status & E1000_STATUS_LU)) &&
+ (!(rxcw & E1000_RXCW_C))) {
+ if(hw->autoneg_failed == 0) {
+ hw->autoneg_failed = 1;
+ return 0;
+ }
+ DEBUGOUT("NOT RXing /C/, disable AutoNeg and force link.\r\n");
+
+ /* Disable auto-negotiation in the TXCW register */
+ E1000_WRITE_REG(hw, TXCW, (hw->txcw & ~E1000_TXCW_ANE));
+
+ /* Force link-up and also force full-duplex. */
+ ctrl = E1000_READ_REG(hw, CTRL);
+ ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD);
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+
+ /* Configure Flow Control after forcing link up. */
+ if((ret_val = e1000_config_fc_after_link_up(hw))) {
+ DEBUGOUT("Error configuring flow control\n");
+ return ret_val;
+ }
+ }
+ /* If we are forcing link and we are receiving /C/ ordered sets, re-enable
+ * auto-negotiation in the TXCW register and disable forced link in the
+ * Device Control register in an attempt to auto-negotiate with our link
+ * partner.
+ */
+ else if(((hw->media_type == e1000_media_type_fiber) ||
+ (hw->media_type == e1000_media_type_internal_serdes)) &&
+ (ctrl & E1000_CTRL_SLU) &&
+ (rxcw & E1000_RXCW_C)) {
+ DEBUGOUT("RXing /C/, enable AutoNeg and stop forcing link.\r\n");
+ E1000_WRITE_REG(hw, TXCW, hw->txcw);
+ E1000_WRITE_REG(hw, CTRL, (ctrl & ~E1000_CTRL_SLU));
+ }
+#if 0
+ /* If we force link for non-auto-negotiation switch, check link status
+ * based on MAC synchronization for internal serdes media type.
+ */
+ else if((hw->media_type == e1000_media_type_internal_serdes) &&
+ !(E1000_TXCW_ANE & E1000_READ_REG(hw, TXCW))) {
+ /* SYNCH bit and IV bit are sticky. */
+ udelay(10);
+ if(E1000_RXCW_SYNCH & E1000_READ_REG(hw, RXCW)) {
+ if(!(rxcw & E1000_RXCW_IV)) {
+ hw->serdes_link_down = FALSE;
+ DEBUGOUT("SERDES: Link is up.\n");
+ }
+ } else {
+ hw->serdes_link_down = TRUE;
+ DEBUGOUT("SERDES: Link is down.\n");
+ }
+ }
+#endif
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+ * Detects the current speed and duplex settings of the hardware.
+ *
+ * hw - Struct containing variables accessed by shared code
+ * speed - Speed of the connection
+ * duplex - Duplex setting of the connection
+ *****************************************************************************/
+static void
+e1000_get_speed_and_duplex(struct e1000_hw *hw,
+ uint16_t *speed,
+ uint16_t *duplex)
+{
+ uint32_t status;
+
+ DEBUGFUNC("e1000_get_speed_and_duplex");
+
+ if(hw->mac_type >= e1000_82543) {
+ status = E1000_READ_REG(hw, STATUS);
+ if(status & E1000_STATUS_SPEED_1000) {
+ *speed = SPEED_1000;
+ DEBUGOUT("1000 Mbs, ");
+ } else if(status & E1000_STATUS_SPEED_100) {
+ *speed = SPEED_100;
+ DEBUGOUT("100 Mbs, ");
+ } else {
+ *speed = SPEED_10;
+ DEBUGOUT("10 Mbs, ");
+ }
+
+ if(status & E1000_STATUS_FD) {
+ *duplex = FULL_DUPLEX;
+ DEBUGOUT("Full Duplex\r\n");
+ } else {
+ *duplex = HALF_DUPLEX;
+ DEBUGOUT(" Half Duplex\r\n");
+ }
+ } else {
+ DEBUGOUT("1000 Mbs, Full Duplex\r\n");
+ *speed = SPEED_1000;
+ *duplex = FULL_DUPLEX;
+ }
+}
+
+/******************************************************************************
+* Blocks until autoneg completes or times out (~4.5 seconds)
+*
+* hw - Struct containing variables accessed by shared code
+******************************************************************************/
+static int
+e1000_wait_autoneg(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ uint16_t i;
+ uint16_t phy_data;
+
+ DEBUGFUNC("e1000_wait_autoneg");
+ DEBUGOUT("Waiting for Auto-Neg to complete.\n");
+
+ /* We will wait for autoneg to complete or 4.5 seconds to expire. */
+ for(i = PHY_AUTO_NEG_TIME; i > 0; i--) {
+ /* Read the MII Status Register and wait for Auto-Neg
+ * Complete bit to be set.
+ */
+ if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+ return ret_val;
+ if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data)))
+ return ret_val;
+ if(phy_data & MII_SR_AUTONEG_COMPLETE) {
+ DEBUGOUT("Auto-Neg complete.\n");
+ return E1000_SUCCESS;
+ }
+ mdelay(100);
+ }
+ DEBUGOUT("Auto-Neg timedout.\n");
+ return -E1000_ERR_TIMEOUT;
+}
+
+/******************************************************************************
+* Raises the Management Data Clock
+*
+* hw - Struct containing variables accessed by shared code
+* ctrl - Device control register's current value
+******************************************************************************/
+static void
+e1000_raise_mdi_clk(struct e1000_hw *hw,
+ uint32_t *ctrl)
+{
+ /* Raise the clock input to the Management Data Clock (by setting the MDC
+ * bit), and then delay 10 microseconds.
+ */
+ E1000_WRITE_REG(hw, CTRL, (*ctrl | E1000_CTRL_MDC));
+ E1000_WRITE_FLUSH(hw);
+ udelay(10);
+}
+
+/******************************************************************************
+* Lowers the Management Data Clock
+*
+* hw - Struct containing variables accessed by shared code
+* ctrl - Device control register's current value
+******************************************************************************/
+static void
+e1000_lower_mdi_clk(struct e1000_hw *hw,
+ uint32_t *ctrl)
+{
+ /* Lower the clock input to the Management Data Clock (by clearing the MDC
+ * bit), and then delay 10 microseconds.
+ */
+ E1000_WRITE_REG(hw, CTRL, (*ctrl & ~E1000_CTRL_MDC));
+ E1000_WRITE_FLUSH(hw);
+ udelay(10);
+}
+
+/******************************************************************************
+* Shifts data bits out to the PHY
+*
+* hw - Struct containing variables accessed by shared code
+* data - Data to send out to the PHY
+* count - Number of bits to shift out
+*
+* Bits are shifted out in MSB to LSB order.
+******************************************************************************/
+static void
+e1000_shift_out_mdi_bits(struct e1000_hw *hw,
+ uint32_t data,
+ uint16_t count)
+{
+ uint32_t ctrl;
+ uint32_t mask;
+
+ /* We need to shift "count" number of bits out to the PHY. So, the value
+ * in the "data" parameter will be shifted out to the PHY one bit at a
+ * time. In order to do this, "data" must be broken down into bits.
+ */
+ mask = 0x01;
+ mask <<= (count - 1);
+
+ ctrl = E1000_READ_REG(hw, CTRL);
+
+ /* Set MDIO_DIR and MDC_DIR direction bits to be used as output pins. */
+ ctrl |= (E1000_CTRL_MDIO_DIR | E1000_CTRL_MDC_DIR);
+
+ while(mask) {
+ /* A "1" is shifted out to the PHY by setting the MDIO bit to "1" and
+ * then raising and lowering the Management Data Clock. A "0" is
+ * shifted out to the PHY by setting the MDIO bit to "0" and then
+ * raising and lowering the clock.
+ */
+ if(data & mask) ctrl |= E1000_CTRL_MDIO;
+ else ctrl &= ~E1000_CTRL_MDIO;
+
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+ E1000_WRITE_FLUSH(hw);
+
+ udelay(10);
+
+ e1000_raise_mdi_clk(hw, &ctrl);
+ e1000_lower_mdi_clk(hw, &ctrl);
+
+ mask = mask >> 1;
+ }
+}
+
+/******************************************************************************
+* Shifts data bits in from the PHY
+*
+* hw - Struct containing variables accessed by shared code
+*
+* Bits are shifted in in MSB to LSB order.
+******************************************************************************/
+static uint16_t
+e1000_shift_in_mdi_bits(struct e1000_hw *hw)
+{
+ uint32_t ctrl;
+ uint16_t data = 0;
+ uint8_t i;
+
+ /* In order to read a register from the PHY, we need to shift in a total
+ * of 18 bits from the PHY. The first two bit (turnaround) times are used
+ * to avoid contention on the MDIO pin when a read operation is performed.
+ * These two bits are ignored by us and thrown away. Bits are "shifted in"
+ * by raising the input to the Management Data Clock (setting the MDC bit),
+ * and then reading the value of the MDIO bit.
+ */
+ ctrl = E1000_READ_REG(hw, CTRL);
+
+ /* Clear MDIO_DIR (SWDPIO1) to indicate this bit is to be used as input. */
+ ctrl &= ~E1000_CTRL_MDIO_DIR;
+ ctrl &= ~E1000_CTRL_MDIO;
+
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+ E1000_WRITE_FLUSH(hw);
+
+ /* Raise and Lower the clock before reading in the data. This accounts for
+ * the turnaround bits. The first clock occurred when we clocked out the
+ * last bit of the Register Address.
+ */
+ e1000_raise_mdi_clk(hw, &ctrl);
+ e1000_lower_mdi_clk(hw, &ctrl);
+
+ for(data = 0, i = 0; i < 16; i++) {
+ data = data << 1;
+ e1000_raise_mdi_clk(hw, &ctrl);
+ ctrl = E1000_READ_REG(hw, CTRL);
+ /* Check to see if we shifted in a "1". */
+ if(ctrl & E1000_CTRL_MDIO) data |= 1;
+ e1000_lower_mdi_clk(hw, &ctrl);
+ }
+
+ e1000_raise_mdi_clk(hw, &ctrl);
+ e1000_lower_mdi_clk(hw, &ctrl);
+
+ return data;
+}
+
+/*****************************************************************************
+* Reads the value from a PHY register, if the value is on a specific non zero
+* page, sets the page first.
+*
+* hw - Struct containing variables accessed by shared code
+* reg_addr - address of the PHY register to read
+******************************************************************************/
+static int
+e1000_read_phy_reg(struct e1000_hw *hw,
+ uint32_t reg_addr,
+ uint16_t *phy_data)
+{
+ uint32_t ret_val;
+
+ DEBUGFUNC("e1000_read_phy_reg");
+
+ if(hw->phy_type == e1000_phy_igp &&
+ (reg_addr > MAX_PHY_MULTI_PAGE_REG)) {
+ if((ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT,
+ (uint16_t)reg_addr)))
+ return ret_val;
+ }
+
+ ret_val = e1000_read_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT & reg_addr,
+ phy_data);
+
+ return ret_val;
+}
+
+static int
+e1000_read_phy_reg_ex(struct e1000_hw *hw,
+ uint32_t reg_addr,
+ uint16_t *phy_data)
+{
+ uint32_t i;
+ uint32_t mdic = 0;
+ const uint32_t phy_addr = 1;
+
+ DEBUGFUNC("e1000_read_phy_reg_ex");
+
+ if(reg_addr > MAX_PHY_REG_ADDRESS) {
+ DEBUGOUT1("PHY Address %d is out of range\n", reg_addr);
+ return -E1000_ERR_PARAM;
+ }
+
+ if(hw->mac_type > e1000_82543) {
+ /* Set up Op-code, Phy Address, and register address in the MDI
+ * Control register. The MAC will take care of interfacing with the
+ * PHY to retrieve the desired data.
+ */
+ mdic = ((reg_addr << E1000_MDIC_REG_SHIFT) |
+ (phy_addr << E1000_MDIC_PHY_SHIFT) |
+ (E1000_MDIC_OP_READ));
+
+ E1000_WRITE_REG(hw, MDIC, mdic);
+
+ /* Poll the ready bit to see if the MDI read completed */
+ for(i = 0; i < 64; i++) {
+ udelay(50);
+ mdic = E1000_READ_REG(hw, MDIC);
+ if(mdic & E1000_MDIC_READY) break;
+ }
+ if(!(mdic & E1000_MDIC_READY)) {
+ DEBUGOUT("MDI Read did not complete\n");
+ return -E1000_ERR_PHY;
+ }
+ if(mdic & E1000_MDIC_ERROR) {
+ DEBUGOUT("MDI Error\n");
+ return -E1000_ERR_PHY;
+ }
+ *phy_data = (uint16_t) mdic;
+ } else {
+ /* We must first send a preamble through the MDIO pin to signal the
+ * beginning of an MII instruction. This is done by sending 32
+ * consecutive "1" bits.
+ */
+ e1000_shift_out_mdi_bits(hw, PHY_PREAMBLE, PHY_PREAMBLE_SIZE);
+
+ /* Now combine the next few fields that are required for a read
+ * operation. We use this method instead of calling the
+ * e1000_shift_out_mdi_bits routine five different times. The format of
+ * a MII read instruction consists of a shift out of 14 bits and is
+ * defined as follows:
+ * <Preamble><SOF><Op Code><Phy Addr><Reg Addr>
+ * followed by a shift in of 18 bits. This first two bits shifted in
+ * are TurnAround bits used to avoid contention on the MDIO pin when a
+ * READ operation is performed. These two bits are thrown away
+ * followed by a shift in of 16 bits which contains the desired data.
+ */
+ mdic = ((reg_addr) | (phy_addr << 5) |
+ (PHY_OP_READ << 10) | (PHY_SOF << 12));
+
+ e1000_shift_out_mdi_bits(hw, mdic, 14);
+
+ /* Now that we've shifted out the read command to the MII, we need to
+ * "shift in" the 16-bit value (18 total bits) of the requested PHY
+ * register address.
+ */
+ *phy_data = e1000_shift_in_mdi_bits(hw);
+ }
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Writes a value to a PHY register
+*
+* hw - Struct containing variables accessed by shared code
+* reg_addr - address of the PHY register to write
+* data - data to write to the PHY
+******************************************************************************/
+static int
+e1000_write_phy_reg(struct e1000_hw *hw,
+ uint32_t reg_addr,
+ uint16_t phy_data)
+{
+ uint32_t ret_val;
+
+ DEBUGFUNC("e1000_write_phy_reg");
+
+ if(hw->phy_type == e1000_phy_igp &&
+ (reg_addr > MAX_PHY_MULTI_PAGE_REG)) {
+ if((ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT,
+ (uint16_t)reg_addr)))
+ return ret_val;
+ }
+
+ ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT & reg_addr,
+ phy_data);
+
+ return ret_val;
+}
+
+static int
+e1000_write_phy_reg_ex(struct e1000_hw *hw,
+ uint32_t reg_addr,
+ uint16_t phy_data)
+{
+ uint32_t i;
+ uint32_t mdic = 0;
+ const uint32_t phy_addr = 1;
+
+ DEBUGFUNC("e1000_write_phy_reg_ex");
+
+ if(reg_addr > MAX_PHY_REG_ADDRESS) {
+ DEBUGOUT1("PHY Address %d is out of range\n", reg_addr);
+ return -E1000_ERR_PARAM;
+ }
+
+ if(hw->mac_type > e1000_82543) {
+ /* Set up Op-code, Phy Address, register address, and data intended
+ * for the PHY register in the MDI Control register. The MAC will take
+ * care of interfacing with the PHY to send the desired data.
+ */
+ mdic = (((uint32_t) phy_data) |
+ (reg_addr << E1000_MDIC_REG_SHIFT) |
+ (phy_addr << E1000_MDIC_PHY_SHIFT) |
+ (E1000_MDIC_OP_WRITE));
+
+ E1000_WRITE_REG(hw, MDIC, mdic);
+
+ /* Poll the ready bit to see if the MDI read completed */
+ for(i = 0; i < 640; i++) {
+ udelay(5);
+ mdic = E1000_READ_REG(hw, MDIC);
+ if(mdic & E1000_MDIC_READY) break;
+ }
+ if(!(mdic & E1000_MDIC_READY)) {
+ DEBUGOUT("MDI Write did not complete\n");
+ return -E1000_ERR_PHY;
+ }
+ } else {
+ /* We'll need to use the SW defined pins to shift the write command
+ * out to the PHY. We first send a preamble to the PHY to signal the
+ * beginning of the MII instruction. This is done by sending 32
+ * consecutive "1" bits.
+ */
+ e1000_shift_out_mdi_bits(hw, PHY_PREAMBLE, PHY_PREAMBLE_SIZE);
+
+ /* Now combine the remaining required fields that will indicate a
+ * write operation. We use this method instead of calling the
+ * e1000_shift_out_mdi_bits routine for each field in the command. The
+ * format of a MII write instruction is as follows:
+ * <Preamble><SOF><Op Code><Phy Addr><Reg Addr><Turnaround><Data>.
+ */
+ mdic = ((PHY_TURNAROUND) | (reg_addr << 2) | (phy_addr << 7) |
+ (PHY_OP_WRITE << 12) | (PHY_SOF << 14));
+ mdic <<= 16;
+ mdic |= (uint32_t) phy_data;
+
+ e1000_shift_out_mdi_bits(hw, mdic, 32);
+ }
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Returns the PHY to the power-on reset state
+*
+* hw - Struct containing variables accessed by shared code
+******************************************************************************/
+static void
+e1000_phy_hw_reset(struct e1000_hw *hw)
+{
+ uint32_t ctrl, ctrl_ext;
+
+ DEBUGFUNC("e1000_phy_hw_reset");
+
+ DEBUGOUT("Resetting Phy...\n");
+
+ if(hw->mac_type > e1000_82543) {
+ /* Read the device control register and assert the E1000_CTRL_PHY_RST
+ * bit. Then, take it out of reset.
+ */
+ ctrl = E1000_READ_REG(hw, CTRL);
+ E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PHY_RST);
+ E1000_WRITE_FLUSH(hw);
+ mdelay(10);
+ E1000_WRITE_REG(hw, CTRL, ctrl);
+ E1000_WRITE_FLUSH(hw);
+ } else {
+ /* Read the Extended Device Control Register, assert the PHY_RESET_DIR
+ * bit to put the PHY into reset. Then, take it out of reset.
+ */
+ ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
+ ctrl_ext |= E1000_CTRL_EXT_SDP4_DIR;
+ ctrl_ext &= ~E1000_CTRL_EXT_SDP4_DATA;
+ E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
+ E1000_WRITE_FLUSH(hw);
+ mdelay(10);
+ ctrl_ext |= E1000_CTRL_EXT_SDP4_DATA;
+ E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
+ E1000_WRITE_FLUSH(hw);
+ }
+ udelay(150);
+}
+
+/******************************************************************************
+* Resets the PHY
+*
+* hw - Struct containing variables accessed by shared code
+*
+* Sets bit 15 of the MII Control regiser
+******************************************************************************/
+static int
+e1000_phy_reset(struct e1000_hw *hw)
+{
+ int32_t ret_val;
+ uint16_t phy_data;
+
+ DEBUGFUNC("e1000_phy_reset");
+
+ if(hw->mac_type != e1000_82541_rev_2) {
+ if((ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data)))
+ return ret_val;
+
+ phy_data |= MII_CR_RESET;
+ if((ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data)))
+ return ret_val;
+
+ udelay(1);
+ } else e1000_phy_hw_reset(hw);
+
+ if(hw->phy_type == e1000_phy_igp)
+ e1000_phy_init_script(hw);
+
+ return E1000_SUCCESS;
+}
+
+/******************************************************************************
+* Probes the expected PHY address for known PHY IDs
+*
+* hw - Struct containing variables accessed by shared code
+******************************************************************************/
+static int
+e1000_detect_gig_phy(struct e1000_hw *hw)
+{
+ int32_t phy_init_status, ret_val;
+ uint16_t phy_id_high, phy_id_low;
+ boolean_t match = FALSE;
+
+ DEBUGFUNC("e1000_detect_gig_phy");
+
+ /* Read the PHY ID Registers to identify which PHY is onboard. */
+ if((ret_val = e1000_read_phy_reg(hw, PHY_ID1, &phy_id_high)))
+ return ret_val;
+
+ hw->phy_id = (uint32_t) (phy_id_high << 16);
+ udelay(20);
+ if((ret_val = e1000_read_phy_reg(hw, PHY_ID2, &phy_id_low)))
+ return ret_val;
+
+ hw->phy_id |= (uint32_t) (phy_id_low & PHY_REVISION_MASK);
+#ifdef LINUX_DRIVER
+ hw->phy_revision = (uint32_t) phy_id_low & ~PHY_REVISION_MASK;
+#endif
+
+ switch(hw->mac_type) {
+ case e1000_82543:
+ if(hw->phy_id == M88E1000_E_PHY_ID) match = TRUE;
+ break;
+ case e1000_82544:
+ if(hw->phy_id == M88E1000_I_PHY_ID) match = TRUE;
+ break;
+ case e1000_82540:
+ case e1000_82545:
+ case e1000_82545_rev_3:
+ case e1000_82546:
+ case e1000_82546_rev_3:
+ if(hw->phy_id == M88E1011_I_PHY_ID) match = TRUE;
+ break;
+ case e1000_82541:
+ case e1000_82541_rev_2:
+ case e1000_82547:
+ case e1000_82547_rev_2:
+ if(hw->phy_id == IGP01E1000_I_PHY_ID) match = TRUE;
+ break;
+ default:
+ DEBUGOUT1("Invalid MAC type %d\n", hw->mac_type);
+ return -E1000_ERR_CONFIG;
+ }
+ phy_init_status = e1000_set_phy_type(hw);
+
+ if ((match) && (phy_init_status == E1000_SUCCESS)) {
+ DEBUGOUT1("PHY ID 0x%X detected\n", hw->phy_id);
+ return E1000_SUCCESS;
+ }
+ DEBUGOUT1("Invalid PHY ID 0x%X\n", hw->phy_id);
+ return -E1000_ERR_PHY;
+}
+
+/******************************************************************************
+ * Sets up eeprom variables in the hw struct. Must be called after mac_type
+ * is configured.
+ *
+ * hw - Struct containing variables accessed by shared code
+ *****************************************************************************/
+static void
+e1000_init_eeprom_params(struct e1000_hw *hw)
+{
+ struct e1000_eeprom_info *eeprom = &hw->eeprom;
+ uint32_t eecd = E1000_READ_REG(hw, EECD);
+ uint16_t eeprom_size;
+
+ DEBUGFUNC("e1000_init_eeprom_params");
+
+ switch (hw->mac_type) {
+ case e1000_82542_rev2_0:
+ case e1000_82542_rev2_1:
+ case e1000_82543:
+ case e1000_82544:
+ eeprom->type = e1000_eeprom_microwire;
+ eeprom->word_size = 64;
+ eeprom->opcode_bits = 3;
+ eeprom->address_bits = 6;
+ eeprom->delay_usec = 50;
+ break;
+ case e1000_82540:
+ case e1000_82545:
+ case e1000_82545_rev_3:
+ case e1000_82546:
+ case e1000_82546_rev_3:
+ eeprom->type = e1000_eeprom_microwire;
+ eeprom->opcode_bits = 3;
+ eeprom->delay_usec = 50;
+ if(eecd & E1000_EECD_SIZE) {
+ eeprom->word_size = 256;
+ eeprom->address_bits = 8;
+ } else {
+ eeprom->word_size = 64;
+ eeprom->address_bits = 6;
+ }
+ break;
+ case e1000_82541:
+ case e1000_82541_rev_2:
+ case e1000_82547:
+ case e1000_82547_rev_2:
+ if (eecd & E1000_EECD_TYPE) {
+ eeprom->type = e1000_eeprom_spi;
+ if (eecd & E1000_EECD_ADDR_BITS) {
+ eeprom->page_size = 32;
+ eeprom->address_bits = 16;
+ } else {
+ eeprom->page_size = 8;
+ eeprom->address_bits = 8;
+ }
+ } else {
+ eeprom->type = e1000_eeprom_microwire;
+ eeprom->opcode_bits = 3;
+ eeprom->delay_usec = 50;
+ if (eecd & E1000_EECD_ADDR_BITS) {
+ eeprom->word_size = 256;
+ eeprom->address_bits = 8;
+ } else {
+ eeprom->word_size = 64;
+ eeprom->address_bits = 6;
+ }
+ }
+ break;
+ default:
+ eeprom->type = e1000_eeprom_spi;
+ if (eecd & E1000_EECD_ADDR_BITS) {
+ eeprom->page_size = 32;
+ eeprom->address_bits = 16;
+ } else {
+ eeprom->page_size = 8;
+ eeprom->address_bits = 8;
+ }
+ break;
+ }
+
+ if (eeprom->type == e1000_eeprom_spi) {
+ eeprom->opcode_bits = 8;
+ eeprom->delay_usec = 1;
+ eeprom->word_size = 64;
+ if (e1000_read_eeprom(hw, EEPROM_CFG, 1, &eeprom_size) == 0) {
+ eeprom_size &= EEPROM_SIZE_MASK;
+
+ switch (eeprom_size) {
+ case EEPROM_SIZE_16KB:
+ eeprom->word_size = 8192;
+ break;
+ case EEPROM_SIZE_8KB:
+ eeprom->word_size = 4096;
+ break;
+ case EEPROM_SIZE_4KB:
+ eeprom->word_size = 2048;
+ break;
+ case EEPROM_SIZE_2KB:
+ eeprom->word_size = 1024;
+ break;
+ case EEPROM_SIZE_1KB:
+ eeprom->word_size = 512;
+ break;
+ case EEPROM_SIZE_512B:
+ eeprom->word_size = 256;
+ break;
+ case EEPROM_SIZE_128B:
+ default:
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * e1000_reset - Reset the adapter
+ */
+
+static int
+e1000_reset(struct e1000_hw *hw)
+{
+ uint32_t pba;
+ /* Repartition Pba for greater than 9k mtu
+ * To take effect CTRL.RST is required.
+ */
+
+ if(hw->mac_type < e1000_82547) {
+ pba = E1000_PBA_48K;
+ } else {
+ pba = E1000_PBA_30K;
+ }
+ E1000_WRITE_REG(hw, PBA, pba);
+
+ /* flow control settings */
+#if 0
+ hw->fc_high_water = FC_DEFAULT_HI_THRESH;
+ hw->fc_low_water = FC_DEFAULT_LO_THRESH;
+ hw->fc_pause_time = FC_DEFAULT_TX_TIMER;
+ hw->fc_send_xon = 1;
+ hw->fc = hw->original_fc;
+#endif
+
+ e1000_reset_hw(hw);
+ if(hw->mac_type >= e1000_82544)
+ E1000_WRITE_REG(hw, WUC, 0);
+ return e1000_init_hw(hw);
+}
+
+/**
+ * e1000_sw_init - Initialize general software structures (struct e1000_adapter)
+ * @adapter: board private structure to initialize
+ *
+ * e1000_sw_init initializes the Adapter private data structure.
+ * Fields are initialized based on PCI device information and
+ * OS network device settings (MTU size).
+ **/
+
+static int
+e1000_sw_init(struct pci_device *pdev, struct e1000_hw *hw)
+{
+ int result;
+
+ /* PCI config space info */
+ pci_read_config_word(pdev, PCI_VENDOR_ID, &hw->vendor_id);
+ pci_read_config_word(pdev, PCI_DEVICE_ID, &hw->device_id);
+ pci_read_config_byte(pdev, PCI_REVISION, &hw->revision_id);
+#if 0
+ pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID,
+ &hw->subsystem_vendor_id);
+ pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &hw->subsystem_id);
+#endif
+
+ pci_read_config_word(pdev, PCI_COMMAND, &hw->pci_cmd_word);
+
+ /* identify the MAC */
+
+ result = e1000_set_mac_type(hw);
+ if (result) {
+ E1000_ERR("Unknown MAC Type\n");
+ return result;
+ }
+
+ /* initialize eeprom parameters */
+
+ e1000_init_eeprom_params(hw);
+
+#if 0
+ if((hw->mac_type == e1000_82541) ||
+ (hw->mac_type == e1000_82547) ||
+ (hw->mac_type == e1000_82541_rev_2) ||
+ (hw->mac_type == e1000_82547_rev_2))
+ hw->phy_init_script = 1;
+#endif
+
+ e1000_set_media_type(hw);
+
+#if 0
+ if(hw->mac_type < e1000_82543)
+ hw->report_tx_early = 0;
+ else
+ hw->report_tx_early = 1;
+
+ hw->wait_autoneg_complete = FALSE;
+#endif
+ hw->tbi_compatibility_en = TRUE;
+#if 0
+ hw->adaptive_ifs = TRUE;
+
+ /* Copper options */
+
+ if(hw->media_type == e1000_media_type_copper) {
+ hw->mdix = AUTO_ALL_MODES;
+ hw->disable_polarity_correction = FALSE;
+ hw->master_slave = E1000_MASTER_SLAVE;
+ }
+#endif
+ return E1000_SUCCESS;
+}
+
+static void fill_rx (void)
+{
+ struct e1000_rx_desc *rd;
+ rx_last = rx_tail;
+ rd = rx_base + rx_tail;
+ rx_tail = (rx_tail + 1) % 8;
+ memset (rd, 0, 16);
+ rd->buffer_addr = virt_to_bus(&packet);
+ E1000_WRITE_REG (&hw, RDT, rx_tail);
+}
+
+static void init_descriptor (void)
+{
+ unsigned long ptr;
+ unsigned long tctl;
+
+ ptr = virt_to_phys(tx_pool);
+ if (ptr & 0xf)
+ ptr = (ptr + 0x10) & (~0xf);
+
+ tx_base = phys_to_virt(ptr);
+
+ E1000_WRITE_REG (&hw, TDBAL, virt_to_bus(tx_base));
+ E1000_WRITE_REG (&hw, TDBAH, 0);
+ E1000_WRITE_REG (&hw, TDLEN, 128);
+
+ /* Setup the HW Tx Head and Tail descriptor pointers */
+
+ E1000_WRITE_REG (&hw, TDH, 0);
+ E1000_WRITE_REG (&hw, TDT, 0);
+ tx_tail = 0;
+
+ /* Program the Transmit Control Register */
+
+#ifdef LINUX_DRIVER_TCTL
+ tctl = E1000_READ_REG(&hw, TCTL);
+
+ tctl &= ~E1000_TCTL_CT;
+ tctl |= E1000_TCTL_EN | E1000_TCTL_PSP |
+ (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT);
+#else
+ tctl = E1000_TCTL_PSP | E1000_TCTL_EN |
+ (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT) |
+ (E1000_HDX_COLLISION_DISTANCE << E1000_COLD_SHIFT);
+#endif
+
+ E1000_WRITE_REG (&hw, TCTL, tctl);
+
+ e1000_config_collision_dist(&hw);
+
+
+ rx_tail = 0;
+ /* disable receive */
+ E1000_WRITE_REG (&hw, RCTL, 0);
+ ptr = virt_to_phys(rx_pool);
+ if (ptr & 0xf)
+ ptr = (ptr + 0x10) & (~0xf);
+ rx_base = phys_to_virt(ptr);
+
+ /* Setup the Base and Length of the Rx Descriptor Ring */
+
+ E1000_WRITE_REG (&hw, RDBAL, virt_to_bus(rx_base));
+ E1000_WRITE_REG (&hw, RDBAH, 0);
+
+ E1000_WRITE_REG (&hw, RDLEN, 128);
+
+ /* Setup the HW Rx Head and Tail Descriptor Pointers */
+ E1000_WRITE_REG (&hw, RDH, 0);
+ E1000_WRITE_REG (&hw, RDT, 0);
+
+ E1000_WRITE_REG (&hw, RCTL,
+ E1000_RCTL_EN |
+ E1000_RCTL_BAM |
+ E1000_RCTL_SZ_2048 |
+ E1000_RCTL_MPE);
+ fill_rx();
+}
+
+
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int
+e1000_poll (struct nic *nic, int retrieve)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+ struct e1000_rx_desc *rd;
+ uint32_t icr;
+
+ rd = rx_base + rx_last;
+ if (!rd->status & E1000_RXD_STAT_DD)
+ return 0;
+
+ if ( ! retrieve ) return 1;
+
+ // printf("recv: packet %! -> %! len=%d \n", packet+6, packet,rd->Length);
+ memcpy (nic->packet, packet, rd->length);
+ nic->packetlen = rd->length;
+ fill_rx ();
+
+ /* Acknowledge interrupt. */
+ icr = E1000_READ_REG(&hw, ICR);
+
+ return 1;
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void
+e1000_transmit (struct nic *nic, const char *d, /* Destination */
+ unsigned int type, /* Type */
+ unsigned int size, /* size */
+ const char *p) /* Packet */
+{
+ /* send the packet to destination */
+ struct eth_hdr {
+ unsigned char dst_addr[ETH_ALEN];
+ unsigned char src_addr[ETH_ALEN];
+ unsigned short type;
+ } hdr;
+ struct e1000_tx_desc *txhd; /* header */
+ struct e1000_tx_desc *txp; /* payload */
+ DEBUGFUNC("send");
+
+ memcpy (&hdr.dst_addr, d, ETH_ALEN);
+ memcpy (&hdr.src_addr, nic->node_addr, ETH_ALEN);
+
+ hdr.type = htons (type);
+ txhd = tx_base + tx_tail;
+ tx_tail = (tx_tail + 1) % 8;
+ txp = tx_base + tx_tail;
+ tx_tail = (tx_tail + 1) % 8;
+
+ txhd->buffer_addr = virt_to_bus (&hdr);
+ txhd->lower.data = sizeof (hdr);
+ txhd->upper.data = 0;
+
+ txp->buffer_addr = virt_to_bus(p);
+ txp->lower.data = E1000_TXD_CMD_RPS | E1000_TXD_CMD_EOP | E1000_TXD_CMD_IFCS | size;
+ txp->upper.data = 0;
+
+ E1000_WRITE_REG (&hw, TDT, tx_tail);
+ while (!(txp->upper.data & E1000_TXD_STAT_DD)) {
+ udelay(10); /* give the nic a chance to write to the register */
+ poll_interruptions();
+ }
+ DEBUGFUNC("send end");
+}
+
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void e1000_disable (struct dev *dev __unused)
+{
+ /* Clear the transmit ring */
+ E1000_WRITE_REG (&hw, TDH, 0);
+ E1000_WRITE_REG (&hw, TDT, 0);
+
+ /* Clear the receive ring */
+ E1000_WRITE_REG (&hw, RDH, 0);
+ E1000_WRITE_REG (&hw, RDT, 0);
+
+ /* put the card in its initial state */
+ switch(hw.mac_type) {
+ case e1000_82544:
+ case e1000_82540:
+ case e1000_82545:
+ case e1000_82546:
+ case e1000_82541:
+ case e1000_82541_rev_2:
+ /* These controllers can't ack the 64-bit write when issuing the
+ * reset, so use IO-mapping as a workaround to issue the reset */
+ E1000_WRITE_REG_IO(&hw, CTRL, E1000_CTRL_RST);
+ break;
+ case e1000_82545_rev_3:
+ case e1000_82546_rev_3:
+ /* Reset is performed on a shadow of the control register */
+ E1000_WRITE_REG(&hw, CTRL_DUP, E1000_CTRL_RST);
+ break;
+ default:
+ E1000_WRITE_REG(&hw, CTRL, E1000_CTRL_RST);
+ break;
+ }
+
+ /* Turn off the ethernet interface */
+ E1000_WRITE_REG (&hw, RCTL, 0);
+ E1000_WRITE_REG (&hw, TCTL, 0);
+ mdelay (10);
+
+ /* Unmap my window to the device */
+ iounmap(hw.hw_addr);
+}
+
+/**************************************************************************
+IRQ - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void e1000_irq(struct nic *nic __unused, irq_action_t action)
+{
+ switch ( action ) {
+ case DISABLE :
+ E1000_WRITE_REG(&hw, IMC, ~0);
+ E1000_WRITE_FLUSH(&hw);
+ break;
+ case ENABLE :
+ E1000_WRITE_REG(&hw, IMS,
+ E1000_IMS_RXT0 | E1000_IMS_RXSEQ);
+ E1000_WRITE_FLUSH(&hw);
+ break;
+ case FORCE :
+ E1000_WRITE_REG(&hw, ICS, E1000_ICS_RXT0);
+ break;
+ }
+}
+
+#define IORESOURCE_IO 0x00000100 /* Resource type */
+#define BAR_0 0
+#define BAR_1 1
+#define BAR_5 5
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+You should omit the last argument struct pci_device * for a non-PCI NIC
+***************************************************************************/
+static int e1000_probe(struct dev *dev, struct pci_device *p)
+{
+ struct nic *nic = (struct nic *)dev;
+ unsigned long mmio_start, mmio_len;
+ int ret_val, i;
+
+ if (p == 0)
+ return 0;
+ /* Initialize hw with default values */
+ memset(&hw, 0, sizeof(hw));
+ hw.pdev = p;
+
+#if 1
+ /* Are these variables needed? */
+ hw.fc = e1000_fc_none;
+#if 0
+ hw.original_fc = e1000_fc_none;
+#endif
+ hw.autoneg_failed = 0;
+#if 0
+ hw.get_link_status = TRUE;
+#endif
+#endif
+
+ mmio_start = pci_bar_start(p, PCI_BASE_ADDRESS_0);
+ mmio_len = pci_bar_size(p, PCI_BASE_ADDRESS_0);
+ hw.hw_addr = ioremap(mmio_start, mmio_len);
+
+ for(i = BAR_1; i <= BAR_5; i++) {
+ if(pci_bar_size(p, i) == 0)
+ continue;
+ if(pci_find_capability(p, i) & IORESOURCE_IO) {
+ hw.io_base = pci_bar_start(p, i);
+ break;
+ }
+ }
+
+ adjust_pci_device(p);
+
+ nic->ioaddr = p->ioaddr & ~3;
+ nic->irqno = p->irq;
+
+ /* From Matt Hortman <mbhortman@acpthinclient.com> */
+ /* MAC and Phy settings */
+
+ /* setup the private structure */
+ if (e1000_sw_init(p, &hw) < 0) {
+ iounmap(hw.hw_addr);
+ return 0;
+ }
+
+ /* make sure the EEPROM is good */
+
+ if (e1000_validate_eeprom_checksum(&hw) < 0) {
+ printf ("The EEPROM Checksum Is Not Valid\n");
+ iounmap(hw.hw_addr);
+ return 0;
+ }
+
+ /* copy the MAC address out of the EEPROM */
+
+ e1000_read_mac_addr(&hw);
+ memcpy (nic->node_addr, hw.mac_addr, ETH_ALEN);
+
+ printf("Ethernet addr: %!\n", nic->node_addr);
+
+ /* reset the hardware with the new settings */
+
+ ret_val = e1000_reset(&hw);
+ if (ret_val < 0) {
+ if ((ret_val == -E1000_ERR_NOLINK) ||
+ (ret_val == -E1000_ERR_TIMEOUT)) {
+ E1000_ERR("Valid Link not detected\n");
+ } else {
+ E1000_ERR("Hardware Initialization Failed\n");
+ }
+ iounmap(hw.hw_addr);
+ return 0;
+ }
+ init_descriptor();
+
+ /* point to NIC specific routines */
+ dev->disable = e1000_disable;
+ nic->poll = e1000_poll;
+ nic->transmit = e1000_transmit;
+ nic->irq = e1000_irq;
+
+ return 1;
+}
+
+static struct pci_id e1000_nics[] = {
+PCI_ROM(0x8086, 0x1000, "e1000-82542", "Intel EtherExpressPro1000"),
+PCI_ROM(0x8086, 0x1001, "e1000-82543gc-fiber", "Intel EtherExpressPro1000 82543GC Fiber"),
+PCI_ROM(0x8086, 0x1004, "e1000-82543gc-copper", "Intel EtherExpressPro1000 82543GC Copper"),
+PCI_ROM(0x8086, 0x1008, "e1000-82544ei-copper", "Intel EtherExpressPro1000 82544EI Copper"),
+PCI_ROM(0x8086, 0x1009, "e1000-82544ei-fiber", "Intel EtherExpressPro1000 82544EI Fiber"),
+PCI_ROM(0x8086, 0x100C, "e1000-82544gc-copper", "Intel EtherExpressPro1000 82544GC Copper"),
+PCI_ROM(0x8086, 0x100D, "e1000-82544gc-lom", "Intel EtherExpressPro1000 82544GC LOM"),
+PCI_ROM(0x8086, 0x100E, "e1000-82540em", "Intel EtherExpressPro1000 82540EM"),
+PCI_ROM(0x8086, 0x100F, "e1000-82545em-copper", "Intel EtherExpressPro1000 82545EM Copper"),
+PCI_ROM(0x8086, 0x1010, "e1000-82546eb-copper", "Intel EtherExpressPro1000 82546EB Copper"),
+PCI_ROM(0x8086, 0x1011, "e1000-82545em-fiber", "Intel EtherExpressPro1000 82545EM Fiber"),
+PCI_ROM(0x8086, 0x1012, "e1000-82546eb-fiber", "Intel EtherExpressPro1000 82546EB Copper"),
+PCI_ROM(0x8086, 0x1013, "e1000-82541ei", "Intel EtherExpressPro1000 82541EI"),
+PCI_ROM(0x8086, 0x1015, "e1000-82540em-lom", "Intel EtherExpressPro1000 82540EM LOM"),
+PCI_ROM(0x8086, 0x1016, "e1000-82540ep-lom", "Intel EtherExpressPro1000 82540EP LOM"),
+PCI_ROM(0x8086, 0x1017, "e1000-82540ep", "Intel EtherExpressPro1000 82540EP"),
+PCI_ROM(0x8086, 0x1018, "e1000-82541ep", "Intel EtherExpressPro1000 82541EP"),
+PCI_ROM(0x8086, 0x1019, "e1000-82547ei", "Intel EtherExpressPro1000 82547EI"),
+PCI_ROM(0x8086, 0x101d, "e1000-82546eb-quad-copper", "Intel EtherExpressPro1000 82546EB Quad Copper"),
+PCI_ROM(0x8086, 0x101e, "e1000-82540ep-lp", "Intel EtherExpressPro1000 82540EP LP"),
+PCI_ROM(0x8086, 0x1026, "e1000-82545gm-copper", "Intel EtherExpressPro1000 82545GM Copper"),
+PCI_ROM(0x8086, 0x1027, "e1000-82545gm-fiber", "Intel EtherExpressPro1000 82545GM Fiber"),
+PCI_ROM(0x8086, 0x1028, "e1000-82545gm-serdes", "Intel EtherExpressPro1000 82545GM SERDES"),
+PCI_ROM(0x8086, 0x1075, "e1000-82547gi", "Intel EtherExpressPro1000 82547GI"),
+PCI_ROM(0x8086, 0x1076, "e1000-82541gi", "Intel EtherExpressPro1000 82541GI"),
+PCI_ROM(0x8086, 0x1077, "e1000-82541gi-mobile", "Intel EtherExpressPro1000 82541GI Mobile"),
+PCI_ROM(0x8086, 0x1078, "e1000-82541er", "Intel EtherExpressPro1000 82541ER"),
+PCI_ROM(0x8086, 0x1079, "e1000-82546gb-copper", "Intel EtherExpressPro1000 82546GB Copper"),
+PCI_ROM(0x8086, 0x107a, "e1000-82546gb-fiber", "Intel EtherExpressPro1000 82546GB Fiber"),
+PCI_ROM(0x8086, 0x107b, "e1000-82546gb-serdes", "Intel EtherExpressPro1000 82546GB SERDES"),
+};
+
+static struct pci_driver e1000_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "E1000",
+ .probe = e1000_probe,
+ .ids = e1000_nics,
+ .id_count = sizeof(e1000_nics)/sizeof(e1000_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/e1000_hw.h b/src/drivers/net/e1000_hw.h
new file mode 100644
index 00000000..7c7f48f6
--- /dev/null
+++ b/src/drivers/net/e1000_hw.h
@@ -0,0 +1,2058 @@
+/*******************************************************************************
+
+
+ Copyright(c) 1999 - 2003 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+/* e1000_hw.h
+ * Structures, enums, and macros for the MAC
+ */
+
+#ifndef _E1000_HW_H_
+#define _E1000_HW_H_
+
+/* Forward declarations of structures used by the shared code */
+struct e1000_hw;
+struct e1000_hw_stats;
+
+/* Enumerated types specific to the e1000 hardware */
+/* Media Access Controlers */
+typedef enum {
+ e1000_undefined = 0,
+ e1000_82542_rev2_0,
+ e1000_82542_rev2_1,
+ e1000_82543,
+ e1000_82544,
+ e1000_82540,
+ e1000_82545,
+ e1000_82545_rev_3,
+ e1000_82546,
+ e1000_82546_rev_3,
+ e1000_82541,
+ e1000_82541_rev_2,
+ e1000_82547,
+ e1000_82547_rev_2,
+ e1000_num_macs
+} e1000_mac_type;
+
+typedef enum {
+ e1000_eeprom_uninitialized = 0,
+ e1000_eeprom_spi,
+ e1000_eeprom_microwire,
+ e1000_num_eeprom_types
+} e1000_eeprom_type;
+
+/* Media Types */
+typedef enum {
+ e1000_media_type_copper = 0,
+ e1000_media_type_fiber = 1,
+ e1000_media_type_internal_serdes = 2,
+ e1000_num_media_types
+} e1000_media_type;
+
+typedef enum {
+ e1000_10_half = 0,
+ e1000_10_full = 1,
+ e1000_100_half = 2,
+ e1000_100_full = 3
+} e1000_speed_duplex_type;
+
+/* Flow Control Settings */
+typedef enum {
+ e1000_fc_none = 0,
+ e1000_fc_rx_pause = 1,
+ e1000_fc_tx_pause = 2,
+ e1000_fc_full = 3,
+ e1000_fc_default = 0xFF
+} e1000_fc_type;
+
+/* PCI bus types */
+typedef enum {
+ e1000_bus_type_unknown = 0,
+ e1000_bus_type_pci,
+ e1000_bus_type_pcix,
+ e1000_bus_type_reserved
+} e1000_bus_type;
+
+/* PCI bus speeds */
+typedef enum {
+ e1000_bus_speed_unknown = 0,
+ e1000_bus_speed_33,
+ e1000_bus_speed_66,
+ e1000_bus_speed_100,
+ e1000_bus_speed_120,
+ e1000_bus_speed_133,
+ e1000_bus_speed_reserved
+} e1000_bus_speed;
+
+/* PCI bus widths */
+typedef enum {
+ e1000_bus_width_unknown = 0,
+ e1000_bus_width_32,
+ e1000_bus_width_64,
+ e1000_bus_width_reserved
+} e1000_bus_width;
+
+/* PHY status info structure and supporting enums */
+typedef enum {
+ e1000_cable_length_50 = 0,
+ e1000_cable_length_50_80,
+ e1000_cable_length_80_110,
+ e1000_cable_length_110_140,
+ e1000_cable_length_140,
+ e1000_cable_length_undefined = 0xFF
+} e1000_cable_length;
+
+typedef enum {
+ e1000_igp_cable_length_10 = 10,
+ e1000_igp_cable_length_20 = 20,
+ e1000_igp_cable_length_30 = 30,
+ e1000_igp_cable_length_40 = 40,
+ e1000_igp_cable_length_50 = 50,
+ e1000_igp_cable_length_60 = 60,
+ e1000_igp_cable_length_70 = 70,
+ e1000_igp_cable_length_80 = 80,
+ e1000_igp_cable_length_90 = 90,
+ e1000_igp_cable_length_100 = 100,
+ e1000_igp_cable_length_110 = 110,
+ e1000_igp_cable_length_120 = 120,
+ e1000_igp_cable_length_130 = 130,
+ e1000_igp_cable_length_140 = 140,
+ e1000_igp_cable_length_150 = 150,
+ e1000_igp_cable_length_160 = 160,
+ e1000_igp_cable_length_170 = 170,
+ e1000_igp_cable_length_180 = 180
+} e1000_igp_cable_length;
+
+typedef enum {
+ e1000_10bt_ext_dist_enable_normal = 0,
+ e1000_10bt_ext_dist_enable_lower,
+ e1000_10bt_ext_dist_enable_undefined = 0xFF
+} e1000_10bt_ext_dist_enable;
+
+typedef enum {
+ e1000_rev_polarity_normal = 0,
+ e1000_rev_polarity_reversed,
+ e1000_rev_polarity_undefined = 0xFF
+} e1000_rev_polarity;
+
+typedef enum {
+ e1000_downshift_normal = 0,
+ e1000_downshift_activated,
+ e1000_downshift_undefined = 0xFF
+} e1000_downshift;
+
+typedef enum {
+ e1000_polarity_reversal_enabled = 0,
+ e1000_polarity_reversal_disabled,
+ e1000_polarity_reversal_undefined = 0xFF
+} e1000_polarity_reversal;
+
+typedef enum {
+ e1000_auto_x_mode_manual_mdi = 0,
+ e1000_auto_x_mode_manual_mdix,
+ e1000_auto_x_mode_auto1,
+ e1000_auto_x_mode_auto2,
+ e1000_auto_x_mode_undefined = 0xFF
+} e1000_auto_x_mode;
+
+typedef enum {
+ e1000_1000t_rx_status_not_ok = 0,
+ e1000_1000t_rx_status_ok,
+ e1000_1000t_rx_status_undefined = 0xFF
+} e1000_1000t_rx_status;
+
+typedef enum {
+ e1000_phy_m88 = 0,
+ e1000_phy_igp,
+ e1000_phy_undefined = 0xFF
+} e1000_phy_type;
+
+typedef enum {
+ e1000_ms_hw_default = 0,
+ e1000_ms_force_master,
+ e1000_ms_force_slave,
+ e1000_ms_auto
+} e1000_ms_type;
+
+typedef enum {
+ e1000_ffe_config_enabled = 0,
+ e1000_ffe_config_active,
+ e1000_ffe_config_blocked
+} e1000_ffe_config;
+
+typedef enum {
+ e1000_dsp_config_disabled = 0,
+ e1000_dsp_config_enabled,
+ e1000_dsp_config_activated,
+ e1000_dsp_config_undefined = 0xFF
+} e1000_dsp_config;
+
+struct e1000_phy_info {
+ e1000_cable_length cable_length;
+ e1000_10bt_ext_dist_enable extended_10bt_distance;
+ e1000_rev_polarity cable_polarity;
+ e1000_downshift downshift;
+ e1000_polarity_reversal polarity_correction;
+ e1000_auto_x_mode mdix_mode;
+ e1000_1000t_rx_status local_rx;
+ e1000_1000t_rx_status remote_rx;
+};
+
+struct e1000_phy_stats {
+ uint32_t idle_errors;
+ uint32_t receive_errors;
+};
+
+struct e1000_eeprom_info {
+ e1000_eeprom_type type;
+ uint16_t word_size;
+ uint16_t opcode_bits;
+ uint16_t address_bits;
+ uint16_t delay_usec;
+ uint16_t page_size;
+};
+
+
+
+/* Error Codes */
+#define E1000_SUCCESS 0
+#define E1000_ERR_EEPROM 1
+#define E1000_ERR_PHY 2
+#define E1000_ERR_CONFIG 3
+#define E1000_ERR_PARAM 4
+#define E1000_ERR_MAC_TYPE 5
+#define E1000_ERR_PHY_TYPE 6
+#define E1000_ERR_NOLINK 7
+#define E1000_ERR_TIMEOUT 8
+
+#define E1000_READ_REG_IO(a, reg) \
+ e1000_read_reg_io((a), E1000_##reg)
+#define E1000_WRITE_REG_IO(a, reg, val) \
+ e1000_write_reg_io((a), E1000_##reg, val)
+
+/* PCI Device IDs */
+#define E1000_DEV_ID_82542 0x1000
+#define E1000_DEV_ID_82543GC_FIBER 0x1001
+#define E1000_DEV_ID_82543GC_COPPER 0x1004
+#define E1000_DEV_ID_82544EI_COPPER 0x1008
+#define E1000_DEV_ID_82544EI_FIBER 0x1009
+#define E1000_DEV_ID_82544GC_COPPER 0x100C
+#define E1000_DEV_ID_82544GC_LOM 0x100D
+#define E1000_DEV_ID_82540EM 0x100E
+#define E1000_DEV_ID_82540EM_LOM 0x1015
+#define E1000_DEV_ID_82540EP_LOM 0x1016
+#define E1000_DEV_ID_82540EP 0x1017
+#define E1000_DEV_ID_82540EP_LP 0x101E
+#define E1000_DEV_ID_82545EM_COPPER 0x100F
+#define E1000_DEV_ID_82545EM_FIBER 0x1011
+#define E1000_DEV_ID_82545GM_COPPER 0x1026
+#define E1000_DEV_ID_82545GM_FIBER 0x1027
+#define E1000_DEV_ID_82545GM_SERDES 0x1028
+#define E1000_DEV_ID_82546EB_COPPER 0x1010
+#define E1000_DEV_ID_82546EB_FIBER 0x1012
+#define E1000_DEV_ID_82546EB_QUAD_COPPER 0x101D
+#define E1000_DEV_ID_82541EI 0x1013
+#define E1000_DEV_ID_82541EI_MOBILE 0x1018
+#define E1000_DEV_ID_82541ER 0x1078
+#define E1000_DEV_ID_82547GI 0x1075
+#define E1000_DEV_ID_82541GI 0x1076
+#define E1000_DEV_ID_82541GI_MOBILE 0x1077
+#define E1000_DEV_ID_82546GB_COPPER 0x1079
+#define E1000_DEV_ID_82546GB_FIBER 0x107A
+#define E1000_DEV_ID_82546GB_SERDES 0x107B
+#define E1000_DEV_ID_82547EI 0x1019
+
+#define NODE_ADDRESS_SIZE 6
+#define ETH_LENGTH_OF_ADDRESS 6
+
+/* MAC decode size is 128K - This is the size of BAR0 */
+#define MAC_DECODE_SIZE (128 * 1024)
+
+#define E1000_82542_2_0_REV_ID 2
+#define E1000_82542_2_1_REV_ID 3
+
+#define SPEED_10 10
+#define SPEED_100 100
+#define SPEED_1000 1000
+#define HALF_DUPLEX 1
+#define FULL_DUPLEX 2
+
+/* The sizes (in bytes) of a ethernet packet */
+#define ENET_HEADER_SIZE 14
+#define MAXIMUM_ETHERNET_FRAME_SIZE 1518 /* With FCS */
+#define MINIMUM_ETHERNET_FRAME_SIZE 64 /* With FCS */
+#define ETHERNET_FCS_SIZE 4
+#define MAXIMUM_ETHERNET_PACKET_SIZE \
+ (MAXIMUM_ETHERNET_FRAME_SIZE - ETHERNET_FCS_SIZE)
+#define MINIMUM_ETHERNET_PACKET_SIZE \
+ (MINIMUM_ETHERNET_FRAME_SIZE - ETHERNET_FCS_SIZE)
+#define CRC_LENGTH ETHERNET_FCS_SIZE
+#define MAX_JUMBO_FRAME_SIZE 0x3F00
+
+
+/* 802.1q VLAN Packet Sizes */
+#define VLAN_TAG_SIZE 4 /* 802.3ac tag (not DMAed) */
+
+/* Ethertype field values */
+#define ETHERNET_IEEE_VLAN_TYPE 0x8100 /* 802.3ac packet */
+#define ETHERNET_IP_TYPE 0x0800 /* IP packets */
+#define ETHERNET_ARP_TYPE 0x0806 /* Address Resolution Protocol (ARP) */
+
+/* Packet Header defines */
+#define IP_PROTOCOL_TCP 6
+#define IP_PROTOCOL_UDP 0x11
+
+/* This defines the bits that are set in the Interrupt Mask
+ * Set/Read Register. Each bit is documented below:
+ * o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0)
+ * o RXSEQ = Receive Sequence Error
+ */
+#define POLL_IMS_ENABLE_MASK ( \
+ E1000_IMS_RXDMT0 | \
+ E1000_IMS_RXSEQ)
+
+/* This defines the bits that are set in the Interrupt Mask
+ * Set/Read Register. Each bit is documented below:
+ * o RXT0 = Receiver Timer Interrupt (ring 0)
+ * o TXDW = Transmit Descriptor Written Back
+ * o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0)
+ * o RXSEQ = Receive Sequence Error
+ * o LSC = Link Status Change
+ */
+#define IMS_ENABLE_MASK ( \
+ E1000_IMS_RXT0 | \
+ E1000_IMS_TXDW | \
+ E1000_IMS_RXDMT0 | \
+ E1000_IMS_RXSEQ | \
+ E1000_IMS_LSC)
+
+/* Number of high/low register pairs in the RAR. The RAR (Receive Address
+ * Registers) holds the directed and multicast addresses that we monitor. We
+ * reserve one of these spots for our directed address, allowing us room for
+ * E1000_RAR_ENTRIES - 1 multicast addresses.
+ */
+#define E1000_RAR_ENTRIES 15
+
+#define MIN_NUMBER_OF_DESCRIPTORS 8
+#define MAX_NUMBER_OF_DESCRIPTORS 0xFFF8
+
+/* Receive Descriptor */
+struct e1000_rx_desc {
+ uint64_t buffer_addr; /* Address of the descriptor's data buffer */
+ uint16_t length; /* Length of data DMAed into data buffer */
+ uint16_t csum; /* Packet checksum */
+ uint8_t status; /* Descriptor status */
+ uint8_t errors; /* Descriptor Errors */
+ uint16_t special;
+};
+
+/* Receive Decriptor bit definitions */
+#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */
+#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */
+#define E1000_RXD_STAT_IXSM 0x04 /* Ignore checksum */
+#define E1000_RXD_STAT_VP 0x08 /* IEEE VLAN Packet */
+#define E1000_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */
+#define E1000_RXD_STAT_IPCS 0x40 /* IP xsum calculated */
+#define E1000_RXD_STAT_PIF 0x80 /* passed in-exact filter */
+#define E1000_RXD_ERR_CE 0x01 /* CRC Error */
+#define E1000_RXD_ERR_SE 0x02 /* Symbol Error */
+#define E1000_RXD_ERR_SEQ 0x04 /* Sequence Error */
+#define E1000_RXD_ERR_CXE 0x10 /* Carrier Extension Error */
+#define E1000_RXD_ERR_TCPE 0x20 /* TCP/UDP Checksum Error */
+#define E1000_RXD_ERR_IPE 0x40 /* IP Checksum Error */
+#define E1000_RXD_ERR_RXE 0x80 /* Rx Data Error */
+#define E1000_RXD_SPC_VLAN_MASK 0x0FFF /* VLAN ID is in lower 12 bits */
+#define E1000_RXD_SPC_PRI_MASK 0xE000 /* Priority is in upper 3 bits */
+#define E1000_RXD_SPC_PRI_SHIFT 0x000D /* Priority is in upper 3 of 16 */
+#define E1000_RXD_SPC_CFI_MASK 0x1000 /* CFI is bit 12 */
+#define E1000_RXD_SPC_CFI_SHIFT 0x000C /* CFI is bit 12 */
+
+/* mask to determine if packets should be dropped due to frame errors */
+#define E1000_RXD_ERR_FRAME_ERR_MASK ( \
+ E1000_RXD_ERR_CE | \
+ E1000_RXD_ERR_SE | \
+ E1000_RXD_ERR_SEQ | \
+ E1000_RXD_ERR_CXE | \
+ E1000_RXD_ERR_RXE)
+
+/* Transmit Descriptor */
+struct e1000_tx_desc {
+ uint64_t buffer_addr; /* Address of the descriptor's data buffer */
+ union {
+ uint32_t data;
+ struct {
+ uint16_t length; /* Data buffer length */
+ uint8_t cso; /* Checksum offset */
+ uint8_t cmd; /* Descriptor control */
+ } flags;
+ } lower;
+ union {
+ uint32_t data;
+ struct {
+ uint8_t status; /* Descriptor status */
+ uint8_t css; /* Checksum start */
+ uint16_t special;
+ } fields;
+ } upper;
+};
+
+/* Transmit Descriptor bit definitions */
+#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */
+#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */
+#define E1000_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */
+#define E1000_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */
+#define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */
+#define E1000_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */
+#define E1000_TXD_CMD_IC 0x04000000 /* Insert Checksum */
+#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */
+#define E1000_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */
+#define E1000_TXD_CMD_DEXT 0x20000000 /* Descriptor extension (0 = legacy) */
+#define E1000_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */
+#define E1000_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */
+#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */
+#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */
+#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */
+#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */
+#define E1000_TXD_CMD_TCP 0x01000000 /* TCP packet */
+#define E1000_TXD_CMD_IP 0x02000000 /* IP packet */
+#define E1000_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */
+#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */
+
+/* Offload Context Descriptor */
+struct e1000_context_desc {
+ union {
+ uint32_t ip_config;
+ struct {
+ uint8_t ipcss; /* IP checksum start */
+ uint8_t ipcso; /* IP checksum offset */
+ uint16_t ipcse; /* IP checksum end */
+ } ip_fields;
+ } lower_setup;
+ union {
+ uint32_t tcp_config;
+ struct {
+ uint8_t tucss; /* TCP checksum start */
+ uint8_t tucso; /* TCP checksum offset */
+ uint16_t tucse; /* TCP checksum end */
+ } tcp_fields;
+ } upper_setup;
+ uint32_t cmd_and_length; /* */
+ union {
+ uint32_t data;
+ struct {
+ uint8_t status; /* Descriptor status */
+ uint8_t hdr_len; /* Header length */
+ uint16_t mss; /* Maximum segment size */
+ } fields;
+ } tcp_seg_setup;
+};
+
+/* Offload data descriptor */
+struct e1000_data_desc {
+ uint64_t buffer_addr; /* Address of the descriptor's buffer address */
+ union {
+ uint32_t data;
+ struct {
+ uint16_t length; /* Data buffer length */
+ uint8_t typ_len_ext; /* */
+ uint8_t cmd; /* */
+ } flags;
+ } lower;
+ union {
+ uint32_t data;
+ struct {
+ uint8_t status; /* Descriptor status */
+ uint8_t popts; /* Packet Options */
+ uint16_t special; /* */
+ } fields;
+ } upper;
+};
+
+/* Filters */
+#define E1000_NUM_UNICAST 16 /* Unicast filter entries */
+#define E1000_MC_TBL_SIZE 128 /* Multicast Filter Table (4096 bits) */
+#define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */
+
+
+/* Receive Address Register */
+struct e1000_rar {
+ volatile uint32_t low; /* receive address low */
+ volatile uint32_t high; /* receive address high */
+};
+
+/* Number of entries in the Multicast Table Array (MTA). */
+#define E1000_NUM_MTA_REGISTERS 128
+
+/* IPv4 Address Table Entry */
+struct e1000_ipv4_at_entry {
+ volatile uint32_t ipv4_addr; /* IP Address (RW) */
+ volatile uint32_t reserved;
+};
+
+/* Four wakeup IP addresses are supported */
+#define E1000_WAKEUP_IP_ADDRESS_COUNT_MAX 4
+#define E1000_IP4AT_SIZE E1000_WAKEUP_IP_ADDRESS_COUNT_MAX
+#define E1000_IP6AT_SIZE 1
+
+/* IPv6 Address Table Entry */
+struct e1000_ipv6_at_entry {
+ volatile uint8_t ipv6_addr[16];
+};
+
+/* Flexible Filter Length Table Entry */
+struct e1000_fflt_entry {
+ volatile uint32_t length; /* Flexible Filter Length (RW) */
+ volatile uint32_t reserved;
+};
+
+/* Flexible Filter Mask Table Entry */
+struct e1000_ffmt_entry {
+ volatile uint32_t mask; /* Flexible Filter Mask (RW) */
+ volatile uint32_t reserved;
+};
+
+/* Flexible Filter Value Table Entry */
+struct e1000_ffvt_entry {
+ volatile uint32_t value; /* Flexible Filter Value (RW) */
+ volatile uint32_t reserved;
+};
+
+/* Four Flexible Filters are supported */
+#define E1000_FLEXIBLE_FILTER_COUNT_MAX 4
+
+/* Each Flexible Filter is at most 128 (0x80) bytes in length */
+#define E1000_FLEXIBLE_FILTER_SIZE_MAX 128
+
+#define E1000_FFLT_SIZE E1000_FLEXIBLE_FILTER_COUNT_MAX
+#define E1000_FFMT_SIZE E1000_FLEXIBLE_FILTER_SIZE_MAX
+#define E1000_FFVT_SIZE E1000_FLEXIBLE_FILTER_SIZE_MAX
+
+/* Register Set. (82543, 82544)
+ *
+ * Registers are defined to be 32 bits and should be accessed as 32 bit values.
+ * These registers are physically located on the NIC, but are mapped into the
+ * host memory address space.
+ *
+ * RW - register is both readable and writable
+ * RO - register is read only
+ * WO - register is write only
+ * R/clr - register is read only and is cleared when read
+ * A - register array
+ */
+#define E1000_CTRL 0x00000 /* Device Control - RW */
+#define E1000_CTRL_DUP 0x00004 /* Device Control Duplicate (Shadow) - RW */
+#define E1000_STATUS 0x00008 /* Device Status - RO */
+#define E1000_EECD 0x00010 /* EEPROM/Flash Control - RW */
+#define E1000_EERD 0x00014 /* EEPROM Read - RW */
+#define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */
+#define E1000_FLA 0x0001C /* Flash Access - RW */
+#define E1000_MDIC 0x00020 /* MDI Control - RW */
+#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */
+#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */
+#define E1000_FCT 0x00030 /* Flow Control Type - RW */
+#define E1000_VET 0x00038 /* VLAN Ether Type - RW */
+#define E1000_ICR 0x000C0 /* Interrupt Cause Read - R/clr */
+#define E1000_ITR 0x000C4 /* Interrupt Throttling Rate - RW */
+#define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */
+#define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */
+#define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */
+#define E1000_RCTL 0x00100 /* RX Control - RW */
+#define E1000_FCTTV 0x00170 /* Flow Control Transmit Timer Value - RW */
+#define E1000_TXCW 0x00178 /* TX Configuration Word - RW */
+#define E1000_RXCW 0x00180 /* RX Configuration Word - RO */
+#define E1000_TCTL 0x00400 /* TX Control - RW */
+#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */
+#define E1000_TBT 0x00448 /* TX Burst Timer - RW */
+#define E1000_AIT 0x00458 /* Adaptive Interframe Spacing Throttle - RW */
+#define E1000_LEDCTL 0x00E00 /* LED Control - RW */
+#define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */
+#define E1000_FCRTL 0x02160 /* Flow Control Receive Threshold Low - RW */
+#define E1000_FCRTH 0x02168 /* Flow Control Receive Threshold High - RW */
+#define E1000_RDBAL 0x02800 /* RX Descriptor Base Address Low - RW */
+#define E1000_RDBAH 0x02804 /* RX Descriptor Base Address High - RW */
+#define E1000_RDLEN 0x02808 /* RX Descriptor Length - RW */
+#define E1000_RDH 0x02810 /* RX Descriptor Head - RW */
+#define E1000_RDT 0x02818 /* RX Descriptor Tail - RW */
+#define E1000_RDTR 0x02820 /* RX Delay Timer - RW */
+#define E1000_RXDCTL 0x02828 /* RX Descriptor Control - RW */
+#define E1000_RADV 0x0282C /* RX Interrupt Absolute Delay Timer - RW */
+#define E1000_RSRPD 0x02C00 /* RX Small Packet Detect - RW */
+#define E1000_TXDMAC 0x03000 /* TX DMA Control - RW */
+#define E1000_TDFH 0x03410 /* TX Data FIFO Head - RW */
+#define E1000_TDFT 0x03418 /* TX Data FIFO Tail - RW */
+#define E1000_TDFHS 0x03420 /* TX Data FIFO Head Saved - RW */
+#define E1000_TDFTS 0x03428 /* TX Data FIFO Tail Saved - RW */
+#define E1000_TDFPC 0x03430 /* TX Data FIFO Packet Count - RW */
+#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */
+#define E1000_TDBAH 0x03804 /* TX Descriptor Base Address High - RW */
+#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */
+#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */
+#define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */
+#define E1000_TIDV 0x03820 /* TX Interrupt Delay Value - RW */
+#define E1000_TXDCTL 0x03828 /* TX Descriptor Control - RW */
+#define E1000_TADV 0x0382C /* TX Interrupt Absolute Delay Val - RW */
+#define E1000_TSPMT 0x03830 /* TCP Segmentation PAD & Min Threshold - RW */
+#define E1000_CRCERRS 0x04000 /* CRC Error Count - R/clr */
+#define E1000_ALGNERRC 0x04004 /* Alignment Error Count - R/clr */
+#define E1000_SYMERRS 0x04008 /* Symbol Error Count - R/clr */
+#define E1000_RXERRC 0x0400C /* Receive Error Count - R/clr */
+#define E1000_MPC 0x04010 /* Missed Packet Count - R/clr */
+#define E1000_SCC 0x04014 /* Single Collision Count - R/clr */
+#define E1000_ECOL 0x04018 /* Excessive Collision Count - R/clr */
+#define E1000_MCC 0x0401C /* Multiple Collision Count - R/clr */
+#define E1000_LATECOL 0x04020 /* Late Collision Count - R/clr */
+#define E1000_COLC 0x04028 /* Collision Count - R/clr */
+#define E1000_DC 0x04030 /* Defer Count - R/clr */
+#define E1000_TNCRS 0x04034 /* TX-No CRS - R/clr */
+#define E1000_SEC 0x04038 /* Sequence Error Count - R/clr */
+#define E1000_CEXTERR 0x0403C /* Carrier Extension Error Count - R/clr */
+#define E1000_RLEC 0x04040 /* Receive Length Error Count - R/clr */
+#define E1000_XONRXC 0x04048 /* XON RX Count - R/clr */
+#define E1000_XONTXC 0x0404C /* XON TX Count - R/clr */
+#define E1000_XOFFRXC 0x04050 /* XOFF RX Count - R/clr */
+#define E1000_XOFFTXC 0x04054 /* XOFF TX Count - R/clr */
+#define E1000_FCRUC 0x04058 /* Flow Control RX Unsupported Count- R/clr */
+#define E1000_PRC64 0x0405C /* Packets RX (64 bytes) - R/clr */
+#define E1000_PRC127 0x04060 /* Packets RX (65-127 bytes) - R/clr */
+#define E1000_PRC255 0x04064 /* Packets RX (128-255 bytes) - R/clr */
+#define E1000_PRC511 0x04068 /* Packets RX (255-511 bytes) - R/clr */
+#define E1000_PRC1023 0x0406C /* Packets RX (512-1023 bytes) - R/clr */
+#define E1000_PRC1522 0x04070 /* Packets RX (1024-1522 bytes) - R/clr */
+#define E1000_GPRC 0x04074 /* Good Packets RX Count - R/clr */
+#define E1000_BPRC 0x04078 /* Broadcast Packets RX Count - R/clr */
+#define E1000_MPRC 0x0407C /* Multicast Packets RX Count - R/clr */
+#define E1000_GPTC 0x04080 /* Good Packets TX Count - R/clr */
+#define E1000_GORCL 0x04088 /* Good Octets RX Count Low - R/clr */
+#define E1000_GORCH 0x0408C /* Good Octets RX Count High - R/clr */
+#define E1000_GOTCL 0x04090 /* Good Octets TX Count Low - R/clr */
+#define E1000_GOTCH 0x04094 /* Good Octets TX Count High - R/clr */
+#define E1000_RNBC 0x040A0 /* RX No Buffers Count - R/clr */
+#define E1000_RUC 0x040A4 /* RX Undersize Count - R/clr */
+#define E1000_RFC 0x040A8 /* RX Fragment Count - R/clr */
+#define E1000_ROC 0x040AC /* RX Oversize Count - R/clr */
+#define E1000_RJC 0x040B0 /* RX Jabber Count - R/clr */
+#define E1000_MGTPRC 0x040B4 /* Management Packets RX Count - R/clr */
+#define E1000_MGTPDC 0x040B8 /* Management Packets Dropped Count - R/clr */
+#define E1000_MGTPTC 0x040BC /* Management Packets TX Count - R/clr */
+#define E1000_TORL 0x040C0 /* Total Octets RX Low - R/clr */
+#define E1000_TORH 0x040C4 /* Total Octets RX High - R/clr */
+#define E1000_TOTL 0x040C8 /* Total Octets TX Low - R/clr */
+#define E1000_TOTH 0x040CC /* Total Octets TX High - R/clr */
+#define E1000_TPR 0x040D0 /* Total Packets RX - R/clr */
+#define E1000_TPT 0x040D4 /* Total Packets TX - R/clr */
+#define E1000_PTC64 0x040D8 /* Packets TX (64 bytes) - R/clr */
+#define E1000_PTC127 0x040DC /* Packets TX (65-127 bytes) - R/clr */
+#define E1000_PTC255 0x040E0 /* Packets TX (128-255 bytes) - R/clr */
+#define E1000_PTC511 0x040E4 /* Packets TX (256-511 bytes) - R/clr */
+#define E1000_PTC1023 0x040E8 /* Packets TX (512-1023 bytes) - R/clr */
+#define E1000_PTC1522 0x040EC /* Packets TX (1024-1522 Bytes) - R/clr */
+#define E1000_MPTC 0x040F0 /* Multicast Packets TX Count - R/clr */
+#define E1000_BPTC 0x040F4 /* Broadcast Packets TX Count - R/clr */
+#define E1000_TSCTC 0x040F8 /* TCP Segmentation Context TX - R/clr */
+#define E1000_TSCTFC 0x040FC /* TCP Segmentation Context TX Fail - R/clr */
+#define E1000_RXCSUM 0x05000 /* RX Checksum Control - RW */
+#define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */
+#define E1000_RA 0x05400 /* Receive Address - RW Array */
+#define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */
+#define E1000_WUC 0x05800 /* Wakeup Control - RW */
+#define E1000_WUFC 0x05808 /* Wakeup Filter Control - RW */
+#define E1000_WUS 0x05810 /* Wakeup Status - RO */
+#define E1000_MANC 0x05820 /* Management Control - RW */
+#define E1000_IPAV 0x05838 /* IP Address Valid - RW */
+#define E1000_IP4AT 0x05840 /* IPv4 Address Table - RW Array */
+#define E1000_IP6AT 0x05880 /* IPv6 Address Table - RW Array */
+#define E1000_WUPL 0x05900 /* Wakeup Packet Length - RW */
+#define E1000_WUPM 0x05A00 /* Wakeup Packet Memory - RO A */
+#define E1000_FFLT 0x05F00 /* Flexible Filter Length Table - RW Array */
+#define E1000_FFMT 0x09000 /* Flexible Filter Mask Table - RW Array */
+#define E1000_FFVT 0x09800 /* Flexible Filter Value Table - RW Array */
+
+/* Register Set (82542)
+ *
+ * Some of the 82542 registers are located at different offsets than they are
+ * in more current versions of the 8254x. Despite the difference in location,
+ * the registers function in the same manner.
+ */
+#define E1000_82542_CTRL E1000_CTRL
+#define E1000_82542_CTRL_DUP E1000_CTRL_DUP
+#define E1000_82542_STATUS E1000_STATUS
+#define E1000_82542_EECD E1000_EECD
+#define E1000_82542_EERD E1000_EERD
+#define E1000_82542_CTRL_EXT E1000_CTRL_EXT
+#define E1000_82542_FLA E1000_FLA
+#define E1000_82542_MDIC E1000_MDIC
+#define E1000_82542_FCAL E1000_FCAL
+#define E1000_82542_FCAH E1000_FCAH
+#define E1000_82542_FCT E1000_FCT
+#define E1000_82542_VET E1000_VET
+#define E1000_82542_RA 0x00040
+#define E1000_82542_ICR E1000_ICR
+#define E1000_82542_ITR E1000_ITR
+#define E1000_82542_ICS E1000_ICS
+#define E1000_82542_IMS E1000_IMS
+#define E1000_82542_IMC E1000_IMC
+#define E1000_82542_RCTL E1000_RCTL
+#define E1000_82542_RDTR 0x00108
+#define E1000_82542_RDBAL 0x00110
+#define E1000_82542_RDBAH 0x00114
+#define E1000_82542_RDLEN 0x00118
+#define E1000_82542_RDH 0x00120
+#define E1000_82542_RDT 0x00128
+#define E1000_82542_FCRTH 0x00160
+#define E1000_82542_FCRTL 0x00168
+#define E1000_82542_FCTTV E1000_FCTTV
+#define E1000_82542_TXCW E1000_TXCW
+#define E1000_82542_RXCW E1000_RXCW
+#define E1000_82542_MTA 0x00200
+#define E1000_82542_TCTL E1000_TCTL
+#define E1000_82542_TIPG E1000_TIPG
+#define E1000_82542_TDBAL 0x00420
+#define E1000_82542_TDBAH 0x00424
+#define E1000_82542_TDLEN 0x00428
+#define E1000_82542_TDH 0x00430
+#define E1000_82542_TDT 0x00438
+#define E1000_82542_TIDV 0x00440
+#define E1000_82542_TBT E1000_TBT
+#define E1000_82542_AIT E1000_AIT
+#define E1000_82542_VFTA 0x00600
+#define E1000_82542_LEDCTL E1000_LEDCTL
+#define E1000_82542_PBA E1000_PBA
+#define E1000_82542_RXDCTL E1000_RXDCTL
+#define E1000_82542_RADV E1000_RADV
+#define E1000_82542_RSRPD E1000_RSRPD
+#define E1000_82542_TXDMAC E1000_TXDMAC
+#define E1000_82542_TDFHS E1000_TDFHS
+#define E1000_82542_TDFTS E1000_TDFTS
+#define E1000_82542_TDFPC E1000_TDFPC
+#define E1000_82542_TXDCTL E1000_TXDCTL
+#define E1000_82542_TADV E1000_TADV
+#define E1000_82542_TSPMT E1000_TSPMT
+#define E1000_82542_CRCERRS E1000_CRCERRS
+#define E1000_82542_ALGNERRC E1000_ALGNERRC
+#define E1000_82542_SYMERRS E1000_SYMERRS
+#define E1000_82542_RXERRC E1000_RXERRC
+#define E1000_82542_MPC E1000_MPC
+#define E1000_82542_SCC E1000_SCC
+#define E1000_82542_ECOL E1000_ECOL
+#define E1000_82542_MCC E1000_MCC
+#define E1000_82542_LATECOL E1000_LATECOL
+#define E1000_82542_COLC E1000_COLC
+#define E1000_82542_DC E1000_DC
+#define E1000_82542_TNCRS E1000_TNCRS
+#define E1000_82542_SEC E1000_SEC
+#define E1000_82542_CEXTERR E1000_CEXTERR
+#define E1000_82542_RLEC E1000_RLEC
+#define E1000_82542_XONRXC E1000_XONRXC
+#define E1000_82542_XONTXC E1000_XONTXC
+#define E1000_82542_XOFFRXC E1000_XOFFRXC
+#define E1000_82542_XOFFTXC E1000_XOFFTXC
+#define E1000_82542_FCRUC E1000_FCRUC
+#define E1000_82542_PRC64 E1000_PRC64
+#define E1000_82542_PRC127 E1000_PRC127
+#define E1000_82542_PRC255 E1000_PRC255
+#define E1000_82542_PRC511 E1000_PRC511
+#define E1000_82542_PRC1023 E1000_PRC1023
+#define E1000_82542_PRC1522 E1000_PRC1522
+#define E1000_82542_GPRC E1000_GPRC
+#define E1000_82542_BPRC E1000_BPRC
+#define E1000_82542_MPRC E1000_MPRC
+#define E1000_82542_GPTC E1000_GPTC
+#define E1000_82542_GORCL E1000_GORCL
+#define E1000_82542_GORCH E1000_GORCH
+#define E1000_82542_GOTCL E1000_GOTCL
+#define E1000_82542_GOTCH E1000_GOTCH
+#define E1000_82542_RNBC E1000_RNBC
+#define E1000_82542_RUC E1000_RUC
+#define E1000_82542_RFC E1000_RFC
+#define E1000_82542_ROC E1000_ROC
+#define E1000_82542_RJC E1000_RJC
+#define E1000_82542_MGTPRC E1000_MGTPRC
+#define E1000_82542_MGTPDC E1000_MGTPDC
+#define E1000_82542_MGTPTC E1000_MGTPTC
+#define E1000_82542_TORL E1000_TORL
+#define E1000_82542_TORH E1000_TORH
+#define E1000_82542_TOTL E1000_TOTL
+#define E1000_82542_TOTH E1000_TOTH
+#define E1000_82542_TPR E1000_TPR
+#define E1000_82542_TPT E1000_TPT
+#define E1000_82542_PTC64 E1000_PTC64
+#define E1000_82542_PTC127 E1000_PTC127
+#define E1000_82542_PTC255 E1000_PTC255
+#define E1000_82542_PTC511 E1000_PTC511
+#define E1000_82542_PTC1023 E1000_PTC1023
+#define E1000_82542_PTC1522 E1000_PTC1522
+#define E1000_82542_MPTC E1000_MPTC
+#define E1000_82542_BPTC E1000_BPTC
+#define E1000_82542_TSCTC E1000_TSCTC
+#define E1000_82542_TSCTFC E1000_TSCTFC
+#define E1000_82542_RXCSUM E1000_RXCSUM
+#define E1000_82542_WUC E1000_WUC
+#define E1000_82542_WUFC E1000_WUFC
+#define E1000_82542_WUS E1000_WUS
+#define E1000_82542_MANC E1000_MANC
+#define E1000_82542_IPAV E1000_IPAV
+#define E1000_82542_IP4AT E1000_IP4AT
+#define E1000_82542_IP6AT E1000_IP6AT
+#define E1000_82542_WUPL E1000_WUPL
+#define E1000_82542_WUPM E1000_WUPM
+#define E1000_82542_FFLT E1000_FFLT
+#define E1000_82542_TDFH 0x08010
+#define E1000_82542_TDFT 0x08018
+#define E1000_82542_FFMT E1000_FFMT
+#define E1000_82542_FFVT E1000_FFVT
+
+/* Statistics counters collected by the MAC */
+struct e1000_hw_stats {
+ uint64_t crcerrs;
+ uint64_t algnerrc;
+ uint64_t symerrs;
+ uint64_t rxerrc;
+ uint64_t mpc;
+ uint64_t scc;
+ uint64_t ecol;
+ uint64_t mcc;
+ uint64_t latecol;
+ uint64_t colc;
+ uint64_t dc;
+ uint64_t tncrs;
+ uint64_t sec;
+ uint64_t cexterr;
+ uint64_t rlec;
+ uint64_t xonrxc;
+ uint64_t xontxc;
+ uint64_t xoffrxc;
+ uint64_t xofftxc;
+ uint64_t fcruc;
+ uint64_t prc64;
+ uint64_t prc127;
+ uint64_t prc255;
+ uint64_t prc511;
+ uint64_t prc1023;
+ uint64_t prc1522;
+ uint64_t gprc;
+ uint64_t bprc;
+ uint64_t mprc;
+ uint64_t gptc;
+ uint64_t gorcl;
+ uint64_t gorch;
+ uint64_t gotcl;
+ uint64_t gotch;
+ uint64_t rnbc;
+ uint64_t ruc;
+ uint64_t rfc;
+ uint64_t roc;
+ uint64_t rjc;
+ uint64_t mgprc;
+ uint64_t mgpdc;
+ uint64_t mgptc;
+ uint64_t torl;
+ uint64_t torh;
+ uint64_t totl;
+ uint64_t toth;
+ uint64_t tpr;
+ uint64_t tpt;
+ uint64_t ptc64;
+ uint64_t ptc127;
+ uint64_t ptc255;
+ uint64_t ptc511;
+ uint64_t ptc1023;
+ uint64_t ptc1522;
+ uint64_t mptc;
+ uint64_t bptc;
+ uint64_t tsctc;
+ uint64_t tsctfc;
+};
+
+/* Structure containing variables used by the shared code (e1000_hw.c) */
+struct e1000_hw {
+ struct pci_device *pdev;
+ uint8_t *hw_addr;
+ e1000_mac_type mac_type;
+ e1000_phy_type phy_type;
+#if 0
+ uint32_t phy_init_script;
+#endif
+ e1000_media_type media_type;
+ e1000_fc_type fc;
+#if 0
+ e1000_bus_speed bus_speed;
+ e1000_bus_width bus_width;
+ e1000_bus_type bus_type;
+#endif
+ struct e1000_eeprom_info eeprom;
+#if 0
+ e1000_ms_type master_slave;
+ e1000_ms_type original_master_slave;
+ e1000_ffe_config ffe_config_state;
+#endif
+ uint32_t io_base;
+ uint32_t phy_id;
+#ifdef LINUX_DRIVER
+ uint32_t phy_revision;
+#endif
+ uint32_t phy_addr;
+#if 0
+ uint32_t original_fc;
+#endif
+ uint32_t txcw;
+ uint32_t autoneg_failed;
+#if 0
+ uint32_t max_frame_size;
+ uint32_t min_frame_size;
+ uint32_t mc_filter_type;
+ uint32_t num_mc_addrs;
+ uint32_t collision_delta;
+ uint32_t tx_packet_delta;
+ uint32_t ledctl_default;
+ uint32_t ledctl_mode1;
+ uint32_t ledctl_mode2;
+ uint16_t phy_spd_default;
+#endif
+ uint16_t autoneg_advertised;
+ uint16_t pci_cmd_word;
+#if 0
+ uint16_t fc_high_water;
+ uint16_t fc_low_water;
+ uint16_t fc_pause_time;
+ uint16_t current_ifs_val;
+ uint16_t ifs_min_val;
+ uint16_t ifs_max_val;
+ uint16_t ifs_step_size;
+ uint16_t ifs_ratio;
+#endif
+ uint16_t device_id;
+ uint16_t vendor_id;
+#if 0
+ uint16_t subsystem_id;
+ uint16_t subsystem_vendor_id;
+#endif
+ uint8_t revision_id;
+#if 0
+ uint8_t autoneg;
+ uint8_t mdix;
+ uint8_t forced_speed_duplex;
+ uint8_t wait_autoneg_complete;
+ uint8_t dma_fairness;
+#endif
+ uint8_t mac_addr[NODE_ADDRESS_SIZE];
+#if 0
+ uint8_t perm_mac_addr[NODE_ADDRESS_SIZE];
+ boolean_t disable_polarity_correction;
+ boolean_t speed_downgraded;
+ e1000_dsp_config dsp_config_state;
+ boolean_t get_link_status;
+ boolean_t serdes_link_down;
+#endif
+ boolean_t tbi_compatibility_en;
+ boolean_t tbi_compatibility_on;
+#if 0
+ boolean_t phy_reset_disable;
+ boolean_t fc_send_xon;
+ boolean_t fc_strict_ieee;
+ boolean_t report_tx_early;
+ boolean_t adaptive_ifs;
+ boolean_t ifs_params_forced;
+ boolean_t in_ifs_mode;
+#endif
+};
+
+
+#define E1000_EEPROM_SWDPIN0 0x0001 /* SWDPIN 0 EEPROM Value */
+#define E1000_EEPROM_LED_LOGIC 0x0020 /* Led Logic Word */
+
+/* Register Bit Masks */
+/* Device Control */
+#define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */
+#define E1000_CTRL_BEM 0x00000002 /* Endian Mode.0=little,1=big */
+#define E1000_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */
+#define E1000_CTRL_LRST 0x00000008 /* Link reset. 0=normal,1=reset */
+#define E1000_CTRL_TME 0x00000010 /* Test mode. 0=normal,1=test */
+#define E1000_CTRL_SLE 0x00000020 /* Serial Link on 0=dis,1=en */
+#define E1000_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */
+#define E1000_CTRL_SLU 0x00000040 /* Set link up (Force Link) */
+#define E1000_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */
+#define E1000_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */
+#define E1000_CTRL_SPD_10 0x00000000 /* Force 10Mb */
+#define E1000_CTRL_SPD_100 0x00000100 /* Force 100Mb */
+#define E1000_CTRL_SPD_1000 0x00000200 /* Force 1Gb */
+#define E1000_CTRL_BEM32 0x00000400 /* Big Endian 32 mode */
+#define E1000_CTRL_FRCSPD 0x00000800 /* Force Speed */
+#define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */
+#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */
+#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */
+#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */
+#define E1000_CTRL_SWDPIN3 0x00200000 /* SWDPIN 3 value */
+#define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */
+#define E1000_CTRL_SWDPIO1 0x00800000 /* SWDPIN 1 input or output */
+#define E1000_CTRL_SWDPIO2 0x01000000 /* SWDPIN 2 input or output */
+#define E1000_CTRL_SWDPIO3 0x02000000 /* SWDPIN 3 input or output */
+#define E1000_CTRL_RST 0x04000000 /* Global reset */
+#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */
+#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */
+#define E1000_CTRL_RTE 0x20000000 /* Routing tag enable */
+#define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */
+#define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */
+
+/* Device Status */
+#define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */
+#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */
+#define E1000_STATUS_FUNC_MASK 0x0000000C /* PCI Function Mask */
+#define E1000_STATUS_FUNC_0 0x00000000 /* Function 0 */
+#define E1000_STATUS_FUNC_1 0x00000004 /* Function 1 */
+#define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */
+#define E1000_STATUS_TBIMODE 0x00000020 /* TBI mode */
+#define E1000_STATUS_SPEED_MASK 0x000000C0
+#define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */
+#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */
+#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */
+#define E1000_STATUS_ASDV 0x00000300 /* Auto speed detect value */
+#define E1000_STATUS_MTXCKOK 0x00000400 /* MTX clock running OK */
+#define E1000_STATUS_PCI66 0x00000800 /* In 66Mhz slot */
+#define E1000_STATUS_BUS64 0x00001000 /* In 64 bit slot */
+#define E1000_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */
+#define E1000_STATUS_PCIX_SPEED 0x0000C000 /* PCI-X bus speed */
+
+/* Constants used to intrepret the masked PCI-X bus speed. */
+#define E1000_STATUS_PCIX_SPEED_66 0x00000000 /* PCI-X bus speed 50-66 MHz */
+#define E1000_STATUS_PCIX_SPEED_100 0x00004000 /* PCI-X bus speed 66-100 MHz */
+#define E1000_STATUS_PCIX_SPEED_133 0x00008000 /* PCI-X bus speed 100-133 MHz */
+
+/* EEPROM/Flash Control */
+#define E1000_EECD_SK 0x00000001 /* EEPROM Clock */
+#define E1000_EECD_CS 0x00000002 /* EEPROM Chip Select */
+#define E1000_EECD_DI 0x00000004 /* EEPROM Data In */
+#define E1000_EECD_DO 0x00000008 /* EEPROM Data Out */
+#define E1000_EECD_FWE_MASK 0x00000030
+#define E1000_EECD_FWE_DIS 0x00000010 /* Disable FLASH writes */
+#define E1000_EECD_FWE_EN 0x00000020 /* Enable FLASH writes */
+#define E1000_EECD_FWE_SHIFT 4
+#define E1000_EECD_REQ 0x00000040 /* EEPROM Access Request */
+#define E1000_EECD_GNT 0x00000080 /* EEPROM Access Grant */
+#define E1000_EECD_PRES 0x00000100 /* EEPROM Present */
+#define E1000_EECD_SIZE 0x00000200 /* EEPROM Size (0=64 word 1=256 word) */
+#define E1000_EECD_ADDR_BITS 0x00000400 /* EEPROM Addressing bits based on type
+ * (0-small, 1-large) */
+#define E1000_EECD_TYPE 0x00002000 /* EEPROM Type (1-SPI, 0-Microwire) */
+#ifndef E1000_EEPROM_GRANT_ATTEMPTS
+#define E1000_EEPROM_GRANT_ATTEMPTS 1000 /* EEPROM # attempts to gain grant */
+#endif
+
+/* EEPROM Read */
+#define E1000_EERD_START 0x00000001 /* Start Read */
+#define E1000_EERD_DONE 0x00000010 /* Read Done */
+#define E1000_EERD_ADDR_SHIFT 8
+#define E1000_EERD_ADDR_MASK 0x0000FF00 /* Read Address */
+#define E1000_EERD_DATA_SHIFT 16
+#define E1000_EERD_DATA_MASK 0xFFFF0000 /* Read Data */
+
+/* SPI EEPROM Status Register */
+#define EEPROM_STATUS_RDY_SPI 0x01
+#define EEPROM_STATUS_WEN_SPI 0x02
+#define EEPROM_STATUS_BP0_SPI 0x04
+#define EEPROM_STATUS_BP1_SPI 0x08
+#define EEPROM_STATUS_WPEN_SPI 0x80
+
+/* Extended Device Control */
+#define E1000_CTRL_EXT_GPI0_EN 0x00000001 /* Maps SDP4 to GPI0 */
+#define E1000_CTRL_EXT_GPI1_EN 0x00000002 /* Maps SDP5 to GPI1 */
+#define E1000_CTRL_EXT_PHYINT_EN E1000_CTRL_EXT_GPI1_EN
+#define E1000_CTRL_EXT_GPI2_EN 0x00000004 /* Maps SDP6 to GPI2 */
+#define E1000_CTRL_EXT_GPI3_EN 0x00000008 /* Maps SDP7 to GPI3 */
+#define E1000_CTRL_EXT_SDP4_DATA 0x00000010 /* Value of SW Defineable Pin 4 */
+#define E1000_CTRL_EXT_SDP5_DATA 0x00000020 /* Value of SW Defineable Pin 5 */
+#define E1000_CTRL_EXT_PHY_INT E1000_CTRL_EXT_SDP5_DATA
+#define E1000_CTRL_EXT_SDP6_DATA 0x00000040 /* Value of SW Defineable Pin 6 */
+#define E1000_CTRL_EXT_SDP7_DATA 0x00000080 /* Value of SW Defineable Pin 7 */
+#define E1000_CTRL_EXT_SDP4_DIR 0x00000100 /* Direction of SDP4 0=in 1=out */
+#define E1000_CTRL_EXT_SDP5_DIR 0x00000200 /* Direction of SDP5 0=in 1=out */
+#define E1000_CTRL_EXT_SDP6_DIR 0x00000400 /* Direction of SDP6 0=in 1=out */
+#define E1000_CTRL_EXT_SDP7_DIR 0x00000800 /* Direction of SDP7 0=in 1=out */
+#define E1000_CTRL_EXT_ASDCHK 0x00001000 /* Initiate an ASD sequence */
+#define E1000_CTRL_EXT_EE_RST 0x00002000 /* Reinitialize from EEPROM */
+#define E1000_CTRL_EXT_IPS 0x00004000 /* Invert Power State */
+#define E1000_CTRL_EXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */
+#define E1000_CTRL_EXT_LINK_MODE_MASK 0x00C00000
+#define E1000_CTRL_EXT_LINK_MODE_GMII 0x00000000
+#define E1000_CTRL_EXT_LINK_MODE_TBI 0x00C00000
+#define E1000_CTRL_EXT_WR_WMARK_MASK 0x03000000
+#define E1000_CTRL_EXT_WR_WMARK_256 0x00000000
+#define E1000_CTRL_EXT_WR_WMARK_320 0x01000000
+#define E1000_CTRL_EXT_WR_WMARK_384 0x02000000
+#define E1000_CTRL_EXT_WR_WMARK_448 0x03000000
+
+/* MDI Control */
+#define E1000_MDIC_DATA_MASK 0x0000FFFF
+#define E1000_MDIC_REG_MASK 0x001F0000
+#define E1000_MDIC_REG_SHIFT 16
+#define E1000_MDIC_PHY_MASK 0x03E00000
+#define E1000_MDIC_PHY_SHIFT 21
+#define E1000_MDIC_OP_WRITE 0x04000000
+#define E1000_MDIC_OP_READ 0x08000000
+#define E1000_MDIC_READY 0x10000000
+#define E1000_MDIC_INT_EN 0x20000000
+#define E1000_MDIC_ERROR 0x40000000
+
+/* LED Control */
+#define E1000_LEDCTL_LED0_MODE_MASK 0x0000000F
+#define E1000_LEDCTL_LED0_MODE_SHIFT 0
+#define E1000_LEDCTL_LED0_IVRT 0x00000040
+#define E1000_LEDCTL_LED0_BLINK 0x00000080
+#define E1000_LEDCTL_LED1_MODE_MASK 0x00000F00
+#define E1000_LEDCTL_LED1_MODE_SHIFT 8
+#define E1000_LEDCTL_LED1_IVRT 0x00004000
+#define E1000_LEDCTL_LED1_BLINK 0x00008000
+#define E1000_LEDCTL_LED2_MODE_MASK 0x000F0000
+#define E1000_LEDCTL_LED2_MODE_SHIFT 16
+#define E1000_LEDCTL_LED2_IVRT 0x00400000
+#define E1000_LEDCTL_LED2_BLINK 0x00800000
+#define E1000_LEDCTL_LED3_MODE_MASK 0x0F000000
+#define E1000_LEDCTL_LED3_MODE_SHIFT 24
+#define E1000_LEDCTL_LED3_IVRT 0x40000000
+#define E1000_LEDCTL_LED3_BLINK 0x80000000
+
+#define E1000_LEDCTL_MODE_LINK_10_1000 0x0
+#define E1000_LEDCTL_MODE_LINK_100_1000 0x1
+#define E1000_LEDCTL_MODE_LINK_UP 0x2
+#define E1000_LEDCTL_MODE_ACTIVITY 0x3
+#define E1000_LEDCTL_MODE_LINK_ACTIVITY 0x4
+#define E1000_LEDCTL_MODE_LINK_10 0x5
+#define E1000_LEDCTL_MODE_LINK_100 0x6
+#define E1000_LEDCTL_MODE_LINK_1000 0x7
+#define E1000_LEDCTL_MODE_PCIX_MODE 0x8
+#define E1000_LEDCTL_MODE_FULL_DUPLEX 0x9
+#define E1000_LEDCTL_MODE_COLLISION 0xA
+#define E1000_LEDCTL_MODE_BUS_SPEED 0xB
+#define E1000_LEDCTL_MODE_BUS_SIZE 0xC
+#define E1000_LEDCTL_MODE_PAUSED 0xD
+#define E1000_LEDCTL_MODE_LED_ON 0xE
+#define E1000_LEDCTL_MODE_LED_OFF 0xF
+
+/* Receive Address */
+#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */
+
+/* Interrupt Cause Read */
+#define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */
+#define E1000_ICR_TXQE 0x00000002 /* Transmit Queue empty */
+#define E1000_ICR_LSC 0x00000004 /* Link Status Change */
+#define E1000_ICR_RXSEQ 0x00000008 /* rx sequence error */
+#define E1000_ICR_RXDMT0 0x00000010 /* rx desc min. threshold (0) */
+#define E1000_ICR_RXO 0x00000040 /* rx overrun */
+#define E1000_ICR_RXT0 0x00000080 /* rx timer intr (ring 0) */
+#define E1000_ICR_MDAC 0x00000200 /* MDIO access complete */
+#define E1000_ICR_RXCFG 0x00000400 /* RX /c/ ordered set */
+#define E1000_ICR_GPI_EN0 0x00000800 /* GP Int 0 */
+#define E1000_ICR_GPI_EN1 0x00001000 /* GP Int 1 */
+#define E1000_ICR_GPI_EN2 0x00002000 /* GP Int 2 */
+#define E1000_ICR_GPI_EN3 0x00004000 /* GP Int 3 */
+#define E1000_ICR_TXD_LOW 0x00008000
+#define E1000_ICR_SRPD 0x00010000
+
+/* Interrupt Cause Set */
+#define E1000_ICS_TXDW E1000_ICR_TXDW /* Transmit desc written back */
+#define E1000_ICS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */
+#define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */
+#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */
+#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */
+#define E1000_ICS_RXO E1000_ICR_RXO /* rx overrun */
+#define E1000_ICS_RXT0 E1000_ICR_RXT0 /* rx timer intr */
+#define E1000_ICS_MDAC E1000_ICR_MDAC /* MDIO access complete */
+#define E1000_ICS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */
+#define E1000_ICS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */
+#define E1000_ICS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */
+#define E1000_ICS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */
+#define E1000_ICS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */
+#define E1000_ICS_TXD_LOW E1000_ICR_TXD_LOW
+#define E1000_ICS_SRPD E1000_ICR_SRPD
+
+/* Interrupt Mask Set */
+#define E1000_IMS_TXDW E1000_ICR_TXDW /* Transmit desc written back */
+#define E1000_IMS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */
+#define E1000_IMS_LSC E1000_ICR_LSC /* Link Status Change */
+#define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */
+#define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */
+#define E1000_IMS_RXO E1000_ICR_RXO /* rx overrun */
+#define E1000_IMS_RXT0 E1000_ICR_RXT0 /* rx timer intr */
+#define E1000_IMS_MDAC E1000_ICR_MDAC /* MDIO access complete */
+#define E1000_IMS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */
+#define E1000_IMS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */
+#define E1000_IMS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */
+#define E1000_IMS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */
+#define E1000_IMS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */
+#define E1000_IMS_TXD_LOW E1000_ICR_TXD_LOW
+#define E1000_IMS_SRPD E1000_ICR_SRPD
+
+/* Interrupt Mask Clear */
+#define E1000_IMC_TXDW E1000_ICR_TXDW /* Transmit desc written back */
+#define E1000_IMC_TXQE E1000_ICR_TXQE /* Transmit Queue empty */
+#define E1000_IMC_LSC E1000_ICR_LSC /* Link Status Change */
+#define E1000_IMC_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */
+#define E1000_IMC_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */
+#define E1000_IMC_RXO E1000_ICR_RXO /* rx overrun */
+#define E1000_IMC_RXT0 E1000_ICR_RXT0 /* rx timer intr */
+#define E1000_IMC_MDAC E1000_ICR_MDAC /* MDIO access complete */
+#define E1000_IMC_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */
+#define E1000_IMC_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */
+#define E1000_IMC_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */
+#define E1000_IMC_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */
+#define E1000_IMC_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */
+#define E1000_IMC_TXD_LOW E1000_ICR_TXD_LOW
+#define E1000_IMC_SRPD E1000_ICR_SRPD
+
+/* Receive Control */
+#define E1000_RCTL_RST 0x00000001 /* Software reset */
+#define E1000_RCTL_EN 0x00000002 /* enable */
+#define E1000_RCTL_SBP 0x00000004 /* store bad packet */
+#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */
+#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enab */
+#define E1000_RCTL_LPE 0x00000020 /* long packet enable */
+#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */
+#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */
+#define E1000_RCTL_LBM_SLP 0x00000080 /* serial link loopback mode */
+#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */
+#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */
+#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */
+#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */
+#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */
+#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 11:0 */
+#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 12:1 */
+#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 13:2 */
+#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */
+#define E1000_RCTL_MDR 0x00004000 /* multicast desc ring 0 */
+#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */
+/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */
+#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */
+#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */
+#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */
+#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */
+/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */
+#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */
+#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */
+#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */
+#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */
+#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */
+#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */
+#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */
+#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */
+#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */
+
+/* Receive Descriptor */
+#define E1000_RDT_DELAY 0x0000ffff /* Delay timer (1=1024us) */
+#define E1000_RDT_FPDB 0x80000000 /* Flush descriptor block */
+#define E1000_RDLEN_LEN 0x0007ff80 /* descriptor length */
+#define E1000_RDH_RDH 0x0000ffff /* receive descriptor head */
+#define E1000_RDT_RDT 0x0000ffff /* receive descriptor tail */
+
+/* Flow Control */
+#define E1000_FCRTH_RTH 0x0000FFF8 /* Mask Bits[15:3] for RTH */
+#define E1000_FCRTH_XFCE 0x80000000 /* External Flow Control Enable */
+#define E1000_FCRTL_RTL 0x0000FFF8 /* Mask Bits[15:3] for RTL */
+#define E1000_FCRTL_XONE 0x80000000 /* Enable XON frame transmission */
+
+/* Receive Descriptor Control */
+#define E1000_RXDCTL_PTHRESH 0x0000003F /* RXDCTL Prefetch Threshold */
+#define E1000_RXDCTL_HTHRESH 0x00003F00 /* RXDCTL Host Threshold */
+#define E1000_RXDCTL_WTHRESH 0x003F0000 /* RXDCTL Writeback Threshold */
+#define E1000_RXDCTL_GRAN 0x01000000 /* RXDCTL Granularity */
+
+/* Transmit Descriptor Control */
+#define E1000_TXDCTL_PTHRESH 0x000000FF /* TXDCTL Prefetch Threshold */
+#define E1000_TXDCTL_HTHRESH 0x0000FF00 /* TXDCTL Host Threshold */
+#define E1000_TXDCTL_WTHRESH 0x00FF0000 /* TXDCTL Writeback Threshold */
+#define E1000_TXDCTL_GRAN 0x01000000 /* TXDCTL Granularity */
+#define E1000_TXDCTL_LWTHRESH 0xFE000000 /* TXDCTL Low Threshold */
+#define E1000_TXDCTL_FULL_TX_DESC_WB 0x01010000 /* GRAN=1, WTHRESH=1 */
+
+/* Transmit Configuration Word */
+#define E1000_TXCW_FD 0x00000020 /* TXCW full duplex */
+#define E1000_TXCW_HD 0x00000040 /* TXCW half duplex */
+#define E1000_TXCW_PAUSE 0x00000080 /* TXCW sym pause request */
+#define E1000_TXCW_ASM_DIR 0x00000100 /* TXCW astm pause direction */
+#define E1000_TXCW_PAUSE_MASK 0x00000180 /* TXCW pause request mask */
+#define E1000_TXCW_RF 0x00003000 /* TXCW remote fault */
+#define E1000_TXCW_NP 0x00008000 /* TXCW next page */
+#define E1000_TXCW_CW 0x0000ffff /* TxConfigWord mask */
+#define E1000_TXCW_TXC 0x40000000 /* Transmit Config control */
+#define E1000_TXCW_ANE 0x80000000 /* Auto-neg enable */
+
+/* Receive Configuration Word */
+#define E1000_RXCW_CW 0x0000ffff /* RxConfigWord mask */
+#define E1000_RXCW_NC 0x04000000 /* Receive config no carrier */
+#define E1000_RXCW_IV 0x08000000 /* Receive config invalid */
+#define E1000_RXCW_CC 0x10000000 /* Receive config change */
+#define E1000_RXCW_C 0x20000000 /* Receive config */
+#define E1000_RXCW_SYNCH 0x40000000 /* Receive config synch */
+#define E1000_RXCW_ANC 0x80000000 /* Auto-neg complete */
+
+/* Transmit Control */
+#define E1000_TCTL_RST 0x00000001 /* software reset */
+#define E1000_TCTL_EN 0x00000002 /* enable tx */
+#define E1000_TCTL_BCE 0x00000004 /* busy check enable */
+#define E1000_TCTL_PSP 0x00000008 /* pad short packets */
+#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */
+#define E1000_TCTL_COLD 0x003ff000 /* collision distance */
+#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */
+#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */
+#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */
+#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */
+
+/* Receive Checksum Control */
+#define E1000_RXCSUM_PCSS_MASK 0x000000FF /* Packet Checksum Start */
+#define E1000_RXCSUM_IPOFL 0x00000100 /* IPv4 checksum offload */
+#define E1000_RXCSUM_TUOFL 0x00000200 /* TCP / UDP checksum offload */
+#define E1000_RXCSUM_IPV6OFL 0x00000400 /* IPv6 checksum offload */
+
+/* Definitions for power management and wakeup registers */
+/* Wake Up Control */
+#define E1000_WUC_APME 0x00000001 /* APM Enable */
+#define E1000_WUC_PME_EN 0x00000002 /* PME Enable */
+#define E1000_WUC_PME_STATUS 0x00000004 /* PME Status */
+#define E1000_WUC_APMPME 0x00000008 /* Assert PME on APM Wakeup */
+#define E1000_WUC_SPM 0x80000000 /* Enable SPM */
+
+/* Wake Up Filter Control */
+#define E1000_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */
+#define E1000_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */
+#define E1000_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */
+#define E1000_WUFC_MC 0x00000008 /* Directed Multicast Wakeup Enable */
+#define E1000_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */
+#define E1000_WUFC_ARP 0x00000020 /* ARP Request Packet Wakeup Enable */
+#define E1000_WUFC_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Enable */
+#define E1000_WUFC_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Enable */
+#define E1000_WUFC_FLX0 0x00010000 /* Flexible Filter 0 Enable */
+#define E1000_WUFC_FLX1 0x00020000 /* Flexible Filter 1 Enable */
+#define E1000_WUFC_FLX2 0x00040000 /* Flexible Filter 2 Enable */
+#define E1000_WUFC_FLX3 0x00080000 /* Flexible Filter 3 Enable */
+#define E1000_WUFC_ALL_FILTERS 0x000F00FF /* Mask for all wakeup filters */
+#define E1000_WUFC_FLX_OFFSET 16 /* Offset to the Flexible Filters bits */
+#define E1000_WUFC_FLX_FILTERS 0x000F0000 /* Mask for the 4 flexible filters */
+
+/* Wake Up Status */
+#define E1000_WUS_LNKC 0x00000001 /* Link Status Changed */
+#define E1000_WUS_MAG 0x00000002 /* Magic Packet Received */
+#define E1000_WUS_EX 0x00000004 /* Directed Exact Received */
+#define E1000_WUS_MC 0x00000008 /* Directed Multicast Received */
+#define E1000_WUS_BC 0x00000010 /* Broadcast Received */
+#define E1000_WUS_ARP 0x00000020 /* ARP Request Packet Received */
+#define E1000_WUS_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Received */
+#define E1000_WUS_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Received */
+#define E1000_WUS_FLX0 0x00010000 /* Flexible Filter 0 Match */
+#define E1000_WUS_FLX1 0x00020000 /* Flexible Filter 1 Match */
+#define E1000_WUS_FLX2 0x00040000 /* Flexible Filter 2 Match */
+#define E1000_WUS_FLX3 0x00080000 /* Flexible Filter 3 Match */
+#define E1000_WUS_FLX_FILTERS 0x000F0000 /* Mask for the 4 flexible filters */
+
+/* Management Control */
+#define E1000_MANC_SMBUS_EN 0x00000001 /* SMBus Enabled - RO */
+#define E1000_MANC_ASF_EN 0x00000002 /* ASF Enabled - RO */
+#define E1000_MANC_R_ON_FORCE 0x00000004 /* Reset on Force TCO - RO */
+#define E1000_MANC_RMCP_EN 0x00000100 /* Enable RCMP 026Fh Filtering */
+#define E1000_MANC_0298_EN 0x00000200 /* Enable RCMP 0298h Filtering */
+#define E1000_MANC_IPV4_EN 0x00000400 /* Enable IPv4 */
+#define E1000_MANC_IPV6_EN 0x00000800 /* Enable IPv6 */
+#define E1000_MANC_SNAP_EN 0x00001000 /* Accept LLC/SNAP */
+#define E1000_MANC_ARP_EN 0x00002000 /* Enable ARP Request Filtering */
+#define E1000_MANC_NEIGHBOR_EN 0x00004000 /* Enable Neighbor Discovery
+ * Filtering */
+#define E1000_MANC_TCO_RESET 0x00010000 /* TCO Reset Occurred */
+#define E1000_MANC_RCV_TCO_EN 0x00020000 /* Receive TCO Packets Enabled */
+#define E1000_MANC_REPORT_STATUS 0x00040000 /* Status Reporting Enabled */
+#define E1000_MANC_SMB_REQ 0x01000000 /* SMBus Request */
+#define E1000_MANC_SMB_GNT 0x02000000 /* SMBus Grant */
+#define E1000_MANC_SMB_CLK_IN 0x04000000 /* SMBus Clock In */
+#define E1000_MANC_SMB_DATA_IN 0x08000000 /* SMBus Data In */
+#define E1000_MANC_SMB_DATA_OUT 0x10000000 /* SMBus Data Out */
+#define E1000_MANC_SMB_CLK_OUT 0x20000000 /* SMBus Clock Out */
+
+#define E1000_MANC_SMB_DATA_OUT_SHIFT 28 /* SMBus Data Out Shift */
+#define E1000_MANC_SMB_CLK_OUT_SHIFT 29 /* SMBus Clock Out Shift */
+
+/* Wake Up Packet Length */
+#define E1000_WUPL_LENGTH_MASK 0x0FFF /* Only the lower 12 bits are valid */
+
+#define E1000_MDALIGN 4096
+
+/* EEPROM Commands - Microwire */
+#define EEPROM_READ_OPCODE_MICROWIRE 0x6 /* EEPROM read opcode */
+#define EEPROM_WRITE_OPCODE_MICROWIRE 0x5 /* EEPROM write opcode */
+#define EEPROM_ERASE_OPCODE_MICROWIRE 0x7 /* EEPROM erase opcode */
+#define EEPROM_EWEN_OPCODE_MICROWIRE 0x13 /* EEPROM erase/write enable */
+#define EEPROM_EWDS_OPCODE_MICROWIRE 0x10 /* EEPROM erast/write disable */
+
+/* EEPROM Commands - SPI */
+#define EEPROM_MAX_RETRY_SPI 5000 /* Max wait of 5ms, for RDY signal */
+#define EEPROM_READ_OPCODE_SPI 0x3 /* EEPROM read opcode */
+#define EEPROM_WRITE_OPCODE_SPI 0x2 /* EEPROM write opcode */
+#define EEPROM_A8_OPCODE_SPI 0x8 /* opcode bit-3 = address bit-8 */
+#define EEPROM_WREN_OPCODE_SPI 0x6 /* EEPROM set Write Enable latch */
+#define EEPROM_WRDI_OPCODE_SPI 0x4 /* EEPROM reset Write Enable latch */
+#define EEPROM_RDSR_OPCODE_SPI 0x5 /* EEPROM read Status register */
+#define EEPROM_WRSR_OPCODE_SPI 0x1 /* EEPROM write Status register */
+
+/* EEPROM Size definitions */
+#define EEPROM_SIZE_16KB 0x1800
+#define EEPROM_SIZE_8KB 0x1400
+#define EEPROM_SIZE_4KB 0x1000
+#define EEPROM_SIZE_2KB 0x0C00
+#define EEPROM_SIZE_1KB 0x0800
+#define EEPROM_SIZE_512B 0x0400
+#define EEPROM_SIZE_128B 0x0000
+#define EEPROM_SIZE_MASK 0x1C00
+
+/* EEPROM Word Offsets */
+#define EEPROM_COMPAT 0x0003
+#define EEPROM_ID_LED_SETTINGS 0x0004
+#define EEPROM_SERDES_AMPLITUDE 0x0006 /* For SERDES output amplitude adjustment. */
+#define EEPROM_INIT_CONTROL1_REG 0x000A
+#define EEPROM_INIT_CONTROL2_REG 0x000F
+#define EEPROM_INIT_CONTROL3_PORT_B 0x0014
+#define EEPROM_INIT_CONTROL3_PORT_A 0x0024
+#define EEPROM_CFG 0x0012
+#define EEPROM_FLASH_VERSION 0x0032
+#define EEPROM_CHECKSUM_REG 0x003F
+
+/* Word definitions for ID LED Settings */
+#define ID_LED_RESERVED_0000 0x0000
+#define ID_LED_RESERVED_FFFF 0xFFFF
+#define ID_LED_DEFAULT ((ID_LED_OFF1_ON2 << 12) | \
+ (ID_LED_OFF1_OFF2 << 8) | \
+ (ID_LED_DEF1_DEF2 << 4) | \
+ (ID_LED_DEF1_DEF2))
+#define ID_LED_DEF1_DEF2 0x1
+#define ID_LED_DEF1_ON2 0x2
+#define ID_LED_DEF1_OFF2 0x3
+#define ID_LED_ON1_DEF2 0x4
+#define ID_LED_ON1_ON2 0x5
+#define ID_LED_ON1_OFF2 0x6
+#define ID_LED_OFF1_DEF2 0x7
+#define ID_LED_OFF1_ON2 0x8
+#define ID_LED_OFF1_OFF2 0x9
+
+#define IGP_ACTIVITY_LED_MASK 0xFFFFF0FF
+#define IGP_ACTIVITY_LED_ENABLE 0x0300
+#define IGP_LED3_MODE 0x07000000
+
+
+/* Mask bits for SERDES amplitude adjustment in Word 6 of the EEPROM */
+#define EEPROM_SERDES_AMPLITUDE_MASK 0x000F
+
+/* Mask bits for fields in Word 0x0a of the EEPROM */
+#define EEPROM_WORD0A_ILOS 0x0010
+#define EEPROM_WORD0A_SWDPIO 0x01E0
+#define EEPROM_WORD0A_LRST 0x0200
+#define EEPROM_WORD0A_FD 0x0400
+#define EEPROM_WORD0A_66MHZ 0x0800
+
+/* Mask bits for fields in Word 0x0f of the EEPROM */
+#define EEPROM_WORD0F_PAUSE_MASK 0x3000
+#define EEPROM_WORD0F_PAUSE 0x1000
+#define EEPROM_WORD0F_ASM_DIR 0x2000
+#define EEPROM_WORD0F_ANE 0x0800
+#define EEPROM_WORD0F_SWPDIO_EXT 0x00F0
+
+/* For checksumming, the sum of all words in the EEPROM should equal 0xBABA. */
+#define EEPROM_SUM 0xBABA
+
+/* EEPROM Map defines (WORD OFFSETS)*/
+#define EEPROM_NODE_ADDRESS_BYTE_0 0
+#define EEPROM_PBA_BYTE_1 8
+
+#define EEPROM_RESERVED_WORD 0xFFFF
+
+/* EEPROM Map Sizes (Byte Counts) */
+#define PBA_SIZE 4
+
+/* Collision related configuration parameters */
+#define E1000_COLLISION_THRESHOLD 16
+#define E1000_CT_SHIFT 4
+#define E1000_COLLISION_DISTANCE 64
+#define E1000_FDX_COLLISION_DISTANCE E1000_COLLISION_DISTANCE
+#define E1000_HDX_COLLISION_DISTANCE E1000_COLLISION_DISTANCE
+#define E1000_COLD_SHIFT 12
+
+/* Number of Transmit and Receive Descriptors must be a multiple of 8 */
+#define REQ_TX_DESCRIPTOR_MULTIPLE 8
+#define REQ_RX_DESCRIPTOR_MULTIPLE 8
+
+/* Default values for the transmit IPG register */
+#define DEFAULT_82542_TIPG_IPGT 10
+#define DEFAULT_82543_TIPG_IPGT_FIBER 9
+#define DEFAULT_82543_TIPG_IPGT_COPPER 8
+
+#define E1000_TIPG_IPGT_MASK 0x000003FF
+#define E1000_TIPG_IPGR1_MASK 0x000FFC00
+#define E1000_TIPG_IPGR2_MASK 0x3FF00000
+
+#define DEFAULT_82542_TIPG_IPGR1 2
+#define DEFAULT_82543_TIPG_IPGR1 8
+#define E1000_TIPG_IPGR1_SHIFT 10
+
+#define DEFAULT_82542_TIPG_IPGR2 10
+#define DEFAULT_82543_TIPG_IPGR2 6
+#define E1000_TIPG_IPGR2_SHIFT 20
+
+#define E1000_TXDMAC_DPP 0x00000001
+
+/* Adaptive IFS defines */
+#define TX_THRESHOLD_START 8
+#define TX_THRESHOLD_INCREMENT 10
+#define TX_THRESHOLD_DECREMENT 1
+#define TX_THRESHOLD_STOP 190
+#define TX_THRESHOLD_DISABLE 0
+#define TX_THRESHOLD_TIMER_MS 10000
+#define MIN_NUM_XMITS 1000
+#define IFS_MAX 80
+#define IFS_STEP 10
+#define IFS_MIN 40
+#define IFS_RATIO 4
+
+/* PBA constants */
+#define E1000_PBA_16K 0x0010 /* 16KB, default TX allocation */
+#define E1000_PBA_22K 0x0016
+#define E1000_PBA_24K 0x0018
+#define E1000_PBA_30K 0x001E
+#define E1000_PBA_40K 0x0028
+#define E1000_PBA_48K 0x0030 /* 48KB, default RX allocation */
+
+/* Flow Control Constants */
+#define FLOW_CONTROL_ADDRESS_LOW 0x00C28001
+#define FLOW_CONTROL_ADDRESS_HIGH 0x00000100
+#define FLOW_CONTROL_TYPE 0x8808
+
+/* The historical defaults for the flow control values are given below. */
+#define FC_DEFAULT_HI_THRESH (0x8000) /* 32KB */
+#define FC_DEFAULT_LO_THRESH (0x4000) /* 16KB */
+#define FC_DEFAULT_TX_TIMER (0x100) /* ~130 us */
+
+/* PCIX Config space */
+#define PCIX_COMMAND_REGISTER 0xE6
+#define PCIX_STATUS_REGISTER_LO 0xE8
+#define PCIX_STATUS_REGISTER_HI 0xEA
+
+#define PCIX_COMMAND_MMRBC_MASK 0x000C
+#define PCIX_COMMAND_MMRBC_SHIFT 0x2
+#define PCIX_STATUS_HI_MMRBC_MASK 0x0060
+#define PCIX_STATUS_HI_MMRBC_SHIFT 0x5
+#define PCIX_STATUS_HI_MMRBC_4K 0x3
+#define PCIX_STATUS_HI_MMRBC_2K 0x2
+
+
+/* Number of bits required to shift right the "pause" bits from the
+ * EEPROM (bits 13:12) to the "pause" (bits 8:7) field in the TXCW register.
+ */
+#define PAUSE_SHIFT 5
+
+/* Number of bits required to shift left the "SWDPIO" bits from the
+ * EEPROM (bits 8:5) to the "SWDPIO" (bits 25:22) field in the CTRL register.
+ */
+#define SWDPIO_SHIFT 17
+
+/* Number of bits required to shift left the "SWDPIO_EXT" bits from the
+ * EEPROM word F (bits 7:4) to the bits 11:8 of The Extended CTRL register.
+ */
+#define SWDPIO__EXT_SHIFT 4
+
+/* Number of bits required to shift left the "ILOS" bit from the EEPROM
+ * (bit 4) to the "ILOS" (bit 7) field in the CTRL register.
+ */
+#define ILOS_SHIFT 3
+
+
+#define RECEIVE_BUFFER_ALIGN_SIZE (256)
+
+/* Number of milliseconds we wait for auto-negotiation to complete */
+#define LINK_UP_TIMEOUT 500
+
+#define E1000_TX_BUFFER_SIZE ((uint32_t)1514)
+
+/* The carrier extension symbol, as received by the NIC. */
+#define CARRIER_EXTENSION 0x0F
+
+/* TBI_ACCEPT macro definition:
+ *
+ * This macro requires:
+ * adapter = a pointer to struct e1000_hw
+ * status = the 8 bit status field of the RX descriptor with EOP set
+ * error = the 8 bit error field of the RX descriptor with EOP set
+ * length = the sum of all the length fields of the RX descriptors that
+ * make up the current frame
+ * last_byte = the last byte of the frame DMAed by the hardware
+ * max_frame_length = the maximum frame length we want to accept.
+ * min_frame_length = the minimum frame length we want to accept.
+ *
+ * This macro is a conditional that should be used in the interrupt
+ * handler's Rx processing routine when RxErrors have been detected.
+ *
+ * Typical use:
+ * ...
+ * if (TBI_ACCEPT) {
+ * accept_frame = TRUE;
+ * e1000_tbi_adjust_stats(adapter, MacAddress);
+ * frame_length--;
+ * } else {
+ * accept_frame = FALSE;
+ * }
+ * ...
+ */
+
+#define TBI_ACCEPT(adapter, status, errors, length, last_byte) \
+ ((adapter)->tbi_compatibility_on && \
+ (((errors) & E1000_RXD_ERR_FRAME_ERR_MASK) == E1000_RXD_ERR_CE) && \
+ ((last_byte) == CARRIER_EXTENSION) && \
+ (((status) & E1000_RXD_STAT_VP) ? \
+ (((length) > ((adapter)->min_frame_size - VLAN_TAG_SIZE)) && \
+ ((length) <= ((adapter)->max_frame_size + 1))) : \
+ (((length) > (adapter)->min_frame_size) && \
+ ((length) <= ((adapter)->max_frame_size + VLAN_TAG_SIZE + 1)))))
+
+
+/* Structures, enums, and macros for the PHY */
+
+/* Bit definitions for the Management Data IO (MDIO) and Management Data
+ * Clock (MDC) pins in the Device Control Register.
+ */
+#define E1000_CTRL_PHY_RESET_DIR E1000_CTRL_SWDPIO0
+#define E1000_CTRL_PHY_RESET E1000_CTRL_SWDPIN0
+#define E1000_CTRL_MDIO_DIR E1000_CTRL_SWDPIO2
+#define E1000_CTRL_MDIO E1000_CTRL_SWDPIN2
+#define E1000_CTRL_MDC_DIR E1000_CTRL_SWDPIO3
+#define E1000_CTRL_MDC E1000_CTRL_SWDPIN3
+#define E1000_CTRL_PHY_RESET_DIR4 E1000_CTRL_EXT_SDP4_DIR
+#define E1000_CTRL_PHY_RESET4 E1000_CTRL_EXT_SDP4_DATA
+
+/* PHY 1000 MII Register/Bit Definitions */
+/* PHY Registers defined by IEEE */
+#define PHY_CTRL 0x00 /* Control Register */
+#define PHY_STATUS 0x01 /* Status Regiser */
+#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */
+#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */
+#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */
+#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */
+#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */
+#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */
+#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */
+#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */
+#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */
+#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */
+
+/* M88E1000 Specific Registers */
+#define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */
+#define M88E1000_PHY_SPEC_STATUS 0x11 /* PHY Specific Status Register */
+#define M88E1000_INT_ENABLE 0x12 /* Interrupt Enable Register */
+#define M88E1000_INT_STATUS 0x13 /* Interrupt Status Register */
+#define M88E1000_EXT_PHY_SPEC_CTRL 0x14 /* Extended PHY Specific Control */
+#define M88E1000_RX_ERR_CNTR 0x15 /* Receive Error Counter */
+
+#define M88E1000_PHY_EXT_CTRL 0x1A /* PHY extend control register */
+#define M88E1000_PHY_PAGE_SELECT 0x1D /* Reg 29 for page number setting */
+#define M88E1000_PHY_GEN_CONTROL 0x1E /* Its meaning depends on reg 29 */
+#define M88E1000_PHY_VCO_REG_BIT8 0x100 /* Bits 8 & 11 are adjusted for */
+#define M88E1000_PHY_VCO_REG_BIT11 0x800 /* improved BER performance */
+
+#define IGP01E1000_IEEE_REGS_PAGE 0x0000
+#define IGP01E1000_IEEE_RESTART_AUTONEG 0x3300
+#define IGP01E1000_IEEE_FORCE_GIGA 0x0140
+
+/* IGP01E1000 Specific Registers */
+#define IGP01E1000_PHY_PORT_CONFIG 0x10 /* PHY Specific Port Config Register */
+#define IGP01E1000_PHY_PORT_STATUS 0x11 /* PHY Specific Status Register */
+#define IGP01E1000_PHY_PORT_CTRL 0x12 /* PHY Specific Control Register */
+#define IGP01E1000_PHY_LINK_HEALTH 0x13 /* PHY Link Health Register */
+#define IGP01E1000_GMII_FIFO 0x14 /* GMII FIFO Register */
+#define IGP01E1000_PHY_CHANNEL_QUALITY 0x15 /* PHY Channel Quality Register */
+#define IGP01E1000_PHY_PAGE_SELECT 0x1F /* PHY Page Select Core Register */
+
+/* IGP01E1000 AGC Registers - stores the cable length values*/
+#define IGP01E1000_PHY_AGC_A 0x1172
+#define IGP01E1000_PHY_AGC_B 0x1272
+#define IGP01E1000_PHY_AGC_C 0x1472
+#define IGP01E1000_PHY_AGC_D 0x1872
+
+/* IGP01E1000 DSP Reset Register */
+#define IGP01E1000_PHY_DSP_RESET 0x1F33
+#define IGP01E1000_PHY_DSP_SET 0x1F71
+#define IGP01E1000_PHY_DSP_FFE 0x1F35
+
+#define IGP01E1000_PHY_CHANNEL_NUM 4
+#define IGP01E1000_PHY_AGC_PARAM_A 0x1171
+#define IGP01E1000_PHY_AGC_PARAM_B 0x1271
+#define IGP01E1000_PHY_AGC_PARAM_C 0x1471
+#define IGP01E1000_PHY_AGC_PARAM_D 0x1871
+
+#define IGP01E1000_PHY_EDAC_MU_INDEX 0xC000
+#define IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS 0x8000
+
+#define IGP01E1000_PHY_ANALOG_TX_STATE 0x2890
+#define IGP01E1000_PHY_ANALOG_CLASS_A 0x2000
+#define IGP01E1000_PHY_FORCE_ANALOG_ENABLE 0x0004
+#define IGP01E1000_PHY_DSP_FFE_CM_CP 0x0069
+
+#define IGP01E1000_PHY_DSP_FFE_DEFAULT 0x002A
+/* IGP01E1000 PCS Initialization register - stores the polarity status when
+ * speed = 1000 Mbps. */
+#define IGP01E1000_PHY_PCS_INIT_REG 0x00B4
+#define IGP01E1000_PHY_PCS_CTRL_REG 0x00B5
+
+#define IGP01E1000_ANALOG_REGS_PAGE 0x20C0
+
+#define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */
+#define MAX_PHY_MULTI_PAGE_REG 0xF /*Registers that are equal on all pages*/
+/* PHY Control Register */
+#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */
+#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */
+#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */
+#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */
+#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */
+#define MII_CR_POWER_DOWN 0x0800 /* Power down */
+#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */
+#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */
+#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */
+#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */
+
+/* PHY Status Register */
+#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */
+#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */
+#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */
+#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */
+#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */
+#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */
+#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */
+#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */
+#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */
+#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */
+#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */
+#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */
+#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */
+#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */
+#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */
+
+/* Autoneg Advertisement Register */
+#define NWAY_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */
+#define NWAY_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */
+#define NWAY_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */
+#define NWAY_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */
+#define NWAY_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */
+#define NWAY_AR_100T4_CAPS 0x0200 /* 100T4 Capable */
+#define NWAY_AR_PAUSE 0x0400 /* Pause operation desired */
+#define NWAY_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */
+#define NWAY_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */
+#define NWAY_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */
+
+/* Link Partner Ability Register (Base Page) */
+#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */
+#define NWAY_LPAR_10T_HD_CAPS 0x0020 /* LP is 10T Half Duplex Capable */
+#define NWAY_LPAR_10T_FD_CAPS 0x0040 /* LP is 10T Full Duplex Capable */
+#define NWAY_LPAR_100TX_HD_CAPS 0x0080 /* LP is 100TX Half Duplex Capable */
+#define NWAY_LPAR_100TX_FD_CAPS 0x0100 /* LP is 100TX Full Duplex Capable */
+#define NWAY_LPAR_100T4_CAPS 0x0200 /* LP is 100T4 Capable */
+#define NWAY_LPAR_PAUSE 0x0400 /* LP Pause operation desired */
+#define NWAY_LPAR_ASM_DIR 0x0800 /* LP Asymmetric Pause Direction bit */
+#define NWAY_LPAR_REMOTE_FAULT 0x2000 /* LP has detected Remote Fault */
+#define NWAY_LPAR_ACKNOWLEDGE 0x4000 /* LP has rx'd link code word */
+#define NWAY_LPAR_NEXT_PAGE 0x8000 /* Next Page ability supported */
+
+/* Autoneg Expansion Register */
+#define NWAY_ER_LP_NWAY_CAPS 0x0001 /* LP has Auto Neg Capability */
+#define NWAY_ER_PAGE_RXD 0x0002 /* LP is 10T Half Duplex Capable */
+#define NWAY_ER_NEXT_PAGE_CAPS 0x0004 /* LP is 10T Full Duplex Capable */
+#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP is 100TX Half Duplex Capable */
+#define NWAY_ER_PAR_DETECT_FAULT 0x0010 /* LP is 100TX Full Duplex Capable */
+
+/* Next Page TX Register */
+#define NPTX_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */
+#define NPTX_TOGGLE 0x0800 /* Toggles between exchanges
+ * of different NP
+ */
+#define NPTX_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg
+ * 0 = cannot comply with msg
+ */
+#define NPTX_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */
+#define NPTX_NEXT_PAGE 0x8000 /* 1 = addition NP will follow
+ * 0 = sending last NP
+ */
+
+/* Link Partner Next Page Register */
+#define LP_RNPR_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */
+#define LP_RNPR_TOGGLE 0x0800 /* Toggles between exchanges
+ * of different NP
+ */
+#define LP_RNPR_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg
+ * 0 = cannot comply with msg
+ */
+#define LP_RNPR_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */
+#define LP_RNPR_ACKNOWLDGE 0x4000 /* 1 = ACK / 0 = NO ACK */
+#define LP_RNPR_NEXT_PAGE 0x8000 /* 1 = addition NP will follow
+ * 0 = sending last NP
+ */
+
+/* 1000BASE-T Control Register */
+#define CR_1000T_ASYM_PAUSE 0x0080 /* Advertise asymmetric pause bit */
+#define CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */
+#define CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */
+#define CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */
+ /* 0=DTE device */
+#define CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */
+ /* 0=Configure PHY as Slave */
+#define CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */
+ /* 0=Automatic Master/Slave config */
+#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */
+#define CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */
+#define CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */
+#define CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */
+#define CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */
+
+/* 1000BASE-T Status Register */
+#define SR_1000T_IDLE_ERROR_CNT 0x00FF /* Num idle errors since last read */
+#define SR_1000T_ASYM_PAUSE_DIR 0x0100 /* LP asymmetric pause direction bit */
+#define SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */
+#define SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */
+#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */
+#define SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */
+#define SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */
+#define SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */
+#define SR_1000T_REMOTE_RX_STATUS_SHIFT 12
+#define SR_1000T_LOCAL_RX_STATUS_SHIFT 13
+#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT 5
+#define FFE_IDLE_ERR_COUNT_TIMEOUT_20 20
+#define FFE_IDLE_ERR_COUNT_TIMEOUT_100 100
+
+/* Extended Status Register */
+#define IEEE_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */
+#define IEEE_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */
+#define IEEE_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */
+#define IEEE_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */
+
+#define PHY_TX_POLARITY_MASK 0x0100 /* register 10h bit 8 (polarity bit) */
+#define PHY_TX_NORMAL_POLARITY 0 /* register 10h bit 8 (normal polarity) */
+
+#define AUTO_POLARITY_DISABLE 0x0010 /* register 11h bit 4 */
+ /* (0=enable, 1=disable) */
+
+/* M88E1000 PHY Specific Control Register */
+#define M88E1000_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Function disabled */
+#define M88E1000_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enabled */
+#define M88E1000_PSCR_SQE_TEST 0x0004 /* 1=SQE Test enabled */
+#define M88E1000_PSCR_CLK125_DISABLE 0x0010 /* 1=CLK125 low,
+ * 0=CLK125 toggling
+ */
+#define M88E1000_PSCR_MDI_MANUAL_MODE 0x0000 /* MDI Crossover Mode bits 6:5 */
+ /* Manual MDI configuration */
+#define M88E1000_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */
+#define M88E1000_PSCR_AUTO_X_1000T 0x0040 /* 1000BASE-T: Auto crossover,
+ * 100BASE-TX/10BASE-T:
+ * MDI Mode
+ */
+#define M88E1000_PSCR_AUTO_X_MODE 0x0060 /* Auto crossover enabled
+ * all speeds.
+ */
+#define M88E1000_PSCR_10BT_EXT_DIST_ENABLE 0x0080
+ /* 1=Enable Extended 10BASE-T distance
+ * (Lower 10BASE-T RX Threshold)
+ * 0=Normal 10BASE-T RX Threshold */
+#define M88E1000_PSCR_MII_5BIT_ENABLE 0x0100
+ /* 1=5-Bit interface in 100BASE-TX
+ * 0=MII interface in 100BASE-TX */
+#define M88E1000_PSCR_SCRAMBLER_DISABLE 0x0200 /* 1=Scrambler disable */
+#define M88E1000_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force link good */
+#define M88E1000_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */
+
+#define M88E1000_PSCR_POLARITY_REVERSAL_SHIFT 1
+#define M88E1000_PSCR_AUTO_X_MODE_SHIFT 5
+#define M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT 7
+
+/* M88E1000 PHY Specific Status Register */
+#define M88E1000_PSSR_JABBER 0x0001 /* 1=Jabber */
+#define M88E1000_PSSR_REV_POLARITY 0x0002 /* 1=Polarity reversed */
+#define M88E1000_PSSR_DOWNSHIFT 0x0020 /* 1=Downshifted */
+#define M88E1000_PSSR_MDIX 0x0040 /* 1=MDIX; 0=MDI */
+#define M88E1000_PSSR_CABLE_LENGTH 0x0380 /* 0=<50M;1=50-80M;2=80-110M;
+ * 3=110-140M;4=>140M */
+#define M88E1000_PSSR_LINK 0x0400 /* 1=Link up, 0=Link down */
+#define M88E1000_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */
+#define M88E1000_PSSR_PAGE_RCVD 0x1000 /* 1=Page received */
+#define M88E1000_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */
+#define M88E1000_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */
+#define M88E1000_PSSR_10MBS 0x0000 /* 00=10Mbs */
+#define M88E1000_PSSR_100MBS 0x4000 /* 01=100Mbs */
+#define M88E1000_PSSR_1000MBS 0x8000 /* 10=1000Mbs */
+
+#define M88E1000_PSSR_REV_POLARITY_SHIFT 1
+#define M88E1000_PSSR_DOWNSHIFT_SHIFT 5
+#define M88E1000_PSSR_MDIX_SHIFT 6
+#define M88E1000_PSSR_CABLE_LENGTH_SHIFT 7
+
+/* M88E1000 Extended PHY Specific Control Register */
+#define M88E1000_EPSCR_FIBER_LOOPBACK 0x4000 /* 1=Fiber loopback */
+#define M88E1000_EPSCR_DOWN_NO_IDLE 0x8000 /* 1=Lost lock detect enabled.
+ * Will assert lost lock and bring
+ * link down if idle not seen
+ * within 1ms in 1000BASE-T
+ */
+/* Number of times we will attempt to autonegotiate before downshifting if we
+ * are the master */
+#define M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK 0x0C00
+#define M88E1000_EPSCR_MASTER_DOWNSHIFT_1X 0x0000
+#define M88E1000_EPSCR_MASTER_DOWNSHIFT_2X 0x0400
+#define M88E1000_EPSCR_MASTER_DOWNSHIFT_3X 0x0800
+#define M88E1000_EPSCR_MASTER_DOWNSHIFT_4X 0x0C00
+/* Number of times we will attempt to autonegotiate before downshifting if we
+ * are the slave */
+#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK 0x0300
+#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_DIS 0x0000
+#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X 0x0100
+#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_2X 0x0200
+#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_3X 0x0300
+#define M88E1000_EPSCR_TX_CLK_2_5 0x0060 /* 2.5 MHz TX_CLK */
+#define M88E1000_EPSCR_TX_CLK_25 0x0070 /* 25 MHz TX_CLK */
+#define M88E1000_EPSCR_TX_CLK_0 0x0000 /* NO TX_CLK */
+
+/* IGP01E1000 Specific Port Config Register - R/W */
+#define IGP01E1000_PSCFR_AUTO_MDIX_PAR_DETECT 0x0010
+#define IGP01E1000_PSCFR_PRE_EN 0x0020
+#define IGP01E1000_PSCFR_SMART_SPEED 0x0080
+#define IGP01E1000_PSCFR_DISABLE_TPLOOPBACK 0x0100
+#define IGP01E1000_PSCFR_DISABLE_JABBER 0x0400
+#define IGP01E1000_PSCFR_DISABLE_TRANSMIT 0x2000
+
+/* IGP01E1000 Specific Port Status Register - R/O */
+#define IGP01E1000_PSSR_AUTONEG_FAILED 0x0001 /* RO LH SC */
+#define IGP01E1000_PSSR_POLARITY_REVERSED 0x0002
+#define IGP01E1000_PSSR_CABLE_LENGTH 0x007C
+#define IGP01E1000_PSSR_FULL_DUPLEX 0x0200
+#define IGP01E1000_PSSR_LINK_UP 0x0400
+#define IGP01E1000_PSSR_MDIX 0x0800
+#define IGP01E1000_PSSR_SPEED_MASK 0xC000 /* speed bits mask */
+#define IGP01E1000_PSSR_SPEED_10MBPS 0x4000
+#define IGP01E1000_PSSR_SPEED_100MBPS 0x8000
+#define IGP01E1000_PSSR_SPEED_1000MBPS 0xC000
+#define IGP01E1000_PSSR_CABLE_LENGTH_SHIFT 0x0002 /* shift right 2 */
+#define IGP01E1000_PSSR_MDIX_SHIFT 0x000B /* shift right 11 */
+
+/* IGP01E1000 Specific Port Control Register - R/W */
+#define IGP01E1000_PSCR_TP_LOOPBACK 0x0001
+#define IGP01E1000_PSCR_CORRECT_NC_SCMBLR 0x0200
+#define IGP01E1000_PSCR_TEN_CRS_SELECT 0x0400
+#define IGP01E1000_PSCR_FLIP_CHIP 0x0800
+#define IGP01E1000_PSCR_AUTO_MDIX 0x1000
+#define IGP01E1000_PSCR_FORCE_MDI_MDIX 0x2000 /* 0-MDI, 1-MDIX */
+
+/* IGP01E1000 Specific Port Link Health Register */
+#define IGP01E1000_PLHR_SS_DOWNGRADE 0x8000
+#define IGP01E1000_PLHR_GIG_SCRAMBLER_ERROR 0x4000
+#define IGP01E1000_PLHR_GIG_REM_RCVR_NOK 0x0800 /* LH */
+#define IGP01E1000_PLHR_IDLE_ERROR_CNT_OFLOW 0x0400 /* LH */
+#define IGP01E1000_PLHR_DATA_ERR_1 0x0200 /* LH */
+#define IGP01E1000_PLHR_DATA_ERR_0 0x0100
+#define IGP01E1000_PLHR_AUTONEG_FAULT 0x0010
+#define IGP01E1000_PLHR_AUTONEG_ACTIVE 0x0008
+#define IGP01E1000_PLHR_VALID_CHANNEL_D 0x0004
+#define IGP01E1000_PLHR_VALID_CHANNEL_C 0x0002
+#define IGP01E1000_PLHR_VALID_CHANNEL_B 0x0001
+#define IGP01E1000_PLHR_VALID_CHANNEL_A 0x0000
+
+/* IGP01E1000 Channel Quality Register */
+#define IGP01E1000_MSE_CHANNEL_D 0x000F
+#define IGP01E1000_MSE_CHANNEL_C 0x00F0
+#define IGP01E1000_MSE_CHANNEL_B 0x0F00
+#define IGP01E1000_MSE_CHANNEL_A 0xF000
+
+/* IGP01E1000 DSP reset macros */
+#define DSP_RESET_ENABLE 0x0
+#define DSP_RESET_DISABLE 0x2
+#define E1000_MAX_DSP_RESETS 10
+
+/* IGP01E1000 AGC Registers */
+
+#define IGP01E1000_AGC_LENGTH_SHIFT 7 /* Coarse - 13:11, Fine - 10:7 */
+
+/* 7 bits (3 Coarse + 4 Fine) --> 128 optional values */
+#define IGP01E1000_AGC_LENGTH_TABLE_SIZE 128
+
+/* The precision of the length is +/- 10 meters */
+#define IGP01E1000_AGC_RANGE 10
+
+/* IGP01E1000 PCS Initialization register */
+/* bits 3:6 in the PCS registers stores the channels polarity */
+#define IGP01E1000_PHY_POLARITY_MASK 0x0078
+
+/* IGP01E1000 GMII FIFO Register */
+#define IGP01E1000_GMII_FLEX_SPD 0x10 /* Enable flexible speed
+ * on Link-Up */
+#define IGP01E1000_GMII_SPD 0x20 /* Enable SPD */
+
+/* IGP01E1000 Analog Register */
+#define IGP01E1000_ANALOG_SPARE_FUSE_STATUS 0x20D1
+#define IGP01E1000_ANALOG_FUSE_STATUS 0x20D0
+#define IGP01E1000_ANALOG_FUSE_CONTROL 0x20DC
+#define IGP01E1000_ANALOG_FUSE_BYPASS 0x20DE
+
+#define IGP01E1000_ANALOG_FUSE_POLY_MASK 0xF000
+#define IGP01E1000_ANALOG_FUSE_FINE_MASK 0x0F80
+#define IGP01E1000_ANALOG_FUSE_COARSE_MASK 0x0070
+#define IGP01E1000_ANALOG_SPARE_FUSE_ENABLED 0x0100
+#define IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL 0x0002
+
+#define IGP01E1000_ANALOG_FUSE_COARSE_THRESH 0x0040
+#define IGP01E1000_ANALOG_FUSE_COARSE_10 0x0010
+#define IGP01E1000_ANALOG_FUSE_FINE_1 0x0080
+#define IGP01E1000_ANALOG_FUSE_FINE_10 0x0500
+
+/* Bit definitions for valid PHY IDs. */
+#define M88E1000_E_PHY_ID 0x01410C50
+#define M88E1000_I_PHY_ID 0x01410C30
+#define M88E1011_I_PHY_ID 0x01410C20
+#define IGP01E1000_I_PHY_ID 0x02A80380
+#define M88E1000_12_PHY_ID M88E1000_E_PHY_ID
+#define M88E1000_14_PHY_ID M88E1000_E_PHY_ID
+#define M88E1011_I_REV_4 0x04
+
+/* Miscellaneous PHY bit definitions. */
+#define PHY_PREAMBLE 0xFFFFFFFF
+#define PHY_SOF 0x01
+#define PHY_OP_READ 0x02
+#define PHY_OP_WRITE 0x01
+#define PHY_TURNAROUND 0x02
+#define PHY_PREAMBLE_SIZE 32
+#define MII_CR_SPEED_1000 0x0040
+#define MII_CR_SPEED_100 0x2000
+#define MII_CR_SPEED_10 0x0000
+#define E1000_PHY_ADDRESS 0x01
+#define PHY_AUTO_NEG_TIME 45 /* 4.5 Seconds */
+#define PHY_FORCE_TIME 20 /* 2.0 Seconds */
+#define PHY_REVISION_MASK 0xFFFFFFF0
+#define DEVICE_SPEED_MASK 0x00000300 /* Device Ctrl Reg Speed Mask */
+#define REG4_SPEED_MASK 0x01E0
+#define REG9_SPEED_MASK 0x0300
+#define ADVERTISE_10_HALF 0x0001
+#define ADVERTISE_10_FULL 0x0002
+#define ADVERTISE_100_HALF 0x0004
+#define ADVERTISE_100_FULL 0x0008
+#define ADVERTISE_1000_HALF 0x0010
+#define ADVERTISE_1000_FULL 0x0020
+#define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x002F /* Everything but 1000-Half */
+#define AUTONEG_ADVERTISE_10_100_ALL 0x000F /* All 10/100 speeds*/
+#define AUTONEG_ADVERTISE_10_ALL 0x0003 /* 10Mbps Full & Half speeds*/
+
+#endif /* _E1000_HW_H_ */
diff --git a/src/drivers/net/eepro.c b/src/drivers/net/eepro.c
new file mode 100644
index 00000000..b27c9f7f
--- /dev/null
+++ b/src/drivers/net/eepro.c
@@ -0,0 +1,625 @@
+#ifdef ALLMULTI
+#error multicast support is not yet implemented
+#endif
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+Intel EEPRO/10 NIC driver for Etherboot
+Adapted from Linux eepro.c from kernel 2.2.17
+
+This board accepts a 32 pin EEPROM (29C256), however a test with a
+27C010 shows that this EPROM also works in the socket, but it's not clear
+how repeatably. The two top address pins appear to be held low, thus
+the bottom 32kB of the 27C010 is visible in the CPU's address space.
+To be sure you could put 4 copies of the code in the 27C010, then
+it doesn't matter whether the extra lines are held low or high, just
+hopefully not floating as CMOS chips don't like floating inputs.
+
+Be careful with seating the EPROM as the socket on my board actually
+has 34 pins, the top row of 2 are not used.
+***************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+#include "isa.h"
+/* we use timer2 for microsecond waits */
+#include "timer.h"
+
+#undef DEBUG /* only after include files */
+
+/* Different 82595 chips */
+#define LAN595 0
+#define LAN595TX 1
+#define LAN595FX 2
+#define LAN595FX_10ISA 3
+
+#define SLOW_DOWN inb(0x80);
+
+/* The station (ethernet) address prefix, used for IDing the board. */
+#define SA_ADDR0 0x00 /* Etherexpress Pro/10 */
+#define SA_ADDR1 0xaa
+#define SA_ADDR2 0x00
+
+#define GetBit(x,y) ((x & (1<<y))>>y)
+
+/* EEPROM Word 0: */
+#define ee_PnP 0 /* Plug 'n Play enable bit */
+#define ee_Word1 1 /* Word 1? */
+#define ee_BusWidth 2 /* 8/16 bit */
+#define ee_FlashAddr 3 /* Flash Address */
+#define ee_FlashMask 0x7 /* Mask */
+#define ee_AutoIO 6 /* */
+#define ee_reserved0 7 /* =0! */
+#define ee_Flash 8 /* Flash there? */
+#define ee_AutoNeg 9 /* Auto Negotiation enabled? */
+#define ee_IO0 10 /* IO Address LSB */
+#define ee_IO0Mask 0x /*...*/
+#define ee_IO1 15 /* IO MSB */
+
+/* EEPROM Word 1: */
+#define ee_IntSel 0 /* Interrupt */
+#define ee_IntMask 0x7
+#define ee_LI 3 /* Link Integrity 0= enabled */
+#define ee_PC 4 /* Polarity Correction 0= enabled */
+#define ee_TPE_AUI 5 /* PortSelection 1=TPE */
+#define ee_Jabber 6 /* Jabber prevention 0= enabled */
+#define ee_AutoPort 7 /* Auto Port Selection 1= Disabled */
+#define ee_SMOUT 8 /* SMout Pin Control 0= Input */
+#define ee_PROM 9 /* Flash EPROM / PROM 0=Flash */
+#define ee_reserved1 10 /* .. 12 =0! */
+#define ee_AltReady 13 /* Alternate Ready, 0=normal */
+#define ee_reserved2 14 /* =0! */
+#define ee_Duplex 15
+
+/* Word2,3,4: */
+#define ee_IA5 0 /*bit start for individual Addr Byte 5 */
+#define ee_IA4 8 /*bit start for individual Addr Byte 5 */
+#define ee_IA3 0 /*bit start for individual Addr Byte 5 */
+#define ee_IA2 8 /*bit start for individual Addr Byte 5 */
+#define ee_IA1 0 /*bit start for individual Addr Byte 5 */
+#define ee_IA0 8 /*bit start for individual Addr Byte 5 */
+
+/* Word 5: */
+#define ee_BNC_TPE 0 /* 0=TPE */
+#define ee_BootType 1 /* 00=None, 01=IPX, 10=ODI, 11=NDIS */
+#define ee_BootTypeMask 0x3
+#define ee_NumConn 3 /* Number of Connections 0= One or Two */
+#define ee_FlashSock 4 /* Presence of Flash Socket 0= Present */
+#define ee_PortTPE 5
+#define ee_PortBNC 6
+#define ee_PortAUI 7
+#define ee_PowerMgt 10 /* 0= disabled */
+#define ee_CP 13 /* Concurrent Processing */
+#define ee_CPMask 0x7
+
+/* Word 6: */
+#define ee_Stepping 0 /* Stepping info */
+#define ee_StepMask 0x0F
+#define ee_BoardID 4 /* Manucaturer Board ID, reserved */
+#define ee_BoardMask 0x0FFF
+
+/* Word 7: */
+#define ee_INT_TO_IRQ 0 /* int to IRQ Mapping = 0x1EB8 for Pro/10+ */
+#define ee_FX_INT2IRQ 0x1EB8 /* the _only_ mapping allowed for FX chips */
+
+/*..*/
+#define ee_SIZE 0x40 /* total EEprom Size */
+#define ee_Checksum 0xBABA /* initial and final value for adding checksum */
+
+
+/* Card identification via EEprom: */
+#define ee_addr_vendor 0x10 /* Word offset for EISA Vendor ID */
+#define ee_addr_id 0x11 /* Word offset for Card ID */
+#define ee_addr_SN 0x12 /* Serial Number */
+#define ee_addr_CRC_8 0x14 /* CRC over last thee Bytes */
+
+
+#define ee_vendor_intel0 0x25 /* Vendor ID Intel */
+#define ee_vendor_intel1 0xD4
+#define ee_id_eepro10p0 0x10 /* ID for eepro/10+ */
+#define ee_id_eepro10p1 0x31
+
+/* now this section could be used by both boards: the oldies and the ee10:
+ * ee10 uses tx buffer before of rx buffer and the oldies the inverse.
+ * (aris)
+ */
+#define RAM_SIZE 0x8000
+
+#define RCV_HEADER 8
+#define RCV_DEFAULT_RAM 0x6000
+#define RCV_RAM rcv_ram
+
+static unsigned rcv_ram = RCV_DEFAULT_RAM;
+
+#define XMT_HEADER 8
+#define XMT_RAM (RAM_SIZE - RCV_RAM)
+
+#define XMT_START ((rcv_start + RCV_RAM) % RAM_SIZE)
+
+#define RCV_LOWER_LIMIT (rcv_start >> 8)
+#define RCV_UPPER_LIMIT (((rcv_start + RCV_RAM) - 2) >> 8)
+#define XMT_LOWER_LIMIT (XMT_START >> 8)
+#define XMT_UPPER_LIMIT (((XMT_START + XMT_RAM) - 2) >> 8)
+
+#define RCV_START_PRO 0x00
+#define RCV_START_10 XMT_RAM
+ /* by default the old driver */
+static unsigned rcv_start = RCV_START_PRO;
+
+#define RCV_DONE 0x0008
+#define RX_OK 0x2000
+#define RX_ERROR 0x0d81
+
+#define TX_DONE_BIT 0x0080
+#define CHAIN_BIT 0x8000
+#define XMT_STATUS 0x02
+#define XMT_CHAIN 0x04
+#define XMT_COUNT 0x06
+
+#define BANK0_SELECT 0x00
+#define BANK1_SELECT 0x40
+#define BANK2_SELECT 0x80
+
+/* Bank 0 registers */
+#define COMMAND_REG 0x00 /* Register 0 */
+#define MC_SETUP 0x03
+#define XMT_CMD 0x04
+#define DIAGNOSE_CMD 0x07
+#define RCV_ENABLE_CMD 0x08
+#define RCV_DISABLE_CMD 0x0a
+#define STOP_RCV_CMD 0x0b
+#define RESET_CMD 0x0e
+#define POWER_DOWN_CMD 0x18
+#define RESUME_XMT_CMD 0x1c
+#define SEL_RESET_CMD 0x1e
+#define STATUS_REG 0x01 /* Register 1 */
+#define RX_INT 0x02
+#define TX_INT 0x04
+#define EXEC_STATUS 0x30
+#define ID_REG 0x02 /* Register 2 */
+#define R_ROBIN_BITS 0xc0 /* round robin counter */
+#define ID_REG_MASK 0x2c
+#define ID_REG_SIG 0x24
+#define AUTO_ENABLE 0x10
+#define INT_MASK_REG 0x03 /* Register 3 */
+#define RX_STOP_MASK 0x01
+#define RX_MASK 0x02
+#define TX_MASK 0x04
+#define EXEC_MASK 0x08
+#define ALL_MASK 0x0f
+#define IO_32_BIT 0x10
+#define RCV_BAR 0x04 /* The following are word (16-bit) registers */
+#define RCV_STOP 0x06
+
+#define XMT_BAR_PRO 0x0a
+#define XMT_BAR_10 0x0b
+static unsigned xmt_bar = XMT_BAR_PRO;
+
+#define HOST_ADDRESS_REG 0x0c
+#define IO_PORT 0x0e
+#define IO_PORT_32_BIT 0x0c
+
+/* Bank 1 registers */
+#define REG1 0x01
+#define WORD_WIDTH 0x02
+#define INT_ENABLE 0x80
+#define INT_NO_REG 0x02
+#define RCV_LOWER_LIMIT_REG 0x08
+#define RCV_UPPER_LIMIT_REG 0x09
+
+#define XMT_LOWER_LIMIT_REG_PRO 0x0a
+#define XMT_UPPER_LIMIT_REG_PRO 0x0b
+#define XMT_LOWER_LIMIT_REG_10 0x0b
+#define XMT_UPPER_LIMIT_REG_10 0x0a
+static unsigned xmt_lower_limit_reg = XMT_LOWER_LIMIT_REG_PRO;
+static unsigned xmt_upper_limit_reg = XMT_UPPER_LIMIT_REG_PRO;
+
+/* Bank 2 registers */
+#define XMT_Chain_Int 0x20 /* Interrupt at the end of the transmit chain */
+#define XMT_Chain_ErrStop 0x40 /* Interrupt at the end of the chain even if there are errors */
+#define RCV_Discard_BadFrame 0x80 /* Throw bad frames away, and continue to receive others */
+#define REG2 0x02
+#define PRMSC_Mode 0x01
+#define Multi_IA 0x20
+#define REG3 0x03
+#define TPE_BIT 0x04
+#define BNC_BIT 0x20
+#define REG13 0x0d
+#define FDX 0x00
+#define A_N_ENABLE 0x02
+
+#define I_ADD_REG0 0x04
+#define I_ADD_REG1 0x05
+#define I_ADD_REG2 0x06
+#define I_ADD_REG3 0x07
+#define I_ADD_REG4 0x08
+#define I_ADD_REG5 0x09
+
+#define EEPROM_REG_PRO 0x0a
+#define EEPROM_REG_10 0x0b
+static unsigned eeprom_reg = EEPROM_REG_PRO;
+
+#define EESK 0x01
+#define EECS 0x02
+#define EEDI 0x04
+#define EEDO 0x08
+
+/* The horrible routine to read a word from the serial EEPROM. */
+/* IMPORTANT - the 82595 will be set to Bank 0 after the eeprom is read */
+
+/* The delay between EEPROM clock transitions. */
+#define eeprom_delay() { udelay(40); }
+#define EE_READ_CMD (6 << 6)
+
+/* do a full reset */
+#define eepro_full_reset(ioaddr) outb(RESET_CMD, ioaddr); udelay(40);
+
+/* do a nice reset */
+#define eepro_sel_reset(ioaddr) { \
+ outb(SEL_RESET_CMD, ioaddr); \
+ SLOW_DOWN; \
+ SLOW_DOWN; \
+ }
+
+/* clear all interrupts */
+#define eepro_clear_int(ioaddr) outb(ALL_MASK, ioaddr + STATUS_REG)
+
+/* enable rx */
+#define eepro_en_rx(ioaddr) outb(RCV_ENABLE_CMD, ioaddr)
+
+/* disable rx */
+#define eepro_dis_rx(ioaddr) outb(RCV_DISABLE_CMD, ioaddr)
+
+/* switch bank */
+#define eepro_sw2bank0(ioaddr) outb(BANK0_SELECT, ioaddr)
+#define eepro_sw2bank1(ioaddr) outb(BANK1_SELECT, ioaddr)
+#define eepro_sw2bank2(ioaddr) outb(BANK2_SELECT, ioaddr)
+
+static unsigned int rx_start, tx_start;
+static int tx_last;
+static unsigned int tx_end;
+static int eepro = 0;
+static unsigned short ioaddr = 0;
+static unsigned int mem_start, mem_end = RCV_DEFAULT_RAM / 1024;
+
+/**************************************************************************
+RESET - Reset adapter
+***************************************************************************/
+static void eepro_reset(struct nic *nic)
+{
+ int temp_reg, i;
+
+ /* put the card in its initial state */
+ eepro_sw2bank2(ioaddr); /* be careful, bank2 now */
+ temp_reg = inb(ioaddr + eeprom_reg);
+#ifdef DEBUG
+ printf("Stepping %d\n", temp_reg >> 5);
+#endif
+ if (temp_reg & 0x10) /* check the TurnOff Enable bit */
+ outb(temp_reg & 0xEF, ioaddr + eeprom_reg);
+ for (i = 0; i < ETH_ALEN; i++) /* fill the MAC address */
+ outb(nic->node_addr[i], ioaddr + I_ADD_REG0 + i);
+ temp_reg = inb(ioaddr + REG1);
+ /* setup Transmit Chaining and discard bad RCV frames */
+ outb(temp_reg | XMT_Chain_Int | XMT_Chain_ErrStop
+ | RCV_Discard_BadFrame, ioaddr + REG1);
+ temp_reg = inb(ioaddr + REG2); /* match broadcast */
+ outb(temp_reg | 0x14, ioaddr + REG2);
+ temp_reg = inb(ioaddr + REG3);
+ outb(temp_reg & 0x3F, ioaddr + REG3); /* clear test mode */
+ /* set the receiving mode */
+ eepro_sw2bank1(ioaddr); /* be careful, bank1 now */
+ /* initialise the RCV and XMT upper and lower limits */
+ outb(RCV_LOWER_LIMIT, ioaddr + RCV_LOWER_LIMIT_REG);
+ outb(RCV_UPPER_LIMIT, ioaddr + RCV_UPPER_LIMIT_REG);
+ outb(XMT_LOWER_LIMIT, ioaddr + xmt_lower_limit_reg);
+ outb(XMT_UPPER_LIMIT, ioaddr + xmt_upper_limit_reg);
+ eepro_sw2bank0(ioaddr); /* Switch back to bank 0 */
+ eepro_clear_int(ioaddr);
+ /* Initialise RCV */
+ rx_start = (unsigned int)bus_to_virt(RCV_LOWER_LIMIT << 8);
+ outw(RCV_LOWER_LIMIT << 8, ioaddr + RCV_BAR);
+ outw(((RCV_UPPER_LIMIT << 8) | 0xFE), ioaddr + RCV_STOP);
+ /* Intialise XMT */
+ outw((XMT_LOWER_LIMIT << 8), ioaddr + xmt_bar);
+ eepro_sel_reset(ioaddr);
+ tx_start = tx_end = (unsigned int)bus_to_virt(XMT_LOWER_LIMIT << 8);
+ tx_last = 0;
+ eepro_en_rx(ioaddr);
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int eepro_poll(struct nic *nic, int retrieve)
+{
+ unsigned int rcv_car = virt_to_bus((void *)rx_start);
+ unsigned int rcv_event, rcv_status, rcv_next_frame, rcv_size;
+
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+#if 0
+ if ((inb(ioaddr + STATUS_REG) & 0x40) == 0)
+ return (0);
+ outb(0x40, ioaddr + STATUS_REG);
+#endif
+ outw(rcv_car, ioaddr + HOST_ADDRESS_REG);
+ rcv_event = inw(ioaddr + IO_PORT);
+ if (rcv_event != RCV_DONE)
+ return (0);
+
+ /* FIXME: I'm guessing this might not work with this card, since
+ it looks like once a rcv_event is started it must be completed.
+ maybe there's another way. */
+ if ( ! retrieve ) return 1;
+
+ rcv_status = inw(ioaddr + IO_PORT);
+ rcv_next_frame = inw(ioaddr + IO_PORT);
+ rcv_size = inw(ioaddr + IO_PORT);
+#if 0
+ printf("%hX %hX %d %hhX\n", rcv_status, rcv_next_frame, rcv_size,
+ inb(ioaddr + STATUS_REG));
+#endif
+ if ((rcv_status & (RX_OK|RX_ERROR)) != RX_OK) {
+ printf("Receive error %hX\n", rcv_status);
+ return (0);
+ }
+ rcv_size &= 0x3FFF;
+ insw(ioaddr + IO_PORT, nic->packet, ((rcv_size + 3) >> 1));
+#if 0
+{
+ int i;
+ for (i = 0; i < 48; i++) {
+ printf("%hhX", nic->packet[i]);
+ putchar(i % 16 == 15 ? '\n' : ' ');
+ }
+}
+#endif
+ nic->packetlen = rcv_size;
+ rcv_car = virt_to_bus((void *) (rx_start + RCV_HEADER + rcv_size));
+ rx_start = (unsigned int)bus_to_virt(rcv_next_frame << 8);
+ if (rcv_car == 0)
+ rcv_car = ((RCV_UPPER_LIMIT << 8) | 0xff);
+ outw(rcv_car - 1, ioaddr + RCV_STOP);
+ return (1);
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void eepro_transmit(
+ struct nic *nic,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+ unsigned int status, tx_available, last, end, length;
+ unsigned short type;
+ int boguscount = 20;
+
+ length = s + ETH_HLEN;
+ if (tx_end > tx_start)
+ tx_available = XMT_RAM - (tx_end - tx_start);
+ else if (tx_end < tx_start)
+ tx_available = tx_start - tx_end;
+ else
+ tx_available = XMT_RAM;
+ last = tx_end;
+ end = last + (((length + 3) >> 1) << 1) + XMT_HEADER;
+ if (end >= (XMT_UPPER_LIMIT << 8)) {
+ last = (XMT_LOWER_LIMIT << 8);
+ end = last + (((length + 3) >> 1) << 1) + XMT_HEADER;
+ }
+ outw(last, ioaddr + HOST_ADDRESS_REG);
+ outw(XMT_CMD, ioaddr + IO_PORT);
+ outw(0, ioaddr + IO_PORT);
+ outw(end, ioaddr + IO_PORT);
+ outw(length, ioaddr + IO_PORT);
+ outsw(ioaddr + IO_PORT, d, ETH_ALEN / 2);
+ outsw(ioaddr + IO_PORT, nic->node_addr, ETH_ALEN / 2);
+ type = htons(t);
+ outsw(ioaddr + IO_PORT, &type, sizeof(type) / 2);
+ outsw(ioaddr + IO_PORT, p, (s + 3) >> 1);
+ /* A dummy read to flush the DRAM write pipeline */
+ status = inw(ioaddr + IO_PORT);
+ outw(last, ioaddr + xmt_bar);
+ outb(XMT_CMD, ioaddr);
+ tx_start = last;
+ tx_last = last;
+ tx_end = end;
+#if 0
+ printf("%d %d\n", tx_start, tx_end);
+#endif
+ while (boguscount > 0) {
+ if (((status = inw(ioaddr + IO_PORT)) & TX_DONE_BIT) == 0) {
+ udelay(40);
+ boguscount--;
+ continue;
+ }
+#if DEBUG
+ if ((status & 0x2000) == 0)
+ printf("Transmit status %hX\n", status);
+#endif
+ }
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void eepro_disable(struct dev *dev __unused)
+{
+ eepro_sw2bank0(ioaddr); /* Switch to bank 0 */
+ /* Flush the Tx and disable Rx */
+ outb(STOP_RCV_CMD, ioaddr);
+ tx_start = tx_end = (unsigned int) (bus_to_virt(XMT_LOWER_LIMIT << 8));
+ tx_last = 0;
+ /* Reset the 82595 */
+ eepro_full_reset(ioaddr);
+}
+
+/**************************************************************************
+DISABLE - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void eepro_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+static int read_eeprom(int location)
+{
+ int i;
+ unsigned short retval = 0;
+ int ee_addr = ioaddr + eeprom_reg;
+ int read_cmd = location | EE_READ_CMD;
+ int ctrl_val = EECS;
+
+ if (eepro == LAN595FX_10ISA) {
+ eepro_sw2bank1(ioaddr);
+ outb(0x00, ioaddr + STATUS_REG);
+ }
+ eepro_sw2bank2(ioaddr);
+ outb(ctrl_val, ee_addr);
+ /* shift the read command bits out */
+ for (i = 8; i >= 0; i--) {
+ short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI : ctrl_val;
+ outb(outval, ee_addr);
+ outb(outval | EESK, ee_addr); /* EEPROM clock tick */
+ eeprom_delay();
+ outb(outval, ee_addr); /* finish EEPROM clock tick */
+ eeprom_delay();
+ }
+ outb(ctrl_val, ee_addr);
+ for (i = 16; i > 0; i--) {
+ outb(ctrl_val | EESK, ee_addr);
+ eeprom_delay();
+ retval = (retval << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0);
+ outb(ctrl_val, ee_addr);
+ eeprom_delay();
+ }
+ /* terminate the EEPROM access */
+ ctrl_val &= ~EECS;
+ outb(ctrl_val | EESK, ee_addr);
+ eeprom_delay();
+ outb(ctrl_val, ee_addr);
+ eeprom_delay();
+ eepro_sw2bank0(ioaddr);
+ return (retval);
+}
+
+static int eepro_probe1(struct nic *nic)
+{
+ int i, id, counter, l_eepro = 0;
+ union {
+ unsigned char caddr[ETH_ALEN];
+ unsigned short saddr[ETH_ALEN/2];
+ } station_addr;
+ char *name;
+
+ id = inb(ioaddr + ID_REG);
+ if ((id & ID_REG_MASK) != ID_REG_SIG)
+ return (0);
+ counter = id & R_ROBIN_BITS;
+ if (((id = inb(ioaddr + ID_REG)) & R_ROBIN_BITS) != (counter + 0x40))
+ return (0);
+ /* yes the 82595 has been found */
+ station_addr.saddr[2] = read_eeprom(2);
+ if (station_addr.saddr[2] == 0x0000 || station_addr.saddr[2] == 0xFFFF) {
+ l_eepro = 3;
+ eepro = LAN595FX_10ISA;
+ eeprom_reg= EEPROM_REG_10;
+ rcv_start = RCV_START_10;
+ xmt_lower_limit_reg = XMT_LOWER_LIMIT_REG_10;
+ xmt_upper_limit_reg = XMT_UPPER_LIMIT_REG_10;
+ station_addr.saddr[2] = read_eeprom(2);
+ }
+ station_addr.saddr[1] = read_eeprom(3);
+ station_addr.saddr[0] = read_eeprom(4);
+ if (l_eepro)
+ name = "Intel EtherExpress 10 ISA";
+ else if (read_eeprom(7) == ee_FX_INT2IRQ) {
+ name = "Intel EtherExpress Pro/10+ ISA";
+ l_eepro = 2;
+ } else if (station_addr.saddr[0] == SA_ADDR1) {
+ name = "Intel EtherExpress Pro/10 ISA";
+ l_eepro = 1;
+ } else {
+ l_eepro = 0;
+ name = "Intel 82595-based LAN card";
+ }
+ station_addr.saddr[0] = swap16(station_addr.saddr[0]);
+ station_addr.saddr[1] = swap16(station_addr.saddr[1]);
+ station_addr.saddr[2] = swap16(station_addr.saddr[2]);
+ for (i = 0; i < ETH_ALEN; i++) {
+ nic->node_addr[i] = station_addr.caddr[i];
+ }
+ printf("\n%s ioaddr %#hX, addr %!", name, ioaddr, nic->node_addr);
+ mem_start = RCV_LOWER_LIMIT << 8;
+ if ((mem_end & 0x3F) < 3 || (mem_end & 0x3F) > 29)
+ mem_end = RCV_UPPER_LIMIT << 8;
+ else {
+ mem_end = mem_end * 1024 + (RCV_LOWER_LIMIT << 8);
+ rcv_ram = mem_end - (RCV_LOWER_LIMIT << 8);
+ }
+ printf(", Rx mem %dK, if %s\n", (mem_end - mem_start) >> 10,
+ GetBit(read_eeprom(5), ee_BNC_TPE) ? "BNC" : "TP");
+ return (1);
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+static int eepro_probe(struct dev *dev, unsigned short *probe_addrs)
+{
+ struct nic *nic = (struct nic *)dev;
+ unsigned short *p;
+ /* same probe list as the Linux driver */
+ static unsigned short ioaddrs[] = {
+ 0x300, 0x210, 0x240, 0x280, 0x2C0, 0x200, 0x320, 0x340, 0x360, 0};
+
+ if (probe_addrs == 0 || probe_addrs[0] == 0)
+ probe_addrs = ioaddrs;
+ for (p = probe_addrs; (ioaddr = *p) != 0; p++) {
+ if (eepro_probe1(nic))
+ break;
+ }
+ if (*p == 0)
+ return (0);
+
+ nic->irqno = 0;
+ nic->ioaddr = *p;
+
+ eepro_reset(nic);
+ /* point to NIC specific routines */
+ dev->disable = eepro_disable;
+ nic->poll = eepro_poll;
+ nic->transmit = eepro_transmit;
+ nic->irq = eepro_irq;
+ /* Based on PnP ISA map */
+ dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
+ dev->devid.device_id = htons(0x828a);
+ return 1;
+}
+
+static struct isa_driver eepro_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "EEPRO",
+ .probe = eepro_probe,
+ .ioaddrs = 0,
+};
diff --git a/src/drivers/net/eepro100.c b/src/drivers/net/eepro100.c
new file mode 100644
index 00000000..e10bc84b
--- /dev/null
+++ b/src/drivers/net/eepro100.c
@@ -0,0 +1,841 @@
+/*
+ * eepro100.c -- This file implements the eepro100 driver for etherboot.
+ *
+ *
+ * Copyright (C) AW Computer Systems.
+ * written by R.E.Wolff -- R.E.Wolff@BitWizard.nl
+ *
+ *
+ * AW Computer Systems is contributing to the free software community
+ * by paying for this driver and then putting the result under GPL.
+ *
+ * If you need a Linux device driver, please contact BitWizard for a
+ * quote.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * date version by what
+ * Written: May 29 1997 V0.10 REW Initial revision.
+ * changes: May 31 1997 V0.90 REW Works!
+ * Jun 1 1997 V0.91 REW Cleanup
+ * Jun 2 1997 V0.92 REW Add some code documentation
+ * Jul 25 1997 V1.00 REW Tested by AW to work in a PROM
+ * Cleanup for publication
+ * Dez 11 2004 V1.10 Kiszka Add RX ring buffer support
+ *
+ * This is the etherboot intel etherexpress Pro/100B driver.
+ *
+ * It was written from scratch, with Donald Beckers eepro100.c kernel
+ * driver as a guideline. Mostly the 82557 related definitions and the
+ * lower level routines have been cut-and-pasted into this source.
+ *
+ * The driver was finished before Intel got the NDA out of the closet.
+ * I still don't have the docs.
+ *
+ *
+ * Datasheet is now published and available from
+ * ftp://download.intel.com/design/network/manuals/8255X_OpenSDM.pdf
+ * - Michael Brown
+ * */
+
+/* Philosophy of this driver.
+ *
+ * Probing:
+ *
+ * Using the pci.c functions of the Etherboot code, the 82557 chip is detected.
+ * It is verified that the BIOS initialized everything properly and if
+ * something is missing it is done now.
+ *
+ *
+ * Initialization:
+ *
+ *
+ * The chip is then initialized to "know" its ethernet address, and to
+ * start recieving packets. The Linux driver has a whole transmit and
+ * recieve ring of buffers. This is neat if you need high performance:
+ * you can write the buffers asynchronously to the chip reading the
+ * buffers and transmitting them over the network. Performance is NOT
+ * an issue here. We can boot a 400k kernel in about two
+ * seconds. (Theory: 0.4 seconds). Booting a system is going to take
+ * about half a minute anyway, so getting 10 times closer to the
+ * theoretical limit is going to make a difference of a few percent. */
+/* Not totally true: busy networks can cause packet drops due to RX
+ * buffer overflows. Fixed in V1.10 of this driver. [Kiszka] */
+/*
+ *
+ * Transmitting and recieving.
+ *
+ * We have only one transmit descriptor. It has two buffer descriptors:
+ * one for the header, and the other for the data.
+ * We have multiple receive buffers (currently: 4). The chip is told to
+ * receive packets and suspend itself once it ran on the last free buffer.
+ * The recieve (poll) routine simply looks at the current recieve buffer,
+ * picks the packet if any, and releases this buffer again (classic ring
+ * buffer concept). This helps to avoid packet drops on busy networks.
+ *
+ * Caveats:
+ *
+ * The Etherboot framework moves the code to the 48k segment from
+ * 0x94000 to 0xa0000. There is just a little room between the end of
+ * this driver and the 0xa0000 address. If you compile in too many
+ * features, this will overflow.
+ * The number under "hex" in the output of size that scrolls by while
+ * compiling should be less than 8000. Maybe even the stack is up there,
+ * so that you need even more headroom.
+ */
+
+/* The etherboot authors seem to dislike the argument ordering in
+ * outb macros that Linux uses. I disklike the confusion that this
+ * has caused even more.... This file uses the Linux argument ordering. */
+/* Sorry not us. It's inherited code from FreeBSD. [The authors] */
+
+#include "etherboot.h"
+#include "nic.h"
+#include "pci.h"
+#include "timer.h"
+
+static int ioaddr;
+
+typedef unsigned char u8;
+typedef signed char s8;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned int u32;
+typedef signed int s32;
+
+enum speedo_offsets {
+ SCBStatus = 0, SCBCmd = 2, /* Rx/Command Unit command and status. */
+ SCBPointer = 4, /* General purpose pointer. */
+ SCBPort = 8, /* Misc. commands and operands. */
+ SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */
+ SCBCtrlMDI = 16, /* MDI interface control. */
+ SCBEarlyRx = 20, /* Early receive byte count. */
+};
+
+enum SCBCmdBits {
+ SCBMaskCmdDone=0x8000, SCBMaskRxDone=0x4000, SCBMaskCmdIdle=0x2000,
+ SCBMaskRxSuspend=0x1000, SCBMaskEarlyRx=0x0800, SCBMaskFlowCtl=0x0400,
+ SCBTriggerIntr=0x0200, SCBMaskAll=0x0100,
+ /* The rest are Rx and Tx commands. */
+ CUStart=0x0010, CUResume=0x0020, CUStatsAddr=0x0040, CUShowStats=0x0050,
+ CUCmdBase=0x0060, /* CU Base address (set to zero) . */
+ CUDumpStats=0x0070, /* Dump then reset stats counters. */
+ RxStart=0x0001, RxResume=0x0002, RxAbort=0x0004, RxAddrLoad=0x0006,
+ RxResumeNoResources=0x0007,
+};
+
+static int do_eeprom_cmd(int cmd, int cmd_len);
+void hd(void *where, int n);
+
+/***********************************************************************/
+/* I82557 related defines */
+/***********************************************************************/
+
+/* Serial EEPROM section.
+ A "bit" grungy, but we work our way through bit-by-bit :->. */
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */
+#define EE_CS 0x02 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
+#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
+#define EE_WRITE_0 0x4802
+#define EE_WRITE_1 0x4806
+#define EE_ENB (0x4800 | EE_CS)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_READ_CMD 6
+
+/* The SCB accepts the following controls for the Tx and Rx units: */
+#define CU_START 0x0010
+#define CU_RESUME 0x0020
+#define CU_STATSADDR 0x0040
+#define CU_SHOWSTATS 0x0050 /* Dump statistics counters. */
+#define CU_CMD_BASE 0x0060 /* Base address to add to add CU commands. */
+#define CU_DUMPSTATS 0x0070 /* Dump then reset stats counters. */
+
+#define RX_START 0x0001
+#define RX_RESUME 0x0002
+#define RX_ABORT 0x0004
+#define RX_ADDR_LOAD 0x0006
+#define RX_RESUMENR 0x0007
+#define INT_MASK 0x0100
+#define DRVR_INT 0x0200 /* Driver generated interrupt. */
+
+enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240,
+ S80C24, PhyUndefined, DP83840A=10, };
+
+/* Commands that can be put in a command list entry. */
+enum commands {
+ CmdNOp = 0,
+ CmdIASetup = 1,
+ CmdConfigure = 2,
+ CmdMulticastList = 3,
+ CmdTx = 4,
+ CmdTDR = 5,
+ CmdDump = 6,
+ CmdDiagnose = 7,
+
+ /* And some extra flags: */
+ CmdSuspend = 0x4000, /* Suspend after completion. */
+ CmdIntr = 0x2000, /* Interrupt after completion. */
+ CmdTxFlex = 0x0008, /* Use "Flexible mode" for CmdTx command. */
+};
+
+/* How to wait for the command unit to accept a command.
+ Typically this takes 0 ticks. */
+static inline void wait_for_cmd_done(int cmd_ioaddr)
+{
+ int wait = 0;
+ int delayed_cmd;
+
+ do
+ if (inb(cmd_ioaddr) == 0) return;
+ while(++wait <= 100);
+ delayed_cmd = inb(cmd_ioaddr);
+ do
+ if (inb(cmd_ioaddr) == 0) break;
+ while(++wait <= 10000);
+ printf("Command %2.2x was not immediately accepted, %d ticks!\n",
+ delayed_cmd, wait);
+}
+
+/* Elements of the dump_statistics block. This block must be lword aligned. */
+static struct speedo_stats {
+ u32 tx_good_frames;
+ u32 tx_coll16_errs;
+ u32 tx_late_colls;
+ u32 tx_underruns;
+ u32 tx_lost_carrier;
+ u32 tx_deferred;
+ u32 tx_one_colls;
+ u32 tx_multi_colls;
+ u32 tx_total_colls;
+ u32 rx_good_frames;
+ u32 rx_crc_errs;
+ u32 rx_align_errs;
+ u32 rx_resource_errs;
+ u32 rx_overrun_errs;
+ u32 rx_colls_errs;
+ u32 rx_runt_errs;
+ u32 done_marker;
+} lstats;
+
+/* A speedo3 TX buffer descriptor with two buffers... */
+static struct TxFD {
+ volatile s16 status;
+ s16 command;
+ u32 link; /* void * */
+ u32 tx_desc_addr; /* (almost) Always points to the tx_buf_addr element. */
+ s32 count; /* # of TBD (=2), Tx start thresh., etc. */
+ /* This constitutes two "TBD" entries: hdr and data */
+ u32 tx_buf_addr0; /* void *, header of frame to be transmitted. */
+ s32 tx_buf_size0; /* Length of Tx hdr. */
+ u32 tx_buf_addr1; /* void *, data to be transmitted. */
+ s32 tx_buf_size1; /* Length of Tx data. */
+} txfd;
+
+struct RxFD { /* Receive frame descriptor. */
+ volatile s16 status;
+ s16 command;
+ u32 link; /* struct RxFD * */
+ u32 rx_buf_addr; /* void * */
+ u16 count;
+ u16 size;
+ char packet[1518];
+};
+
+#define RXFD_COUNT 4
+static struct RxFD rxfds[RXFD_COUNT];
+static unsigned int rxfd = 0;
+
+static int congenb = 0; /* Enable congestion control in the DP83840. */
+static int txfifo = 8; /* Tx FIFO threshold in 4 byte units, 0-15 */
+static int rxfifo = 8; /* Rx FIFO threshold, default 32 bytes. */
+static int txdmacount = 0; /* Tx DMA burst length, 0-127, default 0. */
+static int rxdmacount = 0; /* Rx DMA length, 0 means no preemption. */
+
+/* I don't understand a byte in this structure. It was copied from the
+ * Linux kernel initialization for the eepro100. -- REW */
+static struct ConfCmd {
+ s16 status;
+ s16 command;
+ u32 link;
+ unsigned char data[22];
+} confcmd = {
+ 0, 0, 0, /* filled in later */
+ {22, 0x08, 0, 0, 0, 0x80, 0x32, 0x03, 1, /* 1=Use MII 0=Use AUI */
+ 0, 0x2E, 0, 0x60, 0,
+ 0xf2, 0x48, 0, 0x40, 0xf2, 0x80, /* 0x40=Force full-duplex */
+ 0x3f, 0x05, }
+};
+
+/***********************************************************************/
+/* Locally used functions */
+/***********************************************************************/
+
+/* Support function: mdio_write
+ *
+ * This probably writes to the "physical media interface chip".
+ * -- REW
+ */
+
+static int mdio_write(int phy_id, int location, int value)
+{
+ int val, boguscnt = 64*4; /* <64 usec. to complete, typ 27 ticks */
+
+ outl(0x04000000 | (location<<16) | (phy_id<<21) | value,
+ ioaddr + SCBCtrlMDI);
+ do {
+ udelay(16);
+
+ val = inl(ioaddr + SCBCtrlMDI);
+ if (--boguscnt < 0) {
+ printf(" mdio_write() timed out with val = %X.\n", val);
+ break;
+ }
+ } while (! (val & 0x10000000));
+ return val & 0xffff;
+}
+
+/* Support function: mdio_read
+ *
+ * This probably reads a register in the "physical media interface chip".
+ * -- REW
+ */
+static int mdio_read(int phy_id, int location)
+{
+ int val, boguscnt = 64*4; /* <64 usec. to complete, typ 27 ticks */
+ outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI);
+ do {
+ udelay(16);
+
+ val = inl(ioaddr + SCBCtrlMDI);
+
+ if (--boguscnt < 0) {
+ printf( " mdio_read() timed out with val = %X.\n", val);
+ break;
+ }
+ } while (! (val & 0x10000000));
+ return val & 0xffff;
+}
+
+/* The fixes for the code were kindly provided by Dragan Stancevic
+ <visitor@valinux.com> to strictly follow Intel specifications of EEPROM
+ access timing.
+ The publicly available sheet 64486302 (sec. 3.1) specifies 1us access
+ interval for serial EEPROM. However, it looks like that there is an
+ additional requirement dictating larger udelay's in the code below.
+ 2000/05/24 SAW */
+static int do_eeprom_cmd(int cmd, int cmd_len)
+{
+ unsigned retval = 0;
+ long ee_addr = ioaddr + SCBeeprom;
+
+ outw(EE_ENB, ee_addr); udelay(2);
+ outw(EE_ENB | EE_SHIFT_CLK, ee_addr); udelay(2);
+
+ /* Shift the command bits out. */
+ do {
+ short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0;
+ outw(dataval, ee_addr); udelay(2);
+ outw(dataval | EE_SHIFT_CLK, ee_addr); udelay(2);
+ retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0);
+ } while (--cmd_len >= 0);
+ outw(EE_ENB, ee_addr); udelay(2);
+
+ /* Terminate the EEPROM access. */
+ outw(EE_ENB & ~EE_CS, ee_addr);
+ return retval;
+}
+
+#if 0
+static inline void whereami (const char *str)
+{
+ printf ("%s\n", str);
+ sleep (2);
+}
+#else
+#define whereami(s)
+#endif
+
+static void eepro100_irq(struct nic *nic __unused, irq_action_t action)
+{
+ uint16_t enabled_mask = ( SCBMaskCmdDone | SCBMaskCmdIdle |
+ SCBMaskEarlyRx | SCBMaskFlowCtl );
+
+ switch ( action ) {
+ case DISABLE :
+ outw(SCBMaskAll, ioaddr + SCBCmd);
+ break;
+ case ENABLE :
+ outw(enabled_mask, ioaddr + SCBCmd);
+ break;
+ case FORCE :
+ outw(enabled_mask | SCBTriggerIntr, ioaddr + SCBCmd);
+ break;
+ }
+}
+
+/* function: eepro100_transmit
+ * This transmits a packet.
+ *
+ * Arguments: char d[6]: destination ethernet address.
+ * unsigned short t: ethernet protocol type.
+ * unsigned short s: size of the data-part of the packet.
+ * char *p: the data for the packet.
+ * returns: void.
+ */
+
+static void eepro100_transmit(struct nic *nic, const char *d, unsigned int t, unsigned int s, const char *p)
+{
+ struct eth_hdr {
+ unsigned char dst_addr[ETH_ALEN];
+ unsigned char src_addr[ETH_ALEN];
+ unsigned short type;
+ } hdr;
+ unsigned short status;
+ int s1, s2;
+
+ status = inw(ioaddr + SCBStatus);
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ outw(status & 0xfc00, ioaddr + SCBStatus);
+
+#ifdef DEBUG
+ printf ("transmitting type %hX packet (%d bytes). status = %hX, cmd=%hX\n",
+ t, s, status, inw (ioaddr + SCBCmd));
+#endif
+
+ memcpy (&hdr.dst_addr, d, ETH_ALEN);
+ memcpy (&hdr.src_addr, nic->node_addr, ETH_ALEN);
+
+ hdr.type = htons (t);
+
+ txfd.status = 0;
+ txfd.command = CmdSuspend | CmdTx | CmdTxFlex;
+ txfd.link = virt_to_bus (&txfd);
+ txfd.count = 0x02208000;
+ txfd.tx_desc_addr = virt_to_bus(&txfd.tx_buf_addr0);
+
+ txfd.tx_buf_addr0 = virt_to_bus (&hdr);
+ txfd.tx_buf_size0 = sizeof (hdr);
+
+ txfd.tx_buf_addr1 = virt_to_bus (p);
+ txfd.tx_buf_size1 = s;
+
+#ifdef DEBUG
+ printf ("txfd: \n");
+ hd (&txfd, sizeof (txfd));
+#endif
+
+ outl(virt_to_bus(&txfd), ioaddr + SCBPointer);
+ outb(CU_START, ioaddr + SCBCmd);
+ wait_for_cmd_done(ioaddr + SCBCmd);
+
+ s1 = inw (ioaddr + SCBStatus);
+ load_timer2(10*TICKS_PER_MS); /* timeout 10 ms for transmit */
+ while (!txfd.status && timer2_running())
+ /* Wait */;
+ s2 = inw (ioaddr + SCBStatus);
+
+#ifdef DEBUG
+ printf ("s1 = %hX, s2 = %hX.\n", s1, s2);
+#endif
+}
+
+/*
+ * Sometimes the receiver stops making progress. This routine knows how to
+ * get it going again, without losing packets or being otherwise nasty like
+ * a chip reset would be. Previously the driver had a whole sequence
+ * of if RxSuspended, if it's no buffers do one thing, if it's no resources,
+ * do another, etc. But those things don't really matter. Separate logic
+ * in the ISR provides for allocating buffers--the other half of operation
+ * is just making sure the receiver is active. speedo_rx_soft_reset does that.
+ * This problem with the old, more involved algorithm is shown up under
+ * ping floods on the order of 60K packets/second on a 100Mbps fdx network.
+ */
+static void
+speedo_rx_soft_reset(void)
+{
+ int i;
+
+
+#ifdef DEBUG
+ printf("reset\n");
+#endif
+ wait_for_cmd_done(ioaddr + SCBCmd);
+ /*
+ * Put the hardware into a known state.
+ */
+ outb(RX_ABORT, ioaddr + SCBCmd);
+
+ for (i = 0; i < RXFD_COUNT; i++) {
+ rxfds[i].status = 0;
+ rxfds[i].rx_buf_addr = 0xffffffff;
+ rxfds[i].count = 0;
+ rxfds[i].size = 1528;
+ }
+
+ wait_for_cmd_done(ioaddr + SCBCmd);
+
+ outl(virt_to_bus(&rxfds[rxfd]), ioaddr + SCBPointer);
+ outb(RX_START, ioaddr + SCBCmd);
+}
+
+/* function: eepro100_poll / eth_poll
+ * This receives a packet from the network.
+ *
+ * Arguments: none
+ *
+ * returns: 1 if a packet was received.
+ * 0 if no packet was received.
+ * side effects:
+ * returns the packet in the array nic->packet.
+ * returns the length of the packet in nic->packetlen.
+ */
+
+static int eepro100_poll(struct nic *nic, int retrieve)
+{
+ if (rxfds[rxfd].status) {
+ if (!retrieve)
+ return 1;
+#ifdef DEBUG
+ printf("Got a packet: Len = %d, rxfd = %d.\n",
+ rxfds[rxfd].count & 0x3fff, rxfd);
+#endif
+ /* First save the data from the rxfd */
+ nic->packetlen = rxfds[rxfd].count & 0x3fff;
+ memcpy(nic->packet, rxfds[rxfd].packet, nic->packetlen);
+
+ rxfds[rxfd].status = 0;
+ rxfds[rxfd].command = 0xc000;
+ rxfds[rxfd].rx_buf_addr = 0xFFFFFFFF;
+ rxfds[rxfd].count = 0;
+ rxfds[rxfd].size = 1528;
+ rxfds[(rxfd-1) % RXFD_COUNT].command = 0x0000;
+ rxfd = (rxfd+1) % RXFD_COUNT;
+
+#ifdef DEBUG
+ hd (nic->packet, 0x30);
+#endif
+
+ /* Acknowledge all conceivable interrupts */
+ outw(0xff00, ioaddr + SCBStatus);
+
+ return 1;
+ }
+
+ /*
+ * The chip may have suspended reception for various reasons.
+ * Check for that, and re-prime it should this be the case.
+ */
+ switch ((inw(ioaddr + SCBStatus) >> 2) & 0xf) {
+ case 0: /* Idle */
+ break;
+ case 1: /* Suspended */
+ case 2: /* No resources (RxFDs) */
+ case 9: /* Suspended with no more RBDs */
+ case 10: /* No resources due to no RBDs */
+ case 12: /* Ready with no RBDs */
+ speedo_rx_soft_reset();
+ break;
+ default:
+ /* reserved values */
+ break;
+ }
+ return 0;
+}
+
+/* function: eepro100_disable
+ * resets the card. This is used to allow Etherboot or Linux
+ * to probe the card again from a "virginal" state....
+ * Arguments: none
+ *
+ * returns: void.
+ */
+static void eepro100_disable(struct dev *dev __unused)
+{
+/* from eepro100_reset */
+ outl(0, ioaddr + SCBPort);
+/* from eepro100_disable */
+ /* See if this PartialReset solves the problem with interfering with
+ kernel operation after Etherboot hands over. - Ken 20001102 */
+ outl(2, ioaddr + SCBPort);
+
+ /* The following is from the Intel e100 driver.
+ * This hopefully solves the problem with hanging hard DOS images. */
+
+ /* wait for the reset to take effect */
+ udelay(20);
+
+ /* Mask off our interrupt line -- it is unmasked after reset */
+ {
+ u16 intr_status;
+ /* Disable interrupts on our PCI board by setting the mask bit */
+ outw(INT_MASK, ioaddr + SCBCmd);
+ intr_status = inw(ioaddr + SCBStatus);
+ /* ack and clear intrs */
+ outw(intr_status, ioaddr + SCBStatus);
+ inw(ioaddr + SCBStatus);
+ }
+}
+
+/* exported function: eepro100_probe / eth_probe
+ * initializes a card
+ *
+ * side effects:
+ * leaves the ioaddress of the 82557 chip in the variable ioaddr.
+ * leaves the 82557 initialized, and ready to recieve packets.
+ */
+
+static int eepro100_probe(struct dev *dev, struct pci_device *p)
+{
+ struct nic *nic = (struct nic *)dev;
+ unsigned short sum = 0;
+ int i;
+ int read_cmd, ee_size;
+ int options;
+ int rx_mode;
+
+ /* we cache only the first few words of the EEPROM data
+ be careful not to access beyond this array */
+ unsigned short eeprom[16];
+
+ if (p->ioaddr == 0)
+ return 0;
+ ioaddr = p->ioaddr & ~3; /* Mask the bit that says "this is an io addr" */
+ nic->ioaddr = ioaddr;
+
+ adjust_pci_device(p);
+
+ /* Copy IRQ from PCI information */
+ nic->irqno = p->irq;
+
+ if ((do_eeprom_cmd(EE_READ_CMD << 24, 27) & 0xffe0000)
+ == 0xffe0000) {
+ ee_size = 0x100;
+ read_cmd = EE_READ_CMD << 24;
+ } else {
+ ee_size = 0x40;
+ read_cmd = EE_READ_CMD << 22;
+ }
+
+ for (i = 0, sum = 0; i < ee_size; i++) {
+ unsigned short value = do_eeprom_cmd(read_cmd | (i << 16), 27);
+ if (i < (int)(sizeof(eeprom)/sizeof(eeprom[0])))
+ eeprom[i] = value;
+ sum += value;
+ }
+
+ for (i=0;i<ETH_ALEN;i++) {
+ nic->node_addr[i] = (eeprom[i/2] >> (8*(i&1))) & 0xff;
+ }
+ printf ("Ethernet addr: %!\n", nic->node_addr);
+
+ if (sum != 0xBABA)
+ printf("eepro100: Invalid EEPROM checksum %#hX, "
+ "check settings before activating this device!\n", sum);
+ outl(0, ioaddr + SCBPort);
+ udelay (10000);
+ whereami ("Got eeprom.");
+
+ /* Base = 0, disable all interrupts */
+ outl(0, ioaddr + SCBPointer);
+ outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd);
+ wait_for_cmd_done(ioaddr + SCBCmd);
+ whereami ("set rx base addr.");
+
+ outl(virt_to_bus(&lstats), ioaddr + SCBPointer);
+ outb(CU_STATSADDR, ioaddr + SCBCmd);
+ wait_for_cmd_done(ioaddr + SCBCmd);
+ whereami ("set stats addr.");
+
+ /* INIT RX stuff. */
+ for (i = 0; i < RXFD_COUNT; i++) {
+ rxfds[i].status = 0x0000;
+ rxfds[i].command = 0x0000;
+ rxfds[i].rx_buf_addr = 0xFFFFFFFF;
+ rxfds[i].count = 0;
+ rxfds[i].size = 1528;
+ rxfds[i].link = virt_to_bus(&rxfds[i+1]);
+ }
+
+ rxfds[RXFD_COUNT-1].status = 0x0000;
+ rxfds[RXFD_COUNT-1].command = 0xC000;
+ rxfds[RXFD_COUNT-1].link = virt_to_bus(&rxfds[0]);
+
+ outl(virt_to_bus(&rxfds[0]), ioaddr + SCBPointer);
+ outb(RX_START, ioaddr + SCBCmd);
+ wait_for_cmd_done(ioaddr + SCBCmd);
+
+ whereami ("started RX process.");
+
+ /* INIT TX stuff. */
+
+ /* Base = 0 */
+ outl(0, ioaddr + SCBPointer);
+ outb(CU_CMD_BASE, ioaddr + SCBCmd);
+ wait_for_cmd_done(ioaddr + SCBCmd);
+
+ whereami ("set TX base addr.");
+
+ txfd.command = (CmdIASetup);
+ txfd.status = 0x0000;
+ txfd.link = virt_to_bus (&confcmd);
+
+ {
+ char *t = (char *)&txfd.tx_desc_addr;
+
+ for (i=0;i<ETH_ALEN;i++)
+ t[i] = nic->node_addr[i];
+ }
+
+#ifdef DEBUG
+ printf ("Setup_eaddr:\n");
+ hd (&txfd, 0x20);
+#endif
+ /* options = 0x40; */ /* 10mbps half duplex... */
+ options = 0x00; /* Autosense */
+
+#ifdef PROMISC
+ rx_mode = 3;
+#elif ALLMULTI
+ rx_mode = 1;
+#else
+ rx_mode = 0;
+#endif
+
+ if ( ((eeprom[6]>>8) & 0x3f) == DP83840
+ || ((eeprom[6]>>8) & 0x3f) == DP83840A) {
+ int mdi_reg23 = mdio_read(eeprom[6] & 0x1f, 23) | 0x0422;
+ if (congenb)
+ mdi_reg23 |= 0x0100;
+ printf(" DP83840 specific setup, setting register 23 to %hX.\n",
+ mdi_reg23);
+ mdio_write(eeprom[6] & 0x1f, 23, mdi_reg23);
+ }
+ whereami ("Done DP8340 special setup.");
+ if (options != 0) {
+ mdio_write(eeprom[6] & 0x1f, 0,
+ ((options & 0x20) ? 0x2000 : 0) | /* 100mbps? */
+ ((options & 0x10) ? 0x0100 : 0)); /* Full duplex? */
+ whereami ("set mdio_register.");
+ }
+
+ confcmd.command = CmdSuspend | CmdConfigure;
+ confcmd.status = 0x0000;
+ confcmd.link = virt_to_bus (&txfd);
+ confcmd.data[1] = (txfifo << 4) | rxfifo;
+ confcmd.data[4] = rxdmacount;
+ confcmd.data[5] = txdmacount + 0x80;
+ confcmd.data[15] = (rx_mode & 2) ? 0x49: 0x48;
+ confcmd.data[19] = (options & 0x10) ? 0xC0 : 0x80;
+ confcmd.data[21] = (rx_mode & 1) ? 0x0D: 0x05;
+
+ outl(virt_to_bus(&txfd), ioaddr + SCBPointer);
+ outb(CU_START, ioaddr + SCBCmd);
+ wait_for_cmd_done(ioaddr + SCBCmd);
+
+ whereami ("started TX thingy (config, iasetup).");
+
+ load_timer2(10*TICKS_PER_MS);
+ while (!txfd.status && timer2_running())
+ /* Wait */;
+
+ /* Read the status register once to disgard stale data */
+ mdio_read(eeprom[6] & 0x1f, 1);
+ /* Check to see if the network cable is plugged in.
+ * This allows for faster failure if there is nothing
+ * we can do.
+ */
+ if (!(mdio_read(eeprom[6] & 0x1f, 1) & (1 << 2))) {
+ printf("Valid link not established\n");
+ eepro100_disable(dev);
+ return 0;
+ }
+
+ dev->disable = eepro100_disable;
+ nic->poll = eepro100_poll;
+ nic->transmit = eepro100_transmit;
+ nic->irq = eepro100_irq;
+ return 1;
+}
+
+/*********************************************************************/
+
+#ifdef DEBUG
+
+/* Hexdump a number of bytes from memory... */
+void hd (void *where, int n)
+{
+ int i;
+
+ while (n > 0) {
+ printf ("%X ", where);
+ for (i=0;i < ( (n>16)?16:n);i++)
+ printf (" %hhX", ((char *)where)[i]);
+ printf ("\n");
+ n -= 16;
+ where += 16;
+ }
+}
+#endif
+
+static struct pci_id eepro100_nics[] = {
+PCI_ROM(0x8086, 0x1029, "id1029", "Intel EtherExpressPro100 ID1029"),
+PCI_ROM(0x8086, 0x1030, "id1030", "Intel EtherExpressPro100 ID1030"),
+PCI_ROM(0x8086, 0x1031, "82801cam", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"),
+PCI_ROM(0x8086, 0x1032, "eepro100-1032", "Intel PRO/100 VE Network Connection"),
+PCI_ROM(0x8086, 0x1033, "eepro100-1033", "Intel PRO/100 VM Network Connection"),
+PCI_ROM(0x8086, 0x1034, "eepro100-1034", "Intel PRO/100 VM Network Connection"),
+PCI_ROM(0x8086, 0x1035, "eepro100-1035", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"),
+PCI_ROM(0x8086, 0x1036, "eepro100-1036", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"),
+PCI_ROM(0x8086, 0x1037, "eepro100-1037", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"),
+PCI_ROM(0x8086, 0x1038, "id1038", "Intel PRO/100 VM Network Connection"),
+PCI_ROM(0x8086, 0x1039, "82562et", "Intel PRO100 VE 82562ET"),
+PCI_ROM(0x8086, 0x103a, "id103a", "Intel Corporation 82559 InBusiness 10/100"),
+PCI_ROM(0x8086, 0x103b, "82562etb", "Intel PRO100 VE 82562ETB"),
+PCI_ROM(0x8086, 0x103c, "eepro100-103c", "Intel PRO/100 VM Network Connection"),
+PCI_ROM(0x8086, 0x103d, "eepro100-103d", "Intel PRO/100 VE Network Connection"),
+PCI_ROM(0x8086, 0x103e, "eepro100-103e", "Intel PRO/100 VM Network Connection"),
+PCI_ROM(0x8086, 0x1051, "prove", "Intel PRO/100 VE Network Connection"),
+PCI_ROM(0x8086, 0x1059, "82551qm", "Intel PRO/100 M Mobile Connection"),
+PCI_ROM(0x8086, 0x1209, "82559er", "Intel EtherExpressPro100 82559ER"),
+PCI_ROM(0x8086, 0x1227, "82865", "Intel 82865 EtherExpress PRO/100A"),
+PCI_ROM(0x8086, 0x1228, "82556", "Intel 82556 EtherExpress PRO/100 Smart"),
+PCI_ROM(0x8086, 0x1229, "eepro100", "Intel EtherExpressPro100"),
+PCI_ROM(0x8086, 0x2449, "82562em", "Intel EtherExpressPro100 82562EM"),
+PCI_ROM(0x8086, 0x2459, "82562-1", "Intel 82562 based Fast Ethernet Connection"),
+PCI_ROM(0x8086, 0x245d, "82562-2", "Intel 82562 based Fast Ethernet Connection"),
+PCI_ROM(0x8086, 0x1050, "82562ez", "Intel 82562EZ Network Connection"),
+PCI_ROM(0x8086, 0x1051, "eepro100-1051", "Intel 82801EB/ER (ICH5/ICH5R) Chipset Ethernet Controller"),
+PCI_ROM(0x8086, 0x5200, "eepro100-5200", "Intel EtherExpress PRO/100 Intelligent Server"),
+PCI_ROM(0x8086, 0x5201, "eepro100-5201", "Intel EtherExpress PRO/100 Intelligent Server"),
+};
+
+/* Cards with device ids 0x1030 to 0x103F, 0x2449, 0x2459 or 0x245D might need
+ * a workaround for hardware bug on 10 mbit half duplex (see linux driver eepro100.c)
+ * 2003/03/17 gbaum */
+
+
+static struct pci_driver eepro100_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "EEPRO100",
+ .probe = eepro100_probe,
+ .ids = eepro100_nics,
+ .id_count = sizeof(eepro100_nics)/sizeof(eepro100_nics[0]),
+ .class = 0
+};
diff --git a/src/drivers/net/epic100.c b/src/drivers/net/epic100.c
new file mode 100644
index 00000000..cd83edf4
--- /dev/null
+++ b/src/drivers/net/epic100.c
@@ -0,0 +1,520 @@
+
+/* epic100.c: A SMC 83c170 EPIC/100 fast ethernet driver for Etherboot */
+
+/* 05/06/2003 timlegge Fixed relocation and implemented Multicast */
+#define LINUX_OUT_MACROS
+
+#include "etherboot.h"
+#include "pci.h"
+#include "nic.h"
+#include "timer.h"
+#include "epic100.h"
+
+/* Condensed operations for readability */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+#define TX_RING_SIZE 2 /* use at least 2 buffers for TX */
+#define RX_RING_SIZE 2
+
+#define PKT_BUF_SZ 1536 /* Size of each temporary Tx/Rx buffer.*/
+
+/*
+#define DEBUG_RX
+#define DEBUG_TX
+#define DEBUG_EEPROM
+*/
+
+#define EPIC_DEBUG 0 /* debug level */
+
+/* The EPIC100 Rx and Tx buffer descriptors. */
+struct epic_rx_desc {
+ unsigned long status;
+ unsigned long bufaddr;
+ unsigned long buflength;
+ unsigned long next;
+};
+/* description of the tx descriptors control bits commonly used */
+#define TD_STDFLAGS TD_LASTDESC
+
+struct epic_tx_desc {
+ unsigned long status;
+ unsigned long bufaddr;
+ unsigned long buflength;
+ unsigned long next;
+};
+
+#define delay(nanosec) do { int _i = 3; while (--_i > 0) \
+ { __SLOW_DOWN_IO; }} while (0)
+
+static void epic100_open(void);
+static void epic100_init_ring(void);
+static void epic100_disable(struct dev *dev);
+static int epic100_poll(struct nic *nic, int retrieve);
+static void epic100_transmit(struct nic *nic, const char *destaddr,
+ unsigned int type, unsigned int len, const char *data);
+#ifdef DEBUG_EEPROM
+static int read_eeprom(int location);
+#endif
+static int mii_read(int phy_id, int location);
+static void epic100_irq(struct nic *nic, irq_action_t action);
+
+static int ioaddr;
+
+static int command;
+static int intstat;
+static int intmask;
+static int genctl ;
+static int eectl ;
+static int test ;
+static int mmctl ;
+static int mmdata ;
+static int lan0 ;
+static int mc0 ;
+static int rxcon ;
+static int txcon ;
+static int prcdar ;
+static int ptcdar ;
+static int eththr ;
+
+static unsigned int cur_rx, cur_tx; /* The next free ring entry */
+#ifdef DEBUG_EEPROM
+static unsigned short eeprom[64];
+#endif
+static signed char phys[4]; /* MII device addresses. */
+static struct epic_rx_desc rx_ring[RX_RING_SIZE]
+ __attribute__ ((aligned(4)));
+static struct epic_tx_desc tx_ring[TX_RING_SIZE]
+ __attribute__ ((aligned(4)));
+static unsigned char rx_packet[PKT_BUF_SZ * RX_RING_SIZE];
+static unsigned char tx_packet[PKT_BUF_SZ * TX_RING_SIZE];
+
+/***********************************************************************/
+/* Externally visible functions */
+/***********************************************************************/
+
+
+ static int
+epic100_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *)dev;
+ int i;
+ unsigned short* ap;
+ unsigned int phy, phy_idx;
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ /* Ideally we would detect all network cards in slot order. That would
+ be best done a central PCI probe dispatch, which wouldn't work
+ well with the current structure. So instead we detect just the
+ Epic cards in slot order. */
+
+ ioaddr = pci->ioaddr;
+ nic->irqno = 0;
+ nic->ioaddr = pci->ioaddr & ~3;
+
+ /* compute all used static epic100 registers address */
+ command = ioaddr + COMMAND; /* Control Register */
+ intstat = ioaddr + INTSTAT; /* Interrupt Status */
+ intmask = ioaddr + INTMASK; /* Interrupt Mask */
+ genctl = ioaddr + GENCTL; /* General Control */
+ eectl = ioaddr + EECTL; /* EEPROM Control */
+ test = ioaddr + TEST; /* Test register (clocks) */
+ mmctl = ioaddr + MMCTL; /* MII Management Interface Control */
+ mmdata = ioaddr + MMDATA; /* MII Management Interface Data */
+ lan0 = ioaddr + LAN0; /* MAC address. (0x40-0x48) */
+ mc0 = ioaddr + MC0; /* Multicast Control */
+ rxcon = ioaddr + RXCON; /* Receive Control */
+ txcon = ioaddr + TXCON; /* Transmit Control */
+ prcdar = ioaddr + PRCDAR; /* PCI Receive Current Descr Address */
+ ptcdar = ioaddr + PTCDAR; /* PCI Transmit Current Descr Address */
+ eththr = ioaddr + ETHTHR; /* Early Transmit Threshold */
+
+ /* Reset the chip & bring it out of low-power mode. */
+ outl(GC_SOFT_RESET, genctl);
+
+ /* Disable ALL interrupts by setting the interrupt mask. */
+ outl(INTR_DISABLE, intmask);
+
+ /*
+ * set the internal clocks:
+ * Application Note 7.15 says:
+ * In order to set the CLOCK TEST bit in the TEST register,
+ * perform the following:
+ *
+ * Write 0x0008 to the test register at least sixteen
+ * consecutive times.
+ *
+ * The CLOCK TEST bit is Write-Only. Writing it several times
+ * consecutively insures a successful write to the bit...
+ */
+
+ for (i = 0; i < 16; i++) {
+ outl(0x00000008, test);
+ }
+
+#ifdef DEBUG_EEPROM
+{
+ unsigned short sum = 0;
+ unsigned short value;
+ for (i = 0; i < 64; i++) {
+ value = read_eeprom(i);
+ eeprom[i] = value;
+ sum += value;
+ }
+}
+
+#if (EPIC_DEBUG > 1)
+ printf("EEPROM contents\n");
+ for (i = 0; i < 64; i++) {
+ printf(" %hhX%s", eeprom[i], i % 16 == 15 ? "\n" : "");
+ }
+#endif
+#endif
+
+ /* This could also be read from the EEPROM. */
+ ap = (unsigned short*)nic->node_addr;
+ for (i = 0; i < 3; i++)
+ *ap++ = inw(lan0 + i*4);
+
+ printf(" I/O %#hX %! ", ioaddr, nic->node_addr);
+
+ /* Find the connected MII xcvrs. */
+ for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(phys); phy++) {
+ int mii_status = mii_read(phy, 0);
+
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ phys[phy_idx++] = phy;
+#if (EPIC_DEBUG > 1)
+ printf("MII transceiver found at address %d.\n", phy);
+#endif
+ }
+ }
+ if (phy_idx == 0) {
+#if (EPIC_DEBUG > 1)
+ printf("***WARNING***: No MII transceiver found!\n");
+#endif
+ /* Use the known PHY address of the EPII. */
+ phys[0] = 3;
+ }
+
+ epic100_open();
+
+ dev->disable = epic100_disable;
+ nic->poll = epic100_poll;
+ nic->transmit = epic100_transmit;
+ nic->irq = epic100_irq;
+
+ return 1;
+}
+
+static void set_rx_mode(void)
+{
+ unsigned char mc_filter[8];
+ int i;
+ memset(mc_filter, 0xff, sizeof(mc_filter));
+ outl(0x0C, rxcon);
+ for(i = 0; i < 4; i++)
+ outw(((unsigned short *)mc_filter)[i], mc0 + i*4);
+ return;
+}
+
+ static void
+epic100_open(void)
+{
+ int mii_reg5;
+ int full_duplex = 0;
+ unsigned long tmp;
+
+ epic100_init_ring();
+
+ /* Pull the chip out of low-power mode, and set for PCI read multiple. */
+ outl(GC_RX_FIFO_THR_64 | GC_MRC_READ_MULT | GC_ONE_COPY, genctl);
+
+ outl(TX_FIFO_THRESH, eththr);
+
+ tmp = TC_EARLY_TX_ENABLE | TX_SLOT_TIME;
+
+ mii_reg5 = mii_read(phys[0], 5);
+ if (mii_reg5 != 0xffff && (mii_reg5 & 0x0100)) {
+ full_duplex = 1;
+ printf(" full-duplex mode");
+ tmp |= TC_LM_FULL_DPX;
+ } else
+ tmp |= TC_LM_NORMAL;
+
+ outl(tmp, txcon);
+
+ /* Give adress of RX and TX ring to the chip */
+ outl(virt_to_le32desc(&rx_ring), prcdar);
+ outl(virt_to_le32desc(&tx_ring), ptcdar);
+
+ /* Start the chip's Rx process: receive unicast and broadcast */
+ set_rx_mode();
+ outl(CR_START_RX | CR_QUEUE_RX, command);
+
+ putchar('\n');
+}
+
+/* Initialize the Rx and Tx rings. */
+ static void
+epic100_init_ring(void)
+{
+ int i;
+
+ cur_rx = cur_tx = 0;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ rx_ring[i].status = cpu_to_le32(RRING_OWN); /* Owned by Epic chip */
+ rx_ring[i].buflength = cpu_to_le32(PKT_BUF_SZ);
+ rx_ring[i].bufaddr = virt_to_bus(&rx_packet[i * PKT_BUF_SZ]);
+ rx_ring[i].next = virt_to_le32desc(&rx_ring[i + 1]) ;
+ }
+ /* Mark the last entry as wrapping the ring. */
+ rx_ring[i-1].next = virt_to_le32desc(&rx_ring[0]);
+
+ /*
+ *The Tx buffer descriptor is filled in as needed,
+ * but we do need to clear the ownership bit.
+ */
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ tx_ring[i].status = 0x0000; /* Owned by CPU */
+ tx_ring[i].buflength = 0x0000 | cpu_to_le32(TD_STDFLAGS << 16);
+ tx_ring[i].bufaddr = virt_to_bus(&tx_packet[i * PKT_BUF_SZ]);
+ tx_ring[i].next = virt_to_le32desc(&tx_ring[i + 1]);
+ }
+ tx_ring[i-1].next = virt_to_le32desc(&tx_ring[0]);
+}
+
+/* function: epic100_transmit
+ * This transmits a packet.
+ *
+ * Arguments: char d[6]: destination ethernet address.
+ * unsigned short t: ethernet protocol type.
+ * unsigned short s: size of the data-part of the packet.
+ * char *p: the data for the packet.
+ * returns: void.
+ */
+ static void
+epic100_transmit(struct nic *nic, const char *destaddr, unsigned int type,
+ unsigned int len, const char *data)
+{
+ unsigned short nstype;
+ unsigned char *txp;
+ int entry;
+
+ /* Calculate the next Tx descriptor entry. */
+ entry = cur_tx % TX_RING_SIZE;
+
+ if ((tx_ring[entry].status & TRING_OWN) == TRING_OWN) {
+ printf("eth_transmit: Unable to transmit. status=%hX. Resetting...\n",
+ tx_ring[entry].status);
+
+ epic100_open();
+ return;
+ }
+
+ txp = tx_packet + (entry * PKT_BUF_SZ);
+
+ memcpy(txp, destaddr, ETH_ALEN);
+ memcpy(txp + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ nstype = htons(type);
+ memcpy(txp + 12, (char*)&nstype, 2);
+ memcpy(txp + ETH_HLEN, data, len);
+
+ len += ETH_HLEN;
+ len &= 0x0FFF;
+ while(len < ETH_ZLEN)
+ txp[len++] = '\0';
+ /*
+ * Caution: the write order is important here,
+ * set the base address with the "ownership"
+ * bits last.
+ */
+
+ tx_ring[entry].buflength |= cpu_to_le32(len);
+ tx_ring[entry].status = cpu_to_le32(len << 16) |
+ cpu_to_le32(TRING_OWN); /* Pass ownership to the chip. */
+
+ cur_tx++;
+
+ /* Trigger an immediate transmit demand. */
+ outl(CR_QUEUE_TX, command);
+
+ load_timer2(10*TICKS_PER_MS); /* timeout 10 ms for transmit */
+ while ((le32_to_cpu(tx_ring[entry].status) & (TRING_OWN)) && timer2_running())
+ /* Wait */;
+
+ if ((le32_to_cpu(tx_ring[entry].status) & TRING_OWN) != 0)
+ printf("Oops, transmitter timeout, status=%hX\n",
+ tx_ring[entry].status);
+}
+
+/* function: epic100_poll / eth_poll
+ * This receives a packet from the network.
+ *
+ * Arguments: none
+ *
+ * returns: 1 if a packet was received.
+ * 0 if no pacet was received.
+ * side effects:
+ * returns the packet in the array nic->packet.
+ * returns the length of the packet in nic->packetlen.
+ */
+
+ static int
+epic100_poll(struct nic *nic, int retrieve)
+{
+ int entry;
+ int retcode;
+ int status;
+ entry = cur_rx % RX_RING_SIZE;
+
+ if ((rx_ring[entry].status & cpu_to_le32(RRING_OWN)) == RRING_OWN)
+ return (0);
+
+ if ( ! retrieve ) return 1;
+
+ status = le32_to_cpu(rx_ring[entry].status);
+ /* We own the next entry, it's a new packet. Send it up. */
+
+#if (EPIC_DEBUG > 4)
+ printf("epic_poll: entry %d status %hX\n", entry, status);
+#endif
+
+ cur_rx++;
+ if (status & 0x2000) {
+ printf("epic_poll: Giant packet\n");
+ retcode = 0;
+ } else if (status & 0x0006) {
+ /* Rx Frame errors are counted in hardware. */
+ printf("epic_poll: Frame received with errors\n");
+ retcode = 0;
+ } else {
+ /* Omit the four octet CRC from the length. */
+ nic->packetlen = le32_to_cpu((rx_ring[entry].buflength))- 4;
+ memcpy(nic->packet, &rx_packet[entry * PKT_BUF_SZ], nic->packetlen);
+ retcode = 1;
+ }
+
+ /* Clear all error sources. */
+ outl(status & INTR_CLEARERRS, intstat);
+
+ /* Give the descriptor back to the chip */
+ rx_ring[entry].status = RRING_OWN;
+
+ /* Restart Receiver */
+ outl(CR_START_RX | CR_QUEUE_RX, command);
+
+ return retcode;
+}
+
+
+ static void
+epic100_disable(struct dev *dev __unused)
+{
+ /* Soft reset the chip. */
+ outl(GC_SOFT_RESET, genctl);
+}
+
+static void epic100_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+#ifdef DEBUG_EEPROM
+/* Serial EEPROM section. */
+
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */
+#define EE_CS 0x02 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x08 /* EEPROM chip data in. */
+#define EE_WRITE_0 0x01
+#define EE_WRITE_1 0x09
+#define EE_DATA_READ 0x10 /* EEPROM chip data out. */
+#define EE_ENB (0x0001 | EE_CS)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5 << 6)
+#define EE_READ_CMD (6 << 6)
+#define EE_ERASE_CMD (7 << 6)
+
+#define eeprom_delay(n) delay(n)
+
+ static int
+read_eeprom(int location)
+{
+ int i;
+ int retval = 0;
+ int read_cmd = location | EE_READ_CMD;
+
+ outl(EE_ENB & ~EE_CS, eectl);
+ outl(EE_ENB, eectl);
+
+ /* Shift the read command bits out. */
+ for (i = 10; i >= 0; i--) {
+ short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+ outl(EE_ENB | dataval, eectl);
+ eeprom_delay(100);
+ outl(EE_ENB | dataval | EE_SHIFT_CLK, eectl);
+ eeprom_delay(150);
+ outl(EE_ENB | dataval, eectl); /* Finish EEPROM a clock tick. */
+ eeprom_delay(250);
+ }
+ outl(EE_ENB, eectl);
+
+ for (i = 16; i > 0; i--) {
+ outl(EE_ENB | EE_SHIFT_CLK, eectl);
+ eeprom_delay(100);
+ retval = (retval << 1) | ((inl(eectl) & EE_DATA_READ) ? 1 : 0);
+ outl(EE_ENB, eectl);
+ eeprom_delay(100);
+ }
+
+ /* Terminate the EEPROM access. */
+ outl(EE_ENB & ~EE_CS, eectl);
+ return retval;
+}
+#endif
+
+
+#define MII_READOP 1
+#define MII_WRITEOP 2
+
+ static int
+mii_read(int phy_id, int location)
+{
+ int i;
+
+ outl((phy_id << 9) | (location << 4) | MII_READOP, mmctl);
+ /* Typical operation takes < 50 ticks. */
+
+ for (i = 4000; i > 0; i--)
+ if ((inl(mmctl) & MII_READOP) == 0)
+ break;
+ return inw(mmdata);
+}
+
+
+static struct pci_id epic100_nics[] = {
+PCI_ROM(0x10b8, 0x0005, "epic100", "SMC EtherPowerII"), /* SMC 83c170 EPIC/100 */
+PCI_ROM(0x10b8, 0x0006, "smc-83c175", "SMC EPIC/C 83c175"),
+};
+
+static struct pci_driver epic100_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "EPIC100",
+ .probe = epic100_probe,
+ .ids = epic100_nics,
+ .id_count = sizeof(epic100_nics)/sizeof(epic100_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/epic100.h b/src/drivers/net/epic100.h
new file mode 100644
index 00000000..61bd1d94
--- /dev/null
+++ b/src/drivers/net/epic100.h
@@ -0,0 +1,188 @@
+#ifndef _EPIC100_H_
+# define _EPIC100_H_
+
+#ifndef PCI_VENDOR_SMC
+# define PCI_VENDOR_SMC 0x10B8
+#endif
+
+#ifndef PCI_DEVICE_SMC_EPIC100
+# define PCI_DEVICE_SMC_EPIC100 0x0005
+#endif
+
+#define PCI_DEVICE_ID_NONE 0xFFFF
+
+/* Offsets to registers (using SMC names). */
+enum epic100_registers {
+ COMMAND= 0, /* Control Register */
+ INTSTAT= 4, /* Interrupt Status */
+ INTMASK= 8, /* Interrupt Mask */
+ GENCTL = 0x0C, /* General Control */
+ NVCTL = 0x10, /* Non Volatile Control */
+ EECTL = 0x14, /* EEPROM Control */
+ TEST = 0x1C, /* Test register: marked as reserved (see in source code) */
+ CRCCNT = 0x20, /* CRC Error Counter */
+ ALICNT = 0x24, /* Frame Alignment Error Counter */
+ MPCNT = 0x28, /* Missed Packet Counter */
+ MMCTL = 0x30, /* MII Management Interface Control */
+ MMDATA = 0x34, /* MII Management Interface Data */
+ MIICFG = 0x38, /* MII Configuration */
+ IPG = 0x3C, /* InterPacket Gap */
+ LAN0 = 0x40, /* MAC address. (0x40-0x48) */
+ IDCHK = 0x4C, /* BoardID/ Checksum */
+ MC0 = 0x50, /* Multicast filter table. (0x50-0x5c) */
+ RXCON = 0x60, /* Receive Control */
+ TXCON = 0x70, /* Transmit Control */
+ TXSTAT = 0x74, /* Transmit Status */
+ PRCDAR = 0x84, /* PCI Receive Current Descriptor Address */
+ PRSTAT = 0xA4, /* PCI Receive DMA Status */
+ PRCPTHR= 0xB0, /* PCI Receive Copy Threshold */
+ PTCDAR = 0xC4, /* PCI Transmit Current Descriptor Address */
+ ETHTHR = 0xDC /* Early Transmit Threshold */
+};
+
+/* Command register (CR_) bits */
+#define CR_STOP_RX (0x00000001)
+#define CR_START_RX (0x00000002)
+#define CR_QUEUE_TX (0x00000004)
+#define CR_QUEUE_RX (0x00000008)
+#define CR_NEXTFRAME (0x00000010)
+#define CR_STOP_TX_DMA (0x00000020)
+#define CR_STOP_RX_DMA (0x00000040)
+#define CR_TX_UGO (0x00000080)
+
+/* Interrupt register bits. NI means No Interrupt generated */
+
+#define INTR_RX_THR_STA (0x00400000) /* rx copy threshold status NI */
+#define INTR_RX_BUFF_EMPTY (0x00200000) /* rx buffers empty. NI */
+#define INTR_TX_IN_PROG (0x00100000) /* tx copy in progess. NI */
+#define INTR_RX_IN_PROG (0x00080000) /* rx copy in progress. NI */
+#define INTR_TXIDLE (0x00040000) /* tx idle. NI */
+#define INTR_RXIDLE (0x00020000) /* rx idle. NI */
+#define INTR_INTR_ACTIVE (0x00010000) /* Interrupt active. NI */
+#define INTR_RX_STATUS_OK (0x00008000) /* rx status valid. NI */
+#define INTR_PCI_TGT_ABT (0x00004000) /* PCI Target abort */
+#define INTR_PCI_MASTER_ABT (0x00002000) /* PCI Master abort */
+#define INTR_PCI_PARITY_ERR (0x00001000) /* PCI adress parity error */
+#define INTR_PCI_DATA_ERR (0x00000800) /* PCI data parity error */
+#define INTR_RX_THR_CROSSED (0x00000400) /* rx copy threshold crossed */
+#define INTR_CNTFULL (0x00000200) /* Counter overflow */
+#define INTR_TXUNDERRUN (0x00000100) /* tx underrun. */
+#define INTR_TXEMPTY (0x00000080) /* tx queue empty */
+#define INTR_TX_CH_COMPLETE (0x00000040) /* tx chain complete */
+#define INTR_TXDONE (0x00000020) /* tx complete (w or w/o err) */
+#define INTR_RXERROR (0x00000010) /* rx error (CRC) */
+#define INTR_RXOVERFLOW (0x00000008) /* rx buffer overflow */
+#define INTR_RX_QUEUE_EMPTY (0x00000004) /* rx queue empty. */
+#define INTR_RXHEADER (0x00000002) /* header copy complete */
+#define INTR_RXDONE (0x00000001) /* Receive copy complete */
+
+#define INTR_CLEARINTR (0x00007FFF)
+#define INTR_VALIDBITS (0x007FFFFF)
+#define INTR_DISABLE (0x00000000)
+#define INTR_CLEARERRS (0x00007F18)
+#define INTR_ABNINTR (INTR_CNTFULL | INTR_TXUNDERRUN | INTR_RXOVERFLOW)
+
+/* General Control (GC_) bits */
+
+#define GC_SOFT_RESET (0x00000001)
+#define GC_INTR_ENABLE (0x00000002)
+#define GC_SOFT_INTR (0x00000004)
+#define GC_POWER_DOWN (0x00000008)
+#define GC_ONE_COPY (0x00000010)
+#define GC_BIG_ENDIAN (0x00000020)
+#define GC_RX_PREEMPT_TX (0x00000040)
+#define GC_TX_PREEMPT_RX (0x00000080)
+
+/*
+ * Receive FIFO Threshold values
+ * Control the level at which the PCI burst state machine
+ * begins to empty the receive FIFO. Possible values: 0-3
+ *
+ * 0 => 32, 1 => 64, 2 => 96 3 => 128 bytes.
+ */
+#define GC_RX_FIFO_THR_32 (0x00000000)
+#define GC_RX_FIFO_THR_64 (0x00000100)
+#define GC_RX_FIFO_THR_96 (0x00000200)
+#define GC_RX_FIFO_THR_128 (0x00000300)
+
+/* Memory Read Control (MRC_) values */
+#define GC_MRC_MEM_READ (0x00000000)
+#define GC_MRC_READ_MULT (0x00000400)
+#define GC_MRC_READ_LINE (0x00000800)
+
+#define GC_SOFTBIT0 (0x00001000)
+#define GC_SOFTBIT1 (0x00002000)
+#define GC_RESET_PHY (0x00004000)
+
+/* Definitions of the Receive Control (RC_) register bits */
+
+#define RC_SAVE_ERRORED_PKT (0x00000001)
+#define RC_SAVE_RUNT_FRAMES (0x00000002)
+#define RC_RCV_BROADCAST (0x00000004)
+#define RC_RCV_MULTICAST (0x00000008)
+#define RC_RCV_INVERSE_PKT (0x00000010)
+#define RC_PROMISCUOUS_MODE (0x00000020)
+#define RC_MONITOR_MODE (0x00000040)
+#define RC_EARLY_RCV_ENABLE (0x00000080)
+
+/* description of the rx descriptors control bits */
+#define RD_FRAGLIST (0x0001) /* Desc points to a fragment list */
+#define RD_LLFORM (0x0002) /* Frag list format */
+#define RD_HDR_CPY (0x0004) /* Desc used for header copy */
+
+/* Definition of the Transmit CONTROL (TC) register bits */
+
+#define TC_EARLY_TX_ENABLE (0x00000001)
+
+/* Loopback Mode (LM_) Select valuesbits */
+#define TC_LM_NORMAL (0x00000000)
+#define TC_LM_INTERNAL (0x00000002)
+#define TC_LM_EXTERNAL (0x00000004)
+#define TC_LM_FULL_DPX (0x00000006)
+
+#define TX_SLOT_TIME (0x00000078)
+
+/* Bytes transferred to chip before transmission starts. */
+#define TX_FIFO_THRESH 128 /* Rounded down to 4 byte units. */
+
+/* description of rx descriptors status bits */
+#define RRING_PKT_INTACT (0x0001)
+#define RRING_ALIGN_ERR (0x0002)
+#define RRING_CRC_ERR (0x0004)
+#define RRING_MISSED_PKT (0x0008)
+#define RRING_MULTICAST (0x0010)
+#define RRING_BROADCAST (0x0020)
+#define RRING_RECEIVER_DISABLE (0x0040)
+#define RRING_STATUS_VALID (0x1000)
+#define RRING_FRAGLIST_ERR (0x2000)
+#define RRING_HDR_COPIED (0x4000)
+#define RRING_OWN (0x8000)
+
+/* error summary */
+#define RRING_ERROR (RRING_ALIGN_ERR|RRING_CRC_ERR)
+
+/* description of tx descriptors status bits */
+#define TRING_PKT_INTACT (0x0001) /* pkt transmitted. */
+#define TRING_PKT_NONDEFER (0x0002) /* pkt xmitted w/o deferring */
+#define TRING_COLL (0x0004) /* pkt xmitted w collisions */
+#define TRING_CARR (0x0008) /* carrier sense lost */
+#define TRING_UNDERRUN (0x0010) /* DMA underrun */
+#define TRING_HB_COLL (0x0020) /* Collision detect Heartbeat */
+#define TRING_WIN_COLL (0x0040) /* out of window collision */
+#define TRING_DEFERRED (0x0080) /* Deferring */
+#define TRING_COLL_COUNT (0x0F00) /* collision counter (mask) */
+#define TRING_COLL_EXCESS (0x1000) /* tx aborted: excessive colls */
+#define TRING_OWN (0x8000) /* desc ownership bit */
+
+/* error summary */
+#define TRING_ABORT (TRING_COLL_EXCESS|TRING_WIN_COLL|TRING_UNDERRUN)
+#define TRING_ERROR (TRING_DEFERRED|TRING_WIN_COLL|TRING_UNDERRUN|TRING_CARR/*|TRING_COLL*/ )
+
+/* description of the tx descriptors control bits */
+#define TD_FRAGLIST (0x0001) /* Desc points to a fragment list */
+#define TD_LLFORM (0x0002) /* Frag list format */
+#define TD_IAF (0x0004) /* Generate Interrupt after tx */
+#define TD_NOCRC (0x0008) /* No CRC generated */
+#define TD_LASTDESC (0x0010) /* Last desc for this frame */
+
+#endif /* _EPIC100_H_ */
diff --git a/src/drivers/net/forcedeth.c b/src/drivers/net/forcedeth.c
new file mode 100644
index 00000000..326c4cca
--- /dev/null
+++ b/src/drivers/net/forcedeth.c
@@ -0,0 +1,1039 @@
+/**************************************************************************
+* forcedeth.c -- Etherboot device driver for the NVIDIA nForce
+* media access controllers.
+*
+* Note: This driver is based on the Linux driver that was based on
+* a cleanroom reimplementation which was based on reverse
+* engineered documentation written by Carl-Daniel Hailfinger
+* and Andrew de Quincey. It's neither supported nor endorsed
+* by NVIDIA Corp. Use at your own risk.
+*
+* Written 2004 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code based on:
+* forcedeth: Ethernet driver for NVIDIA nForce media access controllers:
+*
+* (C) 2003 Manfred Spraul
+* See Linux Driver for full information
+*
+* Linux Driver Version 0.22, 19 Jan 2004
+*
+*
+* REVISION HISTORY:
+* ================
+* v1.0 01-31-2004 timlegge Initial port of Linux driver
+* v1.1 02-03-2004 timlegge Large Clean up, first release
+*
+* Indent Options: indent -kr -i8
+***************************************************************************/
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include "pci.h"
+/* Include timer support functions */
+#include "timer.h"
+
+#define drv_version "v1.1"
+#define drv_date "02-03-2004"
+
+//#define TFTM_DEBUG
+#ifdef TFTM_DEBUG
+#define dprintf(x) printf x
+#else
+#define dprintf(x)
+#endif
+
+typedef unsigned char u8;
+typedef signed char s8;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned int u32;
+typedef signed int s32;
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+unsigned long BASE;
+/* NIC specific static variables go here */
+
+
+/*
+ * Hardware access:
+ */
+
+#define DEV_NEED_LASTPACKET1 0x0001
+#define DEV_IRQMASK_1 0x0002
+#define DEV_IRQMASK_2 0x0004
+#define DEV_NEED_TIMERIRQ 0x0008
+
+enum {
+ NvRegIrqStatus = 0x000,
+#define NVREG_IRQSTAT_MIIEVENT 0040
+#define NVREG_IRQSTAT_MASK 0x1ff
+ NvRegIrqMask = 0x004,
+#define NVREG_IRQ_RX 0x0002
+#define NVREG_IRQ_RX_NOBUF 0x0004
+#define NVREG_IRQ_TX_ERR 0x0008
+#define NVREG_IRQ_TX2 0x0010
+#define NVREG_IRQ_TIMER 0x0020
+#define NVREG_IRQ_LINK 0x0040
+#define NVREG_IRQ_TX1 0x0100
+#define NVREG_IRQMASK_WANTED_1 0x005f
+#define NVREG_IRQMASK_WANTED_2 0x0147
+#define NVREG_IRQ_UNKNOWN (~(NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR|NVREG_IRQ_TX2|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_TX1))
+
+ NvRegUnknownSetupReg6 = 0x008,
+#define NVREG_UNKSETUP6_VAL 3
+
+/*
+ * NVREG_POLL_DEFAULT is the interval length of the timer source on the nic
+ * NVREG_POLL_DEFAULT=97 would result in an interval length of 1 ms
+ */
+ NvRegPollingInterval = 0x00c,
+#define NVREG_POLL_DEFAULT 970
+ NvRegMisc1 = 0x080,
+#define NVREG_MISC1_HD 0x02
+#define NVREG_MISC1_FORCE 0x3b0f3c
+
+ NvRegTransmitterControl = 0x084,
+#define NVREG_XMITCTL_START 0x01
+ NvRegTransmitterStatus = 0x088,
+#define NVREG_XMITSTAT_BUSY 0x01
+
+ NvRegPacketFilterFlags = 0x8c,
+#define NVREG_PFF_ALWAYS 0x7F0008
+#define NVREG_PFF_PROMISC 0x80
+#define NVREG_PFF_MYADDR 0x20
+
+ NvRegOffloadConfig = 0x90,
+#define NVREG_OFFLOAD_HOMEPHY 0x601
+#define NVREG_OFFLOAD_NORMAL 0x5ee
+ NvRegReceiverControl = 0x094,
+#define NVREG_RCVCTL_START 0x01
+ NvRegReceiverStatus = 0x98,
+#define NVREG_RCVSTAT_BUSY 0x01
+
+ NvRegRandomSeed = 0x9c,
+#define NVREG_RNDSEED_MASK 0x00ff
+#define NVREG_RNDSEED_FORCE 0x7f00
+
+ NvRegUnknownSetupReg1 = 0xA0,
+#define NVREG_UNKSETUP1_VAL 0x16070f
+ NvRegUnknownSetupReg2 = 0xA4,
+#define NVREG_UNKSETUP2_VAL 0x16
+ NvRegMacAddrA = 0xA8,
+ NvRegMacAddrB = 0xAC,
+ NvRegMulticastAddrA = 0xB0,
+#define NVREG_MCASTADDRA_FORCE 0x01
+ NvRegMulticastAddrB = 0xB4,
+ NvRegMulticastMaskA = 0xB8,
+ NvRegMulticastMaskB = 0xBC,
+
+ NvRegTxRingPhysAddr = 0x100,
+ NvRegRxRingPhysAddr = 0x104,
+ NvRegRingSizes = 0x108,
+#define NVREG_RINGSZ_TXSHIFT 0
+#define NVREG_RINGSZ_RXSHIFT 16
+ NvRegUnknownTransmitterReg = 0x10c,
+ NvRegLinkSpeed = 0x110,
+#define NVREG_LINKSPEED_FORCE 0x10000
+#define NVREG_LINKSPEED_10 10
+#define NVREG_LINKSPEED_100 100
+#define NVREG_LINKSPEED_1000 1000
+ NvRegUnknownSetupReg5 = 0x130,
+#define NVREG_UNKSETUP5_BIT31 (1<<31)
+ NvRegUnknownSetupReg3 = 0x134,
+#define NVREG_UNKSETUP3_VAL1 0x200010
+ NvRegTxRxControl = 0x144,
+#define NVREG_TXRXCTL_KICK 0x0001
+#define NVREG_TXRXCTL_BIT1 0x0002
+#define NVREG_TXRXCTL_BIT2 0x0004
+#define NVREG_TXRXCTL_IDLE 0x0008
+#define NVREG_TXRXCTL_RESET 0x0010
+ NvRegMIIStatus = 0x180,
+#define NVREG_MIISTAT_ERROR 0x0001
+#define NVREG_MIISTAT_LINKCHANGE 0x0008
+#define NVREG_MIISTAT_MASK 0x000f
+#define NVREG_MIISTAT_MASK2 0x000f
+ NvRegUnknownSetupReg4 = 0x184,
+#define NVREG_UNKSETUP4_VAL 8
+
+ NvRegAdapterControl = 0x188,
+#define NVREG_ADAPTCTL_START 0x02
+#define NVREG_ADAPTCTL_LINKUP 0x04
+#define NVREG_ADAPTCTL_PHYVALID 0x4000
+#define NVREG_ADAPTCTL_RUNNING 0x100000
+#define NVREG_ADAPTCTL_PHYSHIFT 24
+ NvRegMIISpeed = 0x18c,
+#define NVREG_MIISPEED_BIT8 (1<<8)
+#define NVREG_MIIDELAY 5
+ NvRegMIIControl = 0x190,
+#define NVREG_MIICTL_INUSE 0x10000
+#define NVREG_MIICTL_WRITE 0x08000
+#define NVREG_MIICTL_ADDRSHIFT 5
+ NvRegMIIData = 0x194,
+ NvRegWakeUpFlags = 0x200,
+#define NVREG_WAKEUPFLAGS_VAL 0x7770
+#define NVREG_WAKEUPFLAGS_BUSYSHIFT 24
+#define NVREG_WAKEUPFLAGS_ENABLESHIFT 16
+#define NVREG_WAKEUPFLAGS_D3SHIFT 12
+#define NVREG_WAKEUPFLAGS_D2SHIFT 8
+#define NVREG_WAKEUPFLAGS_D1SHIFT 4
+#define NVREG_WAKEUPFLAGS_D0SHIFT 0
+#define NVREG_WAKEUPFLAGS_ACCEPT_MAGPAT 0x01
+#define NVREG_WAKEUPFLAGS_ACCEPT_WAKEUPPAT 0x02
+#define NVREG_WAKEUPFLAGS_ACCEPT_LINKCHANGE 0x04
+
+ NvRegPatternCRC = 0x204,
+ NvRegPatternMask = 0x208,
+ NvRegPowerCap = 0x268,
+#define NVREG_POWERCAP_D3SUPP (1<<30)
+#define NVREG_POWERCAP_D2SUPP (1<<26)
+#define NVREG_POWERCAP_D1SUPP (1<<25)
+ NvRegPowerState = 0x26c,
+#define NVREG_POWERSTATE_POWEREDUP 0x8000
+#define NVREG_POWERSTATE_VALID 0x0100
+#define NVREG_POWERSTATE_MASK 0x0003
+#define NVREG_POWERSTATE_D0 0x0000
+#define NVREG_POWERSTATE_D1 0x0001
+#define NVREG_POWERSTATE_D2 0x0002
+#define NVREG_POWERSTATE_D3 0x0003
+};
+
+
+
+#define NV_TX_LASTPACKET (1<<0)
+#define NV_TX_RETRYERROR (1<<3)
+#define NV_TX_LASTPACKET1 (1<<8)
+#define NV_TX_DEFERRED (1<<10)
+#define NV_TX_CARRIERLOST (1<<11)
+#define NV_TX_LATECOLLISION (1<<12)
+#define NV_TX_UNDERFLOW (1<<13)
+#define NV_TX_ERROR (1<<14)
+#define NV_TX_VALID (1<<15)
+
+#define NV_RX_DESCRIPTORVALID (1<<0)
+#define NV_RX_MISSEDFRAME (1<<1)
+#define NV_RX_SUBSTRACT1 (1<<3)
+#define NV_RX_ERROR1 (1<<7)
+#define NV_RX_ERROR2 (1<<8)
+#define NV_RX_ERROR3 (1<<9)
+#define NV_RX_ERROR4 (1<<10)
+#define NV_RX_CRCERR (1<<11)
+#define NV_RX_OVERFLOW (1<<12)
+#define NV_RX_FRAMINGERR (1<<13)
+#define NV_RX_ERROR (1<<14)
+#define NV_RX_AVAIL (1<<15)
+
+/* Miscelaneous hardware related defines: */
+#define NV_PCI_REGSZ 0x270
+
+/* various timeout delays: all in usec */
+#define NV_TXRX_RESET_DELAY 4
+#define NV_TXSTOP_DELAY1 10
+#define NV_TXSTOP_DELAY1MAX 500000
+#define NV_TXSTOP_DELAY2 100
+#define NV_RXSTOP_DELAY1 10
+#define NV_RXSTOP_DELAY1MAX 500000
+#define NV_RXSTOP_DELAY2 100
+#define NV_SETUP5_DELAY 5
+#define NV_SETUP5_DELAYMAX 50000
+#define NV_POWERUP_DELAY 5
+#define NV_POWERUP_DELAYMAX 5000
+#define NV_MIIBUSY_DELAY 50
+#define NV_MIIPHY_DELAY 10
+#define NV_MIIPHY_DELAYMAX 10000
+
+#define NV_WAKEUPPATTERNS 5
+#define NV_WAKEUPMASKENTRIES 4
+
+/* General driver defaults */
+#define NV_WATCHDOG_TIMEO (2*HZ)
+#define DEFAULT_MTU 1500 /* also maximum supported, at least for now */
+
+#define RX_RING 4
+#define TX_RING 2
+/* limited to 1 packet until we understand NV_TX_LASTPACKET */
+#define TX_LIMIT_STOP 10
+#define TX_LIMIT_START 5
+
+/* rx/tx mac addr + type + vlan + align + slack*/
+#define RX_NIC_BUFSIZE (DEFAULT_MTU + 64)
+/* even more slack */
+#define RX_ALLOC_BUFSIZE (DEFAULT_MTU + 128)
+
+#define OOM_REFILL (1+HZ/20)
+#define POLL_WAIT (1+HZ/100)
+
+struct ring_desc {
+ u32 PacketBuffer;
+ u16 Length;
+ u16 Flags;
+};
+
+
+/* Define the TX Descriptor */
+static struct ring_desc tx_ring[TX_RING];
+
+/* Create a static buffer of size RX_BUF_SZ for each
+TX Descriptor. All descriptors point to a
+part of this buffer */
+static unsigned char txb[TX_RING * RX_NIC_BUFSIZE];
+
+/* Define the TX Descriptor */
+static struct ring_desc rx_ring[RX_RING];
+
+/* Create a static buffer of size RX_BUF_SZ for each
+RX Descriptor All descriptors point to a
+part of this buffer */
+static unsigned char rxb[RX_RING * RX_NIC_BUFSIZE];
+
+/* Private Storage for the NIC */
+struct forcedeth_private {
+ /* General data:
+ * Locking: spin_lock(&np->lock); */
+ int in_shutdown;
+ u32 linkspeed;
+ int duplex;
+ int phyaddr;
+
+ /* General data: RO fields */
+ u8 *ring_addr;
+ u32 orig_mac[2];
+ u32 irqmask;
+ /* rx specific fields.
+ * Locking: Within irq hander or disable_irq+spin_lock(&np->lock);
+ */
+ struct ring_desc *rx_ring;
+ unsigned int cur_rx, refill_rx;
+ struct sk_buff *rx_skbuff[RX_RING];
+ u32 rx_dma[RX_RING];
+ unsigned int rx_buf_sz;
+
+ /*
+ * tx specific fields.
+ */
+ struct ring_desc *tx_ring;
+ unsigned int next_tx, nic_tx;
+ struct sk_buff *tx_skbuff[TX_RING];
+ u32 tx_dma[TX_RING];
+ u16 tx_flags;
+} npx;
+
+static struct forcedeth_private *np;
+
+static inline void pci_push(u8 * base)
+{
+ /* force out pending posted writes */
+ readl(base);
+}
+static int reg_delay(int offset, u32 mask,
+ u32 target, int delay, int delaymax, const char *msg)
+{
+ u8 *base = (u8 *) BASE;
+
+ pci_push(base);
+ do {
+ udelay(delay);
+ delaymax -= delay;
+ if (delaymax < 0) {
+ if (msg)
+ printf(msg);
+ return 1;
+ }
+ } while ((readl(base + offset) & mask) != target);
+ return 0;
+}
+
+#define MII_READ (-1)
+#define MII_PHYSID1 0x02 /* PHYS ID 1 */
+#define MII_PHYSID2 0x03 /* PHYS ID 2 */
+#define MII_BMCR 0x00 /* Basic mode control register */
+#define MII_BMSR 0x01 /* Basic mode status register */
+#define MII_ADVERTISE 0x04 /* Advertisement control reg */
+#define MII_LPA 0x05 /* Link partner ability reg */
+
+#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */
+
+/* Link partner ability register. */
+#define LPA_SLCT 0x001f /* Same as advertise selector */
+#define LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */
+#define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */
+#define LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */
+#define LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */
+#define LPA_100BASE4 0x0200 /* Can do 100mbps 4k packets */
+#define LPA_RESV 0x1c00 /* Unused... */
+#define LPA_RFAULT 0x2000 /* Link partner faulted */
+#define LPA_LPACK 0x4000 /* Link partner acked us */
+#define LPA_NPAGE 0x8000 /* Next page bit */
+
+/* mii_rw: read/write a register on the PHY.
+ *
+ * Caller must guarantee serialization
+ */
+static int mii_rw(struct nic *nic __unused, int addr, int miireg,
+ int value)
+{
+ u8 *base = (u8 *) BASE;
+ int was_running;
+ u32 reg;
+ int retval;
+
+ writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus);
+ was_running = 0;
+ reg = readl(base + NvRegAdapterControl);
+ if (reg & NVREG_ADAPTCTL_RUNNING) {
+ was_running = 1;
+ writel(reg & ~NVREG_ADAPTCTL_RUNNING,
+ base + NvRegAdapterControl);
+ }
+ reg = readl(base + NvRegMIIControl);
+ if (reg & NVREG_MIICTL_INUSE) {
+ writel(NVREG_MIICTL_INUSE, base + NvRegMIIControl);
+ udelay(NV_MIIBUSY_DELAY);
+ }
+
+ reg =
+ NVREG_MIICTL_INUSE | (addr << NVREG_MIICTL_ADDRSHIFT) | miireg;
+ if (value != MII_READ) {
+ writel(value, base + NvRegMIIData);
+ reg |= NVREG_MIICTL_WRITE;
+ }
+ writel(reg, base + NvRegMIIControl);
+
+ if (reg_delay(NvRegMIIControl, NVREG_MIICTL_INUSE, 0,
+ NV_MIIPHY_DELAY, NV_MIIPHY_DELAYMAX, NULL)) {
+ dprintf(("mii_rw of reg %d at PHY %d timed out.\n",
+ miireg, addr));
+ retval = -1;
+ } else if (value != MII_READ) {
+ /* it was a write operation - fewer failures are detectable */
+ dprintf(("mii_rw wrote 0x%x to reg %d at PHY %d\n",
+ value, miireg, addr));
+ retval = 0;
+ } else if (readl(base + NvRegMIIStatus) & NVREG_MIISTAT_ERROR) {
+ dprintf(("mii_rw of reg %d at PHY %d failed.\n",
+ miireg, addr));
+ retval = -1;
+ } else {
+ /* FIXME: why is that required? */
+ udelay(50);
+ retval = readl(base + NvRegMIIData);
+ dprintf(("mii_rw read from reg %d at PHY %d: 0x%x.\n",
+ miireg, addr, retval));
+ }
+ if (was_running) {
+ reg = readl(base + NvRegAdapterControl);
+ writel(reg | NVREG_ADAPTCTL_RUNNING,
+ base + NvRegAdapterControl);
+ }
+ return retval;
+}
+
+static void start_rx(struct nic *nic __unused)
+{
+ u8 *base = (u8 *) BASE;
+
+ dprintf(("start_rx\n"));
+ /* Already running? Stop it. */
+ if (readl(base + NvRegReceiverControl) & NVREG_RCVCTL_START) {
+ writel(0, base + NvRegReceiverControl);
+ pci_push(base);
+ }
+ writel(np->linkspeed, base + NvRegLinkSpeed);
+ pci_push(base);
+ writel(NVREG_RCVCTL_START, base + NvRegReceiverControl);
+ pci_push(base);
+}
+
+static void stop_rx(void)
+{
+ u8 *base = (u8 *) BASE;
+
+ dprintf(("stop_rx\n"));
+ writel(0, base + NvRegReceiverControl);
+ reg_delay(NvRegReceiverStatus, NVREG_RCVSTAT_BUSY, 0,
+ NV_RXSTOP_DELAY1, NV_RXSTOP_DELAY1MAX,
+ "stop_rx: ReceiverStatus remained busy");
+
+ udelay(NV_RXSTOP_DELAY2);
+ writel(0, base + NvRegLinkSpeed);
+}
+
+static void start_tx(struct nic *nic __unused)
+{
+ u8 *base = (u8 *) BASE;
+
+ dprintf(("start_tx\n"));
+ writel(NVREG_XMITCTL_START, base + NvRegTransmitterControl);
+ pci_push(base);
+}
+
+static void stop_tx(void)
+{
+ u8 *base = (u8 *) BASE;
+
+ dprintf(("stop_tx\n"));
+ writel(0, base + NvRegTransmitterControl);
+ reg_delay(NvRegTransmitterStatus, NVREG_XMITSTAT_BUSY, 0,
+ NV_TXSTOP_DELAY1, NV_TXSTOP_DELAY1MAX,
+ "stop_tx: TransmitterStatus remained busy");
+
+ udelay(NV_TXSTOP_DELAY2);
+ writel(0, base + NvRegUnknownTransmitterReg);
+}
+
+
+static void txrx_reset(struct nic *nic __unused)
+{
+ u8 *base = (u8 *) BASE;
+
+ dprintf(("txrx_reset\n"));
+ writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET,
+ base + NvRegTxRxControl);
+ pci_push(base);
+ udelay(NV_TXRX_RESET_DELAY);
+ writel(NVREG_TXRXCTL_BIT2, base + NvRegTxRxControl);
+ pci_push(base);
+}
+
+/*
+ * alloc_rx: fill rx ring entries.
+ * Return 1 if the allocations for the skbs failed and the
+ * rx engine is without Available descriptors
+ */
+static int alloc_rx(struct nic *nic __unused)
+{
+ unsigned int refill_rx = np->refill_rx;
+ int i;
+ //while (np->cur_rx != refill_rx) {
+ for (i = 0; i < RX_RING; i++) {
+ //int nr = refill_rx % RX_RING;
+ rx_ring[i].PacketBuffer =
+ virt_to_le32desc(&rxb[i * RX_NIC_BUFSIZE]);
+ rx_ring[i].Length = cpu_to_le16(RX_NIC_BUFSIZE);
+ wmb();
+ rx_ring[i].Flags = cpu_to_le16(NV_RX_AVAIL);
+ /* printf("alloc_rx: Packet %d marked as Available\n",
+ refill_rx); */
+ refill_rx++;
+ }
+ np->refill_rx = refill_rx;
+ if (np->cur_rx - refill_rx == RX_RING)
+ return 1;
+ return 0;
+}
+
+static int update_linkspeed(struct nic *nic)
+{
+ int adv, lpa, newdup;
+ u32 newls;
+ adv = mii_rw(nic, np->phyaddr, MII_ADVERTISE, MII_READ);
+ lpa = mii_rw(nic, np->phyaddr, MII_LPA, MII_READ);
+ dprintf(("update_linkspeed: PHY advertises 0x%hX, lpa 0x%hX.\n",
+ adv, lpa));
+
+ /* FIXME: handle parallel detection properly, handle gigabit ethernet */
+ lpa = lpa & adv;
+ if (lpa & LPA_100FULL) {
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_100;
+ newdup = 1;
+ } else if (lpa & LPA_100HALF) {
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_100;
+ newdup = 0;
+ } else if (lpa & LPA_10FULL) {
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+ newdup = 1;
+ } else if (lpa & LPA_10HALF) {
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+ newdup = 0;
+ } else {
+ printf("bad ability %hX - falling back to 10HD.\n", lpa);
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+ newdup = 0;
+ }
+ if (np->duplex != newdup || np->linkspeed != newls) {
+ np->duplex = newdup;
+ np->linkspeed = newls;
+ return 1;
+ }
+ return 0;
+}
+
+
+
+static int init_ring(struct nic *nic)
+{
+ int i;
+
+ np->next_tx = np->nic_tx = 0;
+ for (i = 0; i < TX_RING; i++) {
+ tx_ring[i].Flags = 0;
+ }
+
+ np->cur_rx = 0;
+ np->refill_rx = 0;
+ for (i = 0; i < RX_RING; i++) {
+ rx_ring[i].Flags = 0;
+ }
+ return alloc_rx(nic);
+}
+
+static void set_multicast(struct nic *nic)
+{
+
+ u8 *base = (u8 *) BASE;
+ u32 addr[2];
+ u32 mask[2];
+ u32 pff;
+ u32 alwaysOff[2];
+ u32 alwaysOn[2];
+
+ memset(addr, 0, sizeof(addr));
+ memset(mask, 0, sizeof(mask));
+
+ pff = NVREG_PFF_MYADDR;
+
+ alwaysOn[0] = alwaysOn[1] = alwaysOff[0] = alwaysOff[1] = 0;
+
+ addr[0] = alwaysOn[0];
+ addr[1] = alwaysOn[1];
+ mask[0] = alwaysOn[0] | alwaysOff[0];
+ mask[1] = alwaysOn[1] | alwaysOff[1];
+
+ addr[0] |= NVREG_MCASTADDRA_FORCE;
+ pff |= NVREG_PFF_ALWAYS;
+ stop_rx();
+ writel(addr[0], base + NvRegMulticastAddrA);
+ writel(addr[1], base + NvRegMulticastAddrB);
+ writel(mask[0], base + NvRegMulticastMaskA);
+ writel(mask[1], base + NvRegMulticastMaskB);
+ writel(pff, base + NvRegPacketFilterFlags);
+ start_rx(nic);
+}
+
+/**************************************************************************
+RESET - Reset the NIC to prepare for use
+***************************************************************************/
+static int forcedeth_reset(struct nic *nic)
+{
+ u8 *base = (u8 *) BASE;
+ int ret, oom, i;
+ ret = 0;
+ dprintf(("forcedeth: open\n"));
+
+ /* 1) erase previous misconfiguration */
+ /* 4.1-1: stop adapter: ignored, 4.3 seems to be overkill */
+ writel(NVREG_MCASTADDRA_FORCE, base + NvRegMulticastAddrA);
+ writel(0, base + NvRegMulticastAddrB);
+ writel(0, base + NvRegMulticastMaskA);
+ writel(0, base + NvRegMulticastMaskB);
+ writel(0, base + NvRegPacketFilterFlags);
+ writel(0, base + NvRegAdapterControl);
+ writel(0, base + NvRegLinkSpeed);
+ writel(0, base + NvRegUnknownTransmitterReg);
+ txrx_reset(nic);
+ writel(0, base + NvRegUnknownSetupReg6);
+
+ /* 2) initialize descriptor rings */
+ np->in_shutdown = 0;
+ oom = init_ring(nic);
+
+ /* 3) set mac address */
+ {
+ u32 mac[2];
+
+ mac[0] =
+ (nic->node_addr[0] << 0) + (nic->node_addr[1] << 8) +
+ (nic->node_addr[2] << 16) + (nic->node_addr[3] << 24);
+ mac[1] =
+ (nic->node_addr[4] << 0) + (nic->node_addr[5] << 8);
+
+ writel(mac[0], base + NvRegMacAddrA);
+ writel(mac[1], base + NvRegMacAddrB);
+ }
+
+ /* 4) continue setup */
+ np->linkspeed = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+ np->duplex = 0;
+ writel(NVREG_UNKSETUP3_VAL1, base + NvRegUnknownSetupReg3);
+ writel(0, base + NvRegTxRxControl);
+ pci_push(base);
+ writel(NVREG_TXRXCTL_BIT1, base + NvRegTxRxControl);
+
+ reg_delay(NvRegUnknownSetupReg5, NVREG_UNKSETUP5_BIT31,
+ NVREG_UNKSETUP5_BIT31, NV_SETUP5_DELAY,
+ NV_SETUP5_DELAYMAX,
+ "open: SetupReg5, Bit 31 remained off\n");
+ writel(0, base + NvRegUnknownSetupReg4);
+
+ /* 5) Find a suitable PHY */
+ writel(NVREG_MIISPEED_BIT8 | NVREG_MIIDELAY, base + NvRegMIISpeed);
+ for (i = 1; i < 32; i++) {
+ int id1, id2;
+
+ id1 = mii_rw(nic, i, MII_PHYSID1, MII_READ);
+ if (id1 < 0)
+ continue;
+ id2 = mii_rw(nic, i, MII_PHYSID2, MII_READ);
+ if (id2 < 0)
+ continue;
+ dprintf(("open: Found PHY %04x:%04x at address %d.\n",
+ id1, id2, i));
+ np->phyaddr = i;
+
+ update_linkspeed(nic);
+
+ break;
+ }
+ if (i == 32) {
+ printf("open: failing due to lack of suitable PHY.\n");
+ ret = -1;
+ goto out_drain;
+ }
+
+ printf("%d-Mbs Link, %s-Duplex\n",
+ np->linkspeed & NVREG_LINKSPEED_10 ? 10 : 100,
+ np->duplex ? "Full" : "Half");
+ /* 6) continue setup */
+ writel(NVREG_MISC1_FORCE | (np->duplex ? 0 : NVREG_MISC1_HD),
+ base + NvRegMisc1);
+ writel(readl(base + NvRegTransmitterStatus),
+ base + NvRegTransmitterStatus);
+ writel(NVREG_PFF_ALWAYS, base + NvRegPacketFilterFlags);
+ writel(NVREG_OFFLOAD_NORMAL, base + NvRegOffloadConfig);
+
+ writel(readl(base + NvRegReceiverStatus),
+ base + NvRegReceiverStatus);
+
+ /* FIXME: I cheated and used the calculator to get a random number */
+ i = 75963081;
+ writel(NVREG_RNDSEED_FORCE | (i & NVREG_RNDSEED_MASK),
+ base + NvRegRandomSeed);
+ writel(NVREG_UNKSETUP1_VAL, base + NvRegUnknownSetupReg1);
+ writel(NVREG_UNKSETUP2_VAL, base + NvRegUnknownSetupReg2);
+ writel(NVREG_POLL_DEFAULT, base + NvRegPollingInterval);
+ writel(NVREG_UNKSETUP6_VAL, base + NvRegUnknownSetupReg6);
+ writel((np->
+ phyaddr << NVREG_ADAPTCTL_PHYSHIFT) |
+ NVREG_ADAPTCTL_PHYVALID, base + NvRegAdapterControl);
+ writel(NVREG_UNKSETUP4_VAL, base + NvRegUnknownSetupReg4);
+ writel(NVREG_WAKEUPFLAGS_VAL, base + NvRegWakeUpFlags);
+
+ /* 7) start packet processing */
+ writel((u32) virt_to_le32desc(&rx_ring[0]),
+ base + NvRegRxRingPhysAddr);
+ writel((u32) virt_to_le32desc(&tx_ring[0]),
+ base + NvRegTxRingPhysAddr);
+
+
+ writel(((RX_RING - 1) << NVREG_RINGSZ_RXSHIFT) +
+ ((TX_RING - 1) << NVREG_RINGSZ_TXSHIFT),
+ base + NvRegRingSizes);
+
+ i = readl(base + NvRegPowerState);
+ if ((i & NVREG_POWERSTATE_POWEREDUP) == 0) {
+ writel(NVREG_POWERSTATE_POWEREDUP | i,
+ base + NvRegPowerState);
+ }
+ pci_push(base);
+ udelay(10);
+ writel(readl(base + NvRegPowerState) | NVREG_POWERSTATE_VALID,
+ base + NvRegPowerState);
+ writel(NVREG_ADAPTCTL_RUNNING, base + NvRegAdapterControl);
+
+ writel(0, base + NvRegIrqMask);
+ pci_push(base);
+ writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
+ pci_push(base);
+ writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus);
+ writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
+ pci_push(base);
+/*
+ writel(np->irqmask, base + NvRegIrqMask);
+*/
+ writel(NVREG_MCASTADDRA_FORCE, base + NvRegMulticastAddrA);
+ writel(0, base + NvRegMulticastAddrB);
+ writel(0, base + NvRegMulticastMaskA);
+ writel(0, base + NvRegMulticastMaskB);
+ writel(NVREG_PFF_ALWAYS | NVREG_PFF_MYADDR,
+ base + NvRegPacketFilterFlags);
+
+ set_multicast(nic);
+ //start_rx(nic);
+ start_tx(nic);
+
+ if (!
+ (mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ) &
+ BMSR_ANEGCOMPLETE)) {
+ printf("no link during initialization.\n");
+ }
+
+ udelay(10000);
+ out_drain:
+ return ret;
+}
+
+//extern void hex_dump(const char *data, const unsigned int len);
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int forcedeth_poll(struct nic *nic, int retrieve)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+
+ struct ring_desc *prd;
+ int len;
+ int i;
+
+ i = np->cur_rx % RX_RING;
+ prd = &rx_ring[i];
+
+ if ( ! (prd->Flags & cpu_to_le16(NV_RX_DESCRIPTORVALID)) ) {
+ return 0;
+ }
+
+ if ( ! retrieve ) return 1;
+
+ /* got a valid packet - forward it to the network core */
+ len = cpu_to_le16(prd->Length);
+ nic->packetlen = len;
+ //hex_dump(rxb + (i * RX_NIC_BUFSIZE), len);
+ memcpy(nic->packet, rxb +
+ (i * RX_NIC_BUFSIZE), nic->packetlen);
+
+ wmb();
+ np->cur_rx++;
+ alloc_rx(nic);
+ return 1;
+}
+
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void forcedeth_transmit(struct nic *nic, const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p)
+{ /* Packet */
+ /* send the packet to destination */
+ u8 *ptxb;
+ u16 nstype;
+ //u16 status;
+ u8 *base = (u8 *) BASE;
+ int nr = np->next_tx % TX_RING;
+
+ /* point to the current txb incase multiple tx_rings are used */
+ ptxb = txb + (nr * RX_NIC_BUFSIZE);
+ //np->tx_skbuff[nr] = ptxb;
+
+ /* copy the packet to ring buffer */
+ memcpy(ptxb, d, ETH_ALEN); /* dst */
+ memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */
+ nstype = htons((u16) t); /* type */
+ memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2); /* type */
+ memcpy(ptxb + ETH_HLEN, p, s);
+
+ s += ETH_HLEN;
+ while (s < ETH_ZLEN) /* pad to min length */
+ ptxb[s++] = '\0';
+
+ tx_ring[nr].PacketBuffer = (u32) virt_to_le32desc(ptxb);
+ tx_ring[nr].Length = cpu_to_le16(s - 1);
+
+ wmb();
+ tx_ring[nr].Flags = np->tx_flags;
+
+ writel(NVREG_TXRXCTL_KICK, base + NvRegTxRxControl);
+ pci_push(base);
+ tx_ring[nr].Flags = np->tx_flags;
+ np->next_tx++;
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void forcedeth_disable(struct dev *dev __unused)
+{
+ /* put the card in its initial state */
+ /* This function serves 3 purposes.
+ * This disables DMA and interrupts so we don't receive
+ * unexpected packets or interrupts from the card after
+ * etherboot has finished.
+ * This frees resources so etherboot may use
+ * this driver on another interface
+ * This allows etherboot to reinitialize the interface
+ * if something is something goes wrong.
+ */
+ u8 *base = (u8 *) BASE;
+ np->in_shutdown = 1;
+ stop_tx();
+ stop_rx();
+
+ /* disable interrupts on the nic or we will lock up */
+ writel(0, base + NvRegIrqMask);
+ pci_push(base);
+ dprintf(("Irqmask is zero again\n"));
+
+ /* specia op:o write back the misordered MAC address - otherwise
+ * the next probe_nic would see a wrong address.
+ */
+ writel(np->orig_mac[0], base + NvRegMacAddrA);
+ writel(np->orig_mac[1], base + NvRegMacAddrB);
+}
+
+/**************************************************************************
+IRQ - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void forcedeth_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+#define IORESOURCE_MEM 0x00000200
+#define board_found 1
+#define valid_link 0
+static int forcedeth_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *) dev;
+ unsigned long addr;
+ int sz;
+ u8 *base;
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ printf("forcedeth.c: Found %s, vendor=0x%hX, device=0x%hX\n",
+ pci->name, pci->vendor, pci->dev_id);
+
+ nic->irqno = 0;
+ nic->ioaddr = pci->ioaddr & ~3;
+
+ /* point to private storage */
+ np = &npx;
+
+ adjust_pci_device(pci);
+
+ addr = pci_bar_start(pci, PCI_BASE_ADDRESS_0);
+ sz = pci_bar_size(pci, PCI_BASE_ADDRESS_0);
+
+ /* BASE is used throughout to address the card */
+ BASE = (unsigned long) ioremap(addr, sz);
+ if (!BASE)
+ return 0;
+ //rx_ring[0] = rx_ring;
+ //tx_ring[0] = tx_ring;
+
+ /* read the mac address */
+ base = (u8 *) BASE;
+ np->orig_mac[0] = readl(base + NvRegMacAddrA);
+ np->orig_mac[1] = readl(base + NvRegMacAddrB);
+
+ nic->node_addr[0] = (np->orig_mac[1] >> 8) & 0xff;
+ nic->node_addr[1] = (np->orig_mac[1] >> 0) & 0xff;
+ nic->node_addr[2] = (np->orig_mac[0] >> 24) & 0xff;
+ nic->node_addr[3] = (np->orig_mac[0] >> 16) & 0xff;
+ nic->node_addr[4] = (np->orig_mac[0] >> 8) & 0xff;
+ nic->node_addr[5] = (np->orig_mac[0] >> 0) & 0xff;
+#ifdef LINUX
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ /*
+ * Bad mac address. At least one bios sets the mac address
+ * to 01:23:45:67:89:ab
+ */
+ printk(KERN_ERR
+ "%s: Invalid Mac address detected: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ pci_name(pci_dev), dev->dev_addr[0],
+ dev->dev_addr[1], dev->dev_addr[2],
+ dev->dev_addr[3], dev->dev_addr[4],
+ dev->dev_addr[5]);
+ printk(KERN_ERR
+ "Please complain to your hardware vendor. Switching to a random MAC.\n");
+ dev->dev_addr[0] = 0x00;
+ dev->dev_addr[1] = 0x00;
+ dev->dev_addr[2] = 0x6c;
+ get_random_bytes(&dev->dev_addr[3], 3);
+ }
+#endif
+ printf("%s: MAC Address %!, ", pci->name, nic->node_addr);
+
+ np->tx_flags =
+ cpu_to_le16(NV_TX_LASTPACKET | NV_TX_LASTPACKET1 |
+ NV_TX_VALID);
+ switch (pci->dev_id) {
+ case 0x01C3: // nforce
+ np->irqmask = NVREG_IRQMASK_WANTED_2;
+ np->irqmask |= NVREG_IRQ_TIMER;
+ break;
+ case 0x0066: // nforce2
+ np->tx_flags |= cpu_to_le16(NV_TX_LASTPACKET1);
+ np->irqmask = NVREG_IRQMASK_WANTED_2;
+ np->irqmask |= NVREG_IRQ_TIMER;
+ break;
+ case 0x00D6: // nforce3
+ np->tx_flags |= cpu_to_le16(NV_TX_LASTPACKET1);
+ np->irqmask = NVREG_IRQMASK_WANTED_2;
+ np->irqmask |= NVREG_IRQ_TIMER;
+
+ }
+ dprintf(("%s: forcedeth.c: subsystem: %hX:%hX bound to %s\n",
+ pci->name, pci->vendor, pci->dev_id, pci->name));
+
+ forcedeth_reset(nic);
+// if (board_found && valid_link)
+ /* point to NIC specific routines */
+ dev->disable = forcedeth_disable;
+ nic->poll = forcedeth_poll;
+ nic->transmit = forcedeth_transmit;
+ nic->irq = forcedeth_irq;
+ return 1;
+// }
+ /* else */
+}
+
+static struct pci_id forcedeth_nics[] = {
+ PCI_ROM(0x10de, 0x01C3, "nforce", "nForce Ethernet Controller"),
+ PCI_ROM(0x10de, 0x0066, "nforce2", "nForce2 Ethernet Controller"),
+ PCI_ROM(0x10de, 0x00D6, "nforce3", "nForce3 Ethernet Controller"),
+};
+
+static struct pci_driver forcedeth_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "forcedeth",
+ .probe = forcedeth_probe,
+ .ids = forcedeth_nics,
+ .id_count = sizeof(forcedeth_nics) / sizeof(forcedeth_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/hfa384x.h b/src/drivers/net/hfa384x.h
new file mode 100644
index 00000000..9fcbd294
--- /dev/null
+++ b/src/drivers/net/hfa384x.h
@@ -0,0 +1,2744 @@
+/* src/prism2/include/prism2/hfa384x.h
+*
+* Defines the constants and data structures for the hfa384x
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* [Implementation and usage notes]
+*
+* [References]
+* CW10 Programmer's Manual v1.5
+* IEEE 802.11 D10.0
+*
+* --------------------------------------------------------------------
+*/
+
+#ifndef _HFA384x_H
+#define _HFA384x_H
+
+/*=============================================================*/
+#define HFA384x_FIRMWARE_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+
+#define HFA384x_LEVEL_TO_dBm(v) (0x100 + (v) * 100 / 255 - 100)
+
+/*------ Constants --------------------------------------------*/
+/*--- Mins & Maxs -----------------------------------*/
+#define HFA384x_CMD_ALLOC_LEN_MIN ((UINT16)4)
+#define HFA384x_CMD_ALLOC_LEN_MAX ((UINT16)2400)
+#define HFA384x_BAP_DATALEN_MAX ((UINT16)4096)
+#define HFA384x_BAP_OFFSET_MAX ((UINT16)4096)
+#define HFA384x_PORTID_MAX ((UINT16)7)
+#define HFA384x_NUMPORTS_MAX ((UINT16)(HFA384x_PORTID_MAX+1))
+#define HFA384x_PDR_LEN_MAX ((UINT16)512) /* in bytes, from EK */
+#define HFA384x_PDA_RECS_MAX ((UINT16)200) /* a guess */
+#define HFA384x_PDA_LEN_MAX ((UINT16)1024) /* in bytes, from EK */
+#define HFA384x_SCANRESULT_MAX ((UINT16)31)
+#define HFA384x_HSCANRESULT_MAX ((UINT16)31)
+#define HFA384x_CHINFORESULT_MAX ((UINT16)16)
+#define HFA384x_DRVR_FIDSTACKLEN_MAX (10)
+#define HFA384x_DRVR_TXBUF_MAX (sizeof(hfa384x_tx_frame_t) + \
+ WLAN_DATA_MAXLEN - \
+ WLAN_WEP_IV_LEN - \
+ WLAN_WEP_ICV_LEN + 2)
+#define HFA384x_DRVR_MAGIC (0x4a2d)
+#define HFA384x_INFODATA_MAXLEN (sizeof(hfa384x_infodata_t))
+#define HFA384x_INFOFRM_MAXLEN (sizeof(hfa384x_InfFrame_t))
+#define HFA384x_RID_GUESSING_MAXLEN 2048 /* I'm not really sure */
+#define HFA384x_RIDDATA_MAXLEN HFA384x_RID_GUESSING_MAXLEN
+#define HFA384x_USB_RWMEM_MAXLEN 2048
+
+/*--- Support Constants -----------------------------*/
+#define HFA384x_BAP_PROC ((UINT16)0)
+#define HFA384x_BAP_INT ((UINT16)1)
+#define HFA384x_PORTTYPE_BSS ((UINT16)1)
+#define HFA384x_PORTTYPE_WDS ((UINT16)2)
+#define HFA384x_PORTTYPE_IBSS ((UINT16)3)
+#define HFA384x_WEPFLAGS_PRIVINVOKED ((UINT16)BIT0)
+#define HFA384x_WEPFLAGS_EXCLUDE ((UINT16)BIT1)
+#define HFA384x_WEPFLAGS_DISABLE_TXCRYPT ((UINT16)BIT4)
+#define HFA384x_WEPFLAGS_DISABLE_RXCRYPT ((UINT16)BIT7)
+#define HFA384x_WEPFLAGS_IV_INTERVAL1 ((UINT16)0)
+#define HFA384x_WEPFLAGS_IV_INTERVAL10 ((UINT16)BIT5)
+#define HFA384x_WEPFLAGS_IV_INTERVAL50 ((UINT16)BIT6)
+#define HFA384x_WEPFLAGS_IV_INTERVAL100 ((UINT16)(BIT5 | BIT6))
+#define HFA384x_ROAMMODE_FWSCAN_FWROAM ((UINT16)1)
+#define HFA384x_ROAMMODE_FWSCAN_HOSTROAM ((UINT16)2)
+#define HFA384x_ROAMMODE_HOSTSCAN_HOSTROAM ((UINT16)3)
+#define HFA384x_PORTSTATUS_DISABLED ((UINT16)1)
+#define HFA384x_PORTSTATUS_INITSRCH ((UINT16)2)
+#define HFA384x_PORTSTATUS_CONN_IBSS ((UINT16)3)
+#define HFA384x_PORTSTATUS_CONN_ESS ((UINT16)4)
+#define HFA384x_PORTSTATUS_OOR_ESS ((UINT16)5)
+#define HFA384x_PORTSTATUS_CONN_WDS ((UINT16)6)
+#define HFA384x_PORTSTATUS_HOSTAP ((UINT16)8)
+#define HFA384x_RATEBIT_1 ((UINT16)1)
+#define HFA384x_RATEBIT_2 ((UINT16)2)
+#define HFA384x_RATEBIT_5dot5 ((UINT16)4)
+#define HFA384x_RATEBIT_11 ((UINT16)8)
+
+/*--- Just some symbolic names for legibility -------*/
+#define HFA384x_TXCMD_NORECL ((UINT16)0)
+#define HFA384x_TXCMD_RECL ((UINT16)1)
+
+/*--- MAC Internal memory constants and macros ------*/
+/* masks and macros used to manipulate MAC internal memory addresses. */
+/* MAC internal memory addresses are 23 bit quantities. The MAC uses
+ * a paged address space where the upper 16 bits are the page number
+ * and the lower 7 bits are the offset. There are various Host API
+ * elements that require two 16-bit quantities to specify a MAC
+ * internal memory address. Unfortunately, some of the API's use a
+ * page/offset format where the offset value is JUST the lower seven
+ * bits and the page is the remaining 16 bits. Some of the API's
+ * assume that the 23 bit address has been split at the 16th bit. We
+ * refer to these two formats as AUX format and CMD format. The
+ * macros below help handle some of this.
+ */
+
+/* Handy constant */
+#define HFA384x_ADDR_AUX_OFF_MAX ((UINT16)0x007f)
+
+/* Mask bits for discarding unwanted pieces in a flat address */
+#define HFA384x_ADDR_FLAT_AUX_PAGE_MASK (0x007fff80)
+#define HFA384x_ADDR_FLAT_AUX_OFF_MASK (0x0000007f)
+#define HFA384x_ADDR_FLAT_CMD_PAGE_MASK (0xffff0000)
+#define HFA384x_ADDR_FLAT_CMD_OFF_MASK (0x0000ffff)
+
+/* Mask bits for discarding unwanted pieces in AUX format 16-bit address parts */
+#define HFA384x_ADDR_AUX_PAGE_MASK (0xffff)
+#define HFA384x_ADDR_AUX_OFF_MASK (0x007f)
+
+/* Mask bits for discarding unwanted pieces in CMD format 16-bit address parts */
+#define HFA384x_ADDR_CMD_PAGE_MASK (0x007f)
+#define HFA384x_ADDR_CMD_OFF_MASK (0xffff)
+
+/* Make a 32-bit flat address from AUX format 16-bit page and offset */
+#define HFA384x_ADDR_AUX_MKFLAT(p,o) \
+ (((UINT32)(((UINT16)(p))&HFA384x_ADDR_AUX_PAGE_MASK)) <<7) | \
+ ((UINT32)(((UINT16)(o))&HFA384x_ADDR_AUX_OFF_MASK))
+
+/* Make a 32-bit flat address from CMD format 16-bit page and offset */
+#define HFA384x_ADDR_CMD_MKFLAT(p,o) \
+ (((UINT32)(((UINT16)(p))&HFA384x_ADDR_CMD_PAGE_MASK)) <<16) | \
+ ((UINT32)(((UINT16)(o))&HFA384x_ADDR_CMD_OFF_MASK))
+
+/* Make AUX format offset and page from a 32-bit flat address */
+#define HFA384x_ADDR_AUX_MKPAGE(f) \
+ ((UINT16)((((UINT32)(f))&HFA384x_ADDR_FLAT_AUX_PAGE_MASK)>>7))
+#define HFA384x_ADDR_AUX_MKOFF(f) \
+ ((UINT16)(((UINT32)(f))&HFA384x_ADDR_FLAT_AUX_OFF_MASK))
+
+/* Make CMD format offset and page from a 32-bit flat address */
+#define HFA384x_ADDR_CMD_MKPAGE(f) \
+ ((UINT16)((((UINT32)(f))&HFA384x_ADDR_FLAT_CMD_PAGE_MASK)>>16))
+#define HFA384x_ADDR_CMD_MKOFF(f) \
+ ((UINT16)(((UINT32)(f))&HFA384x_ADDR_FLAT_CMD_OFF_MASK))
+
+/*--- Aux register masks/tests ----------------------*/
+/* Some of the upper bits of the AUX offset register are used to */
+/* select address space. */
+#define HFA384x_AUX_CTL_EXTDS (0x00)
+#define HFA384x_AUX_CTL_NV (0x01)
+#define HFA384x_AUX_CTL_PHY (0x02)
+#define HFA384x_AUX_CTL_ICSRAM (0x03)
+
+/* Make AUX register offset and page values from a flat address */
+#define HFA384x_AUX_MKOFF(f, c) \
+ (HFA384x_ADDR_AUX_MKOFF(f) | (((UINT16)(c))<<12))
+#define HFA384x_AUX_MKPAGE(f) HFA384x_ADDR_AUX_MKPAGE(f)
+
+
+/*--- Controller Memory addresses -------------------*/
+#define HFA3842_PDA_BASE (0x007f0000UL)
+#define HFA3841_PDA_BASE (0x003f0000UL)
+#define HFA3841_PDA_BOGUS_BASE (0x00390000UL)
+
+/*--- Driver Download states -----------------------*/
+#define HFA384x_DLSTATE_DISABLED 0
+#define HFA384x_DLSTATE_RAMENABLED 1
+#define HFA384x_DLSTATE_FLASHENABLED 2
+#define HFA384x_DLSTATE_FLASHWRITTEN 3
+#define HFA384x_DLSTATE_FLASHWRITEPENDING 4
+
+/*--- Register I/O offsets --------------------------*/
+#if ((WLAN_HOSTIF == WLAN_PCMCIA) || (WLAN_HOSTIF == WLAN_PLX))
+
+#define HFA384x_CMD_OFF (0x00)
+#define HFA384x_PARAM0_OFF (0x02)
+#define HFA384x_PARAM1_OFF (0x04)
+#define HFA384x_PARAM2_OFF (0x06)
+#define HFA384x_STATUS_OFF (0x08)
+#define HFA384x_RESP0_OFF (0x0A)
+#define HFA384x_RESP1_OFF (0x0C)
+#define HFA384x_RESP2_OFF (0x0E)
+#define HFA384x_INFOFID_OFF (0x10)
+#define HFA384x_RXFID_OFF (0x20)
+#define HFA384x_ALLOCFID_OFF (0x22)
+#define HFA384x_TXCOMPLFID_OFF (0x24)
+#define HFA384x_SELECT0_OFF (0x18)
+#define HFA384x_OFFSET0_OFF (0x1C)
+#define HFA384x_DATA0_OFF (0x36)
+#define HFA384x_SELECT1_OFF (0x1A)
+#define HFA384x_OFFSET1_OFF (0x1E)
+#define HFA384x_DATA1_OFF (0x38)
+#define HFA384x_EVSTAT_OFF (0x30)
+#define HFA384x_INTEN_OFF (0x32)
+#define HFA384x_EVACK_OFF (0x34)
+#define HFA384x_CONTROL_OFF (0x14)
+#define HFA384x_SWSUPPORT0_OFF (0x28)
+#define HFA384x_SWSUPPORT1_OFF (0x2A)
+#define HFA384x_SWSUPPORT2_OFF (0x2C)
+#define HFA384x_AUXPAGE_OFF (0x3A)
+#define HFA384x_AUXOFFSET_OFF (0x3C)
+#define HFA384x_AUXDATA_OFF (0x3E)
+
+#elif (WLAN_HOSTIF == WLAN_PCI || WLAN_HOSTIF == WLAN_USB)
+
+#define HFA384x_CMD_OFF (0x00)
+#define HFA384x_PARAM0_OFF (0x04)
+#define HFA384x_PARAM1_OFF (0x08)
+#define HFA384x_PARAM2_OFF (0x0c)
+#define HFA384x_STATUS_OFF (0x10)
+#define HFA384x_RESP0_OFF (0x14)
+#define HFA384x_RESP1_OFF (0x18)
+#define HFA384x_RESP2_OFF (0x1c)
+#define HFA384x_INFOFID_OFF (0x20)
+#define HFA384x_RXFID_OFF (0x40)
+#define HFA384x_ALLOCFID_OFF (0x44)
+#define HFA384x_TXCOMPLFID_OFF (0x48)
+#define HFA384x_SELECT0_OFF (0x30)
+#define HFA384x_OFFSET0_OFF (0x38)
+#define HFA384x_DATA0_OFF (0x6c)
+#define HFA384x_SELECT1_OFF (0x34)
+#define HFA384x_OFFSET1_OFF (0x3c)
+#define HFA384x_DATA1_OFF (0x70)
+#define HFA384x_EVSTAT_OFF (0x60)
+#define HFA384x_INTEN_OFF (0x64)
+#define HFA384x_EVACK_OFF (0x68)
+#define HFA384x_CONTROL_OFF (0x28)
+#define HFA384x_SWSUPPORT0_OFF (0x50)
+#define HFA384x_SWSUPPORT1_OFF (0x54)
+#define HFA384x_SWSUPPORT2_OFF (0x58)
+#define HFA384x_AUXPAGE_OFF (0x74)
+#define HFA384x_AUXOFFSET_OFF (0x78)
+#define HFA384x_AUXDATA_OFF (0x7c)
+#define HFA384x_PCICOR_OFF (0x4c)
+#define HFA384x_PCIHCR_OFF (0x5c)
+#define HFA384x_PCI_M0_ADDRH_OFF (0x80)
+#define HFA384x_PCI_M0_ADDRL_OFF (0x84)
+#define HFA384x_PCI_M0_LEN_OFF (0x88)
+#define HFA384x_PCI_M0_CTL_OFF (0x8c)
+#define HFA384x_PCI_STATUS_OFF (0x98)
+#define HFA384x_PCI_M1_ADDRH_OFF (0xa0)
+#define HFA384x_PCI_M1_ADDRL_OFF (0xa4)
+#define HFA384x_PCI_M1_LEN_OFF (0xa8)
+#define HFA384x_PCI_M1_CTL_OFF (0xac)
+
+#endif
+
+/*--- Register Field Masks --------------------------*/
+#define HFA384x_CMD_BUSY ((UINT16)BIT15)
+#define HFA384x_CMD_AINFO ((UINT16)(BIT14 | BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8))
+#define HFA384x_CMD_MACPORT ((UINT16)(BIT10 | BIT9 | BIT8))
+#define HFA384x_CMD_RECL ((UINT16)BIT8)
+#define HFA384x_CMD_WRITE ((UINT16)BIT8)
+#define HFA384x_CMD_PROGMODE ((UINT16)(BIT9 | BIT8))
+#define HFA384x_CMD_CMDCODE ((UINT16)(BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0))
+
+#define HFA384x_STATUS_RESULT ((UINT16)(BIT14 | BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8))
+#define HFA384x_STATUS_CMDCODE ((UINT16)(BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0))
+
+#define HFA384x_OFFSET_BUSY ((UINT16)BIT15)
+#define HFA384x_OFFSET_ERR ((UINT16)BIT14)
+#define HFA384x_OFFSET_DATAOFF ((UINT16)(BIT11 | BIT10 | BIT9 | BIT8 | BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | BIT2 | BIT1))
+
+#define HFA384x_EVSTAT_TICK ((UINT16)BIT15)
+#define HFA384x_EVSTAT_WTERR ((UINT16)BIT14)
+#define HFA384x_EVSTAT_INFDROP ((UINT16)BIT13)
+#define HFA384x_EVSTAT_INFO ((UINT16)BIT7)
+#define HFA384x_EVSTAT_DTIM ((UINT16)BIT5)
+#define HFA384x_EVSTAT_CMD ((UINT16)BIT4)
+#define HFA384x_EVSTAT_ALLOC ((UINT16)BIT3)
+#define HFA384x_EVSTAT_TXEXC ((UINT16)BIT2)
+#define HFA384x_EVSTAT_TX ((UINT16)BIT1)
+#define HFA384x_EVSTAT_RX ((UINT16)BIT0)
+
+#define HFA384x_INTEN_TICK ((UINT16)BIT15)
+#define HFA384x_INTEN_WTERR ((UINT16)BIT14)
+#define HFA384x_INTEN_INFDROP ((UINT16)BIT13)
+#define HFA384x_INTEN_INFO ((UINT16)BIT7)
+#define HFA384x_INTEN_DTIM ((UINT16)BIT5)
+#define HFA384x_INTEN_CMD ((UINT16)BIT4)
+#define HFA384x_INTEN_ALLOC ((UINT16)BIT3)
+#define HFA384x_INTEN_TXEXC ((UINT16)BIT2)
+#define HFA384x_INTEN_TX ((UINT16)BIT1)
+#define HFA384x_INTEN_RX ((UINT16)BIT0)
+
+#define HFA384x_EVACK_TICK ((UINT16)BIT15)
+#define HFA384x_EVACK_WTERR ((UINT16)BIT14)
+#define HFA384x_EVACK_INFDROP ((UINT16)BIT13)
+#define HFA384x_EVACK_INFO ((UINT16)BIT7)
+#define HFA384x_EVACK_DTIM ((UINT16)BIT5)
+#define HFA384x_EVACK_CMD ((UINT16)BIT4)
+#define HFA384x_EVACK_ALLOC ((UINT16)BIT3)
+#define HFA384x_EVACK_TXEXC ((UINT16)BIT2)
+#define HFA384x_EVACK_TX ((UINT16)BIT1)
+#define HFA384x_EVACK_RX ((UINT16)BIT0)
+
+#define HFA384x_CONTROL_AUXEN ((UINT16)(BIT15 | BIT14))
+
+
+/*--- Command Code Constants --------------------------*/
+/*--- Controller Commands --------------------------*/
+#define HFA384x_CMDCODE_INIT ((UINT16)0x00)
+#define HFA384x_CMDCODE_ENABLE ((UINT16)0x01)
+#define HFA384x_CMDCODE_DISABLE ((UINT16)0x02)
+#define HFA384x_CMDCODE_DIAG ((UINT16)0x03)
+
+/*--- Buffer Mgmt Commands --------------------------*/
+#define HFA384x_CMDCODE_ALLOC ((UINT16)0x0A)
+#define HFA384x_CMDCODE_TX ((UINT16)0x0B)
+#define HFA384x_CMDCODE_CLRPRST ((UINT16)0x12)
+
+/*--- Regulate Commands --------------------------*/
+#define HFA384x_CMDCODE_NOTIFY ((UINT16)0x10)
+#define HFA384x_CMDCODE_INQ ((UINT16)0x11)
+
+/*--- Configure Commands --------------------------*/
+#define HFA384x_CMDCODE_ACCESS ((UINT16)0x21)
+#define HFA384x_CMDCODE_DOWNLD ((UINT16)0x22)
+
+/*--- Debugging Commands -----------------------------*/
+#define HFA384x_CMDCODE_MONITOR ((UINT16)(0x38))
+#define HFA384x_MONITOR_ENABLE ((UINT16)(0x0b))
+#define HFA384x_MONITOR_DISABLE ((UINT16)(0x0f))
+
+/*--- Result Codes --------------------------*/
+#define HFA384x_SUCCESS ((UINT16)(0x00))
+#define HFA384x_CARD_FAIL ((UINT16)(0x01))
+#define HFA384x_NO_BUFF ((UINT16)(0x05))
+#define HFA384x_CMD_ERR ((UINT16)(0x7F))
+
+/*--- Programming Modes --------------------------
+ MODE 0: Disable programming
+ MODE 1: Enable volatile memory programming
+ MODE 2: Enable non-volatile memory programming
+ MODE 3: Program non-volatile memory section
+--------------------------------------------------*/
+#define HFA384x_PROGMODE_DISABLE ((UINT16)0x00)
+#define HFA384x_PROGMODE_RAM ((UINT16)0x01)
+#define HFA384x_PROGMODE_NV ((UINT16)0x02)
+#define HFA384x_PROGMODE_NVWRITE ((UINT16)0x03)
+
+/*--- AUX register enable --------------------------*/
+#define HFA384x_AUXPW0 ((UINT16)0xfe01)
+#define HFA384x_AUXPW1 ((UINT16)0xdc23)
+#define HFA384x_AUXPW2 ((UINT16)0xba45)
+
+#define HFA384x_CONTROL_AUX_ISDISABLED ((UINT16)0x0000)
+#define HFA384x_CONTROL_AUX_ISENABLED ((UINT16)0xc000)
+#define HFA384x_CONTROL_AUX_DOENABLE ((UINT16)0x8000)
+#define HFA384x_CONTROL_AUX_DODISABLE ((UINT16)0x4000)
+
+/*--- Record ID Constants --------------------------*/
+/*--------------------------------------------------------------------
+Configuration RIDs: Network Parameters, Static Configuration Entities
+--------------------------------------------------------------------*/
+#define HFA384x_RID_CNFPORTTYPE ((UINT16)0xFC00)
+#define HFA384x_RID_CNFOWNMACADDR ((UINT16)0xFC01)
+#define HFA384x_RID_CNFDESIREDSSID ((UINT16)0xFC02)
+#define HFA384x_RID_CNFOWNCHANNEL ((UINT16)0xFC03)
+#define HFA384x_RID_CNFOWNSSID ((UINT16)0xFC04)
+#define HFA384x_RID_CNFOWNATIMWIN ((UINT16)0xFC05)
+#define HFA384x_RID_CNFSYSSCALE ((UINT16)0xFC06)
+#define HFA384x_RID_CNFMAXDATALEN ((UINT16)0xFC07)
+#define HFA384x_RID_CNFWDSADDR ((UINT16)0xFC08)
+#define HFA384x_RID_CNFPMENABLED ((UINT16)0xFC09)
+#define HFA384x_RID_CNFPMEPS ((UINT16)0xFC0A)
+#define HFA384x_RID_CNFMULTICASTRX ((UINT16)0xFC0B)
+#define HFA384x_RID_CNFMAXSLEEPDUR ((UINT16)0xFC0C)
+#define HFA384x_RID_CNFPMHOLDDUR ((UINT16)0xFC0D)
+#define HFA384x_RID_CNFOWNNAME ((UINT16)0xFC0E)
+#define HFA384x_RID_CNFOWNDTIMPER ((UINT16)0xFC10)
+#define HFA384x_RID_CNFWDSADDR1 ((UINT16)0xFC11)
+#define HFA384x_RID_CNFWDSADDR2 ((UINT16)0xFC12)
+#define HFA384x_RID_CNFWDSADDR3 ((UINT16)0xFC13)
+#define HFA384x_RID_CNFWDSADDR4 ((UINT16)0xFC14)
+#define HFA384x_RID_CNFWDSADDR5 ((UINT16)0xFC15)
+#define HFA384x_RID_CNFWDSADDR6 ((UINT16)0xFC16)
+#define HFA384x_RID_CNFMCASTPMBUFF ((UINT16)0xFC17)
+
+/*--------------------------------------------------------------------
+Configuration RID lengths: Network Params, Static Config Entities
+ This is the length of JUST the DATA part of the RID (does not
+ include the len or code fields)
+--------------------------------------------------------------------*/
+/* TODO: fill in the rest of these */
+#define HFA384x_RID_CNFPORTTYPE_LEN ((UINT16)2)
+#define HFA384x_RID_CNFOWNMACADDR_LEN ((UINT16)6)
+#define HFA384x_RID_CNFDESIREDSSID_LEN ((UINT16)34)
+#define HFA384x_RID_CNFOWNCHANNEL_LEN ((UINT16)2)
+#define HFA384x_RID_CNFOWNSSID_LEN ((UINT16)34)
+#define HFA384x_RID_CNFOWNATIMWIN_LEN ((UINT16)2)
+#define HFA384x_RID_CNFSYSSCALE_LEN ((UINT16)0)
+#define HFA384x_RID_CNFMAXDATALEN_LEN ((UINT16)0)
+#define HFA384x_RID_CNFWDSADDR_LEN ((UINT16)6)
+#define HFA384x_RID_CNFPMENABLED_LEN ((UINT16)0)
+#define HFA384x_RID_CNFPMEPS_LEN ((UINT16)0)
+#define HFA384x_RID_CNFMULTICASTRX_LEN ((UINT16)0)
+#define HFA384x_RID_CNFMAXSLEEPDUR_LEN ((UINT16)0)
+#define HFA384x_RID_CNFPMHOLDDUR_LEN ((UINT16)0)
+#define HFA384x_RID_CNFOWNNAME_LEN ((UINT16)34)
+#define HFA384x_RID_CNFOWNDTIMPER_LEN ((UINT16)0)
+#define HFA384x_RID_CNFWDSADDR1_LEN ((UINT16)6)
+#define HFA384x_RID_CNFWDSADDR2_LEN ((UINT16)6)
+#define HFA384x_RID_CNFWDSADDR3_LEN ((UINT16)6)
+#define HFA384x_RID_CNFWDSADDR4_LEN ((UINT16)6)
+#define HFA384x_RID_CNFWDSADDR5_LEN ((UINT16)6)
+#define HFA384x_RID_CNFWDSADDR6_LEN ((UINT16)6)
+#define HFA384x_RID_CNFMCASTPMBUFF_LEN ((UINT16)0)
+#define HFA384x_RID_CNFAUTHENTICATION_LEN ((UINT16)sizeof(UINT16))
+#define HFA384x_RID_CNFMAXSLEEPDUR_LEN ((UINT16)0)
+
+/*--------------------------------------------------------------------
+Configuration RIDs: Network Parameters, Dynamic Configuration Entities
+--------------------------------------------------------------------*/
+#define HFA384x_RID_GROUPADDR ((UINT16)0xFC80)
+#define HFA384x_RID_CREATEIBSS ((UINT16)0xFC81)
+#define HFA384x_RID_FRAGTHRESH ((UINT16)0xFC82)
+#define HFA384x_RID_RTSTHRESH ((UINT16)0xFC83)
+#define HFA384x_RID_TXRATECNTL ((UINT16)0xFC84)
+#define HFA384x_RID_PROMISCMODE ((UINT16)0xFC85)
+#define HFA384x_RID_FRAGTHRESH0 ((UINT16)0xFC90)
+#define HFA384x_RID_FRAGTHRESH1 ((UINT16)0xFC91)
+#define HFA384x_RID_FRAGTHRESH2 ((UINT16)0xFC92)
+#define HFA384x_RID_FRAGTHRESH3 ((UINT16)0xFC93)
+#define HFA384x_RID_FRAGTHRESH4 ((UINT16)0xFC94)
+#define HFA384x_RID_FRAGTHRESH5 ((UINT16)0xFC95)
+#define HFA384x_RID_FRAGTHRESH6 ((UINT16)0xFC96)
+#define HFA384x_RID_RTSTHRESH0 ((UINT16)0xFC97)
+#define HFA384x_RID_RTSTHRESH1 ((UINT16)0xFC98)
+#define HFA384x_RID_RTSTHRESH2 ((UINT16)0xFC99)
+#define HFA384x_RID_RTSTHRESH3 ((UINT16)0xFC9A)
+#define HFA384x_RID_RTSTHRESH4 ((UINT16)0xFC9B)
+#define HFA384x_RID_RTSTHRESH5 ((UINT16)0xFC9C)
+#define HFA384x_RID_RTSTHRESH6 ((UINT16)0xFC9D)
+#define HFA384x_RID_TXRATECNTL0 ((UINT16)0xFC9E)
+#define HFA384x_RID_TXRATECNTL1 ((UINT16)0xFC9F)
+#define HFA384x_RID_TXRATECNTL2 ((UINT16)0xFCA0)
+#define HFA384x_RID_TXRATECNTL3 ((UINT16)0xFCA1)
+#define HFA384x_RID_TXRATECNTL4 ((UINT16)0xFCA2)
+#define HFA384x_RID_TXRATECNTL5 ((UINT16)0xFCA3)
+#define HFA384x_RID_TXRATECNTL6 ((UINT16)0xFCA4)
+
+/*--------------------------------------------------------------------
+Configuration RID Lengths: Network Param, Dynamic Config Entities
+ This is the length of JUST the DATA part of the RID (does not
+ include the len or code fields)
+--------------------------------------------------------------------*/
+/* TODO: fill in the rest of these */
+#define HFA384x_RID_GROUPADDR_LEN ((UINT16)16 * WLAN_ADDR_LEN)
+#define HFA384x_RID_CREATEIBSS_LEN ((UINT16)0)
+#define HFA384x_RID_FRAGTHRESH_LEN ((UINT16)0)
+#define HFA384x_RID_RTSTHRESH_LEN ((UINT16)0)
+#define HFA384x_RID_TXRATECNTL_LEN ((UINT16)4)
+#define HFA384x_RID_PROMISCMODE_LEN ((UINT16)2)
+#define HFA384x_RID_FRAGTHRESH0_LEN ((UINT16)0)
+#define HFA384x_RID_FRAGTHRESH1_LEN ((UINT16)0)
+#define HFA384x_RID_FRAGTHRESH2_LEN ((UINT16)0)
+#define HFA384x_RID_FRAGTHRESH3_LEN ((UINT16)0)
+#define HFA384x_RID_FRAGTHRESH4_LEN ((UINT16)0)
+#define HFA384x_RID_FRAGTHRESH5_LEN ((UINT16)0)
+#define HFA384x_RID_FRAGTHRESH6_LEN ((UINT16)0)
+#define HFA384x_RID_RTSTHRESH0_LEN ((UINT16)0)
+#define HFA384x_RID_RTSTHRESH1_LEN ((UINT16)0)
+#define HFA384x_RID_RTSTHRESH2_LEN ((UINT16)0)
+#define HFA384x_RID_RTSTHRESH3_LEN ((UINT16)0)
+#define HFA384x_RID_RTSTHRESH4_LEN ((UINT16)0)
+#define HFA384x_RID_RTSTHRESH5_LEN ((UINT16)0)
+#define HFA384x_RID_RTSTHRESH6_LEN ((UINT16)0)
+#define HFA384x_RID_TXRATECNTL0_LEN ((UINT16)0)
+#define HFA384x_RID_TXRATECNTL1_LEN ((UINT16)0)
+#define HFA384x_RID_TXRATECNTL2_LEN ((UINT16)0)
+#define HFA384x_RID_TXRATECNTL3_LEN ((UINT16)0)
+#define HFA384x_RID_TXRATECNTL4_LEN ((UINT16)0)
+#define HFA384x_RID_TXRATECNTL5_LEN ((UINT16)0)
+#define HFA384x_RID_TXRATECNTL6_LEN ((UINT16)0)
+
+/*--------------------------------------------------------------------
+Configuration RIDs: Behavior Parameters
+--------------------------------------------------------------------*/
+#define HFA384x_RID_ITICKTIME ((UINT16)0xFCE0)
+
+/*--------------------------------------------------------------------
+Configuration RID Lengths: Behavior Parameters
+ This is the length of JUST the DATA part of the RID (does not
+ include the len or code fields)
+--------------------------------------------------------------------*/
+#define HFA384x_RID_ITICKTIME_LEN ((UINT16)2)
+
+/*----------------------------------------------------------------------
+Information RIDs: NIC Information
+--------------------------------------------------------------------*/
+#define HFA384x_RID_MAXLOADTIME ((UINT16)0xFD00)
+#define HFA384x_RID_DOWNLOADBUFFER ((UINT16)0xFD01)
+#define HFA384x_RID_PRIIDENTITY ((UINT16)0xFD02)
+#define HFA384x_RID_PRISUPRANGE ((UINT16)0xFD03)
+#define HFA384x_RID_PRI_CFIACTRANGES ((UINT16)0xFD04)
+#define HFA384x_RID_NICSERIALNUMBER ((UINT16)0xFD0A)
+#define HFA384x_RID_NICIDENTITY ((UINT16)0xFD0B)
+#define HFA384x_RID_MFISUPRANGE ((UINT16)0xFD0C)
+#define HFA384x_RID_CFISUPRANGE ((UINT16)0xFD0D)
+#define HFA384x_RID_CHANNELLIST ((UINT16)0xFD10)
+#define HFA384x_RID_REGULATORYDOMAINS ((UINT16)0xFD11)
+#define HFA384x_RID_TEMPTYPE ((UINT16)0xFD12)
+#define HFA384x_RID_CIS ((UINT16)0xFD13)
+#define HFA384x_RID_STAIDENTITY ((UINT16)0xFD20)
+#define HFA384x_RID_STASUPRANGE ((UINT16)0xFD21)
+#define HFA384x_RID_STA_MFIACTRANGES ((UINT16)0xFD22)
+#define HFA384x_RID_STA_CFIACTRANGES ((UINT16)0xFD23)
+#define HFA384x_RID_BUILDSEQ ((UINT16)0xFFFE)
+#define HFA384x_RID_FWID ((UINT16)0xFFFF)
+
+/*----------------------------------------------------------------------
+Information RID Lengths: NIC Information
+ This is the length of JUST the DATA part of the RID (does not
+ include the len or code fields)
+--------------------------------------------------------------------*/
+#define HFA384x_RID_MAXLOADTIME_LEN ((UINT16)0)
+#define HFA384x_RID_DOWNLOADBUFFER_LEN ((UINT16)sizeof(hfa384x_downloadbuffer_t))
+#define HFA384x_RID_PRIIDENTITY_LEN ((UINT16)8)
+#define HFA384x_RID_PRISUPRANGE_LEN ((UINT16)10)
+#define HFA384x_RID_CFIACTRANGES_LEN ((UINT16)10)
+#define HFA384x_RID_NICSERIALNUMBER_LEN ((UINT16)12)
+#define HFA384x_RID_NICIDENTITY_LEN ((UINT16)8)
+#define HFA384x_RID_MFISUPRANGE_LEN ((UINT16)10)
+#define HFA384x_RID_CFISUPRANGE_LEN ((UINT16)10)
+#define HFA384x_RID_CHANNELLIST_LEN ((UINT16)0)
+#define HFA384x_RID_REGULATORYDOMAINS_LEN ((UINT16)12)
+#define HFA384x_RID_TEMPTYPE_LEN ((UINT16)0)
+#define HFA384x_RID_CIS_LEN ((UINT16)480)
+#define HFA384x_RID_STAIDENTITY_LEN ((UINT16)8)
+#define HFA384x_RID_STASUPRANGE_LEN ((UINT16)10)
+#define HFA384x_RID_MFIACTRANGES_LEN ((UINT16)10)
+#define HFA384x_RID_CFIACTRANGES2_LEN ((UINT16)10)
+#define HFA384x_RID_BUILDSEQ_LEN ((UINT16)sizeof(hfa384x_BuildSeq_t))
+#define HFA384x_RID_FWID_LEN ((UINT16)sizeof(hfa384x_FWID_t))
+
+/*--------------------------------------------------------------------
+Information RIDs: MAC Information
+--------------------------------------------------------------------*/
+#define HFA384x_RID_PORTSTATUS ((UINT16)0xFD40)
+#define HFA384x_RID_CURRENTSSID ((UINT16)0xFD41)
+#define HFA384x_RID_CURRENTBSSID ((UINT16)0xFD42)
+#define HFA384x_RID_COMMSQUALITY ((UINT16)0xFD43)
+#define HFA384x_RID_CURRENTTXRATE ((UINT16)0xFD44)
+#define HFA384x_RID_CURRENTBCNINT ((UINT16)0xFD45)
+#define HFA384x_RID_CURRENTSCALETHRESH ((UINT16)0xFD46)
+#define HFA384x_RID_PROTOCOLRSPTIME ((UINT16)0xFD47)
+#define HFA384x_RID_SHORTRETRYLIMIT ((UINT16)0xFD48)
+#define HFA384x_RID_LONGRETRYLIMIT ((UINT16)0xFD49)
+#define HFA384x_RID_MAXTXLIFETIME ((UINT16)0xFD4A)
+#define HFA384x_RID_MAXRXLIFETIME ((UINT16)0xFD4B)
+#define HFA384x_RID_CFPOLLABLE ((UINT16)0xFD4C)
+#define HFA384x_RID_AUTHALGORITHMS ((UINT16)0xFD4D)
+#define HFA384x_RID_PRIVACYOPTIMP ((UINT16)0xFD4F)
+#define HFA384x_RID_DBMCOMMSQUALITY ((UINT16)0xFD51)
+#define HFA384x_RID_CURRENTTXRATE1 ((UINT16)0xFD80)
+#define HFA384x_RID_CURRENTTXRATE2 ((UINT16)0xFD81)
+#define HFA384x_RID_CURRENTTXRATE3 ((UINT16)0xFD82)
+#define HFA384x_RID_CURRENTTXRATE4 ((UINT16)0xFD83)
+#define HFA384x_RID_CURRENTTXRATE5 ((UINT16)0xFD84)
+#define HFA384x_RID_CURRENTTXRATE6 ((UINT16)0xFD85)
+#define HFA384x_RID_OWNMACADDRESS ((UINT16)0xFD86)
+// #define HFA384x_RID_PCFINFO ((UINT16)0xFD87)
+#define HFA384x_RID_SCANRESULTS ((UINT16)0xFD88) // NEW
+#define HFA384x_RID_HOSTSCANRESULTS ((UINT16)0xFD89) // NEW
+#define HFA384x_RID_AUTHENTICATIONUSED ((UINT16)0xFD8A) // NEW
+
+/*--------------------------------------------------------------------
+Information RID Lengths: MAC Information
+ This is the length of JUST the DATA part of the RID (does not
+ include the len or code fields)
+--------------------------------------------------------------------*/
+#define HFA384x_RID_PORTSTATUS_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTSSID_LEN ((UINT16)34)
+#define HFA384x_RID_CURRENTBSSID_LEN ((UINT16)WLAN_BSSID_LEN)
+#define HFA384x_RID_COMMSQUALITY_LEN ((UINT16)sizeof(hfa384x_commsquality_t))
+#define HFA384x_RID_DBMCOMMSQUALITY_LEN ((UINT16)sizeof(hfa384x_dbmcommsquality_t))
+#define HFA384x_RID_CURRENTTXRATE_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTBCNINT_LEN ((UINT16)0)
+#define HFA384x_RID_STACURSCALETHRESH_LEN ((UINT16)12)
+#define HFA384x_RID_APCURSCALETHRESH_LEN ((UINT16)6)
+#define HFA384x_RID_PROTOCOLRSPTIME_LEN ((UINT16)0)
+#define HFA384x_RID_SHORTRETRYLIMIT_LEN ((UINT16)0)
+#define HFA384x_RID_LONGRETRYLIMIT_LEN ((UINT16)0)
+#define HFA384x_RID_MAXTXLIFETIME_LEN ((UINT16)0)
+#define HFA384x_RID_MAXRXLIFETIME_LEN ((UINT16)0)
+#define HFA384x_RID_CFPOLLABLE_LEN ((UINT16)0)
+#define HFA384x_RID_AUTHALGORITHMS_LEN ((UINT16)4)
+#define HFA384x_RID_PRIVACYOPTIMP_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTTXRATE1_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTTXRATE2_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTTXRATE3_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTTXRATE4_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTTXRATE5_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTTXRATE6_LEN ((UINT16)0)
+#define HFA384x_RID_OWNMACADDRESS_LEN ((UINT16)6)
+#define HFA384x_RID_PCFINFO_LEN ((UINT16)6)
+#define HFA384x_RID_CNFAPPCFINFO_LEN ((UINT16)sizeof(hfa384x_PCFInfo_data_t))
+#define HFA384x_RID_SCANREQUEST_LEN ((UINT16)sizeof(hfa384x_ScanRequest_data_t))
+#define HFA384x_RID_JOINREQUEST_LEN ((UINT16)sizeof(hfa384x_JoinRequest_data_t))
+#define HFA384x_RID_AUTHENTICATESTA_LEN ((UINT16)sizeof(hfa384x_authenticateStation_data_t))
+#define HFA384x_RID_CHANNELINFOREQUEST_LEN ((UINT16)sizeof(hfa384x_ChannelInfoRequest_data_t))
+/*--------------------------------------------------------------------
+Information RIDs: Modem Information
+--------------------------------------------------------------------*/
+#define HFA384x_RID_PHYTYPE ((UINT16)0xFDC0)
+#define HFA384x_RID_CURRENTCHANNEL ((UINT16)0xFDC1)
+#define HFA384x_RID_CURRENTPOWERSTATE ((UINT16)0xFDC2)
+#define HFA384x_RID_CCAMODE ((UINT16)0xFDC3)
+#define HFA384x_RID_SUPPORTEDDATARATES ((UINT16)0xFDC6)
+
+/*--------------------------------------------------------------------
+Information RID Lengths: Modem Information
+ This is the length of JUST the DATA part of the RID (does not
+ include the len or code fields)
+--------------------------------------------------------------------*/
+#define HFA384x_RID_PHYTYPE_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTCHANNEL_LEN ((UINT16)0)
+#define HFA384x_RID_CURRENTPOWERSTATE_LEN ((UINT16)0)
+#define HFA384x_RID_CCAMODE_LEN ((UINT16)0)
+#define HFA384x_RID_SUPPORTEDDATARATES_LEN ((UINT16)10)
+
+/*--------------------------------------------------------------------
+API ENHANCEMENTS (NOT ALREADY IMPLEMENTED)
+--------------------------------------------------------------------*/
+#define HFA384x_RID_CNFWEPDEFAULTKEYID ((UINT16)0xFC23)
+#define HFA384x_RID_CNFWEPDEFAULTKEY0 ((UINT16)0xFC24)
+#define HFA384x_RID_CNFWEPDEFAULTKEY1 ((UINT16)0xFC25)
+#define HFA384x_RID_CNFWEPDEFAULTKEY2 ((UINT16)0xFC26)
+#define HFA384x_RID_CNFWEPDEFAULTKEY3 ((UINT16)0xFC27)
+#define HFA384x_RID_CNFWEPFLAGS ((UINT16)0xFC28)
+#define HFA384x_RID_CNFWEPKEYMAPTABLE ((UINT16)0xFC29)
+#define HFA384x_RID_CNFAUTHENTICATION ((UINT16)0xFC2A)
+#define HFA384x_RID_CNFMAXASSOCSTATIONS ((UINT16)0xFC2B)
+#define HFA384x_RID_CNFTXCONTROL ((UINT16)0xFC2C)
+#define HFA384x_RID_CNFROAMINGMODE ((UINT16)0xFC2D)
+#define HFA384x_RID_CNFHOSTAUTH ((UINT16)0xFC2E)
+#define HFA384x_RID_CNFRCVCRCERROR ((UINT16)0xFC30)
+// #define HFA384x_RID_CNFMMLIFE ((UINT16)0xFC31)
+#define HFA384x_RID_CNFALTRETRYCNT ((UINT16)0xFC32)
+#define HFA384x_RID_CNFAPBCNINT ((UINT16)0xFC33)
+#define HFA384x_RID_CNFAPPCFINFO ((UINT16)0xFC34)
+#define HFA384x_RID_CNFSTAPCFINFO ((UINT16)0xFC35)
+#define HFA384x_RID_CNFPRIORITYQUSAGE ((UINT16)0xFC37)
+#define HFA384x_RID_CNFTIMCTRL ((UINT16)0xFC40)
+#define HFA384x_RID_CNFTHIRTY2TALLY ((UINT16)0xFC42)
+#define HFA384x_RID_CNFENHSECURITY ((UINT16)0xFC43)
+#define HFA384x_RID_CNFDBMADJUST ((UINT16)0xFC46) // NEW
+#define HFA384x_RID_CNFSHORTPREAMBLE ((UINT16)0xFCB0)
+#define HFA384x_RID_CNFEXCLONGPREAMBLE ((UINT16)0xFCB1)
+#define HFA384x_RID_CNFAUTHRSPTIMEOUT ((UINT16)0xFCB2)
+#define HFA384x_RID_CNFBASICRATES ((UINT16)0xFCB3)
+#define HFA384x_RID_CNFSUPPRATES ((UINT16)0xFCB4)
+#define HFA384x_RID_CNFFALLBACKCTRL ((UINT16)0xFCB5) // NEW
+#define HFA384x_RID_WEPKEYDISABLE ((UINT16)0xFCB6) // NEW
+#define HFA384x_RID_WEPKEYMAPINDEX ((UINT16)0xFCB7) // NEW AP
+#define HFA384x_RID_BROADCASTKEYID ((UINT16)0xFCB8) // NEW AP
+#define HFA384x_RID_ENTSECFLAGEYID ((UINT16)0xFCB9) // NEW AP
+#define HFA384x_RID_CNFPASSIVESCANCTRL ((UINT16)0xFCB9) // NEW STA
+#define HFA384x_RID_SCANREQUEST ((UINT16)0xFCE1)
+#define HFA384x_RID_JOINREQUEST ((UINT16)0xFCE2)
+#define HFA384x_RID_AUTHENTICATESTA ((UINT16)0xFCE3)
+#define HFA384x_RID_CHANNELINFOREQUEST ((UINT16)0xFCE4)
+#define HFA384x_RID_HOSTSCAN ((UINT16)0xFCE5) // NEW STA
+
+#define HFA384x_RID_CNFWEPDEFAULTKEY_LEN ((UINT16)6)
+#define HFA384x_RID_CNFWEP128DEFAULTKEY_LEN ((UINT16)14)
+#define HFA384x_RID_CNFPRIOQUSAGE_LEN ((UINT16)4)
+/*--------------------------------------------------------------------
+PD Record codes
+--------------------------------------------------------------------*/
+#define HFA384x_PDR_PCB_PARTNUM ((UINT16)0x0001)
+#define HFA384x_PDR_PDAVER ((UINT16)0x0002)
+#define HFA384x_PDR_NIC_SERIAL ((UINT16)0x0003)
+#define HFA384x_PDR_MKK_MEASUREMENTS ((UINT16)0x0004)
+#define HFA384x_PDR_NIC_RAMSIZE ((UINT16)0x0005)
+#define HFA384x_PDR_MFISUPRANGE ((UINT16)0x0006)
+#define HFA384x_PDR_CFISUPRANGE ((UINT16)0x0007)
+#define HFA384x_PDR_NICID ((UINT16)0x0008)
+#define HFA384x_PDR_REFDAC_MEASUREMENTS ((UINT16)0x0010)
+#define HFA384x_PDR_VGDAC_MEASUREMENTS ((UINT16)0x0020)
+#define HFA384x_PDR_LEVEL_COMP_MEASUREMENTS ((UINT16)0x0030)
+#define HFA384x_PDR_MODEM_TRIMDAC_MEASUREMENTS ((UINT16)0x0040)
+#define HFA384x_PDR_COREGA_HACK ((UINT16)0x00ff)
+#define HFA384x_PDR_MAC_ADDRESS ((UINT16)0x0101)
+#define HFA384x_PDR_MKK_CALLNAME ((UINT16)0x0102)
+#define HFA384x_PDR_REGDOMAIN ((UINT16)0x0103)
+#define HFA384x_PDR_ALLOWED_CHANNEL ((UINT16)0x0104)
+#define HFA384x_PDR_DEFAULT_CHANNEL ((UINT16)0x0105)
+#define HFA384x_PDR_PRIVACY_OPTION ((UINT16)0x0106)
+#define HFA384x_PDR_TEMPTYPE ((UINT16)0x0107)
+#define HFA384x_PDR_REFDAC_SETUP ((UINT16)0x0110)
+#define HFA384x_PDR_VGDAC_SETUP ((UINT16)0x0120)
+#define HFA384x_PDR_LEVEL_COMP_SETUP ((UINT16)0x0130)
+#define HFA384x_PDR_TRIMDAC_SETUP ((UINT16)0x0140)
+#define HFA384x_PDR_IFR_SETTING ((UINT16)0x0200)
+#define HFA384x_PDR_RFR_SETTING ((UINT16)0x0201)
+#define HFA384x_PDR_HFA3861_BASELINE ((UINT16)0x0202)
+#define HFA384x_PDR_HFA3861_SHADOW ((UINT16)0x0203)
+#define HFA384x_PDR_HFA3861_IFRF ((UINT16)0x0204)
+#define HFA384x_PDR_HFA3861_CHCALSP ((UINT16)0x0300)
+#define HFA384x_PDR_HFA3861_CHCALI ((UINT16)0x0301)
+#define HFA384x_PDR_3842_NIC_CONFIG ((UINT16)0x0400)
+#define HFA384x_PDR_USB_ID ((UINT16)0x0401)
+#define HFA384x_PDR_PCI_ID ((UINT16)0x0402)
+#define HFA384x_PDR_PCI_IFCONF ((UINT16)0x0403)
+#define HFA384x_PDR_PCI_PMCONF ((UINT16)0x0404)
+#define HFA384x_PDR_RFENRGY ((UINT16)0x0406)
+#define HFA384x_PDR_UNKNOWN407 ((UINT16)0x0407)
+#define HFA384x_PDR_UNKNOWN408 ((UINT16)0x0408)
+#define HFA384x_PDR_UNKNOWN409 ((UINT16)0x0409)
+#define HFA384x_PDR_HFA3861_MANF_TESTSP ((UINT16)0x0900)
+#define HFA384x_PDR_HFA3861_MANF_TESTI ((UINT16)0x0901)
+#define HFA384x_PDR_END_OF_PDA ((UINT16)0x0000)
+
+
+/*=============================================================*/
+/*------ Macros -----------------------------------------------*/
+
+/*--- Register ID macros ------------------------*/
+
+#define HFA384x_CMD HFA384x_CMD_OFF
+#define HFA384x_PARAM0 HFA384x_PARAM0_OFF
+#define HFA384x_PARAM1 HFA384x_PARAM1_OFF
+#define HFA384x_PARAM2 HFA384x_PARAM2_OFF
+#define HFA384x_STATUS HFA384x_STATUS_OFF
+#define HFA384x_RESP0 HFA384x_RESP0_OFF
+#define HFA384x_RESP1 HFA384x_RESP1_OFF
+#define HFA384x_RESP2 HFA384x_RESP2_OFF
+#define HFA384x_INFOFID HFA384x_INFOFID_OFF
+#define HFA384x_RXFID HFA384x_RXFID_OFF
+#define HFA384x_ALLOCFID HFA384x_ALLOCFID_OFF
+#define HFA384x_TXCOMPLFID HFA384x_TXCOMPLFID_OFF
+#define HFA384x_SELECT0 HFA384x_SELECT0_OFF
+#define HFA384x_OFFSET0 HFA384x_OFFSET0_OFF
+#define HFA384x_DATA0 HFA384x_DATA0_OFF
+#define HFA384x_SELECT1 HFA384x_SELECT1_OFF
+#define HFA384x_OFFSET1 HFA384x_OFFSET1_OFF
+#define HFA384x_DATA1 HFA384x_DATA1_OFF
+#define HFA384x_EVSTAT HFA384x_EVSTAT_OFF
+#define HFA384x_INTEN HFA384x_INTEN_OFF
+#define HFA384x_EVACK HFA384x_EVACK_OFF
+#define HFA384x_CONTROL HFA384x_CONTROL_OFF
+#define HFA384x_SWSUPPORT0 HFA384x_SWSUPPORT0_OFF
+#define HFA384x_SWSUPPORT1 HFA384x_SWSUPPORT1_OFF
+#define HFA384x_SWSUPPORT2 HFA384x_SWSUPPORT2_OFF
+#define HFA384x_AUXPAGE HFA384x_AUXPAGE_OFF
+#define HFA384x_AUXOFFSET HFA384x_AUXOFFSET_OFF
+#define HFA384x_AUXDATA HFA384x_AUXDATA_OFF
+#define HFA384x_PCICOR HFA384x_PCICOR_OFF
+
+
+/*--- Register Test/Get/Set Field macros ------------------------*/
+
+#define HFA384x_CMD_ISBUSY(value) ((UINT16)(((UINT16)value) & HFA384x_CMD_BUSY))
+#define HFA384x_CMD_AINFO_GET(value) ((UINT16)(((UINT16)(value) & HFA384x_CMD_AINFO) >> 8))
+#define HFA384x_CMD_AINFO_SET(value) ((UINT16)((UINT16)(value) << 8))
+#define HFA384x_CMD_MACPORT_GET(value) ((UINT16)(HFA384x_CMD_AINFO_GET((UINT16)(value) & HFA384x_CMD_MACPORT)))
+#define HFA384x_CMD_MACPORT_SET(value) ((UINT16)HFA384x_CMD_AINFO_SET(value))
+#define HFA384x_CMD_ISRECL(value) ((UINT16)(HFA384x_CMD_AINFO_GET((UINT16)(value) & HFA384x_CMD_RECL)))
+#define HFA384x_CMD_RECL_SET(value) ((UINT16)HFA384x_CMD_AINFO_SET(value))
+#define HFA384x_CMD_QOS_GET(value) ((UINT16((((UINT16)(value))&((UINT16)0x3000)) >> 12))
+#define HFA384x_CMD_QOS_SET(value) ((UINT16)((((UINT16)(value)) << 12) & 0x3000))
+#define HFA384x_CMD_ISWRITE(value) ((UINT16)(HFA384x_CMD_AINFO_GET((UINT16)(value) & HFA384x_CMD_WRITE)))
+#define HFA384x_CMD_WRITE_SET(value) ((UINT16)HFA384x_CMD_AINFO_SET((UINT16)value))
+#define HFA384x_CMD_PROGMODE_GET(value) ((UINT16)(HFA384x_CMD_AINFO_GET((UINT16)(value) & HFA384x_CMD_PROGMODE)))
+#define HFA384x_CMD_PROGMODE_SET(value) ((UINT16)HFA384x_CMD_AINFO_SET((UINT16)value))
+#define HFA384x_CMD_CMDCODE_GET(value) ((UINT16)(((UINT16)(value)) & HFA384x_CMD_CMDCODE))
+#define HFA384x_CMD_CMDCODE_SET(value) ((UINT16)(value))
+
+#define HFA384x_STATUS_RESULT_GET(value) ((UINT16)((((UINT16)(value)) & HFA384x_STATUS_RESULT) >> 8))
+#define HFA384x_STATUS_RESULT_SET(value) (((UINT16)(value)) << 8)
+#define HFA384x_STATUS_CMDCODE_GET(value) (((UINT16)(value)) & HFA384x_STATUS_CMDCODE)
+#define HFA384x_STATUS_CMDCODE_SET(value) ((UINT16)(value))
+
+#define HFA384x_OFFSET_ISBUSY(value) ((UINT16)(((UINT16)(value)) & HFA384x_OFFSET_BUSY))
+#define HFA384x_OFFSET_ISERR(value) ((UINT16)(((UINT16)(value)) & HFA384x_OFFSET_ERR))
+#define HFA384x_OFFSET_DATAOFF_GET(value) ((UINT16)(((UINT16)(value)) & HFA384x_OFFSET_DATAOFF))
+#define HFA384x_OFFSET_DATAOFF_SET(value) ((UINT16)(value))
+
+#define HFA384x_EVSTAT_ISTICK(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_TICK))
+#define HFA384x_EVSTAT_ISWTERR(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_WTERR))
+#define HFA384x_EVSTAT_ISINFDROP(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_INFDROP))
+#define HFA384x_EVSTAT_ISINFO(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_INFO))
+#define HFA384x_EVSTAT_ISDTIM(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_DTIM))
+#define HFA384x_EVSTAT_ISCMD(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_CMD))
+#define HFA384x_EVSTAT_ISALLOC(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_ALLOC))
+#define HFA384x_EVSTAT_ISTXEXC(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_TXEXC))
+#define HFA384x_EVSTAT_ISTX(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_TX))
+#define HFA384x_EVSTAT_ISRX(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_RX))
+
+#define HFA384x_INTEN_ISTICK(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_TICK))
+#define HFA384x_INTEN_TICK_SET(value) ((UINT16)(((UINT16)(value)) << 15))
+#define HFA384x_INTEN_ISWTERR(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_WTERR))
+#define HFA384x_INTEN_WTERR_SET(value) ((UINT16)(((UINT16)(value)) << 14))
+#define HFA384x_INTEN_ISINFDROP(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_INFDROP))
+#define HFA384x_INTEN_INFDROP_SET(value) ((UINT16)(((UINT16)(value)) << 13))
+#define HFA384x_INTEN_ISINFO(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_INFO))
+#define HFA384x_INTEN_INFO_SET(value) ((UINT16)(((UINT16)(value)) << 7))
+#define HFA384x_INTEN_ISDTIM(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_DTIM))
+#define HFA384x_INTEN_DTIM_SET(value) ((UINT16)(((UINT16)(value)) << 5))
+#define HFA384x_INTEN_ISCMD(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_CMD))
+#define HFA384x_INTEN_CMD_SET(value) ((UINT16)(((UINT16)(value)) << 4))
+#define HFA384x_INTEN_ISALLOC(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_ALLOC))
+#define HFA384x_INTEN_ALLOC_SET(value) ((UINT16)(((UINT16)(value)) << 3))
+#define HFA384x_INTEN_ISTXEXC(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_TXEXC))
+#define HFA384x_INTEN_TXEXC_SET(value) ((UINT16)(((UINT16)(value)) << 2))
+#define HFA384x_INTEN_ISTX(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_TX))
+#define HFA384x_INTEN_TX_SET(value) ((UINT16)(((UINT16)(value)) << 1))
+#define HFA384x_INTEN_ISRX(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_RX))
+#define HFA384x_INTEN_RX_SET(value) ((UINT16)(((UINT16)(value)) << 0))
+
+#define HFA384x_EVACK_ISTICK(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_TICK))
+#define HFA384x_EVACK_TICK_SET(value) ((UINT16)(((UINT16)(value)) << 15))
+#define HFA384x_EVACK_ISWTERR(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_WTERR))
+#define HFA384x_EVACK_WTERR_SET(value) ((UINT16)(((UINT16)(value)) << 14))
+#define HFA384x_EVACK_ISINFDROP(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_INFDROP))
+#define HFA384x_EVACK_INFDROP_SET(value) ((UINT16)(((UINT16)(value)) << 13))
+#define HFA384x_EVACK_ISINFO(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_INFO))
+#define HFA384x_EVACK_INFO_SET(value) ((UINT16)(((UINT16)(value)) << 7))
+#define HFA384x_EVACK_ISDTIM(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_DTIM))
+#define HFA384x_EVACK_DTIM_SET(value) ((UINT16)(((UINT16)(value)) << 5))
+#define HFA384x_EVACK_ISCMD(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_CMD))
+#define HFA384x_EVACK_CMD_SET(value) ((UINT16)(((UINT16)(value)) << 4))
+#define HFA384x_EVACK_ISALLOC(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_ALLOC))
+#define HFA384x_EVACK_ALLOC_SET(value) ((UINT16)(((UINT16)(value)) << 3))
+#define HFA384x_EVACK_ISTXEXC(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_TXEXC))
+#define HFA384x_EVACK_TXEXC_SET(value) ((UINT16)(((UINT16)(value)) << 2))
+#define HFA384x_EVACK_ISTX(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_TX))
+#define HFA384x_EVACK_TX_SET(value) ((UINT16)(((UINT16)(value)) << 1))
+#define HFA384x_EVACK_ISRX(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_RX))
+#define HFA384x_EVACK_RX_SET(value) ((UINT16)(((UINT16)(value)) << 0))
+
+#define HFA384x_CONTROL_AUXEN_SET(value) ((UINT16)(((UINT16)(value)) << 14))
+#define HFA384x_CONTROL_AUXEN_GET(value) ((UINT16)(((UINT16)(value)) >> 14))
+
+/* Byte Order */
+#define hfa384x2host_16(n) (__le16_to_cpu((UINT16)(n)))
+#define hfa384x2host_32(n) (__le32_to_cpu((UINT32)(n)))
+#define host2hfa384x_16(n) (__cpu_to_le16((UINT16)(n)))
+#define host2hfa384x_32(n) (__cpu_to_le32((UINT32)(n)))
+
+/* Host Maintained State Info */
+#define HFA384x_STATE_PREINIT 0
+#define HFA384x_STATE_INIT 1
+#define HFA384x_STATE_RUNNING 2
+
+/*=============================================================*/
+/*------ Types and their related constants --------------------*/
+
+/*-------------------------------------------------------------*/
+/* Commonly used basic types */
+typedef struct hfa384x_bytestr
+{
+ UINT16 len __WLAN_ATTRIB_PACK__;
+ UINT8 data[0] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_bytestr_t;
+
+typedef struct hfa384x_bytestr32
+{
+ UINT16 len __WLAN_ATTRIB_PACK__;
+ UINT8 data[32] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_bytestr32_t;
+
+/*--------------------------------------------------------------------
+Configuration Record Structures:
+ Network Parameters, Static Configuration Entities
+--------------------------------------------------------------------*/
+/* Prototype structure: all configuration record structures start with
+these members */
+
+typedef struct hfa384x_record
+{
+ UINT16 reclen __WLAN_ATTRIB_PACK__;
+ UINT16 rid __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_rec_t;
+
+typedef struct hfa384x_record16
+{
+ UINT16 reclen __WLAN_ATTRIB_PACK__;
+ UINT16 rid __WLAN_ATTRIB_PACK__;
+ UINT16 val __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_rec16_t;
+
+typedef struct hfa384x_record32
+{
+ UINT16 reclen __WLAN_ATTRIB_PACK__;
+ UINT16 rid __WLAN_ATTRIB_PACK__;
+ UINT32 val __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_rec32;
+
+/*-- Hardware/Firmware Component Information ----------*/
+typedef struct hfa384x_compident
+{
+ UINT16 id __WLAN_ATTRIB_PACK__;
+ UINT16 variant __WLAN_ATTRIB_PACK__;
+ UINT16 major __WLAN_ATTRIB_PACK__;
+ UINT16 minor __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_compident_t;
+
+typedef struct hfa384x_caplevel
+{
+ UINT16 role __WLAN_ATTRIB_PACK__;
+ UINT16 id __WLAN_ATTRIB_PACK__;
+ UINT16 variant __WLAN_ATTRIB_PACK__;
+ UINT16 bottom __WLAN_ATTRIB_PACK__;
+ UINT16 top __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_caplevel_t;
+
+/*-- Configuration Record: cnfPortType --*/
+typedef struct hfa384x_cnfPortType
+{
+ UINT16 cnfPortType __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfPortType_t;
+
+/*-- Configuration Record: cnfOwnMACAddress --*/
+typedef struct hfa384x_cnfOwnMACAddress
+{
+ UINT8 cnfOwnMACAddress[6] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnMACAddress_t;
+
+/*-- Configuration Record: cnfDesiredSSID --*/
+typedef struct hfa384x_cnfDesiredSSID
+{
+ UINT8 cnfDesiredSSID[34] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfDesiredSSID_t;
+
+/*-- Configuration Record: cnfOwnChannel --*/
+typedef struct hfa384x_cnfOwnChannel
+{
+ UINT16 cnfOwnChannel __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnChannel_t;
+
+/*-- Configuration Record: cnfOwnSSID --*/
+typedef struct hfa384x_cnfOwnSSID
+{
+ UINT8 cnfOwnSSID[34] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnSSID_t;
+
+/*-- Configuration Record: cnfOwnATIMWindow --*/
+typedef struct hfa384x_cnfOwnATIMWindow
+{
+ UINT16 cnfOwnATIMWindow __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnATIMWindow_t;
+
+/*-- Configuration Record: cnfSystemScale --*/
+typedef struct hfa384x_cnfSystemScale
+{
+ UINT16 cnfSystemScale __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfSystemScale_t;
+
+/*-- Configuration Record: cnfMaxDataLength --*/
+typedef struct hfa384x_cnfMaxDataLength
+{
+ UINT16 cnfMaxDataLength __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfMaxDataLength_t;
+
+/*-- Configuration Record: cnfWDSAddress --*/
+typedef struct hfa384x_cnfWDSAddress
+{
+ UINT8 cnfWDSAddress[6] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfWDSAddress_t;
+
+/*-- Configuration Record: cnfPMEnabled --*/
+typedef struct hfa384x_cnfPMEnabled
+{
+ UINT16 cnfPMEnabled __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfPMEnabled_t;
+
+/*-- Configuration Record: cnfPMEPS --*/
+typedef struct hfa384x_cnfPMEPS
+{
+ UINT16 cnfPMEPS __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfPMEPS_t;
+
+/*-- Configuration Record: cnfMulticastReceive --*/
+typedef struct hfa384x_cnfMulticastReceive
+{
+ UINT16 cnfMulticastReceive __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfMulticastReceive_t;
+
+/*-- Configuration Record: cnfAuthentication --*/
+#define HFA384x_CNFAUTHENTICATION_OPENSYSTEM 0x0001
+#define HFA384x_CNFAUTHENTICATION_SHAREDKEY 0x0002
+
+/*-- Configuration Record: cnfMaxSleepDuration --*/
+typedef struct hfa384x_cnfMaxSleepDuration
+{
+ UINT16 cnfMaxSleepDuration __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfMaxSleepDuration_t;
+
+/*-- Configuration Record: cnfPMHoldoverDuration --*/
+typedef struct hfa384x_cnfPMHoldoverDuration
+{
+ UINT16 cnfPMHoldoverDuration __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfPMHoldoverDuration_t;
+
+/*-- Configuration Record: cnfOwnName --*/
+typedef struct hfa384x_cnfOwnName
+{
+ UINT8 cnfOwnName[34] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnName_t;
+
+/*-- Configuration Record: cnfOwnDTIMPeriod --*/
+typedef struct hfa384x_cnfOwnDTIMPeriod
+{
+ UINT16 cnfOwnDTIMPeriod __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnDTIMPeriod_t;
+
+/*-- Configuration Record: cnfWDSAddress --*/
+typedef struct hfa384x_cnfWDSAddressN
+{
+ UINT8 cnfWDSAddress[6] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfWDSAddressN_t;
+
+/*-- Configuration Record: cnfMulticastPMBuffering --*/
+typedef struct hfa384x_cnfMulticastPMBuffering
+{
+ UINT16 cnfMulticastPMBuffering __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_cnfMulticastPMBuffering_t;
+
+/*--------------------------------------------------------------------
+Configuration Record Structures:
+ Network Parameters, Dynamic Configuration Entities
+--------------------------------------------------------------------*/
+
+/*-- Configuration Record: GroupAddresses --*/
+typedef struct hfa384x_GroupAddresses
+{
+ UINT8 MACAddress[16][6] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_GroupAddresses_t;
+
+/*-- Configuration Record: CreateIBSS --*/
+typedef struct hfa384x_CreateIBSS
+{
+ UINT16 CreateIBSS __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_CreateIBSS_t;
+
+#define HFA384x_CREATEIBSS_JOINCREATEIBSS 0
+#define HFA384x_CREATEIBSS_JOINESS_JOINCREATEIBSS 1
+#define HFA384x_CREATEIBSS_JOINIBSS 2
+#define HFA384x_CREATEIBSS_JOINESS_JOINIBSS 3
+
+/*-- Configuration Record: FragmentationThreshold --*/
+typedef struct hfa384x_FragmentationThreshold
+{
+ UINT16 FragmentationThreshold __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_FragmentationThreshold_t;
+
+/*-- Configuration Record: RTSThreshold --*/
+typedef struct hfa384x_RTSThreshold
+{
+ UINT16 RTSThreshold __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_RTSThreshold_t;
+
+/*-- Configuration Record: TxRateControl --*/
+typedef struct hfa384x_TxRateControl
+{
+ UINT16 TxRateControl __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_TxRateControl_t;
+
+/*-- Configuration Record: PromiscuousMode --*/
+typedef struct hfa384x_PromiscuousMode
+{
+ UINT16 PromiscuousMode __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_PromiscuousMode_t;
+
+/*-- Configuration Record: ScanRequest (data portion only) --*/
+typedef struct hfa384x_ScanRequest_data
+{
+ UINT16 channelList __WLAN_ATTRIB_PACK__;
+ UINT16 txRate __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_ScanRequest_data_t;
+
+/*-- Configuration Record: HostScanRequest (data portion only) --*/
+typedef struct hfa384x_HostScanRequest_data
+{
+ UINT16 channelList __WLAN_ATTRIB_PACK__;
+ UINT16 txRate __WLAN_ATTRIB_PACK__;
+ hfa384x_bytestr32_t ssid __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_HostScanRequest_data_t;
+
+/*-- Configuration Record: JoinRequest (data portion only) --*/
+typedef struct hfa384x_JoinRequest_data
+{
+ UINT8 bssid[WLAN_BSSID_LEN] __WLAN_ATTRIB_PACK__;
+ UINT16 channel __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_JoinRequest_data_t;
+
+/*-- Configuration Record: authenticateStation (data portion only) --*/
+typedef struct hfa384x_authenticateStation_data
+{
+ UINT8 address[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__;
+ UINT16 status __WLAN_ATTRIB_PACK__;
+ UINT16 algorithm __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_authenticateStation_data_t;
+
+/*-- Configuration Record: ChannelInfoRequest (data portion only) --*/
+typedef struct hfa384x_ChannelInfoRequest_data
+{
+ UINT16 channelList __WLAN_ATTRIB_PACK__;
+ UINT16 channelDwellTime __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_ChannelInfoRequest_data_t;
+
+/*--------------------------------------------------------------------
+Configuration Record Structures: Behavior Parameters
+--------------------------------------------------------------------*/
+
+/*-- Configuration Record: TickTime --*/
+typedef struct hfa384x_TickTime
+{
+ UINT16 TickTime __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_TickTime_t;
+
+/*--------------------------------------------------------------------
+Information Record Structures: NIC Information
+--------------------------------------------------------------------*/
+
+/*-- Information Record: MaxLoadTime --*/
+typedef struct hfa384x_MaxLoadTime
+{
+ UINT16 MaxLoadTime __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_MaxLoadTime_t;
+
+/*-- Information Record: DownLoadBuffer --*/
+/* NOTE: The page and offset are in AUX format */
+typedef struct hfa384x_downloadbuffer
+{
+ UINT16 page __WLAN_ATTRIB_PACK__;
+ UINT16 offset __WLAN_ATTRIB_PACK__;
+ UINT16 len __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_downloadbuffer_t;
+
+/*-- Information Record: PRIIdentity --*/
+typedef struct hfa384x_PRIIdentity
+{
+ UINT16 PRICompID __WLAN_ATTRIB_PACK__;
+ UINT16 PRIVariant __WLAN_ATTRIB_PACK__;
+ UINT16 PRIMajorVersion __WLAN_ATTRIB_PACK__;
+ UINT16 PRIMinorVersion __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_PRIIdentity_t;
+
+/*-- Information Record: PRISupRange --*/
+typedef struct hfa384x_PRISupRange
+{
+ UINT16 PRIRole __WLAN_ATTRIB_PACK__;
+ UINT16 PRIID __WLAN_ATTRIB_PACK__;
+ UINT16 PRIVariant __WLAN_ATTRIB_PACK__;
+ UINT16 PRIBottom __WLAN_ATTRIB_PACK__;
+ UINT16 PRITop __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_PRISupRange_t;
+
+/*-- Information Record: CFIActRanges --*/
+typedef struct hfa384x_CFIActRanges
+{
+ UINT16 CFIRole __WLAN_ATTRIB_PACK__;
+ UINT16 CFIID __WLAN_ATTRIB_PACK__;
+ UINT16 CFIVariant __WLAN_ATTRIB_PACK__;
+ UINT16 CFIBottom __WLAN_ATTRIB_PACK__;
+ UINT16 CFITop __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_CFIActRanges_t;
+
+/*-- Information Record: NICSerialNumber --*/
+typedef struct hfa384x_NICSerialNumber
+{
+ UINT8 NICSerialNumber[12] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_NICSerialNumber_t;
+
+/*-- Information Record: NICIdentity --*/
+typedef struct hfa384x_NICIdentity
+{
+ UINT16 NICCompID __WLAN_ATTRIB_PACK__;
+ UINT16 NICVariant __WLAN_ATTRIB_PACK__;
+ UINT16 NICMajorVersion __WLAN_ATTRIB_PACK__;
+ UINT16 NICMinorVersion __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_NICIdentity_t;
+
+/*-- Information Record: MFISupRange --*/
+typedef struct hfa384x_MFISupRange
+{
+ UINT16 MFIRole __WLAN_ATTRIB_PACK__;
+ UINT16 MFIID __WLAN_ATTRIB_PACK__;
+ UINT16 MFIVariant __WLAN_ATTRIB_PACK__;
+ UINT16 MFIBottom __WLAN_ATTRIB_PACK__;
+ UINT16 MFITop __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_MFISupRange_t;
+
+/*-- Information Record: CFISupRange --*/
+typedef struct hfa384x_CFISupRange
+{
+ UINT16 CFIRole __WLAN_ATTRIB_PACK__;
+ UINT16 CFIID __WLAN_ATTRIB_PACK__;
+ UINT16 CFIVariant __WLAN_ATTRIB_PACK__;
+ UINT16 CFIBottom __WLAN_ATTRIB_PACK__;
+ UINT16 CFITop __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_CFISupRange_t;
+
+/*-- Information Record: BUILDSEQ:BuildSeq --*/
+typedef struct hfa384x_BuildSeq {
+ UINT16 primary __WLAN_ATTRIB_PACK__;
+ UINT16 secondary __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_BuildSeq_t;
+
+/*-- Information Record: FWID --*/
+#define HFA384x_FWID_LEN 14
+typedef struct hfa384x_FWID {
+ UINT8 primary[HFA384x_FWID_LEN] __WLAN_ATTRIB_PACK__;
+ UINT8 secondary[HFA384x_FWID_LEN] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_FWID_t;
+
+/*-- Information Record: ChannelList --*/
+typedef struct hfa384x_ChannelList
+{
+ UINT16 ChannelList __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_ChannelList_t;
+
+/*-- Information Record: RegulatoryDomains --*/
+typedef struct hfa384x_RegulatoryDomains
+{
+ UINT8 RegulatoryDomains[12] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_RegulatoryDomains_t;
+
+/*-- Information Record: TempType --*/
+typedef struct hfa384x_TempType
+{
+ UINT16 TempType __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_TempType_t;
+
+/*-- Information Record: CIS --*/
+typedef struct hfa384x_CIS
+{
+ UINT8 CIS[480] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_CIS_t;
+
+/*-- Information Record: STAIdentity --*/
+typedef struct hfa384x_STAIdentity
+{
+ UINT16 STACompID __WLAN_ATTRIB_PACK__;
+ UINT16 STAVariant __WLAN_ATTRIB_PACK__;
+ UINT16 STAMajorVersion __WLAN_ATTRIB_PACK__;
+ UINT16 STAMinorVersion __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_STAIdentity_t;
+
+/*-- Information Record: STASupRange --*/
+typedef struct hfa384x_STASupRange
+{
+ UINT16 STARole __WLAN_ATTRIB_PACK__;
+ UINT16 STAID __WLAN_ATTRIB_PACK__;
+ UINT16 STAVariant __WLAN_ATTRIB_PACK__;
+ UINT16 STABottom __WLAN_ATTRIB_PACK__;
+ UINT16 STATop __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_STASupRange_t;
+
+/*-- Information Record: MFIActRanges --*/
+typedef struct hfa384x_MFIActRanges
+{
+ UINT16 MFIRole __WLAN_ATTRIB_PACK__;
+ UINT16 MFIID __WLAN_ATTRIB_PACK__;
+ UINT16 MFIVariant __WLAN_ATTRIB_PACK__;
+ UINT16 MFIBottom __WLAN_ATTRIB_PACK__;
+ UINT16 MFITop __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_MFIActRanges_t;
+
+/*--------------------------------------------------------------------
+Information Record Structures: NIC Information
+--------------------------------------------------------------------*/
+
+/*-- Information Record: PortStatus --*/
+typedef struct hfa384x_PortStatus
+{
+ UINT16 PortStatus __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_PortStatus_t;
+
+#define HFA384x_PSTATUS_DISABLED ((UINT16)1)
+#define HFA384x_PSTATUS_SEARCHING ((UINT16)2)
+#define HFA384x_PSTATUS_CONN_IBSS ((UINT16)3)
+#define HFA384x_PSTATUS_CONN_ESS ((UINT16)4)
+#define HFA384x_PSTATUS_OUTOFRANGE ((UINT16)5)
+#define HFA384x_PSTATUS_CONN_WDS ((UINT16)6)
+
+/*-- Information Record: CurrentSSID --*/
+typedef struct hfa384x_CurrentSSID
+{
+ UINT8 CurrentSSID[34] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_CurrentSSID_t;
+
+/*-- Information Record: CurrentBSSID --*/
+typedef struct hfa384x_CurrentBSSID
+{
+ UINT8 CurrentBSSID[6] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_CurrentBSSID_t;
+
+/*-- Information Record: commsquality --*/
+typedef struct hfa384x_commsquality
+{
+ UINT16 CQ_currBSS __WLAN_ATTRIB_PACK__;
+ UINT16 ASL_currBSS __WLAN_ATTRIB_PACK__;
+ UINT16 ANL_currFC __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_commsquality_t;
+
+/*-- Information Record: dmbcommsquality --*/
+typedef struct hfa384x_dbmcommsquality
+{
+ UINT16 CQdbm_currBSS __WLAN_ATTRIB_PACK__;
+ UINT16 ASLdbm_currBSS __WLAN_ATTRIB_PACK__;
+ UINT16 ANLdbm_currFC __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_dbmcommsquality_t;
+
+/*-- Information Record: CurrentTxRate --*/
+typedef struct hfa384x_CurrentTxRate
+{
+ UINT16 CurrentTxRate __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_CurrentTxRate_t;
+
+/*-- Information Record: CurrentBeaconInterval --*/
+typedef struct hfa384x_CurrentBeaconInterval
+{
+ UINT16 CurrentBeaconInterval __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_CurrentBeaconInterval_t;
+
+/*-- Information Record: CurrentScaleThresholds --*/
+typedef struct hfa384x_CurrentScaleThresholds
+{
+ UINT16 EnergyDetectThreshold __WLAN_ATTRIB_PACK__;
+ UINT16 CarrierDetectThreshold __WLAN_ATTRIB_PACK__;
+ UINT16 DeferDetectThreshold __WLAN_ATTRIB_PACK__;
+ UINT16 CellSearchThreshold __WLAN_ATTRIB_PACK__; /* Stations only */
+ UINT16 DeadSpotThreshold __WLAN_ATTRIB_PACK__; /* Stations only */
+} __WLAN_ATTRIB_PACK__ hfa384x_CurrentScaleThresholds_t;
+
+/*-- Information Record: ProtocolRspTime --*/
+typedef struct hfa384x_ProtocolRspTime
+{
+ UINT16 ProtocolRspTime __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_ProtocolRspTime_t;
+
+/*-- Information Record: ShortRetryLimit --*/
+typedef struct hfa384x_ShortRetryLimit
+{
+ UINT16 ShortRetryLimit __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_ShortRetryLimit_t;
+
+/*-- Information Record: LongRetryLimit --*/
+typedef struct hfa384x_LongRetryLimit
+{
+ UINT16 LongRetryLimit __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_LongRetryLimit_t;
+
+/*-- Information Record: MaxTransmitLifetime --*/
+typedef struct hfa384x_MaxTransmitLifetime
+{
+ UINT16 MaxTransmitLifetime __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_MaxTransmitLifetime_t;
+
+/*-- Information Record: MaxReceiveLifetime --*/
+typedef struct hfa384x_MaxReceiveLifetime
+{
+ UINT16 MaxReceiveLifetime __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_MaxReceiveLifetime_t;
+
+/*-- Information Record: CFPollable --*/
+typedef struct hfa384x_CFPollable
+{
+ UINT16 CFPollable __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_CFPollable_t;
+
+/*-- Information Record: AuthenticationAlgorithms --*/
+typedef struct hfa384x_AuthenticationAlgorithms
+{
+ UINT16 AuthenticationType __WLAN_ATTRIB_PACK__;
+ UINT16 TypeEnabled __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_AuthenticationAlgorithms_t;
+
+/*-- Information Record: AuthenticationAlgorithms
+(data only --*/
+typedef struct hfa384x_AuthenticationAlgorithms_data
+{
+ UINT16 AuthenticationType __WLAN_ATTRIB_PACK__;
+ UINT16 TypeEnabled __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_AuthenticationAlgorithms_data_t;
+
+/*-- Information Record: PrivacyOptionImplemented --*/
+typedef struct hfa384x_PrivacyOptionImplemented
+{
+ UINT16 PrivacyOptionImplemented __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_PrivacyOptionImplemented_t;
+
+/*-- Information Record: OwnMACAddress --*/
+typedef struct hfa384x_OwnMACAddress
+{
+ UINT8 OwnMACAddress[6] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_OwnMACAddress_t;
+
+/*-- Information Record: PCFInfo --*/
+typedef struct hfa384x_PCFInfo
+{
+ UINT16 MediumOccupancyLimit __WLAN_ATTRIB_PACK__;
+ UINT16 CFPPeriod __WLAN_ATTRIB_PACK__;
+ UINT16 CFPMaxDuration __WLAN_ATTRIB_PACK__;
+ UINT16 CFPFlags __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_PCFInfo_t;
+
+/*-- Information Record: PCFInfo (data portion only) --*/
+typedef struct hfa384x_PCFInfo_data
+{
+ UINT16 MediumOccupancyLimit __WLAN_ATTRIB_PACK__;
+ UINT16 CFPPeriod __WLAN_ATTRIB_PACK__;
+ UINT16 CFPMaxDuration __WLAN_ATTRIB_PACK__;
+ UINT16 CFPFlags __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_PCFInfo_data_t;
+
+/*--------------------------------------------------------------------
+Information Record Structures: Modem Information Records
+--------------------------------------------------------------------*/
+
+/*-- Information Record: PHYType --*/
+typedef struct hfa384x_PHYType
+{
+ UINT16 PHYType __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_PHYType_t;
+
+/*-- Information Record: CurrentChannel --*/
+typedef struct hfa384x_CurrentChannel
+{
+ UINT16 CurrentChannel __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_CurrentChannel_t;
+
+/*-- Information Record: CurrentPowerState --*/
+typedef struct hfa384x_CurrentPowerState
+{
+ UINT16 CurrentPowerState __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_CurrentPowerState_t;
+
+/*-- Information Record: CCAMode --*/
+typedef struct hfa384x_CCAMode
+{
+ UINT16 CCAMode __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_CCAMode_t;
+
+/*-- Information Record: SupportedDataRates --*/
+typedef struct hfa384x_SupportedDataRates
+{
+ UINT8 SupportedDataRates[10] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_SupportedDataRates_t;
+
+/*--------------------------------------------------------------------
+ FRAME DESCRIPTORS AND FRAME STRUCTURES
+
+FRAME DESCRIPTORS: Offsets
+
+----------------------------------------------------------------------
+Control Info (offset 44-51)
+--------------------------------------------------------------------*/
+#define HFA384x_FD_STATUS_OFF ((UINT16)0x44)
+#define HFA384x_FD_TIME_OFF ((UINT16)0x46)
+#define HFA384x_FD_SWSUPPORT_OFF ((UINT16)0x4A)
+#define HFA384x_FD_SILENCE_OFF ((UINT16)0x4A)
+#define HFA384x_FD_SIGNAL_OFF ((UINT16)0x4B)
+#define HFA384x_FD_RATE_OFF ((UINT16)0x4C)
+#define HFA384x_FD_RXFLOW_OFF ((UINT16)0x4D)
+#define HFA384x_FD_RESERVED_OFF ((UINT16)0x4E)
+#define HFA384x_FD_TXCONTROL_OFF ((UINT16)0x50)
+/*--------------------------------------------------------------------
+802.11 Header (offset 52-6B)
+--------------------------------------------------------------------*/
+#define HFA384x_FD_FRAMECONTROL_OFF ((UINT16)0x52)
+#define HFA384x_FD_DURATIONID_OFF ((UINT16)0x54)
+#define HFA384x_FD_ADDRESS1_OFF ((UINT16)0x56)
+#define HFA384x_FD_ADDRESS2_OFF ((UINT16)0x5C)
+#define HFA384x_FD_ADDRESS3_OFF ((UINT16)0x62)
+#define HFA384x_FD_SEQCONTROL_OFF ((UINT16)0x68)
+#define HFA384x_FD_ADDRESS4_OFF ((UINT16)0x6A)
+#define HFA384x_FD_DATALEN_OFF ((UINT16)0x70)
+/*--------------------------------------------------------------------
+802.3 Header (offset 72-7F)
+--------------------------------------------------------------------*/
+#define HFA384x_FD_DESTADDRESS_OFF ((UINT16)0x72)
+#define HFA384x_FD_SRCADDRESS_OFF ((UINT16)0x78)
+#define HFA384x_FD_DATALENGTH_OFF ((UINT16)0x7E)
+
+/*--------------------------------------------------------------------
+FRAME STRUCTURES: Communication Frames
+----------------------------------------------------------------------
+Communication Frames: Transmit Frames
+--------------------------------------------------------------------*/
+/*-- Communication Frame: Transmit Frame Structure --*/
+typedef struct hfa384x_tx_frame
+{
+ UINT16 status __WLAN_ATTRIB_PACK__;
+ UINT16 reserved1 __WLAN_ATTRIB_PACK__;
+ UINT16 reserved2 __WLAN_ATTRIB_PACK__;
+ UINT32 sw_support __WLAN_ATTRIB_PACK__;
+ UINT16 reserved3 __WLAN_ATTRIB_PACK__;
+ UINT16 tx_control __WLAN_ATTRIB_PACK__;
+
+ /*-- 802.11 Header Information --*/
+
+ UINT16 frame_control __WLAN_ATTRIB_PACK__;
+ UINT16 duration_id __WLAN_ATTRIB_PACK__;
+ UINT8 address1[6] __WLAN_ATTRIB_PACK__;
+ UINT8 address2[6] __WLAN_ATTRIB_PACK__;
+ UINT8 address3[6] __WLAN_ATTRIB_PACK__;
+ UINT16 sequence_control __WLAN_ATTRIB_PACK__;
+ UINT8 address4[6] __WLAN_ATTRIB_PACK__;
+ UINT16 data_len __WLAN_ATTRIB_PACK__; /* little endian format */
+
+ /*-- 802.3 Header Information --*/
+
+ UINT8 dest_addr[6] __WLAN_ATTRIB_PACK__;
+ UINT8 src_addr[6] __WLAN_ATTRIB_PACK__;
+ UINT16 data_length __WLAN_ATTRIB_PACK__; /* big endian format */
+} __WLAN_ATTRIB_PACK__ hfa384x_tx_frame_t;
+/*--------------------------------------------------------------------
+Communication Frames: Field Masks for Transmit Frames
+--------------------------------------------------------------------*/
+/*-- Status Field --*/
+#define HFA384x_TXSTATUS_ACKERR ((UINT16)BIT5)
+#define HFA384x_TXSTATUS_FORMERR ((UINT16)BIT3)
+#define HFA384x_TXSTATUS_DISCON ((UINT16)BIT2)
+#define HFA384x_TXSTATUS_AGEDERR ((UINT16)BIT1)
+#define HFA384x_TXSTATUS_RETRYERR ((UINT16)BIT0)
+/*-- Transmit Control Field --*/
+#define HFA384x_TX_CFPOLL ((UINT16)BIT12)
+#define HFA384x_TX_PRST ((UINT16)BIT11)
+#define HFA384x_TX_MACPORT ((UINT16)(BIT10 | BIT9 | BIT8))
+#define HFA384x_TX_NOENCRYPT ((UINT16)BIT7)
+#define HFA384x_TX_RETRYSTRAT ((UINT16)(BIT6 | BIT5))
+#define HFA384x_TX_STRUCTYPE ((UINT16)(BIT4 | BIT3))
+#define HFA384x_TX_TXEX ((UINT16)BIT2)
+#define HFA384x_TX_TXOK ((UINT16)BIT1)
+/*--------------------------------------------------------------------
+Communication Frames: Test/Get/Set Field Values for Transmit Frames
+--------------------------------------------------------------------*/
+/*-- Status Field --*/
+#define HFA384x_TXSTATUS_ISERROR(v) \
+ (((UINT16)(v))&\
+ (HFA384x_TXSTATUS_ACKERR|HFA384x_TXSTATUS_FORMERR|\
+ HFA384x_TXSTATUS_DISCON|HFA384x_TXSTATUS_AGEDERR|\
+ HFA384x_TXSTATUS_RETRYERR))
+
+#define HFA384x_TXSTATUS_ISACKERR(v) ((UINT16)(((UINT16)(v)) & HFA384x_TXSTATUS_ACKERR))
+#define HFA384x_TXSTATUS_ISFORMERR(v) ((UINT16)(((UINT16)(v)) & HFA384x_TXSTATUS_FORMERR))
+#define HFA384x_TXSTATUS_ISDISCON(v) ((UINT16)(((UINT16)(v)) & HFA384x_TXSTATUS_DISCON))
+#define HFA384x_TXSTATUS_ISAGEDERR(v) ((UINT16)(((UINT16)(v)) & HFA384x_TXSTATUS_AGEDERR))
+#define HFA384x_TXSTATUS_ISRETRYERR(v) ((UINT16)(((UINT16)(v)) & HFA384x_TXSTATUS_RETRYERR))
+
+#define HFA384x_TX_GET(v,m,s) ((((UINT16)(v))&((UINT16)(m)))>>((UINT16)(s)))
+#define HFA384x_TX_SET(v,m,s) ((((UINT16)(v))<<((UINT16)(s)))&((UINT16)(m)))
+
+#define HFA384x_TX_CFPOLL_GET(v) HFA384x_TX_GET(v, HFA384x_TX_CFPOLL,12)
+#define HFA384x_TX_CFPOLL_SET(v) HFA384x_TX_SET(v, HFA384x_TX_CFPOLL,12)
+#define HFA384x_TX_PRST_GET(v) HFA384x_TX_GET(v, HFA384x_TX_PRST,11)
+#define HFA384x_TX_PRST_SET(v) HFA384x_TX_SET(v, HFA384x_TX_PRST,11)
+#define HFA384x_TX_MACPORT_GET(v) HFA384x_TX_GET(v, HFA384x_TX_MACPORT, 8)
+#define HFA384x_TX_MACPORT_SET(v) HFA384x_TX_SET(v, HFA384x_TX_MACPORT, 8)
+#define HFA384x_TX_NOENCRYPT_GET(v) HFA384x_TX_GET(v, HFA384x_TX_NOENCRYPT, 7)
+#define HFA384x_TX_NOENCRYPT_SET(v) HFA384x_TX_SET(v, HFA384x_TX_NOENCRYPT, 7)
+#define HFA384x_TX_RETRYSTRAT_GET(v) HFA384x_TX_GET(v, HFA384x_TX_RETRYSTRAT, 5)
+#define HFA384x_TX_RETRYSTRAT_SET(v) HFA384x_TX_SET(v, HFA384x_TX_RETRYSTRAT, 5)
+#define HFA384x_TX_STRUCTYPE_GET(v) HFA384x_TX_GET(v, HFA384x_TX_STRUCTYPE, 3)
+#define HFA384x_TX_STRUCTYPE_SET(v) HFA384x_TX_SET(v, HFA384x_TX_STRUCTYPE, 3)
+#define HFA384x_TX_TXEX_GET(v) HFA384x_TX_GET(v, HFA384x_TX_TXEX, 2)
+#define HFA384x_TX_TXEX_SET(v) HFA384x_TX_SET(v, HFA384x_TX_TXEX, 2)
+#define HFA384x_TX_TXOK_GET(v) HFA384x_TX_GET(v, HFA384x_TX_TXOK, 1)
+#define HFA384x_TX_TXOK_SET(v) HFA384x_TX_SET(v, HFA384x_TX_TXOK, 1)
+/*--------------------------------------------------------------------
+Communication Frames: Receive Frames
+--------------------------------------------------------------------*/
+/*-- Communication Frame: Receive Frame Structure --*/
+typedef struct hfa384x_rx_frame
+{
+ /*-- MAC rx descriptor (hfa384x byte order) --*/
+ UINT16 status __WLAN_ATTRIB_PACK__;
+ UINT32 time __WLAN_ATTRIB_PACK__;
+ UINT8 silence __WLAN_ATTRIB_PACK__;
+ UINT8 signal __WLAN_ATTRIB_PACK__;
+ UINT8 rate __WLAN_ATTRIB_PACK__;
+ UINT8 rx_flow __WLAN_ATTRIB_PACK__;
+ UINT16 reserved1 __WLAN_ATTRIB_PACK__;
+ UINT16 reserved2 __WLAN_ATTRIB_PACK__;
+
+ /*-- 802.11 Header Information (802.11 byte order) --*/
+ UINT16 frame_control __WLAN_ATTRIB_PACK__;
+ UINT16 duration_id __WLAN_ATTRIB_PACK__;
+ UINT8 address1[6] __WLAN_ATTRIB_PACK__;
+ UINT8 address2[6] __WLAN_ATTRIB_PACK__;
+ UINT8 address3[6] __WLAN_ATTRIB_PACK__;
+ UINT16 sequence_control __WLAN_ATTRIB_PACK__;
+ UINT8 address4[6] __WLAN_ATTRIB_PACK__;
+ UINT16 data_len __WLAN_ATTRIB_PACK__; /* hfa384x (little endian) format */
+
+ /*-- 802.3 Header Information --*/
+ UINT8 dest_addr[6] __WLAN_ATTRIB_PACK__;
+ UINT8 src_addr[6] __WLAN_ATTRIB_PACK__;
+ UINT16 data_length __WLAN_ATTRIB_PACK__; /* IEEE? (big endian) format */
+} __WLAN_ATTRIB_PACK__ hfa384x_rx_frame_t;
+/*--------------------------------------------------------------------
+Communication Frames: Field Masks for Receive Frames
+--------------------------------------------------------------------*/
+/*-- Offsets --------*/
+#define HFA384x_RX_DATA_LEN_OFF ((UINT16)44)
+#define HFA384x_RX_80211HDR_OFF ((UINT16)14)
+#define HFA384x_RX_DATA_OFF ((UINT16)60)
+
+/*-- Status Fields --*/
+#define HFA384x_RXSTATUS_MSGTYPE ((UINT16)(BIT15 | BIT14 | BIT13))
+#define HFA384x_RXSTATUS_MACPORT ((UINT16)(BIT10 | BIT9 | BIT8))
+#define HFA384x_RXSTATUS_UNDECR ((UINT16)BIT1)
+#define HFA384x_RXSTATUS_FCSERR ((UINT16)BIT0)
+/*--------------------------------------------------------------------
+Communication Frames: Test/Get/Set Field Values for Receive Frames
+--------------------------------------------------------------------*/
+#define HFA384x_RXSTATUS_MSGTYPE_GET(value) ((UINT16)((((UINT16)(value)) & HFA384x_RXSTATUS_MSGTYPE) >> 13))
+#define HFA384x_RXSTATUS_MSGTYPE_SET(value) ((UINT16)(((UINT16)(value)) << 13))
+#define HFA384x_RXSTATUS_MACPORT_GET(value) ((UINT16)((((UINT16)(value)) & HFA384x_RXSTATUS_MACPORT) >> 8))
+#define HFA384x_RXSTATUS_MACPORT_SET(value) ((UINT16)(((UINT16)(value)) << 8))
+#define HFA384x_RXSTATUS_ISUNDECR(value) ((UINT16)(((UINT16)(value)) & HFA384x_RXSTATUS_UNDECR))
+#define HFA384x_RXSTATUS_ISFCSERR(value) ((UINT16)(((UINT16)(value)) & HFA384x_RXSTATUS_FCSERR))
+/*--------------------------------------------------------------------
+ FRAME STRUCTURES: Information Types and Information Frame Structures
+----------------------------------------------------------------------
+Information Types
+--------------------------------------------------------------------*/
+#define HFA384x_IT_HANDOVERADDR ((UINT16)0xF000UL)
+#define HFA384x_IT_COMMTALLIES ((UINT16)0xF100UL)
+#define HFA384x_IT_SCANRESULTS ((UINT16)0xF101UL)
+#define HFA384x_IT_CHINFORESULTS ((UINT16)0xF102UL)
+#define HFA384x_IT_HOSTSCANRESULTS ((UINT16)0xF103UL)//NEW
+#define HFA384x_IT_LINKSTATUS ((UINT16)0xF200UL)
+#define HFA384x_IT_ASSOCSTATUS ((UINT16)0xF201UL)
+#define HFA384x_IT_AUTHREQ ((UINT16)0xF202UL)
+#define HFA384x_IT_PSUSERCNT ((UINT16)0xF203UL)
+#define HFA384x_IT_KEYIDCHANGED ((UINT16)0xF204UL)//NEW AP
+
+/*--------------------------------------------------------------------
+Information Frames Structures
+----------------------------------------------------------------------
+Information Frames: Notification Frame Structures
+--------------------------------------------------------------------*/
+/*-- Notification Frame,MAC Mgmt: Handover Address --*/
+typedef struct hfa384x_HandoverAddr
+{
+ UINT16 framelen __WLAN_ATTRIB_PACK__;
+ UINT16 infotype __WLAN_ATTRIB_PACK__;
+ UINT8 handover_addr[WLAN_BSSID_LEN] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_HandoverAddr_t;
+
+/*-- Inquiry Frame, Diagnose: Communication Tallies --*/
+typedef struct __WLAN_ATTRIB_PACK__ hfa384x_CommTallies16
+{
+ UINT16 txunicastframes __WLAN_ATTRIB_PACK__;
+ UINT16 txmulticastframes __WLAN_ATTRIB_PACK__;
+ UINT16 txfragments __WLAN_ATTRIB_PACK__;
+ UINT16 txunicastoctets __WLAN_ATTRIB_PACK__;
+ UINT16 txmulticastoctets __WLAN_ATTRIB_PACK__;
+ UINT16 txdeferredtrans __WLAN_ATTRIB_PACK__;
+ UINT16 txsingleretryframes __WLAN_ATTRIB_PACK__;
+ UINT16 txmultipleretryframes __WLAN_ATTRIB_PACK__;
+ UINT16 txretrylimitexceeded __WLAN_ATTRIB_PACK__;
+ UINT16 txdiscards __WLAN_ATTRIB_PACK__;
+ UINT16 rxunicastframes __WLAN_ATTRIB_PACK__;
+ UINT16 rxmulticastframes __WLAN_ATTRIB_PACK__;
+ UINT16 rxfragments __WLAN_ATTRIB_PACK__;
+ UINT16 rxunicastoctets __WLAN_ATTRIB_PACK__;
+ UINT16 rxmulticastoctets __WLAN_ATTRIB_PACK__;
+ UINT16 rxfcserrors __WLAN_ATTRIB_PACK__;
+ UINT16 rxdiscardsnobuffer __WLAN_ATTRIB_PACK__;
+ UINT16 txdiscardswrongsa __WLAN_ATTRIB_PACK__;
+ UINT16 rxdiscardswepundecr __WLAN_ATTRIB_PACK__;
+ UINT16 rxmsginmsgfrag __WLAN_ATTRIB_PACK__;
+ UINT16 rxmsginbadmsgfrag __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_CommTallies16_t;
+
+typedef struct __WLAN_ATTRIB_PACK__ hfa384x_CommTallies32
+{
+ UINT32 txunicastframes __WLAN_ATTRIB_PACK__;
+ UINT32 txmulticastframes __WLAN_ATTRIB_PACK__;
+ UINT32 txfragments __WLAN_ATTRIB_PACK__;
+ UINT32 txunicastoctets __WLAN_ATTRIB_PACK__;
+ UINT32 txmulticastoctets __WLAN_ATTRIB_PACK__;
+ UINT32 txdeferredtrans __WLAN_ATTRIB_PACK__;
+ UINT32 txsingleretryframes __WLAN_ATTRIB_PACK__;
+ UINT32 txmultipleretryframes __WLAN_ATTRIB_PACK__;
+ UINT32 txretrylimitexceeded __WLAN_ATTRIB_PACK__;
+ UINT32 txdiscards __WLAN_ATTRIB_PACK__;
+ UINT32 rxunicastframes __WLAN_ATTRIB_PACK__;
+ UINT32 rxmulticastframes __WLAN_ATTRIB_PACK__;
+ UINT32 rxfragments __WLAN_ATTRIB_PACK__;
+ UINT32 rxunicastoctets __WLAN_ATTRIB_PACK__;
+ UINT32 rxmulticastoctets __WLAN_ATTRIB_PACK__;
+ UINT32 rxfcserrors __WLAN_ATTRIB_PACK__;
+ UINT32 rxdiscardsnobuffer __WLAN_ATTRIB_PACK__;
+ UINT32 txdiscardswrongsa __WLAN_ATTRIB_PACK__;
+ UINT32 rxdiscardswepundecr __WLAN_ATTRIB_PACK__;
+ UINT32 rxmsginmsgfrag __WLAN_ATTRIB_PACK__;
+ UINT32 rxmsginbadmsgfrag __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_CommTallies32_t;
+
+/*-- Inquiry Frame, Diagnose: Scan Results & Subfields--*/
+typedef struct hfa384x_ScanResultSub
+{
+ UINT16 chid __WLAN_ATTRIB_PACK__;
+ UINT16 anl __WLAN_ATTRIB_PACK__;
+ UINT16 sl __WLAN_ATTRIB_PACK__;
+ UINT8 bssid[WLAN_BSSID_LEN] __WLAN_ATTRIB_PACK__;
+ UINT16 bcnint __WLAN_ATTRIB_PACK__;
+ UINT16 capinfo __WLAN_ATTRIB_PACK__;
+ hfa384x_bytestr32_t ssid __WLAN_ATTRIB_PACK__;
+ UINT8 supprates[10] __WLAN_ATTRIB_PACK__; /* 802.11 info element */
+ UINT16 proberesp_rate __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_ScanResultSub_t;
+
+typedef struct hfa384x_ScanResult
+{
+ UINT16 rsvd __WLAN_ATTRIB_PACK__;
+ UINT16 scanreason __WLAN_ATTRIB_PACK__;
+ hfa384x_ScanResultSub_t
+ result[HFA384x_SCANRESULT_MAX] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_ScanResult_t;
+
+/*-- Inquiry Frame, Diagnose: ChInfo Results & Subfields--*/
+typedef struct hfa384x_ChInfoResultSub
+{
+ UINT16 chid __WLAN_ATTRIB_PACK__;
+ UINT16 anl __WLAN_ATTRIB_PACK__;
+ UINT16 pnl __WLAN_ATTRIB_PACK__;
+ UINT16 active __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_ChInfoResultSub_t;
+
+#define HFA384x_CHINFORESULT_BSSACTIVE BIT0
+#define HFA384x_CHINFORESULT_PCFACTIVE BIT1
+
+typedef struct hfa384x_ChInfoResult
+{
+ UINT16 scanchannels __WLAN_ATTRIB_PACK__;
+ hfa384x_ChInfoResultSub_t
+ result[HFA384x_CHINFORESULT_MAX] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_ChInfoResult_t;
+
+/*-- Inquiry Frame, Diagnose: Host Scan Results & Subfields--*/
+typedef struct hfa384x_HScanResultSub
+{
+ UINT16 chid __WLAN_ATTRIB_PACK__;
+ UINT16 anl __WLAN_ATTRIB_PACK__;
+ UINT16 sl __WLAN_ATTRIB_PACK__;
+ UINT8 bssid[WLAN_BSSID_LEN] __WLAN_ATTRIB_PACK__;
+ UINT16 bcnint __WLAN_ATTRIB_PACK__;
+ UINT16 capinfo __WLAN_ATTRIB_PACK__;
+ hfa384x_bytestr32_t ssid __WLAN_ATTRIB_PACK__;
+ UINT8 supprates[10] __WLAN_ATTRIB_PACK__; /* 802.11 info element */
+ UINT16 proberesp_rate __WLAN_ATTRIB_PACK__;
+ UINT16 atim __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_HScanResultSub_t;
+
+typedef struct hfa384x_HScanResult
+{
+ UINT16 nresult __WLAN_ATTRIB_PACK__;
+ UINT16 rsvd __WLAN_ATTRIB_PACK__;
+ hfa384x_HScanResultSub_t
+ result[HFA384x_HSCANRESULT_MAX] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_HScanResult_t;
+
+/*-- Unsolicited Frame, MAC Mgmt: LinkStatus --*/
+
+#define HFA384x_LINK_NOTCONNECTED ((UINT16)0)
+#define HFA384x_LINK_CONNECTED ((UINT16)1)
+#define HFA384x_LINK_DISCONNECTED ((UINT16)2)
+#define HFA384x_LINK_AP_CHANGE ((UINT16)3)
+#define HFA384x_LINK_AP_OUTOFRANGE ((UINT16)4)
+#define HFA384x_LINK_AP_INRANGE ((UINT16)5)
+#define HFA384x_LINK_ASSOCFAIL ((UINT16)6)
+
+typedef struct hfa384x_LinkStatus
+{
+ UINT16 linkstatus __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_LinkStatus_t;
+
+
+/*-- Unsolicited Frame, MAC Mgmt: AssociationStatus (--*/
+
+#define HFA384x_ASSOCSTATUS_STAASSOC ((UINT16)1)
+#define HFA384x_ASSOCSTATUS_REASSOC ((UINT16)2)
+#define HFA384x_ASSOCSTATUS_DISASSOC ((UINT16)3)
+#define HFA384x_ASSOCSTATUS_ASSOCFAIL ((UINT16)4)
+#define HFA384x_ASSOCSTATUS_AUTHFAIL ((UINT16)5)
+
+typedef struct hfa384x_AssocStatus
+{
+ UINT16 assocstatus __WLAN_ATTRIB_PACK__;
+ UINT8 sta_addr[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__;
+ /* old_ap_addr is only valid if assocstatus == 2 */
+ UINT8 old_ap_addr[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__;
+ UINT16 reason __WLAN_ATTRIB_PACK__;
+ UINT16 reserved __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_AssocStatus_t;
+
+/*-- Unsolicited Frame, MAC Mgmt: AuthRequest (AP Only) --*/
+
+typedef struct hfa384x_AuthRequest
+{
+ UINT8 sta_addr[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__;
+ UINT16 algorithm __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_AuthReq_t;
+
+/*-- Unsolicited Frame, MAC Mgmt: PSUserCount (AP Only) --*/
+
+typedef struct hfa384x_PSUserCount
+{
+ UINT16 usercnt __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_PSUserCount_t;
+
+/*-- Collection of all Inf frames ---------------*/
+typedef union hfa384x_infodata {
+ hfa384x_CommTallies16_t commtallies16 __WLAN_ATTRIB_PACK__;
+ hfa384x_CommTallies32_t commtallies32 __WLAN_ATTRIB_PACK__;
+ hfa384x_ScanResult_t scanresult __WLAN_ATTRIB_PACK__;
+ hfa384x_ChInfoResult_t chinforesult __WLAN_ATTRIB_PACK__;
+ hfa384x_HScanResult_t hscanresult __WLAN_ATTRIB_PACK__;
+ hfa384x_LinkStatus_t linkstatus __WLAN_ATTRIB_PACK__;
+ hfa384x_AssocStatus_t assocstatus __WLAN_ATTRIB_PACK__;
+ hfa384x_AuthReq_t authreq __WLAN_ATTRIB_PACK__;
+ hfa384x_PSUserCount_t psusercnt __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_infodata_t;
+
+typedef struct hfa384x_InfFrame
+{
+ UINT16 framelen __WLAN_ATTRIB_PACK__;
+ UINT16 infotype __WLAN_ATTRIB_PACK__;
+ hfa384x_infodata_t info __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_InfFrame_t;
+
+#if (WLAN_HOSTIF == WLAN_USB)
+/*--------------------------------------------------------------------
+USB Packet structures and constants.
+--------------------------------------------------------------------*/
+
+/* Should be sent to the ctrlout endpoint */
+#define HFA384x_USB_ENBULKIN 6
+
+/* Should be sent to the bulkout endpoint */
+#define HFA384x_USB_TXFRM 0
+#define HFA384x_USB_CMDREQ 1
+#define HFA384x_USB_WRIDREQ 2
+#define HFA384x_USB_RRIDREQ 3
+#define HFA384x_USB_WMEMREQ 4
+#define HFA384x_USB_RMEMREQ 5
+
+/* Received from the bulkin endpoint */
+#define HFA384x_USB_ISFRM(a) ((a) < 0x7fff)
+#define HFA384x_USB_ISTXFRM(a) (HFA384x_USB_ISFRM((a)) && ((a) & 0x1000))
+#define HFA384x_USB_ISRXFRM(a) (HFA384x_USB_ISFRM((a)) && !((a) & 0x1000))
+#define HFA384x_USB_INFOFRM 0x8000
+#define HFA384x_USB_CMDRESP 0x8001
+#define HFA384x_USB_WRIDRESP 0x8002
+#define HFA384x_USB_RRIDRESP 0x8003
+#define HFA384x_USB_WMEMRESP 0x8004
+#define HFA384x_USB_RMEMRESP 0x8005
+#define HFA384x_USB_BUFAVAIL 0x8006
+#define HFA384x_USB_ERROR 0x8007
+
+/*------------------------------------*/
+/* Request (bulk OUT) packet contents */
+
+typedef struct hfa384x_usb_txfrm {
+ hfa384x_tx_frame_t desc __WLAN_ATTRIB_PACK__;
+ UINT8 data[WLAN_DATA_MAXLEN] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_txfrm_t;
+
+typedef struct hfa384x_usb_cmdreq {
+ UINT16 type __WLAN_ATTRIB_PACK__;
+ UINT16 cmd __WLAN_ATTRIB_PACK__;
+ UINT16 parm0 __WLAN_ATTRIB_PACK__;
+ UINT16 parm1 __WLAN_ATTRIB_PACK__;
+ UINT16 parm2 __WLAN_ATTRIB_PACK__;
+ UINT8 pad[54] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_cmdreq_t;
+
+typedef struct hfa384x_usb_wridreq {
+ UINT16 type __WLAN_ATTRIB_PACK__;
+ UINT16 frmlen __WLAN_ATTRIB_PACK__;
+ UINT16 rid __WLAN_ATTRIB_PACK__;
+ UINT8 data[HFA384x_RIDDATA_MAXLEN] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_wridreq_t;
+
+typedef struct hfa384x_usb_rridreq {
+ UINT16 type __WLAN_ATTRIB_PACK__;
+ UINT16 frmlen __WLAN_ATTRIB_PACK__;
+ UINT16 rid __WLAN_ATTRIB_PACK__;
+ UINT8 pad[58] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_rridreq_t;
+
+typedef struct hfa384x_usb_wmemreq {
+ UINT16 type __WLAN_ATTRIB_PACK__;
+ UINT16 frmlen __WLAN_ATTRIB_PACK__;
+ UINT16 offset __WLAN_ATTRIB_PACK__;
+ UINT16 page __WLAN_ATTRIB_PACK__;
+ UINT8 data[HFA384x_USB_RWMEM_MAXLEN] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_wmemreq_t;
+
+typedef struct hfa384x_usb_rmemreq {
+ UINT16 type __WLAN_ATTRIB_PACK__;
+ UINT16 frmlen __WLAN_ATTRIB_PACK__;
+ UINT16 offset __WLAN_ATTRIB_PACK__;
+ UINT16 page __WLAN_ATTRIB_PACK__;
+ UINT8 pad[56] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_rmemreq_t;
+
+/*------------------------------------*/
+/* Response (bulk IN) packet contents */
+
+typedef struct hfa384x_usb_rxfrm {
+ hfa384x_rx_frame_t desc __WLAN_ATTRIB_PACK__;
+ UINT8 data[WLAN_DATA_MAXLEN] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_rxfrm_t;
+
+typedef struct hfa384x_usb_infofrm {
+ UINT16 type __WLAN_ATTRIB_PACK__;
+ hfa384x_InfFrame_t info __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_infofrm_t;
+
+typedef struct hfa384x_usb_cmdresp {
+ UINT16 type __WLAN_ATTRIB_PACK__;
+ UINT16 status __WLAN_ATTRIB_PACK__;
+ UINT16 resp0 __WLAN_ATTRIB_PACK__;
+ UINT16 resp1 __WLAN_ATTRIB_PACK__;
+ UINT16 resp2 __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_cmdresp_t;
+
+typedef struct hfa384x_usb_wridresp {
+ UINT16 type __WLAN_ATTRIB_PACK__;
+ UINT16 status __WLAN_ATTRIB_PACK__;
+ UINT16 resp0 __WLAN_ATTRIB_PACK__;
+ UINT16 resp1 __WLAN_ATTRIB_PACK__;
+ UINT16 resp2 __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_wridresp_t;
+
+typedef struct hfa384x_usb_rridresp {
+ UINT16 type __WLAN_ATTRIB_PACK__;
+ UINT16 frmlen __WLAN_ATTRIB_PACK__;
+ UINT16 rid __WLAN_ATTRIB_PACK__;
+ UINT8 data[HFA384x_RIDDATA_MAXLEN] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_rridresp_t;
+
+typedef struct hfa384x_usb_wmemresp {
+ UINT16 type __WLAN_ATTRIB_PACK__;
+ UINT16 status __WLAN_ATTRIB_PACK__;
+ UINT16 resp0 __WLAN_ATTRIB_PACK__;
+ UINT16 resp1 __WLAN_ATTRIB_PACK__;
+ UINT16 resp2 __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_wmemresp_t;
+
+typedef struct hfa384x_usb_rmemresp {
+ UINT16 type __WLAN_ATTRIB_PACK__;
+ UINT16 frmlen __WLAN_ATTRIB_PACK__;
+ UINT8 data[HFA384x_USB_RWMEM_MAXLEN] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_rmemresp_t;
+
+typedef struct hfa384x_usb_bufavail {
+ UINT16 type __WLAN_ATTRIB_PACK__;
+ UINT16 frmlen __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_bufavail_t;
+
+typedef struct hfa384x_usb_error {
+ UINT16 type __WLAN_ATTRIB_PACK__;
+ UINT16 errortype __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_usb_error_t;
+
+/*----------------------------------------------------------*/
+/* Unions for packaging all the known packet types together */
+
+typedef union hfa384x_usbout {
+ UINT16 type __WLAN_ATTRIB_PACK__;
+ hfa384x_usb_txfrm_t txfrm __WLAN_ATTRIB_PACK__;
+ hfa384x_usb_cmdreq_t cmdreq __WLAN_ATTRIB_PACK__;
+ hfa384x_usb_wridreq_t wridreq __WLAN_ATTRIB_PACK__;
+ hfa384x_usb_rridreq_t rridreq __WLAN_ATTRIB_PACK__;
+ hfa384x_usb_wmemreq_t wmemreq __WLAN_ATTRIB_PACK__;
+ hfa384x_usb_rmemreq_t rmemreq __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_usbout_t;
+
+typedef union hfa384x_usbin {
+ UINT16 type __WLAN_ATTRIB_PACK__;
+ hfa384x_usb_rxfrm_t rxfrm __WLAN_ATTRIB_PACK__;
+ hfa384x_usb_txfrm_t txfrm __WLAN_ATTRIB_PACK__;
+ hfa384x_usb_infofrm_t infofrm __WLAN_ATTRIB_PACK__;
+ hfa384x_usb_cmdresp_t cmdresp __WLAN_ATTRIB_PACK__;
+ hfa384x_usb_wridresp_t wridresp __WLAN_ATTRIB_PACK__;
+ hfa384x_usb_rridresp_t rridresp __WLAN_ATTRIB_PACK__;
+ hfa384x_usb_wmemresp_t wmemresp __WLAN_ATTRIB_PACK__;
+ hfa384x_usb_rmemresp_t rmemresp __WLAN_ATTRIB_PACK__;
+ hfa384x_usb_bufavail_t bufavail __WLAN_ATTRIB_PACK__;
+ hfa384x_usb_error_t usberror __WLAN_ATTRIB_PACK__;
+ UINT8 boguspad[3000] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_usbin_t;
+
+#endif /* WLAN_USB */
+
+/*--------------------------------------------------------------------
+PD record structures.
+--------------------------------------------------------------------*/
+
+typedef struct hfa384x_pdr_pcb_partnum
+{
+ UINT8 num[8] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_pcb_partnum_t;
+
+typedef struct hfa384x_pdr_pcb_tracenum
+{
+ UINT8 num[8] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_pcb_tracenum_t;
+
+typedef struct hfa384x_pdr_nic_serial
+{
+ UINT8 num[12] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_nic_serial_t;
+
+typedef struct hfa384x_pdr_mkk_measurements
+{
+ double carrier_freq __WLAN_ATTRIB_PACK__;
+ double occupied_band __WLAN_ATTRIB_PACK__;
+ double power_density __WLAN_ATTRIB_PACK__;
+ double tx_spur_f1 __WLAN_ATTRIB_PACK__;
+ double tx_spur_f2 __WLAN_ATTRIB_PACK__;
+ double tx_spur_f3 __WLAN_ATTRIB_PACK__;
+ double tx_spur_f4 __WLAN_ATTRIB_PACK__;
+ double tx_spur_l1 __WLAN_ATTRIB_PACK__;
+ double tx_spur_l2 __WLAN_ATTRIB_PACK__;
+ double tx_spur_l3 __WLAN_ATTRIB_PACK__;
+ double tx_spur_l4 __WLAN_ATTRIB_PACK__;
+ double rx_spur_f1 __WLAN_ATTRIB_PACK__;
+ double rx_spur_f2 __WLAN_ATTRIB_PACK__;
+ double rx_spur_l1 __WLAN_ATTRIB_PACK__;
+ double rx_spur_l2 __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_mkk_measurements_t;
+
+typedef struct hfa384x_pdr_nic_ramsize
+{
+ UINT8 size[12] __WLAN_ATTRIB_PACK__; /* units of KB */
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_nic_ramsize_t;
+
+typedef struct hfa384x_pdr_mfisuprange
+{
+ UINT16 id __WLAN_ATTRIB_PACK__;
+ UINT16 variant __WLAN_ATTRIB_PACK__;
+ UINT16 bottom __WLAN_ATTRIB_PACK__;
+ UINT16 top __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_mfisuprange_t;
+
+typedef struct hfa384x_pdr_cfisuprange
+{
+ UINT16 id __WLAN_ATTRIB_PACK__;
+ UINT16 variant __WLAN_ATTRIB_PACK__;
+ UINT16 bottom __WLAN_ATTRIB_PACK__;
+ UINT16 top __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_cfisuprange_t;
+
+typedef struct hfa384x_pdr_nicid
+{
+ UINT16 id __WLAN_ATTRIB_PACK__;
+ UINT16 variant __WLAN_ATTRIB_PACK__;
+ UINT16 major __WLAN_ATTRIB_PACK__;
+ UINT16 minor __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_nicid_t;
+
+
+typedef struct hfa384x_pdr_refdac_measurements
+{
+ UINT16 value[0] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_refdac_measurements_t;
+
+typedef struct hfa384x_pdr_vgdac_measurements
+{
+ UINT16 value[0] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_vgdac_measurements_t;
+
+typedef struct hfa384x_pdr_level_comp_measurements
+{
+ UINT16 value[0] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_level_compc_measurements_t;
+
+typedef struct hfa384x_pdr_mac_address
+{
+ UINT8 addr[6] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_mac_address_t;
+
+typedef struct hfa384x_pdr_mkk_callname
+{
+ UINT8 callname[8] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_mkk_callname_t;
+
+typedef struct hfa384x_pdr_regdomain
+{
+ UINT16 numdomains __WLAN_ATTRIB_PACK__;
+ UINT16 domain[5] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_regdomain_t;
+
+typedef struct hfa384x_pdr_allowed_channel
+{
+ UINT16 ch_bitmap __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_allowed_channel_t;
+
+typedef struct hfa384x_pdr_default_channel
+{
+ UINT16 channel __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_default_channel_t;
+
+typedef struct hfa384x_pdr_privacy_option
+{
+ UINT16 available __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_privacy_option_t;
+
+typedef struct hfa384x_pdr_temptype
+{
+ UINT16 type __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_temptype_t;
+
+typedef struct hfa384x_pdr_refdac_setup
+{
+ UINT16 ch_value[14] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_refdac_setup_t;
+
+typedef struct hfa384x_pdr_vgdac_setup
+{
+ UINT16 ch_value[14] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_vgdac_setup_t;
+
+typedef struct hfa384x_pdr_level_comp_setup
+{
+ UINT16 ch_value[14] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_level_comp_setup_t;
+
+typedef struct hfa384x_pdr_trimdac_setup
+{
+ UINT16 trimidac __WLAN_ATTRIB_PACK__;
+ UINT16 trimqdac __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_trimdac_setup_t;
+
+typedef struct hfa384x_pdr_ifr_setting
+{
+ UINT16 value[3] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_ifr_setting_t;
+
+typedef struct hfa384x_pdr_rfr_setting
+{
+ UINT16 value[3] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_rfr_setting_t;
+
+typedef struct hfa384x_pdr_hfa3861_baseline
+{
+ UINT16 value[50] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_baseline_t;
+
+typedef struct hfa384x_pdr_hfa3861_shadow
+{
+ UINT32 value[32] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_shadow_t;
+
+typedef struct hfa384x_pdr_hfa3861_ifrf
+{
+ UINT32 value[20] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_ifrf_t;
+
+typedef struct hfa384x_pdr_hfa3861_chcalsp
+{
+ UINT16 value[14] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_chcalsp_t;
+
+typedef struct hfa384x_pdr_hfa3861_chcali
+{
+ UINT16 value[17] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_chcali_t;
+
+typedef struct hfa384x_pdr_hfa3861_nic_config
+{
+ UINT16 config_bitmap __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_nic_config_t;
+
+typedef struct hfa384x_pdr_hfa3861_manf_testsp
+{
+ UINT16 value[30] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_manf_testsp_t;
+
+typedef struct hfa384x_pdr_hfa3861_manf_testi
+{
+ UINT16 value[30] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_manf_testi_t;
+
+typedef struct hfa384x_end_of_pda
+{
+ UINT16 crc __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdr_end_of_pda_t;
+
+typedef struct hfa384x_pdrec
+{
+ UINT16 len __WLAN_ATTRIB_PACK__; /* in words */
+ UINT16 code __WLAN_ATTRIB_PACK__;
+ union pdr {
+ hfa384x_pdr_pcb_partnum_t pcb_partnum __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_pcb_tracenum_t pcb_tracenum __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_nic_serial_t nic_serial __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_mkk_measurements_t mkk_measurements __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_nic_ramsize_t nic_ramsize __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_mfisuprange_t mfisuprange __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_cfisuprange_t cfisuprange __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_nicid_t nicid __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_refdac_measurements_t refdac_measurements __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_vgdac_measurements_t vgdac_measurements __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_level_compc_measurements_t level_compc_measurements __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_mac_address_t mac_address __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_mkk_callname_t mkk_callname __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_regdomain_t regdomain __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_allowed_channel_t allowed_channel __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_default_channel_t default_channel __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_privacy_option_t privacy_option __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_temptype_t temptype __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_refdac_setup_t refdac_setup __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_vgdac_setup_t vgdac_setup __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_level_comp_setup_t level_comp_setup __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_trimdac_setup_t trimdac_setup __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_ifr_setting_t ifr_setting __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_rfr_setting_t rfr_setting __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_hfa3861_baseline_t hfa3861_baseline __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_hfa3861_shadow_t hfa3861_shadow __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_hfa3861_ifrf_t hfa3861_ifrf __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_hfa3861_chcalsp_t hfa3861_chcalsp __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_hfa3861_chcali_t hfa3861_chcali __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_nic_config_t nic_config __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_hfa3861_manf_testsp_t hfa3861_manf_testsp __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_hfa3861_manf_testi_t hfa3861_manf_testi __WLAN_ATTRIB_PACK__;
+ hfa384x_pdr_end_of_pda_t end_of_pda __WLAN_ATTRIB_PACK__;
+ } data __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ hfa384x_pdrec_t;
+
+
+#ifdef __KERNEL__
+/*--------------------------------------------------------------------
+--- MAC state structure, argument to all functions --
+--- Also, a collection of support types --
+--------------------------------------------------------------------*/
+
+struct hfa384x; /* forward declaration,grrr */
+
+typedef void (*ctlx_usercb_t)(
+ struct hfa384x *hw,
+ UINT result,
+ void *ctlxresult,
+ void *usercb_data);
+
+
+#if (WLAN_HOSTIF == WLAN_USB)
+/* USB Control Exchange (CTLX):
+ * A queue of the structure below is maintained for all of the
+ * Request/Response type USB packets supported by Prism2.
+ */
+/* The following hfa384x_async_* structures are arguments to
+ * the usercb() for the different CTLX types.
+ */
+typedef struct hfa384x_async_cmdresult
+{
+ UINT16 status;
+ UINT16 resp0;
+ UINT16 resp1;
+ UINT16 resp2;
+} hfa384x_async_cmdresult_t;
+
+typedef struct hfa384x_async_rridresult
+{
+ UINT16 rid;
+ void *riddata;
+ UINT riddata_len;
+} hfa384x_async_rridresult_t;
+
+typedef struct hfa384x_async_wridresult
+{
+ UINT16 status;
+ UINT16 resp0;
+ UINT16 resp1;
+ UINT16 resp2;
+} hfa384x_async_wridresult_t;
+
+typedef struct hfa384x_async_rmemresult
+{
+} hfa384x_async_rmemresult_t;
+
+typedef struct hfa384x_async_wmemresult
+{
+} hfa384x_async_wmemresult_t;
+
+
+typedef struct hfa384x_usbctlx
+{
+ struct hfa384x_usbctlx *prev, *next;
+ struct urb outurb; /* OUT for req pkt */
+ struct urb inurb; /* IN for resp pkt */
+ hfa384x_usbout_t outbuf; /* pkt buf for OUT */
+ hfa384x_usbin_t inbuf; /* pkt buf for IN(a copy) */
+ struct timer_list reqtimer; /* For IN(response)wait */
+ struct timer_list resptimer; /* For OUT(request) wait */
+ volatile UINT32 state; /* Tracks running state */
+ int wanna_wakeup; /* Flag to wakeup sync calls */
+ int is_async; /* Q'd by sync or async call */
+ ctlx_usercb_t usercb; /* Async user callback, */
+ void *usercb_data; /* at CTLX completion */
+ int variant; /* Identifies cmd variant */
+} hfa384x_usbctlx_t;
+
+/* hfa384x_usbcmd_t.state values */
+#define HFA384x_USBCTLX_START 9 /* Start state, not Q'd */
+#define HFA384x_USBCTLX_QUEUED 1 /* Queued, data valid */
+#define HFA384x_USBCTLX_REQ_SUBMITTED 2 /* OUT URB submitted */
+#define HFA384x_USBCTLX_REQ_COMPLETE 3 /* OUT URB complete */
+#define HFA384x_USBCTLX_RESP_RECEIVED 4 /* IN URB received */
+#define HFA384x_USBCTLX_REQ_TIMEOUT 5 /* Timer expired waiting for OUT cb*/
+#define HFA384x_USBCTLX_REQ_FAILED 6 /* OUT URB completed w/ error */
+#define HFA384x_USBCTLX_RESP_TIMEOUT 7 /* Timer expired waiting for IN cb */
+#define HFA384x_USBCTLX_REQSUBMIT_FAIL 8 /* Timer expired waiting for IN cb */
+#define HFA384x_USBCTLX_COMPLETE 0 /* Exchange successfully complete */
+
+typedef struct hfa384x_usbctlxq
+{
+ spinlock_t lock;
+ hfa384x_usbctlx_t *head;
+ hfa384x_usbctlx_t *tail;
+} hfa384x_usbctlxq_t;
+#endif
+
+typedef struct hfa484x_metacmd
+{
+ UINT16 cmd;
+
+ UINT16 parm0;
+ UINT16 parm1;
+ UINT16 parm2;
+
+ UINT16 status; /* in host order */
+
+ UINT16 resp0; /* in host order */
+ UINT16 resp1; /* in host order */
+ UINT16 resp2; /* in host order */
+#if 0 //XXX cmd irq stuff
+ UINT16 bulkid; /* what RID/FID to copy down. */
+ int bulklen; /* how much to copy from BAP */
+ char *bulkdata; /* And to where? */
+#endif
+} hfa384x_metacmd_t;
+
+typedef struct hfa384x
+{
+#if (WLAN_HOSTIF != WLAN_USB)
+ /* Resource config */
+ UINT32 iobase;
+ UINT32 membase;
+ UINT32 irq;
+#else
+ /* USB support data */
+ struct usb_device *usb;
+ void *usbcontext; /* actually a wlandev */
+ struct urb rx_urb;
+ struct urb tx_urb;
+ struct urb int_urb;
+ hfa384x_usbin_t rxbuff;
+ hfa384x_usbout_t txbuff;
+ UINT16 intbuff[4];
+ int rxurb_posted;
+ hfa384x_usbctlxq_t ctlxq;
+ int endp_in;
+ int endp_out;
+#endif /* !USB */
+
+ int sniff_fcs;
+ int sniff_channel;
+ int sniff_truncate;
+
+ wait_queue_head_t cmdq; /* wait queue itself */
+
+ /* Controller state */
+ UINT32 state;
+ UINT32 hwremoved;
+ UINT32 isap;
+ UINT8 port_enabled[HFA384x_NUMPORTS_MAX];
+#if (WLAN_HOSTIF != WLAN_USB)
+ UINT auxen;
+#endif /* !USB */
+
+ /* Download support */
+ UINT dlstate;
+ hfa384x_downloadbuffer_t bufinfo;
+ UINT16 dltimeout;
+
+#if (WLAN_HOSTIF != WLAN_USB)
+ spinlock_t cmdlock;
+ int cmdflag; /* wait queue flag */
+ hfa384x_metacmd_t *cmddata; /* for our async callback */
+
+ /* BAP support */
+ spinlock_t baplock;
+
+ /* MAC buffer ids */
+ spinlock_t txfid_lock;
+ UINT16 txfid_head;
+ UINT16 txfid_tail;
+ UINT txfid_N;
+ UINT16 txfid_queue[HFA384x_DRVR_FIDSTACKLEN_MAX];
+ UINT16 infofid;
+ struct semaphore infofid_sem;
+#endif /* !USB */
+
+ int scanflag; /* to signal scan comlete */
+ int join_ap; /* are we joined to a specific ap */
+ int join_retries; /* number of join retries till we fail */
+ hfa384x_JoinRequest_data_t joinreq; /* join request saved data */
+} hfa384x_t;
+
+/*=============================================================*/
+/*--- Function Declarations -----------------------------------*/
+/*=============================================================*/
+#if (WLAN_HOSTIF == WLAN_USB)
+void
+hfa384x_create(
+ hfa384x_t *hw,
+ struct usb_device *usb,
+ void *usbcontext);
+#else
+void
+hfa384x_create(
+ hfa384x_t *hw,
+ UINT irq,
+ UINT32 iobase,
+ UINT32 membase);
+#endif
+
+void hfa384x_destroy(hfa384x_t *hw);
+
+void hfa384x_hwremoved(hfa384x_t *hw);
+
+void
+hfa384x_interrupt(
+ int irq,
+ void *dev_id,
+ struct pt_regs *regs);
+int
+hfa384x_corereset( hfa384x_t *hw, int holdtime, int settletime);
+int
+hfa384x_drvr_chinforesults( hfa384x_t *hw);
+int
+hfa384x_drvr_commtallies( hfa384x_t *hw);
+int
+hfa384x_drvr_disable(hfa384x_t *hw, UINT16 macport);
+int
+hfa384x_drvr_enable(hfa384x_t *hw, UINT16 macport);
+int
+hfa384x_drvr_flashdl_enable(hfa384x_t *hw);
+int
+hfa384x_drvr_flashdl_disable(hfa384x_t *hw);
+int
+hfa384x_drvr_flashdl_write(hfa384x_t *hw, UINT32 daddr, void* buf, UINT32 len);
+int
+hfa384x_drvr_getconfig(hfa384x_t *hw, UINT16 rid, void *buf, UINT16 len);
+int
+hfa384x_drvr_getconfig16(hfa384x_t *hw, UINT16 rid, void *val);
+int
+hfa384x_drvr_getconfig32(hfa384x_t *hw, UINT16 rid, void *val);
+void
+hfa384x_drvr_getconfig_async(
+ hfa384x_t *hw,
+ UINT16 rid,
+ ctlx_usercb_t usercb,
+ void *usercb_data);
+int
+hfa384x_drvr_handover( hfa384x_t *hw, UINT8 *addr);
+int
+hfa384x_drvr_hostscanresults( hfa384x_t *hw);
+int
+hfa384x_drvr_low_level(hfa384x_t *hw, hfa384x_metacmd_t *cmd);
+int
+hfa384x_drvr_mmi_read(hfa384x_t *hw, UINT32 address, UINT32 *result);
+int
+hfa384x_drvr_mmi_write(hfa384x_t *hw, UINT32 address, UINT32 data);
+int
+hfa384x_drvr_ramdl_enable(hfa384x_t *hw, UINT32 exeaddr);
+int
+hfa384x_drvr_ramdl_disable(hfa384x_t *hw);
+int
+hfa384x_drvr_ramdl_write(hfa384x_t *hw, UINT32 daddr, void* buf, UINT32 len);
+int
+hfa384x_drvr_readpda(hfa384x_t *hw, void *buf, UINT len);
+int
+hfa384x_drvr_scanresults( hfa384x_t *hw);
+int
+hfa384x_drvr_setconfig(hfa384x_t *hw, UINT16 rid, void *buf, UINT16 len);
+int
+hfa384x_drvr_setconfig16(hfa384x_t *hw, UINT16 rid, UINT16 *val);
+int
+hfa384x_drvr_setconfig32(hfa384x_t *hw, UINT16 rid, UINT32 *val);
+void
+hfa384x_drvr_setconfig_async(
+ hfa384x_t *hw,
+ UINT16 rid,
+ void *buf,
+ UINT16 len,
+ ctlx_usercb_t usercb,
+ void *usercb_data);
+int
+hfa384x_drvr_start(hfa384x_t *hw);
+int
+hfa384x_drvr_stop(hfa384x_t *hw);
+int
+hfa384x_drvr_txframe(hfa384x_t *hw, struct sk_buff *skb, p80211_hdr_t *p80211_hdr, p80211_metawep_t *p80211_wep);
+
+
+int
+hfa384x_cmd_initialize(hfa384x_t *hw);
+int
+hfa384x_cmd_enable(hfa384x_t *hw, UINT16 macport);
+int
+hfa384x_cmd_disable(hfa384x_t *hw, UINT16 macport);
+int
+hfa384x_cmd_diagnose(hfa384x_t *hw);
+int
+hfa384x_cmd_allocate(hfa384x_t *hw, UINT16 len);
+int
+hfa384x_cmd_transmit(hfa384x_t *hw, UINT16 reclaim, UINT16 qos, UINT16 fid);
+int
+hfa384x_cmd_clearpersist(hfa384x_t *hw, UINT16 fid);
+int
+hfa384x_cmd_notify(hfa384x_t *hw, UINT16 reclaim, UINT16 fid, void *buf, UINT16 len);
+int
+hfa384x_cmd_inquire(hfa384x_t *hw, UINT16 fid);
+int
+hfa384x_cmd_access(hfa384x_t *hw, UINT16 write, UINT16 rid, void *buf, UINT16 len);
+int
+hfa384x_cmd_monitor(hfa384x_t *hw, UINT16 enable);
+int
+hfa384x_cmd_download(
+ hfa384x_t *hw,
+ UINT16 mode,
+ UINT16 lowaddr,
+ UINT16 highaddr,
+ UINT16 codelen);
+int
+hfa384x_cmd_aux_enable(hfa384x_t *hw);
+int
+hfa384x_cmd_aux_disable(hfa384x_t *hw);
+int
+hfa384x_copy_from_bap(
+ hfa384x_t *hw,
+ UINT16 bap,
+ UINT16 id,
+ UINT16 offset,
+ void *buf,
+ UINT len);
+int
+hfa384x_copy_to_bap(
+ hfa384x_t *hw,
+ UINT16 bap,
+ UINT16 id,
+ UINT16 offset,
+ void *buf,
+ UINT len);
+void
+hfa384x_copy_from_aux(
+ hfa384x_t *hw,
+ UINT32 cardaddr,
+ UINT32 auxctl,
+ void *buf,
+ UINT len);
+void
+hfa384x_copy_to_aux(
+ hfa384x_t *hw,
+ UINT32 cardaddr,
+ UINT32 auxctl,
+ void *buf,
+ UINT len);
+
+#if (WLAN_HOSTIF != WLAN_USB)
+
+/*
+ HFA384x is a LITTLE ENDIAN part.
+
+ the get/setreg functions implicitly byte-swap the data to LE.
+ the _noswap variants do not perform a byte-swap on the data.
+*/
+
+static inline UINT16
+__hfa384x_getreg(hfa384x_t *hw, UINT reg);
+
+static inline void
+__hfa384x_setreg(hfa384x_t *hw, UINT16 val, UINT reg);
+
+static inline UINT16
+__hfa384x_getreg_noswap(hfa384x_t *hw, UINT reg);
+
+static inline void
+__hfa384x_setreg_noswap(hfa384x_t *hw, UINT16 val, UINT reg);
+
+#ifdef REVERSE_ENDIAN
+#define hfa384x_getreg __hfa384x_getreg_noswap
+#define hfa384x_setreg __hfa384x_setreg_noswap
+#define hfa384x_getreg_noswap __hfa384x_getreg
+#define hfa384x_setreg_noswap __hfa384x_setreg
+#else
+#define hfa384x_getreg __hfa384x_getreg
+#define hfa384x_setreg __hfa384x_setreg
+#define hfa384x_getreg_noswap __hfa384x_getreg_noswap
+#define hfa384x_setreg_noswap __hfa384x_setreg_noswap
+#endif
+
+/*----------------------------------------------------------------
+* hfa384x_getreg
+*
+* Retrieve the value of one of the MAC registers. Done here
+* because different PRISM2 MAC parts use different buses and such.
+* NOTE: This function returns the value in HOST ORDER!!!!!!
+*
+* Arguments:
+* hw MAC part structure
+* reg Register identifier (offset for I/O based i/f)
+*
+* Returns:
+* Value from the register in HOST ORDER!!!!
+----------------------------------------------------------------*/
+static inline UINT16
+__hfa384x_getreg(hfa384x_t *hw, UINT reg)
+{
+/* printk(KERN_DEBUG "Reading from 0x%0x\n", hw->membase + reg); */
+#if ((WLAN_HOSTIF == WLAN_PCMCIA) || (WLAN_HOSTIF == WLAN_PLX))
+ return wlan_inw_le16_to_cpu(hw->iobase+reg);
+#elif (WLAN_HOSTIF == WLAN_PCI)
+ return __le16_to_cpu(readw(hw->membase + reg));
+#endif
+}
+
+/*----------------------------------------------------------------
+* hfa384x_setreg
+*
+* Set the value of one of the MAC registers. Done here
+* because different PRISM2 MAC parts use different buses and such.
+* NOTE: This function assumes the value is in HOST ORDER!!!!!!
+*
+* Arguments:
+* hw MAC part structure
+* val Value, in HOST ORDER!!, to put in the register
+* reg Register identifier (offset for I/O based i/f)
+*
+* Returns:
+* Nothing
+----------------------------------------------------------------*/
+static inline void
+__hfa384x_setreg(hfa384x_t *hw, UINT16 val, UINT reg)
+{
+#if ((WLAN_HOSTIF == WLAN_PCMCIA) || (WLAN_HOSTIF == WLAN_PLX))
+ wlan_outw_cpu_to_le16( val, hw->iobase + reg);
+ return;
+#elif (WLAN_HOSTIF == WLAN_PCI)
+ writew(__cpu_to_le16(val), hw->membase + reg);
+ return;
+#endif
+}
+
+
+/*----------------------------------------------------------------
+* hfa384x_getreg_noswap
+*
+* Retrieve the value of one of the MAC registers. Done here
+* because different PRISM2 MAC parts use different buses and such.
+*
+* Arguments:
+* hw MAC part structure
+* reg Register identifier (offset for I/O based i/f)
+*
+* Returns:
+* Value from the register.
+----------------------------------------------------------------*/
+static inline UINT16
+__hfa384x_getreg_noswap(hfa384x_t *hw, UINT reg)
+{
+#if ((WLAN_HOSTIF == WLAN_PCMCIA) || (WLAN_HOSTIF == WLAN_PLX))
+ return wlan_inw(hw->iobase+reg);
+#elif (WLAN_HOSTIF == WLAN_PCI)
+ return readw(hw->membase + reg);
+#endif
+}
+
+
+/*----------------------------------------------------------------
+* hfa384x_setreg_noswap
+*
+* Set the value of one of the MAC registers. Done here
+* because different PRISM2 MAC parts use different buses and such.
+*
+* Arguments:
+* hw MAC part structure
+* val Value to put in the register
+* reg Register identifier (offset for I/O based i/f)
+*
+* Returns:
+* Nothing
+----------------------------------------------------------------*/
+static inline void
+__hfa384x_setreg_noswap(hfa384x_t *hw, UINT16 val, UINT reg)
+{
+#if ((WLAN_HOSTIF == WLAN_PCMCIA) || (WLAN_HOSTIF == WLAN_PLX))
+ wlan_outw( val, hw->iobase + reg);
+ return;
+#elif (WLAN_HOSTIF == WLAN_PCI)
+ writew(val, hw->membase + reg);
+ return;
+#endif
+}
+
+#endif /* WLAN_HOSTIF != WLAN_USB */
+#endif /* __KERNEL__ */
+
+#endif /* _HFA384x_H */
diff --git a/src/drivers/net/mtd80x.c b/src/drivers/net/mtd80x.c
new file mode 100644
index 00000000..a09fdfba
--- /dev/null
+++ b/src/drivers/net/mtd80x.c
@@ -0,0 +1,1096 @@
+/**************************************************************************
+*
+* mtd80x.c: Etherboot device driver for the mtd80x Ethernet chip.
+* Written 2004-2004 by Erdem Güven <zuencap@yahoo.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code based on:
+* fealnx.c: A Linux device driver for the mtd80x Ethernet chip
+* Written 1998-2000 by Donald Becker
+*
+***************************************************************************/
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include "pci.h"
+
+#if 0
+#define DBGPRNT( x ) printf x
+#else
+#define DBGPRNT( x )
+#endif
+
+typedef unsigned char u8;
+typedef signed char s8;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned int u32;
+typedef signed int s32;
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+#define get_unaligned(ptr) (*(ptr))
+
+
+/* Operational parameters that are set at compile time. */
+
+/* Keep the ring sizes a power of two for compile efficiency. */
+/* The compiler will convert <unsigned>'%'<2^N> into a bit mask. */
+/* Making the Tx ring too large decreases the effectiveness of channel */
+/* bonding and packet priority. */
+/* There are no ill effects from too-large receive rings. */
+#define TX_RING_SIZE 2
+#define TX_QUEUE_LEN 10 /* Limit ring entries actually used. */
+#define RX_RING_SIZE 4
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define HZ 100
+#define TX_TIME_OUT (6*HZ)
+
+/* Allocation size of Rx buffers with normal sized Ethernet frames.
+ Do not change this value without good reason. This is not a limit,
+ but a way to keep a consistent allocation size among drivers.
+ */
+#define PKT_BUF_SZ 1536
+
+/* Generic MII registers. */
+
+#define MII_BMCR 0x00 /* Basic mode control register */
+#define MII_BMSR 0x01 /* Basic mode status register */
+#define MII_PHYSID1 0x02 /* PHYS ID 1 */
+#define MII_PHYSID2 0x03 /* PHYS ID 2 */
+#define MII_ADVERTISE 0x04 /* Advertisement control reg */
+#define MII_LPA 0x05 /* Link partner ability reg */
+#define MII_EXPANSION 0x06 /* Expansion register */
+#define MII_DCOUNTER 0x12 /* Disconnect counter */
+#define MII_FCSCOUNTER 0x13 /* False carrier counter */
+#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */
+#define MII_RERRCOUNTER 0x15 /* Receive error counter */
+#define MII_SREVISION 0x16 /* Silicon revision */
+#define MII_RESV1 0x17 /* Reserved... */
+#define MII_LBRERROR 0x18 /* Lpback, rx, bypass error */
+#define MII_PHYADDR 0x19 /* PHY address */
+#define MII_RESV2 0x1a /* Reserved... */
+#define MII_TPISTATUS 0x1b /* TPI status for 10mbps */
+#define MII_NCONFIG 0x1c /* Network interface config */
+
+/* Basic mode control register. */
+#define BMCR_RESV 0x007f /* Unused... */
+#define BMCR_CTST 0x0080 /* Collision test */
+#define BMCR_FULLDPLX 0x0100 /* Full duplex */
+#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */
+#define BMCR_ISOLATE 0x0400 /* Disconnect DP83840 from MII */
+#define BMCR_PDOWN 0x0800 /* Powerdown the DP83840 */
+#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */
+#define BMCR_SPEED100 0x2000 /* Select 100Mbps */
+#define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */
+#define BMCR_RESET 0x8000 /* Reset the DP83840 */
+
+/* Basic mode status register. */
+#define BMSR_ERCAP 0x0001 /* Ext-reg capability */
+#define BMSR_JCD 0x0002 /* Jabber detected */
+#define BMSR_LSTATUS 0x0004 /* Link status */
+#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */
+#define BMSR_RFAULT 0x0010 /* Remote fault detected */
+#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */
+#define BMSR_RESV 0x07c0 /* Unused... */
+#define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */
+#define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */
+#define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */
+#define BMSR_100FULL 0x4000 /* Can do 100mbps, full-duplex */
+#define BMSR_100BASE4 0x8000 /* Can do 100mbps, 4k packets */
+
+/* Advertisement control register. */
+#define ADVERTISE_SLCT 0x001f /* Selector bits */
+#define ADVERTISE_CSMA 0x0001 /* Only selector supported */
+#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */
+#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */
+#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */
+#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */
+#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */
+#define ADVERTISE_RESV 0x1c00 /* Unused... */
+#define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */
+#define ADVERTISE_LPACK 0x4000 /* Ack link partners response */
+#define ADVERTISE_NPAGE 0x8000 /* Next page bit */
+
+#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \
+ ADVERTISE_CSMA)
+#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \
+ ADVERTISE_100HALF | ADVERTISE_100FULL)
+
+/* for different PHY */
+enum phy_type_flags {
+ MysonPHY = 1,
+ AhdocPHY = 2,
+ SeeqPHY = 3,
+ MarvellPHY = 4,
+ Myson981 = 5,
+ LevelOnePHY = 6,
+ OtherPHY = 10,
+};
+
+/* A chip capabilities table*/
+enum chip_capability_flags {
+ HAS_MII_XCVR,
+ HAS_CHIP_XCVR,
+};
+
+static
+struct chip_info
+{
+ u16 dev_id;
+ int flag;
+}
+mtd80x_chips[] = {
+ {0x0800, HAS_MII_XCVR},
+ {0x0803, HAS_CHIP_XCVR},
+ {0x0891, HAS_MII_XCVR}
+ };
+static int chip_cnt = sizeof( mtd80x_chips ) / sizeof( struct chip_info );
+
+/* Offsets to the Command and Status Registers. */
+enum mtd_offsets {
+ PAR0 = 0x0, /* physical address 0-3 */
+ PAR1 = 0x04, /* physical address 4-5 */
+ MAR0 = 0x08, /* multicast address 0-3 */
+ MAR1 = 0x0C, /* multicast address 4-7 */
+ FAR0 = 0x10, /* flow-control address 0-3 */
+ FAR1 = 0x14, /* flow-control address 4-5 */
+ TCRRCR = 0x18, /* receive & transmit configuration */
+ BCR = 0x1C, /* bus command */
+ TXPDR = 0x20, /* transmit polling demand */
+ RXPDR = 0x24, /* receive polling demand */
+ RXCWP = 0x28, /* receive current word pointer */
+ TXLBA = 0x2C, /* transmit list base address */
+ RXLBA = 0x30, /* receive list base address */
+ ISR = 0x34, /* interrupt status */
+ IMR = 0x38, /* interrupt mask */
+ FTH = 0x3C, /* flow control high/low threshold */
+ MANAGEMENT = 0x40, /* bootrom/eeprom and mii management */
+ TALLY = 0x44, /* tally counters for crc and mpa */
+ TSR = 0x48, /* tally counter for transmit status */
+ BMCRSR = 0x4c, /* basic mode control and status */
+ PHYIDENTIFIER = 0x50, /* phy identifier */
+ ANARANLPAR = 0x54, /* auto-negotiation advertisement and link
+ partner ability */
+ ANEROCR = 0x58, /* auto-negotiation expansion and pci conf. */
+ BPREMRPSR = 0x5c, /* bypass & receive error mask and phy status */
+};
+
+/* Bits in the interrupt status/enable registers. */
+/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */
+enum intr_status_bits {
+ RFCON = 0x00020000, /* receive flow control xon packet */
+ RFCOFF = 0x00010000, /* receive flow control xoff packet */
+ LSCStatus = 0x00008000, /* link status change */
+ ANCStatus = 0x00004000, /* autonegotiation completed */
+ FBE = 0x00002000, /* fatal bus error */
+ FBEMask = 0x00001800, /* mask bit12-11 */
+ ParityErr = 0x00000000, /* parity error */
+ TargetErr = 0x00001000, /* target abort */
+ MasterErr = 0x00000800, /* master error */
+ TUNF = 0x00000400, /* transmit underflow */
+ ROVF = 0x00000200, /* receive overflow */
+ ETI = 0x00000100, /* transmit early int */
+ ERI = 0x00000080, /* receive early int */
+ CNTOVF = 0x00000040, /* counter overflow */
+ RBU = 0x00000020, /* receive buffer unavailable */
+ TBU = 0x00000010, /* transmit buffer unavilable */
+ TI = 0x00000008, /* transmit interrupt */
+ RI = 0x00000004, /* receive interrupt */
+ RxErr = 0x00000002, /* receive error */
+};
+
+/* Bits in the NetworkConfig register. */
+enum rx_mode_bits {
+ RxModeMask = 0xe0,
+ AcceptAllPhys = 0x80, /* promiscuous mode */
+ AcceptBroadcast = 0x40, /* accept broadcast */
+ AcceptMulticast = 0x20, /* accept mutlicast */
+ AcceptRunt = 0x08, /* receive runt pkt */
+ ALP = 0x04, /* receive long pkt */
+ AcceptErr = 0x02, /* receive error pkt */
+
+ AcceptMyPhys = 0x00000000,
+ RxEnable = 0x00000001,
+ RxFlowCtrl = 0x00002000,
+ TxEnable = 0x00040000,
+ TxModeFDX = 0x00100000,
+ TxThreshold = 0x00e00000,
+
+ PS1000 = 0x00010000,
+ PS10 = 0x00080000,
+ FD = 0x00100000,
+};
+
+/* Bits in network_desc.status */
+enum rx_desc_status_bits {
+ RXOWN = 0x80000000, /* own bit */
+ FLNGMASK = 0x0fff0000, /* frame length */
+ FLNGShift = 16,
+ MARSTATUS = 0x00004000, /* multicast address received */
+ BARSTATUS = 0x00002000, /* broadcast address received */
+ PHYSTATUS = 0x00001000, /* physical address received */
+ RXFSD = 0x00000800, /* first descriptor */
+ RXLSD = 0x00000400, /* last descriptor */
+ ErrorSummary = 0x80, /* error summary */
+ RUNT = 0x40, /* runt packet received */
+ LONG = 0x20, /* long packet received */
+ FAE = 0x10, /* frame align error */
+ CRC = 0x08, /* crc error */
+ RXER = 0x04, /* receive error */
+};
+
+enum rx_desc_control_bits {
+ RXIC = 0x00800000, /* interrupt control */
+ RBSShift = 0,
+};
+
+enum tx_desc_status_bits {
+ TXOWN = 0x80000000, /* own bit */
+ JABTO = 0x00004000, /* jabber timeout */
+ CSL = 0x00002000, /* carrier sense lost */
+ LC = 0x00001000, /* late collision */
+ EC = 0x00000800, /* excessive collision */
+ UDF = 0x00000400, /* fifo underflow */
+ DFR = 0x00000200, /* deferred */
+ HF = 0x00000100, /* heartbeat fail */
+ NCRMask = 0x000000ff, /* collision retry count */
+ NCRShift = 0,
+};
+
+enum tx_desc_control_bits {
+ TXIC = 0x80000000, /* interrupt control */
+ ETIControl = 0x40000000, /* early transmit interrupt */
+ TXLD = 0x20000000, /* last descriptor */
+ TXFD = 0x10000000, /* first descriptor */
+ CRCEnable = 0x08000000, /* crc control */
+ PADEnable = 0x04000000, /* padding control */
+ RetryTxLC = 0x02000000, /* retry late collision */
+ PKTSMask = 0x3ff800, /* packet size bit21-11 */
+ PKTSShift = 11,
+ TBSMask = 0x000007ff, /* transmit buffer bit 10-0 */
+ TBSShift = 0,
+};
+
+/* BootROM/EEPROM/MII Management Register */
+#define MASK_MIIR_MII_READ 0x00000000
+#define MASK_MIIR_MII_WRITE 0x00000008
+#define MASK_MIIR_MII_MDO 0x00000004
+#define MASK_MIIR_MII_MDI 0x00000002
+#define MASK_MIIR_MII_MDC 0x00000001
+
+/* ST+OP+PHYAD+REGAD+TA */
+#define OP_READ 0x6000 /* ST:01+OP:10+PHYAD+REGAD+TA:Z0 */
+#define OP_WRITE 0x5002 /* ST:01+OP:01+PHYAD+REGAD+TA:10 */
+
+/* ------------------------------------------------------------------------- */
+/* Constants for Myson PHY */
+/* ------------------------------------------------------------------------- */
+#define MysonPHYID 0xd0000302
+/* 89-7-27 add, (begin) */
+#define MysonPHYID0 0x0302
+#define StatusRegister 18
+#define SPEED100 0x0400 // bit10
+#define FULLMODE 0x0800 // bit11
+/* 89-7-27 add, (end) */
+
+/* ------------------------------------------------------------------------- */
+/* Constants for Seeq 80225 PHY */
+/* ------------------------------------------------------------------------- */
+#define SeeqPHYID0 0x0016
+
+#define MIIRegister18 18
+#define SPD_DET_100 0x80
+#define DPLX_DET_FULL 0x40
+
+/* ------------------------------------------------------------------------- */
+/* Constants for Ahdoc 101 PHY */
+/* ------------------------------------------------------------------------- */
+#define AhdocPHYID0 0x0022
+
+#define DiagnosticReg 18
+#define DPLX_FULL 0x0800
+#define Speed_100 0x0400
+
+/* 89/6/13 add, */
+/* -------------------------------------------------------------------------- */
+/* Constants */
+/* -------------------------------------------------------------------------- */
+#define MarvellPHYID0 0x0141
+#define LevelOnePHYID0 0x0013
+
+#define MII1000BaseTControlReg 9
+#define MII1000BaseTStatusReg 10
+#define SpecificReg 17
+
+/* for 1000BaseT Control Register */
+#define PHYAbletoPerform1000FullDuplex 0x0200
+#define PHYAbletoPerform1000HalfDuplex 0x0100
+#define PHY1000AbilityMask 0x300
+
+// for phy specific status register, marvell phy.
+#define SpeedMask 0x0c000
+#define Speed_1000M 0x08000
+#define Speed_100M 0x4000
+#define Speed_10M 0
+#define Full_Duplex 0x2000
+
+// 89/12/29 add, for phy specific status register, levelone phy, (begin)
+#define LXT1000_100M 0x08000
+#define LXT1000_1000M 0x0c000
+#define LXT1000_Full 0x200
+// 89/12/29 add, for phy specific status register, levelone phy, (end)
+
+#if 0
+/* for 3-in-1 case */
+#define PS10 0x00080000
+#define FD 0x00100000
+#define PS1000 0x00010000
+#endif
+
+/* for PHY */
+#define LinkIsUp 0x0004
+#define LinkIsUp2 0x00040000
+
+/* Create a static buffer of size PKT_BUF_SZ for each
+TX Descriptor. All descriptors point to a
+part of this buffer */
+static u8 txb[PKT_BUF_SZ * TX_RING_SIZE]
+__attribute__ ((aligned(8)));
+
+/* Create a static buffer of size PKT_BUF_SZ for each
+RX Descriptor All descriptors point to a
+part of this buffer */
+static u8 rxb[PKT_BUF_SZ * RX_RING_SIZE]
+__attribute__ ((aligned(8)));
+
+/* The Tulip Rx and Tx buffer descriptors. */
+struct mtd_desc
+{
+ s32 status;
+ s32 control;
+ u32 buffer;
+ u32 next_desc;
+ struct mtd_desc *next_desc_logical;
+ u8* skbuff;
+ u32 reserved1;
+ u32 reserved2;
+};
+
+struct mtd_private
+{
+ struct mtd_desc rx_ring[RX_RING_SIZE];
+ struct mtd_desc tx_ring[TX_RING_SIZE];
+
+ /* Frequently used values: keep some adjacent for cache effect. */
+ int flags;
+ struct pci_dev *pci_dev;
+ unsigned long crvalue;
+ unsigned long bcrvalue;
+ /*unsigned long imrvalue;*/
+ struct mtd_desc *cur_rx;
+ struct mtd_desc *lack_rxbuf;
+ int really_rx_count;
+ struct mtd_desc *cur_tx;
+ struct mtd_desc *cur_tx_copy;
+ int really_tx_count;
+ int free_tx_count;
+ unsigned int rx_buf_sz; /* Based on MTU+slack. */
+
+ /* These values are keep track of the transceiver/media in use. */
+ unsigned int linkok;
+ unsigned int line_speed;
+ unsigned int duplexmode;
+ unsigned int default_port:
+ 4; /* Last dev->if_port value. */
+ unsigned int PHYType;
+
+ /* MII transceiver section. */
+ int mii_cnt; /* MII device addresses. */
+ unsigned char phys[1]; /* MII device addresses. */
+
+ /*other*/
+ const char *nic_name;
+ int ioaddr;
+ u16 dev_id;
+};
+
+static struct mtd_private mtdx;
+
+static int mdio_read(struct nic * , int phy_id, int location);
+static void mdio_write(struct nic * , int phy_id, int location, int value);
+static void getlinktype(struct nic * );
+static void getlinkstatus(struct nic * );
+static void set_rx_mode(struct nic *);
+
+/**************************************************************************
+ * init_ring - setup the tx and rx descriptors
+ *************************************************************************/
+static void init_ring(struct nic *nic)
+{
+ int i;
+
+ mtdx.cur_rx = &mtdx.rx_ring[0];
+
+ mtdx.rx_buf_sz = PKT_BUF_SZ;
+ /*mtdx.rx_head_desc = &mtdx.rx_ring[0];*/
+
+ /* Initialize all Rx descriptors. */
+ /* Fill in the Rx buffers. Handle allocation failure gracefully. */
+ for (i = 0; i < RX_RING_SIZE; i++)
+ {
+ mtdx.rx_ring[i].status = RXOWN;
+ mtdx.rx_ring[i].control = mtdx.rx_buf_sz << RBSShift;
+ mtdx.rx_ring[i].next_desc = virt_to_le32desc(&mtdx.rx_ring[i+1]);
+ mtdx.rx_ring[i].next_desc_logical = &mtdx.rx_ring[i+1];
+ mtdx.rx_ring[i].buffer = virt_to_le32desc(&rxb[i * PKT_BUF_SZ]);
+ mtdx.rx_ring[i].skbuff = &rxb[i * PKT_BUF_SZ];
+ }
+ /* Mark the last entry as wrapping the ring. */
+ mtdx.rx_ring[i-1].next_desc = virt_to_le32desc(&mtdx.rx_ring[0]);
+ mtdx.rx_ring[i-1].next_desc_logical = &mtdx.rx_ring[0];
+
+ /* We only use one transmit buffer, but two
+ * descriptors so transmit engines have somewhere
+ * to point should they feel the need */
+ mtdx.tx_ring[0].status = 0x00000000;
+ mtdx.tx_ring[0].buffer = virt_to_bus(&txb[0]);
+ mtdx.tx_ring[0].next_desc = virt_to_le32desc(&mtdx.tx_ring[1]);
+
+ /* This descriptor is never used */
+ mtdx.tx_ring[1].status = 0x00000000;
+ mtdx.tx_ring[1].buffer = 0; /*virt_to_bus(&txb[1]); */
+ mtdx.tx_ring[1].next_desc = virt_to_le32desc(&mtdx.tx_ring[0]);
+
+ return;
+}
+
+/**************************************************************************
+RESET - Reset Adapter
+***************************************************************************/
+static void mtd_reset(struct nic *nic)
+{
+ /* Reset the chip to erase previous misconfiguration. */
+ outl(0x00000001, mtdx.ioaddr + BCR);
+
+ init_ring(nic);
+
+ outl(virt_to_bus(mtdx.rx_ring), mtdx.ioaddr + RXLBA);
+ outl(virt_to_bus(mtdx.tx_ring), mtdx.ioaddr + TXLBA);
+
+ /* Initialize other registers. */
+ /* Configure the PCI bus bursts and FIFO thresholds. */
+ mtdx.bcrvalue = 0x10; /* little-endian, 8 burst length */
+ mtdx.crvalue = 0xa00; /* rx 128 burst length */
+
+ if ( mtdx.dev_id == 0x891 ) {
+ mtdx.bcrvalue |= 0x200; /* set PROG bit */
+ mtdx.crvalue |= 0x02000000; /* set enhanced bit */
+ }
+
+ outl( mtdx.bcrvalue, mtdx.ioaddr + BCR);
+
+ /* Restart Rx engine if stopped. */
+ outl(0, mtdx.ioaddr + RXPDR);
+
+ getlinkstatus(nic);
+ if (mtdx.linkok)
+ {
+ char* texts[]={"half","full","10","100","1000"};
+ getlinktype(nic);
+ DBGPRNT(("Link is OK : %s %s\n", texts[mtdx.duplexmode-1], texts[mtdx.line_speed+1] ));
+ } else
+ {
+ DBGPRNT(("No link!!!\n"));
+ }
+
+ mtdx.crvalue |= /*TxEnable |*/ RxEnable | TxThreshold;
+ set_rx_mode(nic);
+
+ /* Clear interrupts by setting the interrupt mask. */
+ outl(FBE | TUNF | CNTOVF | RBU | TI | RI, mtdx.ioaddr + ISR);
+ outl( 0, mtdx.ioaddr + IMR);
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int mtd_poll(struct nic *nic)
+{
+ s32 rx_status = mtdx.cur_rx->status;
+ int retval = 0;
+
+ if( ( rx_status & RXOWN ) != 0 )
+ {
+ return 0;
+ }
+
+ if (rx_status & ErrorSummary)
+ { /* there was a fatal error */
+ printf( "%s: Receive error, Rx status %8.8x, Error(s) %s%s%s\n",
+ mtdx.nic_name, rx_status ,
+ (rx_status & (LONG | RUNT)) ? "length_error ":"",
+ (rx_status & RXER) ? "frame_error ":"",
+ (rx_status & CRC) ? "crc_error ":"" );
+ retval = 0;
+ } else if( !((rx_status & RXFSD) && (rx_status & RXLSD)) )
+ {
+ /* this pkt is too long, over one rx buffer */
+ printf("Pkt is too long, over one rx buffer.\n");
+ retval = 0;
+ } else
+ { /* this received pkt is ok */
+ /* Omit the four octet CRC from the length. */
+ short pkt_len = ((rx_status & FLNGMASK) >> FLNGShift) - 4;
+
+ DBGPRNT(( " netdev_rx() normal Rx pkt length %d"
+ " status %x.\n", pkt_len, rx_status));
+
+ nic->packetlen = pkt_len;
+ memcpy(nic->packet, mtdx.cur_rx->skbuff, pkt_len);
+
+ retval = 1;
+ }
+
+ while( ( mtdx.cur_rx->status & RXOWN ) == 0 )
+ {
+ mtdx.cur_rx->status = RXOWN;
+ mtdx.cur_rx = mtdx.cur_rx->next_desc_logical;
+ }
+
+ /* Restart Rx engine if stopped. */
+ outl(0, mtdx.ioaddr + RXPDR);
+
+ return retval;
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void mtd_transmit(
+ struct nic *nic,
+ const char *dest, /* Destination */
+ unsigned int type, /* Type */
+ unsigned int size, /* size */
+ const char *data) /* Packet */
+{
+ u32 to;
+ u32 tx_status;
+ unsigned int nstype = htons ( type );
+
+ memcpy( txb, dest, ETH_ALEN );
+ memcpy( txb + ETH_ALEN, nic->node_addr, ETH_ALEN );
+ memcpy( txb + 2 * ETH_ALEN, &nstype, 2 );
+ memcpy( txb + ETH_HLEN, data, size );
+
+ size += ETH_HLEN;
+ size &= 0x0FFF;
+ while( size < ETH_ZLEN )
+ {
+ txb[size++] = '\0';
+ }
+
+ mtdx.tx_ring[0].control = TXLD | TXFD | CRCEnable | PADEnable;
+ mtdx.tx_ring[0].control |= (size << PKTSShift); /* pkt size */
+ mtdx.tx_ring[0].control |= (size << TBSShift); /* buffer size */
+ mtdx.tx_ring[0].status = TXOWN;
+
+ /* Point to transmit descriptor */
+ outl(virt_to_bus(mtdx.tx_ring), mtdx.ioaddr + TXLBA);
+ /* Enable Tx */
+ outl( mtdx.crvalue | TxEnable, mtdx.ioaddr + TCRRCR);
+ /* Wake the potentially-idle transmit channel. */
+ outl(0, mtdx.ioaddr + TXPDR);
+
+ to = currticks() + TX_TIME_OUT;
+ while(( mtdx.tx_ring[0].status & TXOWN) && (currticks() < to));
+
+ /* Disable Tx */
+ outl( mtdx.crvalue & (~TxEnable), mtdx.ioaddr + TCRRCR);
+
+ tx_status = mtdx.tx_ring[0].status;
+ if (currticks() >= to){
+ DBGPRNT(("TX Time Out"));
+ } else if( tx_status & (CSL | LC | EC | UDF | HF)){
+ printf("Transmit error: %s %s %s %s %s.\n",
+ tx_status,
+ tx_status & EC ? "abort" : "",
+ tx_status & CSL ? "carrier" : "",
+ tx_status & LC ? "late" : "",
+ tx_status & UDF ? "fifo" : "",
+ tx_status & HF ? "heartbeat" : "" );
+ }
+
+ /*hex_dump( txb, size );*/
+ /*pause();*/
+
+ DBGPRNT(("TRANSMIT\n"));
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void mtd_disable(struct dev *dev)
+{
+ /* put the card in its initial state */
+ /* Disable Tx Rx*/
+ outl( mtdx.crvalue & (~TxEnable) & (~RxEnable), mtdx.ioaddr + TCRRCR);
+ /* Reset the chip to erase previous misconfiguration. */
+ mtd_reset((struct nic *) dev);
+ DBGPRNT(("DISABLE\n"));
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+
+static int mtd_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *)dev;
+ int i;
+
+ if (pci->ioaddr == 0)
+ {
+ return 0;
+ }
+
+ printf(" - ");
+
+ /* Mask the bit that says "this is an io addr" */
+ mtdx.ioaddr = pci->ioaddr & ~3;
+
+ adjust_pci_device(pci);
+
+ mtdx.nic_name = pci->name;
+ mtdx.dev_id = pci->dev_id;
+
+ /* read ethernet id */
+ for (i = 0; i < 6; ++i)
+ {
+ nic->node_addr[i] = inb(mtdx.ioaddr + PAR0 + i);
+ }
+
+ if (memcmp(nic->node_addr, "\0\0\0\0\0", 6) == 0)
+ {
+ return 0;
+ }
+
+ DBGPRNT(("%s : ioaddr %#hX, addr %!\n",mtdx.nic_name, mtdx.ioaddr, nic->node_addr));
+
+ /* Reset the chip to erase previous misconfiguration. */
+ outl(0x00000001, mtdx.ioaddr + BCR);
+
+ /* find the connected MII xcvrs */
+
+ if( mtdx.dev_id != 0x803 )
+ {
+ int phy, phy_idx = 0;
+
+ for (phy = 1; phy < 32 && phy_idx < 1; phy++) {
+ int mii_status = mdio_read(nic, phy, 1);
+
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ mtdx.phys[phy_idx] = phy;
+
+ DBGPRNT(("%s: MII PHY found at address %d, status "
+ "0x%4.4x.\n", mtdx.nic_name, phy, mii_status));
+ /* get phy type */
+ {
+ unsigned int data;
+
+ data = mdio_read(nic, mtdx.phys[phy_idx], 2);
+ if (data == SeeqPHYID0)
+ mtdx.PHYType = SeeqPHY;
+ else if (data == AhdocPHYID0)
+ mtdx.PHYType = AhdocPHY;
+ else if (data == MarvellPHYID0)
+ mtdx.PHYType = MarvellPHY;
+ else if (data == MysonPHYID0)
+ mtdx.PHYType = Myson981;
+ else if (data == LevelOnePHYID0)
+ mtdx.PHYType = LevelOnePHY;
+ else
+ mtdx.PHYType = OtherPHY;
+ }
+ phy_idx++;
+ }
+ }
+
+ mtdx.mii_cnt = phy_idx;
+ if (phy_idx == 0) {
+ printf("%s: MII PHY not found -- this device may "
+ "not operate correctly.\n", mtdx.nic_name);
+ }
+ } else {
+ mtdx.phys[0] = 32;
+ /* get phy type */
+ if (inl(mtdx.ioaddr + PHYIDENTIFIER) == MysonPHYID ) {
+ mtdx.PHYType = MysonPHY;
+ DBGPRNT(("MysonPHY\n"));
+ } else {
+ mtdx.PHYType = OtherPHY;
+ DBGPRNT(("OtherPHY\n"));
+ }
+ }
+
+ getlinkstatus(nic);
+ if( !mtdx.linkok )
+ {
+ printf("No link!!!\n");
+ return 0;
+ }
+
+ mtd_reset( nic );
+
+ /* point to NIC specific routines */
+ dev->disable = mtd_disable;
+ nic->poll = mtd_poll;
+ nic->transmit = mtd_transmit;
+ return 1;
+}
+
+static struct pci_id mtd80x_nics[] =
+ {
+ PCI_ROM(0x1516, 0x0800, "MTD800", "Myson MTD800"),
+ PCI_ROM(0x1516, 0x0803, "MTD803", "Surecom EP-320X"),
+ PCI_ROM(0x1516, 0x0891, "MTD891", "Myson MTD891"),
+ };
+
+/**************************************************************************/
+static void set_rx_mode(struct nic *nic)
+{
+ u32 mc_filter[2]; /* Multicast hash filter */
+ u32 rx_mode;
+
+ /* Too many to match, or accept all multicasts. */
+ mc_filter[1] = mc_filter[0] = ~0;
+ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+
+ outl(mc_filter[0], mtdx.ioaddr + MAR0);
+ outl(mc_filter[1], mtdx.ioaddr + MAR1);
+
+ mtdx.crvalue = ( mtdx.crvalue & ~RxModeMask ) | rx_mode;
+ outb( mtdx.crvalue, mtdx.ioaddr + TCRRCR);
+}
+/**************************************************************************/
+static unsigned int m80x_read_tick(void)
+/* function: Reads the Timer tick count register which decrements by 2 from */
+/* 65536 to 0 every 1/36.414 of a second. Each 2 decrements of the */
+/* count represents 838 nsec's. */
+/* input : none. */
+/* output : none. */
+{
+ unsigned char tmp;
+ int value;
+
+ outb((char) 0x06, 0x43); // Command 8254 to latch T0's count
+
+ // now read the count.
+ tmp = (unsigned char) inb(0x40);
+ value = ((int) tmp) << 8;
+ tmp = (unsigned char) inb(0x40);
+ value |= (((int) tmp) & 0xff);
+ return (value);
+}
+
+static void m80x_delay(unsigned int interval)
+/* function: to wait for a specified time. */
+/* input : interval ... the specified time. */
+/* output : none. */
+{
+ unsigned int interval1, interval2, i = 0;
+
+ interval1 = m80x_read_tick(); // get initial value
+ do
+ {
+ interval2 = m80x_read_tick();
+ if (interval1 < interval2)
+ interval1 += 65536;
+ ++i;
+ } while (((interval1 - interval2) < (u16) interval) && (i < 65535));
+}
+
+
+static u32 m80x_send_cmd_to_phy(long miiport, int opcode, int phyad, int regad)
+{
+ u32 miir;
+ int i;
+ unsigned int mask, data;
+
+ /* enable MII output */
+ miir = (u32) inl(miiport);
+ miir &= 0xfffffff0;
+
+ miir |= MASK_MIIR_MII_WRITE + MASK_MIIR_MII_MDO;
+
+ /* send 32 1's preamble */
+ for (i = 0; i < 32; i++) {
+ /* low MDC; MDO is already high (miir) */
+ miir &= ~MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+
+ /* high MDC */
+ miir |= MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+ }
+
+ /* calculate ST+OP+PHYAD+REGAD+TA */
+ data = opcode | (phyad << 7) | (regad << 2);
+
+ /* sent out */
+ mask = 0x8000;
+ while (mask) {
+ /* low MDC, prepare MDO */
+ miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO);
+ if (mask & data)
+ miir |= MASK_MIIR_MII_MDO;
+
+ outl(miir, miiport);
+ /* high MDC */
+ miir |= MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+ m80x_delay(30);
+
+ /* next */
+ mask >>= 1;
+ if (mask == 0x2 && opcode == OP_READ)
+ miir &= ~MASK_MIIR_MII_WRITE;
+ }
+ return miir;
+}
+
+static int mdio_read(struct nic *nic, int phyad, int regad)
+{
+ long miiport = mtdx.ioaddr + MANAGEMENT;
+ u32 miir;
+ unsigned int mask, data;
+
+ miir = m80x_send_cmd_to_phy(miiport, OP_READ, phyad, regad);
+
+ /* read data */
+ mask = 0x8000;
+ data = 0;
+ while (mask)
+ {
+ /* low MDC */
+ miir &= ~MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+
+ /* read MDI */
+ miir = inl(miiport);
+ if (miir & MASK_MIIR_MII_MDI)
+ data |= mask;
+
+ /* high MDC, and wait */
+ miir |= MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+ m80x_delay((int) 30);
+
+ /* next */
+ mask >>= 1;
+ }
+
+ /* low MDC */
+ miir &= ~MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+
+ return data & 0xffff;
+}
+
+static void mdio_write(struct nic *nic, int phyad, int regad, int data)
+{
+ long miiport = mtdx.ioaddr + MANAGEMENT;
+ u32 miir;
+ unsigned int mask;
+
+ miir = m80x_send_cmd_to_phy(miiport, OP_WRITE, phyad, regad);
+
+ /* write data */
+ mask = 0x8000;
+ while (mask)
+ {
+ /* low MDC, prepare MDO */
+ miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO);
+ if (mask & data)
+ miir |= MASK_MIIR_MII_MDO;
+ outl(miir, miiport);
+
+ /* high MDC */
+ miir |= MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+
+ /* next */
+ mask >>= 1;
+ }
+
+ /* low MDC */
+ miir &= ~MASK_MIIR_MII_MDC;
+ outl(miir, miiport);
+
+ return;
+}
+
+static void getlinkstatus(struct nic *nic)
+/* function: Routine will read MII Status Register to get link status. */
+/* input : dev... pointer to the adapter block. */
+/* output : none. */
+{
+ unsigned int i, DelayTime = 0x1000;
+
+ mtdx.linkok = 0;
+
+ if (mtdx.PHYType == MysonPHY)
+ {
+ for (i = 0; i < DelayTime; ++i) {
+ if (inl(mtdx.ioaddr + BMCRSR) & LinkIsUp2) {
+ mtdx.linkok = 1;
+ return;
+ }
+ // delay
+ m80x_delay(100);
+ }
+ } else
+ {
+ for (i = 0; i < DelayTime; ++i) {
+ if (mdio_read(nic, mtdx.phys[0], MII_BMSR) & BMSR_LSTATUS) {
+ mtdx.linkok = 1;
+ return;
+ }
+ // delay
+ m80x_delay(100);
+ }
+ }
+}
+
+
+static void getlinktype(struct nic *dev)
+{
+ if (mtdx.PHYType == MysonPHY)
+ { /* 3-in-1 case */
+ if (inl(mtdx.ioaddr + TCRRCR) & FD)
+ mtdx.duplexmode = 2; /* full duplex */
+ else
+ mtdx.duplexmode = 1; /* half duplex */
+ if (inl(mtdx.ioaddr + TCRRCR) & PS10)
+ mtdx.line_speed = 1; /* 10M */
+ else
+ mtdx.line_speed = 2; /* 100M */
+ } else
+ {
+ if (mtdx.PHYType == SeeqPHY) { /* this PHY is SEEQ 80225 */
+ unsigned int data;
+
+ data = mdio_read(dev, mtdx.phys[0], MIIRegister18);
+ if (data & SPD_DET_100)
+ mtdx.line_speed = 2; /* 100M */
+ else
+ mtdx.line_speed = 1; /* 10M */
+ if (data & DPLX_DET_FULL)
+ mtdx.duplexmode = 2; /* full duplex mode */
+ else
+ mtdx.duplexmode = 1; /* half duplex mode */
+ } else if (mtdx.PHYType == AhdocPHY) {
+ unsigned int data;
+
+ data = mdio_read(dev, mtdx.phys[0], DiagnosticReg);
+ if (data & Speed_100)
+ mtdx.line_speed = 2; /* 100M */
+ else
+ mtdx.line_speed = 1; /* 10M */
+ if (data & DPLX_FULL)
+ mtdx.duplexmode = 2; /* full duplex mode */
+ else
+ mtdx.duplexmode = 1; /* half duplex mode */
+ }
+ /* 89/6/13 add, (begin) */
+ else if (mtdx.PHYType == MarvellPHY) {
+ unsigned int data;
+
+ data = mdio_read(dev, mtdx.phys[0], SpecificReg);
+ if (data & Full_Duplex)
+ mtdx.duplexmode = 2; /* full duplex mode */
+ else
+ mtdx.duplexmode = 1; /* half duplex mode */
+ data &= SpeedMask;
+ if (data == Speed_1000M)
+ mtdx.line_speed = 3; /* 1000M */
+ else if (data == Speed_100M)
+ mtdx.line_speed = 2; /* 100M */
+ else
+ mtdx.line_speed = 1; /* 10M */
+ }
+ /* 89/6/13 add, (end) */
+ /* 89/7/27 add, (begin) */
+ else if (mtdx.PHYType == Myson981) {
+ unsigned int data;
+
+ data = mdio_read(dev, mtdx.phys[0], StatusRegister);
+
+ if (data & SPEED100)
+ mtdx.line_speed = 2;
+ else
+ mtdx.line_speed = 1;
+
+ if (data & FULLMODE)
+ mtdx.duplexmode = 2;
+ else
+ mtdx.duplexmode = 1;
+ }
+ /* 89/7/27 add, (end) */
+ /* 89/12/29 add */
+ else if (mtdx.PHYType == LevelOnePHY) {
+ unsigned int data;
+
+ data = mdio_read(dev, mtdx.phys[0], SpecificReg);
+ if (data & LXT1000_Full)
+ mtdx.duplexmode = 2; /* full duplex mode */
+ else
+ mtdx.duplexmode = 1; /* half duplex mode */
+ data &= SpeedMask;
+ if (data == LXT1000_1000M)
+ mtdx.line_speed = 3; /* 1000M */
+ else if (data == LXT1000_100M)
+ mtdx.line_speed = 2; /* 100M */
+ else
+ mtdx.line_speed = 1; /* 10M */
+ }
+ // chage crvalue
+ // mtdx.crvalue&=(~PS10)&(~FD);
+ mtdx.crvalue &= (~PS10) & (~FD) & (~PS1000);
+ if (mtdx.line_speed == 1)
+ mtdx.crvalue |= PS10;
+ else if (mtdx.line_speed == 3)
+ mtdx.crvalue |= PS1000;
+ if (mtdx.duplexmode == 2)
+ mtdx.crvalue |= FD;
+ }
+}
+
+
+struct pci_driver mtd80x_driver __pci_driver ={
+ .type = NIC_DRIVER,
+ .name = "MTD80X",
+ .probe = mtd_probe,
+ .ids = mtd80x_nics,
+ .id_count = sizeof(mtd80x_nics)/sizeof(mtd80x_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/natsemi.c b/src/drivers/net/natsemi.c
new file mode 100644
index 00000000..73c1368d
--- /dev/null
+++ b/src/drivers/net/natsemi.c
@@ -0,0 +1,780 @@
+/* -*- Mode:C; c-basic-offset:4; -*- */
+
+/*
+ natsemi.c: An Etherboot driver for the NatSemi DP8381x series.
+
+ Copyright (C) 2001 Entity Cyber, Inc.
+
+ This development of this Etherboot driver was funded by
+
+ Sicom Systems: http://www.sicompos.com/
+
+ Author: Marty Connor (mdc@thinguin.org)
+ Adapted from a Linux driver which was written by Donald Becker
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License (GPL), incorporated herein by reference.
+
+ Original Copyright Notice:
+
+ Written/copyright 1999-2001 by Donald Becker.
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License (GPL), incorporated herein by reference.
+ Drivers based on or derived from this code fall under the GPL and must
+ retain the authorship, copyright and license notice. This file is not
+ a complete program and may only be used when the entire operating
+ system is licensed under the GPL. License for under other terms may be
+ available. Contact the original author for details.
+
+ The original author may be reached as becker@scyld.com, or at
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ Support information and updates available at
+ http://www.scyld.com/network/netsemi.html
+
+ References:
+
+ http://www.scyld.com/expert/100mbps.html
+ http://www.scyld.com/expert/NWay.html
+ Datasheet is available from:
+ http://www.national.com/pf/DP/DP83815.html
+
+*/
+
+/* Revision History */
+
+/*
+ 13 Dec 2003 timlegge 1.1 Enabled Multicast Support
+ 29 May 2001 mdc 1.0
+ Initial Release. Tested with Netgear FA311 and FA312 boards
+*/
+/* Includes */
+
+#include "etherboot.h"
+#include "nic.h"
+#include "pci.h"
+
+/* defines */
+
+#define OWN 0x80000000
+#define DSIZE 0x00000FFF
+#define CRC_SIZE 4
+
+/* Time in ticks before concluding the transmitter is hung. */
+#define TX_TIMEOUT (4*TICKS_PER_SEC)
+
+#define TX_BUF_SIZE 1536
+#define RX_BUF_SIZE 1536
+
+#define NUM_RX_DESC 4 /* Number of Rx descriptor registers. */
+
+typedef uint8_t u8;
+typedef int8_t s8;
+typedef uint16_t u16;
+typedef int16_t s16;
+typedef uint32_t u32;
+typedef int32_t s32;
+
+/* helpful macroes if on a big_endian machine for changing byte order.
+ not strictly needed on Intel */
+#define get_unaligned(ptr) (*(ptr))
+#define put_unaligned(val, ptr) ((void)( *(ptr) = (val) ))
+#define get_u16(ptr) (*(u16 *)(ptr))
+#define virt_to_le32desc(addr) virt_to_bus(addr)
+
+enum pcistuff {
+ PCI_USES_IO = 0x01,
+ PCI_USES_MEM = 0x02,
+ PCI_USES_MASTER = 0x04,
+ PCI_ADDR0 = 0x08,
+ PCI_ADDR1 = 0x10,
+};
+
+/* MMIO operations required */
+#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1)
+
+/* Offsets to the device registers.
+ Unlike software-only systems, device drivers interact with complex hardware.
+ It's not useful to define symbolic names for every register bit in the
+ device.
+*/
+enum register_offsets {
+ ChipCmd = 0x00,
+ ChipConfig = 0x04,
+ EECtrl = 0x08,
+ PCIBusCfg = 0x0C,
+ IntrStatus = 0x10,
+ IntrMask = 0x14,
+ IntrEnable = 0x18,
+ TxRingPtr = 0x20,
+ TxConfig = 0x24,
+ RxRingPtr = 0x30,
+ RxConfig = 0x34,
+ ClkRun = 0x3C,
+ WOLCmd = 0x40,
+ PauseCmd = 0x44,
+ RxFilterAddr = 0x48,
+ RxFilterData = 0x4C,
+ BootRomAddr = 0x50,
+ BootRomData = 0x54,
+ SiliconRev = 0x58,
+ StatsCtrl = 0x5C,
+ StatsData = 0x60,
+ RxPktErrs = 0x60,
+ RxMissed = 0x68,
+ RxCRCErrs = 0x64,
+ PCIPM = 0x44,
+ PhyStatus = 0xC0,
+ MIntrCtrl = 0xC4,
+ MIntrStatus = 0xC8,
+
+ /* These are from the spec, around page 78... on a separate table. */
+ PGSEL = 0xCC,
+ PMDCSR = 0xE4,
+ TSTDAT = 0xFC,
+ DSPCFG = 0xF4,
+ SDCFG = 0x8C
+};
+
+/* Bit in ChipCmd. */
+enum ChipCmdBits {
+ ChipReset = 0x100,
+ RxReset = 0x20,
+ TxReset = 0x10,
+ RxOff = 0x08,
+ RxOn = 0x04,
+ TxOff = 0x02,
+ TxOn = 0x01
+};
+
+/* Bits in the RxMode register. */
+enum rx_mode_bits {
+ AcceptErr = 0x20,
+ AcceptRunt = 0x10,
+ AcceptBroadcast = 0xC0000000,
+ AcceptMulticast = 0x00200000,
+ AcceptAllMulticast = 0x20000000,
+ AcceptAllPhys = 0x10000000,
+ AcceptMyPhys = 0x08000000,
+ RxFilterEnable = 0x80000000
+};
+
+typedef struct _BufferDesc {
+ u32 link;
+ volatile u32 cmdsts;
+ u32 bufptr;
+ u32 software_use;
+} BufferDesc;
+
+/* Bits in network_desc.status */
+enum desc_status_bits {
+ DescOwn = 0x80000000,
+ DescMore = 0x40000000,
+ DescIntr = 0x20000000,
+ DescNoCRC = 0x10000000,
+ DescPktOK = 0x08000000,
+ RxTooLong = 0x00400000
+};
+
+/* Globals */
+
+static int natsemi_debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */
+
+const char *nic_name;
+
+static u32 SavedClkRun;
+
+
+static unsigned short vendor, dev_id;
+static unsigned long ioaddr;
+
+static unsigned int cur_rx;
+
+static unsigned int advertising;
+
+static unsigned int rx_config;
+static unsigned int tx_config;
+
+/* Note: transmit and receive buffers and descriptors must be
+ longword aligned
+*/
+
+static BufferDesc txd __attribute__ ((aligned(4)));
+static BufferDesc rxd[NUM_RX_DESC] __attribute__ ((aligned(4)));
+
+static unsigned char txb[TX_BUF_SIZE] __attribute__ ((aligned(4)));
+static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE] __attribute__ ((aligned(4)));
+
+/* Function Prototypes */
+
+static int natsemi_probe(struct dev *dev, struct pci_device *pci);
+static int eeprom_read(long addr, int location);
+static int mdio_read(int phy_id, int location);
+static void natsemi_init(struct nic *nic);
+static void natsemi_reset(struct nic *nic);
+static void natsemi_init_rxfilter(struct nic *nic);
+static void natsemi_init_txd(struct nic *nic);
+static void natsemi_init_rxd(struct nic *nic);
+static void natsemi_set_rx_mode(struct nic *nic);
+static void natsemi_check_duplex(struct nic *nic);
+static void natsemi_transmit(struct nic *nic, const char *d, unsigned int t, unsigned int s, const char *p);
+static int natsemi_poll(struct nic *nic, int retrieve);
+static void natsemi_disable(struct dev *dev);
+static void natsemi_irq(struct nic *nic, irq_action_t action);
+
+/*
+ * Function: natsemi_probe
+ *
+ * Description: Retrieves the MAC address of the card, and sets up some
+ * globals required by other routines, and initializes the NIC, making it
+ * ready to send and receive packets.
+ *
+ * Side effects:
+ * leaves the ioaddress of the natsemi chip in the variable ioaddr.
+ * leaves the natsemi initialized, and ready to recieve packets.
+ *
+ * Returns: struct nic *: pointer to NIC data structure
+ */
+
+static int
+natsemi_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *)dev;
+ int i;
+ int prev_eedata;
+ u32 tmp;
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ adjust_pci_device(pci);
+
+ /* initialize some commonly used globals */
+
+ nic->irqno = 0;
+ nic->ioaddr = pci->ioaddr & ~3;
+
+ ioaddr = pci->ioaddr & ~3;
+ vendor = pci->vendor;
+ dev_id = pci->dev_id;
+ nic_name = pci->name;
+
+ /* natsemi has a non-standard PM control register
+ * in PCI config space. Some boards apparently need
+ * to be brought to D0 in this manner.
+ */
+ pcibios_read_config_dword(pci->bus, pci->devfn, PCIPM, &tmp);
+ if (tmp & (0x03|0x100)) {
+ /* D0 state, disable PME assertion */
+ u32 newtmp = tmp & ~(0x03|0x100);
+ pcibios_write_config_dword(pci->bus, pci->devfn, PCIPM, newtmp);
+ }
+
+ /* get MAC address */
+
+ prev_eedata = eeprom_read(ioaddr, 6);
+ for (i = 0; i < 3; i++) {
+ int eedata = eeprom_read(ioaddr, i + 7);
+ nic->node_addr[i*2] = (eedata << 1) + (prev_eedata >> 15);
+ nic->node_addr[i*2+1] = eedata >> 7;
+ prev_eedata = eedata;
+ }
+
+ printf("\nnatsemi_probe: MAC addr %! at ioaddr %#hX\n",
+ nic->node_addr, ioaddr);
+ printf("natsemi_probe: Vendor:%#hX Device:%#hX\n", vendor, dev_id);
+
+ /* Reset the chip to erase any previous misconfiguration. */
+ outl(ChipReset, ioaddr + ChipCmd);
+
+ advertising = mdio_read(1, 4);
+ {
+ u32 chip_config = inl(ioaddr + ChipConfig);
+ printf("%s: Transceiver default autoneg. %s "
+ "10%s %s duplex.\n",
+ nic_name,
+ chip_config & 0x2000 ? "enabled, advertise" : "disabled, force",
+ chip_config & 0x4000 ? "0" : "",
+ chip_config & 0x8000 ? "full" : "half");
+ }
+ printf("%s: Transceiver status %hX advertising %hX\n",
+ nic_name, (int)inl(ioaddr + 0x84), advertising);
+
+ /* Disable PME:
+ * The PME bit is initialized from the EEPROM contents.
+ * PCI cards probably have PME disabled, but motherboard
+ * implementations may have PME set to enable WakeOnLan.
+ * With PME set the chip will scan incoming packets but
+ * nothing will be written to memory. */
+ SavedClkRun = inl(ioaddr + ClkRun);
+ outl(SavedClkRun & ~0x100, ioaddr + ClkRun);
+
+ /* initialize device */
+ natsemi_init(nic);
+
+ dev->disable = natsemi_disable;
+ nic->poll = natsemi_poll;
+ nic->transmit = natsemi_transmit;
+ nic->irq = natsemi_irq;
+
+ return 1;
+}
+
+/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces.
+ The EEPROM code is for the common 93c06/46 EEPROMs with 6 bit addresses.
+*/
+
+/* Delay between EEPROM clock transitions.
+ No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need
+ a delay. */
+#define eeprom_delay(ee_addr) inl(ee_addr)
+
+enum EEPROM_Ctrl_Bits {
+ EE_ShiftClk = 0x04,
+ EE_DataIn = 0x01,
+ EE_ChipSelect = 0x08,
+ EE_DataOut = 0x02
+};
+
+#define EE_Write0 (EE_ChipSelect)
+#define EE_Write1 (EE_ChipSelect | EE_DataIn)
+
+/* The EEPROM commands include the alway-set leading bit. */
+enum EEPROM_Cmds {
+ EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6),
+};
+
+static int eeprom_read(long addr, int location)
+{
+ int i;
+ int retval = 0;
+ int ee_addr = addr + EECtrl;
+ int read_cmd = location | EE_ReadCmd;
+ outl(EE_Write0, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 10; i >= 0; i--) {
+ short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0;
+ outl(dataval, ee_addr);
+ eeprom_delay(ee_addr);
+ outl(dataval | EE_ShiftClk, ee_addr);
+ eeprom_delay(ee_addr);
+ }
+ outl(EE_ChipSelect, ee_addr);
+ eeprom_delay(ee_addr);
+
+ for (i = 0; i < 16; i++) {
+ outl(EE_ChipSelect | EE_ShiftClk, ee_addr);
+ eeprom_delay(ee_addr);
+ retval |= (inl(ee_addr) & EE_DataOut) ? 1 << i : 0;
+ outl(EE_ChipSelect, ee_addr);
+ eeprom_delay(ee_addr);
+ }
+
+ /* Terminate the EEPROM access. */
+ outl(EE_Write0, ee_addr);
+ outl(0, ee_addr);
+
+ return retval;
+}
+
+/* MII transceiver control section.
+ The 83815 series has an internal transceiver, and we present the
+ management registers as if they were MII connected. */
+
+static int mdio_read(int phy_id, int location)
+{
+ if (phy_id == 1 && location < 32)
+ return inl(ioaddr + 0x80 + (location<<2)) & 0xffff;
+ else
+ return 0xffff;
+}
+
+/* Function: natsemi_init
+ *
+ * Description: resets the ethernet controller chip and configures
+ * registers and data structures required for sending and receiving packets.
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * returns: void.
+ */
+
+static void
+natsemi_init(struct nic *nic)
+{
+ natsemi_reset(nic);
+
+ /* Disable PME:
+ * The PME bit is initialized from the EEPROM contents.
+ * PCI cards probably have PME disabled, but motherboard
+ * implementations may have PME set to enable WakeOnLan.
+ * With PME set the chip will scan incoming packets but
+ * nothing will be written to memory. */
+ outl(SavedClkRun & ~0x100, ioaddr + ClkRun);
+
+ natsemi_init_rxfilter(nic);
+
+ natsemi_init_txd(nic);
+ natsemi_init_rxd(nic);
+
+ /* Initialize other registers. */
+ /* Configure the PCI bus bursts and FIFO thresholds. */
+ /* Configure for standard, in-spec Ethernet. */
+ if (inl(ioaddr + ChipConfig) & 0x20000000) { /* Full duplex */
+ tx_config = 0xD0801002;
+ rx_config = 0x10000020;
+ } else {
+ tx_config = 0x10801002;
+ rx_config = 0x0020;
+ }
+ outl(tx_config, ioaddr + TxConfig);
+ outl(rx_config, ioaddr + RxConfig);
+
+ natsemi_check_duplex(nic);
+ natsemi_set_rx_mode(nic);
+
+ outl(RxOn, ioaddr + ChipCmd);
+}
+
+/*
+ * Function: natsemi_reset
+ *
+ * Description: soft resets the controller chip
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: void.
+ */
+static void
+natsemi_reset(struct nic *nic __unused)
+{
+ outl(ChipReset, ioaddr + ChipCmd);
+
+ /* On page 78 of the spec, they recommend some settings for "optimum
+ performance" to be done in sequence. These settings optimize some
+ of the 100Mbit autodetection circuitry. Also, we only want to do
+ this for rev C of the chip.
+ */
+ if (inl(ioaddr + SiliconRev) == 0x302) {
+ outw(0x0001, ioaddr + PGSEL);
+ outw(0x189C, ioaddr + PMDCSR);
+ outw(0x0000, ioaddr + TSTDAT);
+ outw(0x5040, ioaddr + DSPCFG);
+ outw(0x008C, ioaddr + SDCFG);
+ }
+ /* Disable interrupts using the mask. */
+ outl(0, ioaddr + IntrMask);
+ outl(0, ioaddr + IntrEnable);
+}
+
+/* Function: natsemi_init_rxfilter
+ *
+ * Description: sets receive filter address to our MAC address
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * returns: void.
+ */
+
+static void
+natsemi_init_rxfilter(struct nic *nic)
+{
+ int i;
+
+ for (i = 0; i < ETH_ALEN; i += 2) {
+ outl(i, ioaddr + RxFilterAddr);
+ outw(nic->node_addr[i] + (nic->node_addr[i+1] << 8), ioaddr + RxFilterData);
+ }
+}
+
+/*
+ * Function: natsemi_init_txd
+ *
+ * Description: initializes the Tx descriptor
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * returns: void.
+ */
+
+static void
+natsemi_init_txd(struct nic *nic __unused)
+{
+ txd.link = (u32) 0;
+ txd.cmdsts = (u32) 0;
+ txd.bufptr = virt_to_bus(&txb[0]);
+
+ /* load Transmit Descriptor Register */
+ outl(virt_to_bus(&txd), ioaddr + TxRingPtr);
+ if (natsemi_debug > 1)
+ printf("natsemi_init_txd: TX descriptor register loaded with: %X\n",
+ inl(ioaddr + TxRingPtr));
+}
+
+/* Function: natsemi_init_rxd
+ *
+ * Description: initializes the Rx descriptor ring
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: void.
+ */
+
+static void
+natsemi_init_rxd(struct nic *nic __unused)
+{
+ int i;
+
+ cur_rx = 0;
+
+ /* init RX descriptor */
+ for (i = 0; i < NUM_RX_DESC; i++) {
+ rxd[i].link = virt_to_bus((i+1 < NUM_RX_DESC) ? &rxd[i+1] : &rxd[0]);
+ rxd[i].cmdsts = (u32) RX_BUF_SIZE;
+ rxd[i].bufptr = virt_to_bus(&rxb[i*RX_BUF_SIZE]);
+ if (natsemi_debug > 1)
+ printf("natsemi_init_rxd: rxd[%d]=%X link=%X cmdsts=%X bufptr=%X\n",
+ i, &rxd[i], rxd[i].link, rxd[i].cmdsts, rxd[i].bufptr);
+ }
+
+ /* load Receive Descriptor Register */
+ outl(virt_to_bus(&rxd[0]), ioaddr + RxRingPtr);
+
+ if (natsemi_debug > 1)
+ printf("natsemi_init_rxd: RX descriptor register loaded with: %X\n",
+ inl(ioaddr + RxRingPtr));
+}
+
+/* Function: natsemi_set_rx_mode
+ *
+ * Description:
+ * sets the receive mode to accept all broadcast packets and packets
+ * with our MAC address, and reject all multicast packets.
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: void.
+ */
+
+static void natsemi_set_rx_mode(struct nic *nic __unused)
+{
+ u32 rx_mode = RxFilterEnable | AcceptBroadcast |
+ AcceptAllMulticast | AcceptMyPhys;
+
+ outl(rx_mode, ioaddr + RxFilterAddr);
+}
+
+static void natsemi_check_duplex(struct nic *nic __unused)
+{
+ int duplex = inl(ioaddr + ChipConfig) & 0x20000000 ? 1 : 0;
+
+ if (natsemi_debug)
+ printf("%s: Setting %s-duplex based on negotiated link"
+ " capability.\n", nic_name,
+ duplex ? "full" : "half");
+ if (duplex) {
+ rx_config |= 0x10000000;
+ tx_config |= 0xC0000000;
+ } else {
+ rx_config &= ~0x10000000;
+ tx_config &= ~0xC0000000;
+ }
+ outl(tx_config, ioaddr + TxConfig);
+ outl(rx_config, ioaddr + RxConfig);
+}
+
+/* Function: natsemi_transmit
+ *
+ * Description: transmits a packet and waits for completion or timeout.
+ *
+ * Arguments: char d[6]: destination ethernet address.
+ * unsigned short t: ethernet protocol type.
+ * unsigned short s: size of the data-part of the packet.
+ * char *p: the data for the packet.
+ *
+ * Returns: void.
+ */
+
+static void
+natsemi_transmit(struct nic *nic,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+ u32 to, nstype;
+ u32 tx_status;
+
+ /* Stop the transmitter */
+ outl(TxOff, ioaddr + ChipCmd);
+
+ /* load Transmit Descriptor Register */
+ outl(virt_to_bus(&txd), ioaddr + TxRingPtr);
+ if (natsemi_debug > 1)
+ printf("natsemi_transmit: TX descriptor register loaded with: %X\n",
+ inl(ioaddr + TxRingPtr));
+
+ memcpy(txb, d, ETH_ALEN);
+ memcpy(txb + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ nstype = htons(t);
+ memcpy(txb + 2 * ETH_ALEN, (char*)&nstype, 2);
+ memcpy(txb + ETH_HLEN, p, s);
+
+ s += ETH_HLEN;
+ s &= DSIZE;
+
+ if (natsemi_debug > 1)
+ printf("natsemi_transmit: sending %d bytes ethtype %hX\n", (int) s, t);
+
+ /* pad to minimum packet size */
+ while (s < ETH_ZLEN)
+ txb[s++] = '\0';
+
+ /* set the transmit buffer descriptor and enable Transmit State Machine */
+ txd.bufptr = virt_to_bus(&txb[0]);
+ txd.cmdsts = (u32) OWN | s;
+
+ /* restart the transmitter */
+ outl(TxOn, ioaddr + ChipCmd);
+
+ if (natsemi_debug > 1)
+ printf("natsemi_transmit: Queued Tx packet size %d.\n", (int) s);
+
+ to = currticks() + TX_TIMEOUT;
+
+ while ((((volatile u32) tx_status=txd.cmdsts) & OWN) && (currticks() < to))
+ /* wait */ ;
+
+ if (currticks() >= to) {
+ printf("natsemi_transmit: TX Timeout! Tx status %X.\n", tx_status);
+ }
+
+ if (!(tx_status & 0x08000000)) {
+ printf("natsemi_transmit: Transmit error, Tx status %X.\n", tx_status);
+ }
+}
+
+/* Function: natsemi_poll
+ *
+ * Description: checks for a received packet and returns it if found.
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: 1 if packet was received.
+ * 0 if no packet was received.
+ *
+ * Side effects:
+ * Returns (copies) the packet to the array nic->packet.
+ * Returns the length of the packet in nic->packetlen.
+ */
+
+static int
+natsemi_poll(struct nic *nic, int retrieve)
+{
+ u32 rx_status = rxd[cur_rx].cmdsts;
+ int retstat = 0;
+
+ if (natsemi_debug > 2)
+ printf("natsemi_poll: cur_rx:%d, status:%X\n", cur_rx, rx_status);
+
+ if (!(rx_status & OWN))
+ return retstat;
+
+ if ( ! retrieve ) return 1;
+
+ if (natsemi_debug > 1)
+ printf("natsemi_poll: got a packet: cur_rx:%d, status:%X\n",
+ cur_rx, rx_status);
+
+ nic->packetlen = (rx_status & DSIZE) - CRC_SIZE;
+
+ if ((rx_status & (DescMore|DescPktOK|RxTooLong)) != DescPktOK) {
+ /* corrupted packet received */
+ printf("natsemi_poll: Corrupted packet received, buffer status = %X\n",
+ rx_status);
+ retstat = 0;
+ } else {
+ /* give packet to higher level routine */
+ memcpy(nic->packet, (rxb + cur_rx*RX_BUF_SIZE), nic->packetlen);
+ retstat = 1;
+ }
+
+ /* return the descriptor and buffer to receive ring */
+ rxd[cur_rx].cmdsts = RX_BUF_SIZE;
+ rxd[cur_rx].bufptr = virt_to_bus(&rxb[cur_rx*RX_BUF_SIZE]);
+
+ if (++cur_rx == NUM_RX_DESC)
+ cur_rx = 0;
+
+ /* re-enable the potentially idle receive state machine */
+ outl(RxOn, ioaddr + ChipCmd);
+
+ return retstat;
+}
+
+/* Function: natsemi_disable
+ *
+ * Description: Turns off interrupts and stops Tx and Rx engines
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: void.
+ */
+
+static void
+natsemi_disable(struct dev *dev)
+{
+ struct nic *nic = (struct nic *)dev;
+ /* merge reset and disable */
+ natsemi_init(nic);
+
+ /* Disable interrupts using the mask. */
+ outl(0, ioaddr + IntrMask);
+ outl(0, ioaddr + IntrEnable);
+
+ /* Stop the chip's Tx and Rx processes. */
+ outl(RxOff | TxOff, ioaddr + ChipCmd);
+
+ /* Restore PME enable bit */
+ outl(SavedClkRun, ioaddr + ClkRun);
+}
+
+/* Function: natsemi_irq
+ *
+ * Description: Enable, Disable, or Force interrupts
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ * irq_action_t action: requested action to perform
+ *
+ * Returns: void.
+ */
+
+static void
+natsemi_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+static struct pci_id natsemi_nics[] = {
+PCI_ROM(0x100b, 0x0020, "dp83815", "DP83815"),
+};
+
+static struct pci_driver natsemi_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "NATSEMI",
+ .probe = natsemi_probe,
+ .ids = natsemi_nics,
+ .id_count = sizeof(natsemi_nics)/sizeof(natsemi_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/ns83820.c b/src/drivers/net/ns83820.c
new file mode 100755
index 00000000..16b41f8b
--- /dev/null
+++ b/src/drivers/net/ns83820.c
@@ -0,0 +1,1020 @@
+/**************************************************************************
+* ns83820.c: Etherboot device driver for the National Semiconductor 83820
+* Written 2004 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code based on:
+* ns83820.c by Benjamin LaHaise with contributions
+* for Linux kernel 2.4.x.
+*
+* Linux Driver Version 0.20, 20020610
+*
+* This development of this Etherboot driver was funded by:
+*
+* NXTV: http://www.nxtv.com/
+*
+* REVISION HISTORY:
+* ================
+*
+* v1.0 02-16-2004 timlegge Initial port of Linux driver
+* v1.1 02-19-2004 timlegge More rohbust transmit and poll
+*
+* Indent Options: indent -kr -i8
+***************************************************************************/
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include "pci.h"
+
+#if ARCH == ia64 /* Support 64-bit addressing */
+#define USE_64BIT_ADDR
+#endif
+
+//#define DDEBUG
+#ifdef DDEBUG
+#define dprintf(x) printf x
+#else
+#define dprintf(x)
+#endif
+
+typedef unsigned char u8;
+typedef signed char s8;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned int u32;
+typedef signed int s32;
+
+#define HZ 100
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+/* NIC specific static variables go here */
+
+/* Global parameters. See MODULE_PARM near the bottom. */
+// static int ihr = 2;
+static int reset_phy = 0;
+static int lnksts = 0; /* CFG_LNKSTS bit polarity */
+
+#if defined(CONFIG_HIGHMEM64G) || defined(__ia64__)
+#define USE_64BIT_ADDR "+"
+#endif
+
+#if defined(USE_64BIT_ADDR)
+#define TRY_DAC 1
+#else
+#define TRY_DAC 0
+#endif
+
+/* tunables */
+#define RX_BUF_SIZE 1500 /* 8192 */
+
+/* Must not exceed ~65000. */
+#define NR_RX_DESC 64
+#define NR_TX_DESC 1
+
+ /* not tunable *//* Extra 6 bytes for 64 bit alignment (divisable by 8) */
+#define REAL_RX_BUF_SIZE (RX_BUF_SIZE + 14 + 6) /* rx/tx mac addr + type */
+
+#define MIN_TX_DESC_FREE 8
+
+/* register defines */
+#define CFGCS 0x04
+
+#define CR_TXE 0x00000001
+#define CR_TXD 0x00000002
+/* Ramit : Here's a tip, don't do a RXD immediately followed by an RXE
+ * The Receive engine skips one descriptor and moves
+ * onto the next one!! */
+#define CR_RXE 0x00000004
+#define CR_RXD 0x00000008
+#define CR_TXR 0x00000010
+#define CR_RXR 0x00000020
+#define CR_SWI 0x00000080
+#define CR_RST 0x00000100
+
+#define PTSCR_EEBIST_FAIL 0x00000001
+#define PTSCR_EEBIST_EN 0x00000002
+#define PTSCR_EELOAD_EN 0x00000004
+#define PTSCR_RBIST_FAIL 0x000001b8
+#define PTSCR_RBIST_DONE 0x00000200
+#define PTSCR_RBIST_EN 0x00000400
+#define PTSCR_RBIST_RST 0x00002000
+
+#define MEAR_EEDI 0x00000001
+#define MEAR_EEDO 0x00000002
+#define MEAR_EECLK 0x00000004
+#define MEAR_EESEL 0x00000008
+#define MEAR_MDIO 0x00000010
+#define MEAR_MDDIR 0x00000020
+#define MEAR_MDC 0x00000040
+
+#define ISR_TXDESC3 0x40000000
+#define ISR_TXDESC2 0x20000000
+#define ISR_TXDESC1 0x10000000
+#define ISR_TXDESC0 0x08000000
+#define ISR_RXDESC3 0x04000000
+#define ISR_RXDESC2 0x02000000
+#define ISR_RXDESC1 0x01000000
+#define ISR_RXDESC0 0x00800000
+#define ISR_TXRCMP 0x00400000
+#define ISR_RXRCMP 0x00200000
+#define ISR_DPERR 0x00100000
+#define ISR_SSERR 0x00080000
+#define ISR_RMABT 0x00040000
+#define ISR_RTABT 0x00020000
+#define ISR_RXSOVR 0x00010000
+#define ISR_HIBINT 0x00008000
+#define ISR_PHY 0x00004000
+#define ISR_PME 0x00002000
+#define ISR_SWI 0x00001000
+#define ISR_MIB 0x00000800
+#define ISR_TXURN 0x00000400
+#define ISR_TXIDLE 0x00000200
+#define ISR_TXERR 0x00000100
+#define ISR_TXDESC 0x00000080
+#define ISR_TXOK 0x00000040
+#define ISR_RXORN 0x00000020
+#define ISR_RXIDLE 0x00000010
+#define ISR_RXEARLY 0x00000008
+#define ISR_RXERR 0x00000004
+#define ISR_RXDESC 0x00000002
+#define ISR_RXOK 0x00000001
+
+#define TXCFG_CSI 0x80000000
+#define TXCFG_HBI 0x40000000
+#define TXCFG_MLB 0x20000000
+#define TXCFG_ATP 0x10000000
+#define TXCFG_ECRETRY 0x00800000
+#define TXCFG_BRST_DIS 0x00080000
+#define TXCFG_MXDMA1024 0x00000000
+#define TXCFG_MXDMA512 0x00700000
+#define TXCFG_MXDMA256 0x00600000
+#define TXCFG_MXDMA128 0x00500000
+#define TXCFG_MXDMA64 0x00400000
+#define TXCFG_MXDMA32 0x00300000
+#define TXCFG_MXDMA16 0x00200000
+#define TXCFG_MXDMA8 0x00100000
+
+#define CFG_LNKSTS 0x80000000
+#define CFG_SPDSTS 0x60000000
+#define CFG_SPDSTS1 0x40000000
+#define CFG_SPDSTS0 0x20000000
+#define CFG_DUPSTS 0x10000000
+#define CFG_TBI_EN 0x01000000
+#define CFG_MODE_1000 0x00400000
+/* Ramit : Dont' ever use AUTO_1000, it never works and is buggy.
+ * Read the Phy response and then configure the MAC accordingly */
+#define CFG_AUTO_1000 0x00200000
+#define CFG_PINT_CTL 0x001c0000
+#define CFG_PINT_DUPSTS 0x00100000
+#define CFG_PINT_LNKSTS 0x00080000
+#define CFG_PINT_SPDSTS 0x00040000
+#define CFG_TMRTEST 0x00020000
+#define CFG_MRM_DIS 0x00010000
+#define CFG_MWI_DIS 0x00008000
+#define CFG_T64ADDR 0x00004000
+#define CFG_PCI64_DET 0x00002000
+#define CFG_DATA64_EN 0x00001000
+#define CFG_M64ADDR 0x00000800
+#define CFG_PHY_RST 0x00000400
+#define CFG_PHY_DIS 0x00000200
+#define CFG_EXTSTS_EN 0x00000100
+#define CFG_REQALG 0x00000080
+#define CFG_SB 0x00000040
+#define CFG_POW 0x00000020
+#define CFG_EXD 0x00000010
+#define CFG_PESEL 0x00000008
+#define CFG_BROM_DIS 0x00000004
+#define CFG_EXT_125 0x00000002
+#define CFG_BEM 0x00000001
+
+#define EXTSTS_UDPPKT 0x00200000
+#define EXTSTS_TCPPKT 0x00080000
+#define EXTSTS_IPPKT 0x00020000
+
+#define SPDSTS_POLARITY (CFG_SPDSTS1 | CFG_SPDSTS0 | CFG_DUPSTS | (lnksts ? CFG_LNKSTS : 0))
+
+#define MIBC_MIBS 0x00000008
+#define MIBC_ACLR 0x00000004
+#define MIBC_FRZ 0x00000002
+#define MIBC_WRN 0x00000001
+
+#define PCR_PSEN (1 << 31)
+#define PCR_PS_MCAST (1 << 30)
+#define PCR_PS_DA (1 << 29)
+#define PCR_STHI_8 (3 << 23)
+#define PCR_STLO_4 (1 << 23)
+#define PCR_FFHI_8K (3 << 21)
+#define PCR_FFLO_4K (1 << 21)
+#define PCR_PAUSE_CNT 0xFFFE
+
+#define RXCFG_AEP 0x80000000
+#define RXCFG_ARP 0x40000000
+#define RXCFG_STRIPCRC 0x20000000
+#define RXCFG_RX_FD 0x10000000
+#define RXCFG_ALP 0x08000000
+#define RXCFG_AIRL 0x04000000
+#define RXCFG_MXDMA512 0x00700000
+#define RXCFG_DRTH 0x0000003e
+#define RXCFG_DRTH0 0x00000002
+
+#define RFCR_RFEN 0x80000000
+#define RFCR_AAB 0x40000000
+#define RFCR_AAM 0x20000000
+#define RFCR_AAU 0x10000000
+#define RFCR_APM 0x08000000
+#define RFCR_APAT 0x07800000
+#define RFCR_APAT3 0x04000000
+#define RFCR_APAT2 0x02000000
+#define RFCR_APAT1 0x01000000
+#define RFCR_APAT0 0x00800000
+#define RFCR_AARP 0x00400000
+#define RFCR_MHEN 0x00200000
+#define RFCR_UHEN 0x00100000
+#define RFCR_ULM 0x00080000
+
+#define VRCR_RUDPE 0x00000080
+#define VRCR_RTCPE 0x00000040
+#define VRCR_RIPE 0x00000020
+#define VRCR_IPEN 0x00000010
+#define VRCR_DUTF 0x00000008
+#define VRCR_DVTF 0x00000004
+#define VRCR_VTREN 0x00000002
+#define VRCR_VTDEN 0x00000001
+
+#define VTCR_PPCHK 0x00000008
+#define VTCR_GCHK 0x00000004
+#define VTCR_VPPTI 0x00000002
+#define VTCR_VGTI 0x00000001
+
+#define CR 0x00
+#define CFG 0x04
+#define MEAR 0x08
+#define PTSCR 0x0c
+#define ISR 0x10
+#define IMR 0x14
+#define IER 0x18
+#define IHR 0x1c
+#define TXDP 0x20
+#define TXDP_HI 0x24
+#define TXCFG 0x28
+#define GPIOR 0x2c
+#define RXDP 0x30
+#define RXDP_HI 0x34
+#define RXCFG 0x38
+#define PQCR 0x3c
+#define WCSR 0x40
+#define PCR 0x44
+#define RFCR 0x48
+#define RFDR 0x4c
+
+#define SRR 0x58
+
+#define VRCR 0xbc
+#define VTCR 0xc0
+#define VDR 0xc4
+#define CCSR 0xcc
+
+#define TBICR 0xe0
+#define TBISR 0xe4
+#define TANAR 0xe8
+#define TANLPAR 0xec
+#define TANER 0xf0
+#define TESR 0xf4
+
+#define TBICR_MR_AN_ENABLE 0x00001000
+#define TBICR_MR_RESTART_AN 0x00000200
+
+#define TBISR_MR_LINK_STATUS 0x00000020
+#define TBISR_MR_AN_COMPLETE 0x00000004
+
+#define TANAR_PS2 0x00000100
+#define TANAR_PS1 0x00000080
+#define TANAR_HALF_DUP 0x00000040
+#define TANAR_FULL_DUP 0x00000020
+
+#define GPIOR_GP5_OE 0x00000200
+#define GPIOR_GP4_OE 0x00000100
+#define GPIOR_GP3_OE 0x00000080
+#define GPIOR_GP2_OE 0x00000040
+#define GPIOR_GP1_OE 0x00000020
+#define GPIOR_GP3_OUT 0x00000004
+#define GPIOR_GP1_OUT 0x00000001
+
+#define LINK_AUTONEGOTIATE 0x01
+#define LINK_DOWN 0x02
+#define LINK_UP 0x04
+
+
+#define __kick_rx() writel(CR_RXE, ns->base + CR)
+
+#define kick_rx() do { \
+ dprintf(("kick_rx: maybe kicking\n")); \
+ writel(virt_to_le32desc(&rx_ring[ns->cur_rx]), ns->base + RXDP); \
+ if (ns->next_rx == ns->next_empty) \
+ printf("uh-oh: next_rx == next_empty???\n"); \
+ __kick_rx(); \
+} while(0)
+
+
+#ifdef USE_64BIT_ADDR
+#define HW_ADDR_LEN 8
+#else
+#define HW_ADDR_LEN 4
+#endif
+
+#define CMDSTS_OWN 0x80000000
+#define CMDSTS_MORE 0x40000000
+#define CMDSTS_INTR 0x20000000
+#define CMDSTS_ERR 0x10000000
+#define CMDSTS_OK 0x08000000
+#define CMDSTS_LEN_MASK 0x0000ffff
+
+#define CMDSTS_DEST_MASK 0x01800000
+#define CMDSTS_DEST_SELF 0x00800000
+#define CMDSTS_DEST_MULTI 0x01000000
+
+#define DESC_SIZE 8 /* Should be cache line sized */
+
+#ifdef USE_64BIT_ADDR
+struct ring_desc {
+ uint64_t link;
+ uint64_t bufptr;
+ u32 cmdsts;
+ u32 extsts; /* Extended status field */
+};
+#else
+struct ring_desc {
+ u32 link;
+ u32 bufptr;
+ u32 cmdsts;
+ u32 extsts; /* Extended status field */
+};
+#endif
+
+/* Define the TX Descriptor */
+static struct ring_desc tx_ring[NR_TX_DESC]
+ __attribute__ ((aligned(8)));
+
+/* Create a static buffer of size REAL_RX_BUF_SIZE for each
+TX Descriptor. All descriptors point to a
+part of this buffer */
+static unsigned char txb[NR_TX_DESC * REAL_RX_BUF_SIZE];
+
+/* Define the TX Descriptor */
+static struct ring_desc rx_ring[NR_RX_DESC]
+ __attribute__ ((aligned(8)));
+
+/* Create a static buffer of size REAL_RX_BUF_SIZE for each
+RX Descriptor All descriptors point to a
+part of this buffer */
+static unsigned char rxb[NR_RX_DESC * REAL_RX_BUF_SIZE]
+ __attribute__ ((aligned(8)));
+
+/* Private Storage for the NIC */
+struct ns83820_private {
+ u8 *base;
+ int up;
+ long idle;
+ u32 *next_rx_desc;
+ u16 next_rx, next_empty;
+ u32 cur_rx;
+ u32 *descs;
+ unsigned ihr;
+ u32 CFG_cache;
+ u32 MEAR_cache;
+ u32 IMR_cache;
+ int linkstate;
+ u16 tx_done_idx;
+ u16 tx_idx;
+ u16 tx_intr_idx;
+ u32 phy_descs;
+ u32 *tx_descs;
+
+} nsx;
+static struct ns83820_private *ns;
+
+static void phy_intr(struct nic *nic __unused)
+{
+ static char *speeds[] =
+ { "10", "100", "1000", "1000(?)", "1000F" };
+ u32 cfg, new_cfg;
+ u32 tbisr, tanar, tanlpar;
+ int speed, fullduplex, newlinkstate;
+
+ cfg = readl(ns->base + CFG) ^ SPDSTS_POLARITY;
+ if (ns->CFG_cache & CFG_TBI_EN) {
+ /* we have an optical transceiver */
+ tbisr = readl(ns->base + TBISR);
+ tanar = readl(ns->base + TANAR);
+ tanlpar = readl(ns->base + TANLPAR);
+ dprintf(("phy_intr: tbisr=%hX, tanar=%hX, tanlpar=%hX\n",
+ tbisr, tanar, tanlpar));
+
+ if ((fullduplex = (tanlpar & TANAR_FULL_DUP)
+ && (tanar & TANAR_FULL_DUP))) {
+
+ /* both of us are full duplex */
+ writel(readl(ns->base + TXCFG)
+ | TXCFG_CSI | TXCFG_HBI | TXCFG_ATP,
+ ns->base + TXCFG);
+ writel(readl(ns->base + RXCFG) | RXCFG_RX_FD,
+ ns->base + RXCFG);
+ /* Light up full duplex LED */
+ writel(readl(ns->base + GPIOR) | GPIOR_GP1_OUT,
+ ns->base + GPIOR);
+
+ } else if (((tanlpar & TANAR_HALF_DUP)
+ && (tanar & TANAR_HALF_DUP))
+ || ((tanlpar & TANAR_FULL_DUP)
+ && (tanar & TANAR_HALF_DUP))
+ || ((tanlpar & TANAR_HALF_DUP)
+ && (tanar & TANAR_FULL_DUP))) {
+
+ /* one or both of us are half duplex */
+ writel((readl(ns->base + TXCFG)
+ & ~(TXCFG_CSI | TXCFG_HBI)) | TXCFG_ATP,
+ ns->base + TXCFG);
+ writel(readl(ns->base + RXCFG) & ~RXCFG_RX_FD,
+ ns->base + RXCFG);
+ /* Turn off full duplex LED */
+ writel(readl(ns->base + GPIOR) & ~GPIOR_GP1_OUT,
+ ns->base + GPIOR);
+ }
+
+ speed = 4; /* 1000F */
+
+ } else {
+ /* we have a copper transceiver */
+ new_cfg =
+ ns->CFG_cache & ~(CFG_SB | CFG_MODE_1000 | CFG_SPDSTS);
+
+ if (cfg & CFG_SPDSTS1)
+ new_cfg |= CFG_MODE_1000;
+ else
+ new_cfg &= ~CFG_MODE_1000;
+
+ speed = ((cfg / CFG_SPDSTS0) & 3);
+ fullduplex = (cfg & CFG_DUPSTS);
+
+ if (fullduplex)
+ new_cfg |= CFG_SB;
+
+ if ((cfg & CFG_LNKSTS) &&
+ ((new_cfg ^ ns->CFG_cache) & CFG_MODE_1000)) {
+ writel(new_cfg, ns->base + CFG);
+ ns->CFG_cache = new_cfg;
+ }
+
+ ns->CFG_cache &= ~CFG_SPDSTS;
+ ns->CFG_cache |= cfg & CFG_SPDSTS;
+ }
+
+ newlinkstate = (cfg & CFG_LNKSTS) ? LINK_UP : LINK_DOWN;
+
+ if (newlinkstate & LINK_UP && ns->linkstate != newlinkstate) {
+ printf("link now %s mbps, %s duplex and up.\n",
+ speeds[speed], fullduplex ? "full" : "half");
+ } else if (newlinkstate & LINK_DOWN
+ && ns->linkstate != newlinkstate) {
+ printf("link now down.\n");
+ }
+ ns->linkstate = newlinkstate;
+}
+static void ns83820_set_multicast(struct nic *nic __unused);
+static void ns83820_setup_rx(struct nic *nic)
+{
+ unsigned i;
+ ns->idle = 1;
+ ns->next_rx = 0;
+ ns->next_rx_desc = ns->descs;
+ ns->next_empty = 0;
+ ns->cur_rx = 0;
+
+
+ for (i = 0; i < NR_RX_DESC; i++) {
+ rx_ring[i].link = virt_to_le32desc(&rx_ring[i + 1]);
+ rx_ring[i].bufptr =
+ virt_to_le32desc(&rxb[i * REAL_RX_BUF_SIZE]);
+ rx_ring[i].cmdsts = cpu_to_le32(REAL_RX_BUF_SIZE);
+ rx_ring[i].extsts = cpu_to_le32(0);
+ }
+// No need to wrap the ring
+// rx_ring[i].link = virt_to_le32desc(&rx_ring[0]);
+ writel(0, ns->base + RXDP_HI);
+ writel(virt_to_le32desc(&rx_ring[0]), ns->base + RXDP);
+
+ dprintf(("starting receiver\n"));
+
+ writel(0x0001, ns->base + CCSR);
+ writel(0, ns->base + RFCR);
+ writel(0x7fc00000, ns->base + RFCR);
+ writel(0xffc00000, ns->base + RFCR);
+
+ ns->up = 1;
+
+ phy_intr(nic);
+
+ /* Okay, let it rip */
+ ns->IMR_cache |= ISR_PHY;
+ ns->IMR_cache |= ISR_RXRCMP;
+ //dev->IMR_cache |= ISR_RXERR;
+ //dev->IMR_cache |= ISR_RXOK;
+ ns->IMR_cache |= ISR_RXORN;
+ ns->IMR_cache |= ISR_RXSOVR;
+ ns->IMR_cache |= ISR_RXDESC;
+ ns->IMR_cache |= ISR_RXIDLE;
+ ns->IMR_cache |= ISR_TXDESC;
+ ns->IMR_cache |= ISR_TXIDLE;
+
+ // No reason to enable interupts...
+ // writel(ns->IMR_cache, ns->base + IMR);
+ // writel(1, ns->base + IER);
+ ns83820_set_multicast(nic);
+ kick_rx();
+}
+
+
+static void ns83820_do_reset(struct nic *nic __unused, u32 which)
+{
+ dprintf(("resetting chip...\n"));
+ writel(which, ns->base + CR);
+ do {
+
+ } while (readl(ns->base + CR) & which);
+ dprintf(("okay!\n"));
+}
+
+static void ns83820_reset(struct nic *nic)
+{
+ unsigned i;
+ dprintf(("ns83820_reset\n"));
+
+ writel(0, ns->base + PQCR);
+
+ ns83820_setup_rx(nic);
+
+ for (i = 0; i < NR_TX_DESC; i++) {
+ tx_ring[i].link = 0;
+ tx_ring[i].bufptr = 0;
+ tx_ring[i].cmdsts = cpu_to_le32(0);
+ tx_ring[i].extsts = cpu_to_le32(0);
+ }
+
+ ns->tx_idx = 0;
+ ns->tx_done_idx = 0;
+ writel(0, ns->base + TXDP_HI);
+ return;
+}
+static void ns83820_getmac(struct nic *nic __unused, u8 * mac)
+{
+ unsigned i;
+ for (i = 0; i < 3; i++) {
+ u32 data;
+ /* Read from the perfect match memory: this is loaded by
+ * the chip from the EEPROM via the EELOAD self test.
+ */
+ writel(i * 2, ns->base + RFCR);
+ data = readl(ns->base + RFDR);
+ *mac++ = data;
+ *mac++ = data >> 8;
+ }
+}
+
+static void ns83820_set_multicast(struct nic *nic __unused)
+{
+ u8 *rfcr = ns->base + RFCR;
+ u32 and_mask = 0xffffffff;
+ u32 or_mask = 0;
+ u32 val;
+
+ /* Support Multicast */
+ and_mask &= ~(RFCR_AAU | RFCR_AAM);
+ or_mask |= RFCR_AAM;
+ val = (readl(rfcr) & and_mask) | or_mask;
+ /* Ramit : RFCR Write Fix doc says RFEN must be 0 modify other bits */
+ writel(val & ~RFCR_RFEN, rfcr);
+ writel(val, rfcr);
+
+}
+static void ns83820_run_bist(struct nic *nic __unused, const char *name,
+ u32 enable, u32 done, u32 fail)
+{
+ int timed_out = 0;
+ long start;
+ u32 status;
+ int loops = 0;
+
+ dprintf(("start %s\n", name))
+
+ start = currticks();
+
+ writel(enable, ns->base + PTSCR);
+ for (;;) {
+ loops++;
+ status = readl(ns->base + PTSCR);
+ if (!(status & enable))
+ break;
+ if (status & done)
+ break;
+ if (status & fail)
+ break;
+ if ((currticks() - start) >= HZ) {
+ timed_out = 1;
+ break;
+ }
+ }
+
+ if (status & fail)
+ printf("%s failed! (0x%hX & 0x%hX)\n", name, status, fail);
+ else if (timed_out)
+ printf("run_bist %s timed out! (%hX)\n", name, status);
+ dprintf(("done %s in %d loops\n", name, loops));
+}
+
+/*************************************
+Check Link
+*************************************/
+static void ns83820_check_intr(struct nic *nic) {
+ int i;
+ u32 isr = readl(ns->base + ISR);
+ if(ISR_PHY & isr)
+ phy_intr(nic);
+ if(( ISR_RXIDLE | ISR_RXDESC | ISR_RXERR) & isr)
+ kick_rx();
+ for (i = 0; i < NR_RX_DESC; i++) {
+ if (rx_ring[i].cmdsts == CMDSTS_OWN) {
+// rx_ring[i].link = virt_to_le32desc(&rx_ring[i + 1]);
+ rx_ring[i].cmdsts = cpu_to_le32(REAL_RX_BUF_SIZE);
+ }
+ }
+}
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int ns83820_poll(struct nic *nic, int retrieve)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+ u32 cmdsts;
+ int entry = ns->cur_rx;
+
+ ns83820_check_intr(nic);
+
+ cmdsts = le32_to_cpu(rx_ring[entry].cmdsts);
+
+ if ( ! ( (CMDSTS_OWN & (cmdsts)) && (cmdsts != (CMDSTS_OWN)) ) )
+ return 0;
+
+ if ( ! retrieve ) return 1;
+
+ if (! (CMDSTS_OK & cmdsts) )
+ return 0;
+
+ nic->packetlen = cmdsts & 0xffff;
+ memcpy(nic->packet,
+ rxb + (entry * REAL_RX_BUF_SIZE),
+ nic->packetlen);
+ // rx_ring[entry].link = 0;
+ rx_ring[entry].cmdsts = cpu_to_le32(CMDSTS_OWN);
+
+ ns->cur_rx = ++ns->cur_rx % NR_RX_DESC;
+
+ if (ns->cur_rx == 0) /* We have wrapped the ring */
+ kick_rx();
+
+ return 1;
+}
+
+static inline void kick_tx(struct nic *nic __unused)
+{
+ dprintf(("kick_tx\n"));
+ writel(CR_TXE, ns->base + CR);
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void ns83820_transmit(struct nic *nic, const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p)
+{ /* Packet */
+ /* send the packet to destination */
+
+ u16 nstype;
+ u32 cmdsts, extsts;
+ int cur_tx = 0;
+ u32 isr = readl(ns->base + ISR);
+ if (ISR_TXIDLE & isr)
+ kick_tx(nic);
+ /* point to the current txb incase multiple tx_rings are used */
+ memcpy(txb, d, ETH_ALEN);
+ memcpy(txb + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ nstype = htons((u16) t);
+ memcpy(txb + 2 * ETH_ALEN, (u8 *) & nstype, 2);
+ memcpy(txb + ETH_HLEN, p, s);
+ s += ETH_HLEN;
+ s &= 0x0FFF;
+ while (s < ETH_ZLEN)
+ txb[s++] = '\0';
+
+ /* Setup the transmit descriptor */
+ extsts = 0;
+ extsts |= EXTSTS_UDPPKT;
+
+ tx_ring[cur_tx].bufptr = virt_to_le32desc(&txb);
+ tx_ring[cur_tx].extsts = cpu_to_le32(extsts);
+
+ cmdsts = cpu_to_le32(0);
+ cmdsts |= cpu_to_le32(CMDSTS_OWN | s);
+ tx_ring[cur_tx].cmdsts = cpu_to_le32(cmdsts);
+
+ writel(virt_to_le32desc(&tx_ring[0]), ns->base + TXDP);
+ kick_tx(nic);
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void ns83820_disable(struct dev *dev)
+{
+ /* put the card in its initial state */
+ /* This function serves 3 purposes.
+ * This disables DMA and interrupts so we don't receive
+ * unexpected packets or interrupts from the card after
+ * etherboot has finished.
+ * This frees resources so etherboot may use
+ * this driver on another interface
+ * This allows etherboot to reinitialize the interface
+ * if something is something goes wrong.
+ */
+ /* disable interrupts */
+ writel(0, ns->base + IMR);
+ writel(0, ns->base + IER);
+ readl(ns->base + IER);
+
+ ns->up = 0;
+
+ ns83820_do_reset((struct nic *) dev, CR_RST);
+
+ ns->IMR_cache &=
+ ~(ISR_RXOK | ISR_RXDESC | ISR_RXERR | ISR_RXEARLY |
+ ISR_RXIDLE);
+ writel(ns->IMR_cache, ns->base + IMR);
+
+ /* touch the pci bus... */
+ readl(ns->base + IMR);
+
+ /* assumes the transmitter is already disabled and reset */
+ writel(0, ns->base + RXDP_HI);
+ writel(0, ns->base + RXDP);
+}
+
+/**************************************************************************
+IRQ - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void ns83820_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+
+#define board_found 1
+#define valid_link 0
+static int ns83820_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *) dev;
+ int sz;
+ long addr;
+ int using_dac = 0;
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ printf("ns83820.c: Found %s, vendor=0x%hX, device=0x%hX\n",
+ pci->name, pci->vendor, pci->dev_id);
+
+ /* point to private storage */
+ ns = &nsx;
+
+ adjust_pci_device(pci);
+
+ addr = pci_bar_start(pci, PCI_BASE_ADDRESS_1);
+ sz = pci_bar_size(pci, PCI_BASE_ADDRESS_1);
+
+ ns->base = ioremap(addr, (1UL << 12));
+// ns->base = ioremap(addr, sz);
+
+ if (!ns->base)
+ return 0;
+
+ nic->irqno = 0;
+ nic->ioaddr = pci->ioaddr & ~3;
+
+ /* disable interrupts */
+ writel(0, ns->base + IMR);
+ writel(0, ns->base + IER);
+ readl(ns->base + IER);
+
+ ns->IMR_cache = 0;
+
+ ns83820_do_reset(nic, CR_RST);
+
+ /* Must reset the ram bist before running it */
+ writel(PTSCR_RBIST_RST, ns->base + PTSCR);
+ ns83820_run_bist(nic, "sram bist", PTSCR_RBIST_EN,
+ PTSCR_RBIST_DONE, PTSCR_RBIST_FAIL);
+ ns83820_run_bist(nic, "eeprom bist", PTSCR_EEBIST_EN, 0,
+ PTSCR_EEBIST_FAIL);
+ ns83820_run_bist(nic, "eeprom load", PTSCR_EELOAD_EN, 0, 0);
+
+ /* I love config registers */
+ ns->CFG_cache = readl(ns->base + CFG);
+
+ if ((ns->CFG_cache & CFG_PCI64_DET)) {
+ printf("%s: detected 64 bit PCI data bus.\n", pci->name);
+ /*dev->CFG_cache |= CFG_DATA64_EN; */
+ if (!(ns->CFG_cache & CFG_DATA64_EN))
+ printf
+ ("%s: EEPROM did not enable 64 bit bus. Disabled.\n",
+ pci->name);
+ } else
+ ns->CFG_cache &= ~(CFG_DATA64_EN);
+
+ ns->CFG_cache &= (CFG_TBI_EN | CFG_MRM_DIS | CFG_MWI_DIS |
+ CFG_T64ADDR | CFG_DATA64_EN | CFG_EXT_125 |
+ CFG_M64ADDR);
+ ns->CFG_cache |=
+ CFG_PINT_DUPSTS | CFG_PINT_LNKSTS | CFG_PINT_SPDSTS |
+ CFG_EXTSTS_EN | CFG_EXD | CFG_PESEL;
+ ns->CFG_cache |= CFG_REQALG;
+ ns->CFG_cache |= CFG_POW;
+ ns->CFG_cache |= CFG_TMRTEST;
+
+ /* When compiled with 64 bit addressing, we must always enable
+ * the 64 bit descriptor format.
+ */
+#ifdef USE_64BIT_ADDR
+ ns->CFG_cache |= CFG_M64ADDR;
+#endif
+
+//FIXME: Enable section on dac or remove this
+ if (using_dac)
+ ns->CFG_cache |= CFG_T64ADDR;
+
+ /* Big endian mode does not seem to do what the docs suggest */
+ ns->CFG_cache &= ~CFG_BEM;
+
+ /* setup optical transceiver if we have one */
+ if (ns->CFG_cache & CFG_TBI_EN) {
+ dprintf(("%s: enabling optical transceiver\n", pci->name));
+ writel(readl(ns->base + GPIOR) | 0x3e8, ns->base + GPIOR);
+
+ /* setup auto negotiation feature advertisement */
+ writel(readl(ns->base + TANAR)
+ | TANAR_HALF_DUP | TANAR_FULL_DUP,
+ ns->base + TANAR);
+
+ /* start auto negotiation */
+ writel(TBICR_MR_AN_ENABLE | TBICR_MR_RESTART_AN,
+ ns->base + TBICR);
+ writel(TBICR_MR_AN_ENABLE, ns->base + TBICR);
+ ns->linkstate = LINK_AUTONEGOTIATE;
+
+ ns->CFG_cache |= CFG_MODE_1000;
+ }
+ writel(ns->CFG_cache, ns->base + CFG);
+ dprintf(("CFG: %hX\n", ns->CFG_cache));
+
+ /* FIXME: reset_phy is defaulted to 0, should we reset anyway? */
+ if (reset_phy) {
+ dprintf(("%s: resetting phy\n", pci->name));
+ writel(ns->CFG_cache | CFG_PHY_RST, ns->base + CFG);
+ writel(ns->CFG_cache, ns->base + CFG);
+ }
+#if 0 /* Huh? This sets the PCI latency register. Should be done via
+ * the PCI layer. FIXME.
+ */
+ if (readl(dev->base + SRR))
+ writel(readl(dev->base + 0x20c) | 0xfe00,
+ dev->base + 0x20c);
+#endif
+
+ /* Note! The DMA burst size interacts with packet
+ * transmission, such that the largest packet that
+ * can be transmitted is 8192 - FLTH - burst size.
+ * If only the transmit fifo was larger...
+ */
+ /* Ramit : 1024 DMA is not a good idea, it ends up banging
+ * some DELL and COMPAQ SMP systems */
+ writel(TXCFG_CSI | TXCFG_HBI | TXCFG_ATP | TXCFG_MXDMA512
+ | ((1600 / 32) * 0x100), ns->base + TXCFG);
+
+ /* Set Rx to full duplex, don't accept runt, errored, long or length
+ * range errored packets. Use 512 byte DMA.
+ */
+ /* Ramit : 1024 DMA is not a good idea, it ends up banging
+ * some DELL and COMPAQ SMP systems
+ * Turn on ALP, only we are accpeting Jumbo Packets */
+ writel(RXCFG_AEP | RXCFG_ARP | RXCFG_AIRL | RXCFG_RX_FD
+ | RXCFG_STRIPCRC
+ //| RXCFG_ALP
+ | (RXCFG_MXDMA512) | 0, ns->base + RXCFG);
+
+ /* Disable priority queueing */
+ writel(0, ns->base + PQCR);
+
+ /* Enable IP checksum validation and detetion of VLAN headers.
+ * Note: do not set the reject options as at least the 0x102
+ * revision of the chip does not properly accept IP fragments
+ * at least for UDP.
+ */
+ /* Ramit : Be sure to turn on RXCFG_ARP if VLAN's are enabled, since
+ * the MAC it calculates the packetsize AFTER stripping the VLAN
+ * header, and if a VLAN Tagged packet of 64 bytes is received (like
+ * a ping with a VLAN header) then the card, strips the 4 byte VLAN
+ * tag and then checks the packet size, so if RXCFG_ARP is not enabled,
+ * it discrards it!. These guys......
+ */
+ writel(VRCR_IPEN | VRCR_VTDEN, ns->base + VRCR);
+
+ /* Enable per-packet TCP/UDP/IP checksumming */
+ writel(VTCR_PPCHK, ns->base + VTCR);
+
+ /* Ramit : Enable async and sync pause frames */
+// writel(0, ns->base + PCR);
+ writel((PCR_PS_MCAST | PCR_PS_DA | PCR_PSEN | PCR_FFLO_4K |
+ PCR_FFHI_8K | PCR_STLO_4 | PCR_STHI_8 | PCR_PAUSE_CNT),
+ ns->base + PCR);
+
+ /* Disable Wake On Lan */
+ writel(0, ns->base + WCSR);
+
+ ns83820_getmac(nic, nic->node_addr);
+ printf("%! at ioaddr 0x%hX, ", nic->node_addr, ns->base);
+
+ if (using_dac) {
+ dprintf(("%s: using 64 bit addressing.\n", pci->name));
+ }
+
+ dprintf(("%s: DP83820 %d.%d: %! io=0x%hX\n",
+ pci->name,
+ (unsigned) readl(ns->base + SRR) >> 8,
+ (unsigned) readl(ns->base + SRR) & 0xff,
+ nic->node_addr, pci->ioaddr));
+
+#ifdef PHY_CODE_IS_FINISHED
+ ns83820_probe_phy(dev);
+#endif
+
+ ns83820_reset(nic);
+ /* point to NIC specific routines */
+ dev->disable = ns83820_disable;
+ nic->poll = ns83820_poll;
+ nic->transmit = ns83820_transmit;
+ nic->irq = ns83820_irq;
+ return 1;
+}
+
+static struct pci_id ns83820_nics[] = {
+ PCI_ROM(0x100b, 0x0022, "ns83820", "National Semiconductor 83820"),
+};
+
+static struct pci_driver ns83820_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "NS83820/PCI",
+ .probe = ns83820_probe,
+ .ids = ns83820_nics,
+ .id_count = sizeof(ns83820_nics) / sizeof(ns83820_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/ns8390.c b/src/drivers/net/ns8390.c
new file mode 100644
index 00000000..dddc0819
--- /dev/null
+++ b/src/drivers/net/ns8390.c
@@ -0,0 +1,1016 @@
+/**************************************************************************
+ETHERBOOT - BOOTP/TFTP Bootstrap Program
+
+Author: Martin Renters
+ Date: May/94
+
+ This code is based heavily on David Greenman's if_ed.c driver
+
+ Copyright (C) 1993-1994, David Greenman, Martin Renters.
+ This software may be used, modified, copied, distributed, and sold, in
+ both source and binary form provided that the above copyright and these
+ terms are retained. Under no circumstances are the authors responsible for
+ the proper functioning of this software, nor do the authors assume any
+ responsibility for damages incurred with its use.
+
+Multicast support added by Timothy Legge (timlegge@users.sourceforge.net) 09/28/2003
+Relocation support added by Ken Yap (ken_yap@users.sourceforge.net) 28/12/02
+3c503 support added by Bill Paul (wpaul@ctr.columbia.edu) on 11/15/94
+SMC8416 support added by Bill Paul (wpaul@ctr.columbia.edu) on 12/25/94
+3c503 PIO support added by Jim Hague (jim.hague@acm.org) on 2/17/98
+RX overrun by Klaus Espenlaub (espenlaub@informatik.uni-ulm.de) on 3/10/99
+ parts taken from the Linux 8390 driver (by Donald Becker and Paul Gortmaker)
+SMC8416 PIO support added by Andrew Bettison (andrewb@zip.com.au) on 4/3/02
+ based on the Linux 8390 driver (by Donald Becker and Paul Gortmaker)
+
+**************************************************************************/
+
+#include "etherboot.h"
+#include "nic.h"
+#include "ns8390.h"
+#ifdef INCLUDE_NS8390
+#include "pci.h"
+#else
+#include "isa.h"
+#endif
+
+static unsigned char eth_vendor, eth_flags;
+#ifdef INCLUDE_WD
+static unsigned char eth_laar;
+#endif
+static unsigned short eth_nic_base, eth_asic_base;
+static unsigned char eth_memsize, eth_rx_start, eth_tx_start;
+static Address eth_bmem, eth_rmem;
+static unsigned char eth_drain_receiver;
+
+#ifdef INCLUDE_WD
+static struct wd_board {
+ const char *name;
+ char id;
+ char flags;
+ char memsize;
+} wd_boards[] = {
+ {"WD8003S", TYPE_WD8003S, 0, MEM_8192},
+ {"WD8003E", TYPE_WD8003E, 0, MEM_8192},
+ {"WD8013EBT", TYPE_WD8013EBT, FLAG_16BIT, MEM_16384},
+ {"WD8003W", TYPE_WD8003W, 0, MEM_8192},
+ {"WD8003EB", TYPE_WD8003EB, 0, MEM_8192},
+ {"WD8013W", TYPE_WD8013W, FLAG_16BIT, MEM_16384},
+ {"WD8003EP/WD8013EP",
+ TYPE_WD8013EP, 0, MEM_8192},
+ {"WD8013WC", TYPE_WD8013WC, FLAG_16BIT, MEM_16384},
+ {"WD8013EPC", TYPE_WD8013EPC, FLAG_16BIT, MEM_16384},
+ {"SMC8216T", TYPE_SMC8216T, FLAG_16BIT | FLAG_790, MEM_16384},
+ {"SMC8216C", TYPE_SMC8216C, FLAG_16BIT | FLAG_790, MEM_16384},
+ {"SMC8416T", TYPE_SMC8416T, FLAG_16BIT | FLAG_790, MEM_8192},
+ {"SMC8416C/BT", TYPE_SMC8416C, FLAG_16BIT | FLAG_790, MEM_8192},
+ {"SMC8013EBP", TYPE_SMC8013EBP,FLAG_16BIT, MEM_16384},
+ {NULL, 0, 0, 0}
+};
+#endif
+
+#ifdef INCLUDE_3C503
+static unsigned char t503_output; /* AUI or internal xcvr (Thinnet) */
+#endif
+
+#if defined(INCLUDE_WD)
+#define ASIC_PIO WD_IAR
+#define eth_probe wd_probe
+#if defined(INCLUDE_3C503) || defined(INCLUDE_NE) || defined(INCLUDE_NS8390)
+Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
+#endif
+#endif
+
+#if defined(INCLUDE_3C503)
+#define eth_probe t503_probe
+#if defined(INCLUDE_NE) || defined(INCLUDE_NS8390) || defined(INCLUDE_WD)
+Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
+#endif
+#endif
+
+#if defined(INCLUDE_NE)
+#define eth_probe ne_probe
+#if defined(INCLUDE_NS8390) || defined(INCLUDE_3C503) || defined(INCLUDE_WD)
+Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
+#endif
+#endif
+
+#if defined(INCLUDE_NS8390)
+#define eth_probe nepci_probe
+#if defined(INCLUDE_NE) || defined(INCLUDE_3C503) || defined(INCLUDE_WD)
+Error you must only define one of INCLUDE_WD, INCLUDE_3C503, INCLUDE_NE, INCLUDE_NS8390
+#endif
+#endif
+
+#if defined(INCLUDE_3C503)
+#define ASIC_PIO _3COM_RFMSB
+#else
+#if defined(INCLUDE_NE) || defined(INCLUDE_NS8390)
+#define ASIC_PIO NE_DATA
+#endif
+#endif
+
+#if defined(INCLUDE_NE) || defined(INCLUDE_NS8390) || (defined(INCLUDE_3C503) && !defined(T503_SHMEM)) || (defined(INCLUDE_WD) && defined(WD_790_PIO))
+/**************************************************************************
+ETH_PIO_READ - Read a frame via Programmed I/O
+**************************************************************************/
+static void eth_pio_read(unsigned int src, unsigned char *dst, unsigned int cnt)
+{
+#ifdef INCLUDE_WD
+ outb(src & 0xff, eth_asic_base + WD_GP2);
+ outb(src >> 8, eth_asic_base + WD_GP2);
+#else
+ outb(D8390_COMMAND_RD2 |
+ D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
+ outb(cnt, eth_nic_base + D8390_P0_RBCR0);
+ outb(cnt>>8, eth_nic_base + D8390_P0_RBCR1);
+ outb(src, eth_nic_base + D8390_P0_RSAR0);
+ outb(src>>8, eth_nic_base + D8390_P0_RSAR1);
+ outb(D8390_COMMAND_RD0 |
+ D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
+
+#ifdef INCLUDE_3C503
+ outb(src & 0xff, eth_asic_base + _3COM_DALSB);
+ outb(src >> 8, eth_asic_base + _3COM_DAMSB);
+ outb(t503_output | _3COM_CR_START, eth_asic_base + _3COM_CR);
+#endif
+#endif
+
+ if (eth_flags & FLAG_16BIT)
+ cnt = (cnt + 1) >> 1;
+
+ while(cnt--) {
+#ifdef INCLUDE_3C503
+ while((inb(eth_asic_base + _3COM_STREG) & _3COM_STREG_DPRDY) == 0)
+ ;
+#endif
+
+ if (eth_flags & FLAG_16BIT) {
+ *((unsigned short *)dst) = inw(eth_asic_base + ASIC_PIO);
+ dst += 2;
+ }
+ else
+ *(dst++) = inb(eth_asic_base + ASIC_PIO);
+ }
+
+#ifdef INCLUDE_3C503
+ outb(t503_output, eth_asic_base + _3COM_CR);
+#endif
+}
+
+/**************************************************************************
+ETH_PIO_WRITE - Write a frame via Programmed I/O
+**************************************************************************/
+static void eth_pio_write(const unsigned char *src, unsigned int dst, unsigned int cnt)
+{
+#ifdef COMPEX_RL2000_FIX
+ unsigned int x;
+#endif /* COMPEX_RL2000_FIX */
+#ifdef INCLUDE_WD
+ outb(dst & 0xff, eth_asic_base + WD_GP2);
+ outb(dst >> 8, eth_asic_base + WD_GP2);
+#else
+ outb(D8390_COMMAND_RD2 |
+ D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
+ outb(D8390_ISR_RDC, eth_nic_base + D8390_P0_ISR);
+ outb(cnt, eth_nic_base + D8390_P0_RBCR0);
+ outb(cnt>>8, eth_nic_base + D8390_P0_RBCR1);
+ outb(dst, eth_nic_base + D8390_P0_RSAR0);
+ outb(dst>>8, eth_nic_base + D8390_P0_RSAR1);
+ outb(D8390_COMMAND_RD1 |
+ D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
+
+#ifdef INCLUDE_3C503
+ outb(dst & 0xff, eth_asic_base + _3COM_DALSB);
+ outb(dst >> 8, eth_asic_base + _3COM_DAMSB);
+
+ outb(t503_output | _3COM_CR_DDIR | _3COM_CR_START, eth_asic_base + _3COM_CR);
+#endif
+#endif
+
+ if (eth_flags & FLAG_16BIT)
+ cnt = (cnt + 1) >> 1;
+
+ while(cnt--)
+ {
+#ifdef INCLUDE_3C503
+ while((inb(eth_asic_base + _3COM_STREG) & _3COM_STREG_DPRDY) == 0)
+ ;
+#endif
+
+ if (eth_flags & FLAG_16BIT) {
+ outw(*((unsigned short *)src), eth_asic_base + ASIC_PIO);
+ src += 2;
+ }
+ else
+ outb(*(src++), eth_asic_base + ASIC_PIO);
+ }
+
+#ifdef INCLUDE_3C503
+ outb(t503_output, eth_asic_base + _3COM_CR);
+#else
+#ifdef COMPEX_RL2000_FIX
+ for (x = 0;
+ x < COMPEX_RL2000_TRIES &&
+ (inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC)
+ != D8390_ISR_RDC;
+ ++x);
+ if (x >= COMPEX_RL2000_TRIES)
+ printf("Warning: Compex RL2000 aborted wait!\n");
+#endif /* COMPEX_RL2000_FIX */
+#ifndef INCLUDE_WD
+ while((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC)
+ != D8390_ISR_RDC);
+#endif
+#endif
+}
+#else
+/**************************************************************************
+ETH_PIO_READ - Dummy routine when NE2000 not compiled in
+**************************************************************************/
+static void eth_pio_read(unsigned int src __unused, unsigned char *dst __unused, unsigned int cnt __unused) {}
+#endif
+
+
+/**************************************************************************
+enable_multycast - Enable Multicast
+**************************************************************************/
+static void enable_multicast(unsigned short eth_nic_base)
+{
+ unsigned char mcfilter[8];
+ int i;
+ memset(mcfilter, 0xFF, 8);
+ outb(4, eth_nic_base+D8390_P0_RCR);
+ outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS1, eth_nic_base + D8390_P0_COMMAND);
+ for(i=0;i<8;i++)
+ {
+ outb(mcfilter[i], eth_nic_base + 8 + i);
+ if(inb(eth_nic_base + 8 + i)!=mcfilter[i])
+ printf("Error SMC 83C690 Multicast filter read/write mishap %d\n",i);
+ }
+ outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS0, eth_nic_base + D8390_P0_COMMAND);
+ outb(4 | 0x08, eth_nic_base+D8390_P0_RCR);
+}
+
+/**************************************************************************
+NS8390_RESET - Reset adapter
+**************************************************************************/
+static void ns8390_reset(struct nic *nic)
+{
+ int i;
+
+ eth_drain_receiver = 0;
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_790)
+ outb(D8390_COMMAND_PS0 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+ else
+#endif
+ outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
+ D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+ if (eth_flags & FLAG_16BIT)
+ outb(0x49, eth_nic_base+D8390_P0_DCR);
+ else
+ outb(0x48, eth_nic_base+D8390_P0_DCR);
+ outb(0, eth_nic_base+D8390_P0_RBCR0);
+ outb(0, eth_nic_base+D8390_P0_RBCR1);
+ outb(0x20, eth_nic_base+D8390_P0_RCR); /* monitor mode */
+ outb(2, eth_nic_base+D8390_P0_TCR);
+ outb(eth_tx_start, eth_nic_base+D8390_P0_TPSR);
+ outb(eth_rx_start, eth_nic_base+D8390_P0_PSTART);
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_790) {
+#ifdef WD_790_PIO
+ outb(0x10, eth_asic_base + 0x06); /* disable interrupts, enable PIO */
+ outb(0x01, eth_nic_base + 0x09); /* enable ring read auto-wrap */
+#else
+ outb(0, eth_nic_base + 0x09);
+#endif
+ }
+#endif
+ outb(eth_memsize, eth_nic_base+D8390_P0_PSTOP);
+ outb(eth_memsize - 1, eth_nic_base+D8390_P0_BOUND);
+ outb(0xFF, eth_nic_base+D8390_P0_ISR);
+ outb(0, eth_nic_base+D8390_P0_IMR);
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_790)
+ outb(D8390_COMMAND_PS1 |
+ D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+ else
+#endif
+ outb(D8390_COMMAND_PS1 |
+ D8390_COMMAND_RD2 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+ for (i=0; i<ETH_ALEN; i++)
+ outb(nic->node_addr[i], eth_nic_base+D8390_P1_PAR0+i);
+ for (i=0; i<ETH_ALEN; i++)
+ outb(0xFF, eth_nic_base+D8390_P1_MAR0+i);
+ outb(eth_rx_start, eth_nic_base+D8390_P1_CURR);
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_790)
+ outb(D8390_COMMAND_PS0 |
+ D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+ else
+#endif
+ outb(D8390_COMMAND_PS0 |
+ D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+ outb(0xFF, eth_nic_base+D8390_P0_ISR);
+ outb(0, eth_nic_base+D8390_P0_TCR); /* transmitter on */
+ outb(4, eth_nic_base+D8390_P0_RCR); /* allow rx broadcast frames */
+
+ enable_multicast(eth_nic_base);
+
+#ifdef INCLUDE_3C503
+ /*
+ * No way to tell whether or not we're supposed to use
+ * the 3Com's transceiver unless the user tells us.
+ * 'flags' should have some compile time default value
+ * which can be changed from the command menu.
+ */
+ t503_output = (nic->flags) ? 0 : _3COM_CR_XSEL;
+ outb(t503_output, eth_asic_base + _3COM_CR);
+#endif
+}
+
+static int ns8390_poll(struct nic *nic, int retrieve);
+
+#ifndef INCLUDE_3C503
+/**************************************************************************
+ETH_RX_OVERRUN - Bring adapter back to work after an RX overrun
+**************************************************************************/
+static void eth_rx_overrun(struct nic *nic)
+{
+ int start_time;
+
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_790)
+ outb(D8390_COMMAND_PS0 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+ else
+#endif
+ outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
+ D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
+
+ /* wait for at least 1.6ms - we wait one timer tick */
+ start_time = currticks();
+ while (currticks() - start_time <= 1)
+ /* Nothing */;
+
+ outb(0, eth_nic_base+D8390_P0_RBCR0); /* reset byte counter */
+ outb(0, eth_nic_base+D8390_P0_RBCR1);
+
+ /*
+ * Linux driver checks for interrupted TX here. This is not necessary,
+ * because the transmit routine waits until the frame is sent.
+ */
+
+ /* enter loopback mode and restart NIC */
+ outb(2, eth_nic_base+D8390_P0_TCR);
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_790)
+ outb(D8390_COMMAND_PS0 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+ else
+#endif
+ outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
+ D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+
+ /* clear the RX ring, acknowledge overrun interrupt */
+ eth_drain_receiver = 1;
+ while (ns8390_poll(nic, 1))
+ /* Nothing */;
+ eth_drain_receiver = 0;
+ outb(D8390_ISR_OVW, eth_nic_base+D8390_P0_ISR);
+
+ /* leave loopback mode - no packets to be resent (see Linux driver) */
+ outb(0, eth_nic_base+D8390_P0_TCR);
+}
+#endif /* INCLUDE_3C503 */
+
+/**************************************************************************
+NS8390_TRANSMIT - Transmit a frame
+**************************************************************************/
+static void ns8390_transmit(
+ struct nic *nic,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+#if defined(INCLUDE_3C503) || (defined(INCLUDE_WD) && ! defined(WD_790_PIO))
+ Address eth_vmem = bus_to_virt(eth_bmem);
+#endif
+#ifdef INCLUDE_3C503
+ if (!(eth_flags & FLAG_PIO)) {
+ memcpy((char *)eth_vmem, d, ETH_ALEN); /* dst */
+ memcpy((char *)eth_vmem+ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */
+ *((char *)eth_vmem+12) = t>>8; /* type */
+ *((char *)eth_vmem+13) = t;
+ memcpy((char *)eth_vmem+ETH_HLEN, p, s);
+ s += ETH_HLEN;
+ while (s < ETH_ZLEN) *((char *)eth_vmem+(s++)) = 0;
+ }
+#endif
+
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_16BIT) {
+ outb(eth_laar | WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
+ inb(0x84);
+ }
+#ifndef WD_790_PIO
+ /* Memory interface */
+ if (eth_flags & FLAG_790) {
+ outb(WD_MSR_MENB, eth_asic_base + WD_MSR);
+ inb(0x84);
+ }
+ inb(0x84);
+ memcpy((char *)eth_vmem, d, ETH_ALEN); /* dst */
+ memcpy((char *)eth_vmem+ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */
+ *((char *)eth_vmem+12) = t>>8; /* type */
+ *((char *)eth_vmem+13) = t;
+ memcpy((char *)eth_vmem+ETH_HLEN, p, s);
+ s += ETH_HLEN;
+ while (s < ETH_ZLEN) *((char *)eth_vmem+(s++)) = 0;
+ if (eth_flags & FLAG_790) {
+ outb(0, eth_asic_base + WD_MSR);
+ inb(0x84);
+ }
+#else
+ inb(0x84);
+#endif
+#endif
+
+#if defined(INCLUDE_3C503)
+ if (eth_flags & FLAG_PIO)
+#endif
+#if defined(INCLUDE_NE) || defined(INCLUDE_NS8390) || (defined(INCLUDE_3C503) && !defined(T503_SHMEM)) || (defined(INCLUDE_WD) && defined(WD_790_PIO))
+ {
+ /* Programmed I/O */
+ unsigned short type;
+ type = (t >> 8) | (t << 8);
+ eth_pio_write(d, eth_tx_start<<8, ETH_ALEN);
+ eth_pio_write(nic->node_addr, (eth_tx_start<<8)+ETH_ALEN, ETH_ALEN);
+ /* bcc generates worse code without (const+const) below */
+ eth_pio_write((unsigned char *)&type, (eth_tx_start<<8)+(ETH_ALEN+ETH_ALEN), 2);
+ eth_pio_write(p, (eth_tx_start<<8)+ETH_HLEN, s);
+ s += ETH_HLEN;
+ if (s < ETH_ZLEN) s = ETH_ZLEN;
+ }
+#endif
+#if defined(INCLUDE_3C503)
+#endif
+
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_16BIT) {
+ outb(eth_laar & ~WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
+ inb(0x84);
+ }
+ if (eth_flags & FLAG_790)
+ outb(D8390_COMMAND_PS0 |
+ D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+ else
+#endif
+ outb(D8390_COMMAND_PS0 |
+ D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+ outb(eth_tx_start, eth_nic_base+D8390_P0_TPSR);
+ outb(s, eth_nic_base+D8390_P0_TBCR0);
+ outb(s>>8, eth_nic_base+D8390_P0_TBCR1);
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_790)
+ outb(D8390_COMMAND_PS0 |
+ D8390_COMMAND_TXP | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+ else
+#endif
+ outb(D8390_COMMAND_PS0 |
+ D8390_COMMAND_TXP | D8390_COMMAND_RD2 |
+ D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
+}
+
+/**************************************************************************
+NS8390_POLL - Wait for a frame
+**************************************************************************/
+static int ns8390_poll(struct nic *nic, int retrieve)
+{
+ int ret = 0;
+ unsigned char rstat, curr, next;
+ unsigned short len, frag;
+ unsigned short pktoff;
+ unsigned char *p;
+ struct ringbuffer pkthdr;
+
+#ifndef INCLUDE_3C503
+ /* avoid infinite recursion: see eth_rx_overrun() */
+ if (!eth_drain_receiver && (inb(eth_nic_base+D8390_P0_ISR) & D8390_ISR_OVW)) {
+ eth_rx_overrun(nic);
+ return(0);
+ }
+#endif /* INCLUDE_3C503 */
+ rstat = inb(eth_nic_base+D8390_P0_RSR);
+ if (!(rstat & D8390_RSTAT_PRX)) return(0);
+ next = inb(eth_nic_base+D8390_P0_BOUND)+1;
+ if (next >= eth_memsize) next = eth_rx_start;
+ outb(D8390_COMMAND_PS1, eth_nic_base+D8390_P0_COMMAND);
+ curr = inb(eth_nic_base+D8390_P1_CURR);
+ outb(D8390_COMMAND_PS0, eth_nic_base+D8390_P0_COMMAND);
+ if (curr >= eth_memsize) curr=eth_rx_start;
+ if (curr == next) return(0);
+
+ if ( ! retrieve ) return 1;
+
+#ifdef INCLUDE_WD
+ if (eth_flags & FLAG_16BIT) {
+ outb(eth_laar | WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
+ inb(0x84);
+ }
+#ifndef WD_790_PIO
+ if (eth_flags & FLAG_790) {
+ outb(WD_MSR_MENB, eth_asic_base + WD_MSR);
+ inb(0x84);
+ }
+#endif
+ inb(0x84);
+#endif
+ pktoff = next << 8;
+ if (eth_flags & FLAG_PIO)
+ eth_pio_read(pktoff, (char *)&pkthdr, 4);
+ else
+ memcpy(&pkthdr, bus_to_virt(eth_rmem + pktoff), 4);
+ pktoff += sizeof(pkthdr);
+ /* incoming length includes FCS so must sub 4 */
+ len = pkthdr.len - 4;
+ if ((pkthdr.status & D8390_RSTAT_PRX) == 0 || len < ETH_ZLEN
+ || len > ETH_FRAME_LEN) {
+ printf("Bogus packet, ignoring\n");
+ return (0);
+ }
+ else {
+ p = nic->packet;
+ nic->packetlen = len; /* available to caller */
+ frag = (eth_memsize << 8) - pktoff;
+ if (len > frag) { /* We have a wrap-around */
+ /* read first part */
+ if (eth_flags & FLAG_PIO)
+ eth_pio_read(pktoff, p, frag);
+ else
+ memcpy(p, bus_to_virt(eth_rmem + pktoff), frag);
+ pktoff = eth_rx_start << 8;
+ p += frag;
+ len -= frag;
+ }
+ /* read second part */
+ if (eth_flags & FLAG_PIO)
+ eth_pio_read(pktoff, p, len);
+ else
+ memcpy(p, bus_to_virt(eth_rmem + pktoff), len);
+ ret = 1;
+ }
+#ifdef INCLUDE_WD
+#ifndef WD_790_PIO
+ if (eth_flags & FLAG_790) {
+ outb(0, eth_asic_base + WD_MSR);
+ inb(0x84);
+ }
+#endif
+ if (eth_flags & FLAG_16BIT) {
+ outb(eth_laar & ~WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
+ inb(0x84);
+ }
+ inb(0x84);
+#endif
+ next = pkthdr.next; /* frame number of next packet */
+ if (next == eth_rx_start)
+ next = eth_memsize;
+ outb(next-1, eth_nic_base+D8390_P0_BOUND);
+ return(ret);
+}
+
+/**************************************************************************
+NS8390_DISABLE - Turn off adapter
+**************************************************************************/
+static void ns8390_disable(struct dev *dev)
+{
+ struct nic *nic = (struct nic *)dev;
+ /* reset and disable merge */
+ ns8390_reset(nic);
+}
+
+/**************************************************************************
+NS8390_IRQ - Enable, Disable, or Force interrupts
+**************************************************************************/
+static void ns8390_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/**************************************************************************
+ETH_PROBE - Look for an adapter
+**************************************************************************/
+#ifdef INCLUDE_NS8390
+static int eth_probe (struct dev *dev, struct pci_device *pci)
+#else
+static int eth_probe (struct dev *dev, unsigned short *probe_addrs __unused)
+#endif
+{
+ struct nic *nic = (struct nic *)dev;
+ int i;
+#ifdef INCLUDE_NS8390
+ unsigned short pci_probe_addrs[] = { pci->ioaddr, 0 };
+ unsigned short *probe_addrs = pci_probe_addrs;
+#endif
+ eth_vendor = VENDOR_NONE;
+ eth_drain_receiver = 0;
+
+ nic->irqno = 0;
+
+#ifdef INCLUDE_WD
+{
+ /******************************************************************
+ Search for WD/SMC cards
+ ******************************************************************/
+ struct wd_board *brd;
+ unsigned short chksum;
+ unsigned char c;
+ for (eth_asic_base = WD_LOW_BASE; eth_asic_base <= WD_HIGH_BASE;
+ eth_asic_base += 0x20) {
+ chksum = 0;
+ for (i=8; i<16; i++)
+ chksum += inb(eth_asic_base+i);
+ /* Extra checks to avoid soundcard */
+ if ((chksum & 0xFF) == 0xFF &&
+ inb(eth_asic_base+8) != 0xFF &&
+ inb(eth_asic_base+9) != 0xFF)
+ break;
+ }
+ if (eth_asic_base > WD_HIGH_BASE)
+ return (0);
+ /* We've found a board */
+ eth_vendor = VENDOR_WD;
+ eth_nic_base = eth_asic_base + WD_NIC_ADDR;
+
+ nic->ioaddr = eth_nic_base;
+
+ c = inb(eth_asic_base+WD_BID); /* Get board id */
+ for (brd = wd_boards; brd->name; brd++)
+ if (brd->id == c) break;
+ if (!brd->name) {
+ printf("Unknown WD/SMC NIC type %hhX\n", c);
+ return (0); /* Unknown type */
+ }
+ eth_flags = brd->flags;
+ eth_memsize = brd->memsize;
+ eth_tx_start = 0;
+ eth_rx_start = D8390_TXBUF_SIZE;
+ if ((c == TYPE_WD8013EP) &&
+ (inb(eth_asic_base + WD_ICR) & WD_ICR_16BIT)) {
+ eth_flags = FLAG_16BIT;
+ eth_memsize = MEM_16384;
+ }
+ if ((c & WD_SOFTCONFIG) && (!(eth_flags & FLAG_790))) {
+ eth_bmem = (0x80000 |
+ ((inb(eth_asic_base + WD_MSR) & 0x3F) << 13));
+ } else
+ eth_bmem = WD_DEFAULT_MEM;
+ if (brd->id == TYPE_SMC8216T || brd->id == TYPE_SMC8216C) {
+ /* from Linux driver, 8416BT detects as 8216 sometimes */
+ unsigned int addr = inb(eth_asic_base + 0xb);
+ if (((addr >> 4) & 3) == 0) {
+ brd += 2;
+ eth_memsize = brd->memsize;
+ }
+ }
+ outb(0x80, eth_asic_base + WD_MSR); /* Reset */
+ for (i=0; i<ETH_ALEN; i++) {
+ nic->node_addr[i] = inb(i+eth_asic_base+WD_LAR);
+ }
+ printf("\n%s base %#hx", brd->name, eth_asic_base);
+ if (eth_flags & FLAG_790) {
+#ifdef WD_790_PIO
+ printf(", PIO mode, addr %!\n", nic->node_addr);
+ eth_bmem = 0;
+ eth_flags |= FLAG_PIO; /* force PIO mode */
+ outb(0, eth_asic_base+WD_MSR);
+#else
+ printf(", memory %#x, addr %!\n", eth_bmem, nic->node_addr);
+ outb(WD_MSR_MENB, eth_asic_base+WD_MSR);
+ outb((inb(eth_asic_base+0x04) |
+ 0x80), eth_asic_base+0x04);
+ outb(((unsigned)(eth_bmem >> 13) & 0x0F) |
+ ((unsigned)(eth_bmem >> 11) & 0x40) |
+ (inb(eth_asic_base+0x0B) & 0xB0), eth_asic_base+0x0B);
+ outb((inb(eth_asic_base+0x04) &
+ ~0x80), eth_asic_base+0x04);
+#endif
+ } else {
+ printf(", memory %#x, addr %!\n", eth_bmem, nic->node_addr);
+ outb(((unsigned)(eth_bmem >> 13) & 0x3F) | 0x40, eth_asic_base+WD_MSR);
+ }
+ if (eth_flags & FLAG_16BIT) {
+ if (eth_flags & FLAG_790) {
+ eth_laar = inb(eth_asic_base + WD_LAAR);
+ outb(WD_LAAR_M16EN, eth_asic_base + WD_LAAR);
+ } else {
+ outb((eth_laar =
+ WD_LAAR_L16EN | 1), eth_asic_base + WD_LAAR);
+/*
+ The previous line used to be
+ WD_LAAR_M16EN | WD_LAAR_L16EN | 1));
+ jluke@deakin.edu.au reported that removing WD_LAAR_M16EN made
+ it work for WD8013s. This seems to work for my 8013 boards. I
+ don't know what is really happening. I wish I had data sheets
+ or more time to decode the Linux driver. - Ken
+*/
+ }
+ inb(0x84);
+ }
+}
+#endif
+#ifdef INCLUDE_3C503
+#ifdef T503_AUI
+ nic->flags = 1; /* aui */
+#else
+ nic->flags = 0; /* no aui */
+#endif
+ /******************************************************************
+ Search for 3Com 3c503 if no WD/SMC cards
+ ******************************************************************/
+ if (eth_vendor == VENDOR_NONE) {
+ int idx;
+ int iobase_reg, membase_reg;
+ static unsigned short base[] = {
+ 0x300, 0x310, 0x330, 0x350,
+ 0x250, 0x280, 0x2A0, 0x2E0, 0 };
+
+ /* Loop through possible addresses checking each one */
+
+ for (idx = 0; (eth_nic_base = base[idx]) != 0; ++idx) {
+
+ eth_asic_base = eth_nic_base + _3COM_ASIC_OFFSET;
+/*
+ * Note that we use the same settings for both 8 and 16 bit cards:
+ * both have an 8K bank of memory at page 1 while only the 16 bit
+ * cards have a bank at page 0.
+ */
+ eth_memsize = MEM_16384;
+ eth_tx_start = 32;
+ eth_rx_start = 32 + D8390_TXBUF_SIZE;
+
+ /* Check our base address. iobase and membase should */
+ /* both have a maximum of 1 bit set or be 0. */
+
+ iobase_reg = inb(eth_asic_base + _3COM_BCFR);
+ membase_reg = inb(eth_asic_base + _3COM_PCFR);
+
+ if ((iobase_reg & (iobase_reg - 1)) ||
+ (membase_reg & (membase_reg - 1)))
+ continue; /* nope */
+
+ /* Now get the shared memory address */
+
+ eth_flags = 0;
+
+ switch (membase_reg) {
+ case _3COM_PCFR_DC000:
+ eth_bmem = 0xdc000;
+ break;
+ case _3COM_PCFR_D8000:
+ eth_bmem = 0xd8000;
+ break;
+ case _3COM_PCFR_CC000:
+ eth_bmem = 0xcc000;
+ break;
+ case _3COM_PCFR_C8000:
+ eth_bmem = 0xc8000;
+ break;
+ case _3COM_PCFR_PIO:
+ eth_flags |= FLAG_PIO;
+ eth_bmem = 0;
+ break;
+ default:
+ continue; /* nope */
+ }
+ break;
+ }
+
+ if (base[idx] == 0) /* not found */
+ return (0);
+#ifndef T503_SHMEM
+ eth_flags |= FLAG_PIO; /* force PIO mode */
+ eth_bmem = 0;
+#endif
+ eth_vendor = VENDOR_3COM;
+
+
+ /* Need this to make ns8390_poll() happy. */
+
+ eth_rmem = eth_bmem - 0x2000;
+
+ /* Reset NIC and ASIC */
+
+ outb(_3COM_CR_RST | _3COM_CR_XSEL, eth_asic_base + _3COM_CR );
+ outb(_3COM_CR_XSEL, eth_asic_base + _3COM_CR );
+
+ /* Get our ethernet address */
+
+ outb(_3COM_CR_EALO | _3COM_CR_XSEL, eth_asic_base + _3COM_CR);
+ nic->ioaddr = eth_nic_base;
+ printf("\n3Com 3c503 base %#hx, ", eth_nic_base);
+ if (eth_flags & FLAG_PIO)
+ printf("PIO mode");
+ else
+ printf("memory %#x", eth_bmem);
+ for (i=0; i<ETH_ALEN; i++) {
+ nic->node_addr[i] = inb(eth_nic_base+i);
+ }
+ printf(", %s, addr %!\n", nic->flags ? "AUI" : "internal xcvr",
+ nic->node_addr);
+ outb(_3COM_CR_XSEL, eth_asic_base + _3COM_CR);
+ /*
+ * Initialize GA configuration register. Set bank and enable shared
+ * mem. We always use bank 1. Disable interrupts.
+ */
+ outb(_3COM_GACFR_RSEL |
+ _3COM_GACFR_MBS0 | _3COM_GACFR_TCM | _3COM_GACFR_NIM, eth_asic_base + _3COM_GACFR);
+
+ outb(0xff, eth_asic_base + _3COM_VPTR2);
+ outb(0xff, eth_asic_base + _3COM_VPTR1);
+ outb(0x00, eth_asic_base + _3COM_VPTR0);
+ /*
+ * Clear memory and verify that it worked (we use only 8K)
+ */
+
+ if (!(eth_flags & FLAG_PIO)) {
+ memset(bus_to_virt(eth_bmem), 0, 0x2000);
+ for(i = 0; i < 0x2000; ++i)
+ if (*((char *)(bus_to_virt(eth_bmem+i)))) {
+ printf ("Failed to clear 3c503 shared mem.\n");
+ return (0);
+ }
+ }
+ /*
+ * Initialize GA page/start/stop registers.
+ */
+ outb(eth_tx_start, eth_asic_base + _3COM_PSTR);
+ outb(eth_memsize, eth_asic_base + _3COM_PSPR);
+ }
+#endif
+#if defined(INCLUDE_NE) || defined(INCLUDE_NS8390)
+{
+ /******************************************************************
+ Search for NE1000/2000 if no WD/SMC or 3com cards
+ ******************************************************************/
+ unsigned char c;
+ if (eth_vendor == VENDOR_NONE) {
+ char romdata[16], testbuf[32];
+ int idx;
+ static char test[] = "NE*000 memory";
+ static unsigned short base[] = {
+#ifdef NE_SCAN
+ NE_SCAN,
+#endif
+ 0 };
+ /* if no addresses supplied, fall back on defaults */
+ if (probe_addrs == 0 || probe_addrs[0] == 0)
+ probe_addrs = base;
+ eth_bmem = 0; /* No shared memory */
+ for (idx = 0; (eth_nic_base = probe_addrs[idx]) != 0; ++idx) {
+ eth_flags = FLAG_PIO;
+ eth_asic_base = eth_nic_base + NE_ASIC_OFFSET;
+ eth_memsize = MEM_16384;
+ eth_tx_start = 32;
+ eth_rx_start = 32 + D8390_TXBUF_SIZE;
+ c = inb(eth_asic_base + NE_RESET);
+ outb(c, eth_asic_base + NE_RESET);
+ inb(0x84);
+ outb(D8390_COMMAND_STP |
+ D8390_COMMAND_RD2, eth_nic_base + D8390_P0_COMMAND);
+ outb(D8390_RCR_MON, eth_nic_base + D8390_P0_RCR);
+ outb(D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
+ outb(MEM_8192, eth_nic_base + D8390_P0_PSTART);
+ outb(MEM_16384, eth_nic_base + D8390_P0_PSTOP);
+#ifdef NS8390_FORCE_16BIT
+ eth_flags |= FLAG_16BIT; /* force 16-bit mode */
+#endif
+
+ eth_pio_write(test, 8192, sizeof(test));
+ eth_pio_read(8192, testbuf, sizeof(test));
+ if (!memcmp(test, testbuf, sizeof(test)))
+ break;
+ eth_flags |= FLAG_16BIT;
+ eth_memsize = MEM_32768;
+ eth_tx_start = 64;
+ eth_rx_start = 64 + D8390_TXBUF_SIZE;
+ outb(D8390_DCR_WTS |
+ D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
+ outb(MEM_16384, eth_nic_base + D8390_P0_PSTART);
+ outb(MEM_32768, eth_nic_base + D8390_P0_PSTOP);
+ eth_pio_write(test, 16384, sizeof(test));
+ eth_pio_read(16384, testbuf, sizeof(test));
+ if (!memcmp(testbuf, test, sizeof(test)))
+ break;
+ }
+ if (eth_nic_base == 0)
+ return (0);
+ if (eth_nic_base > ISA_MAX_ADDR) /* PCI probably */
+ eth_flags |= FLAG_16BIT;
+ eth_vendor = VENDOR_NOVELL;
+ eth_pio_read(0, romdata, sizeof(romdata));
+ for (i=0; i<ETH_ALEN; i++) {
+ nic->node_addr[i] = romdata[i + ((eth_flags & FLAG_16BIT) ? i : 0)];
+ }
+ nic->ioaddr = eth_nic_base;
+ printf("\nNE%c000 base %#hx, addr %!\n",
+ (eth_flags & FLAG_16BIT) ? '2' : '1', eth_nic_base,
+ nic->node_addr);
+ }
+}
+#endif
+ if (eth_vendor == VENDOR_NONE)
+ return(0);
+ if (eth_vendor != VENDOR_3COM)
+ eth_rmem = eth_bmem;
+ ns8390_reset(nic);
+
+ dev->disable = ns8390_disable;
+ nic->poll = ns8390_poll;
+ nic->transmit = ns8390_transmit;
+ nic->irq = ns8390_irq;
+
+ /* Based on PnP ISA map */
+#ifdef INCLUDE_WD
+ dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
+ dev->devid.device_id = htons(0x812a);
+#endif
+#ifdef INCLUDE_3C503
+ dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
+ dev->devid.device_id = htons(0x80f3);
+#endif
+#ifdef INCLUDE_NE
+ dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
+ dev->devid.device_id = htons(0x80d6);
+#endif
+ return 1;
+}
+
+#ifdef INCLUDE_WD
+static struct isa_driver wd_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "WD",
+ .probe = wd_probe,
+ .ioaddrs = 0,
+};
+#endif
+
+#ifdef INCLUDE_3C503
+static struct isa_driver t503_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "3C503",
+ .probe = t503_probe,
+ .ioaddrs = 0,
+};
+#endif
+
+#ifdef INCLUDE_NE
+static struct isa_driver ne_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "NE*000",
+ .probe = ne_probe,
+ .ioaddrs = 0,
+};
+#endif
+
+#ifdef INCLUDE_NS8390
+static struct pci_id nepci_nics[] = {
+/* A few NE2000 PCI clones, list not exhaustive */
+PCI_ROM(0x10ec, 0x8029, "rtl8029", "Realtek 8029"),
+PCI_ROM(0x1186, 0x0300, "dlink-528", "D-Link DE-528"),
+PCI_ROM(0x1050, 0x0940, "winbond940", "Winbond NE2000-PCI"), /* Winbond 86C940 / 89C940 */
+PCI_ROM(0x1050, 0x5a5a, "winbond940f", "Winbond W89c940F"), /* Winbond 89C940F */
+PCI_ROM(0x11f6, 0x1401, "compexrl2000", "Compex ReadyLink 2000"),
+PCI_ROM(0x8e2e, 0x3000, "ktiet32p2", "KTI ET32P2"),
+PCI_ROM(0x4a14, 0x5000, "nv5000sc", "NetVin NV5000SC"),
+PCI_ROM(0x12c3, 0x0058, "holtek80232", "Holtek HT80232"),
+PCI_ROM(0x12c3, 0x5598, "holtek80229", "Holtek HT80229"),
+PCI_ROM(0x10bd, 0x0e34, "surecom-ne34", "Surecom NE34"),
+PCI_ROM(0x1106, 0x0926, "via86c926", "Via 86c926"),
+};
+
+static struct pci_driver nepci_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "NE2000/PCI",
+ .probe = nepci_probe,
+ .ids = nepci_nics,
+ .id_count = sizeof(nepci_nics)/sizeof(nepci_nics[0]),
+ .class = 0,
+};
+
+#endif /* INCLUDE_NS8390 */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/src/drivers/net/ns8390.h b/src/drivers/net/ns8390.h
new file mode 100644
index 00000000..2c4e972d
--- /dev/null
+++ b/src/drivers/net/ns8390.h
@@ -0,0 +1,238 @@
+/**************************************************************************
+ETHERBOOT - BOOTP/TFTP Bootstrap Program
+
+Author: Martin Renters
+ Date: Jun/94
+
+**************************************************************************/
+
+#define VENDOR_NONE 0
+#define VENDOR_WD 1
+#define VENDOR_NOVELL 2
+#define VENDOR_3COM 3
+
+#define FLAG_PIO 0x01
+#define FLAG_16BIT 0x02
+#define FLAG_790 0x04
+
+#define MEM_8192 32
+#define MEM_16384 64
+#define MEM_32768 128
+
+#define ISA_MAX_ADDR 0x400
+
+/**************************************************************************
+Western Digital/SMC Board Definitions
+**************************************************************************/
+#define WD_LOW_BASE 0x200
+#define WD_HIGH_BASE 0x3e0
+#ifndef WD_DEFAULT_MEM
+#define WD_DEFAULT_MEM 0xD0000
+#endif
+#define WD_NIC_ADDR 0x10
+
+/**************************************************************************
+Western Digital/SMC ASIC Addresses
+**************************************************************************/
+#define WD_MSR 0x00
+#define WD_ICR 0x01
+#define WD_IAR 0x02
+#define WD_BIO 0x03
+#define WD_IRR 0x04
+#define WD_LAAR 0x05
+#define WD_IJR 0x06
+#define WD_GP2 0x07
+#define WD_LAR 0x08
+#define WD_BID 0x0E
+
+#define WD_ICR_16BIT 0x01
+
+#define WD_MSR_MENB 0x40
+
+#define WD_LAAR_L16EN 0x40
+#define WD_LAAR_M16EN 0x80
+
+#define WD_SOFTCONFIG 0x20
+
+/**************************************************************************
+Western Digital/SMC Board Types
+**************************************************************************/
+#define TYPE_WD8003S 0x02
+#define TYPE_WD8003E 0x03
+#define TYPE_WD8013EBT 0x05
+#define TYPE_WD8003W 0x24
+#define TYPE_WD8003EB 0x25
+#define TYPE_WD8013W 0x26
+#define TYPE_WD8013EP 0x27
+#define TYPE_WD8013WC 0x28
+#define TYPE_WD8013EPC 0x29
+#define TYPE_SMC8216T 0x2a
+#define TYPE_SMC8216C 0x2b
+#define TYPE_SMC8416T 0x00 /* Bogus entries: the 8416 generates the */
+#define TYPE_SMC8416C 0x00 /* the same codes as the 8216. */
+#define TYPE_SMC8013EBP 0x2c
+
+/**************************************************************************
+3com 3c503 definitions
+**************************************************************************/
+
+#ifndef _3COM_BASE
+#define _3COM_BASE 0x300
+#endif
+
+#define _3COM_TX_PAGE_OFFSET_8BIT 0x20
+#define _3COM_TX_PAGE_OFFSET_16BIT 0x0
+#define _3COM_RX_PAGE_OFFSET_16BIT 0x20
+
+#define _3COM_ASIC_OFFSET 0x400
+#define _3COM_NIC_OFFSET 0x0
+
+#define _3COM_PSTR 0
+#define _3COM_PSPR 1
+
+#define _3COM_BCFR 3
+#define _3COM_BCFR_2E0 0x01
+#define _3COM_BCFR_2A0 0x02
+#define _3COM_BCFR_280 0x04
+#define _3COM_BCFR_250 0x08
+#define _3COM_BCFR_350 0x10
+#define _3COM_BCFR_330 0x20
+#define _3COM_BCFR_310 0x40
+#define _3COM_BCFR_300 0x80
+#define _3COM_PCFR 4
+#define _3COM_PCFR_PIO 0
+#define _3COM_PCFR_C8000 0x10
+#define _3COM_PCFR_CC000 0x20
+#define _3COM_PCFR_D8000 0x40
+#define _3COM_PCFR_DC000 0x80
+#define _3COM_CR 6
+#define _3COM_CR_RST 0x01 /* Reset GA and NIC */
+#define _3COM_CR_XSEL 0x02 /* Transceiver select. BNC=1(def) AUI=0 */
+#define _3COM_CR_EALO 0x04 /* window EA PROM 0-15 to I/O base */
+#define _3COM_CR_EAHI 0x08 /* window EA PROM 16-31 to I/O base */
+#define _3COM_CR_SHARE 0x10 /* select interrupt sharing option */
+#define _3COM_CR_DBSEL 0x20 /* Double buffer select */
+#define _3COM_CR_DDIR 0x40 /* DMA direction select */
+#define _3COM_CR_START 0x80 /* Start DMA controller */
+#define _3COM_GACFR 5
+#define _3COM_GACFR_MBS0 0x01
+#define _3COM_GACFR_MBS1 0x02
+#define _3COM_GACFR_MBS2 0x04
+#define _3COM_GACFR_RSEL 0x08 /* enable shared memory */
+#define _3COM_GACFR_TEST 0x10 /* for GA testing */
+#define _3COM_GACFR_OWS 0x20 /* select 0WS access to GA */
+#define _3COM_GACFR_TCM 0x40 /* Mask DMA interrupts */
+#define _3COM_GACFR_NIM 0x80 /* Mask NIC interrupts */
+#define _3COM_STREG 7
+#define _3COM_STREG_REV 0x07 /* GA revision */
+#define _3COM_STREG_DIP 0x08 /* DMA in progress */
+#define _3COM_STREG_DTC 0x10 /* DMA terminal count */
+#define _3COM_STREG_OFLW 0x20 /* Overflow */
+#define _3COM_STREG_UFLW 0x40 /* Underflow */
+#define _3COM_STREG_DPRDY 0x80 /* Data port ready */
+#define _3COM_IDCFR 8
+#define _3COM_IDCFR_DRQ0 0x01 /* DMA request 1 select */
+#define _3COM_IDCFR_DRQ1 0x02 /* DMA request 2 select */
+#define _3COM_IDCFR_DRQ2 0x04 /* DMA request 3 select */
+#define _3COM_IDCFR_UNUSED 0x08 /* not used */
+#define _3COM_IDCFR_IRQ2 0x10 /* Interrupt request 2 select */
+#define _3COM_IDCFR_IRQ3 0x20 /* Interrupt request 3 select */
+#define _3COM_IDCFR_IRQ4 0x40 /* Interrupt request 4 select */
+#define _3COM_IDCFR_IRQ5 0x80 /* Interrupt request 5 select */
+#define _3COM_IRQ2 2
+#define _3COM_IRQ3 3
+#define _3COM_IRQ4 4
+#define _3COM_IRQ5 5
+#define _3COM_DAMSB 9
+#define _3COM_DALSB 0x0a
+#define _3COM_VPTR2 0x0b
+#define _3COM_VPTR1 0x0c
+#define _3COM_VPTR0 0x0d
+#define _3COM_RFMSB 0x0e
+#define _3COM_RFLSB 0x0f
+
+/**************************************************************************
+NE1000/2000 definitions
+**************************************************************************/
+#define NE_ASIC_OFFSET 0x10
+#define NE_RESET 0x0F /* Used to reset card */
+#define NE_DATA 0x00 /* Used to read/write NIC mem */
+
+#define COMPEX_RL2000_TRIES 200
+
+/**************************************************************************
+8390 Register Definitions
+**************************************************************************/
+#define D8390_P0_COMMAND 0x00
+#define D8390_P0_PSTART 0x01
+#define D8390_P0_PSTOP 0x02
+#define D8390_P0_BOUND 0x03
+#define D8390_P0_TSR 0x04
+#define D8390_P0_TPSR 0x04
+#define D8390_P0_TBCR0 0x05
+#define D8390_P0_TBCR1 0x06
+#define D8390_P0_ISR 0x07
+#define D8390_P0_RSAR0 0x08
+#define D8390_P0_RSAR1 0x09
+#define D8390_P0_RBCR0 0x0A
+#define D8390_P0_RBCR1 0x0B
+#define D8390_P0_RSR 0x0C
+#define D8390_P0_RCR 0x0C
+#define D8390_P0_TCR 0x0D
+#define D8390_P0_DCR 0x0E
+#define D8390_P0_IMR 0x0F
+#define D8390_P1_COMMAND 0x00
+#define D8390_P1_PAR0 0x01
+#define D8390_P1_PAR1 0x02
+#define D8390_P1_PAR2 0x03
+#define D8390_P1_PAR3 0x04
+#define D8390_P1_PAR4 0x05
+#define D8390_P1_PAR5 0x06
+#define D8390_P1_CURR 0x07
+#define D8390_P1_MAR0 0x08
+
+#define D8390_COMMAND_PS0 0x0 /* Page 0 select */
+#define D8390_COMMAND_PS1 0x40 /* Page 1 select */
+#define D8390_COMMAND_PS2 0x80 /* Page 2 select */
+#define D8390_COMMAND_RD2 0x20 /* Remote DMA control */
+#define D8390_COMMAND_RD1 0x10
+#define D8390_COMMAND_RD0 0x08
+#define D8390_COMMAND_TXP 0x04 /* transmit packet */
+#define D8390_COMMAND_STA 0x02 /* start */
+#define D8390_COMMAND_STP 0x01 /* stop */
+
+#define D8390_RCR_MON 0x20 /* monitor mode */
+
+#define D8390_DCR_FT1 0x40
+#define D8390_DCR_LS 0x08 /* Loopback select */
+#define D8390_DCR_WTS 0x01 /* Word transfer select */
+
+#define D8390_ISR_PRX 0x01 /* successful recv */
+#define D8390_ISR_PTX 0x02 /* successful xmit */
+#define D8390_ISR_RXE 0x04 /* receive error */
+#define D8390_ISR_TXE 0x08 /* transmit error */
+#define D8390_ISR_OVW 0x10 /* Overflow */
+#define D8390_ISR_CNT 0x20 /* Counter overflow */
+#define D8390_ISR_RDC 0x40 /* Remote DMA complete */
+#define D8390_ISR_RST 0x80 /* reset */
+
+#define D8390_RSTAT_PRX 0x01 /* successful recv */
+#define D8390_RSTAT_CRC 0x02 /* CRC error */
+#define D8390_RSTAT_FAE 0x04 /* Frame alignment error */
+#define D8390_RSTAT_OVER 0x08 /* FIFO overrun */
+
+#define D8390_TXBUF_SIZE 6
+#define D8390_RXBUF_END 32
+#define D8390_PAGE_SIZE 256
+
+struct ringbuffer {
+ unsigned char status;
+ unsigned char next;
+ unsigned short len;
+};
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/src/drivers/net/p80211hdr.h b/src/drivers/net/p80211hdr.h
new file mode 100644
index 00000000..50d92796
--- /dev/null
+++ b/src/drivers/net/p80211hdr.h
@@ -0,0 +1,262 @@
+/* src/include/wlan/p80211hdr.h
+*
+* Macros, types, and functions for handling 802.11 MAC headers
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* This file declares the constants and types used in the interface
+* between a wlan driver and the user mode utilities.
+*
+* Note:
+* - Constant values are always in HOST byte order. To assign
+* values to multi-byte fields they _must_ be converted to
+* ieee byte order. To retrieve multi-byte values from incoming
+* frames, they must be converted to host order.
+*
+* All functions declared here are implemented in p80211.c
+* --------------------------------------------------------------------
+*/
+
+#ifndef _P80211HDR_H
+#define _P80211HDR_H
+
+/*================================================================*/
+/* System Includes */
+
+/*================================================================*/
+/* Project Includes */
+
+#ifndef _WLAN_COMPAT_H
+#include <wlan/wlan_compat.h>
+#endif
+
+
+/*================================================================*/
+/* Constants */
+
+/*--- Sizes -----------------------------------------------*/
+#define WLAN_ADDR_LEN 6
+#define WLAN_CRC_LEN 4
+#define WLAN_BSSID_LEN 6
+#define WLAN_BSS_TS_LEN 8
+#define WLAN_HDR_A3_LEN 24
+#define WLAN_HDR_A4_LEN 30
+#define WLAN_SSID_MAXLEN 32
+#define WLAN_DATA_MAXLEN 2312
+#define WLAN_A3FR_MAXLEN (WLAN_HDR_A3_LEN + WLAN_DATA_MAXLEN + WLAN_CRC_LEN)
+#define WLAN_A4FR_MAXLEN (WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN + WLAN_CRC_LEN)
+#define WLAN_BEACON_FR_MAXLEN (WLAN_HDR_A3_LEN + 334)
+#define WLAN_ATIM_FR_MAXLEN (WLAN_HDR_A3_LEN + 0)
+#define WLAN_DISASSOC_FR_MAXLEN (WLAN_HDR_A3_LEN + 2)
+#define WLAN_ASSOCREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 48)
+#define WLAN_ASSOCRESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 16)
+#define WLAN_REASSOCREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 54)
+#define WLAN_REASSOCRESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 16)
+#define WLAN_PROBEREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 44)
+#define WLAN_PROBERESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 78)
+#define WLAN_AUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 261)
+#define WLAN_DEAUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 2)
+#define WLAN_WEP_NKEYS 4
+#define WLAN_WEP_MAXKEYLEN 13
+#define WLAN_CHALLENGE_IE_LEN 130
+#define WLAN_CHALLENGE_LEN 128
+#define WLAN_WEP_IV_LEN 4
+#define WLAN_WEP_ICV_LEN 4
+
+/*--- Frame Control Field -------------------------------------*/
+/* Frame Types */
+#define WLAN_FTYPE_MGMT 0x00
+#define WLAN_FTYPE_CTL 0x01
+#define WLAN_FTYPE_DATA 0x02
+
+/* Frame subtypes */
+/* Management */
+#define WLAN_FSTYPE_ASSOCREQ 0x00
+#define WLAN_FSTYPE_ASSOCRESP 0x01
+#define WLAN_FSTYPE_REASSOCREQ 0x02
+#define WLAN_FSTYPE_REASSOCRESP 0x03
+#define WLAN_FSTYPE_PROBEREQ 0x04
+#define WLAN_FSTYPE_PROBERESP 0x05
+#define WLAN_FSTYPE_BEACON 0x08
+#define WLAN_FSTYPE_ATIM 0x09
+#define WLAN_FSTYPE_DISASSOC 0x0a
+#define WLAN_FSTYPE_AUTHEN 0x0b
+#define WLAN_FSTYPE_DEAUTHEN 0x0c
+
+/* Control */
+#define WLAN_FSTYPE_PSPOLL 0x0a
+#define WLAN_FSTYPE_RTS 0x0b
+#define WLAN_FSTYPE_CTS 0x0c
+#define WLAN_FSTYPE_ACK 0x0d
+#define WLAN_FSTYPE_CFEND 0x0e
+#define WLAN_FSTYPE_CFENDCFACK 0x0f
+
+/* Data */
+#define WLAN_FSTYPE_DATAONLY 0x00
+#define WLAN_FSTYPE_DATA_CFACK 0x01
+#define WLAN_FSTYPE_DATA_CFPOLL 0x02
+#define WLAN_FSTYPE_DATA_CFACK_CFPOLL 0x03
+#define WLAN_FSTYPE_NULL 0x04
+#define WLAN_FSTYPE_CFACK 0x05
+#define WLAN_FSTYPE_CFPOLL 0x06
+#define WLAN_FSTYPE_CFACK_CFPOLL 0x07
+
+
+/*================================================================*/
+/* Macros */
+
+/*--- FC Macros ----------------------------------------------*/
+/* Macros to get/set the bitfields of the Frame Control Field */
+/* GET_FC_??? - takes the host byte-order value of an FC */
+/* and retrieves the value of one of the */
+/* bitfields and moves that value so its lsb is */
+/* in bit 0. */
+/* SET_FC_??? - takes a host order value for one of the FC */
+/* bitfields and moves it to the proper bit */
+/* location for ORing into a host order FC. */
+/* To send the FC produced from SET_FC_???, */
+/* one must put the bytes in IEEE order. */
+/* e.g. */
+/* printf("the frame subtype is %x", */
+/* GET_FC_FTYPE( ieee2host( rx.fc ))) */
+/* */
+/* tx.fc = host2ieee( SET_FC_FTYPE(WLAN_FTYP_CTL) | */
+/* SET_FC_FSTYPE(WLAN_FSTYPE_RTS) ); */
+/*------------------------------------------------------------*/
+
+#define WLAN_GET_FC_PVER(n) (((UINT16)(n)) & (BIT0 | BIT1))
+#define WLAN_GET_FC_FTYPE(n) ((((UINT16)(n)) & (BIT2 | BIT3)) >> 2)
+#define WLAN_GET_FC_FSTYPE(n) ((((UINT16)(n)) & (BIT4|BIT5|BIT6|BIT7)) >> 4)
+#define WLAN_GET_FC_TODS(n) ((((UINT16)(n)) & (BIT8)) >> 8)
+#define WLAN_GET_FC_FROMDS(n) ((((UINT16)(n)) & (BIT9)) >> 9)
+#define WLAN_GET_FC_MOREFRAG(n) ((((UINT16)(n)) & (BIT10)) >> 10)
+#define WLAN_GET_FC_RETRY(n) ((((UINT16)(n)) & (BIT11)) >> 11)
+#define WLAN_GET_FC_PWRMGT(n) ((((UINT16)(n)) & (BIT12)) >> 12)
+#define WLAN_GET_FC_MOREDATA(n) ((((UINT16)(n)) & (BIT13)) >> 13)
+#define WLAN_GET_FC_ISWEP(n) ((((UINT16)(n)) & (BIT14)) >> 14)
+#define WLAN_GET_FC_ORDER(n) ((((UINT16)(n)) & (BIT15)) >> 15)
+
+#define WLAN_SET_FC_PVER(n) ((UINT16)(n))
+#define WLAN_SET_FC_FTYPE(n) (((UINT16)(n)) << 2)
+#define WLAN_SET_FC_FSTYPE(n) (((UINT16)(n)) << 4)
+#define WLAN_SET_FC_TODS(n) (((UINT16)(n)) << 8)
+#define WLAN_SET_FC_FROMDS(n) (((UINT16)(n)) << 9)
+#define WLAN_SET_FC_MOREFRAG(n) (((UINT16)(n)) << 10)
+#define WLAN_SET_FC_RETRY(n) (((UINT16)(n)) << 11)
+#define WLAN_SET_FC_PWRMGT(n) (((UINT16)(n)) << 12)
+#define WLAN_SET_FC_MOREDATA(n) (((UINT16)(n)) << 13)
+#define WLAN_SET_FC_ISWEP(n) (((UINT16)(n)) << 14)
+#define WLAN_SET_FC_ORDER(n) (((UINT16)(n)) << 15)
+
+/*--- Duration Macros ----------------------------------------*/
+/* Macros to get/set the bitfields of the Duration Field */
+/* - the duration value is only valid when bit15 is zero */
+/* - the firmware handles these values, so I'm not going */
+/* these macros right now. */
+/*------------------------------------------------------------*/
+
+/*--- Sequence Control Macros -------------------------------*/
+/* Macros to get/set the bitfields of the Sequence Control */
+/* Field. */
+/*------------------------------------------------------------*/
+#define WLAN_GET_SEQ_FRGNUM(n) (((UINT16)(n)) & (BIT0|BIT1|BIT2|BIT3))
+#define WLAN_GET_SEQ_SEQNUM(n) ((((UINT16)(n)) & (~(BIT0|BIT1|BIT2|BIT3))) >> 4)
+
+/*--- Data ptr macro -----------------------------------------*/
+/* Creates a UINT8* to the data portion of a frame */
+/* Assumes you're passing in a ptr to the beginning of the hdr*/
+/*------------------------------------------------------------*/
+#define WLAN_HDR_A3_DATAP(p) (((UINT8*)(p)) + WLAN_HDR_A3_LEN)
+#define WLAN_HDR_A4_DATAP(p) (((UINT8*)(p)) + WLAN_HDR_A4_LEN)
+
+#define DOT11_RATE5_ISBASIC_GET(r) (((UINT8)(r)) & BIT7)
+
+/*================================================================*/
+/* Types */
+
+/* BSS Timestamp */
+typedef UINT8 wlan_bss_ts_t[WLAN_BSS_TS_LEN];
+
+/* Generic 802.11 Header types */
+__WLAN_PRAGMA_PACK1__
+typedef struct p80211_hdr_a3
+{
+ UINT16 fc __WLAN_ATTRIB_PACK__;
+ UINT16 dur __WLAN_ATTRIB_PACK__;
+ UINT8 a1[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__;
+ UINT8 a2[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__;
+ UINT8 a3[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__;
+ UINT16 seq __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ p80211_hdr_a3_t;
+__WLAN_PRAGMA_PACKDFLT__
+
+__WLAN_PRAGMA_PACK1__
+typedef struct p80211_hdr_a4
+{
+ UINT16 fc __WLAN_ATTRIB_PACK__;
+ UINT16 dur __WLAN_ATTRIB_PACK__;
+ UINT8 a1[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__;
+ UINT8 a2[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__;
+ UINT8 a3[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__;
+ UINT16 seq __WLAN_ATTRIB_PACK__;
+ UINT8 a4[WLAN_ADDR_LEN] __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ p80211_hdr_a4_t;
+__WLAN_PRAGMA_PACKDFLT__
+
+typedef union p80211_hdr
+{
+ p80211_hdr_a3_t a3 __WLAN_ATTRIB_PACK__;
+ p80211_hdr_a4_t a4 __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ p80211_hdr_t;
+
+
+/*================================================================*/
+/* Extern Declarations */
+
+
+/*================================================================*/
+/* Function Declarations */
+
+void p802addr_to_str( char *buf, UINT8 *addr);
+
+#endif /* _P80211HDR_H */
diff --git a/src/drivers/net/pcnet32.c b/src/drivers/net/pcnet32.c
new file mode 100644
index 00000000..c2fb897a
--- /dev/null
+++ b/src/drivers/net/pcnet32.c
@@ -0,0 +1,1004 @@
+/**************************************************************************
+*
+* pcnet32.c -- Etherboot device driver for the AMD PCnet32
+* Written 2003-2003 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code based on:
+* pcnet32.c: An AMD PCnet32 ethernet driver for linux:
+*
+* (C) 1996-1999 Thomas Bogendoerfer
+* See Linux Driver for full information
+*
+* The transmit and poll functions were written with reference to:
+* lance.c - LANCE NIC driver for Etherboot written by Ken Yap
+*
+* Linux Driver Version 1.27a, 10.02.2002
+*
+*
+* REVISION HISTORY:
+* ================
+* v1.0 08-06-2003 timlegge Initial port of Linux driver
+* v1.1 08-23-2003 timlegge Add multicast support
+* v1.2 01-17-2004 timlegge Initial driver output cleanup
+* v1.3 03-29-2004 timlegge More driver cleanup
+*
+* Indent Options: indent -kr -i8
+***************************************************************************/
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include "pci.h"
+/* Include the time functions */
+#include "timer.h"
+#include "mii.h"
+/* void hex_dump(const char *data, const unsigned int len); */
+
+/* Etherboot Specific definations */
+#define drv_version "v1.3"
+#define drv_date "03-29-2004"
+
+typedef unsigned char u8;
+typedef signed char s8;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned int u32;
+typedef signed int s32;
+
+static u32 ioaddr; /* Globally used for the card's io address */
+
+#ifdef EDEBUG
+#define dprintf(x) printf x
+#else
+#define dprintf(x)
+#endif
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+/* End Etherboot Specific */
+
+int cards_found /* __initdata */ ;
+
+#ifdef REMOVE
+/* FIXME: Remove these they are probably pointless */
+
+/*
+ * VLB I/O addresses
+ */
+static unsigned int pcnet32_portlist[] /*__initdata */ =
+{ 0x300, 0x320, 0x340, 0x360, 0 };
+
+static int pcnet32_debug = 1;
+static int tx_start = 1; /* Mapping -- 0:20, 1:64, 2:128, 3:~220 (depends on chip vers) */
+static int pcnet32vlb; /* check for VLB cards ? */
+
+static struct net_device *pcnet32_dev;
+
+static int max_interrupt_work = 80;
+static int rx_copybreak = 200;
+#endif
+#define PCNET32_PORT_AUI 0x00
+#define PCNET32_PORT_10BT 0x01
+#define PCNET32_PORT_GPSI 0x02
+#define PCNET32_PORT_MII 0x03
+
+#define PCNET32_PORT_PORTSEL 0x03
+#define PCNET32_PORT_ASEL 0x04
+#define PCNET32_PORT_100 0x40
+#define PCNET32_PORT_FD 0x80
+
+#define PCNET32_DMA_MASK 0xffffffff
+
+/*
+ * table to translate option values from tulip
+ * to internal options
+ */
+static unsigned char options_mapping[] = {
+ PCNET32_PORT_ASEL, /* 0 Auto-select */
+ PCNET32_PORT_AUI, /* 1 BNC/AUI */
+ PCNET32_PORT_AUI, /* 2 AUI/BNC */
+ PCNET32_PORT_ASEL, /* 3 not supported */
+ PCNET32_PORT_10BT | PCNET32_PORT_FD, /* 4 10baseT-FD */
+ PCNET32_PORT_ASEL, /* 5 not supported */
+ PCNET32_PORT_ASEL, /* 6 not supported */
+ PCNET32_PORT_ASEL, /* 7 not supported */
+ PCNET32_PORT_ASEL, /* 8 not supported */
+ PCNET32_PORT_MII, /* 9 MII 10baseT */
+ PCNET32_PORT_MII | PCNET32_PORT_FD, /* 10 MII 10baseT-FD */
+ PCNET32_PORT_MII, /* 11 MII (autosel) */
+ PCNET32_PORT_10BT, /* 12 10BaseT */
+ PCNET32_PORT_MII | PCNET32_PORT_100, /* 13 MII 100BaseTx */
+ PCNET32_PORT_MII | PCNET32_PORT_100 | PCNET32_PORT_FD, /* 14 MII 100BaseTx-FD */
+ PCNET32_PORT_ASEL /* 15 not supported */
+};
+
+#define MAX_UNITS 8 /* More are supported, limit only on options */
+static int options[MAX_UNITS];
+static int full_duplex[MAX_UNITS];
+
+/*
+ * Theory of Operation
+ *
+ * This driver uses the same software structure as the normal lance
+ * driver. So look for a verbose description in lance.c. The differences
+ * to the normal lance driver is the use of the 32bit mode of PCnet32
+ * and PCnetPCI chips. Because these chips are 32bit chips, there is no
+ * 16MB limitation and we don't need bounce buffers.
+ */
+
+
+
+/*
+ * Set the number of Tx and Rx buffers, using Log_2(# buffers).
+ * Reasonable default values are 4 Tx buffers, and 16 Rx buffers.
+ * That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4).
+ */
+#ifndef PCNET32_LOG_TX_BUFFERS
+#define PCNET32_LOG_TX_BUFFERS 1
+#define PCNET32_LOG_RX_BUFFERS 2
+#endif
+
+#define TX_RING_SIZE (1 << (PCNET32_LOG_TX_BUFFERS))
+#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
+/* FIXME: Fix this to allow multiple tx_ring descriptors */
+#define TX_RING_LEN_BITS 0x0000 /*PCNET32_LOG_TX_BUFFERS) << 12) */
+
+#define RX_RING_SIZE (1 << (PCNET32_LOG_RX_BUFFERS))
+#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
+#define RX_RING_LEN_BITS ((PCNET32_LOG_RX_BUFFERS) << 4)
+
+#define PKT_BUF_SZ 1544
+
+/* Offsets from base I/O address. */
+#define PCNET32_WIO_RDP 0x10
+#define PCNET32_WIO_RAP 0x12
+#define PCNET32_WIO_RESET 0x14
+#define PCNET32_WIO_BDP 0x16
+
+#define PCNET32_DWIO_RDP 0x10
+#define PCNET32_DWIO_RAP 0x14
+#define PCNET32_DWIO_RESET 0x18
+#define PCNET32_DWIO_BDP 0x1C
+
+#define PCNET32_TOTAL_SIZE 0x20
+
+/* Buffers for the tx and Rx */
+
+/* Create a static buffer of size PKT_BUF_SZ for each
+TX Descriptor. All descriptors point to a
+part of this buffer */
+static unsigned char txb[PKT_BUF_SZ * TX_RING_SIZE];
+// __attribute__ ((aligned(16)));
+
+/* Create a static buffer of size PKT_BUF_SZ for each
+RX Descriptor All descriptors point to a
+part of this buffer */
+static unsigned char rxb[RX_RING_SIZE * PKT_BUF_SZ];
+// __attribute__ ((aligned(16)));
+
+/* The PCNET32 Rx and Tx ring descriptors. */
+struct pcnet32_rx_head {
+ u32 base;
+ s16 buf_length;
+ s16 status;
+ u32 msg_length;
+ u32 reserved;
+};
+
+struct pcnet32_tx_head {
+ u32 base;
+ s16 length;
+ s16 status;
+ u32 misc;
+ u32 reserved;
+};
+
+/* The PCNET32 32-Bit initialization block, described in databook. */
+struct pcnet32_init_block {
+ u16 mode;
+ u16 tlen_rlen;
+ u8 phys_addr[6];
+ u16 reserved;
+ u32 filter[2];
+ /* Receive and transmit ring base, along with extra bits. */
+ u32 rx_ring;
+ u32 tx_ring;
+};
+/* PCnet32 access functions */
+struct pcnet32_access {
+ u16(*read_csr) (unsigned long, int);
+ void (*write_csr) (unsigned long, int, u16);
+ u16(*read_bcr) (unsigned long, int);
+ void (*write_bcr) (unsigned long, int, u16);
+ u16(*read_rap) (unsigned long);
+ void (*write_rap) (unsigned long, u16);
+ void (*reset) (unsigned long);
+};
+
+/* Define the TX Descriptor */
+static struct pcnet32_tx_head tx_ring[TX_RING_SIZE]
+ __attribute__ ((aligned(16)));
+
+
+/* Define the RX Descriptor */
+static struct pcnet32_rx_head rx_ring[RX_RING_SIZE]
+ __attribute__ ((aligned(16)));
+
+/* May need to be moved to mii.h */
+struct mii_if_info {
+ int phy_id;
+ int advertising;
+ unsigned int full_duplex:1; /* is full duplex? */
+};
+
+/*
+ * The first three fields of pcnet32_private are read by the ethernet device
+ * so we allocate the structure should be allocated by pci_alloc_consistent().
+ */
+#define MII_CNT 4
+struct pcnet32_private {
+ struct pcnet32_init_block init_block;
+ struct pci_dev *pci_dev; /* Pointer to the associated pci device structure */
+ const char *name;
+ /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+ struct sk_buff *tx_skbuff[TX_RING_SIZE];
+ struct sk_buff *rx_skbuff[RX_RING_SIZE];
+ struct pcnet32_access a;
+ unsigned int cur_rx, cur_tx; /* The next free ring entry */
+ char tx_full;
+ int options;
+ int shared_irq:1, /* shared irq possible */
+ ltint:1, /* enable TxDone-intr inhibitor */
+ dxsuflo:1, /* disable transmit stop on uflo */
+ mii:1; /* mii port available */
+ struct mii_if_info mii_if;
+ unsigned char phys[MII_CNT];
+ struct net_device *next;
+ int full_duplex:1;
+} lpx;
+
+static struct pcnet32_private *lp;
+
+static int mdio_read(struct nic *nic __unused, int phy_id, int reg_num);
+#if 0
+static void mdio_write(struct nic *nic __unused, int phy_id, int reg_num,
+ int val);
+#endif
+enum pci_flags_bit {
+ PCI_USES_IO = 1, PCI_USES_MEM = 2, PCI_USES_MASTER = 4,
+ PCI_ADDR0 = 0x10 << 0, PCI_ADDR1 = 0x10 << 1, PCI_ADDR2 =
+ 0x10 << 2, PCI_ADDR3 = 0x10 << 3,
+};
+
+
+static u16 pcnet32_wio_read_csr(unsigned long addr, int index)
+{
+ outw(index, addr + PCNET32_WIO_RAP);
+ return inw(addr + PCNET32_WIO_RDP);
+}
+
+static void pcnet32_wio_write_csr(unsigned long addr, int index, u16 val)
+{
+ outw(index, addr + PCNET32_WIO_RAP);
+ outw(val, addr + PCNET32_WIO_RDP);
+}
+
+static u16 pcnet32_wio_read_bcr(unsigned long addr, int index)
+{
+ outw(index, addr + PCNET32_WIO_RAP);
+ return inw(addr + PCNET32_WIO_BDP);
+}
+
+static void pcnet32_wio_write_bcr(unsigned long addr, int index, u16 val)
+{
+ outw(index, addr + PCNET32_WIO_RAP);
+ outw(val, addr + PCNET32_WIO_BDP);
+}
+
+static u16 pcnet32_wio_read_rap(unsigned long addr)
+{
+ return inw(addr + PCNET32_WIO_RAP);
+}
+
+static void pcnet32_wio_write_rap(unsigned long addr, u16 val)
+{
+ outw(val, addr + PCNET32_WIO_RAP);
+}
+
+static void pcnet32_wio_reset(unsigned long addr)
+{
+ inw(addr + PCNET32_WIO_RESET);
+}
+
+static int pcnet32_wio_check(unsigned long addr)
+{
+ outw(88, addr + PCNET32_WIO_RAP);
+ return (inw(addr + PCNET32_WIO_RAP) == 88);
+}
+
+static struct pcnet32_access pcnet32_wio = {
+ read_csr:pcnet32_wio_read_csr,
+ write_csr:pcnet32_wio_write_csr,
+ read_bcr:pcnet32_wio_read_bcr,
+ write_bcr:pcnet32_wio_write_bcr,
+ read_rap:pcnet32_wio_read_rap,
+ write_rap:pcnet32_wio_write_rap,
+ reset:pcnet32_wio_reset
+};
+
+static u16 pcnet32_dwio_read_csr(unsigned long addr, int index)
+{
+ outl(index, addr + PCNET32_DWIO_RAP);
+ return (inl(addr + PCNET32_DWIO_RDP) & 0xffff);
+}
+
+static void pcnet32_dwio_write_csr(unsigned long addr, int index, u16 val)
+{
+ outl(index, addr + PCNET32_DWIO_RAP);
+ outl(val, addr + PCNET32_DWIO_RDP);
+}
+
+static u16 pcnet32_dwio_read_bcr(unsigned long addr, int index)
+{
+ outl(index, addr + PCNET32_DWIO_RAP);
+ return (inl(addr + PCNET32_DWIO_BDP) & 0xffff);
+}
+
+static void pcnet32_dwio_write_bcr(unsigned long addr, int index, u16 val)
+{
+ outl(index, addr + PCNET32_DWIO_RAP);
+ outl(val, addr + PCNET32_DWIO_BDP);
+}
+
+static u16 pcnet32_dwio_read_rap(unsigned long addr)
+{
+ return (inl(addr + PCNET32_DWIO_RAP) & 0xffff);
+}
+
+static void pcnet32_dwio_write_rap(unsigned long addr, u16 val)
+{
+ outl(val, addr + PCNET32_DWIO_RAP);
+}
+
+static void pcnet32_dwio_reset(unsigned long addr)
+{
+ inl(addr + PCNET32_DWIO_RESET);
+}
+
+static int pcnet32_dwio_check(unsigned long addr)
+{
+ outl(88, addr + PCNET32_DWIO_RAP);
+ return ((inl(addr + PCNET32_DWIO_RAP) & 0xffff) == 88);
+}
+
+static struct pcnet32_access pcnet32_dwio = {
+ read_csr:pcnet32_dwio_read_csr,
+ write_csr:pcnet32_dwio_write_csr,
+ read_bcr:pcnet32_dwio_read_bcr,
+ write_bcr:pcnet32_dwio_write_bcr,
+ read_rap:pcnet32_dwio_read_rap,
+ write_rap:pcnet32_dwio_write_rap,
+ reset:pcnet32_dwio_reset
+};
+
+
+/* Initialize the PCNET32 Rx and Tx rings. */
+static int pcnet32_init_ring(struct nic *nic)
+{
+ int i;
+
+ lp->tx_full = 0;
+ lp->cur_rx = lp->cur_tx = 0;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ rx_ring[i].base = (u32) virt_to_le32desc(&rxb[i]);
+ rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ);
+ rx_ring[i].status = le16_to_cpu(0x8000);
+ }
+
+ /* The Tx buffer address is filled in as needed, but we do need to clear
+ the upper ownership bit. */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ tx_ring[i].base = 0;
+ tx_ring[i].status = 0;
+ }
+
+
+ lp->init_block.tlen_rlen =
+ le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS);
+ for (i = 0; i < 6; i++)
+ lp->init_block.phys_addr[i] = nic->node_addr[i];
+ lp->init_block.rx_ring = (u32) virt_to_le32desc(&rx_ring[0]);
+ lp->init_block.tx_ring = (u32) virt_to_le32desc(&tx_ring[0]);
+ return 0;
+}
+
+/**************************************************************************
+RESET - Reset adapter
+***************************************************************************/
+static void pcnet32_reset(struct nic *nic)
+{
+ /* put the card in its initial state */
+ u16 val;
+ int i;
+
+ /* Reset the PCNET32 */
+ lp->a.reset(ioaddr);
+
+ /* switch pcnet32 to 32bit mode */
+ lp->a.write_bcr(ioaddr, 20, 2);
+
+ /* set/reset autoselect bit */
+ val = lp->a.read_bcr(ioaddr, 2) & ~2;
+ if (lp->options & PCNET32_PORT_ASEL)
+ val |= 2;
+ lp->a.write_bcr(ioaddr, 2, val);
+ /* handle full duplex setting */
+ if (lp->full_duplex) {
+ val = lp->a.read_bcr(ioaddr, 9) & ~3;
+ if (lp->options & PCNET32_PORT_FD) {
+ val |= 1;
+ if (lp->options ==
+ (PCNET32_PORT_FD | PCNET32_PORT_AUI))
+ val |= 2;
+ } else if (lp->options & PCNET32_PORT_ASEL) {
+ /* workaround of xSeries250, turn on for 79C975 only */
+ i = ((lp->a.
+ read_csr(ioaddr,
+ 88) | (lp->a.read_csr(ioaddr,
+ 89) << 16)) >>
+ 12) & 0xffff;
+ if (i == 0x2627)
+ val |= 3;
+ }
+ lp->a.write_bcr(ioaddr, 9, val);
+ }
+
+ /* set/reset GPSI bit in test register */
+ val = lp->a.read_csr(ioaddr, 124) & ~0x10;
+ if ((lp->options & PCNET32_PORT_PORTSEL) == PCNET32_PORT_GPSI)
+ val |= 0x10;
+ lp->a.write_csr(ioaddr, 124, val);
+
+ if (lp->mii && !(lp->options & PCNET32_PORT_ASEL)) {
+ val = lp->a.read_bcr(ioaddr, 32) & ~0x38; /* disable Auto Negotiation, set 10Mpbs, HD */
+ if (lp->options & PCNET32_PORT_FD)
+ val |= 0x10;
+ if (lp->options & PCNET32_PORT_100)
+ val |= 0x08;
+ lp->a.write_bcr(ioaddr, 32, val);
+ } else {
+ if (lp->options & PCNET32_PORT_ASEL) { /* enable auto negotiate, setup, disable fd */
+ val = lp->a.read_bcr(ioaddr, 32) & ~0x98;
+ val |= 0x20;
+ lp->a.write_bcr(ioaddr, 32, val);
+ }
+ }
+
+#ifdef DO_DXSUFLO
+ if (lp->dxsuflo) { /* Disable transmit stop on underflow */
+ val = lp->a.read_csr(ioaddr, 3);
+ val |= 0x40;
+ lp->a.write_csr(ioaddr, 3, val);
+ }
+#endif
+
+ if (lp->ltint) { /* Enable TxDone-intr inhibitor */
+ val = lp->a.read_csr(ioaddr, 5);
+ val |= (1 << 14);
+ lp->a.write_csr(ioaddr, 5, val);
+ }
+ lp->init_block.mode =
+ le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7);
+ lp->init_block.filter[0] = 0xffffffff;
+ lp->init_block.filter[1] = 0xffffffff;
+
+ pcnet32_init_ring(nic);
+
+
+ /* Re-initialize the PCNET32, and start it when done. */
+ lp->a.write_csr(ioaddr, 1,
+ (virt_to_bus(&lp->init_block)) & 0xffff);
+ lp->a.write_csr(ioaddr, 2, (virt_to_bus(&lp->init_block)) >> 16);
+ lp->a.write_csr(ioaddr, 4, 0x0915);
+ lp->a.write_csr(ioaddr, 0, 0x0001);
+
+
+ i = 0;
+ while (i++ < 100)
+ if (lp->a.read_csr(ioaddr, 0) & 0x0100)
+ break;
+ /*
+ * We used to clear the InitDone bit, 0x0100, here but Mark Stockton
+ * reports that doing so triggers a bug in the '974.
+ */
+ lp->a.write_csr(ioaddr, 0, 0x0042);
+
+ dprintf(("pcnet32 open, csr0 %hX.\n", lp->a.read_csr(ioaddr, 0)));
+
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int pcnet32_poll(struct nic *nic __unused, int retrieve)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+
+ int status;
+ int entry;
+
+ entry = lp->cur_rx & RX_RING_MOD_MASK;
+ status = ((short) le16_to_cpu(rx_ring[entry].status) >> 8);
+
+ if (status < 0)
+ return 0;
+
+ if ( ! retrieve ) return 1;
+
+ if (status == 0x03) {
+ nic->packetlen =
+ (le32_to_cpu(rx_ring[entry].msg_length) & 0xfff) - 4;
+ memcpy(nic->packet, &rxb[entry], nic->packetlen);
+
+ /* Andrew Boyd of QNX reports that some revs of the 79C765
+ * clear the buffer length */
+ rx_ring[entry].buf_length = le16_to_cpu(-PKT_BUF_SZ);
+ rx_ring[entry].status |= le16_to_cpu(0x8000); /* prime for next receive */
+ /* Switch to the next Rx ring buffer */
+ lp->cur_rx++;
+
+ } else {
+ return 0;
+ }
+
+ return 1;
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void pcnet32_transmit(struct nic *nic __unused, const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p)
+{ /* Packet */
+ /* send the packet to destination */
+ unsigned long time;
+ u8 *ptxb;
+ u16 nstype;
+ u16 status;
+ int entry = 0; /*lp->cur_tx & TX_RING_MOD_MASK; */
+
+ status = 0x8300;
+ /* point to the current txb incase multiple tx_rings are used */
+ ptxb = txb + (lp->cur_tx * PKT_BUF_SZ);
+
+ /* copy the packet to ring buffer */
+ memcpy(ptxb, d, ETH_ALEN); /* dst */
+ memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */
+ nstype = htons((u16) t); /* type */
+ memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2); /* type */
+ memcpy(ptxb + ETH_HLEN, p, s);
+
+ s += ETH_HLEN;
+ while (s < ETH_ZLEN) /* pad to min length */
+ ptxb[s++] = '\0';
+
+ tx_ring[entry].length = le16_to_cpu(-s);
+ tx_ring[entry].misc = 0x00000000;
+ tx_ring[entry].base = (u32) virt_to_le32desc(ptxb);
+
+ /* we set the top byte as the very last thing */
+ tx_ring[entry].status = le16_to_cpu(status);
+
+
+ /* Trigger an immediate send poll */
+ lp->a.write_csr(ioaddr, 0, 0x0048);
+
+ /* wait for transmit complete */
+ lp->cur_tx = 0; /* (lp->cur_tx + 1); */
+ time = currticks() + TICKS_PER_SEC; /* wait one second */
+ while (currticks() < time &&
+ ((short) le16_to_cpu(tx_ring[entry].status) < 0));
+
+ if ((short) le16_to_cpu(tx_ring[entry].status) < 0)
+ printf("PCNET32 timed out on transmit\n");
+
+ /* Stop pointing at the current txb
+ * otherwise the card continues to send the packet */
+ tx_ring[entry].base = 0;
+
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void pcnet32_disable(struct dev *dev __unused)
+{
+ /* Stop the PCNET32 here -- it ocassionally polls memory if we don't */
+ lp->a.write_csr(ioaddr, 0, 0x0004);
+
+ /*
+ * Switch back to 16-bit mode to avoid problesm with dumb
+ * DOS packet driver after a warm reboot
+ */
+ lp->a.write_bcr(ioaddr, 20, 4);
+}
+
+/**************************************************************************
+IRQ - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void pcnet32_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+You should omit the last argument struct pci_device * for a non-PCI NIC
+***************************************************************************/
+static int pcnet32_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *) dev;
+ int i, media;
+ int fdx, mii, fset, dxsuflo, ltint;
+ int chip_version;
+ char *chipname;
+ struct pcnet32_access *a = NULL;
+ u8 promaddr[6];
+
+ int shared = 1;
+ if (pci->ioaddr == 0)
+ return 0;
+
+ /* BASE is used throughout to address the card */
+ ioaddr = pci->ioaddr;
+ printf("pcnet32.c: Found %s, Vendor=0x%hX Device=0x%hX\n",
+ pci->name, pci->vendor, pci->dev_id);
+
+ nic->irqno = 0;
+ nic->ioaddr = pci->ioaddr & ~3;
+
+ /* reset the chip */
+ pcnet32_wio_reset(ioaddr);
+
+ /* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */
+ if (pcnet32_wio_read_csr(ioaddr, 0) == 4
+ && pcnet32_wio_check(ioaddr)) {
+ a = &pcnet32_wio;
+ } else {
+ pcnet32_dwio_reset(ioaddr);
+ if (pcnet32_dwio_read_csr(ioaddr, 0) == 4
+ && pcnet32_dwio_check(ioaddr)) {
+ a = &pcnet32_dwio;
+ } else
+ return 0;
+ }
+
+ chip_version =
+ a->read_csr(ioaddr, 88) | (a->read_csr(ioaddr, 89) << 16);
+
+ dprintf(("PCnet chip version is %0xhX\n", chip_version));
+ if ((chip_version & 0xfff) != 0x003)
+ return 0;
+
+ /* initialize variables */
+ fdx = mii = fset = dxsuflo = ltint = 0;
+ chip_version = (chip_version >> 12) & 0xffff;
+
+ switch (chip_version) {
+ case 0x2420:
+ chipname = "PCnet/PCI 79C970"; /* PCI */
+ break;
+ case 0x2430:
+ if (shared)
+ chipname = "PCnet/PCI 79C970"; /* 970 gives the wrong chip id back */
+ else
+ chipname = "PCnet/32 79C965"; /* 486/VL bus */
+ break;
+ case 0x2621:
+ chipname = "PCnet/PCI II 79C970A"; /* PCI */
+ fdx = 1;
+ break;
+ case 0x2623:
+ chipname = "PCnet/FAST 79C971"; /* PCI */
+ fdx = 1;
+ mii = 1;
+ fset = 1;
+ ltint = 1;
+ break;
+ case 0x2624:
+ chipname = "PCnet/FAST+ 79C972"; /* PCI */
+ fdx = 1;
+ mii = 1;
+ fset = 1;
+ break;
+ case 0x2625:
+ chipname = "PCnet/FAST III 79C973"; /* PCI */
+ fdx = 1;
+ mii = 1;
+ break;
+ case 0x2626:
+ chipname = "PCnet/Home 79C978"; /* PCI */
+ fdx = 1;
+ /*
+ * This is based on specs published at www.amd.com. This section
+ * assumes that a card with a 79C978 wants to go into 1Mb HomePNA
+ * mode. The 79C978 can also go into standard ethernet, and there
+ * probably should be some sort of module option to select the
+ * mode by which the card should operate
+ */
+ /* switch to home wiring mode */
+ media = a->read_bcr(ioaddr, 49);
+
+ printf("media reset to %#x.\n", media);
+ a->write_bcr(ioaddr, 49, media);
+ break;
+ case 0x2627:
+ chipname = "PCnet/FAST III 79C975"; /* PCI */
+ fdx = 1;
+ mii = 1;
+ break;
+ default:
+ printf("PCnet version %#x, no PCnet32 chip.\n",
+ chip_version);
+ return 0;
+ }
+
+ /*
+ * On selected chips turn on the BCR18:NOUFLO bit. This stops transmit
+ * starting until the packet is loaded. Strike one for reliability, lose
+ * one for latency - although on PCI this isnt a big loss. Older chips
+ * have FIFO's smaller than a packet, so you can't do this.
+ */
+
+ if (fset) {
+ a->write_bcr(ioaddr, 18,
+ (a->read_bcr(ioaddr, 18) | 0x0800));
+ a->write_csr(ioaddr, 80,
+ (a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00);
+ dxsuflo = 1;
+ ltint = 1;
+ }
+
+ dprintf(("%s at %hX,", chipname, ioaddr));
+
+ /* read PROM address */
+ for (i = 0; i < 6; i++)
+ promaddr[i] = inb(ioaddr + i);
+
+ /* Update the nic structure with the MAC Address */
+ for (i = 0; i < ETH_ALEN; i++) {
+ nic->node_addr[i] = promaddr[i];
+ }
+ /* Print out some hardware info */
+ printf("%s: %! at ioaddr %hX, ", pci->name, nic->node_addr,
+ ioaddr);
+
+ /* Set to pci bus master */
+ adjust_pci_device(pci);
+
+ /* point to private storage */
+ lp = &lpx;
+
+#if EBDEBUG
+ if (((chip_version + 1) & 0xfffe) == 0x2624) { /* Version 0x2623 or 0x2624 */
+ i = a->read_csr(ioaddr, 80) & 0x0C00; /* Check tx_start_pt */
+ dprintf((" tx_start_pt(0x%hX):", i));
+ switch (i >> 10) {
+ case 0:
+ dprintf((" 20 bytes,"));
+ break;
+ case 1:
+ dprintf((" 64 bytes,"));
+ break;
+ case 2:
+ dprintf((" 128 bytes,"));
+ break;
+ case 3:
+ dprintf(("~220 bytes,"));
+ break;
+ }
+ i = a->read_bcr(ioaddr, 18); /* Check Burst/Bus control */
+ dprintf((" BCR18(%hX):", i & 0xffff));
+ if (i & (1 << 5))
+ dprintf(("BurstWrEn "));
+ if (i & (1 << 6))
+ dprintf(("BurstRdEn "));
+ if (i & (1 << 7))
+ dprintf(("DWordIO "));
+ if (i & (1 << 11))
+ dprintf(("NoUFlow "));
+ i = a->read_bcr(ioaddr, 25);
+ dprintf((" SRAMSIZE=0x%hX,", i << 8));
+ i = a->read_bcr(ioaddr, 26);
+ dprintf((" SRAM_BND=0x%hX,", i << 8));
+ i = a->read_bcr(ioaddr, 27);
+ if (i & (1 << 14))
+ dprintf(("LowLatRx"));
+ }
+#endif
+ lp->name = chipname;
+ lp->shared_irq = shared;
+ lp->full_duplex = fdx;
+ lp->dxsuflo = dxsuflo;
+ lp->ltint = ltint;
+ lp->mii = mii;
+ /* FIXME: Fix Options for only one card */
+ if ((cards_found >= MAX_UNITS)
+ || ((unsigned int) options[cards_found] > sizeof(options_mapping)))
+ lp->options = PCNET32_PORT_ASEL;
+ else
+ lp->options = options_mapping[options[cards_found]];
+
+ if (fdx && !(lp->options & PCNET32_PORT_ASEL) &&
+ ((cards_found >= MAX_UNITS) || full_duplex[cards_found]))
+ lp->options |= PCNET32_PORT_FD;
+
+ if (!a) {
+ printf("No access methods\n");
+ return 0;
+ }
+ lp->a = *a;
+
+ /* detect special T1/E1 WAN card by checking for MAC address */
+ if (nic->node_addr[0] == 0x00 && nic->node_addr[1] == 0xe0
+ && nic->node_addr[2] == 0x75)
+ lp->options = PCNET32_PORT_FD | PCNET32_PORT_GPSI;
+
+ lp->init_block.mode = le16_to_cpu(0x0003); /* Disable Rx and Tx. */
+ lp->init_block.tlen_rlen =
+ le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS);
+ for (i = 0; i < 6; i++)
+ lp->init_block.phys_addr[i] = nic->node_addr[i];
+ lp->init_block.filter[0] = 0xffffffff;
+ lp->init_block.filter[1] = 0xffffffff;
+ lp->init_block.rx_ring = virt_to_bus(&rx_ring);
+ lp->init_block.tx_ring = virt_to_bus(&tx_ring);
+
+ /* switch pcnet32 to 32bit mode */
+ a->write_bcr(ioaddr, 20, 2);
+
+
+ a->write_csr(ioaddr, 1, (virt_to_bus(&lp->init_block)) & 0xffff);
+ a->write_csr(ioaddr, 2, (virt_to_bus(&lp->init_block)) >> 16);
+
+ /*
+ * To auto-IRQ we enable the initialization-done and DMA error
+ * interrupts. For ISA boards we get a DMA error, but VLB and PCI
+ * boards will work.
+ */
+ /* Trigger an initialization just for the interrupt. */
+
+ a->write_csr(ioaddr, 0, 0x41);
+ mdelay(1);
+
+ cards_found++;
+
+ /* point to NIC specific routines */
+ pcnet32_reset(nic);
+ if (1) {
+ int tmp;
+ int phy, phy_idx = 0;
+ u16 mii_lpa;
+ lp->phys[0] = 1; /* Default Setting */
+ for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) {
+ int mii_status = mdio_read(nic, phy, MII_BMSR);
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ lp->phys[phy_idx++] = phy;
+ lp->mii_if.advertising =
+ mdio_read(nic, phy, MII_ADVERTISE);
+ if ((mii_status & 0x0040) == 0) {
+ tmp = phy;
+ dprintf (("MII PHY found at address %d, status "
+ "%hX advertising %hX\n", phy, mii_status,
+ lp->mii_if.advertising));
+ }
+ }
+ }
+ if (phy_idx == 0)
+ printf("No MII transceiver found!\n");
+ lp->mii_if.phy_id = lp->phys[0];
+
+ lp->mii_if.advertising =
+ mdio_read(nic, lp->phys[0], MII_ADVERTISE);
+
+ mii_lpa = mdio_read(nic, lp->phys[0], MII_LPA);
+ lp->mii_if.advertising &= mii_lpa;
+ if (lp->mii_if.advertising & ADVERTISE_100FULL)
+ printf("100Mbps Full-Duplex\n");
+ else if (lp->mii_if.advertising & ADVERTISE_100HALF)
+ printf("100Mbps Half-Duplex\n");
+ else if (lp->mii_if.advertising & ADVERTISE_10FULL)
+ printf("10Mbps Full-Duplex\n");
+ else if (lp->mii_if.advertising & ADVERTISE_10HALF)
+ printf("10Mbps Half-Duplex\n");
+ else
+ printf("\n");
+ }
+
+ nic->poll = pcnet32_poll;
+ nic->transmit = pcnet32_transmit;
+ dev->disable = pcnet32_disable;
+ nic->irq = pcnet32_irq;
+
+ return 1;
+}
+static int mdio_read(struct nic *nic __unused, int phy_id, int reg_num)
+{
+ u16 val_out;
+ int phyaddr;
+
+ if (!lp->mii)
+ return 0;
+
+ phyaddr = lp->a.read_bcr(ioaddr, 33);
+
+ lp->a.write_bcr(ioaddr, 33,
+ ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
+ val_out = lp->a.read_bcr(ioaddr, 34);
+ lp->a.write_bcr(ioaddr, 33, phyaddr);
+
+ return val_out;
+}
+
+#if 0
+static void mdio_write(struct nic *nic __unused, int phy_id, int reg_num,
+ int val)
+{
+ int phyaddr;
+
+ if (!lp->mii)
+ return;
+
+ phyaddr = lp->a.read_bcr(ioaddr, 33);
+
+ lp->a.write_bcr(ioaddr, 33,
+ ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
+ lp->a.write_bcr(ioaddr, 34, val);
+ lp->a.write_bcr(ioaddr, 33, phyaddr);
+}
+#endif
+
+static struct pci_id pcnet32_nics[] = {
+ PCI_ROM(0x1022, 0x2000, "lancepci", "AMD Lance/PCI"),
+ PCI_ROM(0x1022, 0x2625, "pcnetfastiii", "AMD Lance/PCI PCNet/32"),
+ PCI_ROM(0x1022, 0x2001, "amdhomepna", "AMD Lance/HomePNA"),
+};
+
+static struct pci_driver pcnet32_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "PCNET32/PCI",
+ .probe = pcnet32_probe,
+ .ids = pcnet32_nics,
+ .id_count = sizeof(pcnet32_nics) / sizeof(pcnet32_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/pnic.c b/src/drivers/net/pnic.c
new file mode 100644
index 00000000..25c9cc1b
--- /dev/null
+++ b/src/drivers/net/pnic.c
@@ -0,0 +1,267 @@
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+Bochs Pseudo NIC driver for Etherboot
+***************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ *
+ * See pnic_api.h for an explanation of the Bochs Pseudo NIC.
+ */
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include "pci.h"
+
+/* PNIC API */
+#include "pnic_api.h"
+
+/* Private data structure */
+typedef struct {
+ uint16_t api_version;
+} pnic_priv_data_t;
+
+/* Function prototypes */
+static int pnic_api_check ( uint16_t api_version );
+
+/* NIC specific static variables go here */
+static uint8_t tx_buffer[ETH_FRAME_LEN];
+
+/*
+ * Utility functions: issue a PNIC command, retrieve result. Use
+ * pnic_command_quiet if you don't want failure codes to be
+ * automatically printed. Returns the PNIC status code.
+ *
+ * Set output_length to NULL only if you expect to receive exactly
+ * output_max_length bytes, otherwise it'll complain that you didn't
+ * get enough data (on the assumption that if you not interested in
+ * discovering the output length then you're expecting a fixed amount
+ * of data).
+ */
+
+static uint16_t pnic_command_quiet ( struct nic *nic, uint16_t command,
+ void *input, uint16_t input_length,
+ void *output, uint16_t output_max_length,
+ uint16_t *output_length ) {
+ int i;
+ uint16_t status;
+ uint16_t _output_length;
+
+ if ( input != NULL ) {
+ /* Write input length */
+ outw ( input_length, nic->ioaddr + PNIC_REG_LEN );
+ /* Write input data */
+ for ( i = 0; i < input_length; i++ ) {
+ outb( ((char*)input)[i], nic->ioaddr + PNIC_REG_DATA );
+ }
+ }
+ /* Write command */
+ outw ( command, nic->ioaddr + PNIC_REG_CMD );
+ /* Retrieve status */
+ status = inw ( nic->ioaddr + PNIC_REG_STAT );
+ /* Retrieve output length */
+ _output_length = inw ( nic->ioaddr + PNIC_REG_LEN );
+ if ( output_length == NULL ) {
+ if ( _output_length != output_max_length ) {
+ printf ( "pnic_command %#hx: wrong data length "
+ "returned (expected %d, got %d)\n", command,
+ output_max_length, _output_length );
+ }
+ } else {
+ *output_length = _output_length;
+ }
+ if ( output != NULL ) {
+ if ( _output_length > output_max_length ) {
+ printf ( "pnic_command %#hx: output buffer too small "
+ "(have %d, need %d)\n", command,
+ output_max_length, _output_length );
+ _output_length = output_max_length;
+ }
+ /* Retrieve output data */
+ for ( i = 0; i < _output_length; i++ ) {
+ ((char*)output)[i] =
+ inb ( nic->ioaddr + PNIC_REG_DATA );
+ }
+ }
+ return status;
+}
+
+static uint16_t pnic_command ( struct nic *nic, uint16_t command,
+ void *input, uint16_t input_length,
+ void *output, uint16_t output_max_length,
+ uint16_t *output_length ) {
+ pnic_priv_data_t *priv = (pnic_priv_data_t*)nic->priv_data;
+ uint16_t status = pnic_command_quiet ( nic, command,
+ input, input_length,
+ output, output_max_length,
+ output_length );
+ if ( status == PNIC_STATUS_OK ) return status;
+ printf ( "PNIC command %#hx (len %#hx) failed with status %#hx\n",
+ command, input_length, status );
+ if ( priv->api_version ) pnic_api_check(priv->api_version);
+ return status;
+}
+
+/* Check API version matches that of NIC */
+static int pnic_api_check ( uint16_t api_version ) {
+ if ( api_version != PNIC_API_VERSION ) {
+ printf ( "Warning: API version mismatch! "
+ "(NIC's is %d.%d, ours is %d.%d)\n",
+ api_version >> 8, api_version & 0xff,
+ PNIC_API_VERSION >> 8, PNIC_API_VERSION & 0xff );
+ }
+ if ( api_version < PNIC_API_VERSION ) {
+ printf ( "*** You may need to update your copy of Bochs ***\n" );
+ }
+ return ( api_version == PNIC_API_VERSION );
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int pnic_poll(struct nic *nic, int retrieve)
+{
+ uint16_t length;
+ uint16_t qlen;
+
+ /* Check receive queue length to see if there's anything to
+ * get. Necessary since once we've called PNIC_CMD_RECV we
+ * have to read out the packet, otherwise it's lost forever.
+ */
+ if ( pnic_command ( nic, PNIC_CMD_RECV_QLEN, NULL, 0,
+ &qlen, sizeof(qlen), NULL )
+ != PNIC_STATUS_OK ) return ( 0 );
+ if ( qlen == 0 ) return ( 0 );
+
+ /* There is a packet ready. Return 1 if we're only checking. */
+ if ( ! retrieve ) return ( 1 );
+
+ /* Retrieve the packet */
+ if ( pnic_command ( nic, PNIC_CMD_RECV, NULL, 0,
+ nic->packet, ETH_FRAME_LEN, &length )
+ != PNIC_STATUS_OK ) return ( 0 );
+ nic->packetlen = length;
+ return ( 1 );
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void pnic_transmit(
+ struct nic *nic,
+ const char *dest, /* Destination */
+ unsigned int type, /* Type */
+ unsigned int size, /* size */
+ const char *data) /* Packet */
+{
+ unsigned int nstype = htons ( type );
+
+ if ( ( ETH_HLEN + size ) >= ETH_FRAME_LEN ) {
+ printf ( "pnic_transmit: packet too large\n" );
+ return;
+ }
+
+ /* Assemble packet */
+ memcpy ( tx_buffer, dest, ETH_ALEN );
+ memcpy ( tx_buffer + ETH_ALEN, nic->node_addr, ETH_ALEN );
+ memcpy ( tx_buffer + 2 * ETH_ALEN, &nstype, 2 );
+ memcpy ( tx_buffer + ETH_HLEN, data, size );
+
+ pnic_command ( nic, PNIC_CMD_XMIT, tx_buffer, ETH_HLEN + size,
+ NULL, 0, NULL );
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void pnic_disable(struct dev *dev)
+{
+ struct nic *nic = (struct nic *)dev;
+ pnic_command ( nic, PNIC_CMD_RESET, NULL, 0, NULL, 0, NULL );
+}
+
+/**************************************************************************
+IRQ - Handle card interrupt status
+***************************************************************************/
+static void pnic_irq ( struct nic *nic, irq_action_t action )
+{
+ uint8_t enabled;
+
+ switch ( action ) {
+ case DISABLE :
+ case ENABLE :
+ enabled = ( action == ENABLE ? 1 : 0 );
+ pnic_command ( nic, PNIC_CMD_MASK_IRQ,
+ &enabled, sizeof(enabled), NULL, 0, NULL );
+ break;
+ case FORCE :
+ pnic_command ( nic, PNIC_CMD_FORCE_IRQ,
+ NULL, 0, NULL, 0, NULL );
+ break;
+ }
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+
+static int pnic_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *)dev;
+ static pnic_priv_data_t priv;
+ uint16_t status;
+
+ printf(" - ");
+
+ /* Clear private data structure and chain it in */
+ memset ( &priv, 0, sizeof(priv) );
+ nic->priv_data = &priv;
+
+ /* Mask the bit that says "this is an io addr" */
+ nic->ioaddr = pci->ioaddr & ~3;
+ nic->irqno = pci->irq;
+ /* Not sure what this does, but the rtl8139 driver does it */
+ adjust_pci_device(pci);
+
+ status = pnic_command_quiet( nic, PNIC_CMD_API_VER, NULL, 0,
+ &priv.api_version,
+ sizeof(priv.api_version), NULL );
+ if ( status != PNIC_STATUS_OK ) {
+ printf ( "PNIC failed installation check, code %#hx\n",
+ status );
+ return 0;
+ }
+ pnic_api_check(priv.api_version);
+ status = pnic_command ( nic, PNIC_CMD_READ_MAC, NULL, 0,
+ nic->node_addr, ETH_ALEN, NULL );
+ printf ( "Detected Bochs Pseudo NIC MAC %! (API v%d.%d) at %#hx\n",
+ nic->node_addr, priv.api_version>>8, priv.api_version&0xff,
+ nic->ioaddr );
+
+ /* point to NIC specific routines */
+ dev->disable = pnic_disable;
+ nic->poll = pnic_poll;
+ nic->transmit = pnic_transmit;
+ nic->irq = pnic_irq;
+ return 1;
+}
+
+static struct pci_id pnic_nics[] = {
+/* genrules.pl doesn't let us use macros for PCI IDs...*/
+PCI_ROM(0xfefe, 0xefef, "pnic", "Bochs Pseudo NIC Adaptor"),
+};
+
+static struct pci_driver pnic_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "PNIC",
+ .probe = pnic_probe,
+ .ids = pnic_nics,
+ .id_count = sizeof(pnic_nics)/sizeof(pnic_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/pnic_api.h b/src/drivers/net/pnic_api.h
new file mode 100644
index 00000000..6d117fa6
--- /dev/null
+++ b/src/drivers/net/pnic_api.h
@@ -0,0 +1,59 @@
+/*
+ * Constants etc. for the Bochs/Etherboot pseudo-NIC
+ *
+ * This header file must be valid C and C++.
+ *
+ * Operation of the pseudo-NIC (PNIC) is pretty simple. To write a
+ * command plus data, first write the length of the data to
+ * PNIC_REG_LEN, then write the data a byte at a type to
+ * PNIC_REG_DATA, then write the command code to PNIC_REG_CMD. The
+ * status will be available from PNIC_REG_STAT. The length of any
+ * data returned will be in PNIC_REG_LEN and can be read a byte at a
+ * time from PNIC_REG_DATA.
+ */
+
+/*
+ * PCI parameters
+ */
+#define PNIC_PCI_VENDOR 0xfefe /* Hopefully these won't clash with */
+#define PNIC_PCI_DEVICE 0xefef /* any real PCI device IDs. */
+
+/*
+ * 'Hardware' register addresses, offset from io_base
+ */
+#define PNIC_REG_CMD 0x00 /* Command register, 2 bytes, write only */
+#define PNIC_REG_STAT 0x00 /* Status register, 2 bytes, read only */
+#define PNIC_REG_LEN 0x02 /* Length register, 2 bytes, read-write */
+#define PNIC_REG_DATA 0x04 /* Data port, 1 byte, read-write */
+/*
+ * PNIC_MAX_REG used in Bochs to claim i/o space
+ */
+#define PNIC_MAX_REG 0x04
+
+/*
+ * Command code definitions: write these into PNIC_REG_CMD
+ */
+#define PNIC_CMD_NOOP 0x0000
+#define PNIC_CMD_API_VER 0x0001
+#define PNIC_CMD_READ_MAC 0x0002
+#define PNIC_CMD_RESET 0x0003
+#define PNIC_CMD_XMIT 0x0004
+#define PNIC_CMD_RECV 0x0005
+#define PNIC_CMD_RECV_QLEN 0x0006
+#define PNIC_CMD_MASK_IRQ 0x0007
+#define PNIC_CMD_FORCE_IRQ 0x0008
+
+/*
+ * Status code definitions: read these from PNIC_REG_STAT
+ *
+ * We avoid using status codes that might be confused with
+ * randomly-read data (e.g. 0x0000, 0xffff etc.)
+ */
+#define PNIC_STATUS_OK 0x4f4b /* 'OK' */
+#define PNIC_STATUS_UNKNOWN_CMD 0x3f3f /* '??' */
+
+/*
+ * Other miscellaneous information
+ */
+
+#define PNIC_API_VERSION 0x0101 /* 1.1 */
diff --git a/src/drivers/net/prism2.c b/src/drivers/net/prism2.c
new file mode 100644
index 00000000..da010811
--- /dev/null
+++ b/src/drivers/net/prism2.c
@@ -0,0 +1,948 @@
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+Prism2 NIC driver for Etherboot
+
+Written by Michael Brown of Fen Systems Ltd
+$Id$
+***************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include "pci.h"
+
+/*
+ * Hard-coded SSID
+ * Leave blank in order to connect to any available SSID
+ */
+
+static const char hardcoded_ssid[] = "";
+
+/*
+ * Maximum number of info packets to wait for on a join attempt.
+ * Some APs (including the Linksys WAP11) will send a "you are disconnected" packet
+ * before sending the "you are connected" packet, if the card has previously been
+ * attached to the AP.
+ *
+ * 2 is probably a sensible value, but YMMV.
+ */
+
+#define MAX_JOIN_INFO_COUNT 2
+
+/*
+ * Type of Prism2 interface to support
+ * If not already defined, select PLX
+ */
+#ifndef WLAN_HOSTIF
+#define WLAN_HOSTIF WLAN_PLX
+#endif
+
+/*
+ * Include wlan_compat, p80211 and hfa384x header files from Linux Prism2 driver
+ * We need to hack some defines in order to avoid compiling kernel-specific routines
+ */
+
+#define __LINUX_WLAN__
+#undef __KERNEL__
+#define __I386__
+#include "wlan_compat.h"
+#include "p80211hdr.h"
+#include "hfa384x.h"
+#define BAP_TIMEOUT ( 5000 )
+
+/*
+ * A few hacks to make the coding environment more Linux-like. This makes it somewhat
+ * quicker to convert code from the Linux Prism2 driver.
+ */
+#include <errno.h>
+#include "timer.h"
+#define __le16_to_cpu(x) (x)
+#define __le32_to_cpu(x) (x)
+#define __cpu_to_le16(x) (x)
+#define __cpu_to_le32(x) (x)
+
+/*
+ * PLX9052 PCI register offsets
+ * Taken from PLX9052 datasheet available from http://www.plxtech.com/download/9052/databook/9052db-20.pdf
+ */
+
+#define PLX_LOCAL_CONFIG_REGISTER_BASE ( PCI_BASE_ADDRESS_1 )
+#define PLX_LOCAL_ADDRESS_SPACE_0_BASE ( PCI_BASE_ADDRESS_2 )
+#define PLX_LOCAL_ADDRESS_SPACE_1_BASE ( PCI_BASE_ADDRESS_3 )
+#define PLX_LOCAL_ADDRESS_SPACE_2_BASE ( PCI_BASE_ADDRESS_4 )
+#define PLX_LOCAL_ADDRESS_SPACE_3_BASE ( PCI_BASE_ADDRESS_5 )
+
+#define PRISM2_PLX_ATTR_MEM_BASE ( PLX_LOCAL_ADDRESS_SPACE_0_BASE )
+#define PRISM2_PLX_IO_BASE ( PLX_LOCAL_ADDRESS_SPACE_1_BASE )
+
+#define PRISM2_PCI_MEM_BASE ( PCI_BASE_ADDRESS_0 )
+
+/*
+ * PCMCIA CIS types
+ * Taken from cistpl.h in pcmcia-cs
+ */
+
+#define CISTPL_VERS_1 ( 0x15 )
+#define CISTPL_END ( 0xff )
+
+#define CIS_STEP ( 2 )
+#define CISTPL_HEADER_LEN ( 2 * CIS_STEP )
+#define CISTPL_LEN_OFF ( 1 * CIS_STEP )
+#define CISTPL_VERS_1_STR_OFF ( 4 * CIS_STEP )
+
+/*
+ * Prism2 constants
+ * Taken from prism2sta.c in linux-wlan-ng
+ */
+
+#define COR_OFFSET ( 0x3e0 ) /* COR attribute offset of Prism2 PC card */
+#define COR_VALUE ( 0x41 ) /* Enable PC card with irq in level trigger (but interrupts disabled) */
+
+/* NIC specific static variables */
+
+/* The hfa384x_t structure is used extensively in the Linux driver but is ifdef'd out in our include since __KERNEL__ is not defined.
+ * This is a dummy version that contains only the fields we are interested in.
+ */
+
+typedef struct hfa384x
+{
+ UINT32 iobase;
+ UINT32 membase;
+ UINT16 lastcmd;
+ UINT16 status; /* in host order */
+ UINT16 resp0; /* in host order */
+ UINT16 resp1; /* in host order */
+ UINT16 resp2; /* in host order */
+ UINT8 bssid[WLAN_BSSID_LEN];
+} hfa384x_t;
+
+/* The global instance of the hardware (i.e. where we store iobase and membase, in the absence of anywhere better to put them */
+static hfa384x_t hw_global = {
+ 0, 0, 0, 0, 0, 0, 0, {0,0,0,0,0,0}
+};
+
+/*
+ * 802.11 headers in addition to those in hfa384x_tx_frame_t (LLC and SNAP)
+ * Taken from p80211conv.h
+ */
+
+typedef struct wlan_llc
+{
+ UINT8 dsap __WLAN_ATTRIB_PACK__;
+ UINT8 ssap __WLAN_ATTRIB_PACK__;
+ UINT8 ctl __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ wlan_llc_t;
+
+static const wlan_llc_t wlan_llc_snap = { 0xaa, 0xaa, 0x03 }; /* LLC header indicating SNAP (?) */
+
+#define WLAN_IEEE_OUI_LEN 3
+typedef struct wlan_snap
+{
+ UINT8 oui[WLAN_IEEE_OUI_LEN] __WLAN_ATTRIB_PACK__;
+ UINT16 type __WLAN_ATTRIB_PACK__;
+} __WLAN_ATTRIB_PACK__ wlan_snap_t;
+
+typedef struct wlan_80211hdr
+{
+ wlan_llc_t llc;
+ wlan_snap_t snap;
+} wlan_80211hdr_t;
+
+/*
+ * Function prototypes
+ */
+
+#if (WLAN_HOSTIF == WLAN_PLX)
+static int prism2_find_plx ( hfa384x_t *hw, struct pci_device *p );
+#elif (WLAN_HOSTIF == WLAN_PCI)
+static int prism2_find_pci ( hfa384x_t *hw, struct pci_device *p );
+#endif
+
+/*
+ * Hardware-level hfa384x functions
+ * These are based on the ones in hfa384x.h (which are ifdef'd out since __KERNEL__ is not defined).
+ * Basically, these functions are the result of hand-evaluating all the ifdefs and defines in the hfa384x.h versions.
+ */
+
+/* Retrieve the value of one of the MAC registers. */
+static inline UINT16 hfa384x_getreg( hfa384x_t *hw, UINT reg )
+{
+#if (WLAN_HOSTIF == WLAN_PLX)
+ return inw ( hw->iobase + reg );
+#elif (WLAN_HOSTIF == WLAN_PCI)
+ return readw ( hw->membase + reg );
+#endif
+}
+
+/* Set the value of one of the MAC registers. */
+static inline void hfa384x_setreg( hfa384x_t *hw, UINT16 val, UINT reg )
+{
+#if (WLAN_HOSTIF == WLAN_PLX)
+ outw ( val, hw->iobase + reg );
+#elif (WLAN_HOSTIF == WLAN_PCI)
+ writew ( val, hw->membase + reg );
+#endif
+ return;
+}
+
+/*
+ * Noswap versions
+ * Etherboot is i386 only, so swap and noswap are the same...
+ */
+static inline UINT16 hfa384x_getreg_noswap( hfa384x_t *hw, UINT reg )
+{
+ return hfa384x_getreg ( hw, reg );
+}
+static inline void hfa384x_setreg_noswap( hfa384x_t *hw, UINT16 val, UINT reg )
+{
+ hfa384x_setreg ( hw, val, reg );
+}
+
+/*
+ * Low-level hfa384x functions
+ * These are based on the ones in hfa384x.c, modified to work in the Etherboot environment.
+ */
+
+/*
+ * hfa384x_docmd_wait
+ *
+ * Waits for availability of the Command register, then
+ * issues the given command. Then polls the Evstat register
+ * waiting for command completion.
+ * Arguments:
+ * hw device structure
+ * cmd Command in host order
+ * parm0 Parameter0 in host order
+ * parm1 Parameter1 in host order
+ * parm2 Parameter2 in host order
+ * Returns:
+ * 0 success
+ * >0 command indicated error, Status and Resp0-2 are
+ * in hw structure.
+ */
+static int hfa384x_docmd_wait( hfa384x_t *hw, UINT16 cmd, UINT16 parm0, UINT16 parm1, UINT16 parm2)
+{
+ UINT16 reg = 0;
+ UINT16 counter = 0;
+
+ /* wait for the busy bit to clear */
+ counter = 0;
+ reg = hfa384x_getreg(hw, HFA384x_CMD);
+ while ( HFA384x_CMD_ISBUSY(reg) && (counter < 10) ) {
+ reg = hfa384x_getreg(hw, HFA384x_CMD);
+ counter++;
+ udelay(10);
+ }
+ if (HFA384x_CMD_ISBUSY(reg)) {
+ printf("hfa384x_cmd timeout(1), reg=0x%0hx.\n", reg);
+ return -ETIMEDOUT;
+ }
+
+ /* busy bit clear, write command */
+ hfa384x_setreg(hw, parm0, HFA384x_PARAM0);
+ hfa384x_setreg(hw, parm1, HFA384x_PARAM1);
+ hfa384x_setreg(hw, parm2, HFA384x_PARAM2);
+ hw->lastcmd = cmd;
+ hfa384x_setreg(hw, cmd, HFA384x_CMD);
+
+ /* Now wait for completion */
+ counter = 0;
+ reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
+ /* Initialization is the problem. It takes about
+ 100ms. "normal" commands are typically is about
+ 200-400 us (I've never seen less than 200). Longer
+ is better so that we're not hammering the bus. */
+ while ( !HFA384x_EVSTAT_ISCMD(reg) && (counter < 5000)) {
+ reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
+ counter++;
+ udelay(200);
+ }
+ if ( ! HFA384x_EVSTAT_ISCMD(reg) ) {
+ printf("hfa384x_cmd timeout(2), reg=0x%0hx.\n", reg);
+ return -ETIMEDOUT;
+ }
+
+ /* Read status and response */
+ hw->status = hfa384x_getreg(hw, HFA384x_STATUS);
+ hw->resp0 = hfa384x_getreg(hw, HFA384x_RESP0);
+ hw->resp1 = hfa384x_getreg(hw, HFA384x_RESP1);
+ hw->resp2 = hfa384x_getreg(hw, HFA384x_RESP2);
+ hfa384x_setreg(hw, HFA384x_EVACK_CMD, HFA384x_EVACK);
+ return HFA384x_STATUS_RESULT_GET(hw->status);
+}
+
+/*
+ * Prepare BAP for access. Assigns FID and RID, sets offset register
+ * and waits for BAP to become available.
+ *
+ * Arguments:
+ * hw device structure
+ * id FID or RID, destined for the select register (host order)
+ * offset An _even_ offset into the buffer for the given FID/RID.
+ * Returns:
+ * 0 success
+ */
+static int hfa384x_prepare_bap(hfa384x_t *hw, UINT16 id, UINT16 offset)
+{
+ int result = 0;
+ UINT16 reg;
+ UINT16 i;
+
+ /* Validate offset, buf, and len */
+ if ( (offset > HFA384x_BAP_OFFSET_MAX) || (offset % 2) ) {
+ result = -EINVAL;
+ } else {
+ /* Write fid/rid and offset */
+ hfa384x_setreg(hw, id, HFA384x_SELECT0);
+ udelay(10);
+ hfa384x_setreg(hw, offset, HFA384x_OFFSET0);
+ /* Wait for offset[busy] to clear (see BAP_TIMEOUT) */
+ i = 0;
+ do {
+ reg = hfa384x_getreg(hw, HFA384x_OFFSET0);
+ if ( i > 0 ) udelay(2);
+ i++;
+ } while ( i < BAP_TIMEOUT && HFA384x_OFFSET_ISBUSY(reg));
+ if ( i >= BAP_TIMEOUT ) {
+ /* failure */
+ result = reg;
+ } else if ( HFA384x_OFFSET_ISERR(reg) ){
+ /* failure */
+ result = reg;
+ }
+ }
+ return result;
+}
+
+/*
+ * Copy data from BAP to memory.
+ *
+ * Arguments:
+ * hw device structure
+ * id FID or RID, destined for the select register (host order)
+ * offset An _even_ offset into the buffer for the given FID/RID.
+ * buf ptr to array of bytes
+ * len length of data to transfer in bytes
+ * Returns:
+ * 0 success
+ */
+static int hfa384x_copy_from_bap(hfa384x_t *hw, UINT16 id, UINT16 offset,
+ void *buf, UINT len)
+{
+ int result = 0;
+ UINT8 *d = (UINT8*)buf;
+ UINT16 i;
+ UINT16 reg = 0;
+
+ /* Prepare BAP */
+ result = hfa384x_prepare_bap ( hw, id, offset );
+ if ( result == 0 ) {
+ /* Read even(len) buf contents from data reg */
+ for ( i = 0; i < (len & 0xfffe); i+=2 ) {
+ *(UINT16*)(&(d[i])) = hfa384x_getreg_noswap(hw, HFA384x_DATA0);
+ }
+ /* If len odd, handle last byte */
+ if ( len % 2 ){
+ reg = hfa384x_getreg_noswap(hw, HFA384x_DATA0);
+ d[len-1] = ((UINT8*)(&reg))[0];
+ }
+ }
+ if (result) {
+ printf ( "copy_from_bap(%#hx, %#hx, %d) failed, result=%#hx\n", id, offset, len, result);
+ }
+ return result;
+}
+
+/*
+ * Copy data from memory to BAP.
+ *
+ * Arguments:
+ * hw device structure
+ * id FID or RID, destined for the select register (host order)
+ * offset An _even_ offset into the buffer for the given FID/RID.
+ * buf ptr to array of bytes
+ * len length of data to transfer in bytes
+ * Returns:
+ * 0 success
+ */
+static int hfa384x_copy_to_bap(hfa384x_t *hw, UINT16 id, UINT16 offset,
+ void *buf, UINT len)
+{
+ int result = 0;
+ UINT8 *d = (UINT8*)buf;
+ UINT16 i;
+ UINT16 savereg;
+
+ /* Prepare BAP */
+ result = hfa384x_prepare_bap ( hw, id, offset );
+ if ( result == 0 ) {
+ /* Write even(len) buf contents to data reg */
+ for ( i = 0; i < (len & 0xfffe); i+=2 ) {
+ hfa384x_setreg_noswap(hw, *(UINT16*)(&(d[i])), HFA384x_DATA0);
+ }
+ /* If len odd, handle last byte */
+ if ( len % 2 ){
+ savereg = hfa384x_getreg_noswap(hw, HFA384x_DATA0);
+ result = hfa384x_prepare_bap ( hw, id, offset + (len & 0xfffe) );
+ if ( result == 0 ) {
+ ((UINT8*)(&savereg))[0] = d[len-1];
+ hfa384x_setreg_noswap(hw, savereg, HFA384x_DATA0);
+ }
+ }
+ }
+ if (result) {
+ printf ( "copy_to_bap(%#hx, %#hx, %d) failed, result=%#hx\n", id, offset, len, result);
+ }
+ return result;
+}
+
+/*
+ * Request a given record to be copied to/from the record buffer.
+ *
+ * Arguments:
+ * hw device structure
+ * write [0|1] copy the record buffer to the given
+ * configuration record. (host order)
+ * rid RID of the record to read/write. (host order)
+ *
+ * Returns:
+ * 0 success
+ */
+static inline int hfa384x_cmd_access(hfa384x_t *hw, UINT16 write, UINT16 rid)
+{
+ return hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ACCESS) | HFA384x_CMD_WRITE_SET(write), rid, 0, 0);
+}
+
+/*
+ * Performs the sequence necessary to read a config/info item.
+ *
+ * Arguments:
+ * hw device structure
+ * rid config/info record id (host order)
+ * buf host side record buffer. Upon return it will
+ * contain the body portion of the record (minus the
+ * RID and len).
+ * len buffer length (in bytes, should match record length)
+ *
+ * Returns:
+ * 0 success
+ */
+static int hfa384x_drvr_getconfig(hfa384x_t *hw, UINT16 rid, void *buf, UINT16 len)
+{
+ int result = 0;
+ hfa384x_rec_t rec;
+
+ /* Request read of RID */
+ result = hfa384x_cmd_access( hw, 0, rid);
+ if ( result ) {
+ printf("Call to hfa384x_cmd_access failed\n");
+ return -1;
+ }
+ /* Copy out record length */
+ result = hfa384x_copy_from_bap( hw, rid, 0, &rec, sizeof(rec));
+ if ( result ) {
+ return -1;
+ }
+ /* Validate the record length */
+ if ( ((hfa384x2host_16(rec.reclen)-1)*2) != len ) { /* note body len calculation in bytes */
+ printf ( "RID len mismatch, rid=%#hx hlen=%d fwlen=%d\n", rid, len, (hfa384x2host_16(rec.reclen)-1)*2);
+ return -1;
+ }
+ /* Copy out record data */
+ result = hfa384x_copy_from_bap( hw, rid, sizeof(rec), buf, len);
+ return result;
+}
+
+/*
+ * Performs the sequence necessary to read a 16/32 bit config/info item
+ * and convert it to host order.
+ *
+ * Arguments:
+ * hw device structure
+ * rid config/info record id (in host order)
+ * val ptr to 16/32 bit buffer to receive value (in host order)
+ *
+ * Returns:
+ * 0 success
+ */
+#if 0 /* Not actually used anywhere */
+static int hfa384x_drvr_getconfig16(hfa384x_t *hw, UINT16 rid, void *val)
+{
+ int result = 0;
+ result = hfa384x_drvr_getconfig(hw, rid, val, sizeof(UINT16));
+ if ( result == 0 ) {
+ *((UINT16*)val) = hfa384x2host_16(*((UINT16*)val));
+ }
+ return result;
+}
+#endif
+#if 0 /* Not actually used anywhere */
+static int hfa384x_drvr_getconfig32(hfa384x_t *hw, UINT16 rid, void *val)
+{
+ int result = 0;
+ result = hfa384x_drvr_getconfig(hw, rid, val, sizeof(UINT32));
+ if ( result == 0 ) {
+ *((UINT32*)val) = hfa384x2host_32(*((UINT32*)val));
+ }
+ return result;
+}
+#endif
+
+/*
+ * Performs the sequence necessary to write a config/info item.
+ *
+ * Arguments:
+ * hw device structure
+ * rid config/info record id (in host order)
+ * buf host side record buffer
+ * len buffer length (in bytes)
+ *
+ * Returns:
+ * 0 success
+ */
+static int hfa384x_drvr_setconfig(hfa384x_t *hw, UINT16 rid, void *buf, UINT16 len)
+{
+ int result = 0;
+ hfa384x_rec_t rec;
+
+ rec.rid = host2hfa384x_16(rid);
+ rec.reclen = host2hfa384x_16((len/2) + 1); /* note conversion to words, +1 for rid field */
+ /* write the record header */
+ result = hfa384x_copy_to_bap( hw, rid, 0, &rec, sizeof(rec));
+ if ( result ) {
+ printf("Failure writing record header\n");
+ return -1;
+ }
+ /* write the record data (if there is any) */
+ if ( len > 0 ) {
+ result = hfa384x_copy_to_bap( hw, rid, sizeof(rec), buf, len);
+ if ( result ) {
+ printf("Failure writing record data\n");
+ return -1;
+ }
+ }
+ /* Trigger setting of record */
+ result = hfa384x_cmd_access( hw, 1, rid);
+ return result;
+}
+
+/*
+ * Performs the sequence necessary to write a 16/32 bit config/info item.
+ *
+ * Arguments:
+ * hw device structure
+ * rid config/info record id (in host order)
+ * val 16/32 bit value to store (in host order)
+ *
+ * Returns:
+ * 0 success
+ */
+static int hfa384x_drvr_setconfig16(hfa384x_t *hw, UINT16 rid, UINT16 *val)
+{
+ UINT16 value;
+ value = host2hfa384x_16(*val);
+ return hfa384x_drvr_setconfig(hw, rid, &value, sizeof(UINT16));
+}
+#if 0 /* Not actually used anywhere */
+static int hfa384x_drvr_setconfig32(hfa384x_t *hw, UINT16 rid, UINT32 *val)
+{
+ UINT32 value;
+ value = host2hfa384x_32(*val);
+ return hfa384x_drvr_setconfig(hw, rid, &value, sizeof(UINT32));
+}
+#endif
+
+/*
+ * Wait for an event, with specified checking interval and timeout.
+ * Automatically acknolwedges events.
+ *
+ * Arguments:
+ * hw device structure
+ * event_mask EVSTAT register mask of events to wait for
+ * event_ack EVACK register set of events to be acknowledged if they happen (can be
+ * used to acknowledge "ignorable" events in addition to the "main" event)
+ * wait Time (in us) to wait between each poll of the register
+ * timeout Maximum number of polls before timing out
+ * descr Descriptive text string of what is being waited for
+ * (will be printed out if a timeout happens)
+ *
+ * Returns:
+ * value of EVSTAT register, or 0 on failure
+ */
+static int hfa384x_wait_for_event(hfa384x_t *hw, UINT16 event_mask, UINT16 event_ack, int wait, int timeout, const char *descr)
+{
+ UINT16 reg;
+ int count = 0;
+
+ do {
+ reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
+ if ( count > 0 ) udelay(wait);
+ count++;
+ } while ( !(reg & event_mask) && count < timeout);
+ if ( count >= timeout ) {
+ printf("hfa384x: Timed out waiting for %s\n", descr);
+ return 0; /* Return failure */
+ }
+ /* Acknowledge all events that we were waiting on */
+ hfa384x_setreg(hw, reg & ( event_mask | event_ack ), HFA384x_EVACK);
+ return reg;
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int prism2_poll(struct nic *nic, int retrieve)
+{
+ UINT16 reg;
+ UINT16 rxfid;
+ UINT16 result;
+ hfa384x_rx_frame_t rxdesc;
+ hfa384x_t *hw = &hw_global;
+
+ /* Check for received packet */
+ reg = hfa384x_getreg(hw, HFA384x_EVSTAT);
+ if ( ! HFA384x_EVSTAT_ISRX(reg) ) {
+ /* No packet received - return 0 */
+ return 0;
+ }
+
+ if ( ! retrieve ) return 1;
+
+ /* Acknowledge RX event */
+ hfa384x_setreg(hw, HFA384x_EVACK_RX_SET(1), HFA384x_EVACK);
+ /* Get RX FID */
+ rxfid = hfa384x_getreg(hw, HFA384x_RXFID);
+ /* Get the descriptor (including headers) */
+ result = hfa384x_copy_from_bap(hw, rxfid, 0, &rxdesc, sizeof(rxdesc));
+ if ( result ) {
+ return 0; /* fail */
+ }
+ /* Byte order convert once up front. */
+ rxdesc.status = hfa384x2host_16(rxdesc.status);
+ rxdesc.time = hfa384x2host_32(rxdesc.time);
+ rxdesc.data_len = hfa384x2host_16(rxdesc.data_len);
+
+ /* Fill in nic->packetlen */
+ nic->packetlen = rxdesc.data_len;
+ if ( nic->packetlen > 0 ) {
+ /* Fill in nic->packet */
+ /*
+ * NOTE: Packets as received have an 8-byte header (LLC+SNAP(?)) terminating with the packet type.
+ * Etherboot expects a 14-byte header terminating with the packet type (it ignores the rest of the
+ * header), so we use a quick hack to achieve this.
+ */
+ result = hfa384x_copy_from_bap(hw, rxfid, HFA384x_RX_DATA_OFF,
+ nic->packet + ETH_HLEN - sizeof(wlan_80211hdr_t), nic->packetlen);
+ if ( result ) {
+ return 0; /* fail */
+ }
+ }
+ return 1; /* Packet successfully received */
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void prism2_transmit(
+ struct nic *nic,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+ hfa384x_t *hw = &hw_global;
+ hfa384x_tx_frame_t txdesc;
+ wlan_80211hdr_t p80211hdr = { wlan_llc_snap, {{0,0,0},0} };
+ UINT16 fid;
+ UINT16 status;
+ int result;
+
+ // Request FID allocation
+ result = hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ALLOC), HFA384x_DRVR_TXBUF_MAX, 0, 0);
+ if (result != 0) {
+ printf("hfa384x: Tx FID allocate command failed: Aborting transmit..\n");
+ return;
+ }
+ if ( !hfa384x_wait_for_event(hw, HFA384x_EVSTAT_ALLOC, HFA384x_EVACK_INFO, 10, 50, "Tx FID to be allocated\n" ) ) return;
+ fid = hfa384x_getreg(hw, HFA384x_ALLOCFID);
+
+ /* Build Tx frame structure */
+ memset(&txdesc, 0, sizeof(txdesc));
+ txdesc.tx_control = host2hfa384x_16( HFA384x_TX_MACPORT_SET(0) | HFA384x_TX_STRUCTYPE_SET(1) |
+ HFA384x_TX_TXEX_SET(1) | HFA384x_TX_TXOK_SET(1) );
+ txdesc.frame_control = host2ieee16( WLAN_SET_FC_FTYPE(WLAN_FTYPE_DATA) |
+ WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_DATAONLY) |
+ WLAN_SET_FC_TODS(1) );
+ memcpy(txdesc.address1, hw->bssid, WLAN_ADDR_LEN);
+ memcpy(txdesc.address2, nic->node_addr, WLAN_ADDR_LEN);
+ memcpy(txdesc.address3, d, WLAN_ADDR_LEN);
+ txdesc.data_len = host2hfa384x_16( sizeof(txdesc) + sizeof(p80211hdr) + s );
+ /* Set up SNAP header */
+ /* Let OUI default to RFC1042 (0x000000) */
+ p80211hdr.snap.type = htons(t);
+
+ /* Copy txdesc, p80211hdr and payload parts to FID */
+ result = hfa384x_copy_to_bap(hw, fid, 0, &txdesc, sizeof(txdesc));
+ if ( result ) return; /* fail */
+ result = hfa384x_copy_to_bap( hw, fid, sizeof(txdesc), &p80211hdr, sizeof(p80211hdr) );
+ if ( result ) return; /* fail */
+ result = hfa384x_copy_to_bap( hw, fid, sizeof(txdesc) + sizeof(p80211hdr), (UINT8*)p, s );
+ if ( result ) return; /* fail */
+
+ /* Issue Tx command */
+ result = hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_TX), fid, 0, 0);
+ if ( result != 0 ) {
+ printf("hfa384x: Transmit failed with result %#hx.\n", result);
+ return;
+ }
+
+ /* Wait for transmit completion (or exception) */
+ result = hfa384x_wait_for_event(hw, HFA384x_EVSTAT_TXEXC | HFA384x_EVSTAT_TX, HFA384x_EVACK_INFO,
+ 200, 500, "Tx to complete\n" );
+ if ( !result ) return; /* timeout failure */
+ if ( HFA384x_EVSTAT_ISTXEXC(result) ) {
+ fid = hfa384x_getreg(hw, HFA384x_TXCOMPLFID);
+ printf ( "Tx exception occurred with fid %#hx\n", fid );
+ result = hfa384x_copy_from_bap(hw, fid, 0, &status, sizeof(status));
+ if ( result ) return; /* fail */
+ printf("hfa384x: Tx error occurred (status %#hx):\n", status);
+ if ( HFA384x_TXSTATUS_ISACKERR(status) ) { printf(" ...acknowledgement error\n"); }
+ if ( HFA384x_TXSTATUS_ISFORMERR(status) ) { printf(" ...format error\n"); }
+ if ( HFA384x_TXSTATUS_ISDISCON(status) ) { printf(" ...disconnected error\n"); }
+ if ( HFA384x_TXSTATUS_ISAGEDERR(status) ) { printf(" ...AGED error\n"); }
+ if ( HFA384x_TXSTATUS_ISRETRYERR(status) ) { printf(" ...retry error\n"); }
+ return; /* fail */
+ }
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void prism2_disable(struct dev *dev __unused)
+{
+ /* put the card in its initial state */
+}
+
+/**************************************************************************
+IRQ - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void prism2_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+You should omit the last argument struct pci_device * for a non-PCI NIC
+***************************************************************************/
+#if (WLAN_HOSTIF == WLAN_PLX)
+static int prism2_plx_probe(struct dev *dev, struct pci_device *p)
+#elif (WLAN_HOSTIF == WLAN_PCI)
+static int prism2_pci_probe(struct dev *dev, struct pci_device *p)
+#endif
+{
+ struct nic *nic = (struct nic *)dev;
+ hfa384x_t *hw = &hw_global;
+ int result;
+ UINT16 tmp16 = 0;
+ UINT16 infofid;
+ hfa384x_InfFrame_t inf;
+ char ssid[HFA384x_RID_CNFDESIREDSSID_LEN];
+ int info_count = 0;
+
+ /* Find and intialise PLX Prism2 card */
+#if (WLAN_HOSTIF == WLAN_PLX)
+ if ( ! prism2_find_plx ( hw, p ) ) return 0;
+ nic->ioaddr = hw->iobase;
+#elif (WLAN_HOSTIF == WLAN_PCI)
+ if ( ! prism2_find_pci ( hw, p ) ) return 0;
+ nic->ioaddr = hw->membase;
+#endif
+
+ nic->irqno = 0;
+
+ /* Initialize card */
+ result = hfa384x_docmd_wait(hw, HFA384x_CMDCODE_INIT, 0,0,0); /* Send initialize command */
+ if ( result ) printf ( "Initialize command returned %#hx\n", result );
+ hfa384x_setreg(hw, 0, HFA384x_INTEN); /* Disable interrupts */
+ hfa384x_setreg(hw, 0xffff, HFA384x_EVACK); /* Acknowledge any spurious events */
+
+ /* Retrieve MAC address (and fill out nic->node_addr) */
+ hfa384x_drvr_getconfig ( hw, HFA384x_RID_CNFOWNMACADDR, nic->node_addr, HFA384x_RID_CNFOWNMACADDR_LEN );
+ printf ( "MAC address %!\n", nic->node_addr );
+
+ /* Prepare card for autojoin */
+ /* This procedure is reverse-engineered from a register-level trace of the Linux driver's join process */
+ tmp16 = WLAN_DATA_MAXLEN; /* Set maximum data length */
+ result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFMAXDATALEN, &tmp16);
+ if ( result ) printf ( "Set Max Data Length command returned %#hx\n", result );
+ tmp16 = 0x000f; /* Set transmit rate(?) */
+ result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_TXRATECNTL, &tmp16);
+ if ( result ) printf ( "Set Transmit Rate command returned %#hx\n", result );
+ tmp16 = HFA384x_CNFAUTHENTICATION_OPENSYSTEM; /* Set authentication type to OpenSystem */
+ result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFAUTHENTICATION, &tmp16);
+ if ( result ) printf ( "Set Authentication Type command returned %#hx\n", result );
+ /* Set SSID */
+ memset(ssid, 0, HFA384x_RID_CNFDESIREDSSID_LEN);
+ for ( tmp16=0; tmp16<sizeof(hardcoded_ssid); tmp16++ ) { ssid[2+tmp16] = hardcoded_ssid[tmp16]; }
+ ssid[0] = sizeof(hardcoded_ssid) - 1; /* Ignore terminating zero */
+ result = hfa384x_drvr_setconfig(hw, HFA384x_RID_CNFDESIREDSSID, ssid, HFA384x_RID_CNFDESIREDSSID_LEN); /* Set the SSID */
+ if ( result ) printf ( "Set SSID command returned %#hx\n", result );
+ tmp16 = 1; /* Set port type to ESS port */
+ result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFPORTTYPE, &tmp16);
+ if ( result ) printf ( "Set port type command returned %#hx\n", result );
+ /* Enable card */
+ result = hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ENABLE) | HFA384x_CMD_MACPORT_SET(0), 0,0,0);
+ if ( result ) printf ( "Enable command returned %#hx\n", result );
+
+ do {
+ /* Increment info_count, abort if too many attempts.
+ * See comment next to definition of MAX_JOIN_INFO_COUNT for explanation.
+ */
+ info_count++;
+ if ( info_count > MAX_JOIN_INFO_COUNT ) {
+ printf ( "Too many failed attempts - aborting\n" );
+ return 0;
+ }
+
+ /* Wait for info frame to indicate link status */
+ if ( sizeof(hardcoded_ssid) == 1 ) {
+ /* Empty SSID => join to any SSID */
+ printf ( "Attempting to autojoin to any available access point (attempt %d)...", info_count );
+ } else {
+ printf ( "Attempting to autojoin to SSID %s (attempt %d)...", &ssid[2], info_count );
+ }
+
+ if ( !hfa384x_wait_for_event(hw, HFA384x_EVSTAT_INFO, 0, 1000, 2000, "Info event" ) ) return 0;
+ printf("done\n");
+ infofid = hfa384x_getreg(hw, HFA384x_INFOFID);
+ /* Retrieve the length */
+ result = hfa384x_copy_from_bap( hw, infofid, 0, &inf.framelen, sizeof(UINT16));
+ if ( result ) return 0; /* fail */
+ inf.framelen = hfa384x2host_16(inf.framelen);
+ /* Retrieve the rest */
+ result = hfa384x_copy_from_bap( hw, infofid, sizeof(UINT16),
+ &(inf.infotype), inf.framelen * sizeof(UINT16));
+ if ( result ) return 0; /* fail */
+ if ( inf.infotype != HFA384x_IT_LINKSTATUS ) {
+ /* Not a Link Status info frame: die */
+ printf ( "Unexpected info frame type %#hx (not LinkStatus type)\n", inf.infotype );
+ return 0;
+ }
+ inf.info.linkstatus.linkstatus = hfa384x2host_16(inf.info.linkstatus.linkstatus);
+ if ( inf.info.linkstatus.linkstatus != HFA384x_LINK_CONNECTED ) {
+ /* Link not connected - retry */
+ printf ( "Link not connected (status %#hx)\n", inf.info.linkstatus.linkstatus );
+ }
+ } while ( inf.info.linkstatus.linkstatus != HFA384x_LINK_CONNECTED );
+
+ /* Retrieve BSSID and print Connected message */
+ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_CURRENTBSSID, hw->bssid, WLAN_BSSID_LEN);
+ printf ( "Link connected (BSSID %! - MAC address %!)\n", hw->bssid, nic->node_addr );
+
+ /* point to NIC specific routines */
+ dev->disable = prism2_disable;
+ nic->poll = prism2_poll;
+ nic->transmit = prism2_transmit;
+ nic->irq = prism2_irq;
+ return 1;
+}
+
+#if (WLAN_HOSTIF == WLAN_PLX)
+/*
+ * Find PLX card. Prints out information strings from PCMCIA CIS as visual
+ * confirmation of presence of card.
+ *
+ * Arguments:
+ * hw device structure to be filled in
+ * p PCI device structure
+ *
+ * Returns:
+ * 1 Success
+ */
+static int prism2_find_plx ( hfa384x_t *hw, struct pci_device *p )
+{
+ int found = 0;
+ uint32_t plx_lcr = 0; /* PLX9052 Local Configuration Register Base (I/O) */
+ uint32_t attr_mem = 0; /* Prism2 Attribute Memory Base */
+ uint32_t iobase = 0; /* Prism2 I/O Base */
+ unsigned char *cis_tpl = NULL;
+ unsigned char *cis_string;
+
+ /* Obtain all memory and IO base addresses */
+ pcibios_read_config_dword( p->bus, p->devfn, PLX_LOCAL_CONFIG_REGISTER_BASE, &plx_lcr);
+ plx_lcr &= PCI_BASE_ADDRESS_IO_MASK;
+ pcibios_read_config_dword( p->bus, p->devfn, PRISM2_PLX_ATTR_MEM_BASE, &attr_mem);
+ pcibios_read_config_dword( p->bus, p->devfn, PRISM2_PLX_IO_BASE, &iobase);
+ iobase &= PCI_BASE_ADDRESS_IO_MASK;
+
+ /* Fill out hw structure */
+ hw->membase = attr_mem;
+ hw->iobase = iobase;
+ printf ( "PLX9052 has local config registers at %#hx\n", plx_lcr );
+ printf ( "Prism2 has attribute memory at %#x and I/O base at %#hx\n", attr_mem, iobase );
+
+ /* Search for CIS strings */
+ printf ( "Searching for PCMCIA card...\n" );
+ cis_tpl = bus_to_virt(attr_mem);
+ while ( *cis_tpl != CISTPL_END ) {
+ if ( *cis_tpl == CISTPL_VERS_1 ) {
+ /* CISTPL_VERS_1 contains some nice text strings */
+ printf ( "...found " );
+ found = 1;
+ cis_string = cis_tpl + CISTPL_VERS_1_STR_OFF;
+ while ( ! ( ( *cis_string == 0 ) && ( *(cis_string+CIS_STEP) == 0 ) ) ) {
+ printf ( "%c", *cis_string == 0 ? ' ' : *cis_string );
+ cis_string += CIS_STEP;
+ }
+ printf ( "\n" );
+ }
+ /* printf ( "CIS tuple type %#hhx, length %#hhx\n", *cis_tpl, *(cis_tpl+CISTPL_LEN_OFF) ); */
+ cis_tpl += CISTPL_HEADER_LEN + CIS_STEP * ( *(cis_tpl+CISTPL_LEN_OFF) );
+ }
+ if ( found == 0 ) {
+ printf ( "...nothing found\n" );
+ }
+ ((unsigned char *)bus_to_virt(attr_mem))[COR_OFFSET] = COR_VALUE; /* Write COR to enable PC card */
+ return found;
+}
+#endif /* WLAN_PLX */
+
+#if (WLAN_HOSTIF == WLAN_PCI)
+/*
+ * Find PCI card.
+ *
+ * Arguments:
+ * hw device structure to be filled in
+ * p PCI device structure
+ *
+ * Returns:
+ * 1 Success
+ */
+static int prism2_find_pci ( hfa384x_t *hw, struct pci_device *p )
+{
+ uint32_t membase = 0; /* Prism2.5 Memory Base */
+ pcibios_read_config_dword( p->bus, p->devfn, PRISM2_PCI_MEM_BASE, &membase);
+ membase &= PCI_BASE_ADDRESS_MEM_MASK;
+ hw->membase = (uint32_t) phys_to_virt(membase);
+ printf ( "Prism2.5 has registers at %#x\n", hw->membase );
+ return 1;
+}
+#endif /* WLAN_PCI */
+
diff --git a/src/drivers/net/prism2_pci.c b/src/drivers/net/prism2_pci.c
new file mode 100644
index 00000000..be2b06cc
--- /dev/null
+++ b/src/drivers/net/prism2_pci.c
@@ -0,0 +1,32 @@
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+Prism2 NIC driver for Etherboot
+Wrapper for prism2_pci
+
+Written by Michael Brown of Fen Systems Ltd
+$Id$
+***************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+#define WLAN_HOSTIF WLAN_PCI
+#include "prism2.c"
+
+static struct pci_id prism2_pci_nics[] = {
+PCI_ROM(0x1260, 0x3873, "prism2_pci", "Harris Semiconductor Prism2.5 clone"), /* Generic Prism2.5 PCI device */
+};
+
+static struct pci_driver prism2_pci_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "Prism2_PCI",
+ .probe = prism2_pci_probe,
+ .ids = prism2_pci_nics,
+ .id_count = sizeof(prism2_pci_nics)/sizeof(prism2_pci_nics[0]),
+ .class = 0,
+};
+
diff --git a/src/drivers/net/prism2_plx.c b/src/drivers/net/prism2_plx.c
new file mode 100644
index 00000000..65954f75
--- /dev/null
+++ b/src/drivers/net/prism2_plx.c
@@ -0,0 +1,42 @@
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+Prism2 NIC driver for Etherboot
+Wrapper for prism2_plx
+
+Written by Michael Brown of Fen Systems Ltd
+$Id$
+***************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+#define WLAN_HOSTIF WLAN_PLX
+#include "prism2.c"
+
+static struct pci_id prism2_plx_nics[] = {
+PCI_ROM(0x1385, 0x4100, "ma301", "Netgear MA301"),
+PCI_ROM(0x10b7, 0x7770, "3c-airconnect", "3Com AirConnect"),
+PCI_ROM(0x111a, 0x1023, "ss1023", "Siemens SpeedStream SS1023"),
+PCI_ROM(0x15e8, 0x0130, "correga", "Correga"),
+PCI_ROM(0x1638, 0x1100, "smc2602w", "SMC EZConnect SMC2602W"), /* or Eumitcom PCI WL11000, Addtron AWA-100 */
+PCI_ROM(0x16ab, 0x1100, "gl24110p", "Global Sun Tech GL24110P"),
+PCI_ROM(0x16ab, 0x1101, "16ab-1101", "Unknown"),
+PCI_ROM(0x16ab, 0x1102, "wdt11", "Linksys WDT11"),
+PCI_ROM(0x16ec, 0x3685, "usr2415", "USR 2415"),
+PCI_ROM(0xec80, 0xec00, "f5d6000", "Belkin F5D6000"),
+PCI_ROM(0x126c, 0x8030, "emobility", "Nortel emobility"),
+};
+
+static struct pci_driver prism2_plx_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "Prism2_PLX",
+ .probe = prism2_plx_probe,
+ .ids = prism2_plx_nics,
+ .id_count = sizeof(prism2_plx_nics)/sizeof(prism2_plx_nics[0]),
+ .class = 0,
+};
+
diff --git a/src/drivers/net/r8169.c b/src/drivers/net/r8169.c
new file mode 100644
index 00000000..83373dee
--- /dev/null
+++ b/src/drivers/net/r8169.c
@@ -0,0 +1,854 @@
+/**************************************************************************
+* r8169.c: Etherboot device driver for the RealTek RTL-8169 Gigabit
+* Written 2003 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code based on:
+* r8169.c: A RealTek RTL-8169 Gigabit Ethernet driver
+* for Linux kernel 2.4.x.
+*
+* Written 2002 ShuChen <shuchen@realtek.com.tw>
+* See Linux Driver for full information
+*
+* Linux Driver Version 1.27a, 10.02.2002
+*
+* Thanks to:
+* Jean Chen of RealTek Semiconductor Corp. for
+* providing the evaluation NIC used to develop
+* this driver. RealTek's support for Etherboot
+* is appreciated.
+*
+* REVISION HISTORY:
+* ================
+*
+* v1.0 11-26-2003 timlegge Initial port of Linux driver
+* v1.5 01-17-2004 timlegge Initial driver output cleanup
+* v1.6 03-27-2004 timlegge Additional Cleanup
+*
+* Indent Options: indent -kr -i8
+***************************************************************************/
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include "pci.h"
+#include "timer.h"
+
+#define drv_version "v1.6"
+#define drv_date "03-27-2004"
+
+typedef unsigned char u8;
+typedef signed char s8;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned int u32;
+typedef signed int s32;
+
+#define HZ 1000
+
+static u32 ioaddr;
+
+#ifdef EDEBUG
+#define dprintf(x) printf x
+#else
+#define dprintf(x)
+#endif
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+/* media options
+ _10_Half = 0x01,
+ _10_Full = 0x02,
+ _100_Half = 0x04,
+ _100_Full = 0x08,
+ _1000_Full = 0x10,
+*/
+static int media = -1;
+
+#if 0
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 20;
+#endif
+
+#if 0
+/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
+ The RTL chips use a 64 element hash table based on the Ethernet CRC. */
+static int multicast_filter_limit = 32;
+#endif
+
+/* MAC address length*/
+#define MAC_ADDR_LEN 6
+
+/* max supported gigabit ethernet frame size -- must be at least (dev->mtu+14+4).*/
+#define MAX_ETH_FRAME_SIZE 1536
+
+#define TX_FIFO_THRESH 256 /* In bytes */
+
+#define RX_FIFO_THRESH 7 /* 7 means NO threshold, Rx buffer level before first PCI xfer. */
+#define RX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
+#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */
+#define EarlyTxThld 0x3F /* 0x3F means NO early transmit */
+#define RxPacketMaxSize 0x0800 /* Maximum size supported is 16K-1 */
+#define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */
+
+#define NUM_TX_DESC 1 /* Number of Tx descriptor registers */
+#define NUM_RX_DESC 4 /* Number of Rx descriptor registers */
+#define RX_BUF_SIZE 1536 /* Rx Buffer size */
+
+#define RTL_MIN_IO_SIZE 0x80
+#define TX_TIMEOUT (6*HZ)
+
+/* write/read MMIO register */
+#define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg))
+#define RTL_W16(reg, val16) writew ((val16), ioaddr + (reg))
+#define RTL_W32(reg, val32) writel ((val32), ioaddr + (reg))
+#define RTL_R8(reg) readb (ioaddr + (reg))
+#define RTL_R16(reg) readw (ioaddr + (reg))
+#define RTL_R32(reg) ((unsigned long) readl (ioaddr + (reg)))
+
+enum RTL8169_registers {
+ MAC0 = 0, /* Ethernet hardware address. */
+ MAR0 = 8, /* Multicast filter. */
+ TxDescStartAddr = 0x20,
+ TxHDescStartAddr = 0x28,
+ FLASH = 0x30,
+ ERSR = 0x36,
+ ChipCmd = 0x37,
+ TxPoll = 0x38,
+ IntrMask = 0x3C,
+ IntrStatus = 0x3E,
+ TxConfig = 0x40,
+ RxConfig = 0x44,
+ RxMissed = 0x4C,
+ Cfg9346 = 0x50,
+ Config0 = 0x51,
+ Config1 = 0x52,
+ Config2 = 0x53,
+ Config3 = 0x54,
+ Config4 = 0x55,
+ Config5 = 0x56,
+ MultiIntr = 0x5C,
+ PHYAR = 0x60,
+ TBICSR = 0x64,
+ TBI_ANAR = 0x68,
+ TBI_LPAR = 0x6A,
+ PHYstatus = 0x6C,
+ RxMaxSize = 0xDA,
+ CPlusCmd = 0xE0,
+ RxDescStartAddr = 0xE4,
+ EarlyTxThres = 0xEC,
+ FuncEvent = 0xF0,
+ FuncEventMask = 0xF4,
+ FuncPresetState = 0xF8,
+ FuncForceEvent = 0xFC,
+};
+
+enum RTL8169_register_content {
+ /*InterruptStatusBits */
+ SYSErr = 0x8000,
+ PCSTimeout = 0x4000,
+ SWInt = 0x0100,
+ TxDescUnavail = 0x80,
+ RxFIFOOver = 0x40,
+ RxUnderrun = 0x20,
+ RxOverflow = 0x10,
+ TxErr = 0x08,
+ TxOK = 0x04,
+ RxErr = 0x02,
+ RxOK = 0x01,
+
+ /*RxStatusDesc */
+ RxRES = 0x00200000,
+ RxCRC = 0x00080000,
+ RxRUNT = 0x00100000,
+ RxRWT = 0x00400000,
+
+ /*ChipCmdBits */
+ CmdReset = 0x10,
+ CmdRxEnb = 0x08,
+ CmdTxEnb = 0x04,
+ RxBufEmpty = 0x01,
+
+ /*Cfg9346Bits */
+ Cfg9346_Lock = 0x00,
+ Cfg9346_Unlock = 0xC0,
+
+ /*rx_mode_bits */
+ AcceptErr = 0x20,
+ AcceptRunt = 0x10,
+ AcceptBroadcast = 0x08,
+ AcceptMulticast = 0x04,
+ AcceptMyPhys = 0x02,
+ AcceptAllPhys = 0x01,
+
+ /*RxConfigBits */
+ RxCfgFIFOShift = 13,
+ RxCfgDMAShift = 8,
+
+ /*TxConfigBits */
+ TxInterFrameGapShift = 24,
+ TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */
+
+ /*rtl8169_PHYstatus */
+ TBI_Enable = 0x80,
+ TxFlowCtrl = 0x40,
+ RxFlowCtrl = 0x20,
+ _1000bpsF = 0x10,
+ _100bps = 0x08,
+ _10bps = 0x04,
+ LinkStatus = 0x02,
+ FullDup = 0x01,
+
+ /*GIGABIT_PHY_registers */
+ PHY_CTRL_REG = 0,
+ PHY_STAT_REG = 1,
+ PHY_AUTO_NEGO_REG = 4,
+ PHY_1000_CTRL_REG = 9,
+
+ /*GIGABIT_PHY_REG_BIT */
+ PHY_Restart_Auto_Nego = 0x0200,
+ PHY_Enable_Auto_Nego = 0x1000,
+
+ /* PHY_STAT_REG = 1; */
+ PHY_Auto_Neco_Comp = 0x0020,
+
+ /* PHY_AUTO_NEGO_REG = 4; */
+ PHY_Cap_10_Half = 0x0020,
+ PHY_Cap_10_Full = 0x0040,
+ PHY_Cap_100_Half = 0x0080,
+ PHY_Cap_100_Full = 0x0100,
+
+ /* PHY_1000_CTRL_REG = 9; */
+ PHY_Cap_1000_Full = 0x0200,
+
+ PHY_Cap_Null = 0x0,
+
+ /*_MediaType*/
+ _10_Half = 0x01,
+ _10_Full = 0x02,
+ _100_Half = 0x04,
+ _100_Full = 0x08,
+ _1000_Full = 0x10,
+
+ /*_TBICSRBit*/
+ TBILinkOK = 0x02000000,
+};
+
+static struct {
+ const char *name;
+ u8 version; /* depend on RTL8169 docs */
+ u32 RxConfigMask; /* should clear the bits supported by this chip */
+} rtl_chip_info[] = {
+ {
+"RTL-8169", 0x00, 0xff7e1880,},};
+
+enum _DescStatusBit {
+ OWNbit = 0x80000000,
+ EORbit = 0x40000000,
+ FSbit = 0x20000000,
+ LSbit = 0x10000000,
+};
+
+struct TxDesc {
+ u32 status;
+ u32 vlan_tag;
+ u32 buf_addr;
+ u32 buf_Haddr;
+};
+
+struct RxDesc {
+ u32 status;
+ u32 vlan_tag;
+ u32 buf_addr;
+ u32 buf_Haddr;
+};
+
+/* The descriptors for this card are required to be aligned on
+256 byte boundaries. As the align attribute does not do more than
+16 bytes of alignment it requires some extra steps. Add 256 to the
+size of the array and the init_ring adjusts the alignment */
+
+/* Define the TX Descriptor */
+static u8 tx_ring[NUM_TX_DESC * sizeof(struct TxDesc) + 256];
+
+/* Create a static buffer of size RX_BUF_SZ for each
+TX Descriptor. All descriptors point to a
+part of this buffer */
+static unsigned char txb[NUM_TX_DESC * RX_BUF_SIZE];
+
+/* Define the RX Descriptor */
+static u8 rx_ring[NUM_RX_DESC * sizeof(struct TxDesc) + 256];
+
+/* Create a static buffer of size RX_BUF_SZ for each
+RX Descriptor All descriptors point to a
+part of this buffer */
+static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE];
+
+struct rtl8169_private {
+ void *mmio_addr; /* memory map physical address */
+ int chipset;
+ unsigned long cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */
+ unsigned long cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */
+ unsigned char *TxDescArrays; /* Index of Tx Descriptor buffer */
+ unsigned char *RxDescArrays; /* Index of Rx Descriptor buffer */
+ struct TxDesc *TxDescArray; /* Index of 256-alignment Tx Descriptor buffer */
+ struct RxDesc *RxDescArray; /* Index of 256-alignment Rx Descriptor buffer */
+ unsigned char *RxBufferRing[NUM_RX_DESC]; /* Index of Rx Buffer array */
+ unsigned char *Tx_skbuff[NUM_TX_DESC];
+} tpx;
+
+static struct rtl8169_private *tpc;
+
+static const u16 rtl8169_intr_mask =
+ SYSErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | TxErr |
+ TxOK | RxErr | RxOK;
+static const unsigned int rtl8169_rx_config =
+ (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift);
+
+void mdio_write(int RegAddr, int value)
+{
+ int i;
+
+ RTL_W32(PHYAR, 0x80000000 | (RegAddr & 0xFF) << 16 | value);
+ udelay(1000);
+
+ for (i = 2000; i > 0; i--) {
+ /* Check if the RTL8169 has completed writing to the specified MII register */
+ if (!(RTL_R32(PHYAR) & 0x80000000)) {
+ break;
+ } else {
+ udelay(100);
+ }
+ }
+}
+
+int mdio_read(int RegAddr)
+{
+ int i, value = -1;
+
+ RTL_W32(PHYAR, 0x0 | (RegAddr & 0xFF) << 16);
+ udelay(1000);
+
+ for (i = 2000; i > 0; i--) {
+ /* Check if the RTL8169 has completed retrieving data from the specified MII register */
+ if (RTL_R32(PHYAR) & 0x80000000) {
+ value = (int) (RTL_R32(PHYAR) & 0xFFFF);
+ break;
+ } else {
+ udelay(100);
+ }
+ }
+ return value;
+}
+
+static int rtl8169_init_board(struct pci_device *pdev)
+{
+ int i;
+ unsigned long rtreg_base, rtreg_len;
+ u32 tmp;
+
+ rtreg_base = pci_bar_start(pdev, PCI_BASE_ADDRESS_1);
+ rtreg_len = pci_bar_size(pdev, PCI_BASE_ADDRESS_1);
+
+ /* check for weird/broken PCI region reporting */
+ if (rtreg_len < RTL_MIN_IO_SIZE) {
+ printf("Invalid PCI region size(s), aborting\n");
+ }
+
+ adjust_pci_device(pdev);
+/* pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); */
+
+ /* ioremap MMIO region */
+ ioaddr = (unsigned long) ioremap(rtreg_base, rtreg_len);
+ if (ioaddr == 0)
+ return 0;
+
+ tpc->mmio_addr = &ioaddr;
+ /* Soft reset the chip. */
+ RTL_W8(ChipCmd, CmdReset);
+
+ /* Check that the chip has finished the reset. */
+ for (i = 1000; i > 0; i--)
+ if ((RTL_R8(ChipCmd) & CmdReset) == 0)
+ break;
+ else
+ udelay(10);
+
+ /* identify chip attached to board */
+ tmp = RTL_R32(TxConfig);
+ tmp = ((tmp & 0x7c000000) + ((tmp & 0x00800000) << 2)) >> 24;
+
+ for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--)
+ if (tmp == rtl_chip_info[i].version) {
+ tpc->chipset = i;
+ goto match;
+ }
+ /* if unknown chip, assume array element #0, original RTL-8169 in this case */
+ dprintf(("PCI device: unknown chip version, assuming RTL-8169\n"));
+ dprintf(("PCI device: TxConfig = 0x%hX\n",
+ (unsigned long) RTL_R32(TxConfig)));
+ tpc->chipset = 0;
+ return 1;
+ match:
+ return 0;
+
+}
+
+/**************************************************************************
+IRQ - Wait for a frame
+***************************************************************************/
+void r8169_irq ( struct nic *nic __unused, irq_action_t action ) {
+ int intr_status = 0;
+ int interested = RxUnderrun | RxOverflow | RxFIFOOver | RxErr | RxOK;
+
+ switch ( action ) {
+ case DISABLE:
+ case ENABLE:
+ intr_status = RTL_R16(IntrStatus);
+ /* h/w no longer present (hotplug?) or major error,
+ bail */
+ if (intr_status == 0xFFFF)
+ break;
+
+ intr_status = intr_status & ~interested;
+ if ( action == ENABLE )
+ intr_status = intr_status | interested;
+ RTL_W16(IntrMask, intr_status);
+ break;
+ case FORCE :
+ RTL_W8(TxPoll, (RTL_R8(TxPoll) | 0x01));
+ break;
+ }
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int r8169_poll(struct nic *nic, int retreive)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+ int cur_rx;
+ unsigned int intr_status = 0;
+ cur_rx = tpc->cur_rx;
+ if ((tpc->RxDescArray[cur_rx].status & OWNbit) == 0) {
+ /* There is a packet ready */
+ if(!retreive)
+ return 1;
+ intr_status = RTL_R16(IntrStatus);
+ /* h/w no longer present (hotplug?) or major error,
+ bail */
+ if (intr_status == 0xFFFF)
+ return 0;
+ RTL_W16(IntrStatus, intr_status &
+ ~(RxFIFOOver | RxOverflow | RxOK));
+
+ if (!(tpc->RxDescArray[cur_rx].status & RxRES)) {
+ nic->packetlen = (int) (tpc->RxDescArray[cur_rx].
+ status & 0x00001FFF) - 4;
+ memcpy(nic->packet, tpc->RxBufferRing[cur_rx],
+ nic->packetlen);
+ if (cur_rx == NUM_RX_DESC - 1)
+ tpc->RxDescArray[cur_rx].status =
+ (OWNbit | EORbit) + RX_BUF_SIZE;
+ else
+ tpc->RxDescArray[cur_rx].status =
+ OWNbit + RX_BUF_SIZE;
+ tpc->RxDescArray[cur_rx].buf_addr =
+ virt_to_bus(tpc->RxBufferRing[cur_rx]);
+ } else
+ printf("Error Rx");
+ /* FIXME: shouldn't I reset the status on an error */
+ cur_rx = (cur_rx + 1) % NUM_RX_DESC;
+ tpc->cur_rx = cur_rx;
+ RTL_W16(IntrStatus, intr_status &
+ (RxFIFOOver | RxOverflow | RxOK));
+
+ return 1;
+
+ }
+ tpc->cur_rx = cur_rx;
+ /* FIXME: There is no reason to do this as cur_rx did not change */
+
+ return (0); /* initially as this is called to flush the input */
+
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void r8169_transmit(struct nic *nic, const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p)
+{ /* Packet */
+ /* send the packet to destination */
+
+ u16 nstype;
+ u32 to;
+ u8 *ptxb;
+ int entry = tpc->cur_tx % NUM_TX_DESC;
+
+ /* point to the current txb incase multiple tx_rings are used */
+ ptxb = tpc->Tx_skbuff[entry * MAX_ETH_FRAME_SIZE];
+ memcpy(ptxb, d, ETH_ALEN);
+ memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ nstype = htons((u16) t);
+ memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2);
+ memcpy(ptxb + ETH_HLEN, p, s);
+ s += ETH_HLEN;
+ s &= 0x0FFF;
+ while (s < ETH_ZLEN)
+ ptxb[s++] = '\0';
+
+ tpc->TxDescArray[entry].buf_addr = virt_to_bus(ptxb);
+ if (entry != (NUM_TX_DESC - 1))
+ tpc->TxDescArray[entry].status =
+ (OWNbit | FSbit | LSbit) | ((s > ETH_ZLEN) ? s :
+ ETH_ZLEN);
+ else
+ tpc->TxDescArray[entry].status =
+ (OWNbit | EORbit | FSbit | LSbit) | ((s > ETH_ZLEN) ? s
+ : ETH_ZLEN);
+ RTL_W8(TxPoll, 0x40); /* set polling bit */
+
+ tpc->cur_tx++;
+ to = currticks() + TX_TIMEOUT;
+ while ((tpc->TxDescArray[entry].status & OWNbit) && (currticks() < to)); /* wait */
+
+ if (currticks() >= to) {
+ printf("TX Time Out");
+ }
+}
+
+static void rtl8169_set_rx_mode(struct nic *nic __unused)
+{
+ u32 mc_filter[2]; /* Multicast hash filter */
+ int rx_mode;
+ u32 tmp = 0;
+
+ /* IFF_ALLMULTI */
+ /* Too many to filter perfectly -- accept all multicasts. */
+ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+ mc_filter[1] = mc_filter[0] = 0xffffffff;
+
+ tmp =
+ rtl8169_rx_config | rx_mode | (RTL_R32(RxConfig) &
+ rtl_chip_info[tpc->chipset].
+ RxConfigMask);
+
+ RTL_W32(RxConfig, tmp);
+ RTL_W32(MAR0 + 0, mc_filter[0]);
+ RTL_W32(MAR0 + 4, mc_filter[1]);
+}
+static void rtl8169_hw_start(struct nic *nic)
+{
+ u32 i;
+
+ /* Soft reset the chip. */
+ RTL_W8(ChipCmd, CmdReset);
+
+ /* Check that the chip has finished the reset. */
+ for (i = 1000; i > 0; i--) {
+ if ((RTL_R8(ChipCmd) & CmdReset) == 0)
+ break;
+ else
+ udelay(10);
+ }
+
+ RTL_W8(Cfg9346, Cfg9346_Unlock);
+ RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
+ RTL_W8(EarlyTxThres, EarlyTxThld);
+
+ /* For gigabit rtl8169 */
+ RTL_W16(RxMaxSize, RxPacketMaxSize);
+
+ /* Set Rx Config register */
+ i = rtl8169_rx_config | (RTL_R32(RxConfig) &
+ rtl_chip_info[tpc->chipset].RxConfigMask);
+ RTL_W32(RxConfig, i);
+
+ /* Set DMA burst size and Interframe Gap Time */
+ RTL_W32(TxConfig,
+ (TX_DMA_BURST << TxDMAShift) | (InterFrameGap <<
+ TxInterFrameGapShift));
+
+
+ tpc->cur_rx = 0;
+
+ RTL_W32(TxDescStartAddr, virt_to_le32desc(tpc->TxDescArray));
+ RTL_W32(RxDescStartAddr, virt_to_le32desc(tpc->RxDescArray));
+ RTL_W8(Cfg9346, Cfg9346_Lock);
+ udelay(10);
+
+ RTL_W32(RxMissed, 0);
+
+ rtl8169_set_rx_mode(nic);
+
+ /* no early-rx interrupts */
+ RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000);
+}
+
+static void rtl8169_init_ring(struct nic *nic __unused)
+{
+ int i;
+
+ tpc->cur_rx = 0;
+ tpc->cur_tx = 0;
+ memset(tpc->TxDescArray, 0x0, NUM_TX_DESC * sizeof(struct TxDesc));
+ memset(tpc->RxDescArray, 0x0, NUM_RX_DESC * sizeof(struct RxDesc));
+
+ for (i = 0; i < NUM_TX_DESC; i++) {
+ tpc->Tx_skbuff[i] = &txb[i];
+ }
+
+ for (i = 0; i < NUM_RX_DESC; i++) {
+ if (i == (NUM_RX_DESC - 1))
+ tpc->RxDescArray[i].status =
+ (OWNbit | EORbit) + RX_BUF_SIZE;
+ else
+ tpc->RxDescArray[i].status = OWNbit + RX_BUF_SIZE;
+
+ tpc->RxBufferRing[i] = &rxb[i * RX_BUF_SIZE];
+ tpc->RxDescArray[i].buf_addr =
+ virt_to_bus(tpc->RxBufferRing[i]);
+ }
+}
+
+/**************************************************************************
+RESET - Finish setting up the ethernet interface
+***************************************************************************/
+static void r8169_reset(struct nic *nic)
+{
+ int i;
+ u8 diff;
+ u32 TxPhyAddr, RxPhyAddr;
+
+ tpc->TxDescArrays = tx_ring;
+ if (tpc->TxDescArrays == 0)
+ printf("Allot Error");
+ /* Tx Desscriptor needs 256 bytes alignment; */
+ TxPhyAddr = virt_to_bus(tpc->TxDescArrays);
+ diff = 256 - (TxPhyAddr - ((TxPhyAddr >> 8) << 8));
+ TxPhyAddr += diff;
+ tpc->TxDescArray = (struct TxDesc *) (tpc->TxDescArrays + diff);
+
+ tpc->RxDescArrays = rx_ring;
+ /* Rx Desscriptor needs 256 bytes alignment; */
+ RxPhyAddr = virt_to_bus(tpc->RxDescArrays);
+ diff = 256 - (RxPhyAddr - ((RxPhyAddr >> 8) << 8));
+ RxPhyAddr += diff;
+ tpc->RxDescArray = (struct RxDesc *) (tpc->RxDescArrays + diff);
+
+ if (tpc->TxDescArrays == NULL || tpc->RxDescArrays == NULL) {
+ printf("Allocate RxDescArray or TxDescArray failed\n");
+ return;
+ }
+
+ rtl8169_init_ring(nic);
+ rtl8169_hw_start(nic);
+ /* Construct a perfect filter frame with the mac address as first match
+ * and broadcast for all others */
+ for (i = 0; i < 192; i++)
+ txb[i] = 0xFF;
+
+ txb[0] = nic->node_addr[0];
+ txb[1] = nic->node_addr[1];
+ txb[2] = nic->node_addr[2];
+ txb[3] = nic->node_addr[3];
+ txb[4] = nic->node_addr[4];
+ txb[5] = nic->node_addr[5];
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void r8169_disable(struct dev *dev __unused)
+{
+ int i;
+ /* Stop the chip's Tx and Rx DMA processes. */
+ RTL_W8(ChipCmd, 0x00);
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ RTL_W16(IntrMask, 0x0000);
+
+ RTL_W32(RxMissed, 0);
+
+ tpc->TxDescArrays = NULL;
+ tpc->RxDescArrays = NULL;
+ tpc->TxDescArray = NULL;
+ tpc->RxDescArray = NULL;
+ for (i = 0; i < NUM_RX_DESC; i++) {
+ tpc->RxBufferRing[i] = NULL;
+ }
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+
+#define board_found 1
+#define valid_link 0
+static int r8169_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *) dev;
+ static int board_idx = -1;
+ static int printed_version = 0;
+ int i, rc;
+ int option = -1, Cap10_100 = 0, Cap1000 = 0;
+
+ printf("r8169.c: Found %s, Vendor=%hX Device=%hX\n",
+ pci->name, pci->vendor, pci->dev_id);
+
+ board_idx++;
+
+ printed_version = 1;
+
+ /* point to private storage */
+ tpc = &tpx;
+
+ rc = rtl8169_init_board(pci); /* Return code is meaningless */
+
+ /* Get MAC address. FIXME: read EEPROM */
+ for (i = 0; i < MAC_ADDR_LEN; i++)
+ nic->node_addr[i] = RTL_R8(MAC0 + i);
+
+ dprintf(("%s: Identified chip type is '%s'.\n", pci->name,
+ rtl_chip_info[tpc->chipset].name));
+ /* Print out some hardware info */
+ printf("%s: %! at ioaddr %hX, ", pci->name, nic->node_addr,
+ ioaddr);
+
+ /* if TBI is not endbled */
+ if (!(RTL_R8(PHYstatus) & TBI_Enable)) {
+ int val = mdio_read(PHY_AUTO_NEGO_REG);
+
+ option = media;
+ /* Force RTL8169 in 10/100/1000 Full/Half mode. */
+ if (option > 0) {
+ printf(" Force-mode Enabled.\n");
+ Cap10_100 = 0, Cap1000 = 0;
+ switch (option) {
+ case _10_Half:
+ Cap10_100 = PHY_Cap_10_Half;
+ Cap1000 = PHY_Cap_Null;
+ break;
+ case _10_Full:
+ Cap10_100 = PHY_Cap_10_Full;
+ Cap1000 = PHY_Cap_Null;
+ break;
+ case _100_Half:
+ Cap10_100 = PHY_Cap_100_Half;
+ Cap1000 = PHY_Cap_Null;
+ break;
+ case _100_Full:
+ Cap10_100 = PHY_Cap_100_Full;
+ Cap1000 = PHY_Cap_Null;
+ break;
+ case _1000_Full:
+ Cap10_100 = PHY_Cap_Null;
+ Cap1000 = PHY_Cap_1000_Full;
+ break;
+ default:
+ break;
+ }
+ /* leave PHY_AUTO_NEGO_REG bit4:0 unchanged */
+ mdio_write(PHY_AUTO_NEGO_REG,
+ Cap10_100 | (val & 0x1F));
+ mdio_write(PHY_1000_CTRL_REG, Cap1000);
+ } else {
+ dprintf(("Auto-negotiation Enabled.\n",
+ pci->name));
+
+ /* enable 10/100 Full/Half Mode, leave PHY_AUTO_NEGO_REG bit4:0 unchanged */
+ mdio_write(PHY_AUTO_NEGO_REG,
+ PHY_Cap_10_Half | PHY_Cap_10_Full |
+ PHY_Cap_100_Half | PHY_Cap_100_Full |
+ (val & 0x1F));
+
+ /* enable 1000 Full Mode */
+ mdio_write(PHY_1000_CTRL_REG, PHY_Cap_1000_Full);
+
+ }
+
+ /* Enable auto-negotiation and restart auto-nigotiation */
+ mdio_write(PHY_CTRL_REG,
+ PHY_Enable_Auto_Nego | PHY_Restart_Auto_Nego);
+ udelay(100);
+
+ /* wait for auto-negotiation process */
+ for (i = 10000; i > 0; i--) {
+ /* Check if auto-negotiation complete */
+ if (mdio_read(PHY_STAT_REG) & PHY_Auto_Neco_Comp) {
+ udelay(100);
+ option = RTL_R8(PHYstatus);
+ if (option & _1000bpsF) {
+ printf
+ ("1000Mbps Full-duplex operation.\n");
+ } else {
+ printf
+ ("%sMbps %s-duplex operation.\n",
+ (option & _100bps) ? "100" :
+ "10",
+ (option & FullDup) ? "Full" :
+ "Half");
+ }
+ break;
+ } else {
+ udelay(100);
+ }
+ } /* end for-loop to wait for auto-negotiation process */
+
+ } else {
+ udelay(100);
+ printf
+ ("%s: 1000Mbps Full-duplex operation, TBI Link %s!\n",
+ pci->name,
+ (RTL_R32(TBICSR) & TBILinkOK) ? "OK" : "Failed");
+
+ }
+
+ r8169_reset(nic);
+ /* point to NIC specific routines */
+ dev->disable = r8169_disable;
+ nic->poll = r8169_poll;
+ nic->transmit = r8169_transmit;
+ nic->irqno = pci->irq;
+ nic->irq = r8169_irq;
+ nic->ioaddr = ioaddr;
+ return 1;
+
+}
+
+static struct pci_id r8169_nics[] = {
+ PCI_ROM(0x10ec, 0x8169, "r8169", "RealTek RTL8169 Gigabit Ethernet"),
+};
+
+static struct pci_driver r8169_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "r8169/PCI",
+ .probe = r8169_probe,
+ .ids = r8169_nics,
+ .id_count = sizeof(r8169_nics) / sizeof(r8169_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/rtl8139.c b/src/drivers/net/rtl8139.c
new file mode 100644
index 00000000..ee078d54
--- /dev/null
+++ b/src/drivers/net/rtl8139.c
@@ -0,0 +1,551 @@
+/* rtl8139.c - etherboot driver for the Realtek 8139 chipset
+
+ ported from the linux driver written by Donald Becker
+ by Rainer Bawidamann (Rainer.Bawidamann@informatik.uni-ulm.de) 1999
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ changes to the original driver:
+ - removed support for interrupts, switching to polling mode (yuck!)
+ - removed support for the 8129 chip (external MII)
+
+*/
+
+/*********************************************************************/
+/* Revision History */
+/*********************************************************************/
+
+/*
+ 28 Dec 2002 ken_yap@users.sourceforge.net (Ken Yap)
+ Put in virt_to_bus calls to allow Etherboot relocation.
+
+ 06 Apr 2001 ken_yap@users.sourceforge.net (Ken Yap)
+ Following email from Hyun-Joon Cha, added a disable routine, otherwise
+ NIC remains live and can crash the kernel later.
+
+ 4 Feb 2000 espenlaub@informatik.uni-ulm.de (Klaus Espenlaub)
+ Shuffled things around, removed the leftovers from the 8129 support
+ that was in the Linux driver and added a bit more 8139 definitions.
+ Moved the 8K receive buffer to a fixed, available address outside the
+ 0x98000-0x9ffff range. This is a bit of a hack, but currently the only
+ way to make room for the Etherboot features that need substantial amounts
+ of code like the ANSI console support. Currently the buffer is just below
+ 0x10000, so this even conforms to the tagged boot image specification,
+ which reserves the ranges 0x00000-0x10000 and 0x98000-0xA0000. My
+ interpretation of this "reserved" is that Etherboot may do whatever it
+ likes, as long as its environment is kept intact (like the BIOS
+ variables). Hopefully fixed rtl_poll() once and for all. The symptoms
+ were that if Etherboot was left at the boot menu for several minutes, the
+ first eth_poll failed. Seems like I am the only person who does this.
+ First of all I fixed the debugging code and then set out for a long bug
+ hunting session. It took me about a week full time work - poking around
+ various places in the driver, reading Don Becker's and Jeff Garzik's Linux
+ driver and even the FreeBSD driver (what a piece of crap!) - and
+ eventually spotted the nasty thing: the transmit routine was acknowledging
+ each and every interrupt pending, including the RxOverrun and RxFIFIOver
+ interrupts. This confused the RTL8139 thoroughly. It destroyed the
+ Rx ring contents by dumping the 2K FIFO contents right where we wanted to
+ get the next packet. Oh well, what fun.
+
+ 18 Jan 2000 mdc@thinguin.org (Marty Connor)
+ Drastically simplified error handling. Basically, if any error
+ in transmission or reception occurs, the card is reset.
+ Also, pointed all transmit descriptors to the same buffer to
+ save buffer space. This should decrease driver size and avoid
+ corruption because of exceeding 32K during runtime.
+
+ 28 Jul 1999 (Matthias Meixner - meixner@rbg.informatik.tu-darmstadt.de)
+ rtl_poll was quite broken: it used the RxOK interrupt flag instead
+ of the RxBufferEmpty flag which often resulted in very bad
+ transmission performace - below 1kBytes/s.
+
+*/
+
+#include "etherboot.h"
+#include "nic.h"
+#include "pci.h"
+#include "timer.h"
+
+#define RTL_TIMEOUT (1*TICKS_PER_SEC)
+
+/* PCI Tuning Parameters
+ Threshold is bytes transferred to chip before transmission starts. */
+#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */
+#define RX_FIFO_THRESH 4 /* Rx buffer level before first PCI xfer. */
+#define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 bytes */
+#define TX_DMA_BURST 4 /* Calculate as 16<<val. */
+#define NUM_TX_DESC 4 /* Number of Tx descriptor registers. */
+#define TX_BUF_SIZE ETH_FRAME_LEN /* FCS is added by the chip */
+#define RX_BUF_LEN_IDX 0 /* 0, 1, 2 is allowed - 8,16,32K rx buffer */
+#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX)
+
+#undef DEBUG_TX
+#undef DEBUG_RX
+
+/* Symbolic offsets to registers. */
+enum RTL8139_registers {
+ MAC0=0, /* Ethernet hardware address. */
+ MAR0=8, /* Multicast filter. */
+ TxStatus0=0x10, /* Transmit status (four 32bit registers). */
+ TxAddr0=0x20, /* Tx descriptors (also four 32bit). */
+ RxBuf=0x30, RxEarlyCnt=0x34, RxEarlyStatus=0x36,
+ ChipCmd=0x37, RxBufPtr=0x38, RxBufAddr=0x3A,
+ IntrMask=0x3C, IntrStatus=0x3E,
+ TxConfig=0x40, RxConfig=0x44,
+ Timer=0x48, /* general-purpose counter. */
+ RxMissed=0x4C, /* 24 bits valid, write clears. */
+ Cfg9346=0x50, Config0=0x51, Config1=0x52,
+ TimerIntrReg=0x54, /* intr if gp counter reaches this value */
+ MediaStatus=0x58,
+ Config3=0x59,
+ MultiIntr=0x5C,
+ RevisionID=0x5E, /* revision of the RTL8139 chip */
+ TxSummary=0x60,
+ MII_BMCR=0x62, MII_BMSR=0x64, NWayAdvert=0x66, NWayLPAR=0x68,
+ NWayExpansion=0x6A,
+ DisconnectCnt=0x6C, FalseCarrierCnt=0x6E,
+ NWayTestReg=0x70,
+ RxCnt=0x72, /* packet received counter */
+ CSCR=0x74, /* chip status and configuration register */
+ PhyParm1=0x78,TwisterParm=0x7c,PhyParm2=0x80, /* undocumented */
+ /* from 0x84 onwards are a number of power management/wakeup frame
+ * definitions we will probably never need to know about. */
+};
+
+enum RxEarlyStatusBits {
+ ERGood=0x08, ERBad=0x04, EROVW=0x02, EROK=0x01
+};
+
+enum ChipCmdBits {
+ CmdReset=0x10, CmdRxEnb=0x08, CmdTxEnb=0x04, RxBufEmpty=0x01, };
+
+enum IntrMaskBits {
+ SERR=0x8000, TimeOut=0x4000, LenChg=0x2000,
+ FOVW=0x40, PUN_LinkChg=0x20, RXOVW=0x10,
+ TER=0x08, TOK=0x04, RER=0x02, ROK=0x01
+};
+
+/* Interrupt register bits, using my own meaningful names. */
+enum IntrStatusBits {
+ PCIErr=0x8000, PCSTimeout=0x4000, CableLenChange= 0x2000,
+ RxFIFOOver=0x40, RxUnderrun=0x20, RxOverflow=0x10,
+ TxErr=0x08, TxOK=0x04, RxErr=0x02, RxOK=0x01,
+};
+enum TxStatusBits {
+ TxHostOwns=0x2000, TxUnderrun=0x4000, TxStatOK=0x8000,
+ TxOutOfWindow=0x20000000, TxAborted=0x40000000,
+ TxCarrierLost=0x80000000,
+};
+enum RxStatusBits {
+ RxMulticast=0x8000, RxPhysical=0x4000, RxBroadcast=0x2000,
+ RxBadSymbol=0x0020, RxRunt=0x0010, RxTooLong=0x0008, RxCRCErr=0x0004,
+ RxBadAlign=0x0002, RxStatusOK=0x0001,
+};
+
+enum MediaStatusBits {
+ MSRTxFlowEnable=0x80, MSRRxFlowEnable=0x40, MSRSpeed10=0x08,
+ MSRLinkFail=0x04, MSRRxPauseFlag=0x02, MSRTxPauseFlag=0x01,
+};
+
+enum MIIBMCRBits {
+ BMCRReset=0x8000, BMCRSpeed100=0x2000, BMCRNWayEnable=0x1000,
+ BMCRRestartNWay=0x0200, BMCRDuplex=0x0100,
+};
+
+enum CSCRBits {
+ CSCR_LinkOKBit=0x0400, CSCR_LinkChangeBit=0x0800,
+ CSCR_LinkStatusBits=0x0f000, CSCR_LinkDownOffCmd=0x003c0,
+ CSCR_LinkDownCmd=0x0f3c0,
+};
+
+/* Bits in RxConfig. */
+enum rx_mode_bits {
+ RxCfgWrap=0x80,
+ AcceptErr=0x20, AcceptRunt=0x10, AcceptBroadcast=0x08,
+ AcceptMulticast=0x04, AcceptMyPhys=0x02, AcceptAllPhys=0x01,
+};
+
+static unsigned int cur_rx,cur_tx;
+
+/* The RTL8139 can only transmit from a contiguous, aligned memory block. */
+static unsigned char tx_buffer[TX_BUF_SIZE] __attribute__((aligned(4)));
+static unsigned char rx_ring[RX_BUF_LEN+16] __attribute__((aligned(4)));
+
+static int rtl8139_probe(struct dev *dev, struct pci_device *pci);
+static int read_eeprom(struct nic *nic, int location, int addr_len);
+static void rtl_reset(struct nic *nic);
+static void rtl_transmit(struct nic *nic, const char *destaddr,
+ unsigned int type, unsigned int len, const char *data);
+static int rtl_poll(struct nic *nic, int retrieve);
+static void rtl_disable(struct dev *);
+static void rtl_irq(struct nic *nic, irq_action_t action);
+
+
+static int rtl8139_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *)dev;
+ int i;
+ int speed10, fullduplex;
+ int addr_len;
+ unsigned short *ap = (unsigned short*)nic->node_addr;
+
+ /* There are enough "RTL8139" strings on the console already, so
+ * be brief and concentrate on the interesting pieces of info... */
+ printf(" - ");
+
+ /* Mask the bit that says "this is an io addr" */
+ nic->ioaddr = pci->ioaddr & ~3;
+
+ /* Copy IRQ from PCI information */
+ nic->irqno = pci->irq;
+
+ adjust_pci_device(pci);
+
+ /* Bring the chip out of low-power mode. */
+ outb(0x00, nic->ioaddr + Config1);
+
+ addr_len = read_eeprom(nic,0,8) == 0x8129 ? 8 : 6;
+ for (i = 0; i < 3; i++)
+ *ap++ = read_eeprom(nic,i + 7,addr_len);
+
+ speed10 = inb(nic->ioaddr + MediaStatus) & MSRSpeed10;
+ fullduplex = inw(nic->ioaddr + MII_BMCR) & BMCRDuplex;
+ printf("ioaddr %#hX, irq %d, addr %! %sMbps %s-duplex\n", nic->ioaddr,
+ nic->irqno, nic->node_addr, speed10 ? "10" : "100",
+ fullduplex ? "full" : "half");
+
+ rtl_reset(nic);
+
+ if (inb(nic->ioaddr + MediaStatus) & MSRLinkFail) {
+ printf("Cable not connected or other link failure\n");
+ return(0);
+ }
+
+ dev->disable = rtl_disable;
+ nic->poll = rtl_poll;
+ nic->transmit = rtl_transmit;
+ nic->irq = rtl_irq;
+
+ return 1;
+}
+
+/* Serial EEPROM section. */
+
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */
+#define EE_CS 0x08 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */
+#define EE_WRITE_0 0x00
+#define EE_WRITE_1 0x02
+#define EE_DATA_READ 0x01 /* EEPROM chip data out. */
+#define EE_ENB (0x80 | EE_CS)
+
+/*
+ Delay between EEPROM clock transitions.
+ No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
+*/
+
+#define eeprom_delay() inl(ee_addr)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5)
+#define EE_READ_CMD (6)
+#define EE_ERASE_CMD (7)
+
+static int read_eeprom(struct nic *nic, int location, int addr_len)
+{
+ int i;
+ unsigned int retval = 0;
+ long ee_addr = nic->ioaddr + Cfg9346;
+ int read_cmd = location | (EE_READ_CMD << addr_len);
+
+ outb(EE_ENB & ~EE_CS, ee_addr);
+ outb(EE_ENB, ee_addr);
+ eeprom_delay();
+
+ /* Shift the read command bits out. */
+ for (i = 4 + addr_len; i >= 0; i--) {
+ int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+ outb(EE_ENB | dataval, ee_addr);
+ eeprom_delay();
+ outb(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ }
+ outb(EE_ENB, ee_addr);
+ eeprom_delay();
+
+ for (i = 16; i > 0; i--) {
+ outb(EE_ENB | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0);
+ outb(EE_ENB, ee_addr);
+ eeprom_delay();
+ }
+
+ /* Terminate the EEPROM access. */
+ outb(~EE_CS, ee_addr);
+ eeprom_delay();
+ return retval;
+}
+
+static const unsigned int rtl8139_rx_config =
+ (RX_BUF_LEN_IDX << 11) |
+ (RX_FIFO_THRESH << 13) |
+ (RX_DMA_BURST << 8);
+
+static void set_rx_mode(struct nic *nic) {
+ unsigned int mc_filter[2];
+ int rx_mode;
+ /* !IFF_PROMISC */
+ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+ mc_filter[1] = mc_filter[0] = 0xffffffff;
+
+ outl(rtl8139_rx_config | rx_mode, nic->ioaddr + RxConfig);
+
+ outl(mc_filter[0], nic->ioaddr + MAR0 + 0);
+ outl(mc_filter[1], nic->ioaddr + MAR0 + 4);
+}
+
+static void rtl_reset(struct nic* nic)
+{
+ int i;
+
+ outb(CmdReset, nic->ioaddr + ChipCmd);
+
+ cur_rx = 0;
+ cur_tx = 0;
+
+ /* Give the chip 10ms to finish the reset. */
+ load_timer2(10*TICKS_PER_MS);
+ while ((inb(nic->ioaddr + ChipCmd) & CmdReset) != 0 &&
+ timer2_running())
+ /* wait */;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ outb(nic->node_addr[i], nic->ioaddr + MAC0 + i);
+
+ /* Must enable Tx/Rx before setting transfer thresholds! */
+ outb(CmdRxEnb | CmdTxEnb, nic->ioaddr + ChipCmd);
+ outl((RX_FIFO_THRESH<<13) | (RX_BUF_LEN_IDX<<11) | (RX_DMA_BURST<<8),
+ nic->ioaddr + RxConfig); /* accept no frames yet! */
+ outl((TX_DMA_BURST<<8)|0x03000000, nic->ioaddr + TxConfig);
+
+ /* The Linux driver changes Config1 here to use a different LED pattern
+ * for half duplex or full/autodetect duplex (for full/autodetect, the
+ * outputs are TX/RX, Link10/100, FULL, while for half duplex it uses
+ * TX/RX, Link100, Link10). This is messy, because it doesn't match
+ * the inscription on the mounting bracket. It should not be changed
+ * from the configuration EEPROM default, because the card manufacturer
+ * should have set that to match the card. */
+
+#ifdef DEBUG_RX
+ printf("rx ring address is %X\n",(unsigned long)rx_ring);
+#endif
+ outl((unsigned long)virt_to_bus(rx_ring), nic->ioaddr + RxBuf);
+
+
+
+ /* If we add multicast support, the MAR0 register would have to be
+ * initialized to 0xffffffffffffffff (two 32 bit accesses). Etherboot
+ * only needs broadcast (for ARP/RARP/BOOTP/DHCP) and unicast. */
+
+ outb(CmdRxEnb | CmdTxEnb, nic->ioaddr + ChipCmd);
+
+ outl(rtl8139_rx_config, nic->ioaddr + RxConfig);
+
+ /* Start the chip's Tx and Rx process. */
+ outl(0, nic->ioaddr + RxMissed);
+
+ /* set_rx_mode */
+ set_rx_mode(nic);
+
+ /* Disable all known interrupts by setting the interrupt mask. */
+ outw(0, nic->ioaddr + IntrMask);
+}
+
+static void rtl_transmit(struct nic *nic, const char *destaddr,
+ unsigned int type, unsigned int len, const char *data)
+{
+ unsigned int status, to, nstype;
+ unsigned long txstatus;
+
+ /* nstype assignment moved up here to avoid gcc 3.0.3 compiler bug */
+ nstype = htons(type);
+ memcpy(tx_buffer, destaddr, ETH_ALEN);
+ memcpy(tx_buffer + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ memcpy(tx_buffer + 2 * ETH_ALEN, &nstype, 2);
+ memcpy(tx_buffer + ETH_HLEN, data, len);
+
+ len += ETH_HLEN;
+#ifdef DEBUG_TX
+ printf("sending %d bytes ethtype %hX\n", len, type);
+#endif
+
+ /* Note: RTL8139 doesn't auto-pad, send minimum payload (another 4
+ * bytes are sent automatically for the FCS, totalling to 64 bytes). */
+ while (len < ETH_ZLEN) {
+ tx_buffer[len++] = '\0';
+ }
+
+ outl((unsigned long)virt_to_bus(tx_buffer), nic->ioaddr + TxAddr0 + cur_tx*4);
+ outl(((TX_FIFO_THRESH<<11) & 0x003f0000) | len,
+ nic->ioaddr + TxStatus0 + cur_tx*4);
+
+ to = currticks() + RTL_TIMEOUT;
+
+ do {
+ status = inw(nic->ioaddr + IntrStatus);
+ /* Only acknlowledge interrupt sources we can properly handle
+ * here - the RxOverflow/RxFIFOOver MUST be handled in the
+ * rtl_poll() function. */
+ outw(status & (TxOK | TxErr | PCIErr), nic->ioaddr + IntrStatus);
+ if ((status & (TxOK | TxErr | PCIErr)) != 0) break;
+ } while (currticks() < to);
+
+ txstatus = inl(nic->ioaddr+ TxStatus0 + cur_tx*4);
+
+ if (status & TxOK) {
+ cur_tx = (cur_tx + 1) % NUM_TX_DESC;
+#ifdef DEBUG_TX
+ printf("tx done (%d ticks), status %hX txstatus %X\n",
+ to-currticks(), status, txstatus);
+#endif
+ } else {
+#ifdef DEBUG_TX
+ printf("tx timeout/error (%d ticks), status %hX txstatus %X\n",
+ currticks()-to, status, txstatus);
+#endif
+ rtl_reset(nic);
+ }
+}
+
+static int rtl_poll(struct nic *nic, int retrieve)
+{
+ unsigned int status;
+ unsigned int ring_offs;
+ unsigned int rx_size, rx_status;
+
+ if (inb(nic->ioaddr + ChipCmd) & RxBufEmpty) {
+ return 0;
+ }
+
+ /* There is a packet ready */
+ if ( ! retrieve ) return 1;
+
+ status = inw(nic->ioaddr + IntrStatus);
+ /* See below for the rest of the interrupt acknowledges. */
+ outw(status & ~(RxFIFOOver | RxOverflow | RxOK), nic->ioaddr + IntrStatus);
+
+#ifdef DEBUG_RX
+ printf("rtl_poll: int %hX ", status);
+#endif
+
+ ring_offs = cur_rx % RX_BUF_LEN;
+ rx_status = *(unsigned int*)(rx_ring + ring_offs);
+ rx_size = rx_status >> 16;
+ rx_status &= 0xffff;
+
+ if ((rx_status & (RxBadSymbol|RxRunt|RxTooLong|RxCRCErr|RxBadAlign)) ||
+ (rx_size < ETH_ZLEN) || (rx_size > ETH_FRAME_LEN + 4)) {
+ printf("rx error %hX\n", rx_status);
+ rtl_reset(nic); /* this clears all interrupts still pending */
+ return 0;
+ }
+
+ /* Received a good packet */
+ nic->packetlen = rx_size - 4; /* no one cares about the FCS */
+ if (ring_offs+4+rx_size-4 > RX_BUF_LEN) {
+ int semi_count = RX_BUF_LEN - ring_offs - 4;
+
+ memcpy(nic->packet, rx_ring + ring_offs + 4, semi_count);
+ memcpy(nic->packet+semi_count, rx_ring, rx_size-4-semi_count);
+#ifdef DEBUG_RX
+ printf("rx packet %d+%d bytes", semi_count,rx_size-4-semi_count);
+#endif
+ } else {
+ memcpy(nic->packet, rx_ring + ring_offs + 4, nic->packetlen);
+#ifdef DEBUG_RX
+ printf("rx packet %d bytes", rx_size-4);
+#endif
+ }
+#ifdef DEBUG_RX
+ printf(" at %X type %hhX%hhX rxstatus %hX\n",
+ (unsigned long)(rx_ring+ring_offs+4),
+ nic->packet[12], nic->packet[13], rx_status);
+#endif
+ cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
+ outw(cur_rx - 16, nic->ioaddr + RxBufPtr);
+ /* See RTL8139 Programming Guide V0.1 for the official handling of
+ * Rx overflow situations. The document itself contains basically no
+ * usable information, except for a few exception handling rules. */
+ outw(status & (RxFIFOOver | RxOverflow | RxOK), nic->ioaddr + IntrStatus);
+ return 1;
+}
+
+static void rtl_irq(struct nic *nic, irq_action_t action)
+{
+ unsigned int mask;
+ /* Bit of a guess as to which interrupts we should allow */
+ unsigned int interested = ROK | RER | RXOVW | FOVW | SERR;
+
+ switch ( action ) {
+ case DISABLE :
+ case ENABLE :
+ mask = inw(nic->ioaddr + IntrMask);
+ mask = mask & ~interested;
+ if ( action == ENABLE ) mask = mask | interested;
+ outw(mask, nic->ioaddr + IntrMask);
+ break;
+ case FORCE :
+ /* Apparently writing a 1 to this read-only bit of a
+ * read-only and otherwise unrelated register will
+ * force an interrupt. If you ever want to see how
+ * not to write a datasheet, read the one for the
+ * RTL8139...
+ */
+ outb(EROK, nic->ioaddr + RxEarlyStatus);
+ break;
+ }
+}
+
+static void rtl_disable(struct dev *dev)
+{
+ struct nic *nic = (struct nic *)dev;
+ /* merge reset and disable */
+ rtl_reset(nic);
+
+ /* reset the chip */
+ outb(CmdReset, nic->ioaddr + ChipCmd);
+
+ /* 10 ms timeout */
+ load_timer2(10*TICKS_PER_MS);
+ while ((inb(nic->ioaddr + ChipCmd) & CmdReset) != 0 && timer2_running())
+ /* wait */;
+}
+
+static struct pci_id rtl8139_nics[] = {
+PCI_ROM(0x10ec, 0x8129, "rtl8129", "Realtek 8129"),
+PCI_ROM(0x10ec, 0x8139, "rtl8139", "Realtek 8139"),
+PCI_ROM(0x10ec, 0x8138, "rtl8139b", "Realtek 8139B"),
+PCI_ROM(0x1186, 0x1300, "dfe538", "DFE530TX+/DFE538TX"),
+PCI_ROM(0x1113, 0x1211, "smc1211-1", "SMC EZ10/100"),
+PCI_ROM(0x1112, 0x1211, "smc1211", "SMC EZ10/100"),
+PCI_ROM(0x1500, 0x1360, "delta8139", "Delta Electronics 8139"),
+PCI_ROM(0x4033, 0x1360, "addtron8139", "Addtron Technology 8139"),
+PCI_ROM(0x1186, 0x1340, "dfe690txd", "D-Link DFE690TXD"),
+PCI_ROM(0x13d1, 0xab06, "fe2000vx", "AboCom FE2000VX"),
+PCI_ROM(0x1259, 0xa117, "allied8139", "Allied Telesyn 8139"),
+PCI_ROM(0x14ea, 0xab06, "fnw3603tx", "Planex FNW-3603-TX"),
+PCI_ROM(0x14ea, 0xab07, "fnw3800tx", "Planex FNW-3800-TX"),
+PCI_ROM(0xffff, 0x8139, "clone-rtl8139", "Cloned 8139"),
+};
+
+static struct pci_driver rtl8139_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "RTL8139",
+ .probe = rtl8139_probe,
+ .ids = rtl8139_nics,
+ .id_count = sizeof(rtl8139_nics)/sizeof(rtl8139_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/sis900.c b/src/drivers/net/sis900.c
new file mode 100644
index 00000000..c4c873da
--- /dev/null
+++ b/src/drivers/net/sis900.c
@@ -0,0 +1,1271 @@
+/* -*- Mode:C; c-basic-offset:4; -*- */
+
+/*
+ sis900.c: An SiS 900/7016 PCI Fast Ethernet driver for Etherboot
+ Copyright (C) 2001 Entity Cyber, Inc.
+
+ Revision: 1.0 March 1, 2001
+
+ Author: Marty Connor (mdc@thinguin.org)
+
+ Adapted from a Linux driver which was written by Donald Becker
+ and modified by Ollie Lho and Chin-Shan Li of SiS Corporation.
+ Rewritten for Etherboot by Marty Connor.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License (GPL), incorporated herein by reference.
+
+ References:
+ SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support,
+ preliminary Rev. 1.0 Jan. 14, 1998
+ SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support,
+ preliminary Rev. 1.0 Nov. 10, 1998
+ SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution,
+ preliminary Rev. 1.0 Jan. 18, 1998
+ http://www.sis.com.tw/support/databook.htm */
+
+/* Revision History */
+
+/*
+ 07 Dec 2003 timlegge - Enabled Multicast Support
+ 06 Dec 2003 timlegge - Fixed relocation issue in 5.2
+ 04 Jan 2002 Chien-Yu Chen, Doug Ambrisko, Marty Connor Patch to Etherboot 5.0.5
+ Added support for the SiS 630ET plus various bug fixes from linux kernel
+ source 2.4.17.
+ 01 March 2001 mdc 1.0
+ Initial Release. Tested with PCI based sis900 card and ThinkNIC
+ computer.
+ 20 March 2001 P.Koegel
+ added support for sis630e and PHY ICS1893 and RTL8201
+ Testet with SIS730S chipset + ICS1893
+*/
+
+
+/* Includes */
+
+#include "etherboot.h"
+#include "nic.h"
+#include "pci.h"
+#include "timer.h"
+
+#include "sis900.h"
+
+/* Globals */
+
+static int sis900_debug = 0;
+
+static unsigned short vendor, dev_id;
+static unsigned long ioaddr;
+static u8 pci_revision;
+
+static unsigned int cur_phy;
+
+static unsigned int cur_rx;
+
+static BufferDesc txd;
+static BufferDesc rxd[NUM_RX_DESC];
+static unsigned char txb[TX_BUF_SIZE];
+static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE];
+
+#if 0
+static struct mac_chip_info {
+ const char *name;
+ u16 vendor_id, device_id, flags;
+ int io_size;
+} mac_chip_table[] = {
+ { "SiS 900 PCI Fast Ethernet", PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS900,
+ PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE},
+ { "SiS 7016 PCI Fast Ethernet",PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS7016,
+ PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE},
+ {0,0,0,0,0} /* 0 terminated list. */
+};
+#endif
+
+static void sis900_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex);
+static void amd79c901_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex);
+static void ics1893_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex);
+static void rtl8201_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex);
+static void vt6103_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex);
+
+static struct mii_chip_info {
+ const char * name;
+ u16 phy_id0;
+ u16 phy_id1;
+ void (*read_mode) (struct nic *nic, int phy_addr, int *speed, int *duplex);
+} mii_chip_table[] = {
+ {"SiS 900 Internal MII PHY", 0x001d, 0x8000, sis900_read_mode},
+ {"SiS 7014 Physical Layer Solution", 0x0016, 0xf830,sis900_read_mode},
+ {"AMD 79C901 10BASE-T PHY", 0x0000, 0x6B70, amd79c901_read_mode},
+ {"AMD 79C901 HomePNA PHY", 0x0000, 0x6B90, amd79c901_read_mode},
+ {"ICS 1893 Integrated PHYceiver" , 0x0015, 0xf440,ics1893_read_mode},
+// {"NS 83851 PHY",0x2000, 0x5C20, MIX },
+ {"RTL 8201 10/100Mbps Phyceiver" , 0x0000, 0x8200,rtl8201_read_mode},
+ {"VIA 6103 10/100Mbps Phyceiver", 0x0101, 0x8f20,vt6103_read_mode},
+ {0,0,0,0}
+};
+
+static struct mii_phy {
+ struct mii_phy * next;
+ struct mii_chip_info * chip_info;
+ int phy_addr;
+ u16 status;
+} mii;
+
+
+// PCI to ISA bridge for SIS640E access
+static struct pci_id pci_isa_bridge_list[] = {
+ { 0x1039, 0x0008,
+ "SIS 85C503/5513 PCI to ISA bridge"},
+};
+
+static struct pci_driver sis_bridge_driver __pci_driver = {
+ .type = BRIDGE_DRIVER,
+ .name = "",
+ .probe = 0,
+ .ids = pci_isa_bridge_list,
+ .id_count = sizeof(pci_isa_bridge_list)/sizeof(pci_isa_bridge_list[0]),
+ .class = 0,
+};
+
+/* Function Prototypes */
+
+static int sis900_probe(struct dev *dev, struct pci_device *pci);
+
+static u16 sis900_read_eeprom(int location);
+static void sis900_mdio_reset(long mdio_addr);
+static void sis900_mdio_idle(long mdio_addr);
+static u16 sis900_mdio_read(int phy_id, int location);
+#if 0
+static void sis900_mdio_write(int phy_id, int location, int val);
+#endif
+static void sis900_init(struct nic *nic);
+
+static void sis900_reset(struct nic *nic);
+
+static void sis900_init_rxfilter(struct nic *nic);
+static void sis900_init_txd(struct nic *nic);
+static void sis900_init_rxd(struct nic *nic);
+static void sis900_set_rx_mode(struct nic *nic);
+static void sis900_check_mode(struct nic *nic);
+
+static void sis900_transmit(struct nic *nic, const char *d,
+ unsigned int t, unsigned int s, const char *p);
+static int sis900_poll(struct nic *nic, int retrieve);
+
+static void sis900_disable(struct dev *dev);
+
+static void sis900_irq(struct nic *nic, irq_action_t action);
+
+/**
+ * sis900_get_mac_addr: - Get MAC address for stand alone SiS900 model
+ * @pci_dev: the sis900 pci device
+ * @net_dev: the net device to get address for
+ *
+ * Older SiS900 and friends, use EEPROM to store MAC address.
+ * MAC address is read from read_eeprom() into @net_dev->dev_addr.
+ */
+
+static int sis900_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic)
+{
+ u16 signature;
+ int i;
+
+ /* check to see if we have sane EEPROM */
+ signature = (u16) sis900_read_eeprom( EEPROMSignature);
+ if (signature == 0xffff || signature == 0x0000) {
+ printf ("sis900_probe: Error EERPOM read %hX\n", signature);
+ return 0;
+ }
+
+ /* get MAC address from EEPROM */
+ for (i = 0; i < 3; i++)
+ ((u16 *)(nic->node_addr))[i] = sis900_read_eeprom(i+EEPROMMACAddr);
+ return 1;
+}
+
+/**
+ * sis96x_get_mac_addr: - Get MAC address for SiS962 or SiS963 model
+ * @pci_dev: the sis900 pci device
+ * @net_dev: the net device to get address for
+ *
+ * SiS962 or SiS963 model, use EEPROM to store MAC address. And EEPROM
+ * is shared by
+ * LAN and 1394. When access EEPROM, send EEREQ signal to hardware first
+ * and wait for EEGNT. If EEGNT is ON, EEPROM is permitted to be access
+ * by LAN, otherwise is not. After MAC address is read from EEPROM, send
+ * EEDONE signal to refuse EEPROM access by LAN.
+ * The EEPROM map of SiS962 or SiS963 is different to SiS900.
+ * The signature field in SiS962 or SiS963 spec is meaningless.
+ * MAC address is read into @net_dev->dev_addr.
+ */
+
+static int sis96x_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic)
+{
+/* long ioaddr = net_dev->base_addr; */
+ long ee_addr = ioaddr + mear;
+ u32 waittime = 0;
+ int i;
+
+ printf("Alternate function\n");
+
+ outl(EEREQ, ee_addr);
+ while(waittime < 2000) {
+ if(inl(ee_addr) & EEGNT) {
+
+ /* get MAC address from EEPROM */
+ for (i = 0; i < 3; i++)
+ ((u16 *)(nic->node_addr))[i] = sis900_read_eeprom(i+EEPROMMACAddr);
+
+ outl(EEDONE, ee_addr);
+ return 1;
+ } else {
+ udelay(1);
+ waittime ++;
+ }
+ }
+ outl(EEDONE, ee_addr);
+ return 0;
+}
+
+/**
+ * sis630e_get_mac_addr: - Get MAC address for SiS630E model
+ * @pci_dev: the sis900 pci device
+ * @net_dev: the net device to get address for
+ *
+ * SiS630E model, use APC CMOS RAM to store MAC address.
+ * APC CMOS RAM is accessed through ISA bridge.
+ * MAC address is read into @net_dev->dev_addr.
+ */
+
+static int sis630e_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic)
+{
+ u8 reg;
+ int i;
+ struct pci_device p[1];
+
+ /* find PCI to ISA bridge */
+ memset(p, 0, sizeof(p));
+ do {
+ find_pci(BRIDGE_DRIVER, p);
+ } while(p->driver && p->driver != &sis_bridge_driver);
+
+ /* error on failure */
+ if (!p->driver)
+ return 0;
+
+ pcibios_read_config_byte(p->bus,p->devfn, 0x48, &reg);
+ pcibios_write_config_byte(p->bus,p->devfn, 0x48, reg | 0x40);
+
+ for (i = 0; i < ETH_ALEN; i++)
+ {
+ outb(0x09 + i, 0x70);
+ ((u8 *)(nic->node_addr))[i] = inb(0x71);
+ }
+ pcibios_write_config_byte(p->bus,p->devfn, 0x48, reg & ~0x40);
+
+ return 1;
+}
+
+/**
+ * sis630e_get_mac_addr: - Get MAC address for SiS630E model
+ * @pci_dev: the sis900 pci device
+ * @net_dev: the net device to get address for
+ *
+ * SiS630E model, use APC CMOS RAM to store MAC address.
+ * APC CMOS RAM is accessed through ISA bridge.
+ * MAC address is read into @net_dev->dev_addr.
+ */
+
+static int sis635_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic)
+{
+ u32 rfcrSave;
+ u32 i;
+
+
+ rfcrSave = inl(rfcr + ioaddr);
+
+ outl(rfcrSave | RELOAD, ioaddr + cr);
+ outl(0, ioaddr + cr);
+
+ /* disable packet filtering before setting filter */
+ outl(rfcrSave & ~RFEN, rfcr + ioaddr);
+
+ /* load MAC addr to filter data register */
+ for (i = 0 ; i < 3 ; i++) {
+ outl((i << RFADDR_shift), ioaddr + rfcr);
+ *( ((u16 *)nic->node_addr) + i) = inw(ioaddr + rfdr);
+ }
+
+ /* enable packet filitering */
+ outl(rfcrSave | RFEN, rfcr + ioaddr);
+
+ return 1;
+}
+
+/*
+ * Function: sis900_probe
+ *
+ * Description: initializes initializes the NIC, retrieves the
+ * MAC address of the card, and sets up some globals required by
+ * other routines.
+ *
+ * Side effects:
+ * leaves the ioaddress of the sis900 chip in the variable ioaddr.
+ * leaves the sis900 initialized, and ready to recieve packets.
+ *
+ * Returns: struct nic *: pointer to NIC data structure
+ */
+
+static int sis900_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *)dev;
+ int i;
+ int found=0;
+ int phy_addr;
+ u8 revision;
+ int ret;
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ nic->irqno = 0;
+ nic->ioaddr = pci->ioaddr & ~3;
+ ioaddr = pci->ioaddr & ~3;
+ vendor = pci->vendor;
+ dev_id = pci->dev_id;
+
+ /* wakeup chip */
+ pcibios_write_config_dword(pci->bus, pci->devfn, 0x40, 0x00000000);
+
+ adjust_pci_device(pci);
+
+ /* get MAC address */
+ ret = 0;
+ pcibios_read_config_byte(pci->bus,pci->devfn, PCI_REVISION, &revision);
+
+ /* save for use later in sis900_reset() */
+ pci_revision = revision;
+
+ if (revision == SIS630E_900_REV)
+ ret = sis630e_get_mac_addr(pci, nic);
+ else if ((revision > 0x81) && (revision <= 0x90))
+ ret = sis635_get_mac_addr(pci, nic);
+ else if (revision == SIS96x_900_REV)
+ ret = sis96x_get_mac_addr(pci, nic);
+ else
+ ret = sis900_get_mac_addr(pci, nic);
+
+ if (ret == 0)
+ {
+ printf ("sis900_probe: Error MAC address not found\n");
+ return 0;
+ }
+
+ /* 630ET : set the mii access mode as software-mode */
+ if (revision == SIS630ET_900_REV)
+ outl(ACCESSMODE | inl(ioaddr + cr), ioaddr + cr);
+
+ printf("\nsis900_probe: MAC addr %! at ioaddr %#hX\n",
+ nic->node_addr, ioaddr);
+ printf("sis900_probe: Vendor:%#hX Device:%#hX\n", vendor, dev_id);
+
+ /* probe for mii transceiver */
+ /* search for total of 32 possible mii phy addresses */
+
+ found = 0;
+ for (phy_addr = 0; phy_addr < 32; phy_addr++) {
+ u16 mii_status;
+ u16 phy_id0, phy_id1;
+
+ mii_status = sis900_mdio_read(phy_addr, MII_STATUS);
+ if (mii_status == 0xffff || mii_status == 0x0000)
+ /* the mii is not accessable, try next one */
+ continue;
+
+ phy_id0 = sis900_mdio_read(phy_addr, MII_PHY_ID0);
+ phy_id1 = sis900_mdio_read(phy_addr, MII_PHY_ID1);
+
+ /* search our mii table for the current mii */
+ for (i = 0; mii_chip_table[i].phy_id1; i++) {
+
+ if ((phy_id0 == mii_chip_table[i].phy_id0) &&
+ ((phy_id1 & 0xFFF0) == mii_chip_table[i].phy_id1)){
+
+ printf("sis900_probe: %s transceiver found at address %d.\n",
+ mii_chip_table[i].name, phy_addr);
+
+ mii.chip_info = &mii_chip_table[i];
+ mii.phy_addr = phy_addr;
+ mii.status = sis900_mdio_read(phy_addr, MII_STATUS);
+ mii.next = NULL;
+
+ found=1;
+ break;
+ }
+ }
+ }
+
+ if (found == 0) {
+ printf("sis900_probe: No MII transceivers found!\n");
+ return 0;
+ }
+
+ /* Arbitrarily select the last PHY found as current PHY */
+ cur_phy = mii.phy_addr;
+ printf("sis900_probe: Using %s as default\n", mii.chip_info->name);
+
+ /* initialize device */
+ sis900_init(nic);
+
+ dev->disable = sis900_disable;
+ nic->poll = sis900_poll;
+ nic->transmit = sis900_transmit;
+ nic->irq = sis900_irq;
+
+ return 1;
+}
+
+
+/*
+ * EEPROM Routines: These functions read and write to EEPROM for
+ * retrieving the MAC address and other configuration information about
+ * the card.
+ */
+
+/* Delay between EEPROM clock transitions. */
+#define eeprom_delay() inl(ee_addr)
+
+
+/* Function: sis900_read_eeprom
+ *
+ * Description: reads and returns a given location from EEPROM
+ *
+ * Arguments: int location: requested EEPROM location
+ *
+ * Returns: u16: contents of requested EEPROM location
+ *
+ */
+
+/* Read Serial EEPROM through EEPROM Access Register, Note that location is
+ in word (16 bits) unit */
+static u16 sis900_read_eeprom(int location)
+{
+ int i;
+ u16 retval = 0;
+ long ee_addr = ioaddr + mear;
+ u32 read_cmd = location | EEread;
+
+ outl(0, ee_addr);
+ eeprom_delay();
+ outl(EECS, ee_addr);
+ eeprom_delay();
+
+ /* Shift the read command (9) bits out. */
+ for (i = 8; i >= 0; i--) {
+ u32 dataval = (read_cmd & (1 << i)) ? EEDI | EECS : EECS;
+ outl(dataval, ee_addr);
+ eeprom_delay();
+ outl(dataval | EECLK, ee_addr);
+ eeprom_delay();
+ }
+ outl(EECS, ee_addr);
+ eeprom_delay();
+
+ /* read the 16-bits data in */
+ for (i = 16; i > 0; i--) {
+ outl(EECS, ee_addr);
+ eeprom_delay();
+ outl(EECS | EECLK, ee_addr);
+ eeprom_delay();
+ retval = (retval << 1) | ((inl(ee_addr) & EEDO) ? 1 : 0);
+ eeprom_delay();
+ }
+
+ /* Terminate the EEPROM access. */
+ outl(0, ee_addr);
+ eeprom_delay();
+// outl(EECLK, ee_addr);
+
+ return (retval);
+}
+
+#define sis900_mdio_delay() inl(mdio_addr)
+
+
+/*
+ Read and write the MII management registers using software-generated
+ serial MDIO protocol. Note that the command bits and data bits are
+ send out seperately
+*/
+
+static void sis900_mdio_idle(long mdio_addr)
+{
+ outl(MDIO | MDDIR, mdio_addr);
+ sis900_mdio_delay();
+ outl(MDIO | MDDIR | MDC, mdio_addr);
+}
+
+/* Syncronize the MII management interface by shifting 32 one bits out. */
+static void sis900_mdio_reset(long mdio_addr)
+{
+ int i;
+
+ for (i = 31; i >= 0; i--) {
+ outl(MDDIR | MDIO, mdio_addr);
+ sis900_mdio_delay();
+ outl(MDDIR | MDIO | MDC, mdio_addr);
+ sis900_mdio_delay();
+ }
+ return;
+}
+
+static u16 sis900_mdio_read(int phy_id, int location)
+{
+ long mdio_addr = ioaddr + mear;
+ int mii_cmd = MIIread|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
+ u16 retval = 0;
+ int i;
+
+ sis900_mdio_reset(mdio_addr);
+ sis900_mdio_idle(mdio_addr);
+
+ for (i = 15; i >= 0; i--) {
+ int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR;
+ outl(dataval, mdio_addr);
+ sis900_mdio_delay();
+ outl(dataval | MDC, mdio_addr);
+ sis900_mdio_delay();
+ }
+
+ /* Read the 16 data bits. */
+ for (i = 16; i > 0; i--) {
+ outl(0, mdio_addr);
+ sis900_mdio_delay();
+ retval = (retval << 1) | ((inl(mdio_addr) & MDIO) ? 1 : 0);
+ outl(MDC, mdio_addr);
+ sis900_mdio_delay();
+ }
+ outl(0x00, mdio_addr);
+ return retval;
+}
+
+#if 0
+static void sis900_mdio_write(int phy_id, int location, int value)
+{
+ long mdio_addr = ioaddr + mear;
+ int mii_cmd = MIIwrite|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
+ int i;
+
+ sis900_mdio_reset(mdio_addr);
+ sis900_mdio_idle(mdio_addr);
+
+ /* Shift the command bits out. */
+ for (i = 15; i >= 0; i--) {
+ int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR;
+ outb(dataval, mdio_addr);
+ sis900_mdio_delay();
+ outb(dataval | MDC, mdio_addr);
+ sis900_mdio_delay();
+ }
+ sis900_mdio_delay();
+
+ /* Shift the value bits out. */
+ for (i = 15; i >= 0; i--) {
+ int dataval = (value & (1 << i)) ? MDDIR | MDIO : MDDIR;
+ outl(dataval, mdio_addr);
+ sis900_mdio_delay();
+ outl(dataval | MDC, mdio_addr);
+ sis900_mdio_delay();
+ }
+ sis900_mdio_delay();
+
+ /* Clear out extra bits. */
+ for (i = 2; i > 0; i--) {
+ outb(0, mdio_addr);
+ sis900_mdio_delay();
+ outb(MDC, mdio_addr);
+ sis900_mdio_delay();
+ }
+ outl(0x00, mdio_addr);
+ return;
+}
+#endif
+
+
+/* Function: sis900_init
+ *
+ * Description: resets the ethernet controller chip and various
+ * data structures required for sending and receiving packets.
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * returns: void.
+ */
+
+static void
+sis900_init(struct nic *nic)
+{
+ /* Soft reset the chip. */
+ sis900_reset(nic);
+
+ sis900_init_rxfilter(nic);
+
+ sis900_init_txd(nic);
+ sis900_init_rxd(nic);
+
+ sis900_set_rx_mode(nic);
+
+ sis900_check_mode(nic);
+
+ outl(RxENA| inl(ioaddr + cr), ioaddr + cr);
+}
+
+
+/*
+ * Function: sis900_reset
+ *
+ * Description: disables interrupts and soft resets the controller chip
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: void.
+ */
+
+static void
+sis900_reset(struct nic *nic __unused)
+{
+ int i = 0;
+ u32 status = TxRCMP | RxRCMP;
+
+ outl(0, ioaddr + ier);
+ outl(0, ioaddr + imr);
+ outl(0, ioaddr + rfcr);
+
+ outl(RxRESET | TxRESET | RESET | inl(ioaddr + cr), ioaddr + cr);
+
+ /* Check that the chip has finished the reset. */
+ while (status && (i++ < 1000)) {
+ status ^= (inl(isr + ioaddr) & status);
+ }
+
+ if( (pci_revision >= SIS635A_900_REV) || (pci_revision == SIS900B_900_REV) )
+ outl(PESEL | RND_CNT, ioaddr + cfg);
+ else
+ outl(PESEL, ioaddr + cfg);
+}
+
+
+/* Function: sis_init_rxfilter
+ *
+ * Description: sets receive filter address to our MAC address
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * returns: void.
+ */
+
+static void
+sis900_init_rxfilter(struct nic *nic)
+{
+ u32 rfcrSave;
+ int i;
+
+ rfcrSave = inl(rfcr + ioaddr);
+
+ /* disable packet filtering before setting filter */
+ outl(rfcrSave & ~RFEN, rfcr + ioaddr);
+
+ /* load MAC addr to filter data register */
+ for (i = 0 ; i < 3 ; i++) {
+ u32 w;
+
+ w = (u32) *((u16 *)(nic->node_addr)+i);
+ outl((i << RFADDR_shift), ioaddr + rfcr);
+ outl(w, ioaddr + rfdr);
+
+ if (sis900_debug > 0)
+ printf("sis900_init_rxfilter: Receive Filter Addrss[%d]=%X\n",
+ i, inl(ioaddr + rfdr));
+ }
+
+ /* enable packet filitering */
+ outl(rfcrSave | RFEN, rfcr + ioaddr);
+}
+
+
+/*
+ * Function: sis_init_txd
+ *
+ * Description: initializes the Tx descriptor
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * returns: void.
+ */
+
+static void
+sis900_init_txd(struct nic *nic __unused)
+{
+ txd.link = (u32) 0;
+ txd.cmdsts = (u32) 0;
+ txd.bufptr = virt_to_bus(&txb[0]);
+
+ /* load Transmit Descriptor Register */
+ outl(virt_to_bus(&txd), ioaddr + txdp);
+ if (sis900_debug > 0)
+ printf("sis900_init_txd: TX descriptor register loaded with: %X\n",
+ inl(ioaddr + txdp));
+}
+
+
+/* Function: sis_init_rxd
+ *
+ * Description: initializes the Rx descriptor ring
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: void.
+ */
+
+static void
+sis900_init_rxd(struct nic *nic __unused)
+{
+ int i;
+
+ cur_rx = 0;
+
+ /* init RX descriptor */
+ for (i = 0; i < NUM_RX_DESC; i++) {
+ rxd[i].link = virt_to_bus((i+1 < NUM_RX_DESC) ? &rxd[i+1] : &rxd[0]);
+ rxd[i].cmdsts = (u32) RX_BUF_SIZE;
+ rxd[i].bufptr = virt_to_bus(&rxb[i*RX_BUF_SIZE]);
+ if (sis900_debug > 0)
+ printf("sis900_init_rxd: rxd[%d]=%X link=%X cmdsts=%X bufptr=%X\n",
+ i, &rxd[i], rxd[i].link, rxd[i].cmdsts, rxd[i].bufptr);
+ }
+
+ /* load Receive Descriptor Register */
+ outl(virt_to_bus(&rxd[0]), ioaddr + rxdp);
+
+ if (sis900_debug > 0)
+ printf("sis900_init_rxd: RX descriptor register loaded with: %X\n",
+ inl(ioaddr + rxdp));
+
+}
+
+
+/* Function: sis_init_rxd
+ *
+ * Description:
+ * sets the receive mode to accept all broadcast packets and packets
+ * with our MAC address, and reject all multicast packets.
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: void.
+ */
+
+static void sis900_set_rx_mode(struct nic *nic __unused)
+{
+ int i, table_entries;
+ u32 rx_mode;
+ u16 mc_filter[16] = {0}; /* 256/128 bits multicast hash table */
+
+ if((pci_revision == SIS635A_900_REV) || (pci_revision == SIS900B_900_REV))
+ table_entries = 16;
+ else
+ table_entries = 8;
+
+ /* accept all multicast packet */
+ rx_mode = RFAAB | RFAAM;
+ for (i = 0; i < table_entries; i++)
+ mc_filter[i] = 0xffff;
+
+ /* update Multicast Hash Table in Receive Filter */
+ for (i = 0; i < table_entries; i++) {
+ /* why plus 0x04? That makes the correct value for hash table. */
+ outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr);
+ outl(mc_filter[i], ioaddr + rfdr);
+ }
+
+ /* Accept Broadcast and multicast packets, destination addresses that match
+ our MAC address */
+ outl(RFEN | rx_mode, ioaddr + rfcr);
+
+ return;
+}
+
+
+/* Function: sis900_check_mode
+ *
+ * Description: checks the state of transmit and receive
+ * parameters on the NIC, and updates NIC registers to match
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: void.
+ */
+
+static void
+sis900_check_mode(struct nic *nic)
+{
+ int speed, duplex;
+ u32 tx_flags = 0, rx_flags = 0;
+
+ mii.chip_info->read_mode(nic, cur_phy, &speed, &duplex);
+
+ if( inl(ioaddr + cfg) & EDB_MASTER_EN ) {
+ tx_flags = TxATP | (DMA_BURST_64 << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift);
+ rx_flags = DMA_BURST_64 << RxMXDMA_shift;
+ }
+ else {
+ tx_flags = TxATP | (DMA_BURST_512 << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift);
+ rx_flags = DMA_BURST_512 << RxMXDMA_shift;
+ }
+
+ if (speed == HW_SPEED_HOME || speed == HW_SPEED_10_MBPS) {
+ rx_flags |= (RxDRNT_10 << RxDRNT_shift);
+ tx_flags |= (TxDRNT_10 << TxDRNT_shift);
+ }
+ else {
+ rx_flags |= (RxDRNT_100 << RxDRNT_shift);
+ tx_flags |= (TxDRNT_100 << TxDRNT_shift);
+ }
+
+ if (duplex == FDX_CAPABLE_FULL_SELECTED) {
+ tx_flags |= (TxCSI | TxHBI);
+ rx_flags |= RxATX;
+ }
+
+ outl (tx_flags, ioaddr + txcfg);
+ outl (rx_flags, ioaddr + rxcfg);
+}
+
+
+/* Function: sis900_read_mode
+ *
+ * Description: retrieves and displays speed and duplex
+ * parameters from the NIC
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: void.
+ */
+
+static void
+sis900_read_mode(struct nic *nic __unused, int phy_addr, int *speed, int *duplex)
+{
+ int i = 0;
+ u32 status;
+ u16 phy_id0, phy_id1;
+
+ /* STSOUT register is Latched on Transition, read operation updates it */
+ while (i++ < 2)
+ status = sis900_mdio_read(phy_addr, MII_STSOUT);
+
+ *speed = HW_SPEED_10_MBPS;
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+
+ if (status & (MII_NWAY_TX | MII_NWAY_TX_FDX))
+ *speed = HW_SPEED_100_MBPS;
+ if (status & ( MII_NWAY_TX_FDX | MII_NWAY_T_FDX))
+ *duplex = FDX_CAPABLE_FULL_SELECTED;
+
+ /* Workaround for Realtek RTL8201 PHY issue */
+ phy_id0 = sis900_mdio_read(phy_addr, MII_PHY_ID0);
+ phy_id1 = sis900_mdio_read(phy_addr, MII_PHY_ID1);
+ if((phy_id0 == 0x0000) && ((phy_id1 & 0xFFF0) == 0x8200)){
+ if(sis900_mdio_read(phy_addr, MII_CONTROL) & MII_CNTL_FDX)
+ *duplex = FDX_CAPABLE_FULL_SELECTED;
+ if(sis900_mdio_read(phy_addr, 0x0019) & 0x01)
+ *speed = HW_SPEED_100_MBPS;
+ }
+
+ if (status & MII_STSOUT_LINK_FAIL)
+ printf("sis900_read_mode: Media Link Off\n");
+ else
+ printf("sis900_read_mode: Media Link On %s %s-duplex \n",
+ *speed == HW_SPEED_100_MBPS ?
+ "100mbps" : "10mbps",
+ *duplex == FDX_CAPABLE_FULL_SELECTED ?
+ "full" : "half");
+}
+
+
+/* Function: amd79c901_read_mode
+ *
+ * Description: retrieves and displays speed and duplex
+ * parameters from the NIC
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: void.
+ */
+
+static void
+amd79c901_read_mode(struct nic *nic __unused, int phy_addr, int *speed, int *duplex)
+{
+ int i;
+ u16 status;
+
+ for (i = 0; i < 2; i++)
+ status = sis900_mdio_read(phy_addr, MII_STATUS);
+
+ if (status & MII_STAT_CAN_AUTO) {
+ /* 10BASE-T PHY */
+ for (i = 0; i < 2; i++)
+ status = sis900_mdio_read(phy_addr, MII_STATUS_SUMMARY);
+ if (status & MII_STSSUM_SPD)
+ *speed = HW_SPEED_100_MBPS;
+ else
+ *speed = HW_SPEED_10_MBPS;
+ if (status & MII_STSSUM_DPLX)
+ *duplex = FDX_CAPABLE_FULL_SELECTED;
+ else
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+
+ if (status & MII_STSSUM_LINK)
+ printf("amd79c901_read_mode: Media Link On %s %s-duplex \n",
+ *speed == HW_SPEED_100_MBPS ?
+ "100mbps" : "10mbps",
+ *duplex == FDX_CAPABLE_FULL_SELECTED ?
+ "full" : "half");
+ else
+ printf("amd79c901_read_mode: Media Link Off\n");
+ }
+ else {
+ /* HomePNA */
+ *speed = HW_SPEED_HOME;
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+ if (status & MII_STAT_LINK)
+ printf("amd79c901_read_mode:Media Link On 1mbps half-duplex \n");
+ else
+ printf("amd79c901_read_mode: Media Link Off\n");
+ }
+}
+
+
+/**
+ * ics1893_read_mode: - read media mode for ICS1893 PHY
+ * @net_dev: the net device to read mode for
+ * @phy_addr: mii phy address
+ * @speed: the transmit speed to be determined
+ * @duplex: the duplex mode to be determined
+ *
+ * ICS1893 PHY use Quick Poll Detailed Status register
+ * to determine the speed and duplex mode for sis900
+ */
+
+static void ics1893_read_mode(struct nic *nic __unused, int phy_addr, int *speed, int *duplex)
+{
+ int i = 0;
+ u32 status;
+
+ /* MII_QPDSTS is Latched, read twice in succession will reflect the current state */
+ for (i = 0; i < 2; i++)
+ status = sis900_mdio_read(phy_addr, MII_QPDSTS);
+
+ if (status & MII_STSICS_SPD)
+ *speed = HW_SPEED_100_MBPS;
+ else
+ *speed = HW_SPEED_10_MBPS;
+
+ if (status & MII_STSICS_DPLX)
+ *duplex = FDX_CAPABLE_FULL_SELECTED;
+ else
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+
+ if (status & MII_STSICS_LINKSTS)
+ printf("ics1893_read_mode: Media Link On %s %s-duplex \n",
+ *speed == HW_SPEED_100_MBPS ?
+ "100mbps" : "10mbps",
+ *duplex == FDX_CAPABLE_FULL_SELECTED ?
+ "full" : "half");
+ else
+ printf("ics1893_read_mode: Media Link Off\n");
+}
+
+/**
+ * rtl8201_read_mode: - read media mode for rtl8201 phy
+ * @nic: the net device to read mode for
+ * @phy_addr: mii phy address
+ * @speed: the transmit speed to be determined
+ * @duplex: the duplex mode to be determined
+ *
+ * read MII_STATUS register from rtl8201 phy
+ * to determine the speed and duplex mode for sis900
+ */
+
+static void rtl8201_read_mode(struct nic *nic __unused, int phy_addr, int *speed, int *duplex)
+{
+ u32 status;
+
+ status = sis900_mdio_read(phy_addr, MII_STATUS);
+
+ if (status & MII_STAT_CAN_TX_FDX) {
+ *speed = HW_SPEED_100_MBPS;
+ *duplex = FDX_CAPABLE_FULL_SELECTED;
+ }
+ else if (status & MII_STAT_CAN_TX) {
+ *speed = HW_SPEED_100_MBPS;
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+ }
+ else if (status & MII_STAT_CAN_T_FDX) {
+ *speed = HW_SPEED_10_MBPS;
+ *duplex = FDX_CAPABLE_FULL_SELECTED;
+ }
+ else if (status & MII_STAT_CAN_T) {
+ *speed = HW_SPEED_10_MBPS;
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+ }
+
+ if (status & MII_STAT_LINK)
+ printf("rtl8201_read_mode: Media Link On %s %s-duplex \n",
+ *speed == HW_SPEED_100_MBPS ?
+ "100mbps" : "10mbps",
+ *duplex == FDX_CAPABLE_FULL_SELECTED ?
+ "full" : "half");
+ else
+ printf("rtl8201_read_config_mode: Media Link Off\n");
+}
+
+/**
+ * vt6103_read_mode: - read media mode for vt6103 phy
+ * @nic: the net device to read mode for
+ * @phy_addr: mii phy address
+ * @speed: the transmit speed to be determined
+ * @duplex: the duplex mode to be determined
+ *
+ * read MII_STATUS register from rtl8201 phy
+ * to determine the speed and duplex mode for sis900
+ */
+
+static void vt6103_read_mode(struct nic *nic __unused, int phy_addr, int *speed, int *duplex)
+{
+ u32 status;
+
+ status = sis900_mdio_read(phy_addr, MII_STATUS);
+
+ if (status & MII_STAT_CAN_TX_FDX) {
+ *speed = HW_SPEED_100_MBPS;
+ *duplex = FDX_CAPABLE_FULL_SELECTED;
+ }
+ else if (status & MII_STAT_CAN_TX) {
+ *speed = HW_SPEED_100_MBPS;
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+ }
+ else if (status & MII_STAT_CAN_T_FDX) {
+ *speed = HW_SPEED_10_MBPS;
+ *duplex = FDX_CAPABLE_FULL_SELECTED;
+ }
+ else if (status & MII_STAT_CAN_T) {
+ *speed = HW_SPEED_10_MBPS;
+ *duplex = FDX_CAPABLE_HALF_SELECTED;
+ }
+
+ if (status & MII_STAT_LINK)
+ printf("vt6103_read_mode: Media Link On %s %s-duplex \n",
+ *speed == HW_SPEED_100_MBPS ?
+ "100mbps" : "10mbps",
+ *duplex == FDX_CAPABLE_FULL_SELECTED ?
+ "full" : "half");
+ else
+ printf("vt6103_read_config_mode: Media Link Off\n");
+}
+
+/* Function: sis900_transmit
+ *
+ * Description: transmits a packet and waits for completion or timeout.
+ *
+ * Arguments: char d[6]: destination ethernet address.
+ * unsigned short t: ethernet protocol type.
+ * unsigned short s: size of the data-part of the packet.
+ * char *p: the data for the packet.
+ *
+ * Returns: void.
+ */
+
+static void
+sis900_transmit(struct nic *nic,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+ u32 to, nstype;
+ u32 tx_status;
+
+ /* Stop the transmitter */
+ outl(TxDIS | inl(ioaddr + cr), ioaddr + cr);
+
+ /* load Transmit Descriptor Register */
+ outl(virt_to_bus(&txd), ioaddr + txdp);
+ if (sis900_debug > 1)
+ printf("sis900_transmit: TX descriptor register loaded with: %X\n",
+ inl(ioaddr + txdp));
+
+ memcpy(txb, d, ETH_ALEN);
+ memcpy(txb + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ nstype = htons(t);
+ memcpy(txb + 2 * ETH_ALEN, (char*)&nstype, 2);
+ memcpy(txb + ETH_HLEN, p, s);
+
+ s += ETH_HLEN;
+ s &= DSIZE;
+
+ if (sis900_debug > 1)
+ printf("sis900_transmit: sending %d bytes ethtype %hX\n", (int) s, t);
+
+ /* pad to minimum packet size */
+ while (s < ETH_ZLEN)
+ txb[s++] = '\0';
+
+ /* set the transmit buffer descriptor and enable Transmit State Machine */
+ txd.bufptr = virt_to_bus(&txb[0]);
+ txd.cmdsts = (u32) OWN | s;
+
+ /* restart the transmitter */
+ outl(TxENA | inl(ioaddr + cr), ioaddr + cr);
+
+ if (sis900_debug > 1)
+ printf("sis900_transmit: Queued Tx packet size %d.\n", (int) s);
+
+ to = currticks() + TX_TIMEOUT;
+
+ while ((((volatile u32) tx_status=txd.cmdsts) & OWN) && (currticks() < to))
+ /* wait */ ;
+
+ if (currticks() >= to) {
+ printf("sis900_transmit: TX Timeout! Tx status %X.\n", tx_status);
+ }
+
+ if (tx_status & (ABORT | UNDERRUN | OWCOLL)) {
+ /* packet unsuccessfully transmited */
+ printf("sis900_transmit: Transmit error, Tx status %X.\n", tx_status);
+ }
+ /* Disable interrupts by clearing the interrupt mask. */
+ outl(0, ioaddr + imr);
+}
+
+
+/* Function: sis900_poll
+ *
+ * Description: checks for a received packet and returns it if found.
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: 1 if a packet was recieved.
+ * 0 if no pacet was recieved.
+ *
+ * Side effects:
+ * Returns (copies) the packet to the array nic->packet.
+ * Returns the length of the packet in nic->packetlen.
+ */
+
+static int
+sis900_poll(struct nic *nic, int retrieve)
+{
+ u32 rx_status = rxd[cur_rx].cmdsts;
+ int retstat = 0;
+
+ if (sis900_debug > 2)
+ printf("sis900_poll: cur_rx:%d, status:%X\n", cur_rx, rx_status);
+
+ if (!(rx_status & OWN))
+ return retstat;
+
+ if (sis900_debug > 1)
+ printf("sis900_poll: got a packet: cur_rx:%d, status:%X\n",
+ cur_rx, rx_status);
+
+ if ( ! retrieve ) return 1;
+
+ nic->packetlen = (rx_status & DSIZE) - CRC_SIZE;
+
+ if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) {
+ /* corrupted packet received */
+ printf("sis900_poll: Corrupted packet received, buffer status = %X\n",
+ rx_status);
+ retstat = 0;
+ } else {
+ /* give packet to higher level routine */
+ memcpy(nic->packet, (rxb + cur_rx*RX_BUF_SIZE), nic->packetlen);
+ retstat = 1;
+ }
+
+ /* return the descriptor and buffer to receive ring */
+ rxd[cur_rx].cmdsts = RX_BUF_SIZE;
+ rxd[cur_rx].bufptr = virt_to_bus(&rxb[cur_rx*RX_BUF_SIZE]);
+
+ if (++cur_rx == NUM_RX_DESC)
+ cur_rx = 0;
+
+ /* re-enable the potentially idle receive state machine */
+ outl(RxENA | inl(ioaddr + cr), ioaddr + cr);
+
+ return retstat;
+
+}
+
+
+/* Function: sis900_disable
+ *
+ * Description: Turns off interrupts and stops Tx and Rx engines
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ *
+ * Returns: void.
+ */
+
+static void
+sis900_disable(struct dev *dev)
+{
+ struct nic *nic = (struct nic *)dev;
+ /* merge reset and disable */
+ sis900_init(nic);
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ outl(0, ioaddr + imr);
+ outl(0, ioaddr + ier);
+
+ /* Stop the chip's Tx and Rx Status Machine */
+ outl(RxDIS | TxDIS | inl(ioaddr + cr), ioaddr + cr);
+}
+
+
+/* Function: sis900_irq
+ *
+ * Description: Enable, Disable, or Force, interrupts
+ *
+ * Arguments: struct nic *nic: NIC data structure
+ * irq_action_t action: Requested action
+ *
+ * Returns: void.
+ */
+
+static void
+sis900_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+static struct pci_id sis900_nics[] = {
+PCI_ROM(0x1039, 0x0900, "sis900", "SIS900"),
+PCI_ROM(0x1039, 0x7016, "sis7016", "SIS7016"),
+};
+
+static struct pci_driver sis900_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "SIS900",
+ .probe = sis900_probe,
+ .ids = sis900_nics,
+ .id_count = sizeof(sis900_nics)/sizeof(sis900_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/sis900.h b/src/drivers/net/sis900.h
new file mode 100644
index 00000000..89aa3aa5
--- /dev/null
+++ b/src/drivers/net/sis900.h
@@ -0,0 +1,380 @@
+/* -*- Mode:C; c-basic-offset:4; -*- */
+
+/* Definitions for SiS ethernet controllers including 7014/7016 and 900
+ * References:
+ * SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support,
+ * preliminary Rev. 1.0 Jan. 14, 1998
+ * SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support,
+ * preliminary Rev. 1.0 Nov. 10, 1998
+ * SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution,
+ * preliminary Rev. 1.0 Jan. 18, 1998
+ * http://www.sis.com.tw/support/databook.htm
+ */
+
+/* MAC operationl registers of SiS 7016 and SiS 900 ethernet controller */
+/* The I/O extent, SiS 900 needs 256 bytes of io address */
+#define SIS900_TOTAL_SIZE 0x100
+
+/* Symbolic offsets to registers. */
+enum sis900_registers {
+ cr=0x0, /* Command Register */
+ cfg=0x4, /* Configuration Register */
+ mear=0x8, /* EEPROM Access Register */
+ ptscr=0xc, /* PCI Test Control Register */
+ isr=0x10, /* Interrupt Status Register */
+ imr=0x14, /* Interrupt Mask Register */
+ ier=0x18, /* Interrupt Enable Register */
+ epar=0x18, /* Enhanced PHY Access Register */
+ txdp=0x20, /* Transmit Descriptor Pointer Register */
+ txcfg=0x24, /* Transmit Configuration Register */
+ rxdp=0x30, /* Receive Descriptor Pointer Register */
+ rxcfg=0x34, /* Receive Configuration Register */
+ flctrl=0x38, /* Flow Control Register */
+ rxlen=0x3c, /* Receive Packet Length Register */
+ rfcr=0x48, /* Receive Filter Control Register */
+ rfdr=0x4C, /* Receive Filter Data Register */
+ pmctrl=0xB0, /* Power Management Control Register */
+ pmer=0xB4 /* Power Management Wake-up Event Register */
+};
+
+/* Symbolic names for bits in various registers */
+enum sis900_command_register_bits {
+ RELOAD = 0x00000400,
+ ACCESSMODE = 0x00000200,
+ RESET = 0x00000100,
+ SWI = 0x00000080,
+ RxRESET = 0x00000020,
+ TxRESET = 0x00000010,
+ RxDIS = 0x00000008,
+ RxENA = 0x00000004,
+ TxDIS = 0x00000002,
+ TxENA = 0x00000001
+};
+
+enum sis900_configuration_register_bits {
+ DESCRFMT = 0x00000100, /* 7016 specific */
+ REQALG = 0x00000080,
+ SB = 0x00000040,
+ POW = 0x00000020,
+ EXD = 0x00000010,
+ PESEL = 0x00000008,
+ LPM = 0x00000004,
+ BEM = 0x00000001,
+ RND_CNT = 0x00000400,
+ FAIR_BACKOFF = 0x00000200,
+ EDB_MASTER_EN = 0x00002000
+};
+
+enum sis900_eeprom_access_reigster_bits {
+ MDC = 0x00000040,
+ MDDIR = 0x00000020,
+ MDIO = 0x00000010, /* 7016 specific */
+ EECS = 0x00000008,
+ EECLK = 0x00000004,
+ EEDO = 0x00000002,
+ EEDI = 0x00000001
+};
+
+enum sis900_interrupt_register_bits {
+ WKEVT = 0x10000000,
+ TxPAUSEEND = 0x08000000,
+ TxPAUSE = 0x04000000,
+ TxRCMP = 0x02000000,
+ RxRCMP = 0x01000000,
+ DPERR = 0x00800000,
+ SSERR = 0x00400000,
+ RMABT = 0x00200000,
+ RTABT = 0x00100000,
+ RxSOVR = 0x00010000,
+ HIBERR = 0x00008000,
+ SWINT = 0x00001000,
+ MIBINT = 0x00000800,
+ TxURN = 0x00000400,
+ TxIDLE = 0x00000200,
+ TxERR = 0x00000100,
+ TxDESC = 0x00000080,
+ TxOK = 0x00000040,
+ RxORN = 0x00000020,
+ RxIDLE = 0x00000010,
+ RxEARLY = 0x00000008,
+ RxERR = 0x00000004,
+ RxDESC = 0x00000002,
+ RxOK = 0x00000001
+};
+
+enum sis900_interrupt_enable_reigster_bits {
+ IE = 0x00000001
+};
+
+/* maximum dma burst fro transmission and receive*/
+#define MAX_DMA_RANGE 7 /* actually 0 means MAXIMUM !! */
+#define TxMXDMA_shift 20
+#define RxMXDMA_shift 20
+#define TX_DMA_BURST 0
+#define RX_DMA_BURST 0
+
+enum sis900_tx_rx_dma{
+ DMA_BURST_512 = 0, DMA_BURST_64 = 5
+};
+
+/* transmit FIFO threshholds */
+#define TX_FILL_THRESH 16 /* 1/4 FIFO size */
+#define TxFILLT_shift 8
+#define TxDRNT_shift 0
+#define TxDRNT_100 48 /* 3/4 FIFO size */
+#define TxDRNT_10 16 /* 1/2 FIFO size */
+
+enum sis900_transmit_config_register_bits {
+ TxCSI = 0x80000000,
+ TxHBI = 0x40000000,
+ TxMLB = 0x20000000,
+ TxATP = 0x10000000,
+ TxIFG = 0x0C000000,
+ TxFILLT = 0x00003F00,
+ TxDRNT = 0x0000003F
+};
+
+/* recevie FIFO thresholds */
+#define RxDRNT_shift 1
+#define RxDRNT_100 16 /* 1/2 FIFO size */
+#define RxDRNT_10 24 /* 3/4 FIFO size */
+
+enum sis900_reveive_config_register_bits {
+ RxAEP = 0x80000000,
+ RxARP = 0x40000000,
+ RxATX = 0x10000000,
+ RxAJAB = 0x08000000,
+ RxDRNT = 0x0000007F
+};
+
+#define RFAA_shift 28
+#define RFADDR_shift 16
+
+enum sis900_receive_filter_control_register_bits {
+ RFEN = 0x80000000,
+ RFAAB = 0x40000000,
+ RFAAM = 0x20000000,
+ RFAAP = 0x10000000,
+ RFPromiscuous = (RFAAB|RFAAM|RFAAP)
+};
+
+enum sis900_reveive_filter_data_mask {
+ RFDAT = 0x0000FFFF
+};
+
+/* EEPROM Addresses */
+enum sis900_eeprom_address {
+ EEPROMSignature = 0x00,
+ EEPROMVendorID = 0x02,
+ EEPROMDeviceID = 0x03,
+ EEPROMMACAddr = 0x08,
+ EEPROMChecksum = 0x0b
+};
+
+/* The EEPROM commands include the alway-set leading bit. Refer to NM93Cxx datasheet */
+enum sis900_eeprom_command {
+ EEread = 0x0180,
+ EEwrite = 0x0140,
+ EEerase = 0x01C0,
+ EEwriteEnable = 0x0130,
+ EEwriteDisable = 0x0100,
+ EEeraseAll = 0x0120,
+ EEwriteAll = 0x0110,
+ EEaddrMask = 0x013F,
+ EEcmdShift = 16
+};
+/* For SiS962 or SiS963, request the eeprom software access */
+enum sis96x_eeprom_command {
+ EEREQ = 0x00000400, EEDONE = 0x00000200, EEGNT = 0x00000100
+};
+
+/* Manamgement Data I/O (mdio) frame */
+#define MIIread 0x6000
+#define MIIwrite 0x5002
+#define MIIpmdShift 7
+#define MIIregShift 2
+#define MIIcmdLen 16
+#define MIIcmdShift 16
+
+/* Buffer Descriptor Status*/
+enum sis900_buffer_status {
+ OWN = 0x80000000,
+ MORE = 0x40000000,
+ INTR = 0x20000000,
+ SUPCRC = 0x10000000,
+ INCCRC = 0x10000000,
+ OK = 0x08000000,
+ DSIZE = 0x00000FFF
+};
+
+/* Status for TX Buffers */
+enum sis900_tx_buffer_status {
+ ABORT = 0x04000000,
+ UNDERRUN = 0x02000000,
+ NOCARRIER = 0x01000000,
+ DEFERD = 0x00800000,
+ EXCDEFER = 0x00400000,
+ OWCOLL = 0x00200000,
+ EXCCOLL = 0x00100000,
+ COLCNT = 0x000F0000
+};
+
+enum sis900_rx_bufer_status {
+ OVERRUN = 0x02000000,
+ DEST = 0x00800000,
+ BCAST = 0x01800000,
+ MCAST = 0x01000000,
+ UNIMATCH = 0x00800000,
+ TOOLONG = 0x00400000,
+ RUNT = 0x00200000,
+ RXISERR = 0x00100000,
+ CRCERR = 0x00080000,
+ FAERR = 0x00040000,
+ LOOPBK = 0x00020000,
+ RXCOL = 0x00010000
+};
+
+/* MII register offsets */
+enum mii_registers {
+ MII_CONTROL = 0x0000,
+ MII_STATUS = 0x0001,
+ MII_PHY_ID0 = 0x0002,
+ MII_PHY_ID1 = 0x0003,
+ MII_ANADV = 0x0004,
+ MII_ANLPAR = 0x0005,
+ MII_ANEXT = 0x0006
+};
+
+/* mii registers specific to SiS 900 */
+enum sis_mii_registers {
+ MII_CONFIG1 = 0x0010,
+ MII_CONFIG2 = 0x0011,
+ MII_STSOUT = 0x0012,
+ MII_MASK = 0x0013,
+ MII_RESV = 0x0014
+};
+
+/* mii registers specific to AMD 79C901 */
+enum amd_mii_registers {
+ MII_STATUS_SUMMARY = 0x0018
+};
+
+/* mii registers specific to ICS 1893 */
+enum ics_mii_registers {
+ MII_EXTCTRL = 0x0010, MII_QPDSTS = 0x0011, MII_10BTOP = 0x0012,
+ MII_EXTCTRL2 = 0x0013
+};
+
+
+
+/* MII Control register bit definitions. */
+enum mii_control_register_bits {
+ MII_CNTL_FDX = 0x0100,
+ MII_CNTL_RST_AUTO = 0x0200,
+ MII_CNTL_ISOLATE = 0x0400,
+ MII_CNTL_PWRDWN = 0x0800,
+ MII_CNTL_AUTO = 0x1000,
+ MII_CNTL_SPEED = 0x2000,
+ MII_CNTL_LPBK = 0x4000,
+ MII_CNTL_RESET = 0x8000
+};
+
+/* MII Status register bit */
+enum mii_status_register_bits {
+ MII_STAT_EXT = 0x0001,
+ MII_STAT_JAB = 0x0002,
+ MII_STAT_LINK = 0x0004,
+ MII_STAT_CAN_AUTO = 0x0008,
+ MII_STAT_FAULT = 0x0010,
+ MII_STAT_AUTO_DONE = 0x0020,
+ MII_STAT_CAN_T = 0x0800,
+ MII_STAT_CAN_T_FDX = 0x1000,
+ MII_STAT_CAN_TX = 0x2000,
+ MII_STAT_CAN_TX_FDX = 0x4000,
+ MII_STAT_CAN_T4 = 0x8000
+};
+
+#define MII_ID1_OUI_LO 0xFC00 /* low bits of OUI mask */
+#define MII_ID1_MODEL 0x03F0 /* model number */
+#define MII_ID1_REV 0x000F /* model number */
+
+/* MII NWAY Register Bits ...
+ valid for the ANAR (Auto-Negotiation Advertisement) and
+ ANLPAR (Auto-Negotiation Link Partner) registers */
+enum mii_nway_register_bits {
+ MII_NWAY_NODE_SEL = 0x001f,
+ MII_NWAY_CSMA_CD = 0x0001,
+ MII_NWAY_T = 0x0020,
+ MII_NWAY_T_FDX = 0x0040,
+ MII_NWAY_TX = 0x0080,
+ MII_NWAY_TX_FDX = 0x0100,
+ MII_NWAY_T4 = 0x0200,
+ MII_NWAY_PAUSE = 0x0400,
+ MII_NWAY_RF = 0x2000,
+ MII_NWAY_ACK = 0x4000,
+ MII_NWAY_NP = 0x8000
+};
+
+enum mii_stsout_register_bits {
+ MII_STSOUT_LINK_FAIL = 0x4000,
+ MII_STSOUT_SPD = 0x0080,
+ MII_STSOUT_DPLX = 0x0040
+};
+
+enum mii_stsics_register_bits {
+ MII_STSICS_SPD = 0x8000, MII_STSICS_DPLX = 0x4000,
+ MII_STSICS_LINKSTS = 0x0001
+};
+
+enum mii_stssum_register_bits {
+ MII_STSSUM_LINK = 0x0008,
+ MII_STSSUM_DPLX = 0x0004,
+ MII_STSSUM_AUTO = 0x0002,
+ MII_STSSUM_SPD = 0x0001
+};
+
+enum sis900_revision_id {
+ SIS630A_900_REV = 0x80, SIS630E_900_REV = 0x81,
+ SIS630S_900_REV = 0x82, SIS630EA1_900_REV = 0x83,
+ SIS630ET_900_REV = 0x84, SIS635A_900_REV = 0x90,
+ SIS96x_900_REV = 0X91, SIS900B_900_REV = 0x03
+};
+
+enum sis630_revision_id {
+ SIS630A0 = 0x00, SIS630A1 = 0x01,
+ SIS630B0 = 0x10, SIS630B1 = 0x11
+};
+
+#define FDX_CAPABLE_DUPLEX_UNKNOWN 0
+#define FDX_CAPABLE_HALF_SELECTED 1
+#define FDX_CAPABLE_FULL_SELECTED 2
+
+#define HW_SPEED_UNCONFIG 0
+#define HW_SPEED_HOME 1
+#define HW_SPEED_10_MBPS 10
+#define HW_SPEED_100_MBPS 100
+#define HW_SPEED_DEFAULT (HW_SPEED_100_MBPS)
+
+#define CRC_SIZE 4
+#define MAC_HEADER_SIZE 14
+
+#define TX_BUF_SIZE 1536
+#define RX_BUF_SIZE 1536
+
+#define NUM_RX_DESC 4 /* Number of Rx descriptor registers. */
+
+typedef unsigned char u8;
+typedef signed char s8;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned int u32;
+typedef signed int s32;
+
+/* Time in ticks before concluding the transmitter is hung. */
+#define TX_TIMEOUT (4*TICKS_PER_SEC)
+
+typedef struct _BufferDesc {
+ u32 link;
+ volatile u32 cmdsts;
+ u32 bufptr;
+} BufferDesc;
diff --git a/src/drivers/net/sis900.txt b/src/drivers/net/sis900.txt
new file mode 100644
index 00000000..822da0cd
--- /dev/null
+++ b/src/drivers/net/sis900.txt
@@ -0,0 +1,91 @@
+How I added the SIS900 card to Etherboot
+
+Author: Marty Connor (mdc@thinguin.org)
+
+Date: 25 Febrary 2001
+
+Description:
+
+This file is intended to help people who want to write an Etherboot
+driver or port another driver to Etherboot. It is a starting point.
+Perhaps someday I may write a more detailed description of writing an
+Etherboot driver. This text should help get people started, and
+studying sis900.[ch] should help show the basic structure and
+techniques involved in writing and Etherboot driver.
+
+***********************************************************************
+
+0. Back up all the files I need to modify:
+
+cd etherboot-4.7.20/src
+cp Makefile Makefile.orig
+cp config.c config.c.orig
+cp pci.h pci.h.orig
+cp NIC NIC.orig
+cp cards.h cards.h.orig
+
+1. Edit src/Makefile to add SIS900FLAGS to defines
+
+SIS900FLAGS= -DINCLUDE_SIS900
+
+2. edit src/pci.h to add PCI signatures for card
+
+#define PCI_VENDOR_ID_SIS 0x1039
+#define PCI_DEVICE_ID_SIS900 0x0900
+#define PCI_DEVICE_ID_SIS7016 0x7016
+
+3. Edit src/config.c to add the card to the card probe list
+
+#if defined(INCLUDE_NS8390) || defined(INCLUDE_EEPRO100) ||
+ defined(INCLUDE_LANCE) || defined(INCLUDE_EPIC100) ||
+ defined(INCLUDE_TULIP) || defined(INCLUDE_OTULIP) ||
+ defined(INCLUDE_3C90X) || defined(INCLUDE_3C595) ||
+ defined(INCLUDE_RTL8139) || defined(INCLUDE_VIA_RHINE) ||
+ defined(INCLUDE_SIS900) || defined(INCLUDE_W89C840)
+
+... and ...
+
+#ifdef INCLUDE_SIS900
+ { PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS900,
+ "SIS900", 0, 0, 0, 0},
+ { PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS7016,
+ "SIS7016", 0, 0, 0, 0},
+#endif
+
+... and ...
+
+#ifdef INCLUDE_SIS900
+ { "SIS900", sis900_probe, pci_ioaddrs },
+#endif
+
+4. Edit NIC to add sis900 and sis7016 to NIC list
+
+# SIS 900 and SIS 7016
+sis900 sis900 0x1039,0x0900
+sis7016 sis900 0x1039,0x7016
+
+5. Edit cards.h to add sis900 probe routine declaration
+
+#ifdef INCLUDE_SIS900
+extern struct nic *sis900_probe(struct nic *, unsigned short *
+ PCI_ARG(struct pci_device *));
+#endif
+
+***********************************************************************
+
+At this point, you can begin creating your driver source file. See
+the "Writing an Etherboot Driver" section of the Etherboot
+documentation for some hints. See the skel.c file for a starting
+point. If there is a Linux driver for the card, you may be able to
+use that. Copy and learn from existing Etherboot drivers (this is GPL
+/ Open Source software!).
+
+Join the etherboot-developers and etherboot-users mailing lists
+(information is on http://etherboot.sourceforge.net) for information and
+assistance. We invite more developers to help improve Etherboot.
+
+Visit the http://etherboot.sourceforge.net, http://thinguin.org,
+http://rom-o-matic.net, and http://ltsp.org sites for information and
+assistance.
+
+Enjoy.
diff --git a/src/drivers/net/sk_g16.c b/src/drivers/net/sk_g16.c
new file mode 100644
index 00000000..6591c020
--- /dev/null
+++ b/src/drivers/net/sk_g16.c
@@ -0,0 +1,1189 @@
+/* Uses lance chip, not fixed for relocation */
+#ifdef ALLMULTI
+#error multicast support is not yet implemented
+#endif
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+Schneider & Koch G16 NIC driver for Etherboot
+heavily based on SK G16 driver from Linux 2.0.36
+Changes to make it work with Etherboot by Georg Baum <Georg.Baum@gmx.de>
+***************************************************************************/
+
+/*-
+ * Copyright (C) 1994 by PJD Weichmann & SWS Bern, Switzerland
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * Module : sk_g16.c
+ *
+ * Version : $Revision$
+ *
+ * Author : Patrick J.D. Weichmann
+ *
+ * Date Created : 94/05/26
+ * Last Updated : $Date$
+ *
+ * Description : Schneider & Koch G16 Ethernet Device Driver for
+ * Linux Kernel >= 1.1.22
+ * Update History :
+ *
+-*/
+
+/*
+ * The Schneider & Koch (SK) G16 Network device driver is based
+ * on the 'ni6510' driver from Michael Hipp which can be found at
+ * ftp://sunsite.unc.edu/pub/Linux/system/Network/drivers/nidrivers.tar.gz
+ *
+ * Sources: 1) ni6510.c by M. Hipp
+ * 2) depca.c by D.C. Davies
+ * 3) skeleton.c by D. Becker
+ * 4) Am7990 Local Area Network Controller for Ethernet (LANCE),
+ * AMD, Pub. #05698, June 1989
+ *
+ * Many Thanks for helping me to get things working to:
+ *
+ * A. Cox (A.Cox@swansea.ac.uk)
+ * M. Hipp (mhipp@student.uni-tuebingen.de)
+ * R. Bolz (Schneider & Koch, Germany)
+ *
+ * See README.sk_g16 for details about limitations and bugs for the
+ * current version.
+ *
+ * To Do:
+ * - Support of SK_G8 and other SK Network Cards.
+ * - Autoset memory mapped RAM. Check for free memory and then
+ * configure RAM correctly.
+ * - SK_close should really set card in to initial state.
+ * - Test if IRQ 3 is not switched off. Use autoirq() functionality.
+ * (as in /drivers/net/skeleton.c)
+ * - Implement Multicast addressing. At minimum something like
+ * in depca.c.
+ * - Redo the statistics part.
+ * - Try to find out if the board is in 8 Bit or 16 Bit slot.
+ * If in 8 Bit mode don't use IRQ 11.
+ * - (Try to make it slightly faster.)
+ */
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+#include "isa.h"
+
+/* From linux/if_ether.h: */
+#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */
+
+#include "sk_g16.h"
+
+/*
+ * Schneider & Koch Card Definitions
+ * =================================
+ */
+
+#define SK_NAME "SK_G16"
+
+/*
+ * SK_G16 Configuration
+ * --------------------
+ */
+
+/*
+ * Abbreviations
+ * -------------
+ *
+ * RAM - used for the 16KB shared memory
+ * Boot_ROM, ROM - are used for referencing the BootEPROM
+ *
+ * SK_ADDR is a symbolic constant used to configure
+ * the behaviour of the driver and the SK_G16.
+ *
+ * SK_ADDR defines the address where the RAM will be mapped into the real
+ * host memory.
+ * valid addresses are from 0xa0000 to 0xfc000 in 16Kbyte steps.
+ */
+
+#define SK_ADDR 0xcc000
+
+/*
+ * In POS3 are bits A14-A19 of the address bus. These bits can be set
+ * to choose the RAM address. That's why we only can choose the RAM address
+ * in 16KB steps.
+ */
+
+#define POS_ADDR (rom_addr>>14) /* Do not change this line */
+
+/*
+ * SK_G16 I/O PORT's + IRQ's + Boot_ROM locations
+ * ----------------------------------------------
+ */
+
+/*
+ * As nearly every card has also SK_G16 a specified I/O Port region and
+ * only a few possible IRQ's.
+ * In the Installation Guide from Schneider & Koch is listed a possible
+ * Interrupt IRQ2. IRQ2 is always IRQ9 in boards with two cascaded interrupt
+ * controllers. So we use in SK_IRQS IRQ9.
+ */
+
+/* Don't touch any of the following #defines. */
+
+#define SK_IO_PORTS { 0x100, 0x180, 0x208, 0x220, 0x288, 0x320, 0x328, 0x390, 0 }
+
+/*
+ * SK_G16 POS REGISTERS
+ * --------------------
+ */
+
+/*
+ * SK_G16 has a Programmable Option Select (POS) Register.
+ * The POS is composed of 8 separate registers (POS0-7) which
+ * are I/O mapped on an address set by the W1 switch.
+ *
+ */
+
+#define SK_POS_SIZE 8 /* 8 I/O Ports are used by SK_G16 */
+
+#define SK_POS0 ioaddr /* Card-ID Low (R) */
+#define SK_POS1 ioaddr+1 /* Card-ID High (R) */
+#define SK_POS2 ioaddr+2 /* Card-Enable, Boot-ROM Disable (RW) */
+#define SK_POS3 ioaddr+3 /* Base address of RAM */
+#define SK_POS4 ioaddr+4 /* IRQ */
+
+/* POS5 - POS7 are unused */
+
+/*
+ * SK_G16 MAC PREFIX
+ * -----------------
+ */
+
+/*
+ * Scheider & Koch manufacturer code (00:00:a5).
+ * This must be checked, that we are sure it is a SK card.
+ */
+
+#define SK_MAC0 0x00
+#define SK_MAC1 0x00
+#define SK_MAC2 0x5a
+
+/*
+ * SK_G16 ID
+ * ---------
+ */
+
+/*
+ * If POS0,POS1 contain the following ID, then we know
+ * at which I/O Port Address we are.
+ */
+
+#define SK_IDLOW 0xfd
+#define SK_IDHIGH 0x6a
+
+
+/*
+ * LANCE POS Bit definitions
+ * -------------------------
+ */
+
+#define SK_ROM_RAM_ON (POS2_CARD)
+#define SK_ROM_RAM_OFF (POS2_EPROM)
+#define SK_ROM_ON (inb(SK_POS2) & POS2_CARD)
+#define SK_ROM_OFF (inb(SK_POS2) | POS2_EPROM)
+#define SK_RAM_ON (inb(SK_POS2) | POS2_CARD)
+#define SK_RAM_OFF (inb(SK_POS2) & POS2_EPROM)
+
+#define POS2_CARD 0x0001 /* 1 = SK_G16 on 0 = off */
+#define POS2_EPROM 0x0002 /* 1 = Boot EPROM off 0 = on */
+
+/*
+ * SK_G16 Memory mapped Registers
+ * ------------------------------
+ *
+ */
+
+#define SK_IOREG (board->ioreg) /* LANCE data registers. */
+#define SK_PORT (board->port) /* Control, Status register */
+#define SK_IOCOM (board->iocom) /* I/O Command */
+
+/*
+ * SK_G16 Status/Control Register bits
+ * -----------------------------------
+ *
+ * (C) Controlreg (S) Statusreg
+ */
+
+/*
+ * Register transfer: 0 = no transfer
+ * 1 = transferring data between LANCE and I/O reg
+ */
+#define SK_IORUN 0x20
+
+/*
+ * LANCE interrupt: 0 = LANCE interrupt occurred
+ * 1 = no LANCE interrupt occurred
+ */
+#define SK_IRQ 0x10
+
+#define SK_RESET 0x08 /* Reset SK_CARD: 0 = RESET 1 = normal */
+#define SK_RW 0x02 /* 0 = write to 1 = read from */
+#define SK_ADR 0x01 /* 0 = REG DataPort 1 = RAP Reg addr port */
+
+
+#define SK_RREG SK_RW /* Transferdirection to read from lance */
+#define SK_WREG 0 /* Transferdirection to write to lance */
+#define SK_RAP SK_ADR /* Destination Register RAP */
+#define SK_RDATA 0 /* Destination Register REG DataPort */
+
+/*
+ * SK_G16 I/O Command
+ * ------------------
+ */
+
+/*
+ * Any bitcombination sets the internal I/O bit (transfer will start)
+ * when written to I/O Command
+ */
+
+#define SK_DOIO 0x80 /* Do Transfer */
+
+/*
+ * LANCE RAP (Register Address Port).
+ * ---------------------------------
+ */
+
+/*
+ * The LANCE internal registers are selected through the RAP.
+ * The Registers are:
+ *
+ * CSR0 - Status and Control flags
+ * CSR1 - Low order bits of initialize block (bits 15:00)
+ * CSR2 - High order bits of initialize block (bits 07:00, 15:08 are reserved)
+ * CSR3 - Allows redefinition of the Bus Master Interface.
+ * This register must be set to 0x0002, which means BSWAP = 0,
+ * ACON = 1, BCON = 0;
+ *
+ */
+
+#define CSR0 0x00
+#define CSR1 0x01
+#define CSR2 0x02
+#define CSR3 0x03
+
+/*
+ * General Definitions
+ * ===================
+ */
+
+/*
+ * Set the number of Tx and Rx buffers, using Log_2(# buffers).
+ * We have 16KB RAM which can be accessed by the LANCE. In the
+ * memory are not only the buffers but also the ring descriptors and
+ * the initialize block.
+ * Don't change anything unless you really know what you do.
+ */
+
+#define LC_LOG_TX_BUFFERS 1 /* (2 == 2^^1) 2 Transmit buffers */
+#define LC_LOG_RX_BUFFERS 2 /* (8 == 2^^3) 8 Receive buffers */
+
+/* Descriptor ring sizes */
+
+#define TMDNUM (1 << (LC_LOG_TX_BUFFERS)) /* 2 Transmit descriptor rings */
+#define RMDNUM (1 << (LC_LOG_RX_BUFFERS)) /* 8 Receive Buffers */
+
+/* Define Mask for setting RMD, TMD length in the LANCE init_block */
+
+#define TMDNUMMASK (LC_LOG_TX_BUFFERS << 29)
+#define RMDNUMMASK (LC_LOG_RX_BUFFERS << 29)
+
+/*
+ * Data Buffer size is set to maximum packet length.
+ */
+
+#define PKT_BUF_SZ 1518
+
+/*
+ * The number of low I/O ports used by the ethercard.
+ */
+
+#define ETHERCARD_TOTAL_SIZE SK_POS_SIZE
+
+/*
+ * Portreserve is there to mark the Card I/O Port region as used.
+ * Check_region is to check if the region at ioaddr with the size "size"
+ * is free or not.
+ * Snarf_region allocates the I/O Port region.
+ */
+
+#ifndef HAVE_PORTRESERVE
+
+#define check_region(ioaddr1, size) 0
+#define request_region(ioaddr1, size,name) do ; while (0)
+
+#endif
+
+/*
+ * SK_DEBUG
+ *
+ * Here you can choose what level of debugging wanted.
+ *
+ * If SK_DEBUG and SK_DEBUG2 are undefined, then only the
+ * necessary messages will be printed.
+ *
+ * If SK_DEBUG is defined, there will be many debugging prints
+ * which can help to find some mistakes in configuration or even
+ * in the driver code.
+ *
+ * If SK_DEBUG2 is defined, many many messages will be printed
+ * which normally you don't need. I used this to check the interrupt
+ * routine.
+ *
+ * (If you define only SK_DEBUG2 then only the messages for
+ * checking interrupts will be printed!)
+ *
+ * Normal way of live is:
+ *
+ * For the whole thing get going let both symbolic constants
+ * undefined. If you face any problems and you know what's going
+ * on (you know something about the card and you can interpret some
+ * hex LANCE register output) then define SK_DEBUG
+ *
+ */
+
+#undef SK_DEBUG /* debugging */
+#undef SK_DEBUG2 /* debugging with more verbose report */
+
+#ifdef SK_DEBUG
+#define PRINTF(x) printf x
+#else
+#define PRINTF(x) /**/
+#endif
+
+#ifdef SK_DEBUG2
+#define PRINTF2(x) printf x
+#else
+#define PRINTF2(x) /**/
+#endif
+
+/*
+ * SK_G16 RAM
+ *
+ * The components are memory mapped and can be set in a region from
+ * 0x00000 through 0xfc000 in 16KB steps.
+ *
+ * The Network components are: dual ported RAM, Prom, I/O Reg, Status-,
+ * Controlregister and I/O Command.
+ *
+ * dual ported RAM: This is the only memory region which the LANCE chip
+ * has access to. From the Lance it is addressed from 0x0000 to
+ * 0x3fbf. The host accesses it normally.
+ *
+ * PROM: The PROM obtains the ETHERNET-MAC-Address. It is realised as a
+ * 8-Bit PROM, this means only the 16 even addresses are used of the
+ * 32 Byte Address region. Access to a odd address results in invalid
+ * data.
+ *
+ * LANCE I/O Reg: The I/O Reg is build of 4 single Registers, Low-Byte Write,
+ * Hi-Byte Write, Low-Byte Read, Hi-Byte Read.
+ * Transfer from or to the LANCE is always in 16Bit so Low and High
+ * registers are always relevant.
+ *
+ * The Data from the Readregister is not the data in the Writeregister!!
+ *
+ * Port: Status- and Controlregister.
+ * Two different registers which share the same address, Status is
+ * read-only, Control is write-only.
+ *
+ * I/O Command:
+ * Any bitcombination written in here starts the transmission between
+ * Host and LANCE.
+ */
+
+typedef struct
+{
+ unsigned char ram[0x3fc0]; /* 16KB dual ported ram */
+ unsigned char rom[0x0020]; /* 32Byte PROM containing 6Byte MAC */
+ unsigned char res1[0x0010]; /* reserved */
+ unsigned volatile short ioreg;/* LANCE I/O Register */
+ unsigned volatile char port; /* Statusregister and Controlregister */
+ unsigned char iocom; /* I/O Command Register */
+} SK_RAM;
+
+/* struct */
+
+/*
+ * This is the structure for the dual ported ram. We
+ * have exactly 16 320 Bytes. In here there must be:
+ *
+ * - Initialize Block (starting at a word boundary)
+ * - Receive and Transmit Descriptor Rings (quadword boundary)
+ * - Data Buffers (arbitrary boundary)
+ *
+ * This is because LANCE has on SK_G16 only access to the dual ported
+ * RAM and nowhere else.
+ */
+
+struct SK_ram
+{
+ struct init_block ib;
+ struct tmd tmde[TMDNUM];
+ struct rmd rmde[RMDNUM];
+ char tmdbuf[TMDNUM][PKT_BUF_SZ];
+ char rmdbuf[RMDNUM][PKT_BUF_SZ];
+};
+
+/*
+ * Structure where all necessary information is for ring buffer
+ * management and statistics.
+ */
+
+struct priv
+{
+ struct SK_ram *ram; /* dual ported ram structure */
+ struct rmd *rmdhead; /* start of receive ring descriptors */
+ struct tmd *tmdhead; /* start of transmit ring descriptors */
+ int rmdnum; /* actual used ring descriptor */
+ int tmdnum; /* actual transmit descriptor for transmitting data */
+ int tmdlast; /* last sent descriptor used for error handling, etc */
+ void *rmdbufs[RMDNUM]; /* pointer to the receive buffers */
+ void *tmdbufs[TMDNUM]; /* pointer to the transmit buffers */
+};
+
+/* global variable declaration */
+
+/* static variables */
+
+static SK_RAM *board; /* pointer to our memory mapped board components */
+static unsigned short ioaddr; /* base io address */
+static struct priv p_data;
+
+/* Macros */
+
+
+/* Function Prototypes */
+
+/*
+ * Device Driver functions
+ * -----------------------
+ * See for short explanation of each function its definitions header.
+ */
+
+static int SK_probe1(struct nic *nic, short ioaddr1);
+
+static int SK_poll(struct nic *nic, int retrieve);
+static void SK_transmit(
+struct nic *nic,
+const char *d, /* Destination */
+unsigned int t, /* Type */
+unsigned int s, /* size */
+const char *p); /* Packet */
+static void SK_disable(struct dev *dev);
+static int SK_probe(struct dev *dev, unsigned short *probe_addrs);
+
+/*
+ * LANCE Functions
+ * ---------------
+ */
+
+static int SK_lance_init(struct nic *nic, unsigned short mode);
+static void SK_reset_board(void);
+static void SK_set_RAP(int reg_number);
+static int SK_read_reg(int reg_number);
+static int SK_rread_reg(void);
+static void SK_write_reg(int reg_number, int value);
+
+/*
+ * Debugging functions
+ * -------------------
+ */
+
+#ifdef SK_DEBUG
+static void SK_print_pos(struct nic *nic, char *text);
+static void SK_print_ram(struct nic *nic);
+#endif
+
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int SK_poll(struct nic *nic, int retrieve)
+{
+ /* return true if there's an ethernet packet ready to read */
+ struct priv *p; /* SK_G16 private structure */
+ struct rmd *rmdp;
+ int csr0, rmdstat, packet_there;
+ PRINTF2(("## %s: At beginning of SK_poll(). CSR0: %#hX\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ p = nic->priv_data;
+ csr0 = SK_read_reg(CSR0); /* store register for checking */
+
+ rmdp = p->rmdhead + p->rmdnum;
+ packet_there = 0;
+
+ if ( !(rmdp->u.s.status & RX_OWN) && !retrieve ) return 1;
+
+ /*
+ * Acknowledge all of the current interrupt sources, disable
+ * Interrupts (INEA = 0)
+ */
+
+ SK_write_reg(CSR0, csr0 & CSR0_CLRALL);
+
+ if (csr0 & CSR0_ERR) /* LANCE Error */
+ {
+ printf("%s: error: %#hX", SK_NAME, csr0);
+
+ if (csr0 & CSR0_MISS) /* No place to store packet ? */
+ {
+ printf(", Packet dropped.");
+ }
+ putchar('\n');
+ }
+
+ /* As long as we own the next entry, check status and send
+ * it up to higher layer
+ */
+
+ while (!( (rmdstat = rmdp->u.s.status) & RX_OWN))
+ {
+ /*
+ * Start and end of packet must be set, because we use
+ * the ethernet maximum packet length (1518) as buffer size.
+ *
+ * Because our buffers are at maximum OFLO and BUFF errors are
+ * not to be concerned (see Data sheet)
+ */
+
+ if ((rmdstat & (RX_STP | RX_ENP)) != (RX_STP | RX_ENP))
+ {
+ /* Start of a frame > 1518 Bytes ? */
+
+ if (rmdstat & RX_STP)
+ {
+ printf("%s: packet too long\n", SK_NAME);
+ }
+
+ /*
+ * All other packets will be ignored until a new frame with
+ * start (RX_STP) set follows.
+ *
+ * What we do is just give descriptor free for new incoming
+ * packets.
+ */
+
+ rmdp->u.s.status = RX_OWN; /* Relinquish ownership to LANCE */
+
+ }
+ else if (rmdstat & RX_ERR) /* Receive Error ? */
+ {
+ printf("%s: RX error: %#hX\n", SK_NAME, (int) rmdstat);
+ rmdp->u.s.status = RX_OWN; /* Relinquish ownership to LANCE */
+ }
+ else /* We have a packet which can be queued for the upper layers */
+ {
+
+ int len = (rmdp->mlen & 0x0fff); /* extract message length from receive buffer */
+
+ /*
+ * Copy data out of our receive descriptor into nic->packet.
+ *
+ * (rmdp->u.buffer & 0x00ffffff) -> get address of buffer and
+ * ignore status fields)
+ */
+
+ memcpy(nic->packet, (unsigned char *) (rmdp->u.buffer & 0x00ffffff), nic->packetlen = len);
+ packet_there = 1;
+
+
+ /*
+ * Packet is queued and marked for processing so we
+ * free our descriptor
+ */
+
+ rmdp->u.s.status = RX_OWN;
+
+ p->rmdnum++;
+ p->rmdnum %= RMDNUM;
+
+ rmdp = p->rmdhead + p->rmdnum;
+ }
+ }
+ SK_write_reg(CSR0, CSR0_INEA); /* Enable Interrupts */
+ return (packet_there);
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void SK_transmit(
+struct nic *nic,
+const char *d, /* Destination */
+unsigned int t, /* Type */
+unsigned int s, /* size */
+const char *pack) /* Packet */
+{
+ /* send the packet to destination */
+ struct priv *p; /* SK_G16 private structure */
+ struct tmd *tmdp;
+ short len;
+ int csr0, tmdstat;
+
+ PRINTF2(("## %s: At beginning of SK_transmit(). CSR0: %#hX\n",
+ SK_NAME, SK_read_reg(CSR0)));
+ p = nic->priv_data;
+ tmdp = p->tmdhead + p->tmdnum; /* Which descriptor for transmitting */
+
+ /* Copy data into dual ported ram */
+
+ memcpy(&p->ram->tmdbuf[p->tmdnum][0], d, ETH_ALEN); /* dst */
+ memcpy(&p->ram->tmdbuf[p->tmdnum][ETH_ALEN], nic->node_addr, ETH_ALEN); /* src */
+ p->ram->tmdbuf[p->tmdnum][ETH_ALEN + ETH_ALEN] = t >> 8; /* type */
+ p->ram->tmdbuf[p->tmdnum][ETH_ALEN + ETH_ALEN + 1] = t; /* type */
+ memcpy(&p->ram->tmdbuf[p->tmdnum][ETH_HLEN], pack, s);
+ s += ETH_HLEN;
+ while (s < ETH_ZLEN) /* pad to min length */
+ p->ram->tmdbuf[p->tmdnum][s++] = 0;
+ p->ram->tmde[p->tmdnum].status2 = 0x0;
+
+ /* Evaluate Packet length */
+ len = ETH_ZLEN < s ? s : ETH_ZLEN;
+
+ /* Fill in Transmit Message Descriptor */
+
+ tmdp->blen = -len; /* set length to transmit */
+
+ /*
+ * Packet start and end is always set because we use the maximum
+ * packet length as buffer length.
+ * Relinquish ownership to LANCE
+ */
+
+ tmdp->u.s.status = TX_OWN | TX_STP | TX_ENP;
+
+ /* Start Demand Transmission */
+ SK_write_reg(CSR0, CSR0_TDMD | CSR0_INEA);
+
+ csr0 = SK_read_reg(CSR0); /* store register for checking */
+
+ /*
+ * Acknowledge all of the current interrupt sources, disable
+ * Interrupts (INEA = 0)
+ */
+
+ SK_write_reg(CSR0, csr0 & CSR0_CLRALL);
+
+ if (csr0 & CSR0_ERR) /* LANCE Error */
+ {
+ printf("%s: error: %#hX", SK_NAME, csr0);
+
+ if (csr0 & CSR0_MISS) /* No place to store packet ? */
+ {
+ printf(", Packet dropped.");
+ }
+ putchar('\n');
+ }
+
+
+ /* Set next buffer */
+ p->tmdlast++;
+ p->tmdlast &= TMDNUM-1;
+
+ tmdstat = tmdp->u.s.status & 0xff00; /* filter out status bits 15:08 */
+
+ /*
+ * We check status of transmitted packet.
+ * see LANCE data-sheet for error explanation
+ */
+ if (tmdstat & TX_ERR) /* Error occurred */
+ {
+ printf("%s: TX error: %#hX %#hX\n", SK_NAME, (int) tmdstat,
+ (int) tmdp->status2);
+
+ if (tmdp->status2 & TX_TDR) /* TDR problems? */
+ {
+ printf("%s: tdr-problems \n", SK_NAME);
+ }
+
+ if (tmdp->status2 & TX_UFLO) /* Underflow error ? */
+ {
+ /*
+ * If UFLO error occurs it will turn transmitter of.
+ * So we must reinit LANCE
+ */
+
+ SK_lance_init(nic, MODE_NORMAL);
+ }
+
+ tmdp->status2 = 0; /* Clear error flags */
+ }
+
+ SK_write_reg(CSR0, CSR0_INEA); /* Enable Interrupts */
+
+ /* Set pointer to next transmit buffer */
+ p->tmdnum++;
+ p->tmdnum &= TMDNUM-1;
+
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void SK_disable(struct dev *dev)
+{
+ struct nic *nic = (struct nic *)dev;
+
+ /* put the card in its initial state */
+ SK_lance_init(nic, MODE_NORMAL); /* reset and disable merge */
+
+ PRINTF(("## %s: At beginning of SK_disable(). CSR0: %#hX\n",
+ SK_NAME, SK_read_reg(CSR0)));
+ PRINTF(("%s: Shutting %s down CSR0 %#hX\n", SK_NAME, SK_NAME,
+ (int) SK_read_reg(CSR0)));
+
+ SK_write_reg(CSR0, CSR0_STOP); /* STOP the LANCE */
+}
+
+/**************************************************************************
+IRQ - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void SK_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+static int SK_probe(struct dev *dev, unsigned short *probe_addrs)
+{
+ struct nic *nic = (struct nic *)dev;
+ unsigned short *p;
+ static unsigned short io_addrs[] = SK_IO_PORTS;
+ /* if probe_addrs is 0, then routine can use a hardwired default */
+ nic->priv_data = &p_data;
+ if (probe_addrs == 0)
+ probe_addrs = io_addrs;
+ for (p = probe_addrs; (ioaddr = *p) != 0; ++p)
+ {
+ long offset1, offset0 = inb(ioaddr);
+ if ((offset0 == SK_IDLOW) &&
+ ((offset1 = inb(ioaddr + 1)) == SK_IDHIGH))
+ if (SK_probe1(nic, ioaddr) >= 0)
+ break;
+ }
+ /* if board found */
+ if (ioaddr != 0)
+ {
+ nic->ioaddr = ioaddr & ~3;
+ nic->irqno = 0;
+ /* point to NIC specific routines */
+ dev->disable = SK_disable;
+ nic->poll = SK_poll;
+ nic->transmit = SK_transmit;
+ nic->irq = SK_irq;
+ /* FIXME set dev->devid */
+ return 1;
+ }
+ /* else */
+ {
+ return 0;
+ }
+}
+
+int SK_probe1(struct nic *nic, short ioaddr1 __unused)
+{
+ int i,j; /* Counters */
+ unsigned int rom_addr; /* used to store RAM address used for POS_ADDR */
+
+ struct priv *p; /* SK_G16 private structure */
+
+ if (SK_ADDR & 0x3fff || SK_ADDR < 0xa0000)
+ {
+ /*
+ * Now here we could use a routine which searches for a free
+ * place in the ram and set SK_ADDR if found. TODO.
+ */
+ printf("%s: SK_ADDR %#hX is not valid. Check configuration.\n",
+ SK_NAME, SK_ADDR);
+ return -1;
+ }
+
+ rom_addr = SK_ADDR;
+
+ outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off */
+ outb(POS_ADDR, SK_POS3); /* Set RAM address */
+ outb(SK_ROM_RAM_ON, SK_POS2); /* RAM on, BOOT_ROM on */
+#ifdef SK_DEBUG
+ SK_print_pos(nic, "POS registers after ROM, RAM config");
+#endif
+
+ board = (SK_RAM *) rom_addr;
+ PRINTF(("adr[0]: %hX, adr[1]: %hX, adr[2]: %hX\n",
+ board->rom[0], board->rom[2], board->rom[4]));
+
+ /* Read in station address */
+ for (i = 0, j = 0; i < ETH_ALEN; i++, j+=2)
+ {
+ *(nic->node_addr+i) = board->rom[j];
+ }
+
+ /* Check for manufacturer code */
+#ifdef SK_DEBUG
+ if (!(*(nic->node_addr+0) == SK_MAC0 &&
+ *(nic->node_addr+1) == SK_MAC1 &&
+ *(nic->node_addr+2) == SK_MAC2) )
+ {
+ PRINTF(("## %s: We did not find SK_G16 at RAM location.\n",
+ SK_NAME));
+ return -1; /* NO SK_G16 found */
+ }
+#endif
+
+ p = nic->priv_data;
+
+ /* Initialize private structure */
+
+ p->ram = (struct SK_ram *) rom_addr; /* Set dual ported RAM addr */
+ p->tmdhead = &(p->ram)->tmde[0]; /* Set TMD head */
+ p->rmdhead = &(p->ram)->rmde[0]; /* Set RMD head */
+
+ printf("Schneider & Koch G16 at %#hX, mem at %#hX, HW addr: %!\n",
+ (unsigned int) ioaddr, (unsigned int) p->ram, nic->node_addr);
+
+ /* Initialize buffer pointers */
+
+ for (i = 0; i < TMDNUM; i++)
+ {
+ p->tmdbufs[i] = p->ram->tmdbuf[i];
+ }
+
+ for (i = 0; i < RMDNUM; i++)
+ {
+ p->rmdbufs[i] = p->ram->rmdbuf[i];
+ }
+ i = 0;
+
+ if (!(i = SK_lance_init(nic, MODE_NORMAL))) /* LANCE init OK? */
+ {
+
+#ifdef SK_DEBUG
+ /*
+ * This debug block tries to stop LANCE,
+ * reinit LANCE with transmitter and receiver disabled,
+ * then stop again and reinit with NORMAL_MODE
+ */
+
+ printf("## %s: After lance init. CSR0: %#hX\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_write_reg(CSR0, CSR0_STOP);
+ printf("## %s: LANCE stopped. CSR0: %#hX\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_lance_init(nic, MODE_DTX | MODE_DRX);
+ printf("## %s: Reinit with DTX + DRX off. CSR0: %#hX\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_write_reg(CSR0, CSR0_STOP);
+ printf("## %s: LANCE stopped. CSR0: %#hX\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_lance_init(nic, MODE_NORMAL);
+ printf("## %s: LANCE back to normal mode. CSR0: %#hX\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_print_pos(nic, "POS regs before returning OK");
+
+#endif /* SK_DEBUG */
+
+ }
+ else /* LANCE init failed */
+ {
+
+ PRINTF(("## %s: LANCE init failed: CSR0: %#hX\n",
+ SK_NAME, SK_read_reg(CSR0)));
+ return -1;
+ }
+
+#ifdef SK_DEBUG
+ SK_print_pos(nic, "End of SK_probe1");
+ SK_print_ram(nic);
+#endif
+
+ return 0; /* Initialization done */
+
+} /* End of SK_probe1() */
+
+static int SK_lance_init(struct nic *nic, unsigned short mode)
+{
+ int i;
+ struct priv *p = (struct priv *) nic->priv_data;
+ struct tmd *tmdp;
+ struct rmd *rmdp;
+
+ PRINTF(("## %s: At beginning of LANCE init. CSR0: %#hX\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ /* Reset LANCE */
+ SK_reset_board();
+
+ /* Initialize TMD's with start values */
+ p->tmdnum = 0; /* First descriptor for transmitting */
+ p->tmdlast = 0; /* First descriptor for reading stats */
+
+ for (i = 0; i < TMDNUM; i++) /* Init all TMD's */
+ {
+ tmdp = p->tmdhead + i;
+
+ tmdp->u.buffer = (unsigned long) p->tmdbufs[i]; /* assign buffer */
+
+ /* Mark TMD as start and end of packet */
+ tmdp->u.s.status = TX_STP | TX_ENP;
+ }
+
+
+ /* Initialize RMD's with start values */
+
+ p->rmdnum = 0; /* First RMD which will be used */
+
+ for (i = 0; i < RMDNUM; i++) /* Init all RMD's */
+ {
+ rmdp = p->rmdhead + i;
+
+
+ rmdp->u.buffer = (unsigned long) p->rmdbufs[i]; /* assign buffer */
+
+ /*
+ * LANCE must be owner at beginning so that he can fill in
+ * receiving packets, set status and release RMD
+ */
+
+ rmdp->u.s.status = RX_OWN;
+
+ rmdp->blen = -PKT_BUF_SZ; /* Buffer Size in a two's complement */
+
+ rmdp->mlen = 0; /* init message length */
+
+ }
+
+ /* Fill LANCE Initialize Block */
+
+ (p->ram)->ib.mode = mode; /* Set operation mode */
+
+ for (i = 0; i < ETH_ALEN; i++) /* Set physical address */
+ {
+ (p->ram)->ib.paddr[i] = *(nic->node_addr+i);
+ }
+
+ for (i = 0; i < 8; i++) /* Set multicast, logical address */
+ {
+ (p->ram)->ib.laddr[i] = 0; /* We do not use logical addressing */
+ }
+
+ /* Set ring descriptor pointers and set number of descriptors */
+
+ (p->ram)->ib.rdrp = (int) p->rmdhead | RMDNUMMASK;
+ (p->ram)->ib.tdrp = (int) p->tmdhead | TMDNUMMASK;
+
+ /* Prepare LANCE Control and Status Registers */
+
+ SK_write_reg(CSR3, CSR3_ACON); /* Ale Control !!!THIS MUST BE SET!!!! */
+
+ /*
+ * LANCE addresses the RAM from 0x0000 to 0x3fbf and has no access to
+ * PC Memory locations.
+ *
+ * In structure SK_ram is defined that the first thing in ram
+ * is the initialization block. So his address is for LANCE always
+ * 0x0000
+ *
+ * CSR1 contains low order bits 15:0 of initialization block address
+ * CSR2 is built of:
+ * 7:0 High order bits 23:16 of initialization block address
+ * 15:8 reserved, must be 0
+ */
+
+ /* Set initialization block address (must be on word boundary) */
+ SK_write_reg(CSR1, 0); /* Set low order bits 15:0 */
+ SK_write_reg(CSR2, 0); /* Set high order bits 23:16 */
+
+
+ PRINTF(("## %s: After setting CSR1-3. CSR0: %#hX\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ /* Initialize LANCE */
+
+ /*
+ * INIT = Initialize, when set, causes the LANCE to begin the
+ * initialization procedure and access the Init Block.
+ */
+
+ SK_write_reg(CSR0, CSR0_INIT);
+
+ /* Wait until LANCE finished initialization */
+
+ SK_set_RAP(CSR0); /* Register Address Pointer to CSR0 */
+
+ for (i = 0; (i < 100) && !(SK_rread_reg() & CSR0_IDON); i++)
+ ; /* Wait until init done or go ahead if problems (i>=100) */
+
+ if (i >= 100) /* Something is wrong ! */
+ {
+ printf("%s: can't init am7990, status: %#hX "
+ "init_block: %#hX\n",
+ SK_NAME, (int) SK_read_reg(CSR0),
+ (unsigned int) &(p->ram)->ib);
+
+#ifdef SK_DEBUG
+ SK_print_pos(nic, "LANCE INIT failed");
+#endif
+
+ return -1; /* LANCE init failed */
+ }
+
+ PRINTF(("## %s: init done after %d ticks\n", SK_NAME, i));
+
+ /* Clear Initialize done, enable Interrupts, start LANCE */
+
+ SK_write_reg(CSR0, CSR0_IDON | CSR0_INEA | CSR0_STRT);
+
+ PRINTF(("## %s: LANCE started. CSR0: %#hX\n", SK_NAME,
+ SK_read_reg(CSR0)));
+
+ return 0; /* LANCE is up and running */
+
+} /* End of SK_lance_init() */
+
+/* LANCE access functions
+ *
+ * ! CSR1-3 can only be accessed when in CSR0 the STOP bit is set !
+ */
+
+static void SK_reset_board(void)
+{
+ int i;
+
+ PRINTF(("## %s: At beginning of SK_reset_board.\n", SK_NAME));
+ SK_PORT = 0x00; /* Reset active */
+ for (i = 0; i < 10 ; i++) /* Delay min 5ms */
+ ;
+ SK_PORT = SK_RESET; /* Set back to normal operation */
+
+} /* End of SK_reset_board() */
+
+static void SK_set_RAP(int reg_number)
+{
+ SK_IOREG = reg_number;
+ SK_PORT = SK_RESET | SK_RAP | SK_WREG;
+ SK_IOCOM = SK_DOIO;
+
+ while (SK_PORT & SK_IORUN)
+ ;
+} /* End of SK_set_RAP() */
+
+static int SK_read_reg(int reg_number)
+{
+ SK_set_RAP(reg_number);
+
+ SK_PORT = SK_RESET | SK_RDATA | SK_RREG;
+ SK_IOCOM = SK_DOIO;
+
+ while (SK_PORT & SK_IORUN)
+ ;
+ return (SK_IOREG);
+
+} /* End of SK_read_reg() */
+
+static int SK_rread_reg(void)
+{
+ SK_PORT = SK_RESET | SK_RDATA | SK_RREG;
+
+ SK_IOCOM = SK_DOIO;
+
+ while (SK_PORT & SK_IORUN)
+ ;
+ return (SK_IOREG);
+
+} /* End of SK_rread_reg() */
+
+static void SK_write_reg(int reg_number, int value)
+{
+ SK_set_RAP(reg_number);
+
+ SK_IOREG = value;
+ SK_PORT = SK_RESET | SK_RDATA | SK_WREG;
+ SK_IOCOM = SK_DOIO;
+
+ while (SK_PORT & SK_IORUN)
+ ;
+} /* End of SK_write_reg */
+
+/*
+ * Debugging functions
+ * -------------------
+ */
+
+#ifdef SK_DEBUG
+static void SK_print_pos(struct nic *nic, char *text)
+{
+
+ unsigned char pos0 = inb(SK_POS0),
+ pos1 = inb(SK_POS1),
+ pos2 = inb(SK_POS2),
+ pos3 = inb(SK_POS3),
+ pos4 = inb(SK_POS4);
+
+
+ printf("## %s: %s.\n"
+ "## pos0=%#hX pos1=%#hX pos2=%#hX pos3=%#hX pos4=%#hX\n",
+ SK_NAME, text, pos0, pos1, pos2, (pos3<<14), pos4);
+
+} /* End of SK_print_pos() */
+
+static void SK_print_ram(struct nic *nic)
+{
+
+ int i;
+ struct priv *p = (struct priv *) nic->priv_data;
+
+ printf("## %s: RAM Details.\n"
+ "## RAM at %#hX tmdhead: %#hX rmdhead: %#hX initblock: %#hX\n",
+ SK_NAME,
+ (unsigned int) p->ram,
+ (unsigned int) p->tmdhead,
+ (unsigned int) p->rmdhead,
+ (unsigned int) &(p->ram)->ib);
+
+ printf("## ");
+
+ for(i = 0; i < TMDNUM; i++)
+ {
+ if (!(i % 3)) /* Every third line do a newline */
+ {
+ printf("\n## ");
+ }
+ printf("tmdbufs%d: %#hX ", (i+1), (int) p->tmdbufs[i]);
+ }
+ printf("## ");
+
+ for(i = 0; i < RMDNUM; i++)
+ {
+ if (!(i % 3)) /* Every third line do a newline */
+ {
+ printf("\n## ");
+ }
+ printf("rmdbufs%d: %#hX ", (i+1), (int) p->rmdbufs[i]);
+ }
+ putchar('\n');
+
+} /* End of SK_print_ram() */
+#endif
+
+static struct isa_driver SK_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "SK_G16",
+ .probe = SK_probe,
+ .ioaddrs = 0,
+};
diff --git a/src/drivers/net/sk_g16.h b/src/drivers/net/sk_g16.h
new file mode 100644
index 00000000..922cb3b6
--- /dev/null
+++ b/src/drivers/net/sk_g16.h
@@ -0,0 +1,171 @@
+/*-
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * Module : sk_g16.h
+ * Version : $Revision$
+ *
+ * Author : M.Hipp (mhipp@student.uni-tuebingen.de)
+ * changes by : Patrick J.D. Weichmann
+ *
+ * Date Created : 94/05/25
+ *
+ * Description : In here are all necessary definitions of
+ * the am7990 (LANCE) chip used for writing a
+ * network device driver which uses this chip
+ *
+ * $Log$
+ * Revision 1.1 2005/03/08 18:53:40 mcb30
+ * Initial revision
+ *
+ * Revision 1.1 2002/12/12 02:18:20 ebiederm
+ * Moved network drivers into drivers/net
+ *
+-*/
+
+#ifndef SK_G16_H
+
+#define SK_G16_H
+
+
+/*
+ * Control and Status Register 0 (CSR0) bit definitions
+ *
+ * (R=Readable) (W=Writeable) (S=Set on write) (C-Clear on write)
+ *
+ */
+
+#define CSR0_ERR 0x8000 /* Error summary (R) */
+#define CSR0_BABL 0x4000 /* Babble transmitter timeout error (RC) */
+#define CSR0_CERR 0x2000 /* Collision Error (RC) */
+#define CSR0_MISS 0x1000 /* Missed packet (RC) */
+#define CSR0_MERR 0x0800 /* Memory Error (RC) */
+#define CSR0_RINT 0x0400 /* Receiver Interrupt (RC) */
+#define CSR0_TINT 0x0200 /* Transmit Interrupt (RC) */
+#define CSR0_IDON 0x0100 /* Initialization Done (RC) */
+#define CSR0_INTR 0x0080 /* Interrupt Flag (R) */
+#define CSR0_INEA 0x0040 /* Interrupt Enable (RW) */
+#define CSR0_RXON 0x0020 /* Receiver on (R) */
+#define CSR0_TXON 0x0010 /* Transmitter on (R) */
+#define CSR0_TDMD 0x0008 /* Transmit Demand (RS) */
+#define CSR0_STOP 0x0004 /* Stop (RS) */
+#define CSR0_STRT 0x0002 /* Start (RS) */
+#define CSR0_INIT 0x0001 /* Initialize (RS) */
+
+#define CSR0_CLRALL 0x7f00 /* mask for all clearable bits */
+
+/*
+ * Control and Status Register 3 (CSR3) bit definitions
+ *
+ */
+
+#define CSR3_BSWAP 0x0004 /* Byte Swap (RW) */
+#define CSR3_ACON 0x0002 /* ALE Control (RW) */
+#define CSR3_BCON 0x0001 /* Byte Control (RW) */
+
+/*
+ * Initialization Block Mode operation Bit Definitions.
+ */
+
+#define MODE_PROM 0x8000 /* Promiscuous Mode */
+#define MODE_INTL 0x0040 /* Internal Loopback */
+#define MODE_DRTY 0x0020 /* Disable Retry */
+#define MODE_COLL 0x0010 /* Force Collision */
+#define MODE_DTCR 0x0008 /* Disable Transmit CRC) */
+#define MODE_LOOP 0x0004 /* Loopback */
+#define MODE_DTX 0x0002 /* Disable the Transmitter */
+#define MODE_DRX 0x0001 /* Disable the Receiver */
+
+#define MODE_NORMAL 0x0000 /* Normal operation mode */
+
+/*
+ * Receive message descriptor status bit definitions.
+ */
+
+#define RX_OWN 0x80 /* Owner bit 0 = host, 1 = lance */
+#define RX_ERR 0x40 /* Error Summary */
+#define RX_FRAM 0x20 /* Framing Error */
+#define RX_OFLO 0x10 /* Overflow Error */
+#define RX_CRC 0x08 /* CRC Error */
+#define RX_BUFF 0x04 /* Buffer Error */
+#define RX_STP 0x02 /* Start of Packet */
+#define RX_ENP 0x01 /* End of Packet */
+
+
+/*
+ * Transmit message descriptor status bit definitions.
+ */
+
+#define TX_OWN 0x80 /* Owner bit 0 = host, 1 = lance */
+#define TX_ERR 0x40 /* Error Summary */
+#define TX_MORE 0x10 /* More the 1 retry needed to Xmit */
+#define TX_ONE 0x08 /* One retry needed to Xmit */
+#define TX_DEF 0x04 /* Deferred */
+#define TX_STP 0x02 /* Start of Packet */
+#define TX_ENP 0x01 /* End of Packet */
+
+/*
+ * Transmit status (2) (valid if TX_ERR == 1)
+ */
+
+#define TX_BUFF 0x8000 /* Buffering error (no ENP) */
+#define TX_UFLO 0x4000 /* Underflow (late memory) */
+#define TX_LCOL 0x1000 /* Late collision */
+#define TX_LCAR 0x0400 /* Loss of Carrier */
+#define TX_RTRY 0x0200 /* Failed after 16 retransmissions */
+#define TX_TDR 0x003f /* Time-domain-reflectometer-value */
+
+
+/*
+ * Structures used for Communication with the LANCE
+ */
+
+/* LANCE Initialize Block */
+
+struct init_block
+{
+ unsigned short mode; /* Mode Register */
+ unsigned char paddr[6]; /* Physical Address (MAC) */
+ unsigned char laddr[8]; /* Logical Filter Address (not used) */
+ unsigned int rdrp; /* Receive Descriptor Ring pointer */
+ unsigned int tdrp; /* Transmit Descriptor Ring pointer */
+};
+
+
+/* Receive Message Descriptor Entry */
+
+struct rmd
+{
+ union rmd_u
+ {
+ unsigned long buffer; /* Address of buffer */
+ struct rmd_s
+ {
+ unsigned char unused[3];
+ unsigned volatile char status; /* Status Bits */
+ } s;
+ } u;
+ volatile short blen; /* Buffer Length (two's complement) */
+ unsigned short mlen; /* Message Byte Count */
+};
+
+
+/* Transmit Message Descriptor Entry */
+
+struct tmd
+{
+ union tmd_u
+ {
+ unsigned long buffer; /* Address of buffer */
+ struct tmd_s
+ {
+ unsigned char unused[3];
+ unsigned volatile char status; /* Status Bits */
+ } s;
+ } u;
+ unsigned short blen; /* Buffer Length (two's complement) */
+ unsigned volatile short status2; /* Error Status Bits */
+};
+
+#endif /* End of SK_G16_H */
diff --git a/src/drivers/net/skel.c b/src/drivers/net/skel.c
new file mode 100644
index 00000000..4b7f103a
--- /dev/null
+++ b/src/drivers/net/skel.c
@@ -0,0 +1,200 @@
+/**************************************************************************
+Etherboot - BOOTP/TFTP Bootstrap Program
+Skeleton NIC driver for Etherboot
+***************************************************************************/
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ */
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include "pci.h"
+/* to get the ISA support functions, if this is an ISA NIC */
+#include "isa.h"
+
+/* NIC specific static variables go here */
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int skel_poll(struct nic *nic, int retrieve)
+{
+ /* Work out whether or not there's an ethernet packet ready to
+ * read. Return 0 if not.
+ */
+ /*
+ if ( ! <packet_ready> ) return 0;
+ */
+
+ /* retrieve==0 indicates that we are just checking for the
+ * presence of a packet but don't want to read it just yet.
+ */
+ /*
+ if ( ! retrieve ) return 1;
+ */
+
+ /* Copy data to nic->packet. Data should include the
+ * link-layer header (dest MAC, source MAC, type).
+ * Store length of data in nic->packetlen.
+ * Return true to indicate a packet has been read.
+ */
+ /*
+ nic->packetlen = <packet_length>;
+ memcpy ( nic->packet, <packet_data>, <packet_length> );
+ return 1;
+ */
+
+ return 0; /* Remove this line once this method is implemented */
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void skel_transmit(
+ struct nic *nic,
+ const char *dest, /* Destination */
+ unsigned int type, /* Type */
+ unsigned int size, /* size */
+ const char *packet) /* Packet */
+{
+ /* Transmit packet to dest MAC address. You will need to
+ * construct the link-layer header (dest MAC, source MAC,
+ * type).
+ */
+ /*
+ unsigned int nstype = htons ( type );
+ memcpy ( <tx_buffer>, dest, ETH_ALEN );
+ memcpy ( <tx_buffer> + ETH_ALEN, nic->node_addr, ETH_ALEN );
+ memcpy ( <tx_buffer> + 2 * ETH_ALEN, &nstype, 2 );
+ memcpy ( <tx_buffer> + ETH_HLEN, data, size );
+ <transmit_data> ( <tx_buffer>, size + ETH_HLEN );
+ */
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void skel_disable(struct dev *dev)
+{
+ /* put the card in its initial state */
+ /* This function serves 3 purposes.
+ * This disables DMA and interrupts so we don't receive
+ * unexpected packets or interrupts from the card after
+ * etherboot has finished.
+ * This frees resources so etherboot may use
+ * this driver on another interface
+ * This allows etherboot to reinitialize the interface
+ * if something is something goes wrong.
+ */
+}
+
+/**************************************************************************
+IRQ - handle interrupts
+***************************************************************************/
+static void skel_irq(struct nic *nic, irq_action_t action)
+{
+ /* This routine is somewhat optional. Etherboot itself
+ * doesn't use interrupts, but they are required under some
+ * circumstances when we're acting as a PXE stack.
+ *
+ * If you don't implement this routine, the only effect will
+ * be that your driver cannot be used via Etherboot's UNDI
+ * API. This won't affect programs that use only the UDP
+ * portion of the PXE API, such as pxelinux.
+ */
+
+ switch ( action ) {
+ case DISABLE :
+ case ENABLE :
+ /* Set receive interrupt enabled/disabled state */
+ /*
+ outb ( action == ENABLE ? IntrMaskEnabled : IntrMaskDisabled,
+ nic->ioaddr + IntrMaskRegister );
+ */
+ break;
+ case FORCE :
+ /* Force NIC to generate a receive interrupt */
+ /*
+ outb ( ForceInterrupt, nic->ioaddr + IntrForceRegister );
+ */
+ break;
+ }
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+
+#define board_found 1
+#define valid_link 0
+static int skel_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *)dev;
+
+ if (board_found && valid_link)
+ {
+ /* store NIC parameters */
+ nic->ioaddr = pci->ioaddr & ~3;
+ nic->irqno = pci->irq;
+ /* point to NIC specific routines */
+ dev->disable = skel_disable;
+ nic->poll = skel_poll;
+ nic->transmit = skel_transmit;
+ nic->irq = skel_irq;
+ return 1;
+ }
+ /* else */
+ return 0;
+}
+
+static struct pci_id skel_nics[] = {
+PCI_ROM(0x0000, 0x0000, "skel-pci", "Skeleton PCI Adaptor"),
+};
+
+static struct pci_driver skel_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "SKELETON/PCI",
+ .probe = skel_probe,
+ .ids = skel_nics,
+ .id_count = sizeof(skel_nics)/sizeof(skel_nics[0]),
+ .class = 0,
+};
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+static int skel_isa_probe(struct dev *dev, unsigned short *probe_addrs)
+{
+ struct nic *nic = (struct nic *)dev;
+ /* if probe_addrs is 0, then routine can use a hardwired default */
+ if (board_found && valid_link)
+ {
+ /* point to NIC specific routines */
+ dev->disable = skel_disable;
+ nic->poll = skel_poll;
+ nic->transmit = skel_transmit;
+
+ /* Report the ISA pnp id of the board */
+ dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
+ dev->devid.vendor_id = htons(0x1234);
+ return 1;
+ }
+ /* else */
+ return 0;
+}
+
+ISA_ROM("skel-isa", "Skeleton ISA driver")
+static struct isa_driver skel_isa_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "SKELETON/ISA",
+ .probe = skel_isa_probe,
+ .ioaddrs = 0,
+};
+
diff --git a/src/drivers/net/smc9000.c b/src/drivers/net/smc9000.c
new file mode 100644
index 00000000..a05521e8
--- /dev/null
+++ b/src/drivers/net/smc9000.c
@@ -0,0 +1,544 @@
+#ifdef ALLMULTI
+#error multicast support is not yet implemented
+#endif
+ /*------------------------------------------------------------------------
+ * smc9000.c
+ * This is a Etherboot driver for SMC's 9000 series of Ethernet cards.
+ *
+ * Copyright (C) 1998 Daniel Engström <daniel.engstrom@riksnett.no>
+ * Based on the Linux SMC9000 driver, smc9194.c by Eric Stahlman
+ * Copyright (C) 1996 by Erik Stahlman <eric@vt.edu>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * "Features" of the SMC chip:
+ * 4608 byte packet memory. ( for the 91C92/4. Others have more )
+ * EEPROM for configuration
+ * AUI/TP selection
+ *
+ * Authors
+ * Erik Stahlman <erik@vt.edu>
+ * Daniel Engström <daniel.engstrom@riksnett.no>
+ *
+ * History
+ * 98-09-25 Daniel Engström Etherboot driver crated from Eric's
+ * Linux driver.
+ *
+ *---------------------------------------------------------------------------*/
+#define LINUX_OUT_MACROS 1
+#define SMC9000_VERBOSE 1
+#define SMC9000_DEBUG 0
+
+#include "etherboot.h"
+#include "nic.h"
+#include "isa.h"
+#include "smc9000.h"
+
+# define _outb outb
+# define _outw outw
+
+static const char smc9000_version[] = "Version 0.99 98-09-30";
+static unsigned int smc9000_base=0;
+static const char *interfaces[ 2 ] = { "TP", "AUI" };
+static const char *chip_ids[ 15 ] = {
+ NULL, NULL, NULL,
+ /* 3 */ "SMC91C90/91C92",
+ /* 4 */ "SMC91C94",
+ /* 5 */ "SMC91C95",
+ NULL,
+ /* 7 */ "SMC91C100",
+ /* 8 */ "SMC91C100FD",
+ NULL, NULL, NULL,
+ NULL, NULL, NULL
+};
+static const char smc91c96_id[] = "SMC91C96";
+
+/*
+ * Function: smc_reset( int ioaddr )
+ * Purpose:
+ * This sets the SMC91xx chip to its normal state, hopefully from whatever
+ * mess that any other DOS driver has put it in.
+ *
+ * Maybe I should reset more registers to defaults in here? SOFTRESET should
+ * do that for me.
+ *
+ * Method:
+ * 1. send a SOFT RESET
+ * 2. wait for it to finish
+ * 3. reset the memory management unit
+ * 4. clear all interrupts
+ *
+*/
+static void smc_reset(int ioaddr)
+{
+ /* This resets the registers mostly to defaults, but doesn't
+ * affect EEPROM. That seems unnecessary */
+ SMC_SELECT_BANK(ioaddr, 0);
+ _outw( RCR_SOFTRESET, ioaddr + RCR );
+
+ /* this should pause enough for the chip to be happy */
+ SMC_DELAY(ioaddr);
+
+ /* Set the transmit and receive configuration registers to
+ * default values */
+ _outw(RCR_CLEAR, ioaddr + RCR);
+ _outw(TCR_CLEAR, ioaddr + TCR);
+
+ /* Reset the MMU */
+ SMC_SELECT_BANK(ioaddr, 2);
+ _outw( MC_RESET, ioaddr + MMU_CMD );
+
+ /* Note: It doesn't seem that waiting for the MMU busy is needed here,
+ * but this is a place where future chipsets _COULD_ break. Be wary
+ * of issuing another MMU command right after this */
+ _outb(0, ioaddr + INT_MASK);
+}
+
+
+/*----------------------------------------------------------------------
+ * Function: smc_probe( int ioaddr )
+ *
+ * Purpose:
+ * Tests to see if a given ioaddr points to an SMC9xxx chip.
+ * Returns a 0 on success
+ *
+ * Algorithm:
+ * (1) see if the high byte of BANK_SELECT is 0x33
+ * (2) compare the ioaddr with the base register's address
+ * (3) see if I recognize the chip ID in the appropriate register
+ *
+ * ---------------------------------------------------------------------
+ */
+static int smc_probe( int ioaddr )
+{
+ word bank;
+ word revision_register;
+ word base_address_register;
+
+ /* First, see if the high byte is 0x33 */
+ bank = inw(ioaddr + BANK_SELECT);
+ if ((bank & 0xFF00) != 0x3300) {
+ return -1;
+ }
+ /* The above MIGHT indicate a device, but I need to write to further
+ * test this. */
+ _outw(0x0, ioaddr + BANK_SELECT);
+ bank = inw(ioaddr + BANK_SELECT);
+ if ((bank & 0xFF00) != 0x3300) {
+ return -1;
+ }
+
+ /* well, we've already written once, so hopefully another time won't
+ * hurt. This time, I need to switch the bank register to bank 1,
+ * so I can access the base address register */
+ SMC_SELECT_BANK(ioaddr, 1);
+ base_address_register = inw(ioaddr + BASE);
+
+ if (ioaddr != (base_address_register >> 3 & 0x3E0)) {
+#ifdef SMC9000_VERBOSE
+ printf("SMC9000: IOADDR %hX doesn't match configuration (%hX)."
+ "Probably not a SMC chip\n",
+ ioaddr, base_address_register >> 3 & 0x3E0);
+#endif
+ /* well, the base address register didn't match. Must not have
+ * been a SMC chip after all. */
+ return -1;
+ }
+
+
+ /* check if the revision register is something that I recognize.
+ * These might need to be added to later, as future revisions
+ * could be added. */
+ SMC_SELECT_BANK(ioaddr, 3);
+ revision_register = inw(ioaddr + REVISION);
+ if (!chip_ids[(revision_register >> 4) & 0xF]) {
+ /* I don't recognize this chip, so... */
+#ifdef SMC9000_VERBOSE
+ printf("SMC9000: IO %hX: Unrecognized revision register:"
+ " %hX, Contact author.\n", ioaddr, revision_register);
+#endif
+ return -1;
+ }
+
+ /* at this point I'll assume that the chip is an SMC9xxx.
+ * It might be prudent to check a listing of MAC addresses
+ * against the hardware address, or do some other tests. */
+ return 0;
+}
+
+
+/**************************************************************************
+ * ETH_TRANSMIT - Transmit a frame
+ ***************************************************************************/
+static void smc9000_transmit(
+ struct nic *nic,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+ word length; /* real, length incl. header */
+ word numPages;
+ unsigned long time_out;
+ byte packet_no;
+ word status;
+ int i;
+
+ /* We dont pad here since we can have the hardware doing it for us */
+ length = (s + ETH_HLEN + 1)&~1;
+
+ /* convert to MMU pages */
+ numPages = length / 256;
+
+ if (numPages > 7 ) {
+#ifdef SMC9000_VERBOSE
+ printf("SMC9000: Far too big packet error. \n");
+#endif
+ return;
+ }
+
+ /* dont try more than, say 30 times */
+ for (i=0;i<30;i++) {
+ /* now, try to allocate the memory */
+ SMC_SELECT_BANK(smc9000_base, 2);
+ _outw(MC_ALLOC | numPages, smc9000_base + MMU_CMD);
+
+ status = 0;
+ /* wait for the memory allocation to finnish */
+ for (time_out = currticks() + 5*TICKS_PER_SEC; currticks() < time_out; ) {
+ status = inb(smc9000_base + INTERRUPT);
+ if ( status & IM_ALLOC_INT ) {
+ /* acknowledge the interrupt */
+ _outb(IM_ALLOC_INT, smc9000_base + INTERRUPT);
+ break;
+ }
+ }
+
+ if ((status & IM_ALLOC_INT) != 0 ) {
+ /* We've got the memory */
+ break;
+ } else {
+ printf("SMC9000: Memory allocation timed out, resetting MMU.\n");
+ _outw(MC_RESET, smc9000_base + MMU_CMD);
+ }
+ }
+
+ /* If I get here, I _know_ there is a packet slot waiting for me */
+ packet_no = inb(smc9000_base + PNR_ARR + 1);
+ if (packet_no & 0x80) {
+ /* or isn't there? BAD CHIP! */
+ printf("SMC9000: Memory allocation failed. \n");
+ return;
+ }
+
+ /* we have a packet address, so tell the card to use it */
+ _outb(packet_no, smc9000_base + PNR_ARR);
+
+ /* point to the beginning of the packet */
+ _outw(PTR_AUTOINC, smc9000_base + POINTER);
+
+#if SMC9000_DEBUG > 2
+ printf("Trying to xmit packet of length %hX\n", length );
+#endif
+
+ /* send the packet length ( +6 for status, length and ctl byte )
+ * and the status word ( set to zeros ) */
+ _outw(0, smc9000_base + DATA_1 );
+
+ /* send the packet length ( +6 for status words, length, and ctl) */
+ _outb((length+6) & 0xFF, smc9000_base + DATA_1);
+ _outb((length+6) >> 8 , smc9000_base + DATA_1);
+
+ /* Write the contents of the packet */
+
+ /* The ethernet header first... */
+ outsw(smc9000_base + DATA_1, d, ETH_ALEN >> 1);
+ outsw(smc9000_base + DATA_1, nic->node_addr, ETH_ALEN >> 1);
+ _outw(htons(t), smc9000_base + DATA_1);
+
+ /* ... the data ... */
+ outsw(smc9000_base + DATA_1 , p, s >> 1);
+
+ /* ... and the last byte, if there is one. */
+ if ((s & 1) == 0) {
+ _outw(0, smc9000_base + DATA_1);
+ } else {
+ _outb(p[s-1], smc9000_base + DATA_1);
+ _outb(0x20, smc9000_base + DATA_1);
+ }
+
+ /* and let the chipset deal with it */
+ _outw(MC_ENQUEUE , smc9000_base + MMU_CMD);
+
+ status = 0; time_out = currticks() + 5*TICKS_PER_SEC;
+ do {
+ status = inb(smc9000_base + INTERRUPT);
+
+ if ((status & IM_TX_INT ) != 0) {
+ word tx_status;
+
+ /* ack interrupt */
+ _outb(IM_TX_INT, smc9000_base + INTERRUPT);
+
+ packet_no = inw(smc9000_base + FIFO_PORTS);
+ packet_no &= 0x7F;
+
+ /* select this as the packet to read from */
+ _outb( packet_no, smc9000_base + PNR_ARR );
+
+ /* read the first word from this packet */
+ _outw( PTR_AUTOINC | PTR_READ, smc9000_base + POINTER );
+
+ tx_status = inw( smc9000_base + DATA_1 );
+
+ if (0 == (tx_status & TS_SUCCESS)) {
+#ifdef SMC9000_VERBOSE
+ printf("SMC9000: TX FAIL STATUS: %hX \n", tx_status);
+#endif
+ /* re-enable transmit */
+ SMC_SELECT_BANK(smc9000_base, 0);
+ _outw(inw(smc9000_base + TCR ) | TCR_ENABLE, smc9000_base + TCR );
+ }
+
+ /* kill the packet */
+ SMC_SELECT_BANK(smc9000_base, 2);
+ _outw(MC_FREEPKT, smc9000_base + MMU_CMD);
+
+ return;
+ }
+ }while(currticks() < time_out);
+
+ printf("SMC9000: Waring TX timed out, resetting board\n");
+ smc_reset(smc9000_base);
+ return;
+}
+
+/**************************************************************************
+ * ETH_POLL - Wait for a frame
+ ***************************************************************************/
+static int smc9000_poll(struct nic *nic, int retrieve)
+{
+ if(!smc9000_base)
+ return 0;
+
+ SMC_SELECT_BANK(smc9000_base, 2);
+ if (inw(smc9000_base + FIFO_PORTS) & FP_RXEMPTY)
+ return 0;
+
+ if ( ! retrieve ) return 1;
+
+ /* start reading from the start of the packet */
+ _outw(PTR_READ | PTR_RCV | PTR_AUTOINC, smc9000_base + POINTER);
+
+ /* First read the status and check that we're ok */
+ if (!(inw(smc9000_base + DATA_1) & RS_ERRORS)) {
+ /* Next: read the packet length and mask off the top bits */
+ nic->packetlen = (inw(smc9000_base + DATA_1) & 0x07ff);
+
+ /* the packet length includes the 3 extra words */
+ nic->packetlen -= 6;
+#if SMC9000_DEBUG > 2
+ printf(" Reading %d words (and %d byte(s))\n",
+ (nic->packetlen >> 1), nic->packetlen & 1);
+#endif
+ /* read the packet (and the last "extra" word) */
+ insw(smc9000_base + DATA_1, nic->packet, (nic->packetlen+2) >> 1);
+ /* is there an odd last byte ? */
+ if (nic->packet[nic->packetlen+1] & 0x20)
+ nic->packetlen++;
+
+ /* error or good, tell the card to get rid of this packet */
+ _outw(MC_RELEASE, smc9000_base + MMU_CMD);
+ return 1;
+ }
+
+ printf("SMC9000: RX error\n");
+ /* error or good, tell the card to get rid of this packet */
+ _outw(MC_RELEASE, smc9000_base + MMU_CMD);
+ return 0;
+}
+
+static void smc9000_disable(struct dev *dev __unused)
+{
+ if(!smc9000_base)
+ return;
+
+ smc_reset(smc9000_base);
+
+ /* no more interrupts for me */
+ SMC_SELECT_BANK(smc9000_base, 2);
+ _outb( 0, smc9000_base + INT_MASK);
+
+ /* and tell the card to stay away from that nasty outside world */
+ SMC_SELECT_BANK(smc9000_base, 0);
+ _outb( RCR_CLEAR, smc9000_base + RCR );
+ _outb( TCR_CLEAR, smc9000_base + TCR );
+}
+
+static void smc9000_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/**************************************************************************
+ * ETH_PROBE - Look for an adapter
+ ***************************************************************************/
+
+static int smc9000_probe(struct dev *dev, unsigned short *probe_addrs)
+{
+ struct nic *nic = (struct nic *)dev;
+ unsigned short revision;
+ int memory;
+ int media;
+ const char * version_string;
+ const char * if_string;
+ int i;
+
+ /*
+ * the SMC9000 can be at any of the following port addresses. To change,
+ * for a slightly different card, you can add it to the array. Keep in
+ * mind that the array must end in zero.
+ */
+ static unsigned short portlist[] = {
+#ifdef SMC9000_SCAN
+ SMC9000_SCAN,
+#else
+ 0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0,
+ 0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0,
+#endif
+ 0 };
+
+ /* if no addresses supplied, fall back on defaults */
+ if (probe_addrs == 0 || probe_addrs[0] == 0)
+ probe_addrs = portlist;
+
+ /* check every ethernet address */
+ for (i = 0; probe_addrs[i]; i++) {
+ /* check this specific address */
+ if (smc_probe(probe_addrs[i]) == 0)
+ smc9000_base = probe_addrs[i];
+ }
+
+ /* couldn't find anything */
+ if(0 == smc9000_base)
+ goto out;
+
+ nic->irqno = 0;
+ nic->ioaddr = smc9000_base;
+
+ /*
+ * Get the MAC address ( bank 1, regs 4 - 9 )
+ */
+ SMC_SELECT_BANK(smc9000_base, 1);
+ for ( i = 0; i < 6; i += 2 ) {
+ word address;
+
+ address = inw(smc9000_base + ADDR0 + i);
+ nic->node_addr[i+1] = address >> 8;
+ nic->node_addr[i] = address & 0xFF;
+ }
+
+
+ /* get the memory information */
+ SMC_SELECT_BANK(smc9000_base, 0);
+ memory = ( inw(smc9000_base + MCR) >> 9 ) & 0x7; /* multiplier */
+ memory *= 256 * (inw(smc9000_base + MIR) & 0xFF);
+
+ /*
+ * Now, I want to find out more about the chip. This is sort of
+ * redundant, but it's cleaner to have it in both, rather than having
+ * one VERY long probe procedure.
+ */
+ SMC_SELECT_BANK(smc9000_base, 3);
+ revision = inw(smc9000_base + REVISION);
+ version_string = chip_ids[(revision >> 4) & 0xF];
+
+ if (((revision & 0xF0) >> 4 == CHIP_9196) &&
+ ((revision & 0x0F) >= REV_9196)) {
+ /* This is a 91c96. 'c96 has the same chip id as 'c94 (4) but
+ * a revision starting at 6 */
+ version_string = smc91c96_id;
+ }
+
+ if ( !version_string ) {
+ /* I shouldn't get here because this call was done before.... */
+ goto out;
+ }
+
+ /* is it using AUI or 10BaseT ? */
+ SMC_SELECT_BANK(smc9000_base, 1);
+ if (inw(smc9000_base + CONFIG) & CFG_AUI_SELECT)
+ media = 2;
+ else
+ media = 1;
+
+ if_string = interfaces[media - 1];
+
+ /* now, reset the chip, and put it into a known state */
+ smc_reset(smc9000_base);
+
+ printf("SMC9000 %s\n", smc9000_version);
+#ifdef SMC9000_VERBOSE
+ printf("Copyright (C) 1998 Daniel Engstr\x94m\n");
+ printf("Copyright (C) 1996 Eric Stahlman\n");
+#endif
+
+ printf("%s rev:%d I/O port:%hX Interface:%s RAM:%d bytes \n",
+ version_string, revision & 0xF,
+ smc9000_base, if_string, memory );
+ /*
+ * Print the Ethernet address
+ */
+ printf("Ethernet MAC address: %!\n", nic->node_addr);
+
+ SMC_SELECT_BANK(smc9000_base, 0);
+
+ /* see the header file for options in TCR/RCR NORMAL*/
+ _outw(TCR_NORMAL, smc9000_base + TCR);
+ _outw(RCR_NORMAL, smc9000_base + RCR);
+
+ /* Select which interface to use */
+ SMC_SELECT_BANK(smc9000_base, 1);
+ if ( media == 1 ) {
+ _outw( inw( smc9000_base + CONFIG ) & ~CFG_AUI_SELECT,
+ smc9000_base + CONFIG );
+ }
+ else if ( media == 2 ) {
+ _outw( inw( smc9000_base + CONFIG ) | CFG_AUI_SELECT,
+ smc9000_base + CONFIG );
+ }
+
+ dev->disable = smc9000_disable;
+ nic->poll = smc9000_poll;
+ nic->transmit = smc9000_transmit;
+ nic->irq = smc9000_irq;
+
+ /* Based on PnP ISA map */
+ dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
+ dev->devid.device_id = htons(0x8228);
+
+ return 1;
+
+out:
+#ifdef SMC9000_VERBOSE
+ /* printf("No SMC9000 adapters found\n"); */
+#endif
+ smc9000_base = 0;
+
+ return (0);
+}
+
+static struct isa_driver smc9000_driver __isa_driver = {
+ .type = NIC_DRIVER,
+ .name = "SMC9000",
+ .probe = smc9000_probe,
+ .ioaddrs = 0,
+};
diff --git a/src/drivers/net/smc9000.h b/src/drivers/net/smc9000.h
new file mode 100644
index 00000000..ac7f9163
--- /dev/null
+++ b/src/drivers/net/smc9000.h
@@ -0,0 +1,205 @@
+/*------------------------------------------------------------------------
+ * smc9000.h
+ *
+ * Copyright (C) 1998 by Daniel Engström
+ * Copyright (C) 1996 by Erik Stahlman
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * This file contains register information and access macros for
+ * the SMC91xxx chipset.
+ *
+ * Information contained in this file was obtained from the SMC91C94
+ * manual from SMC. To get a copy, if you really want one, you can find
+ * information under www.smsc.com in the components division.
+ * ( this thanks to advice from Donald Becker ).
+ *
+ * Authors
+ * Daniel Engström <daniel.engstrom@riksnett.no>
+ * Erik Stahlman <erik@vt.edu>
+ *
+ * History
+ * 96-01-06 Erik Stahlman moved definitions here from main .c
+ * file
+ * 96-01-19 Erik Stahlman polished this up some, and added
+ * better error handling
+ * 98-09-25 Daniel Engström adjusted for Etherboot
+ * 98-09-27 Daniel Engström moved some static strings back to the
+ * main .c file
+ * --------------------------------------------------------------------------*/
+#ifndef _SMC9000_H_
+# define _SMC9000_H_
+
+/* I want some simple types */
+typedef unsigned char byte;
+typedef unsigned short word;
+typedef unsigned long int dword;
+
+/*---------------------------------------------------------------
+ *
+ * A description of the SMC registers is probably in order here,
+ * although for details, the SMC datasheet is invaluable.
+ *
+ * Basically, the chip has 4 banks of registers ( 0 to 3 ), which
+ * are accessed by writing a number into the BANK_SELECT register
+ * ( I also use a SMC_SELECT_BANK macro for this ).
+ *
+ * The banks are configured so that for most purposes, bank 2 is all
+ * that is needed for simple run time tasks.
+ * ----------------------------------------------------------------------*/
+
+/*
+ * Bank Select Register:
+ *
+ * yyyy yyyy 0000 00xx
+ * xx = bank number
+ * yyyy yyyy = 0x33, for identification purposes.
+ */
+#define BANK_SELECT 14
+
+/* BANK 0 */
+
+#define TCR 0 /* transmit control register */
+#define TCR_ENABLE 0x0001 /* if this is 1, we can transmit */
+#define TCR_FDUPLX 0x0800 /* receive packets sent out */
+#define TCR_STP_SQET 0x1000 /* stop transmitting if Signal quality error */
+#define TCR_MON_CNS 0x0400 /* monitors the carrier status */
+#define TCR_PAD_ENABLE 0x0080 /* pads short packets to 64 bytes */
+
+#define TCR_CLEAR 0 /* do NOTHING */
+/* the normal settings for the TCR register : */
+#define TCR_NORMAL (TCR_ENABLE | TCR_PAD_ENABLE)
+
+
+#define EPH_STATUS 2
+#define ES_LINK_OK 0x4000 /* is the link integrity ok ? */
+
+#define RCR 4
+#define RCR_SOFTRESET 0x8000 /* resets the chip */
+#define RCR_STRIP_CRC 0x200 /* strips CRC */
+#define RCR_ENABLE 0x100 /* IFF this is set, we can receive packets */
+#define RCR_ALMUL 0x4 /* receive all multicast packets */
+#define RCR_PROMISC 0x2 /* enable promiscuous mode */
+
+/* the normal settings for the RCR register : */
+#define RCR_NORMAL (RCR_STRIP_CRC | RCR_ENABLE)
+#define RCR_CLEAR 0x0 /* set it to a base state */
+
+#define COUNTER 6
+#define MIR 8
+#define MCR 10
+/* 12 is reserved */
+
+/* BANK 1 */
+#define CONFIG 0
+#define CFG_AUI_SELECT 0x100
+#define BASE 2
+#define ADDR0 4
+#define ADDR1 6
+#define ADDR2 8
+#define GENERAL 10
+#define CONTROL 12
+#define CTL_POWERDOWN 0x2000
+#define CTL_LE_ENABLE 0x80
+#define CTL_CR_ENABLE 0x40
+#define CTL_TE_ENABLE 0x0020
+#define CTL_AUTO_RELEASE 0x0800
+#define CTL_EPROM_ACCESS 0x0003 /* high if Eprom is being read */
+
+/* BANK 2 */
+#define MMU_CMD 0
+#define MC_BUSY 1 /* only readable bit in the register */
+#define MC_NOP 0
+#define MC_ALLOC 0x20 /* or with number of 256 byte packets */
+#define MC_RESET 0x40
+#define MC_REMOVE 0x60 /* remove the current rx packet */
+#define MC_RELEASE 0x80 /* remove and release the current rx packet */
+#define MC_FREEPKT 0xA0 /* Release packet in PNR register */
+#define MC_ENQUEUE 0xC0 /* Enqueue the packet for transmit */
+
+#define PNR_ARR 2
+#define FIFO_PORTS 4
+
+#define FP_RXEMPTY 0x8000
+#define FP_TXEMPTY 0x80
+
+#define POINTER 6
+#define PTR_READ 0x2000
+#define PTR_RCV 0x8000
+#define PTR_AUTOINC 0x4000
+#define PTR_AUTO_INC 0x0040
+
+#define DATA_1 8
+#define DATA_2 10
+#define INTERRUPT 12
+
+#define INT_MASK 13
+#define IM_RCV_INT 0x1
+#define IM_TX_INT 0x2
+#define IM_TX_EMPTY_INT 0x4
+#define IM_ALLOC_INT 0x8
+#define IM_RX_OVRN_INT 0x10
+#define IM_EPH_INT 0x20
+#define IM_ERCV_INT 0x40 /* not on SMC9192 */
+
+/* BANK 3 */
+#define MULTICAST1 0
+#define MULTICAST2 2
+#define MULTICAST3 4
+#define MULTICAST4 6
+#define MGMT 8
+#define REVISION 10 /* ( hi: chip id low: rev # ) */
+
+
+/* this is NOT on SMC9192 */
+#define ERCV 12
+
+/* Note that 9194 and 9196 have the smame chip id,
+ * the 9196 will have revisions starting at 6 */
+#define CHIP_9190 3
+#define CHIP_9194 4
+#define CHIP_9195 5
+#define CHIP_9196 4
+#define CHIP_91100 7
+#define CHIP_91100FD 8
+
+#define REV_9196 6
+
+/*
+ * Transmit status bits
+ */
+#define TS_SUCCESS 0x0001
+#define TS_LOSTCAR 0x0400
+#define TS_LATCOL 0x0200
+#define TS_16COL 0x0010
+
+/*
+ * Receive status bits
+ */
+#define RS_ALGNERR 0x8000
+#define RS_BADCRC 0x2000
+#define RS_ODDFRAME 0x1000
+#define RS_TOOLONG 0x0800
+#define RS_TOOSHORT 0x0400
+#define RS_MULTICAST 0x0001
+#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
+
+
+/*-------------------------------------------------------------------------
+ * I define some macros to make it easier to do somewhat common
+ * or slightly complicated, repeated tasks.
+ --------------------------------------------------------------------------*/
+
+/* select a register bank, 0 to 3 */
+
+#define SMC_SELECT_BANK(x, y) { _outw( y, x + BANK_SELECT ); }
+
+/* define a small delay for the reset */
+#define SMC_DELAY(x) { inw( x + RCR );\
+ inw( x + RCR );\
+ inw( x + RCR ); }
+
+
+#endif /* _SMC_9000_H_ */
+
diff --git a/src/drivers/net/sundance.c b/src/drivers/net/sundance.c
new file mode 100644
index 00000000..4912a121
--- /dev/null
+++ b/src/drivers/net/sundance.c
@@ -0,0 +1,891 @@
+/**************************************************************************
+*
+* sundance.c -- Etherboot device driver for the Sundance ST201 "Alta".
+* Written 2002-2002 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code based on:
+* sundance.c: A Linux device driver for the Sundance ST201 "Alta"
+* Written 1999-2002 by Donald Becker
+*
+* tulip.c: Tulip and Clone Etherboot Driver
+* By Marty Conner
+* Copyright (C) 2001 Entity Cyber, Inc.
+*
+* Linux Driver Version LK1.09a, 10-Jul-2003 (2.4.25)
+*
+* REVISION HISTORY:
+* ================
+* v1.1 01-01-2003 timlegge Initial implementation
+* v1.7 04-10-2003 timlegge Transfers Linux Kernel (30 sec)
+* v1.8 04-13-2003 timlegge Fix multiple transmission bug
+* v1.9 08-19-2003 timlegge Support Multicast
+* v1.10 01-17-2004 timlegge Initial driver output cleanup
+* v1.11 03-21-2004 timlegge Remove unused variables
+* v1.12 03-21-2004 timlegge Remove excess MII defines
+* v1.13 03-24-2004 timlegge Update to Linux 2.4.25 driver
+*
+****************************************************************************/
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include "pci.h"
+#include "timer.h"
+#include "mii.h"
+
+#define drv_version "v1.12"
+#define drv_date "2004-03-21"
+
+typedef unsigned char u8;
+typedef signed char s8;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned int u32;
+typedef signed int s32;
+
+#define HZ 100
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+/* May need to be moved to mii.h */
+struct mii_if_info {
+ int phy_id;
+ int advertising;
+ unsigned int full_duplex:1; /* is full duplex? */
+};
+
+//#define EDEBUG
+#ifdef EDEBUG
+#define dprintf(x) printf x
+#else
+#define dprintf(x)
+#endif
+
+
+/* Set the mtu */
+static int mtu = 1514;
+
+/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
+ The sundance uses a 64 element hash table based on the Ethernet CRC. */
+// static int multicast_filter_limit = 32;
+
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+ Setting to > 1518 effectively disables this feature.
+ This chip can receive into any byte alignment buffers, so word-oriented
+ archs do not need a copy-align of the IP header. */
+static int rx_copybreak = 0;
+static int flowctrl = 1;
+
+/* Allow forcing the media type */
+/* media[] specifies the media type the NIC operates at.
+ autosense Autosensing active media.
+ 10mbps_hd 10Mbps half duplex.
+ 10mbps_fd 10Mbps full duplex.
+ 100mbps_hd 100Mbps half duplex.
+ 100mbps_fd 100Mbps full duplex.
+*/
+static char media[] = "autosense";
+
+/* Operational parameters that are set at compile time. */
+
+/* As Etherboot uses a Polling driver we can keep the number of rings
+to the minimum number required. In general that is 1 transmit and 4 receive receive rings. However some cards require that
+there be a minimum of 2 rings */
+#define TX_RING_SIZE 2
+#define TX_QUEUE_LEN 10 /* Limit ring entries actually used. */
+#define RX_RING_SIZE 4
+
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIME_OUT (4*HZ)
+#define PKT_BUF_SZ 1536
+
+/* Offsets to the device registers.
+ Unlike software-only systems, device drivers interact with complex hardware.
+ It's not useful to define symbolic names for every register bit in the
+ device. The name can only partially document the semantics and make
+ the driver longer and more difficult to read.
+ In general, only the important configuration values or bits changed
+ multiple times should be defined symbolically.
+*/
+enum alta_offsets {
+ DMACtrl = 0x00,
+ TxListPtr = 0x04,
+ TxDMABurstThresh = 0x08,
+ TxDMAUrgentThresh = 0x09,
+ TxDMAPollPeriod = 0x0a,
+ RxDMAStatus = 0x0c,
+ RxListPtr = 0x10,
+ DebugCtrl0 = 0x1a,
+ DebugCtrl1 = 0x1c,
+ RxDMABurstThresh = 0x14,
+ RxDMAUrgentThresh = 0x15,
+ RxDMAPollPeriod = 0x16,
+ LEDCtrl = 0x1a,
+ ASICCtrl = 0x30,
+ EEData = 0x34,
+ EECtrl = 0x36,
+ TxStartThresh = 0x3c,
+ RxEarlyThresh = 0x3e,
+ FlashAddr = 0x40,
+ FlashData = 0x44,
+ TxStatus = 0x46,
+ TxFrameId = 0x47,
+ DownCounter = 0x18,
+ IntrClear = 0x4a,
+ IntrEnable = 0x4c,
+ IntrStatus = 0x4e,
+ MACCtrl0 = 0x50,
+ MACCtrl1 = 0x52,
+ StationAddr = 0x54,
+ MaxFrameSize = 0x5A,
+ RxMode = 0x5c,
+ MIICtrl = 0x5e,
+ MulticastFilter0 = 0x60,
+ MulticastFilter1 = 0x64,
+ RxOctetsLow = 0x68,
+ RxOctetsHigh = 0x6a,
+ TxOctetsLow = 0x6c,
+ TxOctetsHigh = 0x6e,
+ TxFramesOK = 0x70,
+ RxFramesOK = 0x72,
+ StatsCarrierError = 0x74,
+ StatsLateColl = 0x75,
+ StatsMultiColl = 0x76,
+ StatsOneColl = 0x77,
+ StatsTxDefer = 0x78,
+ RxMissed = 0x79,
+ StatsTxXSDefer = 0x7a,
+ StatsTxAbort = 0x7b,
+ StatsBcastTx = 0x7c,
+ StatsBcastRx = 0x7d,
+ StatsMcastTx = 0x7e,
+ StatsMcastRx = 0x7f,
+ /* Aliased and bogus values! */
+ RxStatus = 0x0c,
+};
+enum ASICCtrl_HiWord_bit {
+ GlobalReset = 0x0001,
+ RxReset = 0x0002,
+ TxReset = 0x0004,
+ DMAReset = 0x0008,
+ FIFOReset = 0x0010,
+ NetworkReset = 0x0020,
+ HostReset = 0x0040,
+ ResetBusy = 0x0400,
+};
+
+/* Bits in the interrupt status/mask registers. */
+enum intr_status_bits {
+ IntrSummary = 0x0001, IntrPCIErr = 0x0002, IntrMACCtrl = 0x0008,
+ IntrTxDone = 0x0004, IntrRxDone = 0x0010, IntrRxStart = 0x0020,
+ IntrDrvRqst = 0x0040,
+ StatsMax = 0x0080, LinkChange = 0x0100,
+ IntrTxDMADone = 0x0200, IntrRxDMADone = 0x0400,
+};
+
+/* Bits in the RxMode register. */
+enum rx_mode_bits {
+ AcceptAllIPMulti = 0x20, AcceptMultiHash = 0x10, AcceptAll = 0x08,
+ AcceptBroadcast = 0x04, AcceptMulticast = 0x02, AcceptMyPhys =
+ 0x01,
+};
+/* Bits in MACCtrl. */
+enum mac_ctrl0_bits {
+ EnbFullDuplex = 0x20, EnbRcvLargeFrame = 0x40,
+ EnbFlowCtrl = 0x100, EnbPassRxCRC = 0x200,
+};
+enum mac_ctrl1_bits {
+ StatsEnable = 0x0020, StatsDisable = 0x0040, StatsEnabled = 0x0080,
+ TxEnable = 0x0100, TxDisable = 0x0200, TxEnabled = 0x0400,
+ RxEnable = 0x0800, RxDisable = 0x1000, RxEnabled = 0x2000,
+};
+
+/* The Rx and Tx buffer descriptors.
+ Using only 32 bit fields simplifies software endian correction.
+ This structure must be aligned, and should avoid spanning cache lines.
+*/
+struct netdev_desc {
+ u32 next_desc;
+ u32 status;
+ u32 addr;
+ u32 length;
+};
+
+/* Bits in netdev_desc.status */
+enum desc_status_bits {
+ DescOwn = 0x8000,
+ DescEndPacket = 0x4000,
+ DescEndRing = 0x2000,
+ LastFrag = 0x80000000,
+ DescIntrOnTx = 0x8000,
+ DescIntrOnDMADone = 0x80000000,
+ DisableAlign = 0x00000001,
+};
+
+/**********************************************
+* Descriptor Ring and Buffer defination
+***********************************************/
+/* Define the TX Descriptor */
+static struct netdev_desc tx_ring[TX_RING_SIZE];
+
+/* Create a static buffer of size PKT_BUF_SZ for each TX Descriptor.
+ All descriptors point to a part of this buffer */
+static unsigned char txb[PKT_BUF_SZ * TX_RING_SIZE];
+
+/* Define the RX Descriptor */
+static struct netdev_desc rx_ring[RX_RING_SIZE];
+
+/* Create a static buffer of size PKT_BUF_SZ for each RX Descriptor.
+ All descriptors point to a part of this buffer */
+static unsigned char rxb[RX_RING_SIZE * PKT_BUF_SZ];
+
+/* FIXME: Move BASE to the private structure */
+static u32 BASE;
+#define EEPROM_SIZE 128
+
+enum pci_id_flags_bits {
+ PCI_USES_IO = 1, PCI_USES_MEM = 2, PCI_USES_MASTER = 4,
+ PCI_ADDR0 = 0 << 4, PCI_ADDR1 = 1 << 4, PCI_ADDR2 =
+ 2 << 4, PCI_ADDR3 = 3 << 4,
+};
+
+enum chip_capability_flags { CanHaveMII = 1, KendinPktDropBug = 2, };
+#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO | PCI_ADDR0)
+
+#define MII_CNT 4
+struct sundance_private {
+ const char *nic_name;
+ /* Frequently used values */
+
+ unsigned int cur_rx; /* Producer/consumer ring indicies */
+ unsigned int mtu;
+
+ /* These values keep track of the tranceiver/media in use */
+ unsigned int flowctrl:1;
+ unsigned int an_enable:1;
+
+ unsigned int speed;
+
+ /* MII tranceiver section */
+ struct mii_if_info mii_if;
+ int mii_preamble_required;
+ unsigned char phys[MII_CNT];
+ unsigned char pci_rev_id;
+} sdx;
+
+static struct sundance_private *sdc;
+
+/* Station Address location within the EEPROM */
+#define EEPROM_SA_OFFSET 0x10
+#define DEFAULT_INTR (IntrRxDMADone | IntrPCIErr | \
+ IntrDrvRqst | IntrTxDone | StatsMax | \
+ LinkChange)
+
+static int eeprom_read(long ioaddr, int location);
+static int mdio_read(struct nic *nic, int phy_id, unsigned int location);
+static void mdio_write(struct nic *nic, int phy_id, unsigned int location,
+ int value);
+static void set_rx_mode(struct nic *nic);
+
+static void check_duplex(struct nic *nic)
+{
+ int mii_lpa = mdio_read(nic, sdc->phys[0], MII_LPA);
+ int negotiated = mii_lpa & sdc->mii_if.advertising;
+ int duplex;
+
+ /* Force media */
+ if (!sdc->an_enable || mii_lpa == 0xffff) {
+ if (sdc->mii_if.full_duplex)
+ outw(inw(BASE + MACCtrl0) | EnbFullDuplex,
+ BASE + MACCtrl0);
+ return;
+ }
+
+ /* Autonegotiation */
+ duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
+ if (sdc->mii_if.full_duplex != duplex) {
+ sdc->mii_if.full_duplex = duplex;
+ dprintf(("%s: Setting %s-duplex based on MII #%d "
+ "negotiated capability %4.4x.\n", sdc->nic_name,
+ duplex ? "full" : "half", sdc->phys[0],
+ negotiated));
+ outw(inw(BASE + MACCtrl0) | duplex ? 0x20 : 0,
+ BASE + MACCtrl0);
+ }
+}
+
+
+/**************************************************************************
+ * init_ring - setup the tx and rx descriptors
+ *************************************************************************/
+static void init_ring(struct nic *nic __unused)
+{
+ int i;
+
+ sdc->cur_rx = 0;
+
+ /* Initialize all the Rx descriptors */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ rx_ring[i].next_desc = virt_to_le32desc(&rx_ring[i + 1]);
+ rx_ring[i].status = 0;
+ rx_ring[i].length = 0;
+ rx_ring[i].addr = 0;
+ }
+
+ /* Mark the last entry as wrapping the ring */
+ rx_ring[i - 1].next_desc = virt_to_le32desc(&rx_ring[0]);
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ rx_ring[i].addr = virt_to_le32desc(&rxb[i * PKT_BUF_SZ]);
+ rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ | LastFrag);
+ }
+
+ /* We only use one transmit buffer, but two
+ * descriptors so transmit engines have somewhere
+ * to point should they feel the need */
+ tx_ring[0].status = 0x00000000;
+ tx_ring[0].addr = virt_to_bus(&txb[0]);
+ tx_ring[0].next_desc = 0; /* virt_to_bus(&tx_ring[1]); */
+
+ /* This descriptor is never used */
+ tx_ring[1].status = 0x00000000;
+ tx_ring[1].addr = 0; /*virt_to_bus(&txb[0]); */
+ tx_ring[1].next_desc = 0;
+
+ /* Mark the last entry as wrapping the ring,
+ * though this should never happen */
+ tx_ring[1].length = cpu_to_le32(LastFrag | PKT_BUF_SZ);
+}
+
+/**************************************************************************
+ * RESET - Reset Adapter
+ * ***********************************************************************/
+static void sundance_reset(struct nic *nic)
+{
+ int i;
+
+ init_ring(nic);
+
+ outl(virt_to_le32desc(&rx_ring[0]), BASE + RxListPtr);
+ /* The Tx List Pointer is written as packets are queued */
+
+ /* Initialize other registers. */
+ /* __set_mac_addr(dev); */
+ {
+ u16 addr16;
+
+ addr16 = (nic->node_addr[0] | (nic->node_addr[1] << 8));
+ outw(addr16, BASE + StationAddr);
+ addr16 = (nic->node_addr[2] | (nic->node_addr[3] << 8));
+ outw(addr16, BASE + StationAddr + 2);
+ addr16 = (nic->node_addr[4] | (nic->node_addr[5] << 8));
+ outw(addr16, BASE + StationAddr + 4);
+ }
+
+ outw(sdc->mtu + 14, BASE + MaxFrameSize);
+ if (sdc->mtu > 2047) /* this will never happen with default options */
+ outl(inl(BASE + ASICCtrl) | 0x0c, BASE + ASICCtrl);
+
+ set_rx_mode(nic);
+
+ outw(0, BASE + DownCounter);
+ /* Set the chip to poll every N*30nsec */
+ outb(100, BASE + RxDMAPollPeriod);
+
+ /* Fix DFE-580TX packet drop issue */
+ if (sdc->pci_rev_id >= 0x14)
+ writeb(0x01, BASE + DebugCtrl1);
+
+ outw(RxEnable | TxEnable, BASE + MACCtrl1);
+
+ /* Construct a perfect filter frame with the mac address as first match
+ * and broadcast for all others */
+ for (i = 0; i < 192; i++)
+ txb[i] = 0xFF;
+
+ txb[0] = nic->node_addr[0];
+ txb[1] = nic->node_addr[1];
+ txb[2] = nic->node_addr[2];
+ txb[3] = nic->node_addr[3];
+ txb[4] = nic->node_addr[4];
+ txb[5] = nic->node_addr[5];
+
+ dprintf(("%s: Done sundance_reset, status: Rx %hX Tx %hX "
+ "MAC Control %hX, %hX %hX\n",
+ sdc->nic_name, (int) inl(BASE + RxStatus),
+ (int) inw(BASE + TxStatus), (int) inl(BASE + MACCtrl0),
+ (int) inw(BASE + MACCtrl1), (int) inw(BASE + MACCtrl0)));
+}
+
+/**************************************************************************
+IRQ - Wait for a frame
+***************************************************************************/
+void sundance_irq ( struct nic *nic, irq_action_t action ) {
+ unsigned int intr_status;
+
+ switch ( action ) {
+ case DISABLE :
+ case ENABLE :
+ intr_status = inw(nic->ioaddr + IntrStatus);
+ intr_status = intr_status & ~DEFAULT_INTR;
+ if ( action == ENABLE )
+ intr_status = intr_status | DEFAULT_INTR;
+ outw(intr_status, nic->ioaddr + IntrEnable);
+ break;
+ case FORCE :
+ outw(0x0200, BASE + ASICCtrl);
+ break;
+ }
+}
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int sundance_poll(struct nic *nic, int retreive)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+ int entry = sdc->cur_rx % RX_RING_SIZE;
+ u32 frame_status = le32_to_cpu(rx_ring[entry].status);
+ int intr_status;
+ int pkt_len = 0;
+
+ if (!(frame_status & DescOwn))
+ return 0;
+
+ /* There is a packet ready */
+ if(!retreive)
+ return 1;
+
+ intr_status = inw(nic->ioaddr + IntrStatus);
+ outw(intr_status, nic->ioaddr + IntrStatus);
+
+ pkt_len = frame_status & 0x1fff;
+
+ if (frame_status & 0x001f4000) {
+ dprintf(("Polling frame_status error\n")); /* Do we really care about this */
+ } else {
+ if (pkt_len < rx_copybreak) {
+ /* FIXME: What should happen Will this ever occur */
+ printf("Poll Error: pkt_len < rx_copybreak");
+ } else {
+ nic->packetlen = pkt_len;
+ memcpy(nic->packet, rxb +
+ (sdc->cur_rx * PKT_BUF_SZ), nic->packetlen);
+
+ }
+ }
+ rx_ring[entry].length = cpu_to_le32(PKT_BUF_SZ | LastFrag);
+ rx_ring[entry].status = 0;
+ entry++;
+ sdc->cur_rx = entry % RX_RING_SIZE;
+ outw(DEFAULT_INTR & ~(IntrRxDone|IntrRxDMADone),
+ nic->ioaddr + IntrStatus);
+ return 1;
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void sundance_transmit(struct nic *nic, const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p)
+{ /* Packet */
+ u16 nstype;
+ u32 to;
+
+ /* Disable the Tx */
+ outw(TxDisable, BASE + MACCtrl1);
+
+ memcpy(txb, d, ETH_ALEN);
+ memcpy(txb + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ nstype = htons((u16) t);
+ memcpy(txb + 2 * ETH_ALEN, (u8 *) & nstype, 2);
+ memcpy(txb + ETH_HLEN, p, s);
+
+ s += ETH_HLEN;
+ s &= 0x0FFF;
+ while (s < ETH_ZLEN)
+ txb[s++] = '\0';
+
+ /* Setup the transmit descriptor */
+ tx_ring[0].length = cpu_to_le32(s | LastFrag);
+ tx_ring[0].status = cpu_to_le32(0x00000001);
+
+ /* Point to transmit descriptor */
+ outl(virt_to_le32desc(&tx_ring[0]), BASE + TxListPtr);
+
+ /* Enable Tx */
+ outw(TxEnable, BASE + MACCtrl1);
+ /* Trigger an immediate send */
+ outw(0, BASE + TxStatus);
+
+ to = currticks() + TX_TIME_OUT;
+ while (!(tx_ring[0].status & 0x00010000) && (currticks() < to)); /* wait */
+
+ if (currticks() >= to) {
+ printf("TX Time Out");
+ }
+ /* Disable Tx */
+ outw(TxDisable, BASE + MACCtrl1);
+
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void sundance_disable(struct dev *dev __unused)
+{
+ /* put the card in its initial state */
+ /* This function serves 3 purposes.
+ * This disables DMA and interrupts so we don't receive
+ * unexpected packets or interrupts from the card after
+ * etherboot has finished.
+ * This frees resources so etherboot may use
+ * this driver on another interface
+ * This allows etherboot to reinitialize the interface
+ * if something is something goes wrong.
+ */
+ outw(0x0000, BASE + IntrEnable);
+ /* Stop the Chipchips Tx and Rx Status */
+ outw(TxDisable | RxDisable | StatsDisable, BASE + MACCtrl1);
+}
+
+
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+static int sundance_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *) dev;
+ u8 ee_data[EEPROM_SIZE];
+ u16 mii_ctl;
+ int i;
+ int speed;
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ /* BASE is used throughout to address the card */
+ BASE = pci->ioaddr;
+ printf(" sundance.c: Found %s Vendor=0x%hX Device=0x%hX\n",
+ pci->name, pci->vendor, pci->dev_id);
+
+ /* Get the MAC Address by reading the EEPROM */
+ for (i = 0; i < 3; i++) {
+ ((u16 *) ee_data)[i] =
+ le16_to_cpu(eeprom_read(BASE, i + EEPROM_SA_OFFSET));
+ }
+ /* Update the nic structure with the MAC Address */
+ for (i = 0; i < ETH_ALEN; i++) {
+ nic->node_addr[i] = ee_data[i];
+ }
+
+ /* Set the card as PCI Bus Master */
+ adjust_pci_device(pci);
+
+// sdc->mii_if.dev = pci;
+// sdc->mii_if.phy_id_mask = 0x1f;
+// sdc->mii_if.reg_num_mask = 0x1f;
+
+ /* point to private storage */
+ sdc = &sdx;
+
+ sdc->nic_name = pci->name;
+ sdc->mtu = mtu;
+
+ pci_read_config_byte(pci, PCI_REVISION_ID, &sdc->pci_rev_id);
+ dprintf(("Device revision id: %hx\n", sdc->pci_rev_id));
+ /* Print out some hardware info */
+ printf("%s: %! at ioaddr %hX, ", pci->name, nic->node_addr, BASE);
+ sdc->mii_preamble_required = 0;
+ if (1) {
+ int phy, phy_idx = 0;
+ sdc->phys[0] = 1; /* Default Setting */
+ sdc->mii_preamble_required++;
+ for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) {
+ int mii_status = mdio_read(nic, phy, MII_BMSR);
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ sdc->phys[phy_idx++] = phy;
+ sdc->mii_if.advertising =
+ mdio_read(nic, phy, MII_ADVERTISE);
+ if ((mii_status & 0x0040) == 0)
+ sdc->mii_preamble_required++;
+ dprintf
+ (("%s: MII PHY found at address %d, status " "%hX advertising %hX\n", sdc->nic_name, phy, mii_status, sdc->mii_if.advertising));
+ }
+ }
+ sdc->mii_preamble_required--;
+ if (phy_idx == 0)
+ printf("%s: No MII transceiver found!\n",
+ sdc->nic_name);
+ sdc->mii_if.phy_id = sdc->phys[0];
+ }
+
+ /* Parse override configuration */
+ sdc->an_enable = 1;
+ if (strcasecmp(media, "autosense") != 0) {
+ sdc->an_enable = 0;
+ if (strcasecmp(media, "100mbps_fd") == 0 ||
+ strcasecmp(media, "4") == 0) {
+ sdc->speed = 100;
+ sdc->mii_if.full_duplex = 1;
+ } else if (strcasecmp(media, "100mbps_hd") == 0
+ || strcasecmp(media, "3") == 0) {
+ sdc->speed = 100;
+ sdc->mii_if.full_duplex = 0;
+ } else if (strcasecmp(media, "10mbps_fd") == 0 ||
+ strcasecmp(media, "2") == 0) {
+ sdc->speed = 10;
+ sdc->mii_if.full_duplex = 1;
+ } else if (strcasecmp(media, "10mbps_hd") == 0 ||
+ strcasecmp(media, "1") == 0) {
+ sdc->speed = 10;
+ sdc->mii_if.full_duplex = 0;
+ } else {
+ sdc->an_enable = 1;
+ }
+ }
+ if (flowctrl == 1)
+ sdc->flowctrl = 1;
+
+ /* Fibre PHY? */
+ if (inl(BASE + ASICCtrl) & 0x80) {
+ /* Default 100Mbps Full */
+ if (sdc->an_enable) {
+ sdc->speed = 100;
+ sdc->mii_if.full_duplex = 1;
+ sdc->an_enable = 0;
+ }
+ }
+
+ /* The Linux driver uses flow control and resets the link here. This means the
+ mii section from above would need to be re done I believe. Since it serves
+ no real purpose leave it out. */
+
+ /* Force media type */
+ if (!sdc->an_enable) {
+ mii_ctl = 0;
+ mii_ctl |= (sdc->speed == 100) ? BMCR_SPEED100 : 0;
+ mii_ctl |= (sdc->mii_if.full_duplex) ? BMCR_FULLDPLX : 0;
+ mdio_write(nic, sdc->phys[0], MII_BMCR, mii_ctl);
+ printf("Override speed=%d, %s duplex\n",
+ sdc->speed,
+ sdc->mii_if.full_duplex ? "Full" : "Half");
+ }
+
+ /* Reset the chip to erase previous misconfiguration */
+ dprintf(("ASIC Control is %x.\n", inl(BASE + ASICCtrl)));
+ outw(0x007f, BASE + ASICCtrl + 2);
+ dprintf(("ASIC Control is now %x.\n", inl(BASE + ASICCtrl)));
+
+ sundance_reset(nic);
+ if (sdc->an_enable) {
+ u16 mii_advertise, mii_lpa;
+ mii_advertise =
+ mdio_read(nic, sdc->phys[0], MII_ADVERTISE);
+ mii_lpa = mdio_read(nic, sdc->phys[0], MII_LPA);
+ mii_advertise &= mii_lpa;
+ if (mii_advertise & ADVERTISE_100FULL)
+ sdc->speed = 100;
+ else if (mii_advertise & ADVERTISE_100HALF)
+ sdc->speed = 100;
+ else if (mii_advertise & ADVERTISE_10FULL)
+ sdc->speed = 10;
+ else if (mii_advertise & ADVERTISE_10HALF)
+ sdc->speed = 10;
+ } else {
+ mii_ctl = mdio_read(nic, sdc->phys[0], MII_BMCR);
+ speed = (mii_ctl & BMCR_SPEED100) ? 100 : 10;
+ sdc->speed = speed;
+ printf("%s: Link changed: %dMbps ,", sdc->nic_name, speed);
+ printf("%s duplex.\n", (mii_ctl & BMCR_FULLDPLX) ?
+ "full" : "half");
+ }
+ check_duplex(nic);
+ if (sdc->flowctrl && sdc->mii_if.full_duplex) {
+ outw(inw(BASE + MulticastFilter1 + 2) | 0x0200,
+ BASE + MulticastFilter1 + 2);
+ outw(inw(BASE + MACCtrl0) | EnbFlowCtrl, BASE + MACCtrl0);
+ }
+ printf("%dMbps, %s-Duplex\n", sdc->speed,
+ sdc->mii_if.full_duplex ? "Full" : "Half");
+
+ /* point to NIC specific routines */
+ dev->disable = sundance_disable;
+ nic->poll = sundance_poll;
+ nic->transmit = sundance_transmit;
+ nic->irqno = pci->irq;
+ nic->irq = sundance_irq;
+ nic->ioaddr = BASE;
+
+ return 1;
+}
+
+
+/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. */
+static int eeprom_read(long ioaddr, int location)
+{
+ int boguscnt = 10000; /* Typical 1900 ticks */
+ outw(0x0200 | (location & 0xff), ioaddr + EECtrl);
+ do {
+ if (!(inw(ioaddr + EECtrl) & 0x8000)) {
+ return inw(ioaddr + EEData);
+ }
+ }
+ while (--boguscnt > 0);
+ return 0;
+}
+
+/* MII transceiver control section.
+ Read and write the MII registers using software-generated serial
+ MDIO protocol. See the MII specifications or DP83840A data sheet
+ for details.
+
+ The maximum data clock rate is 2.5 Mhz.
+ The timing is decoupled from the processor clock by flushing the write
+ from the CPU write buffer with a following read, and using PCI
+ transaction time. */
+
+#define mdio_in(mdio_addr) inb(mdio_addr)
+#define mdio_out(value, mdio_addr) outb(value, mdio_addr)
+#define mdio_delay(mdio_addr) inb(mdio_addr)
+
+enum mii_reg_bits {
+ MDIO_ShiftClk = 0x0001, MDIO_Data = 0x0002, MDIO_EnbOutput =
+ 0x0004,
+};
+#define MDIO_EnbIn (0)
+#define MDIO_WRITE0 (MDIO_EnbOutput)
+#define MDIO_WRITE1 (MDIO_Data | MDIO_EnbOutput)
+
+/* Generate the preamble required for initial synchronization and
+ a few older transceivers. */
+static void mdio_sync(long mdio_addr)
+{
+ int bits = 32;
+
+ /* Establish sync by sending at least 32 logic ones. */
+ while (--bits >= 0) {
+ mdio_out(MDIO_WRITE1, mdio_addr);
+ mdio_delay(mdio_addr);
+ mdio_out(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+}
+
+static int
+mdio_read(struct nic *nic __unused, int phy_id, unsigned int location)
+{
+ long mdio_addr = BASE + MIICtrl;
+ int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+ int i, retval = 0;
+
+ if (sdc->mii_preamble_required)
+ mdio_sync(mdio_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 15; i >= 0; i--) {
+ int dataval =
+ (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+
+ mdio_out(dataval, mdio_addr);
+ mdio_delay(mdio_addr);
+ mdio_out(dataval | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 19; i > 0; i--) {
+ mdio_out(MDIO_EnbIn, mdio_addr);
+ mdio_delay(mdio_addr);
+ retval = (retval << 1) | ((mdio_in(mdio_addr) & MDIO_Data)
+ ? 1 : 0);
+ mdio_out(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ return (retval >> 1) & 0xffff;
+}
+
+static void
+mdio_write(struct nic *nic __unused, int phy_id,
+ unsigned int location, int value)
+{
+ long mdio_addr = BASE + MIICtrl;
+ int mii_cmd =
+ (0x5002 << 16) | (phy_id << 23) | (location << 18) | value;
+ int i;
+
+ if (sdc->mii_preamble_required)
+ mdio_sync(mdio_addr);
+
+ /* Shift the command bits out. */
+ for (i = 31; i >= 0; i--) {
+ int dataval =
+ (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+ mdio_out(dataval, mdio_addr);
+ mdio_delay(mdio_addr);
+ mdio_out(dataval | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ /* Clear out extra bits. */
+ for (i = 2; i > 0; i--) {
+ mdio_out(MDIO_EnbIn, mdio_addr);
+ mdio_delay(mdio_addr);
+ mdio_out(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ return;
+}
+
+static void set_rx_mode(struct nic *nic __unused)
+{
+ int i;
+ u16 mc_filter[4]; /* Multicast hash filter */
+ u32 rx_mode;
+
+ memset(mc_filter, 0xff, sizeof(mc_filter));
+ rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+
+ if (sdc->mii_if.full_duplex && sdc->flowctrl)
+ mc_filter[3] |= 0x0200;
+ for (i = 0; i < 4; i++)
+ outw(mc_filter[i], BASE + MulticastFilter0 + i * 2);
+ outb(rx_mode, BASE + RxMode);
+ return;
+}
+
+static struct pci_id sundance_nics[] = {
+ PCI_ROM(0x13f0, 0x0201, "sundance", "ST201 Sundance 'Alta' based Adaptor"),
+ PCI_ROM(0x1186, 0x1002, "dfe530txs", "D-Link DFE530TXS (Sundance ST201 Alta)"),
+};
+
+static struct pci_driver sundance_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "SUNDANCE/PCI",
+ .probe = sundance_probe,
+ .ids = sundance_nics,
+ .id_count = sizeof(sundance_nics) / sizeof(sundance_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/tg3.c b/src/drivers/net/tg3.c
new file mode 100644
index 00000000..e1db54e6
--- /dev/null
+++ b/src/drivers/net/tg3.c
@@ -0,0 +1,3394 @@
+/* $Id$
+ * tg3.c: Broadcom Tigon3 ethernet driver.
+ *
+ * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com)
+ * Copyright (C) 2001, 2002 Jeff Garzik (jgarzik@mandrakesoft.com)
+ * Copyright (C) 2003 Eric Biederman (ebiederman@lnxi.com) [etherboot port]
+ */
+
+/* 11-13-2003 timlegge Fix Issue with NetGear GA302T
+ * 11-18-2003 ebiederm Generalize NetGear Fix to what the code was supposed to be.
+ * 01-06-2005 Alf (Frederic Olivie) Add Dell bcm 5751 (0x1677) support
+ */
+
+#include "etherboot.h"
+#include "nic.h"
+#include "pci.h"
+#include "timer.h"
+#include "string.h"
+#include "tg3.h"
+
+#define SUPPORT_COPPER_PHY 1
+#define SUPPORT_FIBER_PHY 1
+#define SUPPORT_LINK_REPORT 1
+#define SUPPORT_PARTNO_STR 1
+#define SUPPORT_PHY_STR 1
+
+struct tg3 tg3;
+
+/* Dummy defines for error handling */
+#define EBUSY 1
+#define ENODEV 2
+#define EINVAL 3
+#define ENOMEM 4
+
+
+/* These numbers seem to be hard coded in the NIC firmware somehow.
+ * You can't change the ring sizes, but you can change where you place
+ * them in the NIC onboard memory.
+ */
+#define TG3_RX_RING_SIZE 512
+#define TG3_DEF_RX_RING_PENDING 20 /* RX_RING_PENDING seems to be o.k. at 20 and 200 */
+#define TG3_RX_RCB_RING_SIZE 1024
+
+/* (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 ? \
+ 512 : 1024) */
+#define TG3_TX_RING_SIZE 512
+#define TG3_DEF_TX_RING_PENDING (TG3_TX_RING_SIZE - 1)
+
+#define TG3_RX_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * TG3_RX_RING_SIZE)
+#define TG3_RX_RCB_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * TG3_RX_RCB_RING_SIZE)
+
+#define TG3_TX_RING_BYTES (sizeof(struct tg3_tx_buffer_desc) * TG3_TX_RING_SIZE)
+#define NEXT_TX(N) (((N) + 1) & (TG3_TX_RING_SIZE - 1))
+#define PREV_TX(N) (((N) - 1) & (TG3_TX_RING_SIZE - 1))
+
+#define RX_PKT_BUF_SZ (1536 + 2 + 64)
+
+
+static struct bss {
+ struct tg3_rx_buffer_desc rx_std[TG3_RX_RING_SIZE];
+ struct tg3_rx_buffer_desc rx_rcb[TG3_RX_RCB_RING_SIZE];
+ struct tg3_tx_buffer_desc tx_ring[TG3_TX_RING_SIZE];
+ struct tg3_hw_status hw_status;
+ struct tg3_hw_stats hw_stats;
+ unsigned char rx_bufs[TG3_DEF_RX_RING_PENDING][RX_PKT_BUF_SZ];
+} tg3_bss;
+
+/**
+ * pci_save_state - save the PCI configuration space of a device before suspending
+ * @dev: - PCI device that we're dealing with
+ * @buffer: - buffer to hold config space context
+ *
+ * @buffer must be large enough to hold the entire PCI 2.2 config space
+ * (>= 64 bytes).
+ */
+static int pci_save_state(struct pci_device *dev, uint32_t *buffer)
+{
+ int i;
+ for (i = 0; i < 16; i++)
+ pci_read_config_dword(dev, i * 4,&buffer[i]);
+ return 0;
+}
+
+/**
+ * pci_restore_state - Restore the saved state of a PCI device
+ * @dev: - PCI device that we're dealing with
+ * @buffer: - saved PCI config space
+ *
+ */
+static int pci_restore_state(struct pci_device *dev, uint32_t *buffer)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ pci_write_config_dword(dev,i * 4, buffer[i]);
+ return 0;
+}
+
+static void tg3_write_indirect_reg32(uint32_t off, uint32_t val)
+{
+ pci_write_config_dword(tg3.pdev, TG3PCI_REG_BASE_ADDR, off);
+ pci_write_config_dword(tg3.pdev, TG3PCI_REG_DATA, val);
+}
+
+#define tw32(reg,val) tg3_write_indirect_reg32((reg),(val))
+#define tw32_mailbox(reg, val) writel(((val) & 0xffffffff), tg3.regs + (reg))
+#define tw16(reg,val) writew(((val) & 0xffff), tg3.regs + (reg))
+#define tw8(reg,val) writeb(((val) & 0xff), tg3.regs + (reg))
+#define tr32(reg) readl(tg3.regs + (reg))
+#define tr16(reg) readw(tg3.regs + (reg))
+#define tr8(reg) readb(tg3.regs + (reg))
+
+static void tw32_carefully(uint32_t reg, uint32_t val)
+{
+ tw32(reg, val);
+ tr32(reg);
+ udelay(100);
+}
+
+static void tw32_mailbox2(uint32_t reg, uint32_t val)
+{
+ tw32_mailbox(reg, val);
+ tr32(reg);
+}
+
+static void tg3_write_mem(uint32_t off, uint32_t val)
+{
+ pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
+ pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_DATA, val);
+
+ /* Always leave this as zero. */
+ pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);
+}
+
+static void tg3_read_mem(uint32_t off, uint32_t *val)
+{
+ pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
+ pci_read_config_dword(tg3.pdev, TG3PCI_MEM_WIN_DATA, val);
+
+ /* Always leave this as zero. */
+ pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);
+}
+
+static void tg3_disable_ints(struct tg3 *tp)
+{
+ tw32(TG3PCI_MISC_HOST_CTRL,
+ (tp->misc_host_ctrl | MISC_HOST_CTRL_MASK_PCI_INT));
+ tw32_mailbox2(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001);
+}
+
+static void tg3_switch_clocks(struct tg3 *tp)
+{
+ uint32_t orig_clock_ctrl, clock_ctrl;
+
+ clock_ctrl = tr32(TG3PCI_CLOCK_CTRL);
+
+ orig_clock_ctrl = clock_ctrl;
+ clock_ctrl &= (CLOCK_CTRL_FORCE_CLKRUN | CLOCK_CTRL_CLKRUN_OENABLE | 0x1f);
+ tp->pci_clock_ctrl = clock_ctrl;
+
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) &&
+ (!((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750)
+ && (tp->tg3_flags & TG3_FLAG_ENABLE_ASF))) &&
+ (orig_clock_ctrl & CLOCK_CTRL_44MHZ_CORE)!=0) {
+ tw32_carefully(TG3PCI_CLOCK_CTRL,
+ clock_ctrl | (CLOCK_CTRL_44MHZ_CORE | CLOCK_CTRL_ALTCLK));
+ tw32_carefully(TG3PCI_CLOCK_CTRL,
+ clock_ctrl | (CLOCK_CTRL_ALTCLK));
+ }
+ tw32_carefully(TG3PCI_CLOCK_CTRL, clock_ctrl);
+}
+
+#define PHY_BUSY_LOOPS 5000
+
+static int tg3_readphy(struct tg3 *tp, int reg, uint32_t *val)
+{
+ uint32_t frame_val;
+ int loops, ret;
+
+ tw32_carefully(MAC_MI_MODE, tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL);
+
+ *val = 0xffffffff;
+
+ frame_val = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) &
+ MI_COM_PHY_ADDR_MASK);
+ frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) &
+ MI_COM_REG_ADDR_MASK);
+ frame_val |= (MI_COM_CMD_READ | MI_COM_START);
+
+ tw32_carefully(MAC_MI_COM, frame_val);
+
+ loops = PHY_BUSY_LOOPS;
+ while (loops-- > 0) {
+ udelay(10);
+ frame_val = tr32(MAC_MI_COM);
+
+ if ((frame_val & MI_COM_BUSY) == 0) {
+ udelay(5);
+ frame_val = tr32(MAC_MI_COM);
+ break;
+ }
+ }
+
+ ret = -EBUSY;
+ if (loops > 0) {
+ *val = frame_val & MI_COM_DATA_MASK;
+ ret = 0;
+ }
+
+ tw32_carefully(MAC_MI_MODE, tp->mi_mode);
+
+ return ret;
+}
+
+static int tg3_writephy(struct tg3 *tp, int reg, uint32_t val)
+{
+ uint32_t frame_val;
+ int loops, ret;
+
+ tw32_carefully(MAC_MI_MODE, tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL);
+
+ frame_val = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) &
+ MI_COM_PHY_ADDR_MASK);
+ frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) &
+ MI_COM_REG_ADDR_MASK);
+ frame_val |= (val & MI_COM_DATA_MASK);
+ frame_val |= (MI_COM_CMD_WRITE | MI_COM_START);
+
+ tw32_carefully(MAC_MI_COM, frame_val);
+
+ loops = PHY_BUSY_LOOPS;
+ while (loops-- > 0) {
+ udelay(10);
+ frame_val = tr32(MAC_MI_COM);
+ if ((frame_val & MI_COM_BUSY) == 0) {
+ udelay(5);
+ frame_val = tr32(MAC_MI_COM);
+ break;
+ }
+ }
+
+ ret = -EBUSY;
+ if (loops > 0)
+ ret = 0;
+
+ tw32_carefully(MAC_MI_MODE, tp->mi_mode);
+
+ return ret;
+}
+
+static int tg3_writedsp(struct tg3 *tp, uint16_t addr, uint16_t val)
+{
+ int err;
+ err = tg3_writephy(tp, MII_TG3_DSP_ADDRESS, addr);
+ err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, val);
+ return err;
+}
+
+
+static void tg3_phy_set_wirespeed(struct tg3 *tp)
+{
+ uint32_t val;
+
+ if (tp->tg3_flags2 & TG3_FLG2_NO_ETH_WIRE_SPEED)
+ return;
+
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x7007);
+ tg3_readphy(tp, MII_TG3_AUX_CTRL, &val);
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, (val | (1 << 15) | (1 << 4)));
+}
+
+static int tg3_bmcr_reset(struct tg3 *tp)
+{
+ uint32_t phy_control;
+ int limit, err;
+
+ /* OK, reset it, and poll the BMCR_RESET bit until it
+ * clears or we time out.
+ */
+ phy_control = BMCR_RESET;
+ err = tg3_writephy(tp, MII_BMCR, phy_control);
+ if (err != 0)
+ return -EBUSY;
+
+ limit = 5000;
+ while (limit--) {
+ err = tg3_readphy(tp, MII_BMCR, &phy_control);
+ if (err != 0)
+ return -EBUSY;
+
+ if ((phy_control & BMCR_RESET) == 0) {
+ udelay(40);
+ break;
+ }
+ udelay(10);
+ }
+ if (limit <= 0)
+ return -EBUSY;
+
+ return 0;
+}
+
+static int tg3_wait_macro_done(struct tg3 *tp)
+{
+ int limit = 100;
+
+ while (limit--) {
+ uint32_t tmp32;
+
+ tg3_readphy(tp, 0x16, &tmp32);
+ if ((tmp32 & 0x1000) == 0)
+ break;
+ }
+ if (limit <= 0)
+ return -EBUSY;
+
+ return 0;
+}
+
+static int tg3_phy_write_and_check_testpat(struct tg3 *tp, int *resetp)
+{
+ static const uint32_t test_pat[4][6] = {
+ { 0x00005555, 0x00000005, 0x00002aaa, 0x0000000a, 0x00003456, 0x00000003 },
+ { 0x00002aaa, 0x0000000a, 0x00003333, 0x00000003, 0x0000789a, 0x00000005 },
+ { 0x00005a5a, 0x00000005, 0x00002a6a, 0x0000000a, 0x00001bcd, 0x00000003 },
+ { 0x00002a5a, 0x0000000a, 0x000033c3, 0x00000003, 0x00002ef1, 0x00000005 }
+ };
+ int chan;
+
+ for (chan = 0; chan < 4; chan++) {
+ int i;
+
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS,
+ (chan * 0x2000) | 0x0200);
+ tg3_writephy(tp, 0x16, 0x0002);
+
+ for (i = 0; i < 6; i++)
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT,
+ test_pat[chan][i]);
+
+ tg3_writephy(tp, 0x16, 0x0202);
+ if (tg3_wait_macro_done(tp)) {
+ *resetp = 1;
+ return -EBUSY;
+ }
+
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS,
+ (chan * 0x2000) | 0x0200);
+ tg3_writephy(tp, 0x16, 0x0082);
+ if (tg3_wait_macro_done(tp)) {
+ *resetp = 1;
+ return -EBUSY;
+ }
+
+ tg3_writephy(tp, 0x16, 0x0802);
+ if (tg3_wait_macro_done(tp)) {
+ *resetp = 1;
+ return -EBUSY;
+ }
+
+ for (i = 0; i < 6; i += 2) {
+ uint32_t low, high;
+
+ tg3_readphy(tp, MII_TG3_DSP_RW_PORT, &low);
+ tg3_readphy(tp, MII_TG3_DSP_RW_PORT, &high);
+ if (tg3_wait_macro_done(tp)) {
+ *resetp = 1;
+ return -EBUSY;
+ }
+ low &= 0x7fff;
+ high &= 0x000f;
+ if (low != test_pat[chan][i] ||
+ high != test_pat[chan][i+1]) {
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000b);
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x4001);
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x4005);
+
+ return -EBUSY;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int tg3_phy_reset_chanpat(struct tg3 *tp)
+{
+ int chan;
+
+ for (chan = 0; chan < 4; chan++) {
+ int i;
+
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS,
+ (chan * 0x2000) | 0x0200);
+ tg3_writephy(tp, 0x16, 0x0002);
+ for (i = 0; i < 6; i++)
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x000);
+ tg3_writephy(tp, 0x16, 0x0202);
+ if (tg3_wait_macro_done(tp))
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int tg3_phy_reset_5703_4_5(struct tg3 *tp)
+{
+ uint32_t reg32, phy9_orig;
+ int retries, do_phy_reset, err;
+
+ retries = 10;
+ do_phy_reset = 1;
+ do {
+ if (do_phy_reset) {
+ err = tg3_bmcr_reset(tp);
+ if (err)
+ return err;
+ do_phy_reset = 0;
+ }
+
+ /* Disable transmitter and interrupt. */
+ tg3_readphy(tp, MII_TG3_EXT_CTRL, &reg32);
+ reg32 |= 0x3000;
+ tg3_writephy(tp, MII_TG3_EXT_CTRL, reg32);
+
+ /* Set full-duplex, 1000 mbps. */
+ tg3_writephy(tp, MII_BMCR,
+ BMCR_FULLDPLX | TG3_BMCR_SPEED1000);
+
+ /* Set to master mode. */
+ tg3_readphy(tp, MII_TG3_CTRL, &phy9_orig);
+ tg3_writephy(tp, MII_TG3_CTRL,
+ (MII_TG3_CTRL_AS_MASTER |
+ MII_TG3_CTRL_ENABLE_AS_MASTER));
+
+ /* Enable SM_DSP_CLOCK and 6dB. */
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00);
+
+ /* Block the PHY control access. */
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8005);
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0800);
+
+ err = tg3_phy_write_and_check_testpat(tp, &do_phy_reset);
+ if (!err)
+ break;
+ } while (--retries);
+
+ err = tg3_phy_reset_chanpat(tp);
+ if (err)
+ return err;
+
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8005);
+ tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x0000);
+
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8200);
+ tg3_writephy(tp, 0x16, 0x0000);
+
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400);
+
+ tg3_writephy(tp, MII_TG3_CTRL, phy9_orig);
+
+ tg3_readphy(tp, MII_TG3_EXT_CTRL, &reg32);
+ reg32 &= ~0x3000;
+ tg3_writephy(tp, MII_TG3_EXT_CTRL, reg32);
+
+ return err;
+}
+
+/* This will reset the tigon3 PHY if there is no valid
+ * link.
+ */
+static int tg3_phy_reset(struct tg3 *tp)
+{
+ uint32_t phy_status;
+ int err;
+
+ err = tg3_readphy(tp, MII_BMSR, &phy_status);
+ err |= tg3_readphy(tp, MII_BMSR, &phy_status);
+ if (err != 0)
+ return -EBUSY;
+
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)) {
+ err = tg3_phy_reset_5703_4_5(tp);
+ if (err)
+ return err;
+ goto out;
+ }
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) {
+ // Taken from Broadcom's source code
+ tg3_writephy(tp, 0x18, 0x0c00);
+ tg3_writephy(tp, 0x17, 0x000a);
+ tg3_writephy(tp, 0x15, 0x310b);
+ tg3_writephy(tp, 0x17, 0x201f);
+ tg3_writephy(tp, 0x15, 0x9506);
+ tg3_writephy(tp, 0x17, 0x401f);
+ tg3_writephy(tp, 0x15, 0x14e2);
+ tg3_writephy(tp, 0x18, 0x0400);
+ }
+ err = tg3_bmcr_reset(tp);
+ if (err)
+ return err;
+ out:
+ tg3_phy_set_wirespeed(tp);
+ return 0;
+}
+
+static void tg3_set_power_state_0(struct tg3 *tp)
+{
+ uint16_t power_control;
+ int pm = tp->pm_cap;
+
+ /* Make sure register accesses (indirect or otherwise)
+ * will function correctly.
+ */
+ pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL, tp->misc_host_ctrl);
+
+ pci_read_config_word(tp->pdev, pm + PCI_PM_CTRL, &power_control);
+
+ power_control |= PCI_PM_CTRL_PME_STATUS;
+ power_control &= ~(PCI_PM_CTRL_STATE_MASK);
+ power_control |= 0;
+ pci_write_config_word(tp->pdev, pm + PCI_PM_CTRL, power_control);
+
+ tw32_carefully(GRC_LOCAL_CTRL, tp->grc_local_ctrl);
+
+ return;
+}
+
+
+#if SUPPORT_LINK_REPORT
+static void tg3_link_report(struct tg3 *tp)
+{
+ if (!tp->carrier_ok) {
+ printf("Link is down.\n");
+ } else {
+ printf("Link is up at %d Mbps, %s duplex. %s %s %s\n",
+ (tp->link_config.active_speed == SPEED_1000 ?
+ 1000 :
+ (tp->link_config.active_speed == SPEED_100 ?
+ 100 : 10)),
+ (tp->link_config.active_duplex == DUPLEX_FULL ?
+ "full" : "half"),
+ (tp->tg3_flags & TG3_FLAG_TX_PAUSE) ? "TX" : "",
+ (tp->tg3_flags & TG3_FLAG_RX_PAUSE) ? "RX" : "",
+ (tp->tg3_flags & (TG3_FLAG_TX_PAUSE |TG3_FLAG_RX_PAUSE)) ? "flow control" : "");
+ }
+}
+#else
+#define tg3_link_report(tp)
+#endif
+
+static void tg3_setup_flow_control(struct tg3 *tp, uint32_t local_adv, uint32_t remote_adv)
+{
+ uint32_t new_tg3_flags = 0;
+
+ if (local_adv & ADVERTISE_PAUSE_CAP) {
+ if (local_adv & ADVERTISE_PAUSE_ASYM) {
+ if (remote_adv & LPA_PAUSE_CAP)
+ new_tg3_flags |=
+ (TG3_FLAG_RX_PAUSE |
+ TG3_FLAG_TX_PAUSE);
+ else if (remote_adv & LPA_PAUSE_ASYM)
+ new_tg3_flags |=
+ (TG3_FLAG_RX_PAUSE);
+ } else {
+ if (remote_adv & LPA_PAUSE_CAP)
+ new_tg3_flags |=
+ (TG3_FLAG_RX_PAUSE |
+ TG3_FLAG_TX_PAUSE);
+ }
+ } else if (local_adv & ADVERTISE_PAUSE_ASYM) {
+ if ((remote_adv & LPA_PAUSE_CAP) &&
+ (remote_adv & LPA_PAUSE_ASYM))
+ new_tg3_flags |= TG3_FLAG_TX_PAUSE;
+ }
+
+ tp->tg3_flags &= ~(TG3_FLAG_RX_PAUSE | TG3_FLAG_TX_PAUSE);
+ tp->tg3_flags |= new_tg3_flags;
+
+ if (new_tg3_flags & TG3_FLAG_RX_PAUSE)
+ tp->rx_mode |= RX_MODE_FLOW_CTRL_ENABLE;
+ else
+ tp->rx_mode &= ~RX_MODE_FLOW_CTRL_ENABLE;
+
+ if (new_tg3_flags & TG3_FLAG_TX_PAUSE)
+ tp->tx_mode |= TX_MODE_FLOW_CTRL_ENABLE;
+ else
+ tp->tx_mode &= ~TX_MODE_FLOW_CTRL_ENABLE;
+}
+
+#if SUPPORT_COPPER_PHY
+static void tg3_aux_stat_to_speed_duplex(
+ struct tg3 *tp __unused, uint32_t val, uint8_t *speed, uint8_t *duplex)
+{
+ static const uint8_t map[] = {
+ [0] = (SPEED_INVALID << 2) | DUPLEX_INVALID,
+ [MII_TG3_AUX_STAT_10HALF >> 8] = (SPEED_10 << 2) | DUPLEX_HALF,
+ [MII_TG3_AUX_STAT_10FULL >> 8] = (SPEED_10 << 2) | DUPLEX_FULL,
+ [MII_TG3_AUX_STAT_100HALF >> 8] = (SPEED_100 << 2) | DUPLEX_HALF,
+ [MII_TG3_AUX_STAT_100_4 >> 8] = (SPEED_INVALID << 2) | DUPLEX_INVALID,
+ [MII_TG3_AUX_STAT_100FULL >> 8] = (SPEED_100 << 2) | DUPLEX_FULL,
+ [MII_TG3_AUX_STAT_1000HALF >> 8] = (SPEED_1000 << 2) | DUPLEX_HALF,
+ [MII_TG3_AUX_STAT_1000FULL >> 8] = (SPEED_1000 << 2) | DUPLEX_FULL,
+ };
+ uint8_t result;
+ result = map[(val & MII_TG3_AUX_STAT_SPDMASK) >> 8];
+ *speed = result >> 2;
+ *duplex = result & 3;
+}
+
+static int tg3_phy_copper_begin(struct tg3 *tp)
+{
+ uint32_t new_adv;
+
+ tp->link_config.advertising =
+ (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full |
+ ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full |
+ ADVERTISED_Autoneg | ADVERTISED_MII);
+
+ if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) {
+ tp->link_config.advertising &=
+ ~(ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full);
+ }
+
+ new_adv = (ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
+ if (tp->link_config.advertising & ADVERTISED_10baseT_Half) {
+ new_adv |= ADVERTISE_10HALF;
+ }
+ if (tp->link_config.advertising & ADVERTISED_10baseT_Full) {
+ new_adv |= ADVERTISE_10FULL;
+ }
+ if (tp->link_config.advertising & ADVERTISED_100baseT_Half) {
+ new_adv |= ADVERTISE_100HALF;
+ }
+ if (tp->link_config.advertising & ADVERTISED_100baseT_Full) {
+ new_adv |= ADVERTISE_100FULL;
+ }
+ tg3_writephy(tp, MII_ADVERTISE, new_adv);
+
+ if (tp->link_config.advertising &
+ (ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full)) {
+ new_adv = 0;
+ if (tp->link_config.advertising & ADVERTISED_1000baseT_Half) {
+ new_adv |= MII_TG3_CTRL_ADV_1000_HALF;
+ }
+ if (tp->link_config.advertising & ADVERTISED_1000baseT_Full) {
+ new_adv |= MII_TG3_CTRL_ADV_1000_FULL;
+ }
+ if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY) &&
+ (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 ||
+ tp->pci_chip_rev_id == CHIPREV_ID_5701_B0)) {
+ new_adv |= (MII_TG3_CTRL_AS_MASTER |
+ MII_TG3_CTRL_ENABLE_AS_MASTER);
+ }
+ tg3_writephy(tp, MII_TG3_CTRL, new_adv);
+ } else {
+ tg3_writephy(tp, MII_TG3_CTRL, 0);
+ }
+
+ tg3_writephy(tp, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
+
+ return 0;
+}
+
+static int tg3_init_5401phy_dsp(struct tg3 *tp)
+{
+ int err;
+
+ /* Turn off tap power management. */
+ err = tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c20);
+
+ err |= tg3_writedsp(tp, 0x0012, 0x1804);
+ err |= tg3_writedsp(tp, 0x0013, 0x1204);
+ err |= tg3_writedsp(tp, 0x8006, 0x0132);
+ err |= tg3_writedsp(tp, 0x8006, 0x0232);
+ err |= tg3_writedsp(tp, 0x201f, 0x0a20);
+
+ udelay(40);
+
+ return err;
+}
+
+static int tg3_setup_copper_phy(struct tg3 *tp)
+{
+ int current_link_up;
+ uint32_t bmsr, dummy;
+ int i, err;
+
+ tw32_carefully(MAC_STATUS,
+ (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED
+ | MAC_STATUS_LNKSTATE_CHANGED));
+
+ tp->mi_mode = MAC_MI_MODE_BASE;
+ tw32_carefully(MAC_MI_MODE, tp->mi_mode);
+
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x02);
+
+ /* Some third-party PHYs need to be reset on link going
+ * down.
+ */
+ if ( ( (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) ||
+ (tp->pci_chip_rev_id == CHIPREV_ID_5705_A0)) &&
+ (tp->carrier_ok)) {
+ tg3_readphy(tp, MII_BMSR, &bmsr);
+ tg3_readphy(tp, MII_BMSR, &bmsr);
+ if (!(bmsr & BMSR_LSTATUS))
+ tg3_phy_reset(tp);
+ }
+
+ if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) {
+ tg3_readphy(tp, MII_BMSR, &bmsr);
+ tg3_readphy(tp, MII_BMSR, &bmsr);
+
+ if (!(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE))
+ bmsr = 0;
+
+ if (!(bmsr & BMSR_LSTATUS)) {
+ err = tg3_init_5401phy_dsp(tp);
+ if (err)
+ return err;
+
+ tg3_readphy(tp, MII_BMSR, &bmsr);
+ for (i = 0; i < 1000; i++) {
+ udelay(10);
+ tg3_readphy(tp, MII_BMSR, &bmsr);
+ if (bmsr & BMSR_LSTATUS) {
+ udelay(40);
+ break;
+ }
+ }
+
+ if ((tp->phy_id & PHY_ID_REV_MASK) == PHY_REV_BCM5401_B0 &&
+ !(bmsr & BMSR_LSTATUS) &&
+ tp->link_config.active_speed == SPEED_1000) {
+ err = tg3_phy_reset(tp);
+ if (!err)
+ err = tg3_init_5401phy_dsp(tp);
+ if (err)
+ return err;
+ }
+ }
+ } else if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 ||
+ tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) {
+ /* 5701 {A0,B0} CRC bug workaround */
+ tg3_writephy(tp, 0x15, 0x0a75);
+ tg3_writephy(tp, 0x1c, 0x8c68);
+ tg3_writephy(tp, 0x1c, 0x8d68);
+ tg3_writephy(tp, 0x1c, 0x8c68);
+ }
+
+ /* Clear pending interrupts... */
+ tg3_readphy(tp, MII_TG3_ISTAT, &dummy);
+ tg3_readphy(tp, MII_TG3_ISTAT, &dummy);
+
+ tg3_writephy(tp, MII_TG3_IMASK, ~0);
+
+ if (tp->led_mode == led_mode_three_link)
+ tg3_writephy(tp, MII_TG3_EXT_CTRL,
+ MII_TG3_EXT_CTRL_LNK3_LED_MODE);
+ else
+ tg3_writephy(tp, MII_TG3_EXT_CTRL, 0);
+
+ current_link_up = 0;
+
+ tg3_readphy(tp, MII_BMSR, &bmsr);
+ tg3_readphy(tp, MII_BMSR, &bmsr);
+
+ if (bmsr & BMSR_LSTATUS) {
+ uint32_t aux_stat, bmcr;
+
+ tg3_readphy(tp, MII_TG3_AUX_STAT, &aux_stat);
+ for (i = 0; i < 2000; i++) {
+ udelay(10);
+ tg3_readphy(tp, MII_TG3_AUX_STAT, &aux_stat);
+ if (aux_stat)
+ break;
+ }
+
+ tg3_aux_stat_to_speed_duplex(tp, aux_stat,
+ &tp->link_config.active_speed,
+ &tp->link_config.active_duplex);
+ tg3_readphy(tp, MII_BMCR, &bmcr);
+ tg3_readphy(tp, MII_BMCR, &bmcr);
+ if (bmcr & BMCR_ANENABLE) {
+ uint32_t gig_ctrl;
+
+ current_link_up = 1;
+
+ /* Force autoneg restart if we are exiting
+ * low power mode.
+ */
+ tg3_readphy(tp, MII_TG3_CTRL, &gig_ctrl);
+ if (!(gig_ctrl & (MII_TG3_CTRL_ADV_1000_HALF |
+ MII_TG3_CTRL_ADV_1000_FULL))) {
+ current_link_up = 0;
+ }
+ } else {
+ current_link_up = 0;
+ }
+ }
+
+ if (current_link_up == 1 &&
+ (tp->link_config.active_duplex == DUPLEX_FULL)) {
+ uint32_t local_adv, remote_adv;
+
+ tg3_readphy(tp, MII_ADVERTISE, &local_adv);
+ local_adv &= (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+
+ tg3_readphy(tp, MII_LPA, &remote_adv);
+ remote_adv &= (LPA_PAUSE_CAP | LPA_PAUSE_ASYM);
+
+ /* If we are not advertising full pause capability,
+ * something is wrong. Bring the link down and reconfigure.
+ */
+ if (local_adv != ADVERTISE_PAUSE_CAP) {
+ current_link_up = 0;
+ } else {
+ tg3_setup_flow_control(tp, local_adv, remote_adv);
+ }
+ }
+
+ if (current_link_up == 0) {
+ uint32_t tmp;
+
+ tg3_phy_copper_begin(tp);
+
+ tg3_readphy(tp, MII_BMSR, &tmp);
+ tg3_readphy(tp, MII_BMSR, &tmp);
+ if (tmp & BMSR_LSTATUS)
+ current_link_up = 1;
+ }
+
+ tp->mac_mode &= ~MAC_MODE_PORT_MODE_MASK;
+ if (current_link_up == 1) {
+ if (tp->link_config.active_speed == SPEED_100 ||
+ tp->link_config.active_speed == SPEED_10)
+ tp->mac_mode |= MAC_MODE_PORT_MODE_MII;
+ else
+ tp->mac_mode |= MAC_MODE_PORT_MODE_GMII;
+ } else
+ tp->mac_mode |= MAC_MODE_PORT_MODE_GMII;
+
+ tp->mac_mode &= ~MAC_MODE_HALF_DUPLEX;
+ if (tp->link_config.active_duplex == DUPLEX_HALF)
+ tp->mac_mode |= MAC_MODE_HALF_DUPLEX;
+
+ tp->mac_mode &= ~MAC_MODE_LINK_POLARITY;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) {
+ if ((tp->led_mode == led_mode_link10) ||
+ (current_link_up == 1 &&
+ tp->link_config.active_speed == SPEED_10))
+ tp->mac_mode |= MAC_MODE_LINK_POLARITY;
+ } else {
+ if (current_link_up == 1)
+ tp->mac_mode |= MAC_MODE_LINK_POLARITY;
+ tw32(MAC_LED_CTRL, LED_CTRL_PHY_MODE_1);
+ }
+
+ /* ??? Without this setting Netgear GA302T PHY does not
+ * ??? send/receive packets...
+ * With this other PHYs cannot bring up the link
+ */
+ if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5411 &&
+ tp->pci_chip_rev_id == CHIPREV_ID_5700_ALTIMA) {
+ tp->mi_mode |= MAC_MI_MODE_AUTO_POLL;
+ tw32_carefully(MAC_MI_MODE, tp->mi_mode);
+ }
+
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ /* Link change polled. */
+ tw32_carefully(MAC_EVENT, 0);
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 &&
+ current_link_up == 1 &&
+ tp->link_config.active_speed == SPEED_1000 &&
+ ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) ||
+ (tp->tg3_flags & TG3_FLAG_PCI_HIGH_SPEED))) {
+ udelay(120);
+ tw32_carefully(MAC_STATUS,
+ (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED));
+ tg3_write_mem(
+ NIC_SRAM_FIRMWARE_MBOX,
+ NIC_SRAM_FIRMWARE_MBOX_MAGIC2);
+ }
+
+ if (current_link_up != tp->carrier_ok) {
+ tp->carrier_ok = current_link_up;
+ tg3_link_report(tp);
+ }
+
+ return 0;
+}
+#else
+#define tg3_setup_copper_phy(TP) (-EINVAL)
+#endif /* SUPPORT_COPPER_PHY */
+
+#if SUPPORT_FIBER_PHY
+struct tg3_fiber_aneginfo {
+ int state;
+#define ANEG_STATE_UNKNOWN 0
+#define ANEG_STATE_AN_ENABLE 1
+#define ANEG_STATE_RESTART_INIT 2
+#define ANEG_STATE_RESTART 3
+#define ANEG_STATE_DISABLE_LINK_OK 4
+#define ANEG_STATE_ABILITY_DETECT_INIT 5
+#define ANEG_STATE_ABILITY_DETECT 6
+#define ANEG_STATE_ACK_DETECT_INIT 7
+#define ANEG_STATE_ACK_DETECT 8
+#define ANEG_STATE_COMPLETE_ACK_INIT 9
+#define ANEG_STATE_COMPLETE_ACK 10
+#define ANEG_STATE_IDLE_DETECT_INIT 11
+#define ANEG_STATE_IDLE_DETECT 12
+#define ANEG_STATE_LINK_OK 13
+#define ANEG_STATE_NEXT_PAGE_WAIT_INIT 14
+#define ANEG_STATE_NEXT_PAGE_WAIT 15
+
+ uint32_t flags;
+#define MR_AN_ENABLE 0x00000001
+#define MR_RESTART_AN 0x00000002
+#define MR_AN_COMPLETE 0x00000004
+#define MR_PAGE_RX 0x00000008
+#define MR_NP_LOADED 0x00000010
+#define MR_TOGGLE_TX 0x00000020
+#define MR_LP_ADV_FULL_DUPLEX 0x00000040
+#define MR_LP_ADV_HALF_DUPLEX 0x00000080
+#define MR_LP_ADV_SYM_PAUSE 0x00000100
+#define MR_LP_ADV_ASYM_PAUSE 0x00000200
+#define MR_LP_ADV_REMOTE_FAULT1 0x00000400
+#define MR_LP_ADV_REMOTE_FAULT2 0x00000800
+#define MR_LP_ADV_NEXT_PAGE 0x00001000
+#define MR_TOGGLE_RX 0x00002000
+#define MR_NP_RX 0x00004000
+
+#define MR_LINK_OK 0x80000000
+
+ unsigned long link_time, cur_time;
+
+ uint32_t ability_match_cfg;
+ int ability_match_count;
+
+ char ability_match, idle_match, ack_match;
+
+ uint32_t txconfig, rxconfig;
+#define ANEG_CFG_NP 0x00000080
+#define ANEG_CFG_ACK 0x00000040
+#define ANEG_CFG_RF2 0x00000020
+#define ANEG_CFG_RF1 0x00000010
+#define ANEG_CFG_PS2 0x00000001
+#define ANEG_CFG_PS1 0x00008000
+#define ANEG_CFG_HD 0x00004000
+#define ANEG_CFG_FD 0x00002000
+#define ANEG_CFG_INVAL 0x00001f06
+
+};
+#define ANEG_OK 0
+#define ANEG_DONE 1
+#define ANEG_TIMER_ENAB 2
+#define ANEG_FAILED -1
+
+#define ANEG_STATE_SETTLE_TIME 10000
+
+static int tg3_fiber_aneg_smachine(struct tg3 *tp,
+ struct tg3_fiber_aneginfo *ap)
+{
+ unsigned long delta;
+ uint32_t rx_cfg_reg;
+ int ret;
+
+ if (ap->state == ANEG_STATE_UNKNOWN) {
+ ap->rxconfig = 0;
+ ap->link_time = 0;
+ ap->cur_time = 0;
+ ap->ability_match_cfg = 0;
+ ap->ability_match_count = 0;
+ ap->ability_match = 0;
+ ap->idle_match = 0;
+ ap->ack_match = 0;
+ }
+ ap->cur_time++;
+
+ if (tr32(MAC_STATUS) & MAC_STATUS_RCVD_CFG) {
+ rx_cfg_reg = tr32(MAC_RX_AUTO_NEG);
+
+ if (rx_cfg_reg != ap->ability_match_cfg) {
+ ap->ability_match_cfg = rx_cfg_reg;
+ ap->ability_match = 0;
+ ap->ability_match_count = 0;
+ } else {
+ if (++ap->ability_match_count > 1) {
+ ap->ability_match = 1;
+ ap->ability_match_cfg = rx_cfg_reg;
+ }
+ }
+ if (rx_cfg_reg & ANEG_CFG_ACK)
+ ap->ack_match = 1;
+ else
+ ap->ack_match = 0;
+
+ ap->idle_match = 0;
+ } else {
+ ap->idle_match = 1;
+ ap->ability_match_cfg = 0;
+ ap->ability_match_count = 0;
+ ap->ability_match = 0;
+ ap->ack_match = 0;
+
+ rx_cfg_reg = 0;
+ }
+
+ ap->rxconfig = rx_cfg_reg;
+ ret = ANEG_OK;
+
+ switch(ap->state) {
+ case ANEG_STATE_UNKNOWN:
+ if (ap->flags & (MR_AN_ENABLE | MR_RESTART_AN))
+ ap->state = ANEG_STATE_AN_ENABLE;
+
+ /* fallthru */
+ case ANEG_STATE_AN_ENABLE:
+ ap->flags &= ~(MR_AN_COMPLETE | MR_PAGE_RX);
+ if (ap->flags & MR_AN_ENABLE) {
+ ap->link_time = 0;
+ ap->cur_time = 0;
+ ap->ability_match_cfg = 0;
+ ap->ability_match_count = 0;
+ ap->ability_match = 0;
+ ap->idle_match = 0;
+ ap->ack_match = 0;
+
+ ap->state = ANEG_STATE_RESTART_INIT;
+ } else {
+ ap->state = ANEG_STATE_DISABLE_LINK_OK;
+ }
+ break;
+
+ case ANEG_STATE_RESTART_INIT:
+ ap->link_time = ap->cur_time;
+ ap->flags &= ~(MR_NP_LOADED);
+ ap->txconfig = 0;
+ tw32(MAC_TX_AUTO_NEG, 0);
+ tp->mac_mode |= MAC_MODE_SEND_CONFIGS;
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ ret = ANEG_TIMER_ENAB;
+ ap->state = ANEG_STATE_RESTART;
+
+ /* fallthru */
+ case ANEG_STATE_RESTART:
+ delta = ap->cur_time - ap->link_time;
+ if (delta > ANEG_STATE_SETTLE_TIME) {
+ ap->state = ANEG_STATE_ABILITY_DETECT_INIT;
+ } else {
+ ret = ANEG_TIMER_ENAB;
+ }
+ break;
+
+ case ANEG_STATE_DISABLE_LINK_OK:
+ ret = ANEG_DONE;
+ break;
+
+ case ANEG_STATE_ABILITY_DETECT_INIT:
+ ap->flags &= ~(MR_TOGGLE_TX);
+ ap->txconfig = (ANEG_CFG_FD | ANEG_CFG_PS1);
+ tw32(MAC_TX_AUTO_NEG, ap->txconfig);
+ tp->mac_mode |= MAC_MODE_SEND_CONFIGS;
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ ap->state = ANEG_STATE_ABILITY_DETECT;
+ break;
+
+ case ANEG_STATE_ABILITY_DETECT:
+ if (ap->ability_match != 0 && ap->rxconfig != 0) {
+ ap->state = ANEG_STATE_ACK_DETECT_INIT;
+ }
+ break;
+
+ case ANEG_STATE_ACK_DETECT_INIT:
+ ap->txconfig |= ANEG_CFG_ACK;
+ tw32(MAC_TX_AUTO_NEG, ap->txconfig);
+ tp->mac_mode |= MAC_MODE_SEND_CONFIGS;
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ ap->state = ANEG_STATE_ACK_DETECT;
+
+ /* fallthru */
+ case ANEG_STATE_ACK_DETECT:
+ if (ap->ack_match != 0) {
+ if ((ap->rxconfig & ~ANEG_CFG_ACK) ==
+ (ap->ability_match_cfg & ~ANEG_CFG_ACK)) {
+ ap->state = ANEG_STATE_COMPLETE_ACK_INIT;
+ } else {
+ ap->state = ANEG_STATE_AN_ENABLE;
+ }
+ } else if (ap->ability_match != 0 &&
+ ap->rxconfig == 0) {
+ ap->state = ANEG_STATE_AN_ENABLE;
+ }
+ break;
+
+ case ANEG_STATE_COMPLETE_ACK_INIT:
+ if (ap->rxconfig & ANEG_CFG_INVAL) {
+ ret = ANEG_FAILED;
+ break;
+ }
+ ap->flags &= ~(MR_LP_ADV_FULL_DUPLEX |
+ MR_LP_ADV_HALF_DUPLEX |
+ MR_LP_ADV_SYM_PAUSE |
+ MR_LP_ADV_ASYM_PAUSE |
+ MR_LP_ADV_REMOTE_FAULT1 |
+ MR_LP_ADV_REMOTE_FAULT2 |
+ MR_LP_ADV_NEXT_PAGE |
+ MR_TOGGLE_RX |
+ MR_NP_RX);
+ if (ap->rxconfig & ANEG_CFG_FD)
+ ap->flags |= MR_LP_ADV_FULL_DUPLEX;
+ if (ap->rxconfig & ANEG_CFG_HD)
+ ap->flags |= MR_LP_ADV_HALF_DUPLEX;
+ if (ap->rxconfig & ANEG_CFG_PS1)
+ ap->flags |= MR_LP_ADV_SYM_PAUSE;
+ if (ap->rxconfig & ANEG_CFG_PS2)
+ ap->flags |= MR_LP_ADV_ASYM_PAUSE;
+ if (ap->rxconfig & ANEG_CFG_RF1)
+ ap->flags |= MR_LP_ADV_REMOTE_FAULT1;
+ if (ap->rxconfig & ANEG_CFG_RF2)
+ ap->flags |= MR_LP_ADV_REMOTE_FAULT2;
+ if (ap->rxconfig & ANEG_CFG_NP)
+ ap->flags |= MR_LP_ADV_NEXT_PAGE;
+
+ ap->link_time = ap->cur_time;
+
+ ap->flags ^= (MR_TOGGLE_TX);
+ if (ap->rxconfig & 0x0008)
+ ap->flags |= MR_TOGGLE_RX;
+ if (ap->rxconfig & ANEG_CFG_NP)
+ ap->flags |= MR_NP_RX;
+ ap->flags |= MR_PAGE_RX;
+
+ ap->state = ANEG_STATE_COMPLETE_ACK;
+ ret = ANEG_TIMER_ENAB;
+ break;
+
+ case ANEG_STATE_COMPLETE_ACK:
+ if (ap->ability_match != 0 &&
+ ap->rxconfig == 0) {
+ ap->state = ANEG_STATE_AN_ENABLE;
+ break;
+ }
+ delta = ap->cur_time - ap->link_time;
+ if (delta > ANEG_STATE_SETTLE_TIME) {
+ if (!(ap->flags & (MR_LP_ADV_NEXT_PAGE))) {
+ ap->state = ANEG_STATE_IDLE_DETECT_INIT;
+ } else {
+ if ((ap->txconfig & ANEG_CFG_NP) == 0 &&
+ !(ap->flags & MR_NP_RX)) {
+ ap->state = ANEG_STATE_IDLE_DETECT_INIT;
+ } else {
+ ret = ANEG_FAILED;
+ }
+ }
+ }
+ break;
+
+ case ANEG_STATE_IDLE_DETECT_INIT:
+ ap->link_time = ap->cur_time;
+ tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS;
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ ap->state = ANEG_STATE_IDLE_DETECT;
+ ret = ANEG_TIMER_ENAB;
+ break;
+
+ case ANEG_STATE_IDLE_DETECT:
+ if (ap->ability_match != 0 &&
+ ap->rxconfig == 0) {
+ ap->state = ANEG_STATE_AN_ENABLE;
+ break;
+ }
+ delta = ap->cur_time - ap->link_time;
+ if (delta > ANEG_STATE_SETTLE_TIME) {
+ /* XXX another gem from the Broadcom driver :( */
+ ap->state = ANEG_STATE_LINK_OK;
+ }
+ break;
+
+ case ANEG_STATE_LINK_OK:
+ ap->flags |= (MR_AN_COMPLETE | MR_LINK_OK);
+ ret = ANEG_DONE;
+ break;
+
+ case ANEG_STATE_NEXT_PAGE_WAIT_INIT:
+ /* ??? unimplemented */
+ break;
+
+ case ANEG_STATE_NEXT_PAGE_WAIT:
+ /* ??? unimplemented */
+ break;
+
+ default:
+ ret = ANEG_FAILED;
+ break;
+ };
+
+ return ret;
+}
+
+static int tg3_setup_fiber_phy(struct tg3 *tp)
+{
+ uint32_t orig_pause_cfg;
+ uint16_t orig_active_speed;
+ uint8_t orig_active_duplex;
+ int current_link_up;
+ int i;
+
+ orig_pause_cfg =
+ (tp->tg3_flags & (TG3_FLAG_RX_PAUSE |
+ TG3_FLAG_TX_PAUSE));
+ orig_active_speed = tp->link_config.active_speed;
+ orig_active_duplex = tp->link_config.active_duplex;
+
+ tp->mac_mode &= ~(MAC_MODE_PORT_MODE_MASK | MAC_MODE_HALF_DUPLEX);
+ tp->mac_mode |= MAC_MODE_PORT_MODE_TBI;
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ /* Reset when initting first time or we have a link. */
+ if (!(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) ||
+ (tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED)) {
+ /* Set PLL lock range. */
+ tg3_writephy(tp, 0x16, 0x8007);
+
+ /* SW reset */
+ tg3_writephy(tp, MII_BMCR, BMCR_RESET);
+
+ /* Wait for reset to complete. */
+ mdelay(5);
+
+ /* Config mode; select PMA/Ch 1 regs. */
+ tg3_writephy(tp, 0x10, 0x8411);
+
+ /* Enable auto-lock and comdet, select txclk for tx. */
+ tg3_writephy(tp, 0x11, 0x0a10);
+
+ tg3_writephy(tp, 0x18, 0x00a0);
+ tg3_writephy(tp, 0x16, 0x41ff);
+
+ /* Assert and deassert POR. */
+ tg3_writephy(tp, 0x13, 0x0400);
+ udelay(40);
+ tg3_writephy(tp, 0x13, 0x0000);
+
+ tg3_writephy(tp, 0x11, 0x0a50);
+ udelay(40);
+ tg3_writephy(tp, 0x11, 0x0a10);
+
+ /* Wait for signal to stabilize */
+ mdelay(150);
+
+ /* Deselect the channel register so we can read the PHYID
+ * later.
+ */
+ tg3_writephy(tp, 0x10, 0x8011);
+ }
+
+ /* Disable link change interrupt. */
+ tw32_carefully(MAC_EVENT, 0);
+
+ current_link_up = 0;
+ if (tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED) {
+ if (!(tp->tg3_flags & TG3_FLAG_GOT_SERDES_FLOWCTL)) {
+ struct tg3_fiber_aneginfo aninfo;
+ int status = ANEG_FAILED;
+ unsigned int tick;
+ uint32_t tmp;
+
+ memset(&aninfo, 0, sizeof(aninfo));
+ aninfo.flags |= (MR_AN_ENABLE);
+
+ tw32(MAC_TX_AUTO_NEG, 0);
+
+ tmp = tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK;
+ tw32_carefully(MAC_MODE, tmp | MAC_MODE_PORT_MODE_GMII);
+
+ tw32_carefully(MAC_MODE, tp->mac_mode | MAC_MODE_SEND_CONFIGS);
+
+ aninfo.state = ANEG_STATE_UNKNOWN;
+ aninfo.cur_time = 0;
+ tick = 0;
+ while (++tick < 195000) {
+ status = tg3_fiber_aneg_smachine(tp, &aninfo);
+ if (status == ANEG_DONE ||
+ status == ANEG_FAILED)
+ break;
+
+ udelay(1);
+ }
+
+ tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS;
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ if (status == ANEG_DONE &&
+ (aninfo.flags &
+ (MR_AN_COMPLETE | MR_LINK_OK |
+ MR_LP_ADV_FULL_DUPLEX))) {
+ uint32_t local_adv, remote_adv;
+
+ local_adv = ADVERTISE_PAUSE_CAP;
+ remote_adv = 0;
+ if (aninfo.flags & MR_LP_ADV_SYM_PAUSE)
+ remote_adv |= LPA_PAUSE_CAP;
+ if (aninfo.flags & MR_LP_ADV_ASYM_PAUSE)
+ remote_adv |= LPA_PAUSE_ASYM;
+
+ tg3_setup_flow_control(tp, local_adv, remote_adv);
+
+ tp->tg3_flags |=
+ TG3_FLAG_GOT_SERDES_FLOWCTL;
+ current_link_up = 1;
+ }
+ for (i = 0; i < 60; i++) {
+ udelay(20);
+ tw32_carefully(MAC_STATUS,
+ (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED));
+ if ((tr32(MAC_STATUS) &
+ (MAC_STATUS_SYNC_CHANGED |
+ MAC_STATUS_CFG_CHANGED)) == 0)
+ break;
+ }
+ if (current_link_up == 0 &&
+ (tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED)) {
+ current_link_up = 1;
+ }
+ } else {
+ /* Forcing 1000FD link up. */
+ current_link_up = 1;
+ }
+ }
+
+ tp->mac_mode &= ~MAC_MODE_LINK_POLARITY;
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ tp->hw_status->status =
+ (SD_STATUS_UPDATED |
+ (tp->hw_status->status & ~SD_STATUS_LINK_CHG));
+
+ for (i = 0; i < 100; i++) {
+ udelay(20);
+ tw32_carefully(MAC_STATUS,
+ (MAC_STATUS_SYNC_CHANGED | MAC_STATUS_CFG_CHANGED));
+ if ((tr32(MAC_STATUS) &
+ (MAC_STATUS_SYNC_CHANGED |
+ MAC_STATUS_CFG_CHANGED)) == 0)
+ break;
+ }
+
+ if ((tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED) == 0)
+ current_link_up = 0;
+
+ if (current_link_up == 1) {
+ tp->link_config.active_speed = SPEED_1000;
+ tp->link_config.active_duplex = DUPLEX_FULL;
+ } else {
+ tp->link_config.active_speed = SPEED_INVALID;
+ tp->link_config.active_duplex = DUPLEX_INVALID;
+ }
+
+ if (current_link_up != tp->carrier_ok) {
+ tp->carrier_ok = current_link_up;
+ tg3_link_report(tp);
+ } else {
+ uint32_t now_pause_cfg =
+ tp->tg3_flags & (TG3_FLAG_RX_PAUSE |
+ TG3_FLAG_TX_PAUSE);
+ if (orig_pause_cfg != now_pause_cfg ||
+ orig_active_speed != tp->link_config.active_speed ||
+ orig_active_duplex != tp->link_config.active_duplex)
+ tg3_link_report(tp);
+ }
+
+ if ((tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED) == 0) {
+ tw32_carefully(MAC_MODE, tp->mac_mode | MAC_MODE_LINK_POLARITY);
+ if (tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) {
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+ }
+ }
+
+ return 0;
+}
+#else
+#define tg3_setup_fiber_phy(TP) (-EINVAL)
+#endif /* SUPPORT_FIBER_PHY */
+
+static int tg3_setup_phy(struct tg3 *tp)
+{
+ int err;
+
+ if (tp->phy_id == PHY_ID_SERDES) {
+ err = tg3_setup_fiber_phy(tp);
+ } else {
+ err = tg3_setup_copper_phy(tp);
+ }
+
+ if (tp->link_config.active_speed == SPEED_1000 &&
+ tp->link_config.active_duplex == DUPLEX_HALF)
+ tw32(MAC_TX_LENGTHS,
+ ((2 << TX_LENGTHS_IPG_CRS_SHIFT) |
+ (6 << TX_LENGTHS_IPG_SHIFT) |
+ (0xff << TX_LENGTHS_SLOT_TIME_SHIFT)));
+ else
+ tw32(MAC_TX_LENGTHS,
+ ((2 << TX_LENGTHS_IPG_CRS_SHIFT) |
+ (6 << TX_LENGTHS_IPG_SHIFT) |
+ (32 << TX_LENGTHS_SLOT_TIME_SHIFT)));
+
+ return err;
+}
+
+
+#define MAX_WAIT_CNT 1000
+
+/* To stop a block, clear the enable bit and poll till it
+ * clears.
+ */
+static int tg3_stop_block(struct tg3 *tp, unsigned long ofs, uint32_t enable_bit)
+{
+ unsigned int i;
+ uint32_t val;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) {
+ switch(ofs) {
+ case RCVLSC_MODE:
+ case DMAC_MODE:
+ case MBFREE_MODE:
+ case BUFMGR_MODE:
+ case MEMARB_MODE:
+ /* We can't enable/disable these bits of the
+ * 5705, just say success.
+ */
+ return 0;
+ default:
+ break;
+ }
+ }
+ val = tr32(ofs);
+ val &= ~enable_bit;
+ tw32(ofs, val);
+ tr32(ofs);
+
+ for (i = 0; i < MAX_WAIT_CNT; i++) {
+ udelay(100);
+ val = tr32(ofs);
+ if ((val & enable_bit) == 0)
+ break;
+ }
+
+ if (i == MAX_WAIT_CNT) {
+ printf("tg3_stop_block timed out, ofs=%lx enable_bit=%x\n",
+ ofs, enable_bit);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int tg3_abort_hw(struct tg3 *tp)
+{
+ int i, err;
+
+ tg3_disable_ints(tp);
+
+ tp->rx_mode &= ~RX_MODE_ENABLE;
+ tw32_carefully(MAC_RX_MODE, tp->rx_mode);
+
+ err = tg3_stop_block(tp, RCVBDI_MODE, RCVBDI_MODE_ENABLE);
+ err |= tg3_stop_block(tp, RCVLPC_MODE, RCVLPC_MODE_ENABLE);
+ err |= tg3_stop_block(tp, RCVLSC_MODE, RCVLSC_MODE_ENABLE);
+ err |= tg3_stop_block(tp, RCVDBDI_MODE, RCVDBDI_MODE_ENABLE);
+ err |= tg3_stop_block(tp, RCVDCC_MODE, RCVDCC_MODE_ENABLE);
+ err |= tg3_stop_block(tp, RCVCC_MODE, RCVCC_MODE_ENABLE);
+
+ err |= tg3_stop_block(tp, SNDBDS_MODE, SNDBDS_MODE_ENABLE);
+ err |= tg3_stop_block(tp, SNDBDI_MODE, SNDBDI_MODE_ENABLE);
+ err |= tg3_stop_block(tp, SNDDATAI_MODE, SNDDATAI_MODE_ENABLE);
+ err |= tg3_stop_block(tp, RDMAC_MODE, RDMAC_MODE_ENABLE);
+ err |= tg3_stop_block(tp, SNDDATAC_MODE, SNDDATAC_MODE_ENABLE);
+ err |= tg3_stop_block(tp, SNDBDC_MODE, SNDBDC_MODE_ENABLE);
+ if (err)
+ goto out;
+
+ tp->mac_mode &= ~MAC_MODE_TDE_ENABLE;
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ tp->tx_mode &= ~TX_MODE_ENABLE;
+ tw32_carefully(MAC_TX_MODE, tp->tx_mode);
+
+ for (i = 0; i < MAX_WAIT_CNT; i++) {
+ udelay(100);
+ if (!(tr32(MAC_TX_MODE) & TX_MODE_ENABLE))
+ break;
+ }
+ if (i >= MAX_WAIT_CNT) {
+ printf("tg3_abort_hw timed out TX_MODE_ENABLE will not clear MAC_TX_MODE=%x\n",
+ tr32(MAC_TX_MODE));
+ return -ENODEV;
+ }
+
+ err = tg3_stop_block(tp, HOSTCC_MODE, HOSTCC_MODE_ENABLE);
+ err |= tg3_stop_block(tp, WDMAC_MODE, WDMAC_MODE_ENABLE);
+ err |= tg3_stop_block(tp, MBFREE_MODE, MBFREE_MODE_ENABLE);
+
+ tw32(FTQ_RESET, 0xffffffff);
+ tw32(FTQ_RESET, 0x00000000);
+
+ err |= tg3_stop_block(tp, BUFMGR_MODE, BUFMGR_MODE_ENABLE);
+ err |= tg3_stop_block(tp, MEMARB_MODE, MEMARB_MODE_ENABLE);
+ if (err)
+ goto out;
+
+ memset(tp->hw_status, 0, TG3_HW_STATUS_SIZE);
+
+out:
+ return err;
+}
+
+static void tg3_chip_reset(struct tg3 *tp)
+{
+ uint32_t val;
+
+ if (!(tp->tg3_flags2 & TG3_FLG2_SUN_5704)) {
+ /* Force NVRAM to settle.
+ * This deals with a chip bug which can result in EEPROM
+ * corruption.
+ */
+ if (tp->tg3_flags & TG3_FLAG_NVRAM) {
+ int i;
+
+ tw32(NVRAM_SWARB, SWARB_REQ_SET1);
+ for (i = 0; i < 100000; i++) {
+ if (tr32(NVRAM_SWARB) & SWARB_GNT1)
+ break;
+ udelay(10);
+ }
+ }
+ }
+ /* In Etherboot we don't need to worry about the 5701
+ * REG_WRITE_BUG because we do all register writes indirectly.
+ */
+
+ // Alf: here patched
+ /* do the reset */
+ val = GRC_MISC_CFG_CORECLK_RESET;
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)
+ || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750)) {
+ val |= GRC_MISC_CFG_KEEP_GPHY_POWER;
+ }
+
+ // Alf : Please VALIDATE THIS.
+ // It is necessary in my case (5751) to prevent a reboot, but
+ // I have no idea about a side effect on any other version.
+ // It appears to be what's done in tigon3.c from Broadcom
+ if (tp->pci_chip_rev_id != CHIPREV_ID_5750_A0) {
+ tw32(GRC_MISC_CFG, 0x20000000) ;
+ val |= 0x20000000 ;
+ }
+
+ tw32(GRC_MISC_CFG, val);
+
+ /* Flush PCI posted writes. The normal MMIO registers
+ * are inaccessible at this time so this is the only
+ * way to make this reliably. I tried to use indirect
+ * register read/write but this upset some 5701 variants.
+ */
+ pci_read_config_dword(tp->pdev, PCI_COMMAND, &val);
+
+ udelay(120);
+
+ /* Re-enable indirect register accesses. */
+ pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL,
+ tp->misc_host_ctrl);
+
+ /* Set MAX PCI retry to zero. */
+ val = (PCISTATE_ROM_ENABLE | PCISTATE_ROM_RETRY_ENABLE);
+ if (tp->pci_chip_rev_id == CHIPREV_ID_5704_A0 &&
+ (tp->tg3_flags & TG3_FLAG_PCIX_MODE))
+ val |= PCISTATE_RETRY_SAME_DMA;
+ pci_write_config_dword(tp->pdev, TG3PCI_PCISTATE, val);
+
+ pci_restore_state(tp->pdev, tp->pci_cfg_state);
+
+ /* Make sure PCI-X relaxed ordering bit is clear. */
+ pci_read_config_dword(tp->pdev, TG3PCI_X_CAPS, &val);
+ val &= ~PCIX_CAPS_RELAXED_ORDERING;
+ pci_write_config_dword(tp->pdev, TG3PCI_X_CAPS, val);
+
+ tw32(MEMARB_MODE, MEMARB_MODE_ENABLE);
+
+ if (((tp->nic_sram_data_cfg & NIC_SRAM_DATA_CFG_MINI_PCI) != 0) &&
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)) {
+ tp->pci_clock_ctrl |=
+ (CLOCK_CTRL_FORCE_CLKRUN | CLOCK_CTRL_CLKRUN_OENABLE);
+ tw32(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl);
+ }
+
+ tw32(TG3PCI_MISC_HOST_CTRL, tp->misc_host_ctrl);
+}
+
+static void tg3_stop_fw(struct tg3 *tp)
+{
+ if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) {
+ uint32_t val;
+ int i;
+
+ tg3_write_mem(NIC_SRAM_FW_CMD_MBOX, FWCMD_NICDRV_PAUSE_FW);
+ val = tr32(GRC_RX_CPU_EVENT);
+ val |= (1 << 14);
+ tw32(GRC_RX_CPU_EVENT, val);
+
+ /* Wait for RX cpu to ACK the event. */
+ for (i = 0; i < 100; i++) {
+ if (!(tr32(GRC_RX_CPU_EVENT) & (1 << 14)))
+ break;
+ udelay(1);
+ }
+ }
+}
+
+static int tg3_restart_fw(struct tg3 *tp, uint32_t state)
+{
+ uint32_t val;
+ int i;
+
+ tg3_write_mem(NIC_SRAM_FIRMWARE_MBOX,
+ NIC_SRAM_FIRMWARE_MBOX_MAGIC1);
+ /* Wait for firmware initialization to complete. */
+ for (i = 0; i < 100000; i++) {
+ tg3_read_mem(NIC_SRAM_FIRMWARE_MBOX, &val);
+ if (val == (uint32_t) ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1)
+ break;
+ udelay(10);
+ }
+ if (i >= 100000 &&
+ !(tp->tg3_flags2 & TG3_FLG2_SUN_5704)) {
+ printf("Firmware will not restart magic=%x\n",
+ val);
+ return -ENODEV;
+ }
+ if (!(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) {
+ state = DRV_STATE_SUSPEND;
+ }
+
+ if ((tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) &&
+ (tp->pci_chip_rev_id != CHIPREV_ID_5750_A0)) {
+ // Enable PCIE bug fix
+ tg3_read_mem(0x7c00, &val);
+ tg3_write_mem(0x7c00, val | 0x02000000);
+ }
+ tg3_write_mem(NIC_SRAM_FW_DRV_STATE_MBOX, state);
+ return 0;
+}
+
+static int tg3_halt(struct tg3 *tp)
+{
+ tg3_stop_fw(tp);
+ tg3_abort_hw(tp);
+ tg3_chip_reset(tp);
+ return tg3_restart_fw(tp, DRV_STATE_UNLOAD);
+}
+
+static void __tg3_set_mac_addr(struct tg3 *tp)
+{
+ uint32_t addr_high, addr_low;
+ int i;
+
+ addr_high = ((tp->nic->node_addr[0] << 8) |
+ tp->nic->node_addr[1]);
+ addr_low = ((tp->nic->node_addr[2] << 24) |
+ (tp->nic->node_addr[3] << 16) |
+ (tp->nic->node_addr[4] << 8) |
+ (tp->nic->node_addr[5] << 0));
+ for (i = 0; i < 4; i++) {
+ tw32(MAC_ADDR_0_HIGH + (i * 8), addr_high);
+ tw32(MAC_ADDR_0_LOW + (i * 8), addr_low);
+ }
+
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700) &&
+ (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701) &&
+ (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705)) {
+ for(i = 0; i < 12; i++) {
+ tw32(MAC_EXTADDR_0_HIGH + (i * 8), addr_high);
+ tw32(MAC_EXTADDR_0_LOW + (i * 8), addr_low);
+ }
+ }
+ addr_high = (tp->nic->node_addr[0] +
+ tp->nic->node_addr[1] +
+ tp->nic->node_addr[2] +
+ tp->nic->node_addr[3] +
+ tp->nic->node_addr[4] +
+ tp->nic->node_addr[5]) &
+ TX_BACKOFF_SEED_MASK;
+ tw32(MAC_TX_BACKOFF_SEED, addr_high);
+}
+
+static void tg3_set_bdinfo(struct tg3 *tp, uint32_t bdinfo_addr,
+ dma_addr_t mapping, uint32_t maxlen_flags,
+ uint32_t nic_addr)
+{
+ tg3_write_mem((bdinfo_addr +
+ TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_HIGH),
+ ((uint64_t) mapping >> 32));
+ tg3_write_mem((bdinfo_addr +
+ TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW),
+ ((uint64_t) mapping & 0xffffffff));
+ tg3_write_mem((bdinfo_addr +
+ TG3_BDINFO_MAXLEN_FLAGS),
+ maxlen_flags);
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) {
+ tg3_write_mem((bdinfo_addr + TG3_BDINFO_NIC_ADDR), nic_addr);
+ }
+}
+
+
+static void tg3_init_rings(struct tg3 *tp)
+{
+ unsigned i;
+
+ /* Zero out the tg3 variables */
+ memset(&tg3_bss, 0, sizeof(tg3_bss));
+ tp->rx_std = &tg3_bss.rx_std[0];
+ tp->rx_rcb = &tg3_bss.rx_rcb[0];
+ tp->tx_ring = &tg3_bss.tx_ring[0];
+ tp->hw_status = &tg3_bss.hw_status;
+ tp->hw_stats = &tg3_bss.hw_stats;
+ tp->mac_mode = 0;
+
+
+ /* Initialize tx/rx rings for packet processing.
+ *
+ * The chip has been shut down and the driver detached from
+ * the networking, so no interrupts or new tx packets will
+ * end up in the driver.
+ */
+
+ /* Initialize invariants of the rings, we only set this
+ * stuff once. This works because the card does not
+ * write into the rx buffer posting rings.
+ */
+ for (i = 0; i < TG3_RX_RING_SIZE; i++) {
+ struct tg3_rx_buffer_desc *rxd;
+
+ rxd = &tp->rx_std[i];
+ rxd->idx_len = (RX_PKT_BUF_SZ - 2 - 64) << RXD_LEN_SHIFT;
+ rxd->type_flags = (RXD_FLAG_END << RXD_FLAGS_SHIFT);
+ rxd->opaque = (RXD_OPAQUE_RING_STD | (i << RXD_OPAQUE_INDEX_SHIFT));
+
+ /* Note where the receive buffer for the ring is placed */
+ rxd->addr_hi = 0;
+ rxd->addr_lo = virt_to_bus(
+ &tg3_bss.rx_bufs[i%TG3_DEF_RX_RING_PENDING][2]);
+ }
+}
+
+#define TG3_WRITE_SETTINGS(TABLE) \
+do { \
+ const uint32_t *_table, *_end; \
+ _table = TABLE; \
+ _end = _table + sizeof(TABLE)/sizeof(TABLE[0]); \
+ for(; _table < _end; _table += 2) { \
+ tw32(_table[0], _table[1]); \
+ } \
+} while(0)
+
+
+/* initialize/reset the tg3 */
+static int tg3_setup_hw(struct tg3 *tp)
+{
+ uint32_t val, rdmac_mode;
+ int i, err, limit;
+
+ /* Simply don't support setups with extremly buggy firmware in etherboot */
+ if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0) {
+ printf("Error 5701_A0 firmware bug detected\n");
+ return -EINVAL;
+ }
+
+ tg3_disable_ints(tp);
+
+ /* Originally this was all in tg3_init_hw */
+
+ /* Force the chip into D0. */
+ tg3_set_power_state_0(tp);
+
+ tg3_switch_clocks(tp);
+
+ tw32(TG3PCI_MEM_WIN_BASE_ADDR, 0);
+
+ // This should go somewhere else
+#define T3_PCIE_CAPABILITY_ID_REG 0xD0
+#define T3_PCIE_CAPABILITY_ID 0x10
+#define T3_PCIE_CAPABILITY_REG 0xD2
+
+ /* Originally this was all in tg3_reset_hw */
+
+ tg3_stop_fw(tp);
+
+ /* No need to call tg3_abort_hw here, it is called before tg3_setup_hw. */
+
+ tg3_chip_reset(tp);
+
+ tw32(GRC_MODE, tp->grc_mode); /* Redundant? */
+
+ err = tg3_restart_fw(tp, DRV_STATE_START);
+ if (err)
+ return err;
+
+ if (tp->phy_id == PHY_ID_SERDES) {
+ tp->mac_mode = MAC_MODE_PORT_MODE_TBI;
+ }
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+
+ /* This works around an issue with Athlon chipsets on
+ * B3 tigon3 silicon. This bit has no effect on any
+ * other revision.
+ * Alf: Except 5750 ! (which reboots)
+ */
+
+ if (!(tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS))
+ tp->pci_clock_ctrl |= CLOCK_CTRL_DELAY_PCI_GRANT;
+ tw32_carefully(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl);
+
+ if (tp->pci_chip_rev_id == CHIPREV_ID_5704_A0 &&
+ (tp->tg3_flags & TG3_FLAG_PCIX_MODE)) {
+ val = tr32(TG3PCI_PCISTATE);
+ val |= PCISTATE_RETRY_SAME_DMA;
+ tw32(TG3PCI_PCISTATE, val);
+ }
+
+ /* Descriptor ring init may make accesses to the
+ * NIC SRAM area to setup the TX descriptors, so we
+ * can only do this after the hardware has been
+ * successfully reset.
+ */
+ tg3_init_rings(tp);
+
+ /* Clear statistics/status block in chip */
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) {
+ for (i = NIC_SRAM_STATS_BLK;
+ i < NIC_SRAM_STATUS_BLK + TG3_HW_STATUS_SIZE;
+ i += sizeof(uint32_t)) {
+ tg3_write_mem(i, 0);
+ udelay(40);
+ }
+ }
+
+ /* This value is determined during the probe time DMA
+ * engine test, tg3_setup_dma.
+ */
+ tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl);
+
+ tp->grc_mode &= ~(GRC_MODE_HOST_SENDBDS |
+ GRC_MODE_4X_NIC_SEND_RINGS |
+ GRC_MODE_NO_TX_PHDR_CSUM |
+ GRC_MODE_NO_RX_PHDR_CSUM);
+ tp->grc_mode |= GRC_MODE_HOST_SENDBDS;
+ tp->grc_mode |= GRC_MODE_NO_TX_PHDR_CSUM;
+ tp->grc_mode |= GRC_MODE_NO_RX_PHDR_CSUM;
+
+ tw32(GRC_MODE,
+ tp->grc_mode |
+ (GRC_MODE_IRQ_ON_MAC_ATTN | GRC_MODE_HOST_STACKUP));
+
+ /* Setup the timer prescalar register. Clock is always 66Mhz. */
+ tw32(GRC_MISC_CFG,
+ (65 << GRC_MISC_CFG_PRESCALAR_SHIFT));
+
+ /* Initialize MBUF/DESC pool. */
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) {
+ tw32(BUFMGR_MB_POOL_ADDR, NIC_SRAM_MBUF_POOL_BASE);
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704)
+ tw32(BUFMGR_MB_POOL_SIZE, NIC_SRAM_MBUF_POOL_SIZE64);
+ else
+ tw32(BUFMGR_MB_POOL_SIZE, NIC_SRAM_MBUF_POOL_SIZE96);
+ tw32(BUFMGR_DMA_DESC_POOL_ADDR, NIC_SRAM_DMA_DESC_POOL_BASE);
+ tw32(BUFMGR_DMA_DESC_POOL_SIZE, NIC_SRAM_DMA_DESC_POOL_SIZE);
+ }
+ if (!(tp->tg3_flags & TG3_FLAG_JUMBO_ENABLE)) {
+ tw32(BUFMGR_MB_RDMA_LOW_WATER,
+ tp->bufmgr_config.mbuf_read_dma_low_water);
+ tw32(BUFMGR_MB_MACRX_LOW_WATER,
+ tp->bufmgr_config.mbuf_mac_rx_low_water);
+ tw32(BUFMGR_MB_HIGH_WATER,
+ tp->bufmgr_config.mbuf_high_water);
+ } else {
+ tw32(BUFMGR_MB_RDMA_LOW_WATER,
+ tp->bufmgr_config.mbuf_read_dma_low_water_jumbo);
+ tw32(BUFMGR_MB_MACRX_LOW_WATER,
+ tp->bufmgr_config.mbuf_mac_rx_low_water_jumbo);
+ tw32(BUFMGR_MB_HIGH_WATER,
+ tp->bufmgr_config.mbuf_high_water_jumbo);
+ }
+ tw32(BUFMGR_DMA_LOW_WATER,
+ tp->bufmgr_config.dma_low_water);
+ tw32(BUFMGR_DMA_HIGH_WATER,
+ tp->bufmgr_config.dma_high_water);
+
+ tw32(BUFMGR_MODE, BUFMGR_MODE_ENABLE | BUFMGR_MODE_ATTN_ENABLE);
+ for (i = 0; i < 2000; i++) {
+ if (tr32(BUFMGR_MODE) & BUFMGR_MODE_ENABLE)
+ break;
+ udelay(10);
+ }
+ if (i >= 2000) {
+ printf("tg3_setup_hw cannot enable BUFMGR\n");
+ return -ENODEV;
+ }
+
+ tw32(FTQ_RESET, 0xffffffff);
+ tw32(FTQ_RESET, 0x00000000);
+ for (i = 0; i < 2000; i++) {
+ if (tr32(FTQ_RESET) == 0x00000000)
+ break;
+ udelay(10);
+ }
+ if (i >= 2000) {
+ printf("tg3_setup_hw cannot reset FTQ\n");
+ return -ENODEV;
+ }
+
+ /* Initialize TG3_BDINFO's at:
+ * RCVDBDI_STD_BD: standard eth size rx ring
+ * RCVDBDI_JUMBO_BD: jumbo frame rx ring
+ * RCVDBDI_MINI_BD: small frame rx ring (??? does not work)
+ *
+ * like so:
+ * TG3_BDINFO_HOST_ADDR: high/low parts of DMA address of ring
+ * TG3_BDINFO_MAXLEN_FLAGS: (rx max buffer size << 16) |
+ * ring attribute flags
+ * TG3_BDINFO_NIC_ADDR: location of descriptors in nic SRAM
+ *
+ * Standard receive ring @ NIC_SRAM_RX_BUFFER_DESC, 512 entries.
+ * Jumbo receive ring @ NIC_SRAM_RX_JUMBO_BUFFER_DESC, 256 entries.
+ *
+ * ??? No space allocated for mini receive ring? :(
+ *
+ * The size of each ring is fixed in the firmware, but the location is
+ * configurable.
+ */
+ {
+ static const uint32_t table_all[] = {
+ /* Setup replenish thresholds. */
+ RCVBDI_STD_THRESH, TG3_DEF_RX_RING_PENDING / 8,
+
+ /* Etherboot lives below 4GB */
+ RCVDBDI_STD_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_HIGH, 0,
+ RCVDBDI_STD_BD + TG3_BDINFO_NIC_ADDR, NIC_SRAM_RX_BUFFER_DESC,
+ };
+ static const uint32_t table_not_5705[] = {
+ /* Buffer maximum length */
+ RCVDBDI_STD_BD + TG3_BDINFO_MAXLEN_FLAGS, RX_STD_MAX_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT,
+
+ /* Disable the mini frame rx ring */
+ RCVDBDI_MINI_BD + TG3_BDINFO_MAXLEN_FLAGS, BDINFO_FLAGS_DISABLED,
+
+ /* Disable the jumbo frame rx ring */
+ RCVBDI_JUMBO_THRESH, 0,
+ RCVDBDI_JUMBO_BD + TG3_BDINFO_MAXLEN_FLAGS, BDINFO_FLAGS_DISABLED,
+
+
+ };
+ TG3_WRITE_SETTINGS(table_all);
+ tw32(RCVDBDI_STD_BD + TG3_BDINFO_HOST_ADDR + TG3_64BIT_REG_LOW,
+ virt_to_bus(tp->rx_std));
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) {
+ tw32(RCVDBDI_STD_BD + TG3_BDINFO_MAXLEN_FLAGS,
+ RX_STD_MAX_SIZE_5705 << BDINFO_FLAGS_MAXLEN_SHIFT);
+ } else {
+ TG3_WRITE_SETTINGS(table_not_5705);
+ }
+ }
+
+
+ /* There is only one send ring on 5705, no need to explicitly
+ * disable the others.
+ */
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) {
+ /* Clear out send RCB ring in SRAM. */
+ for (i = NIC_SRAM_SEND_RCB; i < NIC_SRAM_RCV_RET_RCB; i += TG3_BDINFO_SIZE)
+ tg3_write_mem(i + TG3_BDINFO_MAXLEN_FLAGS, BDINFO_FLAGS_DISABLED);
+ }
+
+ tp->tx_prod = 0;
+ tw32_mailbox(MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW, 0);
+ tw32_mailbox2(MAILBOX_SNDNIC_PROD_IDX_0 + TG3_64BIT_REG_LOW, 0);
+
+ tg3_set_bdinfo(tp,
+ NIC_SRAM_SEND_RCB,
+ virt_to_bus(tp->tx_ring),
+ (TG3_TX_RING_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT),
+ NIC_SRAM_TX_BUFFER_DESC);
+
+ /* There is only one receive return ring on 5705, no need to explicitly
+ * disable the others.
+ */
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) {
+ for (i = NIC_SRAM_RCV_RET_RCB; i < NIC_SRAM_STATS_BLK; i += TG3_BDINFO_SIZE) {
+ tg3_write_mem(i + TG3_BDINFO_MAXLEN_FLAGS,
+ BDINFO_FLAGS_DISABLED);
+ }
+ }
+
+ tp->rx_rcb_ptr = 0;
+ tw32_mailbox2(MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW, 0);
+
+ tg3_set_bdinfo(tp,
+ NIC_SRAM_RCV_RET_RCB,
+ virt_to_bus(tp->rx_rcb),
+ (TG3_RX_RCB_RING_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT),
+ 0);
+
+ tp->rx_std_ptr = TG3_DEF_RX_RING_PENDING;
+ tw32_mailbox2(MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW,
+ tp->rx_std_ptr);
+
+ tw32_mailbox2(MAILBOX_RCV_JUMBO_PROD_IDX + TG3_64BIT_REG_LOW, 0);
+
+ /* Initialize MAC address and backoff seed. */
+ __tg3_set_mac_addr(tp);
+
+ /* Calculate RDMAC_MODE setting early, we need it to determine
+ * the RCVLPC_STATE_ENABLE mask.
+ */
+ rdmac_mode = (RDMAC_MODE_ENABLE | RDMAC_MODE_TGTABORT_ENAB |
+ RDMAC_MODE_MSTABORT_ENAB | RDMAC_MODE_PARITYERR_ENAB |
+ RDMAC_MODE_ADDROFLOW_ENAB | RDMAC_MODE_FIFOOFLOW_ENAB |
+ RDMAC_MODE_FIFOURUN_ENAB | RDMAC_MODE_FIFOOREAD_ENAB |
+ RDMAC_MODE_LNGREAD_ENAB);
+ if (tp->tg3_flags & TG3_FLAG_SPLIT_MODE)
+ rdmac_mode |= RDMAC_MODE_SPLIT_ENABLE;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) {
+ if (tp->pci_chip_rev_id != CHIPREV_ID_5705_A0) {
+ if (!(tr32(TG3PCI_PCISTATE) & PCISTATE_BUS_SPEED_HIGH) &&
+ !(tp->tg3_flags2 & TG3_FLG2_IS_5788)) {
+ rdmac_mode |= RDMAC_MODE_FIFO_LONG_BURST;
+ }
+ }
+ }
+
+ /* Setup host coalescing engine. */
+ tw32(HOSTCC_MODE, 0);
+ for (i = 0; i < 2000; i++) {
+ if (!(tr32(HOSTCC_MODE) & HOSTCC_MODE_ENABLE))
+ break;
+ udelay(10);
+ }
+
+ tp->mac_mode = MAC_MODE_TXSTAT_ENABLE | MAC_MODE_RXSTAT_ENABLE |
+ MAC_MODE_TDE_ENABLE | MAC_MODE_RDE_ENABLE | MAC_MODE_FHDE_ENABLE;
+ tw32_carefully(MAC_MODE, tp->mac_mode | MAC_MODE_RXSTAT_CLEAR | MAC_MODE_TXSTAT_CLEAR);
+
+ tp->grc_local_ctrl = GRC_LCLCTRL_INT_ON_ATTN | GRC_LCLCTRL_AUTO_SEEPROM;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700)
+ tp->grc_local_ctrl |= (GRC_LCLCTRL_GPIO_OE1 |
+ GRC_LCLCTRL_GPIO_OUTPUT1);
+ tw32_carefully(GRC_LOCAL_CTRL, tp->grc_local_ctrl);
+
+ tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0);
+ tr32(MAILBOX_INTERRUPT_0);
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) {
+ tw32_carefully(DMAC_MODE, DMAC_MODE_ENABLE);
+ }
+
+ val = ( WDMAC_MODE_ENABLE | WDMAC_MODE_TGTABORT_ENAB |
+ WDMAC_MODE_MSTABORT_ENAB | WDMAC_MODE_PARITYERR_ENAB |
+ WDMAC_MODE_ADDROFLOW_ENAB | WDMAC_MODE_FIFOOFLOW_ENAB |
+ WDMAC_MODE_FIFOURUN_ENAB | WDMAC_MODE_FIFOOREAD_ENAB |
+ WDMAC_MODE_LNGREAD_ENAB);
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) &&
+ ((tr32(TG3PCI_PCISTATE) & PCISTATE_BUS_SPEED_HIGH) != 0) &&
+ !(tp->tg3_flags2 & TG3_FLG2_IS_5788)) {
+ val |= WDMAC_MODE_RX_ACCEL;
+ }
+ tw32_carefully(WDMAC_MODE, val);
+
+ if ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) != 0) {
+ val = tr32(TG3PCI_X_CAPS);
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) {
+ val &= PCIX_CAPS_BURST_MASK;
+ val |= (PCIX_CAPS_MAX_BURST_CPIOB << PCIX_CAPS_BURST_SHIFT);
+ } else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) {
+ val &= ~(PCIX_CAPS_SPLIT_MASK | PCIX_CAPS_BURST_MASK);
+ val |= (PCIX_CAPS_MAX_BURST_CPIOB << PCIX_CAPS_BURST_SHIFT);
+ if (tp->tg3_flags & TG3_FLAG_SPLIT_MODE)
+ val |= (tp->split_mode_max_reqs <<
+ PCIX_CAPS_SPLIT_SHIFT);
+ }
+ tw32(TG3PCI_X_CAPS, val);
+ }
+
+ tw32_carefully(RDMAC_MODE, rdmac_mode);
+ {
+ static const uint32_t table_all[] = {
+ /* MTU + ethernet header + FCS + optional VLAN tag */
+ MAC_RX_MTU_SIZE, ETH_MAX_MTU + ETH_HLEN + 8,
+
+ /* The slot time is changed by tg3_setup_phy if we
+ * run at gigabit with half duplex.
+ */
+ MAC_TX_LENGTHS,
+ (2 << TX_LENGTHS_IPG_CRS_SHIFT) |
+ (6 << TX_LENGTHS_IPG_SHIFT) |
+ (32 << TX_LENGTHS_SLOT_TIME_SHIFT),
+
+ /* Receive rules. */
+ MAC_RCV_RULE_CFG, RCV_RULE_CFG_DEFAULT_CLASS,
+ RCVLPC_CONFIG, 0x0181,
+
+ /* Receive/send statistics. */
+ RCVLPC_STATS_ENABLE, 0xffffff,
+ RCVLPC_STATSCTRL, RCVLPC_STATSCTRL_ENABLE,
+ SNDDATAI_STATSENAB, 0xffffff,
+ SNDDATAI_STATSCTRL, (SNDDATAI_SCTRL_ENABLE |SNDDATAI_SCTRL_FASTUPD),
+
+ /* Host coalescing engine */
+ HOSTCC_RXCOL_TICKS, 0,
+ HOSTCC_TXCOL_TICKS, LOW_TXCOL_TICKS,
+ HOSTCC_RXMAX_FRAMES, 1,
+ HOSTCC_TXMAX_FRAMES, LOW_RXMAX_FRAMES,
+ HOSTCC_RXCOAL_MAXF_INT, 1,
+ HOSTCC_TXCOAL_MAXF_INT, 0,
+
+ /* Status/statistics block address. */
+ /* Etherboot lives below 4GB, so HIGH == 0 */
+ HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_HIGH, 0,
+
+ /* No need to enable 32byte coalesce mode. */
+ HOSTCC_MODE, HOSTCC_MODE_ENABLE | 0,
+
+ RCVCC_MODE, RCVCC_MODE_ENABLE | RCVCC_MODE_ATTN_ENABLE,
+ RCVLPC_MODE, RCVLPC_MODE_ENABLE,
+
+ RCVDCC_MODE, RCVDCC_MODE_ENABLE | RCVDCC_MODE_ATTN_ENABLE,
+
+ SNDDATAC_MODE, SNDDATAC_MODE_ENABLE,
+ SNDBDC_MODE, SNDBDC_MODE_ENABLE | SNDBDC_MODE_ATTN_ENABLE,
+ RCVBDI_MODE, RCVBDI_MODE_ENABLE | RCVBDI_MODE_RCB_ATTN_ENAB,
+ RCVDBDI_MODE, RCVDBDI_MODE_ENABLE | RCVDBDI_MODE_INV_RING_SZ,
+ SNDDATAI_MODE, SNDDATAI_MODE_ENABLE,
+ SNDBDI_MODE, SNDBDI_MODE_ENABLE | SNDBDI_MODE_ATTN_ENABLE,
+ SNDBDS_MODE, SNDBDS_MODE_ENABLE | SNDBDS_MODE_ATTN_ENABLE,
+
+ /* Accept all multicast frames. */
+ MAC_HASH_REG_0, 0xffffffff,
+ MAC_HASH_REG_1, 0xffffffff,
+ MAC_HASH_REG_2, 0xffffffff,
+ MAC_HASH_REG_3, 0xffffffff,
+ };
+ static const uint32_t table_not_5705[] = {
+ /* Host coalescing engine */
+ HOSTCC_RXCOAL_TICK_INT, 0,
+ HOSTCC_TXCOAL_TICK_INT, 0,
+
+ /* Status/statistics block address. */
+ /* Etherboot lives below 4GB, so HIGH == 0 */
+ HOSTCC_STAT_COAL_TICKS, DEFAULT_STAT_COAL_TICKS,
+ HOSTCC_STATS_BLK_HOST_ADDR + TG3_64BIT_REG_HIGH, 0,
+ HOSTCC_STATS_BLK_NIC_ADDR, NIC_SRAM_STATS_BLK,
+ HOSTCC_STATUS_BLK_NIC_ADDR, NIC_SRAM_STATUS_BLK,
+
+ RCVLSC_MODE, RCVLSC_MODE_ENABLE | RCVLSC_MODE_ATTN_ENABLE,
+
+ MBFREE_MODE, MBFREE_MODE_ENABLE,
+ };
+ TG3_WRITE_SETTINGS(table_all);
+ tw32(HOSTCC_STATS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW,
+ virt_to_bus(tp->hw_stats));
+ tw32(HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW,
+ virt_to_bus(tp->hw_status));
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) {
+ TG3_WRITE_SETTINGS(table_not_5705);
+ }
+ }
+
+ tp->tx_mode = TX_MODE_ENABLE;
+ tw32_carefully(MAC_TX_MODE, tp->tx_mode);
+
+ tp->rx_mode = RX_MODE_ENABLE;
+ tw32_carefully(MAC_RX_MODE, tp->rx_mode);
+
+ tp->mi_mode = MAC_MI_MODE_BASE;
+ tw32_carefully(MAC_MI_MODE, tp->mi_mode);
+
+ tw32(MAC_LED_CTRL, 0);
+ tw32(MAC_MI_STAT, MAC_MI_STAT_LNKSTAT_ATTN_ENAB);
+ if (tp->phy_id == PHY_ID_SERDES) {
+ tw32_carefully(MAC_RX_MODE, RX_MODE_RESET);
+ }
+ tp->rx_mode |= RX_MODE_KEEP_VLAN_TAG; /* drop tagged vlan packets */
+ tw32_carefully(MAC_RX_MODE, tp->rx_mode);
+
+ if (tp->pci_chip_rev_id == CHIPREV_ID_5703_A1)
+ tw32(MAC_SERDES_CFG, 0x616000);
+
+ /* Prevent chip from dropping frames when flow control
+ * is enabled.
+ */
+ tw32(MAC_LOW_WMARK_MAX_RX_FRAME, 2);
+ tr32(MAC_LOW_WMARK_MAX_RX_FRAME);
+
+ err = tg3_setup_phy(tp);
+
+ /* Ignore CRC stats */
+
+ /* Initialize receive rules. */
+ tw32(MAC_RCV_RULE_0, 0xc2000000 & RCV_RULE_DISABLE_MASK);
+ tw32(MAC_RCV_VALUE_0, 0xffffffff & RCV_RULE_DISABLE_MASK);
+ tw32(MAC_RCV_RULE_1, 0x86000004 & RCV_RULE_DISABLE_MASK);
+ tw32(MAC_RCV_VALUE_1, 0xffffffff & RCV_RULE_DISABLE_MASK);
+
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)
+ || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750))
+ limit = 8;
+ else
+ limit = 16;
+ if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF)
+ limit -= 4;
+ switch (limit) {
+ case 16: tw32(MAC_RCV_RULE_15, 0); tw32(MAC_RCV_VALUE_15, 0);
+ case 15: tw32(MAC_RCV_RULE_14, 0); tw32(MAC_RCV_VALUE_14, 0);
+ case 14: tw32(MAC_RCV_RULE_13, 0); tw32(MAC_RCV_VALUE_13, 0);
+ case 13: tw32(MAC_RCV_RULE_12, 0); tw32(MAC_RCV_VALUE_12, 0);
+ case 12: tw32(MAC_RCV_RULE_11, 0); tw32(MAC_RCV_VALUE_11, 0);
+ case 11: tw32(MAC_RCV_RULE_10, 0); tw32(MAC_RCV_VALUE_10, 0);
+ case 10: tw32(MAC_RCV_RULE_9, 0); tw32(MAC_RCV_VALUE_9, 0);
+ case 9: tw32(MAC_RCV_RULE_8, 0); tw32(MAC_RCV_VALUE_8, 0);
+ case 8: tw32(MAC_RCV_RULE_7, 0); tw32(MAC_RCV_VALUE_7, 0);
+ case 7: tw32(MAC_RCV_RULE_6, 0); tw32(MAC_RCV_VALUE_6, 0);
+ case 6: tw32(MAC_RCV_RULE_5, 0); tw32(MAC_RCV_VALUE_5, 0);
+ case 5: tw32(MAC_RCV_RULE_4, 0); tw32(MAC_RCV_VALUE_4, 0);
+ case 4: /* tw32(MAC_RCV_RULE_3, 0); tw32(MAC_RCV_VALUE_3, 0); */
+ case 3: /* tw32(MAC_RCV_RULE_2, 0); tw32(MAC_RCV_VALUE_2, 0); */
+ case 2:
+ case 1:
+ default:
+ break;
+ };
+
+ return err;
+}
+
+
+
+/* Chips other than 5700/5701 use the NVRAM for fetching info. */
+static void tg3_nvram_init(struct tg3 *tp)
+{
+ tw32(GRC_EEPROM_ADDR,
+ (EEPROM_ADDR_FSM_RESET |
+ (EEPROM_DEFAULT_CLOCK_PERIOD <<
+ EEPROM_ADDR_CLKPERD_SHIFT)));
+
+ mdelay(1);
+
+ /* Enable seeprom accesses. */
+ tw32_carefully(GRC_LOCAL_CTRL,
+ tr32(GRC_LOCAL_CTRL) | GRC_LCLCTRL_AUTO_SEEPROM);
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701) {
+ uint32_t nvcfg1 = tr32(NVRAM_CFG1);
+
+ tp->tg3_flags |= TG3_FLAG_NVRAM;
+ if (nvcfg1 & NVRAM_CFG1_FLASHIF_ENAB) {
+ if (nvcfg1 & NVRAM_CFG1_BUFFERED_MODE)
+ tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED;
+ } else {
+ nvcfg1 &= ~NVRAM_CFG1_COMPAT_BYPASS;
+ tw32(NVRAM_CFG1, nvcfg1);
+ }
+
+ } else {
+ tp->tg3_flags &= ~(TG3_FLAG_NVRAM | TG3_FLAG_NVRAM_BUFFERED);
+ }
+}
+
+
+static int tg3_nvram_read_using_eeprom(
+ struct tg3 *tp __unused, uint32_t offset, uint32_t *val)
+{
+ uint32_t tmp;
+ int i;
+
+ if (offset > EEPROM_ADDR_ADDR_MASK ||
+ (offset % 4) != 0) {
+ return -EINVAL;
+ }
+
+ tmp = tr32(GRC_EEPROM_ADDR) & ~(EEPROM_ADDR_ADDR_MASK |
+ EEPROM_ADDR_DEVID_MASK |
+ EEPROM_ADDR_READ);
+ tw32(GRC_EEPROM_ADDR,
+ tmp |
+ (0 << EEPROM_ADDR_DEVID_SHIFT) |
+ ((offset << EEPROM_ADDR_ADDR_SHIFT) &
+ EEPROM_ADDR_ADDR_MASK) |
+ EEPROM_ADDR_READ | EEPROM_ADDR_START);
+
+ for (i = 0; i < 10000; i++) {
+ tmp = tr32(GRC_EEPROM_ADDR);
+
+ if (tmp & EEPROM_ADDR_COMPLETE)
+ break;
+ udelay(100);
+ }
+ if (!(tmp & EEPROM_ADDR_COMPLETE)) {
+ return -EBUSY;
+ }
+
+ *val = tr32(GRC_EEPROM_DATA);
+ return 0;
+}
+
+static int tg3_nvram_read(struct tg3 *tp, uint32_t offset, uint32_t *val)
+{
+ int i, saw_done_clear;
+
+ if (!(tp->tg3_flags & TG3_FLAG_NVRAM))
+ return tg3_nvram_read_using_eeprom(tp, offset, val);
+
+ if (tp->tg3_flags & TG3_FLAG_NVRAM_BUFFERED)
+ offset = ((offset / NVRAM_BUFFERED_PAGE_SIZE) <<
+ NVRAM_BUFFERED_PAGE_POS) +
+ (offset % NVRAM_BUFFERED_PAGE_SIZE);
+
+ if (offset > NVRAM_ADDR_MSK)
+ return -EINVAL;
+
+ tw32(NVRAM_SWARB, SWARB_REQ_SET1);
+ for (i = 0; i < 1000; i++) {
+ if (tr32(NVRAM_SWARB) & SWARB_GNT1)
+ break;
+ udelay(20);
+ }
+
+ tw32(NVRAM_ADDR, offset);
+ tw32(NVRAM_CMD,
+ NVRAM_CMD_RD | NVRAM_CMD_GO |
+ NVRAM_CMD_FIRST | NVRAM_CMD_LAST | NVRAM_CMD_DONE);
+
+ /* Wait for done bit to clear then set again. */
+ saw_done_clear = 0;
+ for (i = 0; i < 1000; i++) {
+ udelay(10);
+ if (!saw_done_clear &&
+ !(tr32(NVRAM_CMD) & NVRAM_CMD_DONE))
+ saw_done_clear = 1;
+ else if (saw_done_clear &&
+ (tr32(NVRAM_CMD) & NVRAM_CMD_DONE))
+ break;
+ }
+ if (i >= 1000) {
+ tw32(NVRAM_SWARB, SWARB_REQ_CLR1);
+ return -EBUSY;
+ }
+
+ *val = bswap_32(tr32(NVRAM_RDDATA));
+ tw32(NVRAM_SWARB, 0x20);
+
+ return 0;
+}
+
+struct subsys_tbl_ent {
+ uint16_t subsys_vendor, subsys_devid;
+ uint32_t phy_id;
+};
+
+static struct subsys_tbl_ent subsys_id_to_phy_id[] = {
+ /* Broadcom boards. */
+ { 0x14e4, 0x1644, PHY_ID_BCM5401 }, /* BCM95700A6 */
+ { 0x14e4, 0x0001, PHY_ID_BCM5701 }, /* BCM95701A5 */
+ { 0x14e4, 0x0002, PHY_ID_BCM8002 }, /* BCM95700T6 */
+ { 0x14e4, 0x0003, PHY_ID_SERDES }, /* BCM95700A9 */
+ { 0x14e4, 0x0005, PHY_ID_BCM5701 }, /* BCM95701T1 */
+ { 0x14e4, 0x0006, PHY_ID_BCM5701 }, /* BCM95701T8 */
+ { 0x14e4, 0x0007, PHY_ID_SERDES }, /* BCM95701A7 */
+ { 0x14e4, 0x0008, PHY_ID_BCM5701 }, /* BCM95701A10 */
+ { 0x14e4, 0x8008, PHY_ID_BCM5701 }, /* BCM95701A12 */
+ { 0x14e4, 0x0009, PHY_ID_BCM5701 }, /* BCM95703Ax1 */
+ { 0x14e4, 0x8009, PHY_ID_BCM5701 }, /* BCM95703Ax2 */
+
+ /* 3com boards. */
+ { PCI_VENDOR_ID_3COM, 0x1000, PHY_ID_BCM5401 }, /* 3C996T */
+ { PCI_VENDOR_ID_3COM, 0x1006, PHY_ID_BCM5701 }, /* 3C996BT */
+ /* { PCI_VENDOR_ID_3COM, 0x1002, PHY_ID_XXX }, 3C996CT */
+ /* { PCI_VENDOR_ID_3COM, 0x1003, PHY_ID_XXX }, 3C997T */
+ { PCI_VENDOR_ID_3COM, 0x1004, PHY_ID_SERDES }, /* 3C996SX */
+ /* { PCI_VENDOR_ID_3COM, 0x1005, PHY_ID_XXX }, 3C997SZ */
+ { PCI_VENDOR_ID_3COM, 0x1007, PHY_ID_BCM5701 }, /* 3C1000T */
+ { PCI_VENDOR_ID_3COM, 0x1008, PHY_ID_BCM5701 }, /* 3C940BR01 */
+
+ /* DELL boards. */
+ { PCI_VENDOR_ID_DELL, 0x00d1, PHY_ID_BCM5401 }, /* VIPER */
+ { PCI_VENDOR_ID_DELL, 0x0106, PHY_ID_BCM5401 }, /* JAGUAR */
+ { PCI_VENDOR_ID_DELL, 0x0109, PHY_ID_BCM5411 }, /* MERLOT */
+ { PCI_VENDOR_ID_DELL, 0x010a, PHY_ID_BCM5411 }, /* SLIM_MERLOT */
+ { PCI_VENDOR_ID_DELL, 0x0179, PHY_ID_BCM5751 }, /* EtherXpress */
+
+ /* Compaq boards. */
+ { PCI_VENDOR_ID_COMPAQ, 0x007c, PHY_ID_BCM5701 }, /* BANSHEE */
+ { PCI_VENDOR_ID_COMPAQ, 0x009a, PHY_ID_BCM5701 }, /* BANSHEE_2 */
+ { PCI_VENDOR_ID_COMPAQ, 0x007d, PHY_ID_SERDES }, /* CHANGELING */
+ { PCI_VENDOR_ID_COMPAQ, 0x0085, PHY_ID_BCM5701 }, /* NC7780 */
+ { PCI_VENDOR_ID_COMPAQ, 0x0099, PHY_ID_BCM5701 } /* NC7780_2 */
+};
+
+static int tg3_phy_probe(struct tg3 *tp)
+{
+ uint32_t eeprom_phy_id, hw_phy_id_1, hw_phy_id_2;
+ uint32_t hw_phy_id, hw_phy_id_masked;
+ enum phy_led_mode eeprom_led_mode;
+ uint32_t val;
+ unsigned i;
+ int eeprom_signature_found, err;
+
+ tp->phy_id = PHY_ID_INVALID;
+
+ for (i = 0; i < sizeof(subsys_id_to_phy_id)/sizeof(subsys_id_to_phy_id[0]); i++) {
+ if ((subsys_id_to_phy_id[i].subsys_vendor == tp->subsystem_vendor) &&
+ (subsys_id_to_phy_id[i].subsys_devid == tp->subsystem_device)) {
+ tp->phy_id = subsys_id_to_phy_id[i].phy_id;
+ break;
+ }
+ }
+
+ eeprom_phy_id = PHY_ID_INVALID;
+ eeprom_led_mode = led_mode_auto;
+ eeprom_signature_found = 0;
+ tg3_read_mem(NIC_SRAM_DATA_SIG, &val);
+ if (val == NIC_SRAM_DATA_SIG_MAGIC) {
+ uint32_t nic_cfg;
+
+ tg3_read_mem(NIC_SRAM_DATA_CFG, &nic_cfg);
+ tp->nic_sram_data_cfg = nic_cfg;
+
+ eeprom_signature_found = 1;
+
+ if ((nic_cfg & NIC_SRAM_DATA_CFG_PHY_TYPE_MASK) ==
+ NIC_SRAM_DATA_CFG_PHY_TYPE_FIBER) {
+ eeprom_phy_id = PHY_ID_SERDES;
+ } else {
+ uint32_t nic_phy_id;
+
+ tg3_read_mem(NIC_SRAM_DATA_PHY_ID, &nic_phy_id);
+ if (nic_phy_id != 0) {
+ uint32_t id1 = nic_phy_id & NIC_SRAM_DATA_PHY_ID1_MASK;
+ uint32_t id2 = nic_phy_id & NIC_SRAM_DATA_PHY_ID2_MASK;
+
+ eeprom_phy_id = (id1 >> 16) << 10;
+ eeprom_phy_id |= (id2 & 0xfc00) << 16;
+ eeprom_phy_id |= (id2 & 0x03ff) << 0;
+ }
+ }
+
+ switch (nic_cfg & NIC_SRAM_DATA_CFG_LED_MODE_MASK) {
+ case NIC_SRAM_DATA_CFG_LED_TRIPLE_SPD:
+ eeprom_led_mode = led_mode_three_link;
+ break;
+
+ case NIC_SRAM_DATA_CFG_LED_LINK_SPD:
+ eeprom_led_mode = led_mode_link10;
+ break;
+
+ default:
+ eeprom_led_mode = led_mode_auto;
+ break;
+ };
+ if (((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)) &&
+ (nic_cfg & NIC_SRAM_DATA_CFG_EEPROM_WP)) {
+ tp->tg3_flags |= TG3_FLAG_EEPROM_WRITE_PROT;
+ }
+
+ if (nic_cfg & NIC_SRAM_DATA_CFG_ASF_ENABLE)
+ tp->tg3_flags |= TG3_FLAG_ENABLE_ASF;
+ if (nic_cfg & NIC_SRAM_DATA_CFG_FIBER_WOL)
+ tp->tg3_flags |= TG3_FLAG_SERDES_WOL_CAP;
+ }
+
+ /* Now read the physical PHY_ID from the chip and verify
+ * that it is sane. If it doesn't look good, we fall back
+ * to either the hard-coded table based PHY_ID and failing
+ * that the value found in the eeprom area.
+ */
+ err = tg3_readphy(tp, MII_PHYSID1, &hw_phy_id_1);
+ err |= tg3_readphy(tp, MII_PHYSID2, &hw_phy_id_2);
+
+ hw_phy_id = (hw_phy_id_1 & 0xffff) << 10;
+ hw_phy_id |= (hw_phy_id_2 & 0xfc00) << 16;
+ hw_phy_id |= (hw_phy_id_2 & 0x03ff) << 0;
+
+ hw_phy_id_masked = hw_phy_id & PHY_ID_MASK;
+
+ if (!err && KNOWN_PHY_ID(hw_phy_id_masked)) {
+ tp->phy_id = hw_phy_id;
+ } else {
+ /* phy_id currently holds the value found in the
+ * subsys_id_to_phy_id[] table or PHY_ID_INVALID
+ * if a match was not found there.
+ */
+ if (tp->phy_id == PHY_ID_INVALID) {
+ if (!eeprom_signature_found ||
+ !KNOWN_PHY_ID(eeprom_phy_id & PHY_ID_MASK))
+ return -ENODEV;
+ tp->phy_id = eeprom_phy_id;
+ }
+ }
+
+ err = tg3_phy_reset(tp);
+ if (err)
+ return err;
+
+ if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 ||
+ tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) {
+ uint32_t mii_tg3_ctrl;
+
+ /* These chips, when reset, only advertise 10Mb
+ * capabilities. Fix that.
+ */
+ err = tg3_writephy(tp, MII_ADVERTISE,
+ (ADVERTISE_CSMA |
+ ADVERTISE_PAUSE_CAP |
+ ADVERTISE_10HALF |
+ ADVERTISE_10FULL |
+ ADVERTISE_100HALF |
+ ADVERTISE_100FULL));
+ mii_tg3_ctrl = (MII_TG3_CTRL_ADV_1000_HALF |
+ MII_TG3_CTRL_ADV_1000_FULL |
+ MII_TG3_CTRL_AS_MASTER |
+ MII_TG3_CTRL_ENABLE_AS_MASTER);
+ if (tp->tg3_flags & TG3_FLAG_10_100_ONLY)
+ mii_tg3_ctrl = 0;
+
+ err |= tg3_writephy(tp, MII_TG3_CTRL, mii_tg3_ctrl);
+ err |= tg3_writephy(tp, MII_BMCR,
+ (BMCR_ANRESTART | BMCR_ANENABLE));
+ }
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) {
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00);
+ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x201f);
+ tg3_writedsp(tp, MII_TG3_DSP_RW_PORT, 0x2aaa);
+ }
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) {
+ tg3_writephy(tp, 0x1c, 0x8d68);
+ tg3_writephy(tp, 0x1c, 0x8d68);
+ }
+
+ /* Enable Ethernet@WireSpeed */
+ tg3_phy_set_wirespeed(tp);
+
+ if (!err && ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401)) {
+ err = tg3_init_5401phy_dsp(tp);
+ }
+
+ /* Determine the PHY led mode.
+ * Be careful if this gets set wrong it can result in an inability to
+ * establish a link.
+ */
+ if (tp->phy_id == PHY_ID_SERDES) {
+ tp->led_mode = led_mode_three_link;
+ }
+ else if (tp->subsystem_vendor == PCI_VENDOR_ID_DELL) {
+ tp->led_mode = led_mode_link10;
+ } else {
+ tp->led_mode = led_mode_three_link;
+ if (eeprom_signature_found &&
+ eeprom_led_mode != led_mode_auto)
+ tp->led_mode = eeprom_led_mode;
+ }
+
+ if (tp->phy_id == PHY_ID_SERDES)
+ tp->link_config.advertising =
+ (ADVERTISED_1000baseT_Half |
+ ADVERTISED_1000baseT_Full |
+ ADVERTISED_Autoneg |
+ ADVERTISED_FIBRE);
+ if (tp->tg3_flags & TG3_FLAG_10_100_ONLY)
+ tp->link_config.advertising &=
+ ~(ADVERTISED_1000baseT_Half |
+ ADVERTISED_1000baseT_Full);
+
+ return err;
+}
+
+#if SUPPORT_PARTNO_STR
+static void tg3_read_partno(struct tg3 *tp)
+{
+ unsigned char vpd_data[256];
+ int i;
+
+ for (i = 0; i < 256; i += 4) {
+ uint32_t tmp;
+
+ if (tg3_nvram_read(tp, 0x100 + i, &tmp))
+ goto out_not_found;
+
+ vpd_data[i + 0] = ((tmp >> 0) & 0xff);
+ vpd_data[i + 1] = ((tmp >> 8) & 0xff);
+ vpd_data[i + 2] = ((tmp >> 16) & 0xff);
+ vpd_data[i + 3] = ((tmp >> 24) & 0xff);
+ }
+
+ /* Now parse and find the part number. */
+ for (i = 0; i < 256; ) {
+ unsigned char val = vpd_data[i];
+ int block_end;
+
+ if (val == 0x82 || val == 0x91) {
+ i = (i + 3 +
+ (vpd_data[i + 1] +
+ (vpd_data[i + 2] << 8)));
+ continue;
+ }
+
+ if (val != 0x90)
+ goto out_not_found;
+
+ block_end = (i + 3 +
+ (vpd_data[i + 1] +
+ (vpd_data[i + 2] << 8)));
+ i += 3;
+ while (i < block_end) {
+ if (vpd_data[i + 0] == 'P' &&
+ vpd_data[i + 1] == 'N') {
+ int partno_len = vpd_data[i + 2];
+
+ if (partno_len > 24)
+ goto out_not_found;
+
+ memcpy(tp->board_part_number,
+ &vpd_data[i + 3],
+ partno_len);
+
+ /* Success. */
+ return;
+ }
+ }
+
+ /* Part number not found. */
+ goto out_not_found;
+ }
+
+out_not_found:
+ memcpy(tp->board_part_number, "none", sizeof("none"));
+}
+#else
+#define tg3_read_partno(TP) ((TP)->board_part_number[0] = '\0')
+#endif
+
+static int tg3_get_invariants(struct tg3 *tp)
+{
+ uint32_t misc_ctrl_reg;
+ uint32_t pci_state_reg, grc_misc_cfg;
+ uint16_t pci_cmd;
+ uint8_t pci_latency;
+ uint32_t val ;
+ int err;
+
+ /* Read the subsystem vendor and device ids */
+ pci_read_config_word(tp->pdev, PCI_SUBSYSTEM_VENDOR_ID, &tp->subsystem_vendor);
+ pci_read_config_word(tp->pdev, PCI_SUBSYSTEM_ID, &tp->subsystem_device);
+
+ /* The sun_5704 code needs infrastructure etherboot does have
+ * ignore it for now.
+ */
+
+ /* If we have an AMD 762 or Intel ICH/ICH0 chipset, write
+ * reordering to the mailbox registers done by the host
+ * controller can cause major troubles. We read back from
+ * every mailbox register write to force the writes to be
+ * posted to the chip in order.
+ *
+ * TG3_FLAG_MBOX_WRITE_REORDER has been forced on.
+ */
+
+ /* Force memory write invalidate off. If we leave it on,
+ * then on 5700_BX chips we have to enable a workaround.
+ * The workaround is to set the TG3PCI_DMA_RW_CTRL boundry
+ * to match the cacheline size. The Broadcom driver have this
+ * workaround but turns MWI off all the times so never uses
+ * it. This seems to suggest that the workaround is insufficient.
+ */
+ pci_read_config_word(tp->pdev, PCI_COMMAND, &pci_cmd);
+ pci_cmd &= ~PCI_COMMAND_INVALIDATE;
+ /* Also, force SERR#/PERR# in PCI command. */
+ pci_cmd |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR;
+ pci_write_config_word(tp->pdev, PCI_COMMAND, pci_cmd);
+
+ /* It is absolutely critical that TG3PCI_MISC_HOST_CTRL
+ * has the register indirect write enable bit set before
+ * we try to access any of the MMIO registers. It is also
+ * critical that the PCI-X hw workaround situation is decided
+ * before that as well.
+ */
+ pci_read_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL, &misc_ctrl_reg);
+
+ tp->pci_chip_rev_id = (misc_ctrl_reg >> MISC_HOST_CTRL_CHIPREV_SHIFT);
+
+ /* Initialize misc host control in PCI block. */
+ tp->misc_host_ctrl |= (misc_ctrl_reg &
+ MISC_HOST_CTRL_CHIPREV);
+ pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL,
+ tp->misc_host_ctrl);
+
+ pci_read_config_byte(tp->pdev, PCI_LATENCY_TIMER, &pci_latency);
+ if (pci_latency < 64) {
+ pci_write_config_byte(tp->pdev, PCI_LATENCY_TIMER, 64);
+ }
+
+ pci_read_config_dword(tp->pdev, TG3PCI_PCISTATE, &pci_state_reg);
+
+ /* If this is a 5700 BX chipset, and we are in PCI-X
+ * mode, enable register write workaround.
+ *
+ * The workaround is to use indirect register accesses
+ * for all chip writes not to mailbox registers.
+ *
+ * In etherboot to simplify things we just always use this work around.
+ */
+ if ((pci_state_reg & PCISTATE_CONV_PCI_MODE) == 0) {
+ tp->tg3_flags |= TG3_FLAG_PCIX_MODE;
+ }
+ /* Back to back register writes can cause problems on the 5701,
+ * the workaround is to read back all reg writes except those to
+ * mailbox regs.
+ * In etherboot we always use indirect register accesses so
+ * we don't see this.
+ */
+
+ if ((pci_state_reg & PCISTATE_BUS_SPEED_HIGH) != 0)
+ tp->tg3_flags |= TG3_FLAG_PCI_HIGH_SPEED;
+ if ((pci_state_reg & PCISTATE_BUS_32BIT) != 0)
+ tp->tg3_flags |= TG3_FLAG_PCI_32BIT;
+
+ /* Chip-specific fixup from Broadcom driver */
+ if ((tp->pci_chip_rev_id == CHIPREV_ID_5704_A0) &&
+ (!(pci_state_reg & PCISTATE_RETRY_SAME_DMA))) {
+ pci_state_reg |= PCISTATE_RETRY_SAME_DMA;
+ pci_write_config_dword(tp->pdev, TG3PCI_PCISTATE, pci_state_reg);
+ }
+
+ /* determine if it is PCIE system */
+ // Alf : I have no idea what this is about...
+ // But it's definitely usefull
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) {
+ val = tr32(TG3PCI_MSI_CAP_ID) ;
+ if (((val >> 8) & 0xff) == T3_PCIE_CAPABILITY_ID_REG) {
+ val = tr32(T3_PCIE_CAPABILITY_ID_REG) ;
+ if ((val & 0xff) == T3_PCIE_CAPABILITY_ID) {
+ tp->tg3_flags2 |= TG3_FLG2_PCI_EXPRESS ;
+ }
+ }
+ }
+
+ /* Force the chip into D0. */
+ tg3_set_power_state_0(tp);
+
+ /* Etherboot does not ask the tg3 to do checksums */
+ /* Etherboot does not ask the tg3 to do jumbo frames */
+ /* Ehterboot does not ask the tg3 to use WakeOnLan. */
+
+ /* A few boards don't want Ethernet@WireSpeed phy feature */
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) ||
+ ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) &&
+ (tp->pci_chip_rev_id != CHIPREV_ID_5705_A0) &&
+ (tp->pci_chip_rev_id != CHIPREV_ID_5705_A1))) {
+ tp->tg3_flags2 |= TG3_FLG2_NO_ETH_WIRE_SPEED;
+ }
+
+ /* Avoid tagged irq status etherboot does not use irqs */
+
+ /* Only 5701 and later support tagged irq status mode.
+ * Also, 5788 chips cannot use tagged irq status.
+ *
+ * However, since etherboot does not use irqs avoid tagged irqs
+ * status because the interrupt condition is more difficult to
+ * fully clear in that mode.
+ */
+
+ /* Since some 5700_AX && 5700_BX have problems with 32BYTE
+ * coalesce_mode, and the rest work fine anything set.
+ * Don't enable HOST_CC_MODE_32BYTE in etherboot.
+ */
+
+ /* Initialize MAC MI mode, polling disabled. */
+ tw32_carefully(MAC_MI_MODE, tp->mi_mode);
+
+ /* Initialize data/descriptor byte/word swapping. */
+ tw32(GRC_MODE, tp->grc_mode);
+
+ tg3_switch_clocks(tp);
+
+ /* Clear this out for sanity. */
+ tw32(TG3PCI_MEM_WIN_BASE_ADDR, 0);
+
+ /* Etherboot does not need to check if the PCIX_TARGET_HWBUG
+ * is needed. It always uses it.
+ */
+
+ udelay(50);
+ tg3_nvram_init(tp);
+
+ /* The TX descriptors will reside in main memory.
+ */
+
+ /* See which board we are using.
+ */
+ grc_misc_cfg = tr32(GRC_MISC_CFG);
+ grc_misc_cfg &= GRC_MISC_CFG_BOARD_ID_MASK;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 &&
+ grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5704CIOBE) {
+ tp->tg3_flags |= TG3_FLAG_SPLIT_MODE;
+ tp->split_mode_max_reqs = SPLIT_MODE_5704_MAX_REQ;
+ }
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 &&
+ (grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5788 ||
+ grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5788M))
+ tp->tg3_flags2 |= TG3_FLG2_IS_5788;
+
+#define PCI_DEVICE_ID_TIGON3_5901 0x170d
+#define PCI_DEVICE_ID_TIGON3_5901_2 0x170e
+
+ /* these are limited to 10/100 only */
+ if (((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) &&
+ ((grc_misc_cfg == 0x8000) || (grc_misc_cfg == 0x4000))) ||
+ ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) &&
+ (tp->pdev->vendor == PCI_VENDOR_ID_BROADCOM) &&
+ ((tp->pdev->dev_id == PCI_DEVICE_ID_TIGON3_5901) ||
+ (tp->pdev->dev_id == PCI_DEVICE_ID_TIGON3_5901_2)))) {
+ tp->tg3_flags |= TG3_FLAG_10_100_ONLY;
+ }
+
+ err = tg3_phy_probe(tp);
+ if (err) {
+ printf("phy probe failed, err %d\n", err);
+ }
+
+ tg3_read_partno(tp);
+
+
+ /* 5700 BX chips need to have their TX producer index mailboxes
+ * written twice to workaround a bug.
+ * In etherboot we do this unconditionally to simplify things.
+ */
+
+ /* 5700 chips can get confused if TX buffers straddle the
+ * 4GB address boundary in some cases.
+ *
+ * In etherboot we can ignore the problem as etherboot lives below 4GB.
+ */
+
+ /* In etherboot wake-on-lan is unconditionally disabled */
+ return err;
+}
+
+static int tg3_get_device_address(struct tg3 *tp)
+{
+ struct nic *nic = tp->nic;
+ uint32_t hi, lo, mac_offset;
+
+ if (PCI_FUNC(tp->pdev->devfn) == 0)
+ mac_offset = 0x7c;
+ else
+ mac_offset = 0xcc;
+
+ /* First try to get it from MAC address mailbox. */
+ tg3_read_mem(NIC_SRAM_MAC_ADDR_HIGH_MBOX, &hi);
+ if ((hi >> 16) == 0x484b) {
+ nic->node_addr[0] = (hi >> 8) & 0xff;
+ nic->node_addr[1] = (hi >> 0) & 0xff;
+
+ tg3_read_mem(NIC_SRAM_MAC_ADDR_LOW_MBOX, &lo);
+ nic->node_addr[2] = (lo >> 24) & 0xff;
+ nic->node_addr[3] = (lo >> 16) & 0xff;
+ nic->node_addr[4] = (lo >> 8) & 0xff;
+ nic->node_addr[5] = (lo >> 0) & 0xff;
+ }
+ /* Next, try NVRAM. */
+ else if (!tg3_nvram_read(tp, mac_offset + 0, &hi) &&
+ !tg3_nvram_read(tp, mac_offset + 4, &lo)) {
+ nic->node_addr[0] = ((hi >> 16) & 0xff);
+ nic->node_addr[1] = ((hi >> 24) & 0xff);
+ nic->node_addr[2] = ((lo >> 0) & 0xff);
+ nic->node_addr[3] = ((lo >> 8) & 0xff);
+ nic->node_addr[4] = ((lo >> 16) & 0xff);
+ nic->node_addr[5] = ((lo >> 24) & 0xff);
+ }
+ /* Finally just fetch it out of the MAC control regs. */
+ else {
+ hi = tr32(MAC_ADDR_0_HIGH);
+ lo = tr32(MAC_ADDR_0_LOW);
+
+ nic->node_addr[5] = lo & 0xff;
+ nic->node_addr[4] = (lo >> 8) & 0xff;
+ nic->node_addr[3] = (lo >> 16) & 0xff;
+ nic->node_addr[2] = (lo >> 24) & 0xff;
+ nic->node_addr[1] = hi & 0xff;
+ nic->node_addr[0] = (hi >> 8) & 0xff;
+ }
+
+ return 0;
+}
+
+
+static int tg3_setup_dma(struct tg3 *tp)
+{
+ tw32(TG3PCI_CLOCK_CTRL, 0);
+
+ if ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) == 0) {
+ tp->dma_rwctrl =
+ (0x7 << DMA_RWCTRL_PCI_WRITE_CMD_SHIFT) |
+ (0x6 << DMA_RWCTRL_PCI_READ_CMD_SHIFT) |
+ (0x7 << DMA_RWCTRL_WRITE_WATER_SHIFT) |
+ (0x7 << DMA_RWCTRL_READ_WATER_SHIFT) |
+ (0x0f << DMA_RWCTRL_MIN_DMA_SHIFT);
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) {
+ tp->dma_rwctrl &= ~(DMA_RWCTRL_MIN_DMA << DMA_RWCTRL_MIN_DMA_SHIFT);
+ }
+ } else {
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704)
+ tp->dma_rwctrl =
+ (0x7 << DMA_RWCTRL_PCI_WRITE_CMD_SHIFT) |
+ (0x6 << DMA_RWCTRL_PCI_READ_CMD_SHIFT) |
+ (0x3 << DMA_RWCTRL_WRITE_WATER_SHIFT) |
+ (0x7 << DMA_RWCTRL_READ_WATER_SHIFT) |
+ (0x00 << DMA_RWCTRL_MIN_DMA_SHIFT);
+ else
+ tp->dma_rwctrl =
+ (0x7 << DMA_RWCTRL_PCI_WRITE_CMD_SHIFT) |
+ (0x6 << DMA_RWCTRL_PCI_READ_CMD_SHIFT) |
+ (0x3 << DMA_RWCTRL_WRITE_WATER_SHIFT) |
+ (0x3 << DMA_RWCTRL_READ_WATER_SHIFT) |
+ (0x0f << DMA_RWCTRL_MIN_DMA_SHIFT);
+
+ /* Wheee, some more chip bugs... */
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704)) {
+ uint32_t ccval = tr32(TG3PCI_CLOCK_CTRL) & 0x1f;
+
+ if ((ccval == 0x6) || (ccval == 0x7)) {
+ tp->dma_rwctrl |= DMA_RWCTRL_ONE_DMA;
+ }
+ }
+ }
+
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704)) {
+ tp->dma_rwctrl &= ~(DMA_RWCTRL_MIN_DMA << DMA_RWCTRL_MIN_DMA_SHIFT);
+ }
+
+ /*
+ Alf : Tried that, but it does not work. Should be this way though :-(
+ if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) {
+ tp->dma_rwctrl |= 0x001f0000;
+ }
+ */
+ tp->dma_rwctrl |= DMA_RWCTRL_ASSERT_ALL_BE;
+
+ tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl);
+
+ return 0;
+}
+
+static void tg3_init_link_config(struct tg3 *tp)
+{
+ tp->link_config.advertising =
+ (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full |
+ ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full |
+ ADVERTISED_Autoneg | ADVERTISED_MII);
+ tp->carrier_ok = 0;
+ tp->link_config.active_speed = SPEED_INVALID;
+ tp->link_config.active_duplex = DUPLEX_INVALID;
+}
+
+
+#if SUPPORT_PHY_STR
+static const char * tg3_phy_string(struct tg3 *tp)
+{
+ switch (tp->phy_id & PHY_ID_MASK) {
+ case PHY_ID_BCM5400: return "5400";
+ case PHY_ID_BCM5401: return "5401";
+ case PHY_ID_BCM5411: return "5411";
+ case PHY_ID_BCM5701: return "5701";
+ case PHY_ID_BCM5703: return "5703";
+ case PHY_ID_BCM5704: return "5704";
+ case PHY_ID_BCM5705: return "5705";
+ case PHY_ID_BCM5750: return "5750";
+ case PHY_ID_BCM5751: return "5751";
+ case PHY_ID_BCM8002: return "8002/serdes";
+ case PHY_ID_SERDES: return "serdes";
+ default: return "unknown";
+ };
+}
+#else
+#define tg3_phy_string(TP) "?"
+#endif
+
+
+static void tg3_poll_link(struct tg3 *tp)
+{
+ uint32_t mac_stat;
+
+ mac_stat = tr32(MAC_STATUS);
+ if (tp->phy_id == PHY_ID_SERDES) {
+ if (tp->carrier_ok?
+ (mac_stat & MAC_STATUS_LNKSTATE_CHANGED):
+ (mac_stat & MAC_STATUS_PCS_SYNCED)) {
+ tw32_carefully(MAC_MODE, tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK);
+ tw32_carefully(MAC_MODE, tp->mac_mode);
+
+ tg3_setup_phy(tp);
+ }
+ }
+ else {
+ if (mac_stat & MAC_STATUS_LNKSTATE_CHANGED) {
+ tg3_setup_phy(tp);
+ }
+ }
+}
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static void tg3_ack_irqs(struct tg3 *tp)
+{
+ if (tp->hw_status->status & SD_STATUS_UPDATED) {
+ /*
+ * writing any value to intr-mbox-0 clears PCI INTA# and
+ * chip-internal interrupt pending events.
+ * writing non-zero to intr-mbox-0 additional tells the
+ * NIC to stop sending us irqs, engaging "in-intr-handler"
+ * event coalescing.
+ */
+ tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW,
+ 0x00000001);
+ /*
+ * Flush PCI write. This also guarantees that our
+ * status block has been flushed to host memory.
+ */
+ tr32(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW);
+ tp->hw_status->status &= ~SD_STATUS_UPDATED;
+ }
+}
+
+static int tg3_poll(struct nic *nic, int retrieve)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+
+ struct tg3 *tp = &tg3;
+ int result;
+
+ result = 0;
+
+ if ( (tp->hw_status->idx[0].rx_producer != tp->rx_rcb_ptr) && !retrieve )
+ return 1;
+
+ tg3_ack_irqs(tp);
+
+ if (tp->hw_status->idx[0].rx_producer != tp->rx_rcb_ptr) {
+ struct tg3_rx_buffer_desc *desc;
+ unsigned int len;
+ desc = &tp->rx_rcb[tp->rx_rcb_ptr];
+ if ((desc->opaque & RXD_OPAQUE_RING_MASK) == RXD_OPAQUE_RING_STD) {
+ len = ((desc->idx_len & RXD_LEN_MASK) >> RXD_LEN_SHIFT) - 4; /* omit crc */
+
+ nic->packetlen = len;
+ memcpy(nic->packet, bus_to_virt(desc->addr_lo), len);
+ result = 1;
+ }
+ tp->rx_rcb_ptr = (tp->rx_rcb_ptr + 1) % TG3_RX_RCB_RING_SIZE;
+
+ /* ACK the status ring */
+ tw32_mailbox2(MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW, tp->rx_rcb_ptr);
+
+ /* Refill RX ring. */
+ if (result) {
+ tp->rx_std_ptr = (tp->rx_std_ptr + 1) % TG3_RX_RING_SIZE;
+ tw32_mailbox2(MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW, tp->rx_std_ptr);
+ }
+ }
+ tg3_poll_link(tp);
+ return result;
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+#if 0
+static void tg3_set_txd(struct tg3 *tp, int entry,
+ dma_addr_t mapping, int len, uint32_t flags,
+ uint32_t mss_and_is_end)
+{
+ struct tg3_tx_buffer_desc *txd = &tp->tx_ring[entry];
+ int is_end = (mss_and_is_end & 0x1);
+ if (is_end) {
+ flags |= TXD_FLAG_END;
+ }
+
+ txd->addr_hi = 0;
+ txd->addr_lo = mapping & 0xffffffff;
+ txd->len_flags = (len << TXD_LEN_SHIFT) | flags;
+ txd->vlan_tag = 0 << TXD_VLAN_TAG_SHIFT;
+}
+#endif
+
+static void tg3_transmit(struct nic *nic, const char *dst_addr,
+ unsigned int type, unsigned int size, const char *packet)
+{
+ static struct eth_frame {
+ uint8_t dst_addr[ETH_ALEN];
+ uint8_t src_addr[ETH_ALEN];
+ uint16_t type;
+ uint8_t data [ETH_FRAME_LEN - ETH_HLEN];
+ } frame[2];
+ static int frame_idx;
+
+ /* send the packet to destination */
+ struct tg3_tx_buffer_desc *txd;
+ struct tg3 *tp;
+ uint32_t entry;
+ int i;
+
+ /* Wait until there is a free packet frame */
+ tp = &tg3;
+ i = 0;
+ entry = tp->tx_prod;
+ while((tp->hw_status->idx[0].tx_consumer != entry) &&
+ (tp->hw_status->idx[0].tx_consumer != PREV_TX(entry))) {
+ mdelay(10); /* give the nick a chance */
+ poll_interruptions();
+ if (++i > 500) { /* timeout 5s for transmit */
+ printf("transmit timed out\n");
+ tg3_halt(tp);
+ tg3_setup_hw(tp);
+ return;
+ }
+ }
+ if (i != 0) {
+ printf("#");
+ }
+
+ /* Copy the packet to the our local buffer */
+ memcpy(&frame[frame_idx].dst_addr, dst_addr, ETH_ALEN);
+ memcpy(&frame[frame_idx].src_addr, nic->node_addr, ETH_ALEN);
+ frame[frame_idx].type = htons(type);
+ memset(&frame[frame_idx].data, 0, sizeof(frame[frame_idx].data));
+ memcpy(&frame[frame_idx].data, packet, size);
+
+ /* Setup the ring buffer entry to transmit */
+ txd = &tp->tx_ring[entry];
+ txd->addr_hi = 0; /* Etherboot runs under 4GB */
+ txd->addr_lo = virt_to_bus(&frame[frame_idx]);
+ txd->len_flags = ((size + ETH_HLEN) << TXD_LEN_SHIFT) | TXD_FLAG_END;
+ txd->vlan_tag = 0 << TXD_VLAN_TAG_SHIFT;
+
+ /* Advance to the next entry */
+ entry = NEXT_TX(entry);
+ frame_idx ^= 1;
+
+ /* Packets are ready, update Tx producer idx local and on card */
+ tw32_mailbox((MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW), entry);
+ tw32_mailbox2((MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW), entry);
+ tp->tx_prod = entry;
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void tg3_disable(struct dev *dev __unused)
+{
+ struct tg3 *tp = &tg3;
+ /* put the card in its initial state */
+ /* This function serves 3 purposes.
+ * This disables DMA and interrupts so we don't receive
+ * unexpected packets or interrupts from the card after
+ * etherboot has finished.
+ * This frees resources so etherboot may use
+ * this driver on another interface
+ * This allows etherboot to reinitialize the interface
+ * if something is something goes wrong.
+ */
+ tg3_halt(tp);
+ tp->tg3_flags &= ~(TG3_FLAG_INIT_COMPLETE|TG3_FLAG_GOT_SERDES_FLOWCTL);
+ tp->carrier_ok = 0;
+ iounmap((void *)tp->regs);
+}
+
+/**************************************************************************
+IRQ - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void tg3_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+You should omit the last argument struct pci_device * for a non-PCI NIC
+***************************************************************************/
+static int tg3_probe(struct dev *dev, struct pci_device *pdev)
+{
+ struct nic *nic = (struct nic *)dev;
+ struct tg3 *tp = &tg3;
+ unsigned long tg3reg_base, tg3reg_len;
+ int i, err, pm_cap;
+
+ if (pdev == 0)
+ return 0;
+
+ memset(tp, 0, sizeof(*tp));
+
+ adjust_pci_device(pdev);
+
+ nic->irqno = 0;
+ nic->ioaddr = pdev->ioaddr & ~3;
+
+ /* Find power-management capability. */
+ pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM);
+ if (pm_cap == 0) {
+ printf("Cannot find PowerManagement capability, aborting.\n");
+ return 0;
+ }
+ tg3reg_base = pci_bar_start(pdev, PCI_BASE_ADDRESS_0);
+ if (tg3reg_base == -1UL) {
+ printf("Unuseable bar\n");
+ return 0;
+ }
+ tg3reg_len = pci_bar_size(pdev, PCI_BASE_ADDRESS_0);
+
+ tp->pdev = pdev;
+ tp->nic = nic;
+ tp->pm_cap = pm_cap;
+ tp->rx_mode = 0;
+ tp->tx_mode = 0;
+ tp->mi_mode = MAC_MI_MODE_BASE;
+ tp->tg3_flags = 0 & ~TG3_FLAG_INIT_COMPLETE;
+
+ /* The word/byte swap controls here control register access byte
+ * swapping. DMA data byte swapping is controlled in the GRC_MODE
+ * setting below.
+ */
+ tp->misc_host_ctrl =
+ MISC_HOST_CTRL_MASK_PCI_INT |
+ MISC_HOST_CTRL_WORD_SWAP |
+ MISC_HOST_CTRL_INDIR_ACCESS |
+ MISC_HOST_CTRL_PCISTATE_RW;
+
+ /* The NONFRM (non-frame) byte/word swap controls take effect
+ * on descriptor entries, anything which isn't packet data.
+ *
+ * The StrongARM chips on the board (one for tx, one for rx)
+ * are running in big-endian mode.
+ */
+ tp->grc_mode = (GRC_MODE_WSWAP_DATA | GRC_MODE_BSWAP_DATA |
+ GRC_MODE_WSWAP_NONFRM_DATA);
+#if __BYTE_ORDER == __BIG_ENDIAN
+ tp->grc_mode |= GRC_MODE_BSWAP_NONFRM_DATA;
+#endif
+ tp->regs = (unsigned long) ioremap(tg3reg_base, tg3reg_len);
+ if (tp->regs == 0UL) {
+ printf("Cannot map device registers, aborting\n");
+ return 0;
+ }
+
+ tg3_init_link_config(tp);
+
+ err = tg3_get_invariants(tp);
+ if (err) {
+ printf("Problem fetching invariants of chip, aborting.\n");
+ goto err_out_iounmap;
+ }
+
+ err = tg3_get_device_address(tp);
+ if (err) {
+ printf("Could not obtain valid ethernet address, aborting.\n");
+ goto err_out_iounmap;
+ }
+ printf("Ethernet addr: %!\n", nic->node_addr);
+
+ tg3_setup_dma(tp);
+
+ /* Now that we have fully setup the chip, save away a snapshot
+ * of the PCI config space. We need to restore this after
+ * GRC_MISC_CFG core clock resets and some resume events.
+ */
+ pci_save_state(tp->pdev, tp->pci_cfg_state);
+
+ printf("Tigon3 [partno(%s) rev %hx PHY(%s)] (PCI%s:%s:%s)\n",
+ tp->board_part_number,
+ tp->pci_chip_rev_id,
+ tg3_phy_string(tp),
+ ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) ? "X" : ""),
+ ((tp->tg3_flags & TG3_FLAG_PCI_HIGH_SPEED) ?
+ ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) ? "133MHz" : "66MHz") :
+ ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) ? "100MHz" : "33MHz")),
+ ((tp->tg3_flags & TG3_FLAG_PCI_32BIT) ? "32-bit" : "64-bit"));
+
+
+ err = tg3_setup_hw(tp);
+ if (err) {
+ goto err_out_disable;
+ }
+ tp->tg3_flags |= TG3_FLAG_INIT_COMPLETE;
+
+ /* Wait for a reasonable time for the link to come up */
+ tg3_poll_link(tp);
+ for(i = 0; !tp->carrier_ok && (i < VALID_LINK_TIMEOUT*100); i++) {
+ mdelay(1);
+ tg3_poll_link(tp);
+ }
+ if (!tp->carrier_ok){
+ printf("Valid link not established\n");
+ goto err_out_disable;
+ }
+
+ dev->disable = tg3_disable;
+ nic->poll = tg3_poll;
+ nic->transmit = tg3_transmit;
+ nic->irq = tg3_irq;
+
+ return 1;
+
+ err_out_iounmap:
+ iounmap((void *)tp->regs);
+ return 0;
+ err_out_disable:
+ tg3_disable(dev);
+ return 0;
+}
+
+static struct pci_id tg3_nics[] = {
+PCI_ROM(0x14e4, 0x1644, "tg3-5700", "Broadcom Tigon 3 5700"),
+PCI_ROM(0x14e4, 0x1645, "tg3-5701", "Broadcom Tigon 3 5701"),
+PCI_ROM(0x14e4, 0x1646, "tg3-5702", "Broadcom Tigon 3 5702"),
+PCI_ROM(0x14e4, 0x1647, "tg3-5703", "Broadcom Tigon 3 5703"),
+PCI_ROM(0x14e4, 0x1648, "tg3-5704", "Broadcom Tigon 3 5704"),
+PCI_ROM(0x14e4, 0x164d, "tg3-5702FE", "Broadcom Tigon 3 5702FE"),
+PCI_ROM(0x14e4, 0x1653, "tg3-5705", "Broadcom Tigon 3 5705"),
+PCI_ROM(0x14e4, 0x1654, "tg3-5705_2", "Broadcom Tigon 3 5705_2"),
+PCI_ROM(0x14e4, 0x165d, "tg3-5705M", "Broadcom Tigon 3 5705M"),
+PCI_ROM(0x14e4, 0x165e, "tg3-5705M_2", "Broadcom Tigon 3 5705M_2"),
+PCI_ROM(0x14e4, 0x1677, "tg3-5751", "Broadcom Tigon 3 5751"),
+PCI_ROM(0x14e4, 0x1696, "tg3-5782", "Broadcom Tigon 3 5782"),
+PCI_ROM(0x14e4, 0x169c, "tg3-5788", "Broadcom Tigon 3 5788"),
+PCI_ROM(0x14e4, 0x16a6, "tg3-5702X", "Broadcom Tigon 3 5702X"),
+PCI_ROM(0x14e4, 0x16a7, "tg3-5703X", "Broadcom Tigon 3 5703X"),
+PCI_ROM(0x14e4, 0x16a8, "tg3-5704S", "Broadcom Tigon 3 5704S"),
+PCI_ROM(0x14e4, 0x16c6, "tg3-5702A3", "Broadcom Tigon 3 5702A3"),
+PCI_ROM(0x14e4, 0x16c7, "tg3-5703A3", "Broadcom Tigon 3 5703A3"),
+PCI_ROM(0x14e4, 0x170d, "tg3-5901", "Broadcom Tigon 3 5901"),
+PCI_ROM(0x14e4, 0x170e, "tg3-5901_2", "Broadcom Tigon 3 5901_2"),
+PCI_ROM(0x1148, 0x4400, "tg3-9DXX", "Syskonnect 9DXX"),
+PCI_ROM(0x1148, 0x4500, "tg3-9MXX", "Syskonnect 9MXX"),
+PCI_ROM(0x173b, 0x03e8, "tg3-ac1000", "Altima AC1000"),
+PCI_ROM(0x173b, 0x03e9, "tg3-ac1001", "Altima AC1001"),
+PCI_ROM(0x173b, 0x03ea, "tg3-ac9100", "Altima AC9100"),
+PCI_ROM(0x173b, 0x03eb, "tg3-ac1003", "Altima AC1003"),
+};
+
+static struct pci_driver tg3_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "TG3",
+ .probe = tg3_probe,
+ .ids = tg3_nics,
+ .id_count = sizeof(tg3_nics)/sizeof(tg3_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/tg3.h b/src/drivers/net/tg3.h
new file mode 100644
index 00000000..6e05b9cc
--- /dev/null
+++ b/src/drivers/net/tg3.h
@@ -0,0 +1,2211 @@
+/* $Id$
+ * tg3.h: Definitions for Broadcom Tigon3 ethernet driver.
+ *
+ * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com)
+ * Copyright (C) 2001 Jeff Garzik (jgarzik@mandrakesoft.com)
+ */
+
+#ifndef _T3_H
+#define _T3_H
+
+#include "stdint.h"
+
+typedef unsigned long dma_addr_t;
+
+/* From mii.h */
+
+/* Indicates what features are advertised by the interface. */
+#define ADVERTISED_10baseT_Half (1 << 0)
+#define ADVERTISED_10baseT_Full (1 << 1)
+#define ADVERTISED_100baseT_Half (1 << 2)
+#define ADVERTISED_100baseT_Full (1 << 3)
+#define ADVERTISED_1000baseT_Half (1 << 4)
+#define ADVERTISED_1000baseT_Full (1 << 5)
+#define ADVERTISED_Autoneg (1 << 6)
+#define ADVERTISED_TP (1 << 7)
+#define ADVERTISED_AUI (1 << 8)
+#define ADVERTISED_MII (1 << 9)
+#define ADVERTISED_FIBRE (1 << 10)
+#define ADVERTISED_BNC (1 << 11)
+
+/* The following are all involved in forcing a particular link
+ * mode for the device for setting things. When getting the
+ * devices settings, these indicate the current mode and whether
+ * it was foced up into this mode or autonegotiated.
+ */
+
+/* The forced speed, 10Mb, 100Mb, gigabit. */
+#define SPEED_10 0
+#define SPEED_100 1
+#define SPEED_1000 2
+#define SPEED_INVALID 3
+
+
+/* Duplex, half or full. */
+#define DUPLEX_HALF 0x00
+#define DUPLEX_FULL 0x01
+#define DUPLEX_INVALID 0x02
+
+/* Which connector port. */
+#define PORT_TP 0x00
+#define PORT_AUI 0x01
+#define PORT_MII 0x02
+#define PORT_FIBRE 0x03
+#define PORT_BNC 0x04
+
+/* Which tranceiver to use. */
+#define XCVR_INTERNAL 0x00
+#define XCVR_EXTERNAL 0x01
+#define XCVR_DUMMY1 0x02
+#define XCVR_DUMMY2 0x03
+#define XCVR_DUMMY3 0x04
+
+/* Enable or disable autonegotiation. If this is set to enable,
+ * the forced link modes above are completely ignored.
+ */
+#define AUTONEG_DISABLE 0x00
+#define AUTONEG_ENABLE 0x01
+
+/* Wake-On-Lan options. */
+#define WAKE_PHY (1 << 0)
+#define WAKE_UCAST (1 << 1)
+#define WAKE_MCAST (1 << 2)
+#define WAKE_BCAST (1 << 3)
+#define WAKE_ARP (1 << 4)
+#define WAKE_MAGIC (1 << 5)
+#define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */
+
+/* Generic MII registers. */
+
+#define MII_BMCR 0x00 /* Basic mode control register */
+#define MII_BMSR 0x01 /* Basic mode status register */
+#define MII_PHYSID1 0x02 /* PHYS ID 1 */
+#define MII_PHYSID2 0x03 /* PHYS ID 2 */
+#define MII_ADVERTISE 0x04 /* Advertisement control reg */
+#define MII_LPA 0x05 /* Link partner ability reg */
+#define MII_EXPANSION 0x06 /* Expansion register */
+#define MII_DCOUNTER 0x12 /* Disconnect counter */
+#define MII_FCSCOUNTER 0x13 /* False carrier counter */
+#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */
+#define MII_RERRCOUNTER 0x15 /* Receive error counter */
+#define MII_SREVISION 0x16 /* Silicon revision */
+#define MII_RESV1 0x17 /* Reserved... */
+#define MII_LBRERROR 0x18 /* Lpback, rx, bypass error */
+#define MII_PHYADDR 0x19 /* PHY address */
+#define MII_RESV2 0x1a /* Reserved... */
+#define MII_TPISTATUS 0x1b /* TPI status for 10mbps */
+#define MII_NCONFIG 0x1c /* Network interface config */
+
+/* Basic mode control register. */
+#define BMCR_RESV 0x007f /* Unused... */
+#define BMCR_CTST 0x0080 /* Collision test */
+#define BMCR_FULLDPLX 0x0100 /* Full duplex */
+#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */
+#define BMCR_ISOLATE 0x0400 /* Disconnect DP83840 from MII */
+#define BMCR_PDOWN 0x0800 /* Powerdown the DP83840 */
+#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */
+#define BMCR_SPEED100 0x2000 /* Select 100Mbps */
+#define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */
+#define BMCR_RESET 0x8000 /* Reset the DP83840 */
+
+/* Basic mode status register. */
+#define BMSR_ERCAP 0x0001 /* Ext-reg capability */
+#define BMSR_JCD 0x0002 /* Jabber detected */
+#define BMSR_LSTATUS 0x0004 /* Link status */
+#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */
+#define BMSR_RFAULT 0x0010 /* Remote fault detected */
+#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */
+#define BMSR_RESV 0x07c0 /* Unused... */
+#define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */
+#define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */
+#define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */
+#define BMSR_100FULL 0x4000 /* Can do 100mbps, full-duplex */
+#define BMSR_100BASE4 0x8000 /* Can do 100mbps, 4k packets */
+
+/* Advertisement control register. */
+#define ADVERTISE_SLCT 0x001f /* Selector bits */
+#define ADVERTISE_CSMA 0x0001 /* Only selector supported */
+#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */
+#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */
+#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */
+#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */
+#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */
+#define ADVERTISE_RESV 0x1c00 /* Unused... */
+#define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */
+#define ADVERTISE_LPACK 0x4000 /* Ack link partners response */
+#define ADVERTISE_NPAGE 0x8000 /* Next page bit */
+
+#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \
+ ADVERTISE_CSMA)
+#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \
+ ADVERTISE_100HALF | ADVERTISE_100FULL)
+
+/* Link partner ability register. */
+#define LPA_SLCT 0x001f /* Same as advertise selector */
+#define LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */
+#define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */
+#define LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */
+#define LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */
+#define LPA_100BASE4 0x0200 /* Can do 100mbps 4k packets */
+#define LPA_RESV 0x1c00 /* Unused... */
+#define LPA_RFAULT 0x2000 /* Link partner faulted */
+#define LPA_LPACK 0x4000 /* Link partner acked us */
+#define LPA_NPAGE 0x8000 /* Next page bit */
+
+#define LPA_DUPLEX (LPA_10FULL | LPA_100FULL)
+#define LPA_100 (LPA_100FULL | LPA_100HALF | LPA_100BASE4)
+
+/* Expansion register for auto-negotiation. */
+#define EXPANSION_NWAY 0x0001 /* Can do N-way auto-nego */
+#define EXPANSION_LCWP 0x0002 /* Got new RX page code word */
+#define EXPANSION_ENABLENPAGE 0x0004 /* This enables npage words */
+#define EXPANSION_NPCAPABLE 0x0008 /* Link partner supports npage */
+#define EXPANSION_MFAULTS 0x0010 /* Multiple faults detected */
+#define EXPANSION_RESV 0xffe0 /* Unused... */
+
+/* N-way test register. */
+#define NWAYTEST_RESV1 0x00ff /* Unused... */
+#define NWAYTEST_LOOPBACK 0x0100 /* Enable loopback for N-way */
+#define NWAYTEST_RESV2 0xfe00 /* Unused... */
+
+
+/* From tg3.h */
+
+#define TG3_64BIT_REG_HIGH 0x00UL
+#define TG3_64BIT_REG_LOW 0x04UL
+
+/* Descriptor block info. */
+#define TG3_BDINFO_HOST_ADDR 0x0UL /* 64-bit */
+#define TG3_BDINFO_MAXLEN_FLAGS 0x8UL /* 32-bit */
+#define BDINFO_FLAGS_USE_EXT_RECV 0x00000001 /* ext rx_buffer_desc */
+#define BDINFO_FLAGS_DISABLED 0x00000002
+#define BDINFO_FLAGS_MAXLEN_MASK 0xffff0000
+#define BDINFO_FLAGS_MAXLEN_SHIFT 16
+#define TG3_BDINFO_NIC_ADDR 0xcUL /* 32-bit */
+#define TG3_BDINFO_SIZE 0x10UL
+
+#define RX_COPY_THRESHOLD 256
+
+#define RX_STD_MAX_SIZE 1536
+#define RX_STD_MAX_SIZE_5705 512
+#define RX_JUMBO_MAX_SIZE 0xdeadbeef /* XXX */
+
+/* First 256 bytes are a mirror of PCI config space. */
+#define TG3PCI_VENDOR 0x00000000
+#define TG3PCI_VENDOR_BROADCOM 0x14e4
+#define TG3PCI_DEVICE 0x00000002
+#define TG3PCI_DEVICE_TIGON3_1 0x1644 /* BCM5700 */
+#define TG3PCI_DEVICE_TIGON3_2 0x1645 /* BCM5701 */
+#define TG3PCI_DEVICE_TIGON3_3 0x1646 /* BCM5702 */
+#define TG3PCI_DEVICE_TIGON3_4 0x1647 /* BCM5703 */
+#define TG3PCI_COMMAND 0x00000004
+#define TG3PCI_STATUS 0x00000006
+#define TG3PCI_CCREVID 0x00000008
+#define TG3PCI_CACHELINESZ 0x0000000c
+#define TG3PCI_LATTIMER 0x0000000d
+#define TG3PCI_HEADERTYPE 0x0000000e
+#define TG3PCI_BIST 0x0000000f
+#define TG3PCI_BASE0_LOW 0x00000010
+#define TG3PCI_BASE0_HIGH 0x00000014
+/* 0x18 --> 0x2c unused */
+#define TG3PCI_SUBSYSVENID 0x0000002c
+#define TG3PCI_SUBSYSID 0x0000002e
+#define TG3PCI_ROMADDR 0x00000030
+#define TG3PCI_CAPLIST 0x00000034
+/* 0x35 --> 0x3c unused */
+#define TG3PCI_IRQ_LINE 0x0000003c
+#define TG3PCI_IRQ_PIN 0x0000003d
+#define TG3PCI_MIN_GNT 0x0000003e
+#define TG3PCI_MAX_LAT 0x0000003f
+#define TG3PCI_X_CAPS 0x00000040
+#define PCIX_CAPS_RELAXED_ORDERING 0x00020000
+#define PCIX_CAPS_SPLIT_MASK 0x00700000
+#define PCIX_CAPS_SPLIT_SHIFT 20
+#define PCIX_CAPS_BURST_MASK 0x000c0000
+#define PCIX_CAPS_BURST_SHIFT 18
+#define PCIX_CAPS_MAX_BURST_CPIOB 2
+#define TG3PCI_PM_CAP_PTR 0x00000041
+#define TG3PCI_X_COMMAND 0x00000042
+#define TG3PCI_X_STATUS 0x00000044
+#define TG3PCI_PM_CAP_ID 0x00000048
+#define TG3PCI_VPD_CAP_PTR 0x00000049
+#define TG3PCI_PM_CAPS 0x0000004a
+#define TG3PCI_PM_CTRL_STAT 0x0000004c
+#define TG3PCI_BR_SUPP_EXT 0x0000004e
+#define TG3PCI_PM_DATA 0x0000004f
+#define TG3PCI_VPD_CAP_ID 0x00000050
+#define TG3PCI_MSI_CAP_PTR 0x00000051
+#define TG3PCI_VPD_ADDR_FLAG 0x00000052
+#define VPD_ADDR_FLAG_WRITE 0x00008000
+#define TG3PCI_VPD_DATA 0x00000054
+#define TG3PCI_MSI_CAP_ID 0x00000058
+#define TG3PCI_NXT_CAP_PTR 0x00000059
+#define TG3PCI_MSI_CTRL 0x0000005a
+#define TG3PCI_MSI_ADDR_LOW 0x0000005c
+#define TG3PCI_MSI_ADDR_HIGH 0x00000060
+#define TG3PCI_MSI_DATA 0x00000064
+/* 0x66 --> 0x68 unused */
+#define TG3PCI_MISC_HOST_CTRL 0x00000068
+#define MISC_HOST_CTRL_CLEAR_INT 0x00000001
+#define MISC_HOST_CTRL_MASK_PCI_INT 0x00000002
+#define MISC_HOST_CTRL_BYTE_SWAP 0x00000004
+#define MISC_HOST_CTRL_WORD_SWAP 0x00000008
+#define MISC_HOST_CTRL_PCISTATE_RW 0x00000010
+#define MISC_HOST_CTRL_CLKREG_RW 0x00000020
+#define MISC_HOST_CTRL_REGWORD_SWAP 0x00000040
+#define MISC_HOST_CTRL_INDIR_ACCESS 0x00000080
+#define MISC_HOST_CTRL_IRQ_MASK_MODE 0x00000100
+#define MISC_HOST_CTRL_TAGGED_STATUS 0x00000200
+#define MISC_HOST_CTRL_CHIPREV 0xffff0000
+#define MISC_HOST_CTRL_CHIPREV_SHIFT 16
+#define GET_CHIP_REV_ID(MISC_HOST_CTRL) \
+ (((MISC_HOST_CTRL) & MISC_HOST_CTRL_CHIPREV) >> \
+ MISC_HOST_CTRL_CHIPREV_SHIFT)
+#define CHIPREV_ID_5700_A0 0x7000
+#define CHIPREV_ID_5700_A1 0x7001
+#define CHIPREV_ID_5700_B0 0x7100
+#define CHIPREV_ID_5700_B1 0x7101
+#define CHIPREV_ID_5700_B3 0x7102
+#define CHIPREV_ID_5700_ALTIMA 0x7104
+#define CHIPREV_ID_5700_C0 0x7200
+#define CHIPREV_ID_5701_A0 0x0000
+#define CHIPREV_ID_5701_B0 0x0100
+#define CHIPREV_ID_5701_B2 0x0102
+#define CHIPREV_ID_5701_B5 0x0105
+#define CHIPREV_ID_5703_A0 0x1000
+#define CHIPREV_ID_5703_A1 0x1001
+#define CHIPREV_ID_5703_A2 0x1002
+#define CHIPREV_ID_5703_A3 0x1003
+#define CHIPREV_ID_5704_A0 0x2000
+#define CHIPREV_ID_5704_A1 0x2001
+#define CHIPREV_ID_5704_A2 0x2002
+#define CHIPREV_ID_5705_A0 0x3000
+#define CHIPREV_ID_5705_A1 0x3001
+#define CHIPREV_ID_5705_A2 0x3002
+#define CHIPREV_ID_5705_A3 0x3003
+#define CHIPREV_ID_5750_A0 0x4000
+#define CHIPREV_ID_5750_A1 0x4001
+#define CHIPREV_ID_5750_A3 0x4003
+#define GET_ASIC_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 12)
+#define ASIC_REV_5700 0x07
+#define ASIC_REV_5701 0x00
+#define ASIC_REV_5703 0x01
+#define ASIC_REV_5704 0x02
+#define ASIC_REV_5705 0x03
+#define ASIC_REV_5750 0x04
+#define GET_CHIP_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 8)
+#define CHIPREV_5700_AX 0x70
+#define CHIPREV_5700_BX 0x71
+#define CHIPREV_5700_CX 0x72
+#define CHIPREV_5701_AX 0x00
+#define GET_METAL_REV(CHIP_REV_ID) ((CHIP_REV_ID) & 0xff)
+#define METAL_REV_A0 0x00
+#define METAL_REV_A1 0x01
+#define METAL_REV_B0 0x00
+#define METAL_REV_B1 0x01
+#define METAL_REV_B2 0x02
+#define TG3PCI_DMA_RW_CTRL 0x0000006c
+#define DMA_RWCTRL_MIN_DMA 0x000000ff
+#define DMA_RWCTRL_MIN_DMA_SHIFT 0
+#define DMA_RWCTRL_READ_BNDRY_MASK 0x00000700
+#define DMA_RWCTRL_READ_BNDRY_DISAB 0x00000000
+#define DMA_RWCTRL_READ_BNDRY_16 0x00000100
+#define DMA_RWCTRL_READ_BNDRY_32 0x00000200
+#define DMA_RWCTRL_READ_BNDRY_64 0x00000300
+#define DMA_RWCTRL_READ_BNDRY_128 0x00000400
+#define DMA_RWCTRL_READ_BNDRY_256 0x00000500
+#define DMA_RWCTRL_READ_BNDRY_512 0x00000600
+#define DMA_RWCTRL_READ_BNDRY_1024 0x00000700
+#define DMA_RWCTRL_WRITE_BNDRY_MASK 0x00003800
+#define DMA_RWCTRL_WRITE_BNDRY_DISAB 0x00000000
+#define DMA_RWCTRL_WRITE_BNDRY_16 0x00000800
+#define DMA_RWCTRL_WRITE_BNDRY_32 0x00001000
+#define DMA_RWCTRL_WRITE_BNDRY_64 0x00001800
+#define DMA_RWCTRL_WRITE_BNDRY_128 0x00002000
+#define DMA_RWCTRL_WRITE_BNDRY_256 0x00002800
+#define DMA_RWCTRL_WRITE_BNDRY_512 0x00003000
+#define DMA_RWCTRL_WRITE_BNDRY_1024 0x00003800
+#define DMA_RWCTRL_ONE_DMA 0x00004000
+#define DMA_RWCTRL_READ_WATER 0x00070000
+#define DMA_RWCTRL_READ_WATER_SHIFT 16
+#define DMA_RWCTRL_WRITE_WATER 0x00380000
+#define DMA_RWCTRL_WRITE_WATER_SHIFT 19
+#define DMA_RWCTRL_USE_MEM_READ_MULT 0x00400000
+#define DMA_RWCTRL_ASSERT_ALL_BE 0x00800000
+#define DMA_RWCTRL_PCI_READ_CMD 0x0f000000
+#define DMA_RWCTRL_PCI_READ_CMD_SHIFT 24
+#define DMA_RWCTRL_PCI_WRITE_CMD 0xf0000000
+#define DMA_RWCTRL_PCI_WRITE_CMD_SHIFT 28
+#define TG3PCI_PCISTATE 0x00000070
+#define PCISTATE_FORCE_RESET 0x00000001
+#define PCISTATE_INT_NOT_ACTIVE 0x00000002
+#define PCISTATE_CONV_PCI_MODE 0x00000004
+#define PCISTATE_BUS_SPEED_HIGH 0x00000008
+#define PCISTATE_BUS_32BIT 0x00000010
+#define PCISTATE_ROM_ENABLE 0x00000020
+#define PCISTATE_ROM_RETRY_ENABLE 0x00000040
+#define PCISTATE_FLAT_VIEW 0x00000100
+#define PCISTATE_RETRY_SAME_DMA 0x00002000
+#define TG3PCI_CLOCK_CTRL 0x00000074
+#define CLOCK_CTRL_CORECLK_DISABLE 0x00000200
+#define CLOCK_CTRL_RXCLK_DISABLE 0x00000400
+#define CLOCK_CTRL_TXCLK_DISABLE 0x00000800
+#define CLOCK_CTRL_ALTCLK 0x00001000
+#define CLOCK_CTRL_PWRDOWN_PLL133 0x00008000
+#define CLOCK_CTRL_44MHZ_CORE 0x00040000
+#define CLOCK_CTRL_625_CORE 0x00100000
+#define CLOCK_CTRL_FORCE_CLKRUN 0x00200000
+#define CLOCK_CTRL_CLKRUN_OENABLE 0x00400000
+#define CLOCK_CTRL_DELAY_PCI_GRANT 0x80000000
+#define TG3PCI_REG_BASE_ADDR 0x00000078
+#define TG3PCI_MEM_WIN_BASE_ADDR 0x0000007c
+#define TG3PCI_REG_DATA 0x00000080
+#define TG3PCI_MEM_WIN_DATA 0x00000084
+#define TG3PCI_MODE_CTRL 0x00000088
+#define TG3PCI_MISC_CFG 0x0000008c
+#define TG3PCI_MISC_LOCAL_CTRL 0x00000090
+/* 0x94 --> 0x98 unused */
+#define TG3PCI_STD_RING_PROD_IDX 0x00000098 /* 64-bit */
+#define TG3PCI_RCV_RET_RING_CON_IDX 0x000000a0 /* 64-bit */
+#define TG3PCI_SND_PROD_IDX 0x000000a8 /* 64-bit */
+/* 0xb0 --> 0x100 unused */
+
+/* 0x100 --> 0x200 unused */
+
+/* Mailbox registers */
+#define MAILBOX_INTERRUPT_0 0x00000200 /* 64-bit */
+#define MAILBOX_INTERRUPT_1 0x00000208 /* 64-bit */
+#define MAILBOX_INTERRUPT_2 0x00000210 /* 64-bit */
+#define MAILBOX_INTERRUPT_3 0x00000218 /* 64-bit */
+#define MAILBOX_GENERAL_0 0x00000220 /* 64-bit */
+#define MAILBOX_GENERAL_1 0x00000228 /* 64-bit */
+#define MAILBOX_GENERAL_2 0x00000230 /* 64-bit */
+#define MAILBOX_GENERAL_3 0x00000238 /* 64-bit */
+#define MAILBOX_GENERAL_4 0x00000240 /* 64-bit */
+#define MAILBOX_GENERAL_5 0x00000248 /* 64-bit */
+#define MAILBOX_GENERAL_6 0x00000250 /* 64-bit */
+#define MAILBOX_GENERAL_7 0x00000258 /* 64-bit */
+#define MAILBOX_RELOAD_STAT 0x00000260 /* 64-bit */
+#define MAILBOX_RCV_STD_PROD_IDX 0x00000268 /* 64-bit */
+#define MAILBOX_RCV_JUMBO_PROD_IDX 0x00000270 /* 64-bit */
+#define MAILBOX_RCV_MINI_PROD_IDX 0x00000278 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_0 0x00000280 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_1 0x00000288 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_2 0x00000290 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_3 0x00000298 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_4 0x000002a0 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_5 0x000002a8 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_6 0x000002b0 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_7 0x000002b8 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_8 0x000002c0 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_9 0x000002c8 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_10 0x000002d0 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_11 0x000002d8 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_12 0x000002e0 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_13 0x000002e8 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_14 0x000002f0 /* 64-bit */
+#define MAILBOX_RCVRET_CON_IDX_15 0x000002f8 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_0 0x00000300 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_1 0x00000308 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_2 0x00000310 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_3 0x00000318 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_4 0x00000320 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_5 0x00000328 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_6 0x00000330 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_7 0x00000338 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_8 0x00000340 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_9 0x00000348 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_10 0x00000350 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_11 0x00000358 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_12 0x00000360 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_13 0x00000368 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_14 0x00000370 /* 64-bit */
+#define MAILBOX_SNDHOST_PROD_IDX_15 0x00000378 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_0 0x00000380 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_1 0x00000388 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_2 0x00000390 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_3 0x00000398 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_4 0x000003a0 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_5 0x000003a8 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_6 0x000003b0 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_7 0x000003b8 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_8 0x000003c0 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_9 0x000003c8 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_10 0x000003d0 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_11 0x000003d8 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_12 0x000003e0 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_13 0x000003e8 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_14 0x000003f0 /* 64-bit */
+#define MAILBOX_SNDNIC_PROD_IDX_15 0x000003f8 /* 64-bit */
+
+/* MAC control registers */
+#define MAC_MODE 0x00000400
+#define MAC_MODE_RESET 0x00000001
+#define MAC_MODE_HALF_DUPLEX 0x00000002
+#define MAC_MODE_PORT_MODE_MASK 0x0000000c
+#define MAC_MODE_PORT_MODE_TBI 0x0000000c
+#define MAC_MODE_PORT_MODE_GMII 0x00000008
+#define MAC_MODE_PORT_MODE_MII 0x00000004
+#define MAC_MODE_PORT_MODE_NONE 0x00000000
+#define MAC_MODE_PORT_INT_LPBACK 0x00000010
+#define MAC_MODE_TAGGED_MAC_CTRL 0x00000080
+#define MAC_MODE_TX_BURSTING 0x00000100
+#define MAC_MODE_MAX_DEFER 0x00000200
+#define MAC_MODE_LINK_POLARITY 0x00000400
+#define MAC_MODE_RXSTAT_ENABLE 0x00000800
+#define MAC_MODE_RXSTAT_CLEAR 0x00001000
+#define MAC_MODE_RXSTAT_FLUSH 0x00002000
+#define MAC_MODE_TXSTAT_ENABLE 0x00004000
+#define MAC_MODE_TXSTAT_CLEAR 0x00008000
+#define MAC_MODE_TXSTAT_FLUSH 0x00010000
+#define MAC_MODE_SEND_CONFIGS 0x00020000
+#define MAC_MODE_MAGIC_PKT_ENABLE 0x00040000
+#define MAC_MODE_ACPI_ENABLE 0x00080000
+#define MAC_MODE_MIP_ENABLE 0x00100000
+#define MAC_MODE_TDE_ENABLE 0x00200000
+#define MAC_MODE_RDE_ENABLE 0x00400000
+#define MAC_MODE_FHDE_ENABLE 0x00800000
+#define MAC_STATUS 0x00000404
+#define MAC_STATUS_PCS_SYNCED 0x00000001
+#define MAC_STATUS_SIGNAL_DET 0x00000002
+#define MAC_STATUS_RCVD_CFG 0x00000004
+#define MAC_STATUS_CFG_CHANGED 0x00000008
+#define MAC_STATUS_SYNC_CHANGED 0x00000010
+#define MAC_STATUS_PORT_DEC_ERR 0x00000400
+#define MAC_STATUS_LNKSTATE_CHANGED 0x00001000
+#define MAC_STATUS_MI_COMPLETION 0x00400000
+#define MAC_STATUS_MI_INTERRUPT 0x00800000
+#define MAC_STATUS_AP_ERROR 0x01000000
+#define MAC_STATUS_ODI_ERROR 0x02000000
+#define MAC_STATUS_RXSTAT_OVERRUN 0x04000000
+#define MAC_STATUS_TXSTAT_OVERRUN 0x08000000
+#define MAC_EVENT 0x00000408
+#define MAC_EVENT_PORT_DECODE_ERR 0x00000400
+#define MAC_EVENT_LNKSTATE_CHANGED 0x00001000
+#define MAC_EVENT_MI_COMPLETION 0x00400000
+#define MAC_EVENT_MI_INTERRUPT 0x00800000
+#define MAC_EVENT_AP_ERROR 0x01000000
+#define MAC_EVENT_ODI_ERROR 0x02000000
+#define MAC_EVENT_RXSTAT_OVERRUN 0x04000000
+#define MAC_EVENT_TXSTAT_OVERRUN 0x08000000
+#define MAC_LED_CTRL 0x0000040c
+#define LED_CTRL_LNKLED_OVERRIDE 0x00000001
+#define LED_CTRL_1000MBPS_ON 0x00000002
+#define LED_CTRL_100MBPS_ON 0x00000004
+#define LED_CTRL_10MBPS_ON 0x00000008
+#define LED_CTRL_TRAFFIC_OVERRIDE 0x00000010
+#define LED_CTRL_TRAFFIC_BLINK 0x00000020
+#define LED_CTRL_TRAFFIC_LED 0x00000040
+#define LED_CTRL_1000MBPS_STATUS 0x00000080
+#define LED_CTRL_100MBPS_STATUS 0x00000100
+#define LED_CTRL_10MBPS_STATUS 0x00000200
+#define LED_CTRL_TRAFFIC_STATUS 0x00000400
+#define LED_CTRL_MAC_MODE 0x00000000
+#define LED_CTRL_PHY_MODE_1 0x00000800
+#define LED_CTRL_PHY_MODE_2 0x00001000
+#define LED_CTRL_BLINK_RATE_MASK 0x7ff80000
+#define LED_CTRL_BLINK_RATE_SHIFT 19
+#define LED_CTRL_BLINK_PER_OVERRIDE 0x00080000
+#define LED_CTRL_BLINK_RATE_OVERRIDE 0x80000000
+#define MAC_ADDR_0_HIGH 0x00000410 /* upper 2 bytes */
+#define MAC_ADDR_0_LOW 0x00000414 /* lower 4 bytes */
+#define MAC_ADDR_1_HIGH 0x00000418 /* upper 2 bytes */
+#define MAC_ADDR_1_LOW 0x0000041c /* lower 4 bytes */
+#define MAC_ADDR_2_HIGH 0x00000420 /* upper 2 bytes */
+#define MAC_ADDR_2_LOW 0x00000424 /* lower 4 bytes */
+#define MAC_ADDR_3_HIGH 0x00000428 /* upper 2 bytes */
+#define MAC_ADDR_3_LOW 0x0000042c /* lower 4 bytes */
+#define MAC_ACPI_MBUF_PTR 0x00000430
+#define MAC_ACPI_LEN_OFFSET 0x00000434
+#define ACPI_LENOFF_LEN_MASK 0x0000ffff
+#define ACPI_LENOFF_LEN_SHIFT 0
+#define ACPI_LENOFF_OFF_MASK 0x0fff0000
+#define ACPI_LENOFF_OFF_SHIFT 16
+#define MAC_TX_BACKOFF_SEED 0x00000438
+#define TX_BACKOFF_SEED_MASK 0x000003ff
+#define MAC_RX_MTU_SIZE 0x0000043c
+#define RX_MTU_SIZE_MASK 0x0000ffff
+#define MAC_PCS_TEST 0x00000440
+#define PCS_TEST_PATTERN_MASK 0x000fffff
+#define PCS_TEST_PATTERN_SHIFT 0
+#define PCS_TEST_ENABLE 0x00100000
+#define MAC_TX_AUTO_NEG 0x00000444
+#define TX_AUTO_NEG_MASK 0x0000ffff
+#define TX_AUTO_NEG_SHIFT 0
+#define MAC_RX_AUTO_NEG 0x00000448
+#define RX_AUTO_NEG_MASK 0x0000ffff
+#define RX_AUTO_NEG_SHIFT 0
+#define MAC_MI_COM 0x0000044c
+#define MI_COM_CMD_MASK 0x0c000000
+#define MI_COM_CMD_WRITE 0x04000000
+#define MI_COM_CMD_READ 0x08000000
+#define MI_COM_READ_FAILED 0x10000000
+#define MI_COM_START 0x20000000
+#define MI_COM_BUSY 0x20000000
+#define MI_COM_PHY_ADDR_MASK 0x03e00000
+#define MI_COM_PHY_ADDR_SHIFT 21
+#define MI_COM_REG_ADDR_MASK 0x001f0000
+#define MI_COM_REG_ADDR_SHIFT 16
+#define MI_COM_DATA_MASK 0x0000ffff
+#define MAC_MI_STAT 0x00000450
+#define MAC_MI_STAT_LNKSTAT_ATTN_ENAB 0x00000001
+#define MAC_MI_MODE 0x00000454
+#define MAC_MI_MODE_CLK_10MHZ 0x00000001
+#define MAC_MI_MODE_SHORT_PREAMBLE 0x00000002
+#define MAC_MI_MODE_AUTO_POLL 0x00000010
+#define MAC_MI_MODE_CORE_CLK_62MHZ 0x00008000
+#define MAC_MI_MODE_BASE 0x000c0000 /* XXX magic values XXX */
+#define MAC_AUTO_POLL_STATUS 0x00000458
+#define MAC_AUTO_POLL_ERROR 0x00000001
+#define MAC_TX_MODE 0x0000045c
+#define TX_MODE_RESET 0x00000001
+#define TX_MODE_ENABLE 0x00000002
+#define TX_MODE_FLOW_CTRL_ENABLE 0x00000010
+#define TX_MODE_BIG_BCKOFF_ENABLE 0x00000020
+#define TX_MODE_LONG_PAUSE_ENABLE 0x00000040
+#define MAC_TX_STATUS 0x00000460
+#define TX_STATUS_XOFFED 0x00000001
+#define TX_STATUS_SENT_XOFF 0x00000002
+#define TX_STATUS_SENT_XON 0x00000004
+#define TX_STATUS_LINK_UP 0x00000008
+#define TX_STATUS_ODI_UNDERRUN 0x00000010
+#define TX_STATUS_ODI_OVERRUN 0x00000020
+#define MAC_TX_LENGTHS 0x00000464
+#define TX_LENGTHS_SLOT_TIME_MASK 0x000000ff
+#define TX_LENGTHS_SLOT_TIME_SHIFT 0
+#define TX_LENGTHS_IPG_MASK 0x00000f00
+#define TX_LENGTHS_IPG_SHIFT 8
+#define TX_LENGTHS_IPG_CRS_MASK 0x00003000
+#define TX_LENGTHS_IPG_CRS_SHIFT 12
+#define MAC_RX_MODE 0x00000468
+#define RX_MODE_RESET 0x00000001
+#define RX_MODE_ENABLE 0x00000002
+#define RX_MODE_FLOW_CTRL_ENABLE 0x00000004
+#define RX_MODE_KEEP_MAC_CTRL 0x00000008
+#define RX_MODE_KEEP_PAUSE 0x00000010
+#define RX_MODE_ACCEPT_OVERSIZED 0x00000020
+#define RX_MODE_ACCEPT_RUNTS 0x00000040
+#define RX_MODE_LEN_CHECK 0x00000080
+#define RX_MODE_PROMISC 0x00000100
+#define RX_MODE_NO_CRC_CHECK 0x00000200
+#define RX_MODE_KEEP_VLAN_TAG 0x00000400
+#define MAC_RX_STATUS 0x0000046c
+#define RX_STATUS_REMOTE_TX_XOFFED 0x00000001
+#define RX_STATUS_XOFF_RCVD 0x00000002
+#define RX_STATUS_XON_RCVD 0x00000004
+#define MAC_HASH_REG_0 0x00000470
+#define MAC_HASH_REG_1 0x00000474
+#define MAC_HASH_REG_2 0x00000478
+#define MAC_HASH_REG_3 0x0000047c
+#define MAC_RCV_RULE_0 0x00000480
+#define MAC_RCV_VALUE_0 0x00000484
+#define MAC_RCV_RULE_1 0x00000488
+#define MAC_RCV_VALUE_1 0x0000048c
+#define MAC_RCV_RULE_2 0x00000490
+#define MAC_RCV_VALUE_2 0x00000494
+#define MAC_RCV_RULE_3 0x00000498
+#define MAC_RCV_VALUE_3 0x0000049c
+#define MAC_RCV_RULE_4 0x000004a0
+#define MAC_RCV_VALUE_4 0x000004a4
+#define MAC_RCV_RULE_5 0x000004a8
+#define MAC_RCV_VALUE_5 0x000004ac
+#define MAC_RCV_RULE_6 0x000004b0
+#define MAC_RCV_VALUE_6 0x000004b4
+#define MAC_RCV_RULE_7 0x000004b8
+#define MAC_RCV_VALUE_7 0x000004bc
+#define MAC_RCV_RULE_8 0x000004c0
+#define MAC_RCV_VALUE_8 0x000004c4
+#define MAC_RCV_RULE_9 0x000004c8
+#define MAC_RCV_VALUE_9 0x000004cc
+#define MAC_RCV_RULE_10 0x000004d0
+#define MAC_RCV_VALUE_10 0x000004d4
+#define MAC_RCV_RULE_11 0x000004d8
+#define MAC_RCV_VALUE_11 0x000004dc
+#define MAC_RCV_RULE_12 0x000004e0
+#define MAC_RCV_VALUE_12 0x000004e4
+#define MAC_RCV_RULE_13 0x000004e8
+#define MAC_RCV_VALUE_13 0x000004ec
+#define MAC_RCV_RULE_14 0x000004f0
+#define MAC_RCV_VALUE_14 0x000004f4
+#define MAC_RCV_RULE_15 0x000004f8
+#define MAC_RCV_VALUE_15 0x000004fc
+#define RCV_RULE_DISABLE_MASK 0x7fffffff
+#define MAC_RCV_RULE_CFG 0x00000500
+#define RCV_RULE_CFG_DEFAULT_CLASS 0x00000008
+#define MAC_LOW_WMARK_MAX_RX_FRAME 0x00000504
+/* 0x508 --> 0x520 unused */
+#define MAC_HASHREGU_0 0x00000520
+#define MAC_HASHREGU_1 0x00000524
+#define MAC_HASHREGU_2 0x00000528
+#define MAC_HASHREGU_3 0x0000052c
+#define MAC_EXTADDR_0_HIGH 0x00000530
+#define MAC_EXTADDR_0_LOW 0x00000534
+#define MAC_EXTADDR_1_HIGH 0x00000538
+#define MAC_EXTADDR_1_LOW 0x0000053c
+#define MAC_EXTADDR_2_HIGH 0x00000540
+#define MAC_EXTADDR_2_LOW 0x00000544
+#define MAC_EXTADDR_3_HIGH 0x00000548
+#define MAC_EXTADDR_3_LOW 0x0000054c
+#define MAC_EXTADDR_4_HIGH 0x00000550
+#define MAC_EXTADDR_4_LOW 0x00000554
+#define MAC_EXTADDR_5_HIGH 0x00000558
+#define MAC_EXTADDR_5_LOW 0x0000055c
+#define MAC_EXTADDR_6_HIGH 0x00000560
+#define MAC_EXTADDR_6_LOW 0x00000564
+#define MAC_EXTADDR_7_HIGH 0x00000568
+#define MAC_EXTADDR_7_LOW 0x0000056c
+#define MAC_EXTADDR_8_HIGH 0x00000570
+#define MAC_EXTADDR_8_LOW 0x00000574
+#define MAC_EXTADDR_9_HIGH 0x00000578
+#define MAC_EXTADDR_9_LOW 0x0000057c
+#define MAC_EXTADDR_10_HIGH 0x00000580
+#define MAC_EXTADDR_10_LOW 0x00000584
+#define MAC_EXTADDR_11_HIGH 0x00000588
+#define MAC_EXTADDR_11_LOW 0x0000058c
+#define MAC_SERDES_CFG 0x00000590
+#define MAC_SERDES_STAT 0x00000594
+/* 0x598 --> 0x600 unused */
+#define MAC_TX_MAC_STATE_BASE 0x00000600 /* 16 bytes */
+#define MAC_RX_MAC_STATE_BASE 0x00000610 /* 20 bytes */
+/* 0x624 --> 0x800 unused */
+#define MAC_TX_STATS_OCTETS 0x00000800
+#define MAC_TX_STATS_RESV1 0x00000804
+#define MAC_TX_STATS_COLLISIONS 0x00000808
+#define MAC_TX_STATS_XON_SENT 0x0000080c
+#define MAC_TX_STATS_XOFF_SENT 0x00000810
+#define MAC_TX_STATS_RESV2 0x00000814
+#define MAC_TX_STATS_MAC_ERRORS 0x00000818
+#define MAC_TX_STATS_SINGLE_COLLISIONS 0x0000081c
+#define MAC_TX_STATS_MULT_COLLISIONS 0x00000820
+#define MAC_TX_STATS_DEFERRED 0x00000824
+#define MAC_TX_STATS_RESV3 0x00000828
+#define MAC_TX_STATS_EXCESSIVE_COL 0x0000082c
+#define MAC_TX_STATS_LATE_COL 0x00000830
+#define MAC_TX_STATS_RESV4_1 0x00000834
+#define MAC_TX_STATS_RESV4_2 0x00000838
+#define MAC_TX_STATS_RESV4_3 0x0000083c
+#define MAC_TX_STATS_RESV4_4 0x00000840
+#define MAC_TX_STATS_RESV4_5 0x00000844
+#define MAC_TX_STATS_RESV4_6 0x00000848
+#define MAC_TX_STATS_RESV4_7 0x0000084c
+#define MAC_TX_STATS_RESV4_8 0x00000850
+#define MAC_TX_STATS_RESV4_9 0x00000854
+#define MAC_TX_STATS_RESV4_10 0x00000858
+#define MAC_TX_STATS_RESV4_11 0x0000085c
+#define MAC_TX_STATS_RESV4_12 0x00000860
+#define MAC_TX_STATS_RESV4_13 0x00000864
+#define MAC_TX_STATS_RESV4_14 0x00000868
+#define MAC_TX_STATS_UCAST 0x0000086c
+#define MAC_TX_STATS_MCAST 0x00000870
+#define MAC_TX_STATS_BCAST 0x00000874
+#define MAC_TX_STATS_RESV5_1 0x00000878
+#define MAC_TX_STATS_RESV5_2 0x0000087c
+#define MAC_RX_STATS_OCTETS 0x00000880
+#define MAC_RX_STATS_RESV1 0x00000884
+#define MAC_RX_STATS_FRAGMENTS 0x00000888
+#define MAC_RX_STATS_UCAST 0x0000088c
+#define MAC_RX_STATS_MCAST 0x00000890
+#define MAC_RX_STATS_BCAST 0x00000894
+#define MAC_RX_STATS_FCS_ERRORS 0x00000898
+#define MAC_RX_STATS_ALIGN_ERRORS 0x0000089c
+#define MAC_RX_STATS_XON_PAUSE_RECVD 0x000008a0
+#define MAC_RX_STATS_XOFF_PAUSE_RECVD 0x000008a4
+#define MAC_RX_STATS_MAC_CTRL_RECVD 0x000008a8
+#define MAC_RX_STATS_XOFF_ENTERED 0x000008ac
+#define MAC_RX_STATS_FRAME_TOO_LONG 0x000008b0
+#define MAC_RX_STATS_JABBERS 0x000008b4
+#define MAC_RX_STATS_UNDERSIZE 0x000008b8
+/* 0x8bc --> 0xc00 unused */
+
+/* Send data initiator control registers */
+#define SNDDATAI_MODE 0x00000c00
+#define SNDDATAI_MODE_RESET 0x00000001
+#define SNDDATAI_MODE_ENABLE 0x00000002
+#define SNDDATAI_MODE_STAT_OFLOW_ENAB 0x00000004
+#define SNDDATAI_STATUS 0x00000c04
+#define SNDDATAI_STATUS_STAT_OFLOW 0x00000004
+#define SNDDATAI_STATSCTRL 0x00000c08
+#define SNDDATAI_SCTRL_ENABLE 0x00000001
+#define SNDDATAI_SCTRL_FASTUPD 0x00000002
+#define SNDDATAI_SCTRL_CLEAR 0x00000004
+#define SNDDATAI_SCTRL_FLUSH 0x00000008
+#define SNDDATAI_SCTRL_FORCE_ZERO 0x00000010
+#define SNDDATAI_STATSENAB 0x00000c0c
+#define SNDDATAI_STATSINCMASK 0x00000c10
+/* 0xc14 --> 0xc80 unused */
+#define SNDDATAI_COS_CNT_0 0x00000c80
+#define SNDDATAI_COS_CNT_1 0x00000c84
+#define SNDDATAI_COS_CNT_2 0x00000c88
+#define SNDDATAI_COS_CNT_3 0x00000c8c
+#define SNDDATAI_COS_CNT_4 0x00000c90
+#define SNDDATAI_COS_CNT_5 0x00000c94
+#define SNDDATAI_COS_CNT_6 0x00000c98
+#define SNDDATAI_COS_CNT_7 0x00000c9c
+#define SNDDATAI_COS_CNT_8 0x00000ca0
+#define SNDDATAI_COS_CNT_9 0x00000ca4
+#define SNDDATAI_COS_CNT_10 0x00000ca8
+#define SNDDATAI_COS_CNT_11 0x00000cac
+#define SNDDATAI_COS_CNT_12 0x00000cb0
+#define SNDDATAI_COS_CNT_13 0x00000cb4
+#define SNDDATAI_COS_CNT_14 0x00000cb8
+#define SNDDATAI_COS_CNT_15 0x00000cbc
+#define SNDDATAI_DMA_RDQ_FULL_CNT 0x00000cc0
+#define SNDDATAI_DMA_PRIO_RDQ_FULL_CNT 0x00000cc4
+#define SNDDATAI_SDCQ_FULL_CNT 0x00000cc8
+#define SNDDATAI_NICRNG_SSND_PIDX_CNT 0x00000ccc
+#define SNDDATAI_STATS_UPDATED_CNT 0x00000cd0
+#define SNDDATAI_INTERRUPTS_CNT 0x00000cd4
+#define SNDDATAI_AVOID_INTERRUPTS_CNT 0x00000cd8
+#define SNDDATAI_SND_THRESH_HIT_CNT 0x00000cdc
+/* 0xce0 --> 0x1000 unused */
+
+/* Send data completion control registers */
+#define SNDDATAC_MODE 0x00001000
+#define SNDDATAC_MODE_RESET 0x00000001
+#define SNDDATAC_MODE_ENABLE 0x00000002
+/* 0x1004 --> 0x1400 unused */
+
+/* Send BD ring selector */
+#define SNDBDS_MODE 0x00001400
+#define SNDBDS_MODE_RESET 0x00000001
+#define SNDBDS_MODE_ENABLE 0x00000002
+#define SNDBDS_MODE_ATTN_ENABLE 0x00000004
+#define SNDBDS_STATUS 0x00001404
+#define SNDBDS_STATUS_ERROR_ATTN 0x00000004
+#define SNDBDS_HWDIAG 0x00001408
+/* 0x140c --> 0x1440 */
+#define SNDBDS_SEL_CON_IDX_0 0x00001440
+#define SNDBDS_SEL_CON_IDX_1 0x00001444
+#define SNDBDS_SEL_CON_IDX_2 0x00001448
+#define SNDBDS_SEL_CON_IDX_3 0x0000144c
+#define SNDBDS_SEL_CON_IDX_4 0x00001450
+#define SNDBDS_SEL_CON_IDX_5 0x00001454
+#define SNDBDS_SEL_CON_IDX_6 0x00001458
+#define SNDBDS_SEL_CON_IDX_7 0x0000145c
+#define SNDBDS_SEL_CON_IDX_8 0x00001460
+#define SNDBDS_SEL_CON_IDX_9 0x00001464
+#define SNDBDS_SEL_CON_IDX_10 0x00001468
+#define SNDBDS_SEL_CON_IDX_11 0x0000146c
+#define SNDBDS_SEL_CON_IDX_12 0x00001470
+#define SNDBDS_SEL_CON_IDX_13 0x00001474
+#define SNDBDS_SEL_CON_IDX_14 0x00001478
+#define SNDBDS_SEL_CON_IDX_15 0x0000147c
+/* 0x1480 --> 0x1800 unused */
+
+/* Send BD initiator control registers */
+#define SNDBDI_MODE 0x00001800
+#define SNDBDI_MODE_RESET 0x00000001
+#define SNDBDI_MODE_ENABLE 0x00000002
+#define SNDBDI_MODE_ATTN_ENABLE 0x00000004
+#define SNDBDI_STATUS 0x00001804
+#define SNDBDI_STATUS_ERROR_ATTN 0x00000004
+#define SNDBDI_IN_PROD_IDX_0 0x00001808
+#define SNDBDI_IN_PROD_IDX_1 0x0000180c
+#define SNDBDI_IN_PROD_IDX_2 0x00001810
+#define SNDBDI_IN_PROD_IDX_3 0x00001814
+#define SNDBDI_IN_PROD_IDX_4 0x00001818
+#define SNDBDI_IN_PROD_IDX_5 0x0000181c
+#define SNDBDI_IN_PROD_IDX_6 0x00001820
+#define SNDBDI_IN_PROD_IDX_7 0x00001824
+#define SNDBDI_IN_PROD_IDX_8 0x00001828
+#define SNDBDI_IN_PROD_IDX_9 0x0000182c
+#define SNDBDI_IN_PROD_IDX_10 0x00001830
+#define SNDBDI_IN_PROD_IDX_11 0x00001834
+#define SNDBDI_IN_PROD_IDX_12 0x00001838
+#define SNDBDI_IN_PROD_IDX_13 0x0000183c
+#define SNDBDI_IN_PROD_IDX_14 0x00001840
+#define SNDBDI_IN_PROD_IDX_15 0x00001844
+/* 0x1848 --> 0x1c00 unused */
+
+/* Send BD completion control registers */
+#define SNDBDC_MODE 0x00001c00
+#define SNDBDC_MODE_RESET 0x00000001
+#define SNDBDC_MODE_ENABLE 0x00000002
+#define SNDBDC_MODE_ATTN_ENABLE 0x00000004
+/* 0x1c04 --> 0x2000 unused */
+
+/* Receive list placement control registers */
+#define RCVLPC_MODE 0x00002000
+#define RCVLPC_MODE_RESET 0x00000001
+#define RCVLPC_MODE_ENABLE 0x00000002
+#define RCVLPC_MODE_CLASS0_ATTN_ENAB 0x00000004
+#define RCVLPC_MODE_MAPOOR_AATTN_ENAB 0x00000008
+#define RCVLPC_MODE_STAT_OFLOW_ENAB 0x00000010
+#define RCVLPC_STATUS 0x00002004
+#define RCVLPC_STATUS_CLASS0 0x00000004
+#define RCVLPC_STATUS_MAPOOR 0x00000008
+#define RCVLPC_STATUS_STAT_OFLOW 0x00000010
+#define RCVLPC_LOCK 0x00002008
+#define RCVLPC_LOCK_REQ_MASK 0x0000ffff
+#define RCVLPC_LOCK_REQ_SHIFT 0
+#define RCVLPC_LOCK_GRANT_MASK 0xffff0000
+#define RCVLPC_LOCK_GRANT_SHIFT 16
+#define RCVLPC_NON_EMPTY_BITS 0x0000200c
+#define RCVLPC_NON_EMPTY_BITS_MASK 0x0000ffff
+#define RCVLPC_CONFIG 0x00002010
+#define RCVLPC_STATSCTRL 0x00002014
+#define RCVLPC_STATSCTRL_ENABLE 0x00000001
+#define RCVLPC_STATSCTRL_FASTUPD 0x00000002
+#define RCVLPC_STATS_ENABLE 0x00002018
+#define RCVLPC_STATSENAB_LNGBRST_RFIX 0x00400000
+#define RCVLPC_STATS_INCMASK 0x0000201c
+/* 0x2020 --> 0x2100 unused */
+#define RCVLPC_SELLST_BASE 0x00002100 /* 16 16-byte entries */
+#define SELLST_TAIL 0x00000004
+#define SELLST_CONT 0x00000008
+#define SELLST_UNUSED 0x0000000c
+#define RCVLPC_COS_CNTL_BASE 0x00002200 /* 16 4-byte entries */
+#define RCVLPC_DROP_FILTER_CNT 0x00002240
+#define RCVLPC_DMA_WQ_FULL_CNT 0x00002244
+#define RCVLPC_DMA_HIPRIO_WQ_FULL_CNT 0x00002248
+#define RCVLPC_NO_RCV_BD_CNT 0x0000224c
+#define RCVLPC_IN_DISCARDS_CNT 0x00002250
+#define RCVLPC_IN_ERRORS_CNT 0x00002254
+#define RCVLPC_RCV_THRESH_HIT_CNT 0x00002258
+/* 0x225c --> 0x2400 unused */
+
+/* Receive Data and Receive BD Initiator Control */
+#define RCVDBDI_MODE 0x00002400
+#define RCVDBDI_MODE_RESET 0x00000001
+#define RCVDBDI_MODE_ENABLE 0x00000002
+#define RCVDBDI_MODE_JUMBOBD_NEEDED 0x00000004
+#define RCVDBDI_MODE_FRM_TOO_BIG 0x00000008
+#define RCVDBDI_MODE_INV_RING_SZ 0x00000010
+#define RCVDBDI_STATUS 0x00002404
+#define RCVDBDI_STATUS_JUMBOBD_NEEDED 0x00000004
+#define RCVDBDI_STATUS_FRM_TOO_BIG 0x00000008
+#define RCVDBDI_STATUS_INV_RING_SZ 0x00000010
+#define RCVDBDI_SPLIT_FRAME_MINSZ 0x00002408
+/* 0x240c --> 0x2440 unused */
+#define RCVDBDI_JUMBO_BD 0x00002440 /* TG3_BDINFO_... */
+#define RCVDBDI_STD_BD 0x00002450 /* TG3_BDINFO_... */
+#define RCVDBDI_MINI_BD 0x00002460 /* TG3_BDINFO_... */
+#define RCVDBDI_JUMBO_CON_IDX 0x00002470
+#define RCVDBDI_STD_CON_IDX 0x00002474
+#define RCVDBDI_MINI_CON_IDX 0x00002478
+/* 0x247c --> 0x2480 unused */
+#define RCVDBDI_BD_PROD_IDX_0 0x00002480
+#define RCVDBDI_BD_PROD_IDX_1 0x00002484
+#define RCVDBDI_BD_PROD_IDX_2 0x00002488
+#define RCVDBDI_BD_PROD_IDX_3 0x0000248c
+#define RCVDBDI_BD_PROD_IDX_4 0x00002490
+#define RCVDBDI_BD_PROD_IDX_5 0x00002494
+#define RCVDBDI_BD_PROD_IDX_6 0x00002498
+#define RCVDBDI_BD_PROD_IDX_7 0x0000249c
+#define RCVDBDI_BD_PROD_IDX_8 0x000024a0
+#define RCVDBDI_BD_PROD_IDX_9 0x000024a4
+#define RCVDBDI_BD_PROD_IDX_10 0x000024a8
+#define RCVDBDI_BD_PROD_IDX_11 0x000024ac
+#define RCVDBDI_BD_PROD_IDX_12 0x000024b0
+#define RCVDBDI_BD_PROD_IDX_13 0x000024b4
+#define RCVDBDI_BD_PROD_IDX_14 0x000024b8
+#define RCVDBDI_BD_PROD_IDX_15 0x000024bc
+#define RCVDBDI_HWDIAG 0x000024c0
+/* 0x24c4 --> 0x2800 unused */
+
+/* Receive Data Completion Control */
+#define RCVDCC_MODE 0x00002800
+#define RCVDCC_MODE_RESET 0x00000001
+#define RCVDCC_MODE_ENABLE 0x00000002
+#define RCVDCC_MODE_ATTN_ENABLE 0x00000004
+/* 0x2804 --> 0x2c00 unused */
+
+/* Receive BD Initiator Control Registers */
+#define RCVBDI_MODE 0x00002c00
+#define RCVBDI_MODE_RESET 0x00000001
+#define RCVBDI_MODE_ENABLE 0x00000002
+#define RCVBDI_MODE_RCB_ATTN_ENAB 0x00000004
+#define RCVBDI_STATUS 0x00002c04
+#define RCVBDI_STATUS_RCB_ATTN 0x00000004
+#define RCVBDI_JUMBO_PROD_IDX 0x00002c08
+#define RCVBDI_STD_PROD_IDX 0x00002c0c
+#define RCVBDI_MINI_PROD_IDX 0x00002c10
+#define RCVBDI_MINI_THRESH 0x00002c14
+#define RCVBDI_STD_THRESH 0x00002c18
+#define RCVBDI_JUMBO_THRESH 0x00002c1c
+/* 0x2c20 --> 0x3000 unused */
+
+/* Receive BD Completion Control Registers */
+#define RCVCC_MODE 0x00003000
+#define RCVCC_MODE_RESET 0x00000001
+#define RCVCC_MODE_ENABLE 0x00000002
+#define RCVCC_MODE_ATTN_ENABLE 0x00000004
+#define RCVCC_STATUS 0x00003004
+#define RCVCC_STATUS_ERROR_ATTN 0x00000004
+#define RCVCC_JUMP_PROD_IDX 0x00003008
+#define RCVCC_STD_PROD_IDX 0x0000300c
+#define RCVCC_MINI_PROD_IDX 0x00003010
+/* 0x3014 --> 0x3400 unused */
+
+/* Receive list selector control registers */
+#define RCVLSC_MODE 0x00003400
+#define RCVLSC_MODE_RESET 0x00000001
+#define RCVLSC_MODE_ENABLE 0x00000002
+#define RCVLSC_MODE_ATTN_ENABLE 0x00000004
+#define RCVLSC_STATUS 0x00003404
+#define RCVLSC_STATUS_ERROR_ATTN 0x00000004
+/* 0x3408 --> 0x3800 unused */
+
+/* Mbuf cluster free registers */
+#define MBFREE_MODE 0x00003800
+#define MBFREE_MODE_RESET 0x00000001
+#define MBFREE_MODE_ENABLE 0x00000002
+#define MBFREE_STATUS 0x00003804
+/* 0x3808 --> 0x3c00 unused */
+
+/* Host coalescing control registers */
+#define HOSTCC_MODE 0x00003c00
+#define HOSTCC_MODE_RESET 0x00000001
+#define HOSTCC_MODE_ENABLE 0x00000002
+#define HOSTCC_MODE_ATTN 0x00000004
+#define HOSTCC_MODE_NOW 0x00000008
+#define HOSTCC_MODE_FULL_STATUS 0x00000000
+#define HOSTCC_MODE_64BYTE 0x00000080
+#define HOSTCC_MODE_32BYTE 0x00000100
+#define HOSTCC_MODE_CLRTICK_RXBD 0x00000200
+#define HOSTCC_MODE_CLRTICK_TXBD 0x00000400
+#define HOSTCC_MODE_NOINT_ON_NOW 0x00000800
+#define HOSTCC_MODE_NOINT_ON_FORCE 0x00001000
+#define HOSTCC_STATUS 0x00003c04
+#define HOSTCC_STATUS_ERROR_ATTN 0x00000004
+#define HOSTCC_RXCOL_TICKS 0x00003c08
+#define LOW_RXCOL_TICKS 0x00000032
+#define DEFAULT_RXCOL_TICKS 0x00000048
+#define HIGH_RXCOL_TICKS 0x00000096
+#define HOSTCC_TXCOL_TICKS 0x00003c0c
+#define LOW_TXCOL_TICKS 0x00000096
+#define DEFAULT_TXCOL_TICKS 0x0000012c
+#define HIGH_TXCOL_TICKS 0x00000145
+#define HOSTCC_RXMAX_FRAMES 0x00003c10
+#define LOW_RXMAX_FRAMES 0x00000005
+#define DEFAULT_RXMAX_FRAMES 0x00000008
+#define HIGH_RXMAX_FRAMES 0x00000012
+#define HOSTCC_TXMAX_FRAMES 0x00003c14
+#define LOW_TXMAX_FRAMES 0x00000035
+#define DEFAULT_TXMAX_FRAMES 0x0000004b
+#define HIGH_TXMAX_FRAMES 0x00000052
+#define HOSTCC_RXCOAL_TICK_INT 0x00003c18
+#define DEFAULT_RXCOAL_TICK_INT 0x00000019
+#define HOSTCC_TXCOAL_TICK_INT 0x00003c1c
+#define DEFAULT_TXCOAL_TICK_INT 0x00000019
+#define HOSTCC_RXCOAL_MAXF_INT 0x00003c20
+#define DEFAULT_RXCOAL_MAXF_INT 0x00000005
+#define HOSTCC_TXCOAL_MAXF_INT 0x00003c24
+#define DEFAULT_TXCOAL_MAXF_INT 0x00000005
+#define HOSTCC_STAT_COAL_TICKS 0x00003c28
+#define DEFAULT_STAT_COAL_TICKS 0x000f4240
+/* 0x3c2c --> 0x3c30 unused */
+#define HOSTCC_STATS_BLK_HOST_ADDR 0x00003c30 /* 64-bit */
+#define HOSTCC_STATUS_BLK_HOST_ADDR 0x00003c38 /* 64-bit */
+#define HOSTCC_STATS_BLK_NIC_ADDR 0x00003c40
+#define HOSTCC_STATUS_BLK_NIC_ADDR 0x00003c44
+#define HOSTCC_FLOW_ATTN 0x00003c48
+/* 0x3c4c --> 0x3c50 unused */
+#define HOSTCC_JUMBO_CON_IDX 0x00003c50
+#define HOSTCC_STD_CON_IDX 0x00003c54
+#define HOSTCC_MINI_CON_IDX 0x00003c58
+/* 0x3c5c --> 0x3c80 unused */
+#define HOSTCC_RET_PROD_IDX_0 0x00003c80
+#define HOSTCC_RET_PROD_IDX_1 0x00003c84
+#define HOSTCC_RET_PROD_IDX_2 0x00003c88
+#define HOSTCC_RET_PROD_IDX_3 0x00003c8c
+#define HOSTCC_RET_PROD_IDX_4 0x00003c90
+#define HOSTCC_RET_PROD_IDX_5 0x00003c94
+#define HOSTCC_RET_PROD_IDX_6 0x00003c98
+#define HOSTCC_RET_PROD_IDX_7 0x00003c9c
+#define HOSTCC_RET_PROD_IDX_8 0x00003ca0
+#define HOSTCC_RET_PROD_IDX_9 0x00003ca4
+#define HOSTCC_RET_PROD_IDX_10 0x00003ca8
+#define HOSTCC_RET_PROD_IDX_11 0x00003cac
+#define HOSTCC_RET_PROD_IDX_12 0x00003cb0
+#define HOSTCC_RET_PROD_IDX_13 0x00003cb4
+#define HOSTCC_RET_PROD_IDX_14 0x00003cb8
+#define HOSTCC_RET_PROD_IDX_15 0x00003cbc
+#define HOSTCC_SND_CON_IDX_0 0x00003cc0
+#define HOSTCC_SND_CON_IDX_1 0x00003cc4
+#define HOSTCC_SND_CON_IDX_2 0x00003cc8
+#define HOSTCC_SND_CON_IDX_3 0x00003ccc
+#define HOSTCC_SND_CON_IDX_4 0x00003cd0
+#define HOSTCC_SND_CON_IDX_5 0x00003cd4
+#define HOSTCC_SND_CON_IDX_6 0x00003cd8
+#define HOSTCC_SND_CON_IDX_7 0x00003cdc
+#define HOSTCC_SND_CON_IDX_8 0x00003ce0
+#define HOSTCC_SND_CON_IDX_9 0x00003ce4
+#define HOSTCC_SND_CON_IDX_10 0x00003ce8
+#define HOSTCC_SND_CON_IDX_11 0x00003cec
+#define HOSTCC_SND_CON_IDX_12 0x00003cf0
+#define HOSTCC_SND_CON_IDX_13 0x00003cf4
+#define HOSTCC_SND_CON_IDX_14 0x00003cf8
+#define HOSTCC_SND_CON_IDX_15 0x00003cfc
+/* 0x3d00 --> 0x4000 unused */
+
+/* Memory arbiter control registers */
+#define MEMARB_MODE 0x00004000
+#define MEMARB_MODE_RESET 0x00000001
+#define MEMARB_MODE_ENABLE 0x00000002
+#define MEMARB_STATUS 0x00004004
+#define MEMARB_TRAP_ADDR_LOW 0x00004008
+#define MEMARB_TRAP_ADDR_HIGH 0x0000400c
+/* 0x4010 --> 0x4400 unused */
+
+/* Buffer manager control registers */
+#define BUFMGR_MODE 0x00004400
+#define BUFMGR_MODE_RESET 0x00000001
+#define BUFMGR_MODE_ENABLE 0x00000002
+#define BUFMGR_MODE_ATTN_ENABLE 0x00000004
+#define BUFMGR_MODE_BM_TEST 0x00000008
+#define BUFMGR_MODE_MBLOW_ATTN_ENAB 0x00000010
+#define BUFMGR_STATUS 0x00004404
+#define BUFMGR_STATUS_ERROR 0x00000004
+#define BUFMGR_STATUS_MBLOW 0x00000010
+#define BUFMGR_MB_POOL_ADDR 0x00004408
+#define BUFMGR_MB_POOL_SIZE 0x0000440c
+#define BUFMGR_MB_RDMA_LOW_WATER 0x00004410
+#define DEFAULT_MB_RDMA_LOW_WATER 0x00000050
+#define DEFAULT_MB_RDMA_LOW_WATER_5705 0x00000000
+#define DEFAULT_MB_RDMA_LOW_WATER_JUMBO 0x00000130
+#define BUFMGR_MB_MACRX_LOW_WATER 0x00004414
+#define DEFAULT_MB_MACRX_LOW_WATER 0x00000020
+#define DEFAULT_MB_MACRX_LOW_WATER_5705 0x00000010
+#define DEFAULT_MB_MACRX_LOW_WATER_JUMBO 0x00000098
+#define BUFMGR_MB_HIGH_WATER 0x00004418
+#define DEFAULT_MB_HIGH_WATER 0x00000060
+#define DEFAULT_MB_HIGH_WATER_5705 0x00000060
+#define DEFAULT_MB_HIGH_WATER_JUMBO 0x0000017c
+#define BUFMGR_RX_MB_ALLOC_REQ 0x0000441c
+#define BUFMGR_MB_ALLOC_BIT 0x10000000
+#define BUFMGR_RX_MB_ALLOC_RESP 0x00004420
+#define BUFMGR_TX_MB_ALLOC_REQ 0x00004424
+#define BUFMGR_TX_MB_ALLOC_RESP 0x00004428
+#define BUFMGR_DMA_DESC_POOL_ADDR 0x0000442c
+#define BUFMGR_DMA_DESC_POOL_SIZE 0x00004430
+#define BUFMGR_DMA_LOW_WATER 0x00004434
+#define DEFAULT_DMA_LOW_WATER 0x00000005
+#define BUFMGR_DMA_HIGH_WATER 0x00004438
+#define DEFAULT_DMA_HIGH_WATER 0x0000000a
+#define BUFMGR_RX_DMA_ALLOC_REQ 0x0000443c
+#define BUFMGR_RX_DMA_ALLOC_RESP 0x00004440
+#define BUFMGR_TX_DMA_ALLOC_REQ 0x00004444
+#define BUFMGR_TX_DMA_ALLOC_RESP 0x00004448
+#define BUFMGR_HWDIAG_0 0x0000444c
+#define BUFMGR_HWDIAG_1 0x00004450
+#define BUFMGR_HWDIAG_2 0x00004454
+/* 0x4458 --> 0x4800 unused */
+
+/* Read DMA control registers */
+#define RDMAC_MODE 0x00004800
+#define RDMAC_MODE_RESET 0x00000001
+#define RDMAC_MODE_ENABLE 0x00000002
+#define RDMAC_MODE_TGTABORT_ENAB 0x00000004
+#define RDMAC_MODE_MSTABORT_ENAB 0x00000008
+#define RDMAC_MODE_PARITYERR_ENAB 0x00000010
+#define RDMAC_MODE_ADDROFLOW_ENAB 0x00000020
+#define RDMAC_MODE_FIFOOFLOW_ENAB 0x00000040
+#define RDMAC_MODE_FIFOURUN_ENAB 0x00000080
+#define RDMAC_MODE_FIFOOREAD_ENAB 0x00000100
+#define RDMAC_MODE_LNGREAD_ENAB 0x00000200
+#define RDMAC_MODE_SPLIT_ENABLE 0x00000800
+#define RDMAC_MODE_SPLIT_RESET 0x00001000
+#define RDMAC_MODE_FIFO_SIZE_128 0x00020000
+#define RDMAC_MODE_FIFO_LONG_BURST 0x00030000
+#define RDMAC_STATUS 0x00004804
+#define RDMAC_STATUS_TGTABORT 0x00000004
+#define RDMAC_STATUS_MSTABORT 0x00000008
+#define RDMAC_STATUS_PARITYERR 0x00000010
+#define RDMAC_STATUS_ADDROFLOW 0x00000020
+#define RDMAC_STATUS_FIFOOFLOW 0x00000040
+#define RDMAC_STATUS_FIFOURUN 0x00000080
+#define RDMAC_STATUS_FIFOOREAD 0x00000100
+#define RDMAC_STATUS_LNGREAD 0x00000200
+/* 0x4808 --> 0x4c00 unused */
+
+/* Write DMA control registers */
+#define WDMAC_MODE 0x00004c00
+#define WDMAC_MODE_RESET 0x00000001
+#define WDMAC_MODE_ENABLE 0x00000002
+#define WDMAC_MODE_TGTABORT_ENAB 0x00000004
+#define WDMAC_MODE_MSTABORT_ENAB 0x00000008
+#define WDMAC_MODE_PARITYERR_ENAB 0x00000010
+#define WDMAC_MODE_ADDROFLOW_ENAB 0x00000020
+#define WDMAC_MODE_FIFOOFLOW_ENAB 0x00000040
+#define WDMAC_MODE_FIFOURUN_ENAB 0x00000080
+#define WDMAC_MODE_FIFOOREAD_ENAB 0x00000100
+#define WDMAC_MODE_LNGREAD_ENAB 0x00000200
+#define WDMAC_MODE_RX_ACCEL 0x00000400
+#define WDMAC_STATUS 0x00004c04
+#define WDMAC_STATUS_TGTABORT 0x00000004
+#define WDMAC_STATUS_MSTABORT 0x00000008
+#define WDMAC_STATUS_PARITYERR 0x00000010
+#define WDMAC_STATUS_ADDROFLOW 0x00000020
+#define WDMAC_STATUS_FIFOOFLOW 0x00000040
+#define WDMAC_STATUS_FIFOURUN 0x00000080
+#define WDMAC_STATUS_FIFOOREAD 0x00000100
+#define WDMAC_STATUS_LNGREAD 0x00000200
+/* 0x4c08 --> 0x5000 unused */
+
+/* Per-cpu register offsets (arm9) */
+#define CPU_MODE 0x00000000
+#define CPU_MODE_RESET 0x00000001
+#define CPU_MODE_HALT 0x00000400
+#define CPU_STATE 0x00000004
+#define CPU_EVTMASK 0x00000008
+/* 0xc --> 0x1c reserved */
+#define CPU_PC 0x0000001c
+#define CPU_INSN 0x00000020
+#define CPU_SPAD_UFLOW 0x00000024
+#define CPU_WDOG_CLEAR 0x00000028
+#define CPU_WDOG_VECTOR 0x0000002c
+#define CPU_WDOG_PC 0x00000030
+#define CPU_HW_BP 0x00000034
+/* 0x38 --> 0x44 unused */
+#define CPU_WDOG_SAVED_STATE 0x00000044
+#define CPU_LAST_BRANCH_ADDR 0x00000048
+#define CPU_SPAD_UFLOW_SET 0x0000004c
+/* 0x50 --> 0x200 unused */
+#define CPU_R0 0x00000200
+#define CPU_R1 0x00000204
+#define CPU_R2 0x00000208
+#define CPU_R3 0x0000020c
+#define CPU_R4 0x00000210
+#define CPU_R5 0x00000214
+#define CPU_R6 0x00000218
+#define CPU_R7 0x0000021c
+#define CPU_R8 0x00000220
+#define CPU_R9 0x00000224
+#define CPU_R10 0x00000228
+#define CPU_R11 0x0000022c
+#define CPU_R12 0x00000230
+#define CPU_R13 0x00000234
+#define CPU_R14 0x00000238
+#define CPU_R15 0x0000023c
+#define CPU_R16 0x00000240
+#define CPU_R17 0x00000244
+#define CPU_R18 0x00000248
+#define CPU_R19 0x0000024c
+#define CPU_R20 0x00000250
+#define CPU_R21 0x00000254
+#define CPU_R22 0x00000258
+#define CPU_R23 0x0000025c
+#define CPU_R24 0x00000260
+#define CPU_R25 0x00000264
+#define CPU_R26 0x00000268
+#define CPU_R27 0x0000026c
+#define CPU_R28 0x00000270
+#define CPU_R29 0x00000274
+#define CPU_R30 0x00000278
+#define CPU_R31 0x0000027c
+/* 0x280 --> 0x400 unused */
+
+#define RX_CPU_BASE 0x00005000
+#define TX_CPU_BASE 0x00005400
+
+/* Mailboxes */
+#define GRCMBOX_INTERRUPT_0 0x00005800 /* 64-bit */
+#define GRCMBOX_INTERRUPT_1 0x00005808 /* 64-bit */
+#define GRCMBOX_INTERRUPT_2 0x00005810 /* 64-bit */
+#define GRCMBOX_INTERRUPT_3 0x00005818 /* 64-bit */
+#define GRCMBOX_GENERAL_0 0x00005820 /* 64-bit */
+#define GRCMBOX_GENERAL_1 0x00005828 /* 64-bit */
+#define GRCMBOX_GENERAL_2 0x00005830 /* 64-bit */
+#define GRCMBOX_GENERAL_3 0x00005838 /* 64-bit */
+#define GRCMBOX_GENERAL_4 0x00005840 /* 64-bit */
+#define GRCMBOX_GENERAL_5 0x00005848 /* 64-bit */
+#define GRCMBOX_GENERAL_6 0x00005850 /* 64-bit */
+#define GRCMBOX_GENERAL_7 0x00005858 /* 64-bit */
+#define GRCMBOX_RELOAD_STAT 0x00005860 /* 64-bit */
+#define GRCMBOX_RCVSTD_PROD_IDX 0x00005868 /* 64-bit */
+#define GRCMBOX_RCVJUMBO_PROD_IDX 0x00005870 /* 64-bit */
+#define GRCMBOX_RCVMINI_PROD_IDX 0x00005878 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_0 0x00005880 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_1 0x00005888 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_2 0x00005890 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_3 0x00005898 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_4 0x000058a0 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_5 0x000058a8 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_6 0x000058b0 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_7 0x000058b8 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_8 0x000058c0 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_9 0x000058c8 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_10 0x000058d0 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_11 0x000058d8 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_12 0x000058e0 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_13 0x000058e8 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_14 0x000058f0 /* 64-bit */
+#define GRCMBOX_RCVRET_CON_IDX_15 0x000058f8 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_0 0x00005900 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_1 0x00005908 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_2 0x00005910 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_3 0x00005918 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_4 0x00005920 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_5 0x00005928 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_6 0x00005930 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_7 0x00005938 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_8 0x00005940 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_9 0x00005948 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_10 0x00005950 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_11 0x00005958 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_12 0x00005960 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_13 0x00005968 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_14 0x00005970 /* 64-bit */
+#define GRCMBOX_SNDHOST_PROD_IDX_15 0x00005978 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_0 0x00005980 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_1 0x00005988 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_2 0x00005990 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_3 0x00005998 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_4 0x000059a0 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_5 0x000059a8 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_6 0x000059b0 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_7 0x000059b8 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_8 0x000059c0 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_9 0x000059c8 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_10 0x000059d0 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_11 0x000059d8 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_12 0x000059e0 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_13 0x000059e8 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_14 0x000059f0 /* 64-bit */
+#define GRCMBOX_SNDNIC_PROD_IDX_15 0x000059f8 /* 64-bit */
+#define GRCMBOX_HIGH_PRIO_EV_VECTOR 0x00005a00
+#define GRCMBOX_HIGH_PRIO_EV_MASK 0x00005a04
+#define GRCMBOX_LOW_PRIO_EV_VEC 0x00005a08
+#define GRCMBOX_LOW_PRIO_EV_MASK 0x00005a0c
+/* 0x5a10 --> 0x5c00 */
+
+/* Flow Through queues */
+#define FTQ_RESET 0x00005c00
+/* 0x5c04 --> 0x5c10 unused */
+#define FTQ_DMA_NORM_READ_CTL 0x00005c10
+#define FTQ_DMA_NORM_READ_FULL_CNT 0x00005c14
+#define FTQ_DMA_NORM_READ_FIFO_ENQDEQ 0x00005c18
+#define FTQ_DMA_NORM_READ_WRITE_PEEK 0x00005c1c
+#define FTQ_DMA_HIGH_READ_CTL 0x00005c20
+#define FTQ_DMA_HIGH_READ_FULL_CNT 0x00005c24
+#define FTQ_DMA_HIGH_READ_FIFO_ENQDEQ 0x00005c28
+#define FTQ_DMA_HIGH_READ_WRITE_PEEK 0x00005c2c
+#define FTQ_DMA_COMP_DISC_CTL 0x00005c30
+#define FTQ_DMA_COMP_DISC_FULL_CNT 0x00005c34
+#define FTQ_DMA_COMP_DISC_FIFO_ENQDEQ 0x00005c38
+#define FTQ_DMA_COMP_DISC_WRITE_PEEK 0x00005c3c
+#define FTQ_SEND_BD_COMP_CTL 0x00005c40
+#define FTQ_SEND_BD_COMP_FULL_CNT 0x00005c44
+#define FTQ_SEND_BD_COMP_FIFO_ENQDEQ 0x00005c48
+#define FTQ_SEND_BD_COMP_WRITE_PEEK 0x00005c4c
+#define FTQ_SEND_DATA_INIT_CTL 0x00005c50
+#define FTQ_SEND_DATA_INIT_FULL_CNT 0x00005c54
+#define FTQ_SEND_DATA_INIT_FIFO_ENQDEQ 0x00005c58
+#define FTQ_SEND_DATA_INIT_WRITE_PEEK 0x00005c5c
+#define FTQ_DMA_NORM_WRITE_CTL 0x00005c60
+#define FTQ_DMA_NORM_WRITE_FULL_CNT 0x00005c64
+#define FTQ_DMA_NORM_WRITE_FIFO_ENQDEQ 0x00005c68
+#define FTQ_DMA_NORM_WRITE_WRITE_PEEK 0x00005c6c
+#define FTQ_DMA_HIGH_WRITE_CTL 0x00005c70
+#define FTQ_DMA_HIGH_WRITE_FULL_CNT 0x00005c74
+#define FTQ_DMA_HIGH_WRITE_FIFO_ENQDEQ 0x00005c78
+#define FTQ_DMA_HIGH_WRITE_WRITE_PEEK 0x00005c7c
+#define FTQ_SWTYPE1_CTL 0x00005c80
+#define FTQ_SWTYPE1_FULL_CNT 0x00005c84
+#define FTQ_SWTYPE1_FIFO_ENQDEQ 0x00005c88
+#define FTQ_SWTYPE1_WRITE_PEEK 0x00005c8c
+#define FTQ_SEND_DATA_COMP_CTL 0x00005c90
+#define FTQ_SEND_DATA_COMP_FULL_CNT 0x00005c94
+#define FTQ_SEND_DATA_COMP_FIFO_ENQDEQ 0x00005c98
+#define FTQ_SEND_DATA_COMP_WRITE_PEEK 0x00005c9c
+#define FTQ_HOST_COAL_CTL 0x00005ca0
+#define FTQ_HOST_COAL_FULL_CNT 0x00005ca4
+#define FTQ_HOST_COAL_FIFO_ENQDEQ 0x00005ca8
+#define FTQ_HOST_COAL_WRITE_PEEK 0x00005cac
+#define FTQ_MAC_TX_CTL 0x00005cb0
+#define FTQ_MAC_TX_FULL_CNT 0x00005cb4
+#define FTQ_MAC_TX_FIFO_ENQDEQ 0x00005cb8
+#define FTQ_MAC_TX_WRITE_PEEK 0x00005cbc
+#define FTQ_MB_FREE_CTL 0x00005cc0
+#define FTQ_MB_FREE_FULL_CNT 0x00005cc4
+#define FTQ_MB_FREE_FIFO_ENQDEQ 0x00005cc8
+#define FTQ_MB_FREE_WRITE_PEEK 0x00005ccc
+#define FTQ_RCVBD_COMP_CTL 0x00005cd0
+#define FTQ_RCVBD_COMP_FULL_CNT 0x00005cd4
+#define FTQ_RCVBD_COMP_FIFO_ENQDEQ 0x00005cd8
+#define FTQ_RCVBD_COMP_WRITE_PEEK 0x00005cdc
+#define FTQ_RCVLST_PLMT_CTL 0x00005ce0
+#define FTQ_RCVLST_PLMT_FULL_CNT 0x00005ce4
+#define FTQ_RCVLST_PLMT_FIFO_ENQDEQ 0x00005ce8
+#define FTQ_RCVLST_PLMT_WRITE_PEEK 0x00005cec
+#define FTQ_RCVDATA_INI_CTL 0x00005cf0
+#define FTQ_RCVDATA_INI_FULL_CNT 0x00005cf4
+#define FTQ_RCVDATA_INI_FIFO_ENQDEQ 0x00005cf8
+#define FTQ_RCVDATA_INI_WRITE_PEEK 0x00005cfc
+#define FTQ_RCVDATA_COMP_CTL 0x00005d00
+#define FTQ_RCVDATA_COMP_FULL_CNT 0x00005d04
+#define FTQ_RCVDATA_COMP_FIFO_ENQDEQ 0x00005d08
+#define FTQ_RCVDATA_COMP_WRITE_PEEK 0x00005d0c
+#define FTQ_SWTYPE2_CTL 0x00005d10
+#define FTQ_SWTYPE2_FULL_CNT 0x00005d14
+#define FTQ_SWTYPE2_FIFO_ENQDEQ 0x00005d18
+#define FTQ_SWTYPE2_WRITE_PEEK 0x00005d1c
+/* 0x5d20 --> 0x6000 unused */
+
+/* Message signaled interrupt registers */
+#define MSGINT_MODE 0x00006000
+#define MSGINT_MODE_RESET 0x00000001
+#define MSGINT_MODE_ENABLE 0x00000002
+#define MSGINT_STATUS 0x00006004
+#define MSGINT_FIFO 0x00006008
+/* 0x600c --> 0x6400 unused */
+
+/* DMA completion registers */
+#define DMAC_MODE 0x00006400
+#define DMAC_MODE_RESET 0x00000001
+#define DMAC_MODE_ENABLE 0x00000002
+/* 0x6404 --> 0x6800 unused */
+
+/* GRC registers */
+#define GRC_MODE 0x00006800
+#define GRC_MODE_UPD_ON_COAL 0x00000001
+#define GRC_MODE_BSWAP_NONFRM_DATA 0x00000002
+#define GRC_MODE_WSWAP_NONFRM_DATA 0x00000004
+#define GRC_MODE_BSWAP_DATA 0x00000010
+#define GRC_MODE_WSWAP_DATA 0x00000020
+#define GRC_MODE_SPLITHDR 0x00000100
+#define GRC_MODE_NOFRM_CRACKING 0x00000200
+#define GRC_MODE_INCL_CRC 0x00000400
+#define GRC_MODE_ALLOW_BAD_FRMS 0x00000800
+#define GRC_MODE_NOIRQ_ON_SENDS 0x00002000
+#define GRC_MODE_NOIRQ_ON_RCV 0x00004000
+#define GRC_MODE_FORCE_PCI32BIT 0x00008000
+#define GRC_MODE_HOST_STACKUP 0x00010000
+#define GRC_MODE_HOST_SENDBDS 0x00020000
+#define GRC_MODE_NO_TX_PHDR_CSUM 0x00100000
+#define GRC_MODE_NO_RX_PHDR_CSUM 0x00800000
+#define GRC_MODE_IRQ_ON_TX_CPU_ATTN 0x01000000
+#define GRC_MODE_IRQ_ON_RX_CPU_ATTN 0x02000000
+#define GRC_MODE_IRQ_ON_MAC_ATTN 0x04000000
+#define GRC_MODE_IRQ_ON_DMA_ATTN 0x08000000
+#define GRC_MODE_IRQ_ON_FLOW_ATTN 0x10000000
+#define GRC_MODE_4X_NIC_SEND_RINGS 0x20000000
+#define GRC_MODE_MCAST_FRM_ENABLE 0x40000000
+#define GRC_MISC_CFG 0x00006804
+#define GRC_MISC_CFG_CORECLK_RESET 0x00000001
+#define GRC_MISC_CFG_PRESCALAR_MASK 0x000000fe
+#define GRC_MISC_CFG_PRESCALAR_SHIFT 1
+#define GRC_MISC_CFG_BOARD_ID_MASK 0x0001e000
+#define GRC_MISC_CFG_BOARD_ID_5700 0x0001e000
+#define GRC_MISC_CFG_BOARD_ID_5701 0x00000000
+#define GRC_MISC_CFG_BOARD_ID_5702FE 0x00004000
+#define GRC_MISC_CFG_BOARD_ID_5703 0x00000000
+#define GRC_MISC_CFG_BOARD_ID_5703S 0x00002000
+#define GRC_MISC_CFG_BOARD_ID_5704 0x00000000
+#define GRC_MISC_CFG_BOARD_ID_5704CIOBE 0x00004000
+#define GRC_MISC_CFG_BOARD_ID_5704_A2 0x00008000
+#define GRC_MISC_CFG_BOARD_ID_5788 0x00010000
+#define GRC_MISC_CFG_BOARD_ID_5788M 0x00018000
+#define GRC_MISC_CFG_BOARD_ID_AC91002A1 0x00018000
+#define GRC_MISC_CFG_KEEP_GPHY_POWER 0x04000000
+#define GRC_LOCAL_CTRL 0x00006808
+#define GRC_LCLCTRL_INT_ACTIVE 0x00000001
+#define GRC_LCLCTRL_CLEARINT 0x00000002
+#define GRC_LCLCTRL_SETINT 0x00000004
+#define GRC_LCLCTRL_INT_ON_ATTN 0x00000008
+#define GRC_LCLCTRL_GPIO_INPUT0 0x00000100
+#define GRC_LCLCTRL_GPIO_INPUT1 0x00000200
+#define GRC_LCLCTRL_GPIO_INPUT2 0x00000400
+#define GRC_LCLCTRL_GPIO_OE0 0x00000800
+#define GRC_LCLCTRL_GPIO_OE1 0x00001000
+#define GRC_LCLCTRL_GPIO_OE2 0x00002000
+#define GRC_LCLCTRL_GPIO_OUTPUT0 0x00004000
+#define GRC_LCLCTRL_GPIO_OUTPUT1 0x00008000
+#define GRC_LCLCTRL_GPIO_OUTPUT2 0x00010000
+#define GRC_LCLCTRL_EXTMEM_ENABLE 0x00020000
+#define GRC_LCLCTRL_MEMSZ_MASK 0x001c0000
+#define GRC_LCLCTRL_MEMSZ_256K 0x00000000
+#define GRC_LCLCTRL_MEMSZ_512K 0x00040000
+#define GRC_LCLCTRL_MEMSZ_1M 0x00080000
+#define GRC_LCLCTRL_MEMSZ_2M 0x000c0000
+#define GRC_LCLCTRL_MEMSZ_4M 0x00100000
+#define GRC_LCLCTRL_MEMSZ_8M 0x00140000
+#define GRC_LCLCTRL_MEMSZ_16M 0x00180000
+#define GRC_LCLCTRL_BANK_SELECT 0x00200000
+#define GRC_LCLCTRL_SSRAM_TYPE 0x00400000
+#define GRC_LCLCTRL_AUTO_SEEPROM 0x01000000
+#define GRC_TIMER 0x0000680c
+#define GRC_RX_CPU_EVENT 0x00006810
+#define GRC_RX_TIMER_REF 0x00006814
+#define GRC_RX_CPU_SEM 0x00006818
+#define GRC_REMOTE_RX_CPU_ATTN 0x0000681c
+#define GRC_TX_CPU_EVENT 0x00006820
+#define GRC_TX_TIMER_REF 0x00006824
+#define GRC_TX_CPU_SEM 0x00006828
+#define GRC_REMOTE_TX_CPU_ATTN 0x0000682c
+#define GRC_MEM_POWER_UP 0x00006830 /* 64-bit */
+#define GRC_EEPROM_ADDR 0x00006838
+#define EEPROM_ADDR_WRITE 0x00000000
+#define EEPROM_ADDR_READ 0x80000000
+#define EEPROM_ADDR_COMPLETE 0x40000000
+#define EEPROM_ADDR_FSM_RESET 0x20000000
+#define EEPROM_ADDR_DEVID_MASK 0x1c000000
+#define EEPROM_ADDR_DEVID_SHIFT 26
+#define EEPROM_ADDR_START 0x02000000
+#define EEPROM_ADDR_CLKPERD_SHIFT 16
+#define EEPROM_ADDR_ADDR_MASK 0x0000ffff
+#define EEPROM_ADDR_ADDR_SHIFT 0
+#define EEPROM_DEFAULT_CLOCK_PERIOD 0x60
+#define EEPROM_CHIP_SIZE (64 * 1024)
+#define GRC_EEPROM_DATA 0x0000683c
+#define GRC_EEPROM_CTRL 0x00006840
+#define GRC_MDI_CTRL 0x00006844
+#define GRC_SEEPROM_DELAY 0x00006848
+/* 0x684c --> 0x6c00 unused */
+
+/* 0x6c00 --> 0x7000 unused */
+
+/* NVRAM Control registers */
+#define NVRAM_CMD 0x00007000
+#define NVRAM_CMD_RESET 0x00000001
+#define NVRAM_CMD_DONE 0x00000008
+#define NVRAM_CMD_GO 0x00000010
+#define NVRAM_CMD_WR 0x00000020
+#define NVRAM_CMD_RD 0x00000000
+#define NVRAM_CMD_ERASE 0x00000040
+#define NVRAM_CMD_FIRST 0x00000080
+#define NVRAM_CMD_LAST 0x00000100
+#define NVRAM_STAT 0x00007004
+#define NVRAM_WRDATA 0x00007008
+#define NVRAM_ADDR 0x0000700c
+#define NVRAM_ADDR_MSK 0x00ffffff
+#define NVRAM_RDDATA 0x00007010
+#define NVRAM_CFG1 0x00007014
+#define NVRAM_CFG1_FLASHIF_ENAB 0x00000001
+#define NVRAM_CFG1_BUFFERED_MODE 0x00000002
+#define NVRAM_CFG1_PASS_THRU 0x00000004
+#define NVRAM_CFG1_BIT_BANG 0x00000008
+#define NVRAM_CFG1_COMPAT_BYPASS 0x80000000
+#define NVRAM_CFG2 0x00007018
+#define NVRAM_CFG3 0x0000701c
+#define NVRAM_SWARB 0x00007020
+#define SWARB_REQ_SET0 0x00000001
+#define SWARB_REQ_SET1 0x00000002
+#define SWARB_REQ_SET2 0x00000004
+#define SWARB_REQ_SET3 0x00000008
+#define SWARB_REQ_CLR0 0x00000010
+#define SWARB_REQ_CLR1 0x00000020
+#define SWARB_REQ_CLR2 0x00000040
+#define SWARB_REQ_CLR3 0x00000080
+#define SWARB_GNT0 0x00000100
+#define SWARB_GNT1 0x00000200
+#define SWARB_GNT2 0x00000400
+#define SWARB_GNT3 0x00000800
+#define SWARB_REQ0 0x00001000
+#define SWARB_REQ1 0x00002000
+#define SWARB_REQ2 0x00004000
+#define SWARB_REQ3 0x00008000
+#define NVRAM_BUFFERED_PAGE_SIZE 264
+#define NVRAM_BUFFERED_PAGE_POS 9
+/* 0x7024 --> 0x7400 unused */
+
+/* 0x7400 --> 0x8000 unused */
+
+/* 32K Window into NIC internal memory */
+#define NIC_SRAM_WIN_BASE 0x00008000
+
+/* Offsets into first 32k of NIC internal memory. */
+#define NIC_SRAM_PAGE_ZERO 0x00000000
+#define NIC_SRAM_SEND_RCB 0x00000100 /* 16 * TG3_BDINFO_... */
+#define NIC_SRAM_RCV_RET_RCB 0x00000200 /* 16 * TG3_BDINFO_... */
+#define NIC_SRAM_STATS_BLK 0x00000300
+#define NIC_SRAM_STATUS_BLK 0x00000b00
+
+#define NIC_SRAM_FIRMWARE_MBOX 0x00000b50
+#define NIC_SRAM_FIRMWARE_MBOX_MAGIC1 0x4B657654
+#define NIC_SRAM_FIRMWARE_MBOX_MAGIC2 0x4861764b /* !dma on linkchg */
+
+#define NIC_SRAM_DATA_SIG 0x00000b54
+#define NIC_SRAM_DATA_SIG_MAGIC 0x4b657654 /* ascii for 'KevT' */
+
+#define NIC_SRAM_DATA_CFG 0x00000b58
+#define NIC_SRAM_DATA_CFG_LED_MODE_MASK 0x0000000c
+#define NIC_SRAM_DATA_CFG_LED_MODE_UNKNOWN 0x00000000
+#define NIC_SRAM_DATA_CFG_LED_TRIPLE_SPD 0x00000004
+#define NIC_SRAM_DATA_CFG_LED_OPEN_DRAIN 0x00000004
+#define NIC_SRAM_DATA_CFG_LED_LINK_SPD 0x00000008
+#define NIC_SRAM_DATA_CFG_LED_OUTPUT 0x00000008
+#define NIC_SRAM_DATA_CFG_PHY_TYPE_MASK 0x00000030
+#define NIC_SRAM_DATA_CFG_PHY_TYPE_UNKNOWN 0x00000000
+#define NIC_SRAM_DATA_CFG_PHY_TYPE_COPPER 0x00000010
+#define NIC_SRAM_DATA_CFG_PHY_TYPE_FIBER 0x00000020
+#define NIC_SRAM_DATA_CFG_WOL_ENABLE 0x00000040
+#define NIC_SRAM_DATA_CFG_ASF_ENABLE 0x00000080
+#define NIC_SRAM_DATA_CFG_EEPROM_WP 0x00000100
+#define NIC_SRAM_DATA_CFG_MINI_PCI 0x00001000
+#define NIC_SRAM_DATA_CFG_FIBER_WOL 0x00004000
+
+#define NIC_SRAM_DATA_PHY_ID 0x00000b74
+#define NIC_SRAM_DATA_PHY_ID1_MASK 0xffff0000
+#define NIC_SRAM_DATA_PHY_ID2_MASK 0x0000ffff
+
+#define NIC_SRAM_FW_CMD_MBOX 0x00000b78
+#define FWCMD_NICDRV_ALIVE 0x00000001
+#define FWCMD_NICDRV_PAUSE_FW 0x00000002
+#define FWCMD_NICDRV_IPV4ADDR_CHG 0x00000003
+#define FWCMD_NICDRV_IPV6ADDR_CHG 0x00000004
+#define FWCMD_NICDRV_FIX_DMAR 0x00000005
+#define FWCMD_NICDRV_FIX_DMAW 0x00000006
+#define NIC_SRAM_FW_CMD_LEN_MBOX 0x00000b7c
+#define NIC_SRAM_FW_CMD_DATA_MBOX 0x00000b80
+#define NIC_SRAM_FW_ASF_STATUS_MBOX 0x00000c00
+#define NIC_SRAM_FW_DRV_STATE_MBOX 0x00000c04
+#define DRV_STATE_START 0x00000001
+#define DRV_STATE_UNLOAD 0x00000002
+#define DRV_STATE_WOL 0x00000003
+#define DRV_STATE_SUSPEND 0x00000004
+
+#define NIC_SRAM_FW_RESET_TYPE_MBOX 0x00000c08
+
+#define NIC_SRAM_MAC_ADDR_HIGH_MBOX 0x00000c14
+#define NIC_SRAM_MAC_ADDR_LOW_MBOX 0x00000c18
+
+#define NIC_SRAM_RX_MINI_BUFFER_DESC 0x00001000
+
+#define NIC_SRAM_DMA_DESC_POOL_BASE 0x00002000
+#define NIC_SRAM_DMA_DESC_POOL_SIZE 0x00002000
+#define NIC_SRAM_TX_BUFFER_DESC 0x00004000 /* 512 entries */
+#define NIC_SRAM_RX_BUFFER_DESC 0x00006000 /* 256 entries */
+#define NIC_SRAM_RX_JUMBO_BUFFER_DESC 0x00007000 /* 256 entries */
+#define NIC_SRAM_MBUF_POOL_BASE 0x00008000
+#define NIC_SRAM_MBUF_POOL_SIZE96 0x00018000
+#define NIC_SRAM_MBUF_POOL_SIZE64 0x00010000
+#define NIC_SRAM_MBUF_POOL_BASE5705 0x00010000
+#define NIC_SRAM_MBUF_POOL_SIZE5705 0x0000e000
+
+/* Currently this is fixed. */
+#define PHY_ADDR 0x01
+
+/* Tigon3 specific PHY MII registers. */
+#define TG3_BMCR_SPEED1000 0x0040
+
+#define MII_TG3_CTRL 0x09 /* 1000-baseT control register */
+#define MII_TG3_CTRL_ADV_1000_HALF 0x0100
+#define MII_TG3_CTRL_ADV_1000_FULL 0x0200
+#define MII_TG3_CTRL_AS_MASTER 0x0800
+#define MII_TG3_CTRL_ENABLE_AS_MASTER 0x1000
+
+#define MII_TG3_EXT_CTRL 0x10 /* Extended control register */
+#define MII_TG3_EXT_CTRL_LNK3_LED_MODE 0x0002
+#define MII_TG3_EXT_CTRL_TBI 0x8000
+
+#define MII_TG3_EXT_STAT 0x11 /* Extended status register */
+#define MII_TG3_EXT_STAT_LPASS 0x0100
+
+#define MII_TG3_DSP_RW_PORT 0x15 /* DSP coefficient read/write port */
+
+#define MII_TG3_DSP_ADDRESS 0x17 /* DSP address register */
+
+#define MII_TG3_AUX_CTRL 0x18 /* auxilliary control register */
+
+#define MII_TG3_AUX_STAT 0x19 /* auxilliary status register */
+#define MII_TG3_AUX_STAT_LPASS 0x0004
+#define MII_TG3_AUX_STAT_SPDMASK 0x0700
+#define MII_TG3_AUX_STAT_10HALF 0x0100
+#define MII_TG3_AUX_STAT_10FULL 0x0200
+#define MII_TG3_AUX_STAT_100HALF 0x0300
+#define MII_TG3_AUX_STAT_100_4 0x0400
+#define MII_TG3_AUX_STAT_100FULL 0x0500
+#define MII_TG3_AUX_STAT_1000HALF 0x0600
+#define MII_TG3_AUX_STAT_1000FULL 0x0700
+
+#define MII_TG3_ISTAT 0x1a /* IRQ status register */
+#define MII_TG3_IMASK 0x1b /* IRQ mask register */
+
+/* ISTAT/IMASK event bits */
+#define MII_TG3_INT_LINKCHG 0x0002
+#define MII_TG3_INT_SPEEDCHG 0x0004
+#define MII_TG3_INT_DUPLEXCHG 0x0008
+#define MII_TG3_INT_ANEG_PAGE_RX 0x0400
+
+/* XXX Add this to mii.h */
+#ifndef ADVERTISE_PAUSE
+#define ADVERTISE_PAUSE_CAP 0x0400
+#endif
+#ifndef ADVERTISE_PAUSE_ASYM
+#define ADVERTISE_PAUSE_ASYM 0x0800
+#endif
+#ifndef LPA_PAUSE
+#define LPA_PAUSE_CAP 0x0400
+#endif
+#ifndef LPA_PAUSE_ASYM
+#define LPA_PAUSE_ASYM 0x0800
+#endif
+
+/* There are two ways to manage the TX descriptors on the tigon3.
+ * Either the descriptors are in host DMA'able memory, or they
+ * exist only in the cards on-chip SRAM. All 16 send bds are under
+ * the same mode, they may not be configured individually.
+ *
+ * The mode we use is controlled by TG3_FLAG_HOST_TXDS in tp->tg3_flags.
+ *
+ * To use host memory TX descriptors:
+ * 1) Set GRC_MODE_HOST_SENDBDS in GRC_MODE register.
+ * Make sure GRC_MODE_4X_NIC_SEND_RINGS is clear.
+ * 2) Allocate DMA'able memory.
+ * 3) In NIC_SRAM_SEND_RCB (of desired index) of on-chip SRAM:
+ * a) Set TG3_BDINFO_HOST_ADDR to DMA address of memory
+ * obtained in step 2
+ * b) Set TG3_BDINFO_NIC_ADDR to NIC_SRAM_TX_BUFFER_DESC.
+ * c) Set len field of TG3_BDINFO_MAXLEN_FLAGS to number
+ * of TX descriptors. Leave flags field clear.
+ * 4) Access TX descriptors via host memory. The chip
+ * will refetch into local SRAM as needed when producer
+ * index mailboxes are updated.
+ *
+ * To use on-chip TX descriptors:
+ * 1) Set GRC_MODE_4X_NIC_SEND_RINGS in GRC_MODE register.
+ * Make sure GRC_MODE_HOST_SENDBDS is clear.
+ * 2) In NIC_SRAM_SEND_RCB (of desired index) of on-chip SRAM:
+ * a) Set TG3_BDINFO_HOST_ADDR to zero.
+ * b) Set TG3_BDINFO_NIC_ADDR to NIC_SRAM_TX_BUFFER_DESC
+ * c) TG3_BDINFO_MAXLEN_FLAGS is don't care.
+ * 3) Access TX descriptors directly in on-chip SRAM
+ * using normal {read,write}l(). (and not using
+ * pointer dereferencing of ioremap()'d memory like
+ * the broken Broadcom driver does)
+ *
+ * Note that BDINFO_FLAGS_DISABLED should be set in the flags field of
+ * TG3_BDINFO_MAXLEN_FLAGS of all unused SEND_RCB indices.
+ */
+struct tg3_tx_buffer_desc {
+ uint32_t addr_hi;
+ uint32_t addr_lo;
+
+ uint32_t len_flags;
+#define TXD_FLAG_TCPUDP_CSUM 0x0001
+#define TXD_FLAG_IP_CSUM 0x0002
+#define TXD_FLAG_END 0x0004
+#define TXD_FLAG_IP_FRAG 0x0008
+#define TXD_FLAG_IP_FRAG_END 0x0010
+#define TXD_FLAG_VLAN 0x0040
+#define TXD_FLAG_COAL_NOW 0x0080
+#define TXD_FLAG_CPU_PRE_DMA 0x0100
+#define TXD_FLAG_CPU_POST_DMA 0x0200
+#define TXD_FLAG_ADD_SRC_ADDR 0x1000
+#define TXD_FLAG_CHOOSE_SRC_ADDR 0x6000
+#define TXD_FLAG_NO_CRC 0x8000
+#define TXD_LEN_SHIFT 16
+
+ uint32_t vlan_tag;
+#define TXD_VLAN_TAG_SHIFT 0
+#define TXD_MSS_SHIFT 16
+};
+
+#define TXD_ADDR 0x00UL /* 64-bit */
+#define TXD_LEN_FLAGS 0x08UL /* 32-bit (upper 16-bits are len) */
+#define TXD_VLAN_TAG 0x0cUL /* 32-bit (upper 16-bits are tag) */
+#define TXD_SIZE 0x10UL
+
+struct tg3_rx_buffer_desc {
+ uint32_t addr_hi;
+ uint32_t addr_lo;
+
+ uint32_t idx_len;
+#define RXD_IDX_MASK 0xffff0000
+#define RXD_IDX_SHIFT 16
+#define RXD_LEN_MASK 0x0000ffff
+#define RXD_LEN_SHIFT 0
+
+ uint32_t type_flags;
+#define RXD_TYPE_SHIFT 16
+#define RXD_FLAGS_SHIFT 0
+
+#define RXD_FLAG_END 0x0004
+#define RXD_FLAG_MINI 0x0800
+#define RXD_FLAG_JUMBO 0x0020
+#define RXD_FLAG_VLAN 0x0040
+#define RXD_FLAG_ERROR 0x0400
+#define RXD_FLAG_IP_CSUM 0x1000
+#define RXD_FLAG_TCPUDP_CSUM 0x2000
+#define RXD_FLAG_IS_TCP 0x4000
+
+ uint32_t ip_tcp_csum;
+#define RXD_IPCSUM_MASK 0xffff0000
+#define RXD_IPCSUM_SHIFT 16
+#define RXD_TCPCSUM_MASK 0x0000ffff
+#define RXD_TCPCSUM_SHIFT 0
+
+ uint32_t err_vlan;
+
+#define RXD_VLAN_MASK 0x0000ffff
+
+#define RXD_ERR_BAD_CRC 0x00010000
+#define RXD_ERR_COLLISION 0x00020000
+#define RXD_ERR_LINK_LOST 0x00040000
+#define RXD_ERR_PHY_DECODE 0x00080000
+#define RXD_ERR_ODD_NIBBLE_RCVD_MII 0x00100000
+#define RXD_ERR_MAC_ABRT 0x00200000
+#define RXD_ERR_TOO_SMALL 0x00400000
+#define RXD_ERR_NO_RESOURCES 0x00800000
+#define RXD_ERR_HUGE_FRAME 0x01000000
+#define RXD_ERR_MASK 0xffff0000
+
+ uint32_t reserved;
+ uint32_t opaque;
+#define RXD_OPAQUE_INDEX_MASK 0x0000ffff
+#define RXD_OPAQUE_INDEX_SHIFT 0
+#define RXD_OPAQUE_RING_STD 0x00010000
+#define RXD_OPAQUE_RING_JUMBO 0x00020000
+#define RXD_OPAQUE_RING_MINI 0x00040000
+#define RXD_OPAQUE_RING_MASK 0x00070000
+};
+
+struct tg3_ext_rx_buffer_desc {
+ struct {
+ uint32_t addr_hi;
+ uint32_t addr_lo;
+ } addrlist[3];
+ uint32_t len2_len1;
+ uint32_t resv_len3;
+ struct tg3_rx_buffer_desc std;
+};
+
+/* We only use this when testing out the DMA engine
+ * at probe time. This is the internal format of buffer
+ * descriptors used by the chip at NIC_SRAM_DMA_DESCS.
+ */
+struct tg3_internal_buffer_desc {
+ uint32_t addr_hi;
+ uint32_t addr_lo;
+ uint32_t nic_mbuf;
+ /* XXX FIX THIS */
+#if __BYTE_ORDER == __BIG_ENDIAN
+ uint16_t cqid_sqid;
+ uint16_t len;
+#else
+ uint16_t len;
+ uint16_t cqid_sqid;
+#endif
+ uint32_t flags;
+ uint32_t __cookie1;
+ uint32_t __cookie2;
+ uint32_t __cookie3;
+};
+
+#define TG3_HW_STATUS_SIZE 0x50
+struct tg3_hw_status {
+ uint32_t status;
+#define SD_STATUS_UPDATED 0x00000001
+#define SD_STATUS_LINK_CHG 0x00000002
+#define SD_STATUS_ERROR 0x00000004
+
+ uint32_t status_tag;
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+ uint16_t rx_consumer;
+ uint16_t rx_jumbo_consumer;
+#else
+ uint16_t rx_jumbo_consumer;
+ uint16_t rx_consumer;
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+ uint16_t reserved;
+ uint16_t rx_mini_consumer;
+#else
+ uint16_t rx_mini_consumer;
+ uint16_t reserved;
+#endif
+ struct {
+#if __BYTE_ORDER == __BIG_ENDIAN
+ uint16_t tx_consumer;
+ uint16_t rx_producer;
+#else
+ uint16_t rx_producer;
+ uint16_t tx_consumer;
+#endif
+ } idx[16];
+};
+
+typedef struct {
+ uint32_t high, low;
+} tg3_stat64_t;
+
+struct tg3_hw_stats {
+ uint8_t __reserved0[0x400-0x300];
+
+ /* Statistics maintained by Receive MAC. */
+ tg3_stat64_t rx_octets;
+ uint64_t __reserved1;
+ tg3_stat64_t rx_fragments;
+ tg3_stat64_t rx_ucast_packets;
+ tg3_stat64_t rx_mcast_packets;
+ tg3_stat64_t rx_bcast_packets;
+ tg3_stat64_t rx_fcs_errors;
+ tg3_stat64_t rx_align_errors;
+ tg3_stat64_t rx_xon_pause_rcvd;
+ tg3_stat64_t rx_xoff_pause_rcvd;
+ tg3_stat64_t rx_mac_ctrl_rcvd;
+ tg3_stat64_t rx_xoff_entered;
+ tg3_stat64_t rx_frame_too_long_errors;
+ tg3_stat64_t rx_jabbers;
+ tg3_stat64_t rx_undersize_packets;
+ tg3_stat64_t rx_in_length_errors;
+ tg3_stat64_t rx_out_length_errors;
+ tg3_stat64_t rx_64_or_less_octet_packets;
+ tg3_stat64_t rx_65_to_127_octet_packets;
+ tg3_stat64_t rx_128_to_255_octet_packets;
+ tg3_stat64_t rx_256_to_511_octet_packets;
+ tg3_stat64_t rx_512_to_1023_octet_packets;
+ tg3_stat64_t rx_1024_to_1522_octet_packets;
+ tg3_stat64_t rx_1523_to_2047_octet_packets;
+ tg3_stat64_t rx_2048_to_4095_octet_packets;
+ tg3_stat64_t rx_4096_to_8191_octet_packets;
+ tg3_stat64_t rx_8192_to_9022_octet_packets;
+
+ uint64_t __unused0[37];
+
+ /* Statistics maintained by Transmit MAC. */
+ tg3_stat64_t tx_octets;
+ uint64_t __reserved2;
+ tg3_stat64_t tx_collisions;
+ tg3_stat64_t tx_xon_sent;
+ tg3_stat64_t tx_xoff_sent;
+ tg3_stat64_t tx_flow_control;
+ tg3_stat64_t tx_mac_errors;
+ tg3_stat64_t tx_single_collisions;
+ tg3_stat64_t tx_mult_collisions;
+ tg3_stat64_t tx_deferred;
+ uint64_t __reserved3;
+ tg3_stat64_t tx_excessive_collisions;
+ tg3_stat64_t tx_late_collisions;
+ tg3_stat64_t tx_collide_2times;
+ tg3_stat64_t tx_collide_3times;
+ tg3_stat64_t tx_collide_4times;
+ tg3_stat64_t tx_collide_5times;
+ tg3_stat64_t tx_collide_6times;
+ tg3_stat64_t tx_collide_7times;
+ tg3_stat64_t tx_collide_8times;
+ tg3_stat64_t tx_collide_9times;
+ tg3_stat64_t tx_collide_10times;
+ tg3_stat64_t tx_collide_11times;
+ tg3_stat64_t tx_collide_12times;
+ tg3_stat64_t tx_collide_13times;
+ tg3_stat64_t tx_collide_14times;
+ tg3_stat64_t tx_collide_15times;
+ tg3_stat64_t tx_ucast_packets;
+ tg3_stat64_t tx_mcast_packets;
+ tg3_stat64_t tx_bcast_packets;
+ tg3_stat64_t tx_carrier_sense_errors;
+ tg3_stat64_t tx_discards;
+ tg3_stat64_t tx_errors;
+
+ uint64_t __unused1[31];
+
+ /* Statistics maintained by Receive List Placement. */
+ tg3_stat64_t COS_rx_packets[16];
+ tg3_stat64_t COS_rx_filter_dropped;
+ tg3_stat64_t dma_writeq_full;
+ tg3_stat64_t dma_write_prioq_full;
+ tg3_stat64_t rxbds_empty;
+ tg3_stat64_t rx_discards;
+ tg3_stat64_t rx_errors;
+ tg3_stat64_t rx_threshold_hit;
+
+ uint64_t __unused2[9];
+
+ /* Statistics maintained by Send Data Initiator. */
+ tg3_stat64_t COS_out_packets[16];
+ tg3_stat64_t dma_readq_full;
+ tg3_stat64_t dma_read_prioq_full;
+ tg3_stat64_t tx_comp_queue_full;
+
+ /* Statistics maintained by Host Coalescing. */
+ tg3_stat64_t ring_set_send_prod_index;
+ tg3_stat64_t ring_status_update;
+ tg3_stat64_t nic_irqs;
+ tg3_stat64_t nic_avoided_irqs;
+ tg3_stat64_t nic_tx_threshold_hit;
+
+ uint8_t __reserved4[0xb00-0x9c0];
+};
+
+enum phy_led_mode {
+ led_mode_auto,
+ led_mode_three_link,
+ led_mode_link10
+};
+
+#if 0
+/* 'mapping' is superfluous as the chip does not write into
+ * the tx/rx post rings so we could just fetch it from there.
+ * But the cache behavior is better how we are doing it now.
+ */
+struct ring_info {
+ struct sk_buff *skb;
+ DECLARE_PCI_UNMAP_ADDR(mapping)
+};
+
+struct tx_ring_info {
+ struct sk_buff *skb;
+ DECLARE_PCI_UNMAP_ADDR(mapping)
+ uint32_t prev_vlan_tag;
+};
+#endif
+
+struct tg3_config_info {
+ uint32_t flags;
+};
+
+struct tg3_link_config {
+ /* Describes what we're trying to get. */
+ uint32_t advertising;
+#if 0
+ uint16_t speed;
+ uint8_t duplex;
+ uint8_t autoneg;
+#define SPEED_INVALID 0xffff
+#define DUPLEX_INVALID 0xff
+#define AUTONEG_INVALID 0xff
+#endif
+
+ /* Describes what we actually have. */
+ uint8_t active_speed;
+ uint8_t active_duplex;
+
+ /* When we go in and out of low power mode we need
+ * to swap with this state.
+ */
+#if 0
+ int phy_is_low_power;
+ uint16_t orig_speed;
+ uint8_t orig_duplex;
+ uint8_t orig_autoneg;
+#endif
+};
+
+struct tg3_bufmgr_config {
+ uint32_t mbuf_read_dma_low_water;
+ uint32_t mbuf_mac_rx_low_water;
+ uint32_t mbuf_high_water;
+
+ uint32_t mbuf_read_dma_low_water_jumbo;
+ uint32_t mbuf_mac_rx_low_water_jumbo;
+ uint32_t mbuf_high_water_jumbo;
+
+ uint32_t dma_low_water;
+ uint32_t dma_high_water;
+};
+
+struct tg3 {
+#if 0
+ /* SMP locking strategy:
+ *
+ * lock: Held during all operations except TX packet
+ * processing.
+ *
+ * tx_lock: Held during tg3_start_xmit{,_4gbug} and tg3_tx
+ *
+ * If you want to shut up all asynchronous processing you must
+ * acquire both locks, 'lock' taken before 'tx_lock'. IRQs must
+ * be disabled to take 'lock' but only softirq disabling is
+ * necessary for acquisition of 'tx_lock'.
+ */
+ spinlock_t lock;
+ spinlock_t tx_lock;
+#endif
+
+ uint32_t tx_prod;
+#if 0
+ uint32_t tx_cons;
+#endif
+ uint32_t rx_rcb_ptr;
+ uint32_t rx_std_ptr;
+#if 0
+ uint32_t rx_jumbo_ptr;
+ spinlock_t indirect_lock;
+
+ struct net_device_stats net_stats;
+ struct net_device_stats net_stats_prev;
+#endif
+ unsigned long phy_crc_errors;
+
+#if 0
+ uint32_t rx_offset;
+#endif
+ uint32_t tg3_flags;
+#if 0
+#define TG3_FLAG_HOST_TXDS 0x00000001
+#endif
+#define TG3_FLAG_TXD_MBOX_HWBUG 0x00000002
+#define TG3_FLAG_RX_CHECKSUMS 0x00000004
+#define TG3_FLAG_USE_LINKCHG_REG 0x00000008
+#define TG3_FLAG_USE_MI_INTERRUPT 0x00000010
+#define TG3_FLAG_ENABLE_ASF 0x00000020
+#define TG3_FLAG_5701_REG_WRITE_BUG 0x00000040
+#define TG3_FLAG_POLL_SERDES 0x00000080
+#define TG3_FLAG_MBOX_WRITE_REORDER 0x00000100
+#define TG3_FLAG_PCIX_TARGET_HWBUG 0x00000200
+#define TG3_FLAG_WOL_SPEED_100MB 0x00000400
+#define TG3_FLAG_WOL_ENABLE 0x00000800
+#define TG3_FLAG_EEPROM_WRITE_PROT 0x00001000
+#define TG3_FLAG_NVRAM 0x00002000
+#define TG3_FLAG_NVRAM_BUFFERED 0x00004000
+#define TG3_FLAG_RX_PAUSE 0x00008000
+#define TG3_FLAG_TX_PAUSE 0x00010000
+#define TG3_FLAG_PCIX_MODE 0x00020000
+#define TG3_FLAG_PCI_HIGH_SPEED 0x00040000
+#define TG3_FLAG_PCI_32BIT 0x00080000
+#define TG3_FLAG_NO_TX_PSEUDO_CSUM 0x00100000
+#define TG3_FLAG_NO_RX_PSEUDO_CSUM 0x00200000
+#define TG3_FLAG_SERDES_WOL_CAP 0x00400000
+#define TG3_FLAG_JUMBO_ENABLE 0x00800000
+#define TG3_FLAG_10_100_ONLY 0x01000000
+#define TG3_FLAG_PAUSE_AUTONEG 0x02000000
+#define TG3_FLAG_PAUSE_RX 0x04000000
+#define TG3_FLAG_PAUSE_TX 0x08000000
+#define TG3_FLAG_BROKEN_CHECKSUMS 0x10000000
+#define TG3_FLAG_GOT_SERDES_FLOWCTL 0x20000000
+#define TG3_FLAG_SPLIT_MODE 0x40000000
+#define TG3_FLAG_INIT_COMPLETE 0x80000000
+
+ uint32_t tg3_flags2;
+#define TG3_FLG2_RESTART_TIMER 0x00000001
+#define TG3_FLG2_SUN_5704 0x00000002
+#define TG3_FLG2_NO_ETH_WIRE_SPEED 0x00000004
+#define TG3_FLG2_IS_5788 0x00000008
+#define TG3_FLG2_MAX_RXPEND_64 0x00000010
+#define TG3_FLG2_TSO_CAPABLE 0x00000020
+ // Alf: Hope I'm not breaking anything here !
+#define TG3_FLG2_PCI_EXPRESS 0x00000040
+
+
+
+ uint32_t split_mode_max_reqs;
+#define SPLIT_MODE_5704_MAX_REQ 3
+
+#if 0
+ struct timer_list timer;
+ uint16_t timer_counter;
+ uint16_t timer_multiplier;
+ uint32_t timer_offset;
+ uint16_t asf_counter;
+ uint16_t asf_multiplier;
+#endif
+
+ struct tg3_link_config link_config;
+ struct tg3_bufmgr_config bufmgr_config;
+
+#if 0
+ uint32_t rx_pending;
+ uint32_t rx_jumbo_pending;
+ uint32_t tx_pending;
+#endif
+
+ /* cache h/w values, often passed straight to h/w */
+ uint32_t rx_mode;
+ uint32_t tx_mode;
+ uint32_t mac_mode;
+ uint32_t mi_mode;
+ uint32_t misc_host_ctrl;
+ uint32_t grc_mode;
+ uint32_t grc_local_ctrl;
+ uint32_t dma_rwctrl;
+#if 0
+ uint32_t coalesce_mode;
+#endif
+
+ /* PCI block */
+ uint16_t pci_chip_rev_id;
+#if 0
+ uint8_t pci_cacheline_sz;
+ uint8_t pci_lat_timer;
+ uint8_t pci_hdr_type;
+ uint8_t pci_bist;
+#endif
+ uint32_t pci_cfg_state[64 / sizeof(uint32_t)];
+
+ int pm_cap;
+
+ /* PHY info */
+ uint32_t phy_id;
+#define PHY_ID_MASK 0xfffffff0
+#define PHY_ID_BCM5400 0x60008040
+#define PHY_ID_BCM5401 0x60008050
+#define PHY_ID_BCM5411 0x60008070
+#define PHY_ID_BCM5701 0x60008110
+#define PHY_ID_BCM5703 0x60008160
+#define PHY_ID_BCM5704 0x60008190
+#define PHY_ID_BCM5705 0x600081a0
+#define PHY_ID_BCM5750 0x60008180
+#define PHY_ID_BCM8002 0x60010140
+#define PHY_ID_BCM5751 0x00206180
+#define PHY_ID_SERDES 0xfeedbee0
+#define PHY_ID_INVALID 0xffffffff
+#define PHY_ID_REV_MASK 0x0000000f
+#define PHY_REV_BCM5401_B0 0x1
+#define PHY_REV_BCM5401_B2 0x3
+#define PHY_REV_BCM5401_C0 0x6
+#define PHY_REV_BCM5411_X0 0x1 /* Found on Netgear GA302T */
+
+ enum phy_led_mode led_mode;
+
+ char board_part_number[24];
+ uint32_t nic_sram_data_cfg;
+ uint32_t pci_clock_ctrl;
+#if 0
+ struct pci_device *pdev_peer;
+#endif
+
+ /* This macro assumes the passed PHY ID is already masked
+ * with PHY_ID_MASK.
+ */
+#define KNOWN_PHY_ID(X) \
+ ((X) == PHY_ID_BCM5400 || (X) == PHY_ID_BCM5401 || \
+ (X) == PHY_ID_BCM5411 || (X) == PHY_ID_BCM5701 || \
+ (X) == PHY_ID_BCM5703 || (X) == PHY_ID_BCM5704 || \
+ (X) == PHY_ID_BCM5705 || (X) == PHY_ID_BCM5751 || \
+ (X) == PHY_ID_BCM8002 || (X) == PHY_ID_SERDES)
+
+ unsigned long regs;
+ struct pci_device *pdev;
+ struct nic *nic;
+#if 0
+ struct net_device *dev;
+#endif
+#if TG3_VLAN_TAG_USED
+ struct vlan_group *vlgrp;
+#endif
+
+ struct tg3_rx_buffer_desc *rx_std;
+#if 0
+ struct ring_info *rx_std_buffers;
+ dma_addr_t rx_std_mapping;
+ struct tg3_rx_buffer_desc *rx_jumbo;
+ struct ring_info *rx_jumbo_buffers;
+ dma_addr_t rx_jumbo_mapping;
+#endif
+
+ struct tg3_rx_buffer_desc *rx_rcb;
+#if 0
+ dma_addr_t rx_rcb_mapping;
+#endif
+
+ /* TX descs are only used if TG3_FLAG_HOST_TXDS is set. */
+ struct tg3_tx_buffer_desc *tx_ring;
+#if 0
+ struct tx_ring_info *tx_buffers;
+ dma_addr_t tx_desc_mapping;
+#endif
+
+ struct tg3_hw_status *hw_status;
+#if 0
+ dma_addr_t status_mapping;
+#endif
+#if 0
+ uint32_t msg_enable;
+#endif
+
+ struct tg3_hw_stats *hw_stats;
+#if 0
+ dma_addr_t stats_mapping;
+#endif
+
+ int carrier_ok;
+ uint16_t subsystem_vendor;
+ uint16_t subsystem_device;
+};
+
+#endif /* !(_T3_H) */
diff --git a/src/drivers/net/tlan.c b/src/drivers/net/tlan.c
new file mode 100644
index 00000000..0f216ed4
--- /dev/null
+++ b/src/drivers/net/tlan.c
@@ -0,0 +1,1722 @@
+/**************************************************************************
+*
+* tlan.c -- Etherboot device driver for the Texas Instruments ThunderLAN
+* Written 2003-2003 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code based on:
+* lan.c: Linux ThunderLan Driver:
+*
+* by James Banks
+*
+* (C) 1997-1998 Caldera, Inc.
+* (C) 1998 James Banks
+* (C) 1999-2001 Torben Mathiasen
+* (C) 2002 Samuel Chessman
+*
+* REVISION HISTORY:
+* ================
+* v1.0 07-08-2003 timlegge Initial not quite working version
+* v1.1 07-27-2003 timlegge Sync 5.0 and 5.1 versions
+* v1.2 08-19-2003 timlegge Implement Multicast Support
+* v1.3 08-23-2003 timlegge Fix the transmit Function
+* v1.4 01-17-2004 timlegge Initial driver output cleanup
+*
+* Indent Options: indent -kr -i8
+***************************************************************************/
+
+/* to get some global routines like printf */
+#include "etherboot.h"
+/* to get the interface to the body of the program */
+#include "nic.h"
+/* to get the PCI support functions, if this is a PCI NIC */
+#include "pci.h"
+#include "timer.h"
+#include "tlan.h"
+
+#define drv_version "v1.4"
+#define drv_date "01-17-2004"
+
+/* NIC specific static variables go here */
+#define HZ 100
+#define TX_TIME_OUT (6*HZ)
+
+/* Condensed operations for readability. */
+#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+//#define EDEBUG
+#ifdef EDEBUG
+#define dprintf(x) printf x
+#else
+#define dprintf(x)
+#endif
+
+static void TLan_ResetLists(struct nic *nic __unused);
+static void TLan_ResetAdapter(struct nic *nic __unused);
+static void TLan_FinishReset(struct nic *nic __unused);
+
+static void TLan_EeSendStart(u16);
+static int TLan_EeSendByte(u16, u8, int);
+static void TLan_EeReceiveByte(u16, u8 *, int);
+static int TLan_EeReadByte(u16 io_base, u8, u8 *);
+
+static void TLan_PhyDetect(struct nic *nic);
+static void TLan_PhyPowerDown(struct nic *nic);
+static void TLan_PhyPowerUp(struct nic *nic);
+
+
+static void TLan_SetMac(struct nic *nic __unused, int areg, char *mac);
+
+static void TLan_PhyReset(struct nic *nic);
+static void TLan_PhyStartLink(struct nic *nic);
+static void TLan_PhyFinishAutoNeg(struct nic *nic);
+
+#ifdef MONITOR
+static void TLan_PhyMonitor(struct nic *nic);
+#endif
+
+
+static void refill_rx(struct nic *nic __unused);
+
+static int TLan_MiiReadReg(struct nic *nic __unused, u16, u16, u16 *);
+static void TLan_MiiSendData(u16, u32, unsigned);
+static void TLan_MiiSync(u16);
+static void TLan_MiiWriteReg(struct nic *nic __unused, u16, u16, u16);
+
+
+const char *media[] = {
+ "10BaseT-HD ", "10BaseT-FD ", "100baseTx-HD ",
+ "100baseTx-FD", "100baseT4", 0
+};
+
+/* This much match tlan_pci_tbl[]! */
+enum tlan_nics {
+ NETEL10 = 0, NETEL100 = 1, NETFLEX3I = 2, THUNDER = 3, NETFLEX3B =
+ 4, NETEL100PI = 5,
+ NETEL100D = 6, NETEL100I = 7, OC2183 = 8, OC2325 = 9, OC2326 =
+ 10, NETELLIGENT_10_100_WS_5100 = 11,
+ NETELLIGENT_10_T2 = 12
+};
+
+struct pci_id_info {
+ const char *name;
+ int nic_id;
+ struct match_info {
+ u32 pci, pci_mask, subsystem, subsystem_mask;
+ u32 revision, revision_mask; /* Only 8 bits. */
+ } id;
+ u32 flags;
+ u16 addrOfs; /* Address Offset */
+};
+
+static struct pci_id_info tlan_pci_tbl[] = {
+ {"Compaq Netelligent 10 T PCI UTP", NETEL10,
+ {0xae340e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_ACTIVITY_LED, 0x83},
+ {"Compaq Netelligent 10/100 TX PCI UTP", NETEL100,
+ {0xae320e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_ACTIVITY_LED, 0x83},
+ {"Compaq Integrated NetFlex-3/P", NETFLEX3I,
+ {0xae350e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_NONE, 0x83},
+ {"Compaq NetFlex-3/P", THUNDER,
+ {0xf1300e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_UNMANAGED_PHY | TLAN_ADAPTER_BIT_RATE_PHY, 0x83},
+ {"Compaq NetFlex-3/P", NETFLEX3B,
+ {0xf1500e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_NONE, 0x83},
+ {"Compaq Netelligent Integrated 10/100 TX UTP", NETEL100PI,
+ {0xae430e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_ACTIVITY_LED, 0x83},
+ {"Compaq Netelligent Dual 10/100 TX PCI UTP", NETEL100D,
+ {0xae400e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_NONE, 0x83},
+ {"Compaq Netelligent 10/100 TX Embedded UTP", NETEL100I,
+ {0xb0110e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_NONE, 0x83},
+ {"Olicom OC-2183/2185", OC2183,
+ {0x0013108d, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_USE_INTERN_10, 0x83},
+ {"Olicom OC-2325", OC2325,
+ {0x0012108d, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_UNMANAGED_PHY, 0xF8},
+ {"Olicom OC-2326", OC2326,
+ {0x0014108d, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_USE_INTERN_10, 0xF8},
+ {"Compaq Netelligent 10/100 TX UTP", NETELLIGENT_10_100_WS_5100,
+ {0xb0300e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_ACTIVITY_LED, 0x83},
+ {"Compaq Netelligent 10 T/2 PCI UTP/Coax", NETELLIGENT_10_T2,
+ {0xb0120e11, 0xffffffff, 0, 0, 0, 0},
+ TLAN_ADAPTER_NONE, 0x83},
+ {"Compaq NetFlex-3/E", 0, /* EISA card */
+ {0, 0, 0, 0, 0, 0},
+ TLAN_ADAPTER_ACTIVITY_LED | TLAN_ADAPTER_UNMANAGED_PHY |
+ TLAN_ADAPTER_BIT_RATE_PHY, 0x83},
+ {"Compaq NetFlex-3/E", 0, /* EISA card */
+ {0, 0, 0, 0, 0, 0},
+ TLAN_ADAPTER_ACTIVITY_LED, 0x83},
+ {0, 0,
+ {0, 0, 0, 0, 0, 0},
+ 0, 0},
+};
+
+struct TLanList {
+ u32 forward;
+ u16 cStat;
+ u16 frameSize;
+ struct {
+ u32 count;
+ u32 address;
+ } buffer[TLAN_BUFFERS_PER_LIST];
+};
+
+struct TLanList tx_ring[TLAN_NUM_TX_LISTS];
+static unsigned char txb[TLAN_MAX_FRAME_SIZE * TLAN_NUM_TX_LISTS];
+
+struct TLanList rx_ring[TLAN_NUM_RX_LISTS];
+static unsigned char rxb[TLAN_MAX_FRAME_SIZE * TLAN_NUM_RX_LISTS];
+
+typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE];
+
+int chip_idx;
+
+/*****************************************************************
+* TLAN Private Information Structure
+*
+****************************************************************/
+struct tlan_private {
+ unsigned short vendor_id; /* PCI Vendor code */
+ unsigned short dev_id; /* PCI Device code */
+ const char *nic_name;
+ unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indicies */
+ unsigned rx_buf_sz; /* Based on mtu + Slack */
+ struct TLanList *txList;
+ u32 txHead;
+ u32 txInProgress;
+ u32 txTail;
+ int eoc;
+ u32 phyOnline;
+ u32 aui;
+ u32 duplex;
+ u32 phy[2];
+ u32 phyNum;
+ u32 speed;
+ u8 tlanRev;
+ u8 tlanFullDuplex;
+ u8 link;
+ u8 neg_be_verbose;
+} TLanPrivateInfo;
+
+static struct tlan_private *priv;
+
+u32 BASE;
+
+/***************************************************************
+* TLan_ResetLists
+*
+* Returns:
+* Nothing
+* Parms:
+* dev The device structure with the list
+* stuctures to be reset.
+*
+* This routine sets the variables associated with managing
+* the TLAN lists to their initial values.
+*
+**************************************************************/
+
+void TLan_ResetLists(struct nic *nic __unused)
+{
+
+ int i;
+ struct TLanList *list;
+ priv->txHead = 0;
+ priv->txTail = 0;
+
+ for (i = 0; i < TLAN_NUM_TX_LISTS; i++) {
+ list = &tx_ring[i];
+ list->cStat = TLAN_CSTAT_UNUSED;
+ list->buffer[0].address = virt_to_bus(txb +
+ (i * TLAN_MAX_FRAME_SIZE));
+ list->buffer[2].count = 0;
+ list->buffer[2].address = 0;
+ list->buffer[9].address = 0;
+ }
+
+ priv->cur_rx = 0;
+ priv->rx_buf_sz = (TLAN_MAX_FRAME_SIZE);
+// priv->rx_head_desc = &rx_ring[0];
+
+ /* Initialize all the Rx descriptors */
+ for (i = 0; i < TLAN_NUM_RX_LISTS; i++) {
+ rx_ring[i].forward = virt_to_le32desc(&rx_ring[i + 1]);
+ rx_ring[i].cStat = TLAN_CSTAT_READY;
+ rx_ring[i].frameSize = TLAN_MAX_FRAME_SIZE;
+ rx_ring[i].buffer[0].count =
+ TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
+ rx_ring[i].buffer[0].address =
+ virt_to_le32desc(&rxb[i * TLAN_MAX_FRAME_SIZE]);
+ rx_ring[i].buffer[1].count = 0;
+ rx_ring[i].buffer[1].address = 0;
+ }
+
+ /* Mark the last entry as wrapping the ring */
+ rx_ring[i - 1].forward = virt_to_le32desc(&rx_ring[0]);
+ priv->dirty_rx = (unsigned int) (i - TLAN_NUM_RX_LISTS);
+
+} /* TLan_ResetLists */
+
+/***************************************************************
+* TLan_Reset
+*
+* Returns:
+* 0
+* Parms:
+* dev Pointer to device structure of adapter
+* to be reset.
+*
+* This function resets the adapter and it's physical
+* device. See Chap. 3, pp. 9-10 of the "ThunderLAN
+* Programmer's Guide" for details. The routine tries to
+* implement what is detailed there, though adjustments
+* have been made.
+*
+**************************************************************/
+
+void TLan_ResetAdapter(struct nic *nic __unused)
+{
+ int i;
+ u32 addr;
+ u32 data;
+ u8 data8;
+
+ priv->tlanFullDuplex = FALSE;
+ priv->phyOnline = 0;
+/* 1. Assert reset bit. */
+
+ data = inl(BASE + TLAN_HOST_CMD);
+ data |= TLAN_HC_AD_RST;
+ outl(data, BASE + TLAN_HOST_CMD);
+
+ udelay(1000);
+
+/* 2. Turn off interrupts. ( Probably isn't necessary ) */
+
+ data = inl(BASE + TLAN_HOST_CMD);
+ data |= TLAN_HC_INT_OFF;
+ outl(data, BASE + TLAN_HOST_CMD);
+/* 3. Clear AREGs and HASHs. */
+
+ for (i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4) {
+ TLan_DioWrite32(BASE, (u16) i, 0);
+ }
+
+/* 4. Setup NetConfig register. */
+
+ data =
+ TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
+ TLan_DioWrite16(BASE, TLAN_NET_CONFIG, (u16) data);
+
+/* 5. Load Ld_Tmr and Ld_Thr in HOST_CMD. */
+
+ outl(TLAN_HC_LD_TMR | 0x3f, BASE + TLAN_HOST_CMD);
+ outl(TLAN_HC_LD_THR | 0x0, BASE + TLAN_HOST_CMD);
+
+/* 6. Unreset the MII by setting NMRST (in NetSio) to 1. */
+
+ outw(TLAN_NET_SIO, BASE + TLAN_DIO_ADR);
+ addr = BASE + TLAN_DIO_DATA + TLAN_NET_SIO;
+ TLan_SetBit(TLAN_NET_SIO_NMRST, addr);
+
+/* 7. Setup the remaining registers. */
+
+ if (priv->tlanRev >= 0x30) {
+ data8 = TLAN_ID_TX_EOC | TLAN_ID_RX_EOC;
+ TLan_DioWrite8(BASE, TLAN_INT_DIS, data8);
+ }
+ TLan_PhyDetect(nic);
+ data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN;
+
+ if (tlan_pci_tbl[chip_idx].flags & TLAN_ADAPTER_BIT_RATE_PHY) {
+ data |= TLAN_NET_CFG_BIT;
+ if (priv->aui == 1) {
+ TLan_DioWrite8(BASE, TLAN_ACOMMIT, 0x0a);
+ } else if (priv->duplex == TLAN_DUPLEX_FULL) {
+ TLan_DioWrite8(BASE, TLAN_ACOMMIT, 0x00);
+ priv->tlanFullDuplex = TRUE;
+ } else {
+ TLan_DioWrite8(BASE, TLAN_ACOMMIT, 0x08);
+ }
+ }
+
+ if (priv->phyNum == 0) {
+ data |= TLAN_NET_CFG_PHY_EN;
+ }
+ TLan_DioWrite16(BASE, TLAN_NET_CONFIG, (u16) data);
+
+ if (tlan_pci_tbl[chip_idx].flags & TLAN_ADAPTER_UNMANAGED_PHY) {
+ TLan_FinishReset(nic);
+ } else {
+ TLan_PhyPowerDown(nic);
+ }
+
+} /* TLan_ResetAdapter */
+
+void TLan_FinishReset(struct nic *nic)
+{
+
+ u8 data;
+ u32 phy;
+ u8 sio;
+ u16 status;
+ u16 partner;
+ u16 tlphy_ctl;
+ u16 tlphy_par;
+ u16 tlphy_id1, tlphy_id2;
+ int i;
+
+ phy = priv->phy[priv->phyNum];
+
+ data = TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP;
+ if (priv->tlanFullDuplex) {
+ data |= TLAN_NET_CMD_DUPLEX;
+ }
+ TLan_DioWrite8(BASE, TLAN_NET_CMD, data);
+ data = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5;
+ if (priv->phyNum == 0) {
+ data |= TLAN_NET_MASK_MASK7;
+ }
+ TLan_DioWrite8(BASE, TLAN_NET_MASK, data);
+ TLan_DioWrite16(BASE, TLAN_MAX_RX, ((1536) + 7) & ~7);
+ TLan_MiiReadReg(nic, phy, MII_GEN_ID_HI, &tlphy_id1);
+ TLan_MiiReadReg(nic, phy, MII_GEN_ID_LO, &tlphy_id2);
+
+ if ((tlan_pci_tbl[chip_idx].flags & TLAN_ADAPTER_UNMANAGED_PHY)
+ || (priv->aui)) {
+ status = MII_GS_LINK;
+ dprintf(("TLAN: %s: Link forced.\n", priv->nic_name));
+ } else {
+ TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status);
+ udelay(1000);
+ TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status);
+ if ((status & MII_GS_LINK) && /* We only support link info on Nat.Sem. PHY's */
+ (tlphy_id1 == NAT_SEM_ID1)
+ && (tlphy_id2 == NAT_SEM_ID2)) {
+ TLan_MiiReadReg(nic, phy, MII_AN_LPA, &partner);
+ TLan_MiiReadReg(nic, phy, TLAN_TLPHY_PAR,
+ &tlphy_par);
+
+ dprintf(("TLAN: %s: Link active with ",
+ priv->nic_name));
+ if (!(tlphy_par & TLAN_PHY_AN_EN_STAT)) {
+ dprintf(("forced 10%sMbps %s-Duplex\n",
+ tlphy_par & TLAN_PHY_SPEED_100 ? ""
+ : "0",
+ tlphy_par & TLAN_PHY_DUPLEX_FULL ?
+ "Full" : "Half"));
+ } else {
+ dprintf
+ (("AutoNegotiation enabled, at 10%sMbps %s-Duplex\n",
+ tlphy_par & TLAN_PHY_SPEED_100 ? "" :
+ "0",
+ tlphy_par & TLAN_PHY_DUPLEX_FULL ?
+ "Full" : "Half"));
+ dprintf(("TLAN: Partner capability: "));
+ for (i = 5; i <= 10; i++)
+ if (partner & (1 << i))
+ dprintf(("%s", media[i - 5]));
+ dprintf(("\n"));
+ }
+
+ TLan_DioWrite8(BASE, TLAN_LED_REG, TLAN_LED_LINK);
+#ifdef MONITOR
+ /* We have link beat..for now anyway */
+ priv->link = 1;
+ /*Enabling link beat monitoring */
+ /* TLan_SetTimer( nic, (10*HZ), TLAN_TIMER_LINK_BEAT ); */
+ mdelay(10000);
+ TLan_PhyMonitor(nic);
+#endif
+ } else if (status & MII_GS_LINK) {
+ dprintf(("TLAN: %s: Link active\n", priv->nic_name));
+ TLan_DioWrite8(BASE, TLAN_LED_REG, TLAN_LED_LINK);
+ }
+ }
+
+ if (priv->phyNum == 0) {
+ TLan_MiiReadReg(nic, phy, TLAN_TLPHY_CTL, &tlphy_ctl);
+ tlphy_ctl |= TLAN_TC_INTEN;
+ TLan_MiiWriteReg(nic, phy, TLAN_TLPHY_CTL, tlphy_ctl);
+ sio = TLan_DioRead8(BASE, TLAN_NET_SIO);
+ sio |= TLAN_NET_SIO_MINTEN;
+ TLan_DioWrite8(BASE, TLAN_NET_SIO, sio);
+ }
+
+ if (status & MII_GS_LINK) {
+ TLan_SetMac(nic, 0, nic->node_addr);
+ priv->phyOnline = 1;
+ outb((TLAN_HC_INT_ON >> 8), BASE + TLAN_HOST_CMD + 1);
+ outl(virt_to_bus(&rx_ring), BASE + TLAN_CH_PARM);
+ outl(TLAN_HC_GO | TLAN_HC_RT, BASE + TLAN_HOST_CMD);
+ } else {
+ dprintf
+ (("TLAN: %s: Link inactive, will retry in 10 secs...\n",
+ priv->nic_name));
+ /* TLan_SetTimer( nic, (10*HZ), TLAN_TIMER_FINISH_RESET ); */
+ mdelay(10000);
+ TLan_FinishReset(nic);
+ return;
+
+ }
+
+} /* TLan_FinishReset */
+
+/**************************************************************************
+POLL - Wait for a frame
+***************************************************************************/
+static int tlan_poll(struct nic *nic, int retrieve)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+ u32 framesize;
+ u32 host_cmd = 0;
+ u32 ack = 1;
+ int eoc = 0;
+ int entry = priv->cur_rx % TLAN_NUM_RX_LISTS;
+ u16 tmpCStat = le32_to_cpu(rx_ring[entry].cStat);
+ u16 host_int = inw(BASE + TLAN_HOST_INT);
+
+ if ((tmpCStat & TLAN_CSTAT_FRM_CMP) && !retrieve)
+ return 1;
+
+ outw(host_int, BASE + TLAN_HOST_INT);
+
+ if (!(tmpCStat & TLAN_CSTAT_FRM_CMP))
+ return 0;
+
+ /* printf("PI-1: 0x%hX\n", host_int); */
+ if (tmpCStat & TLAN_CSTAT_EOC)
+ eoc = 1;
+
+ framesize = rx_ring[entry].frameSize;
+
+ nic->packetlen = framesize;
+
+ dprintf((".%d.", framesize));
+
+ memcpy(nic->packet, rxb +
+ (priv->cur_rx * TLAN_MAX_FRAME_SIZE), nic->packetlen);
+
+ rx_ring[entry].cStat = 0;
+
+ dprintf(("%d", entry));
+
+ entry = (entry + 1) % TLAN_NUM_RX_LISTS;
+ priv->cur_rx = entry;
+ if (eoc) {
+ if ((rx_ring[entry].cStat & TLAN_CSTAT_READY) ==
+ TLAN_CSTAT_READY) {
+ ack |= TLAN_HC_GO | TLAN_HC_RT;
+ host_cmd = TLAN_HC_ACK | ack | 0x001C0000;
+ outl(host_cmd, BASE + TLAN_HOST_CMD);
+ }
+ } else {
+ host_cmd = TLAN_HC_ACK | ack | (0x000C0000);
+ outl(host_cmd, BASE + TLAN_HOST_CMD);
+
+ dprintf(("AC: 0x%hX\n", inw(BASE + TLAN_CH_PARM)));
+ dprintf(("PI-2: 0x%hX\n", inw(BASE + TLAN_HOST_INT)));
+ }
+ refill_rx(nic);
+ return (1); /* initially as this is called to flush the input */
+}
+
+static void refill_rx(struct nic *nic __unused)
+{
+ int entry = 0;
+
+ for (;
+ (priv->cur_rx - priv->dirty_rx +
+ TLAN_NUM_RX_LISTS) % TLAN_NUM_RX_LISTS > 0;
+ priv->dirty_rx = (priv->dirty_rx + 1) % TLAN_NUM_RX_LISTS) {
+ entry = priv->dirty_rx % TLAN_NUM_TX_LISTS;
+ rx_ring[entry].frameSize = TLAN_MAX_FRAME_SIZE;
+ rx_ring[entry].cStat = TLAN_CSTAT_READY;
+ }
+
+}
+
+/**************************************************************************
+TRANSMIT - Transmit a frame
+***************************************************************************/
+static void tlan_transmit(struct nic *nic, const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p)
+{ /* Packet */
+ u16 nstype;
+ u32 to;
+ struct TLanList *tail_list;
+ struct TLanList *head_list;
+ u8 *tail_buffer;
+ u32 ack = 0;
+ u32 host_cmd;
+ int eoc = 0;
+ u16 tmpCStat;
+#ifdef EBDEBUG
+ u16 host_int = inw(BASE + TLAN_HOST_INT);
+#endif
+ int entry = 0;
+
+ dprintf(("INT0-0x%hX\n", host_int));
+
+ if (!priv->phyOnline) {
+ printf("TRANSMIT: %s PHY is not ready\n", priv->nic_name);
+ return;
+ }
+
+ tail_list = priv->txList + priv->txTail;
+
+ if (tail_list->cStat != TLAN_CSTAT_UNUSED) {
+ printf("TRANSMIT: %s is busy (Head=%d Tail=%d)\n",
+ priv->nic_name, priv->txList, priv->txTail);
+ tx_ring[entry].cStat = TLAN_CSTAT_UNUSED;
+// priv->txBusyCount++;
+ return;
+ }
+
+ tail_list->forward = 0;
+
+ tail_buffer = txb + (priv->txTail * TLAN_MAX_FRAME_SIZE);
+
+ /* send the packet to destination */
+ memcpy(tail_buffer, d, ETH_ALEN);
+ memcpy(tail_buffer + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ nstype = htons((u16) t);
+ memcpy(tail_buffer + 2 * ETH_ALEN, (u8 *) & nstype, 2);
+ memcpy(tail_buffer + ETH_HLEN, p, s);
+
+ s += ETH_HLEN;
+ s &= 0x0FFF;
+ while (s < ETH_ZLEN)
+ tail_buffer[s++] = '\0';
+
+ /*=====================================================*/
+ /* Receive
+ * 0000 0000 0001 1100
+ * 0000 0000 0000 1100
+ * 0000 0000 0000 0011 = 0x0003
+ *
+ * 0000 0000 0000 0000 0000 0000 0000 0011
+ * 0000 0000 0000 1100 0000 0000 0000 0000 = 0x000C0000
+ *
+ * Transmit
+ * 0000 0000 0001 1100
+ * 0000 0000 0000 0100
+ * 0000 0000 0000 0001 = 0x0001
+ *
+ * 0000 0000 0000 0000 0000 0000 0000 0001
+ * 0000 0000 0000 0100 0000 0000 0000 0000 = 0x00040000
+ * */
+
+ /* Setup the transmit descriptor */
+ tail_list->frameSize = (u16) s;
+ tail_list->buffer[0].count = TLAN_LAST_BUFFER | (u32) s;
+ tail_list->buffer[1].count = 0;
+ tail_list->buffer[1].address = 0;
+
+ tail_list->cStat = TLAN_CSTAT_READY;
+
+ dprintf(("INT1-0x%hX\n", inw(BASE + TLAN_HOST_INT)));
+
+ if (!priv->txInProgress) {
+ priv->txInProgress = 1;
+ outl(virt_to_le32desc(tail_list), BASE + TLAN_CH_PARM);
+ outl(TLAN_HC_GO, BASE + TLAN_HOST_CMD);
+ } else {
+ if (priv->txTail == 0) {
+ dprintf(("Out buffer\n"));
+ (priv->txList + (TLAN_NUM_TX_LISTS - 1))->forward =
+ virt_to_le32desc(tail_list);
+ } else {
+ dprintf(("Fix this \n"));
+ (priv->txList + (priv->txTail - 1))->forward =
+ virt_to_le32desc(tail_list);
+ }
+ }
+
+ CIRC_INC(priv->txTail, TLAN_NUM_TX_LISTS);
+
+ dprintf(("INT2-0x%hX\n", inw(BASE + TLAN_HOST_INT)));
+
+ to = currticks() + TX_TIME_OUT;
+ while ((tail_list->cStat == TLAN_CSTAT_READY) && currticks() < to);
+
+ head_list = priv->txList + priv->txHead;
+ while (((tmpCStat = head_list->cStat) & TLAN_CSTAT_FRM_CMP)
+ && (ack < 255)) {
+ ack++;
+ if(tmpCStat & TLAN_CSTAT_EOC)
+ eoc =1;
+ head_list->cStat = TLAN_CSTAT_UNUSED;
+ CIRC_INC(priv->txHead, TLAN_NUM_TX_LISTS);
+ head_list = priv->txList + priv->txHead;
+
+ }
+ if(!ack)
+ printf("Incomplete TX Frame\n");
+
+ if(eoc) {
+ head_list = priv->txList + priv->txHead;
+ if ((head_list->cStat & TLAN_CSTAT_READY) == TLAN_CSTAT_READY) {
+ outl(virt_to_le32desc(head_list), BASE + TLAN_CH_PARM);
+ ack |= TLAN_HC_GO;
+ } else {
+ priv->txInProgress = 0;
+ }
+ }
+ if(ack) {
+ host_cmd = TLAN_HC_ACK | ack;
+ outl(host_cmd, BASE + TLAN_HOST_CMD);
+ }
+
+ if(priv->tlanRev < 0x30 ) {
+ ack = 1;
+ head_list = priv->txList + priv->txHead;
+ if ((head_list->cStat & TLAN_CSTAT_READY) == TLAN_CSTAT_READY) {
+ outl(virt_to_le32desc(head_list), BASE + TLAN_CH_PARM);
+ ack |= TLAN_HC_GO;
+ } else {
+ priv->txInProgress = 0;
+ }
+ host_cmd = TLAN_HC_ACK | ack | 0x00140000;
+ outl(host_cmd, BASE + TLAN_HOST_CMD);
+
+ }
+
+ if (currticks() >= to) {
+ printf("TX Time Out");
+ }
+}
+
+/**************************************************************************
+DISABLE - Turn off ethernet interface
+***************************************************************************/
+static void tlan_disable(struct dev *dev __unused)
+{
+ /* put the card in its initial state */
+ /* This function serves 3 purposes.
+ * This disables DMA and interrupts so we don't receive
+ * unexpected packets or interrupts from the card after
+ * etherboot has finished.
+ * This frees resources so etherboot may use
+ * this driver on another interface
+ * This allows etherboot to reinitialize the interface
+ * if something is something goes wrong.
+ *
+ */
+ outl(TLAN_HC_AD_RST, BASE + TLAN_HOST_CMD);
+}
+
+/**************************************************************************
+IRQ - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void tlan_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+static void TLan_SetMulticastList(struct nic *nic) {
+ int i;
+ u8 tmp;
+
+ /* !IFF_PROMISC */
+ tmp = TLan_DioRead8(BASE, TLAN_NET_CMD);
+ TLan_DioWrite8(BASE, TLAN_NET_CMD, tmp & ~TLAN_NET_CMD_CAF);
+
+ /* IFF_ALLMULTI */
+ for(i = 0; i< 3; i++)
+ TLan_SetMac(nic, i + 1, NULL);
+ TLan_DioWrite32(BASE, TLAN_HASH_1, 0xFFFFFFFF);
+ TLan_DioWrite32(BASE, TLAN_HASH_2, 0xFFFFFFFF);
+
+
+}
+/**************************************************************************
+PROBE - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+
+#define board_found 1
+#define valid_link 0
+static int tlan_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *) dev;
+ u16 data = 0;
+ int err;
+ int i;
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ nic->irqno = 0;
+ nic->ioaddr = pci->ioaddr & ~3;
+
+ BASE = pci->ioaddr;
+
+ printf("tlan.c: Found %s, Vendor 0x%hX, Device 0x%hX\n",
+ pci->name, pci->vendor, pci->dev_id);
+
+ /* Set nic as PCI bus master */
+ adjust_pci_device(pci);
+
+ /* Point to private storage */
+ priv = &TLanPrivateInfo;
+
+ /* Figure out which chip we're dealing with */
+ i = 0;
+ chip_idx = -1;
+ while (tlan_pci_tbl[i].name) {
+ if ((((u32) pci->dev_id << 16) | pci->vendor) ==
+ (tlan_pci_tbl[i].id.pci & 0xffffffff)) {
+ chip_idx = i;
+ break;
+ }
+ i++;
+ }
+
+ priv->vendor_id = pci->vendor;
+ priv->dev_id = pci->dev_id;
+ priv->nic_name = pci->name;
+ priv->eoc = 0;
+
+ err = 0;
+ for (i = 0; i < 6; i++)
+ err |= TLan_EeReadByte(BASE,
+ (u8) tlan_pci_tbl[chip_idx].
+ addrOfs + i,
+ (u8 *) & nic->node_addr[i]);
+ if (err) {
+ printf("TLAN: %s: Error reading MAC from eeprom: %d\n",
+ pci->name, err);
+ } else
+ /* Print out some hardware info */
+ printf("%s: %! at ioaddr %hX, ",
+ pci->name, nic->node_addr, pci->ioaddr);
+
+ priv->tlanRev = TLan_DioRead8(BASE, TLAN_DEF_REVISION);
+ printf("revision: 0x%hX\n", priv->tlanRev);
+
+ TLan_ResetLists(nic);
+ TLan_ResetAdapter(nic);
+
+ data = inl(BASE + TLAN_HOST_CMD);
+ data |= TLAN_HC_INT_OFF;
+ outw(data, BASE + TLAN_HOST_CMD);
+
+ TLan_SetMulticastList(nic);
+ udelay(100);
+ priv->txList = tx_ring;
+
+/* if (board_found && valid_link)
+ {*/
+ /* point to NIC specific routines */
+
+ dev->disable = tlan_disable;
+ nic->poll = tlan_poll;
+ nic->transmit = tlan_transmit;
+ nic->irq = tlan_irq;
+ return 1;
+}
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver Eeprom routines
+
+ The Compaq Netelligent 10 and 10/100 cards use a Microchip 24C02A
+ EEPROM. These functions are based on information in Microchip's
+ data sheet. I don't know how well this functions will work with
+ other EEPROMs.
+
+******************************************************************************
+*****************************************************************************/
+
+
+/***************************************************************
+* TLan_EeSendStart
+*
+* Returns:
+* Nothing
+* Parms:
+* io_base The IO port base address for the
+* TLAN device with the EEPROM to
+* use.
+*
+* This function sends a start cycle to an EEPROM attached
+* to a TLAN chip.
+*
+**************************************************************/
+
+void TLan_EeSendStart(u16 io_base)
+{
+ u16 sio;
+
+ outw(TLAN_NET_SIO, io_base + TLAN_DIO_ADR);
+ sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ TLan_SetBit(TLAN_NET_SIO_ECLOK, sio);
+ TLan_SetBit(TLAN_NET_SIO_EDATA, sio);
+ TLan_SetBit(TLAN_NET_SIO_ETXEN, sio);
+ TLan_ClearBit(TLAN_NET_SIO_EDATA, sio);
+ TLan_ClearBit(TLAN_NET_SIO_ECLOK, sio);
+
+} /* TLan_EeSendStart */
+
+/***************************************************************
+* TLan_EeSendByte
+*
+* Returns:
+* If the correct ack was received, 0, otherwise 1
+* Parms: io_base The IO port base address for the
+* TLAN device with the EEPROM to
+* use.
+* data The 8 bits of information to
+* send to the EEPROM.
+* stop If TLAN_EEPROM_STOP is passed, a
+* stop cycle is sent after the
+* byte is sent after the ack is
+* read.
+*
+* This function sends a byte on the serial EEPROM line,
+* driving the clock to send each bit. The function then
+* reverses transmission direction and reads an acknowledge
+* bit.
+*
+**************************************************************/
+
+int TLan_EeSendByte(u16 io_base, u8 data, int stop)
+{
+ int err;
+ u8 place;
+ u16 sio;
+
+ outw(TLAN_NET_SIO, io_base + TLAN_DIO_ADR);
+ sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ /* Assume clock is low, tx is enabled; */
+ for (place = 0x80; place != 0; place >>= 1) {
+ if (place & data)
+ TLan_SetBit(TLAN_NET_SIO_EDATA, sio);
+ else
+ TLan_ClearBit(TLAN_NET_SIO_EDATA, sio);
+ TLan_SetBit(TLAN_NET_SIO_ECLOK, sio);
+ TLan_ClearBit(TLAN_NET_SIO_ECLOK, sio);
+ }
+ TLan_ClearBit(TLAN_NET_SIO_ETXEN, sio);
+ TLan_SetBit(TLAN_NET_SIO_ECLOK, sio);
+ err = TLan_GetBit(TLAN_NET_SIO_EDATA, sio);
+ TLan_ClearBit(TLAN_NET_SIO_ECLOK, sio);
+ TLan_SetBit(TLAN_NET_SIO_ETXEN, sio);
+
+ if ((!err) && stop) {
+ TLan_ClearBit(TLAN_NET_SIO_EDATA, sio); /* STOP, raise data while clock is high */
+ TLan_SetBit(TLAN_NET_SIO_ECLOK, sio);
+ TLan_SetBit(TLAN_NET_SIO_EDATA, sio);
+ }
+
+ return (err);
+
+} /* TLan_EeSendByte */
+
+/***************************************************************
+* TLan_EeReceiveByte
+*
+* Returns:
+* Nothing
+* Parms:
+* io_base The IO port base address for the
+* TLAN device with the EEPROM to
+* use.
+* data An address to a char to hold the
+* data sent from the EEPROM.
+* stop If TLAN_EEPROM_STOP is passed, a
+* stop cycle is sent after the
+* byte is received, and no ack is
+* sent.
+*
+* This function receives 8 bits of data from the EEPROM
+* over the serial link. It then sends and ack bit, or no
+* ack and a stop bit. This function is used to retrieve
+* data after the address of a byte in the EEPROM has been
+* sent.
+*
+**************************************************************/
+
+void TLan_EeReceiveByte(u16 io_base, u8 * data, int stop)
+{
+ u8 place;
+ u16 sio;
+
+ outw(TLAN_NET_SIO, io_base + TLAN_DIO_ADR);
+ sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+ *data = 0;
+
+ /* Assume clock is low, tx is enabled; */
+ TLan_ClearBit(TLAN_NET_SIO_ETXEN, sio);
+ for (place = 0x80; place; place >>= 1) {
+ TLan_SetBit(TLAN_NET_SIO_ECLOK, sio);
+ if (TLan_GetBit(TLAN_NET_SIO_EDATA, sio))
+ *data |= place;
+ TLan_ClearBit(TLAN_NET_SIO_ECLOK, sio);
+ }
+
+ TLan_SetBit(TLAN_NET_SIO_ETXEN, sio);
+ if (!stop) {
+ TLan_ClearBit(TLAN_NET_SIO_EDATA, sio); /* Ack = 0 */
+ TLan_SetBit(TLAN_NET_SIO_ECLOK, sio);
+ TLan_ClearBit(TLAN_NET_SIO_ECLOK, sio);
+ } else {
+ TLan_SetBit(TLAN_NET_SIO_EDATA, sio); /* No ack = 1 (?) */
+ TLan_SetBit(TLAN_NET_SIO_ECLOK, sio);
+ TLan_ClearBit(TLAN_NET_SIO_ECLOK, sio);
+ TLan_ClearBit(TLAN_NET_SIO_EDATA, sio); /* STOP, raise data while clock is high */
+ TLan_SetBit(TLAN_NET_SIO_ECLOK, sio);
+ TLan_SetBit(TLAN_NET_SIO_EDATA, sio);
+ }
+
+} /* TLan_EeReceiveByte */
+
+/***************************************************************
+* TLan_EeReadByte
+*
+* Returns:
+* No error = 0, else, the stage at which the error
+* occurred.
+* Parms:
+* io_base The IO port base address for the
+* TLAN device with the EEPROM to
+* use.
+* ee_addr The address of the byte in the
+* EEPROM whose contents are to be
+* retrieved.
+* data An address to a char to hold the
+* data obtained from the EEPROM.
+*
+* This function reads a byte of information from an byte
+* cell in the EEPROM.
+*
+**************************************************************/
+
+int TLan_EeReadByte(u16 io_base, u8 ee_addr, u8 * data)
+{
+ int err;
+ int ret = 0;
+
+
+ TLan_EeSendStart(io_base);
+ err = TLan_EeSendByte(io_base, 0xA0, TLAN_EEPROM_ACK);
+ if (err) {
+ ret = 1;
+ goto fail;
+ }
+ err = TLan_EeSendByte(io_base, ee_addr, TLAN_EEPROM_ACK);
+ if (err) {
+ ret = 2;
+ goto fail;
+ }
+ TLan_EeSendStart(io_base);
+ err = TLan_EeSendByte(io_base, 0xA1, TLAN_EEPROM_ACK);
+ if (err) {
+ ret = 3;
+ goto fail;
+ }
+ TLan_EeReceiveByte(io_base, data, TLAN_EEPROM_STOP);
+ fail:
+
+ return ret;
+
+} /* TLan_EeReadByte */
+
+
+/*****************************************************************************
+******************************************************************************
+
+ThunderLAN Driver MII Routines
+
+These routines are based on the information in Chap. 2 of the
+"ThunderLAN Programmer's Guide", pp. 15-24.
+
+******************************************************************************
+*****************************************************************************/
+
+
+/***************************************************************
+* TLan_MiiReadReg
+*
+* Returns:
+* 0 if ack received ok
+* 1 otherwise.
+*
+* Parms:
+* dev The device structure containing
+* The io address and interrupt count
+* for this device.
+* phy The address of the PHY to be queried.
+* reg The register whose contents are to be
+* retreived.
+* val A pointer to a variable to store the
+* retrieved value.
+*
+* This function uses the TLAN's MII bus to retreive the contents
+* of a given register on a PHY. It sends the appropriate info
+* and then reads the 16-bit register value from the MII bus via
+* the TLAN SIO register.
+*
+**************************************************************/
+
+int TLan_MiiReadReg(struct nic *nic __unused, u16 phy, u16 reg, u16 * val)
+{
+ u8 nack;
+ u16 sio, tmp;
+ u32 i;
+ int err;
+ int minten;
+
+ err = FALSE;
+ outw(TLAN_NET_SIO, BASE + TLAN_DIO_ADR);
+ sio = BASE + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ TLan_MiiSync(BASE);
+
+ minten = TLan_GetBit(TLAN_NET_SIO_MINTEN, sio);
+ if (minten)
+ TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio);
+
+ TLan_MiiSendData(BASE, 0x1, 2); /* Start ( 01b ) */
+ TLan_MiiSendData(BASE, 0x2, 2); /* Read ( 10b ) */
+ TLan_MiiSendData(BASE, phy, 5); /* Device # */
+ TLan_MiiSendData(BASE, reg, 5); /* Register # */
+
+
+ TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio); /* Change direction */
+
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Clock Idle bit */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Wait 300ns */
+
+ nack = TLan_GetBit(TLAN_NET_SIO_MDATA, sio); /* Check for ACK */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio); /* Finish ACK */
+ if (nack) { /* No ACK, so fake it */
+ for (i = 0; i < 16; i++) {
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ }
+ tmp = 0xffff;
+ err = TRUE;
+ } else { /* ACK, so read data */
+ for (tmp = 0, i = 0x8000; i; i >>= 1) {
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
+ if (TLan_GetBit(TLAN_NET_SIO_MDATA, sio))
+ tmp |= i;
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ }
+ }
+
+
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Idle cycle */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+
+ if (minten)
+ TLan_SetBit(TLAN_NET_SIO_MINTEN, sio);
+
+ *val = tmp;
+
+ return err;
+
+} /* TLan_MiiReadReg */
+
+/***************************************************************
+* TLan_MiiSendData
+*
+* Returns:
+* Nothing
+* Parms:
+* base_port The base IO port of the adapter in
+* question.
+* dev The address of the PHY to be queried.
+* data The value to be placed on the MII bus.
+* num_bits The number of bits in data that are to
+* be placed on the MII bus.
+*
+* This function sends on sequence of bits on the MII
+* configuration bus.
+*
+**************************************************************/
+
+void TLan_MiiSendData(u16 base_port, u32 data, unsigned num_bits)
+{
+ u16 sio;
+ u32 i;
+
+ if (num_bits == 0)
+ return;
+
+ outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+ TLan_SetBit(TLAN_NET_SIO_MTXEN, sio);
+
+ for (i = (0x1 << (num_bits - 1)); i; i >>= 1) {
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
+ (void) TLan_GetBit(TLAN_NET_SIO_MCLK, sio);
+ if (data & i)
+ TLan_SetBit(TLAN_NET_SIO_MDATA, sio);
+ else
+ TLan_ClearBit(TLAN_NET_SIO_MDATA, sio);
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ (void) TLan_GetBit(TLAN_NET_SIO_MCLK, sio);
+ }
+
+} /* TLan_MiiSendData */
+
+/***************************************************************
+* TLan_MiiSync
+*
+* Returns:
+* Nothing
+* Parms:
+* base_port The base IO port of the adapter in
+* question.
+*
+* This functions syncs all PHYs in terms of the MII configuration
+* bus.
+*
+**************************************************************/
+
+void TLan_MiiSync(u16 base_port)
+{
+ int i;
+ u16 sio;
+
+ outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR);
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio);
+ for (i = 0; i < 32; i++) {
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ }
+
+} /* TLan_MiiSync */
+
+/***************************************************************
+* TLan_MiiWriteReg
+*
+* Returns:
+* Nothing
+* Parms:
+* dev The device structure for the device
+* to write to.
+* phy The address of the PHY to be written to.
+* reg The register whose contents are to be
+* written.
+* val The value to be written to the register.
+*
+* This function uses the TLAN's MII bus to write the contents of a
+* given register on a PHY. It sends the appropriate info and then
+* writes the 16-bit register value from the MII configuration bus
+* via the TLAN SIO register.
+*
+**************************************************************/
+
+void TLan_MiiWriteReg(struct nic *nic __unused, u16 phy, u16 reg, u16 val)
+{
+ u16 sio;
+ int minten;
+
+ outw(TLAN_NET_SIO, BASE + TLAN_DIO_ADR);
+ sio = BASE + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ TLan_MiiSync(BASE);
+
+ minten = TLan_GetBit(TLAN_NET_SIO_MINTEN, sio);
+ if (minten)
+ TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio);
+
+ TLan_MiiSendData(BASE, 0x1, 2); /* Start ( 01b ) */
+ TLan_MiiSendData(BASE, 0x1, 2); /* Write ( 01b ) */
+ TLan_MiiSendData(BASE, phy, 5); /* Device # */
+ TLan_MiiSendData(BASE, reg, 5); /* Register # */
+
+ TLan_MiiSendData(BASE, 0x2, 2); /* Send ACK */
+ TLan_MiiSendData(BASE, val, 16); /* Send Data */
+
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Idle cycle */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+
+ if (minten)
+ TLan_SetBit(TLAN_NET_SIO_MINTEN, sio);
+
+
+} /* TLan_MiiWriteReg */
+
+/***************************************************************
+* TLan_SetMac
+*
+* Returns:
+* Nothing
+* Parms:
+* dev Pointer to device structure of adapter
+* on which to change the AREG.
+* areg The AREG to set the address in (0 - 3).
+* mac A pointer to an array of chars. Each
+* element stores one byte of the address.
+* IE, it isn't in ascii.
+*
+* This function transfers a MAC address to one of the
+* TLAN AREGs (address registers). The TLAN chip locks
+* the register on writing to offset 0 and unlocks the
+* register after writing to offset 5. If NULL is passed
+* in mac, then the AREG is filled with 0's.
+*
+**************************************************************/
+
+void TLan_SetMac(struct nic *nic __unused, int areg, char *mac)
+{
+ int i;
+
+ areg *= 6;
+
+ if (mac != NULL) {
+ for (i = 0; i < 6; i++)
+ TLan_DioWrite8(BASE, TLAN_AREG_0 + areg + i,
+ mac[i]);
+ } else {
+ for (i = 0; i < 6; i++)
+ TLan_DioWrite8(BASE, TLAN_AREG_0 + areg + i, 0);
+ }
+
+} /* TLan_SetMac */
+
+/*********************************************************************
+* TLan_PhyDetect
+*
+* Returns:
+* Nothing
+* Parms:
+* dev A pointer to the device structure of the adapter
+* for which the PHY needs determined.
+*
+* So far I've found that adapters which have external PHYs
+* may also use the internal PHY for part of the functionality.
+* (eg, AUI/Thinnet). This function finds out if this TLAN
+* chip has an internal PHY, and then finds the first external
+* PHY (starting from address 0) if it exists).
+*
+********************************************************************/
+
+void TLan_PhyDetect(struct nic *nic)
+{
+ u16 control;
+ u16 hi;
+ u16 lo;
+ u32 phy;
+
+ if (tlan_pci_tbl[chip_idx].flags & TLAN_ADAPTER_UNMANAGED_PHY) {
+ priv->phyNum = 0xFFFF;
+ return;
+ }
+
+ TLan_MiiReadReg(nic, TLAN_PHY_MAX_ADDR, MII_GEN_ID_HI, &hi);
+
+ if (hi != 0xFFFF) {
+ priv->phy[0] = TLAN_PHY_MAX_ADDR;
+ } else {
+ priv->phy[0] = TLAN_PHY_NONE;
+ }
+
+ priv->phy[1] = TLAN_PHY_NONE;
+ for (phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++) {
+ TLan_MiiReadReg(nic, phy, MII_GEN_CTL, &control);
+ TLan_MiiReadReg(nic, phy, MII_GEN_ID_HI, &hi);
+ TLan_MiiReadReg(nic, phy, MII_GEN_ID_LO, &lo);
+ if ((control != 0xFFFF) || (hi != 0xFFFF)
+ || (lo != 0xFFFF)) {
+ printf("PHY found at %hX %hX %hX %hX\n", phy,
+ control, hi, lo);
+ if ((priv->phy[1] == TLAN_PHY_NONE)
+ && (phy != TLAN_PHY_MAX_ADDR)) {
+ priv->phy[1] = phy;
+ }
+ }
+ }
+
+ if (priv->phy[1] != TLAN_PHY_NONE) {
+ priv->phyNum = 1;
+ } else if (priv->phy[0] != TLAN_PHY_NONE) {
+ priv->phyNum = 0;
+ } else {
+ printf
+ ("TLAN: Cannot initialize device, no PHY was found!\n");
+ }
+
+} /* TLan_PhyDetect */
+
+void TLan_PhyPowerDown(struct nic *nic)
+{
+
+ u16 value;
+ dprintf(("%s: Powering down PHY(s).\n", priv->nic_name));
+ value = MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE;
+ TLan_MiiSync(BASE);
+ TLan_MiiWriteReg(nic, priv->phy[priv->phyNum], MII_GEN_CTL, value);
+ if ((priv->phyNum == 0) && (priv->phy[1] != TLAN_PHY_NONE)
+ &&
+ (!(tlan_pci_tbl[chip_idx].
+ flags & TLAN_ADAPTER_USE_INTERN_10))) {
+ TLan_MiiSync(BASE);
+ TLan_MiiWriteReg(nic, priv->phy[1], MII_GEN_CTL, value);
+ }
+
+ /* Wait for 50 ms and powerup
+ * This is abitrary. It is intended to make sure the
+ * tranceiver settles.
+ */
+ /* TLan_SetTimer( dev, (HZ/20), TLAN_TIMER_PHY_PUP ); */
+ mdelay(50);
+ TLan_PhyPowerUp(nic);
+
+} /* TLan_PhyPowerDown */
+
+
+void TLan_PhyPowerUp(struct nic *nic)
+{
+ u16 value;
+
+ dprintf(("%s: Powering up PHY.\n", priv->nic_name));
+ TLan_MiiSync(BASE);
+ value = MII_GC_LOOPBK;
+ TLan_MiiWriteReg(nic, priv->phy[priv->phyNum], MII_GEN_CTL, value);
+ TLan_MiiSync(BASE);
+ /* Wait for 500 ms and reset the
+ * tranceiver. The TLAN docs say both 50 ms and
+ * 500 ms, so do the longer, just in case.
+ */
+ mdelay(500);
+ TLan_PhyReset(nic);
+ /* TLan_SetTimer( dev, (HZ/20), TLAN_TIMER_PHY_RESET ); */
+
+} /* TLan_PhyPowerUp */
+
+void TLan_PhyReset(struct nic *nic)
+{
+ u16 phy;
+ u16 value;
+
+ phy = priv->phy[priv->phyNum];
+
+ dprintf(("%s: Reseting PHY.\n", priv->nic_name));
+ TLan_MiiSync(BASE);
+ value = MII_GC_LOOPBK | MII_GC_RESET;
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, value);
+ TLan_MiiReadReg(nic, phy, MII_GEN_CTL, &value);
+ while (value & MII_GC_RESET) {
+ TLan_MiiReadReg(nic, phy, MII_GEN_CTL, &value);
+ }
+
+ /* Wait for 500 ms and initialize.
+ * I don't remember why I wait this long.
+ * I've changed this to 50ms, as it seems long enough.
+ */
+ /* TLan_SetTimer( dev, (HZ/20), TLAN_TIMER_PHY_START_LINK ); */
+ mdelay(50);
+ TLan_PhyStartLink(nic);
+
+} /* TLan_PhyReset */
+
+
+void TLan_PhyStartLink(struct nic *nic)
+{
+
+ u16 ability;
+ u16 control;
+ u16 data;
+ u16 phy;
+ u16 status;
+ u16 tctl;
+
+ phy = priv->phy[priv->phyNum];
+ dprintf(("%s: Trying to activate link.\n", priv->nic_name));
+ TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status);
+ TLan_MiiReadReg(nic, phy, MII_GEN_STS, &ability);
+
+ if ((status & MII_GS_AUTONEG) && (!priv->aui)) {
+ ability = status >> 11;
+ if (priv->speed == TLAN_SPEED_10 &&
+ priv->duplex == TLAN_DUPLEX_HALF) {
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x0000);
+ } else if (priv->speed == TLAN_SPEED_10 &&
+ priv->duplex == TLAN_DUPLEX_FULL) {
+ priv->tlanFullDuplex = TRUE;
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x0100);
+ } else if (priv->speed == TLAN_SPEED_100 &&
+ priv->duplex == TLAN_DUPLEX_HALF) {
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x2000);
+ } else if (priv->speed == TLAN_SPEED_100 &&
+ priv->duplex == TLAN_DUPLEX_FULL) {
+ priv->tlanFullDuplex = TRUE;
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x2100);
+ } else {
+
+ /* Set Auto-Neg advertisement */
+ TLan_MiiWriteReg(nic, phy, MII_AN_ADV,
+ (ability << 5) | 1);
+ /* Enablee Auto-Neg */
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x1000);
+ /* Restart Auto-Neg */
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x1200);
+ /* Wait for 4 sec for autonegotiation
+ * to complete. The max spec time is less than this
+ * but the card need additional time to start AN.
+ * .5 sec should be plenty extra.
+ */
+ dprintf(("TLAN: %s: Starting autonegotiation.\n",
+ priv->nic_name));
+ mdelay(4000);
+ TLan_PhyFinishAutoNeg(nic);
+ /* TLan_SetTimer( dev, (2*HZ), TLAN_TIMER_PHY_FINISH_AN ); */
+ return;
+ }
+
+ }
+
+ if ((priv->aui) && (priv->phyNum != 0)) {
+ priv->phyNum = 0;
+ data =
+ TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN |
+ TLAN_NET_CFG_PHY_EN;
+ TLan_DioWrite16(BASE, TLAN_NET_CONFIG, data);
+ mdelay(50);
+ /* TLan_SetTimer( dev, (40*HZ/1000), TLAN_TIMER_PHY_PDOWN ); */
+ TLan_PhyPowerDown(nic);
+ return;
+ } else if (priv->phyNum == 0) {
+ control = 0;
+ TLan_MiiReadReg(nic, phy, TLAN_TLPHY_CTL, &tctl);
+ if (priv->aui) {
+ tctl |= TLAN_TC_AUISEL;
+ } else {
+ tctl &= ~TLAN_TC_AUISEL;
+ if (priv->duplex == TLAN_DUPLEX_FULL) {
+ control |= MII_GC_DUPLEX;
+ priv->tlanFullDuplex = TRUE;
+ }
+ if (priv->speed == TLAN_SPEED_100) {
+ control |= MII_GC_SPEEDSEL;
+ }
+ }
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, control);
+ TLan_MiiWriteReg(nic, phy, TLAN_TLPHY_CTL, tctl);
+ }
+
+ /* Wait for 2 sec to give the tranceiver time
+ * to establish link.
+ */
+ /* TLan_SetTimer( dev, (4*HZ), TLAN_TIMER_FINISH_RESET ); */
+ mdelay(2000);
+ TLan_FinishReset(nic);
+
+} /* TLan_PhyStartLink */
+
+void TLan_PhyFinishAutoNeg(struct nic *nic)
+{
+
+ u16 an_adv;
+ u16 an_lpa;
+ u16 data;
+ u16 mode;
+ u16 phy;
+ u16 status;
+
+ phy = priv->phy[priv->phyNum];
+
+ TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status);
+ udelay(1000);
+ TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status);
+
+ if (!(status & MII_GS_AUTOCMPLT)) {
+ /* Wait for 8 sec to give the process
+ * more time. Perhaps we should fail after a while.
+ */
+ if (!priv->neg_be_verbose++) {
+ printf
+ ("TLAN: Giving autonegotiation more time.\n");
+ printf
+ ("TLAN: Please check that your adapter has\n");
+ printf
+ ("TLAN: been properly connected to a HUB or Switch.\n");
+ printf
+ ("TLAN: Trying to establish link in the background...\n");
+ }
+ mdelay(8000);
+ TLan_PhyFinishAutoNeg(nic);
+ /* TLan_SetTimer( dev, (8*HZ), TLAN_TIMER_PHY_FINISH_AN ); */
+ return;
+ }
+
+ dprintf(("TLAN: %s: Autonegotiation complete.\n", priv->nic_name));
+ TLan_MiiReadReg(nic, phy, MII_AN_ADV, &an_adv);
+ TLan_MiiReadReg(nic, phy, MII_AN_LPA, &an_lpa);
+ mode = an_adv & an_lpa & 0x03E0;
+ if (mode & 0x0100) {
+ printf("Full Duplex\n");
+ priv->tlanFullDuplex = TRUE;
+ } else if (!(mode & 0x0080) && (mode & 0x0040)) {
+ priv->tlanFullDuplex = TRUE;
+ printf("Full Duplex\n");
+ }
+
+ if ((!(mode & 0x0180))
+ && (tlan_pci_tbl[chip_idx].flags & TLAN_ADAPTER_USE_INTERN_10)
+ && (priv->phyNum != 0)) {
+ priv->phyNum = 0;
+ data =
+ TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN |
+ TLAN_NET_CFG_PHY_EN;
+ TLan_DioWrite16(BASE, TLAN_NET_CONFIG, data);
+ /* TLan_SetTimer( nic, (400*HZ/1000), TLAN_TIMER_PHY_PDOWN ); */
+ mdelay(400);
+ TLan_PhyPowerDown(nic);
+ return;
+ }
+
+ if (priv->phyNum == 0) {
+ if ((priv->duplex == TLAN_DUPLEX_FULL)
+ || (an_adv & an_lpa & 0x0040)) {
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL,
+ MII_GC_AUTOENB | MII_GC_DUPLEX);
+ dprintf
+ (("TLAN: Starting internal PHY with FULL-DUPLEX\n"));
+ } else {
+ TLan_MiiWriteReg(nic, phy, MII_GEN_CTL,
+ MII_GC_AUTOENB);
+ dprintf
+ (("TLAN: Starting internal PHY with HALF-DUPLEX\n"));
+ }
+ }
+
+ /* Wait for 100 ms. No reason in partiticular.
+ */
+ /* TLan_SetTimer( dev, (HZ/10), TLAN_TIMER_FINISH_RESET ); */
+ mdelay(100);
+ TLan_FinishReset(nic);
+
+} /* TLan_PhyFinishAutoNeg */
+
+#ifdef MONITOR
+
+/*********************************************************************
+*
+* TLan_phyMonitor
+*
+* Returns:
+* None
+*
+* Params:
+* dev The device structure of this device.
+*
+*
+* This function monitors PHY condition by reading the status
+* register via the MII bus. This can be used to give info
+* about link changes (up/down), and possible switch to alternate
+* media.
+*
+********************************************************************/
+
+void TLan_PhyMonitor(struct net_device *dev)
+{
+ TLanPrivateInfo *priv = dev->priv;
+ u16 phy;
+ u16 phy_status;
+
+ phy = priv->phy[priv->phyNum];
+
+ /* Get PHY status register */
+ TLan_MiiReadReg(nic, phy, MII_GEN_STS, &phy_status);
+
+ /* Check if link has been lost */
+ if (!(phy_status & MII_GS_LINK)) {
+ if (priv->link) {
+ priv->link = 0;
+ printf("TLAN: %s has lost link\n", priv->nic_name);
+ priv->flags &= ~IFF_RUNNING;
+ mdelay(2000);
+ TLan_PhyMonitor(nic);
+ /* TLan_SetTimer( dev, (2*HZ), TLAN_TIMER_LINK_BEAT ); */
+ return;
+ }
+ }
+
+ /* Link restablished? */
+ if ((phy_status & MII_GS_LINK) && !priv->link) {
+ priv->link = 1;
+ printf("TLAN: %s has reestablished link\n",
+ priv->nic_name);
+ priv->flags |= IFF_RUNNING;
+ }
+
+ /* Setup a new monitor */
+ /* TLan_SetTimer( dev, (2*HZ), TLAN_TIMER_LINK_BEAT ); */
+ mdelay(2000);
+ TLan_PhyMonitor(nic);
+}
+
+#endif /* MONITOR */
+
+static struct pci_id tlan_nics[] = {
+ PCI_ROM(0x0e11, 0xae34, "netel10", "Compaq Netelligent 10 T PCI UTP"),
+ PCI_ROM(0x0e11, 0xae32, "netel100","Compaq Netelligent 10/100 TX PCI UTP"),
+ PCI_ROM(0x0e11, 0xae35, "netflex3i", "Compaq Integrated NetFlex-3/P"),
+ PCI_ROM(0x0e11, 0xf130, "thunder", "Compaq NetFlex-3/P"),
+ PCI_ROM(0x0e11, 0xf150, "netflex3b", "Compaq NetFlex-3/P"),
+ PCI_ROM(0x0e11, 0xae43, "netel100pi", "Compaq Netelligent Integrated 10/100 TX UTP"),
+ PCI_ROM(0x0e11, 0xae40, "netel100d", "Compaq Netelligent Dual 10/100 TX PCI UTP"),
+ PCI_ROM(0x0e11, 0xb011, "netel100i", "Compaq Netelligent 10/100 TX Embedded UTP"),
+ PCI_ROM(0x108d, 0x0013, "oc2183", "Olicom OC-2183/2185"),
+ PCI_ROM(0x108d, 0x0012, "oc2325", "Olicom OC-2325"),
+ PCI_ROM(0x108d, 0x0014, "oc2326", "Olicom OC-2326"),
+ PCI_ROM(0x0e11, 0xb030, "netelligent_10_100_ws_5100", "Compaq Netelligent 10/100 TX UTP"),
+ PCI_ROM(0x0e11, 0xb012, "netelligent_10_t2", "Compaq Netelligent 10 T/2 PCI UTP/Coax"),
+};
+
+static struct pci_driver tlan_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "TLAN/PCI",
+ .probe = tlan_probe,
+ .ids = tlan_nics,
+ .id_count = sizeof(tlan_nics) / sizeof(tlan_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/tlan.h b/src/drivers/net/tlan.h
new file mode 100644
index 00000000..35753e6a
--- /dev/null
+++ b/src/drivers/net/tlan.h
@@ -0,0 +1,536 @@
+/**************************************************************************
+*
+* tlan.c -- Etherboot device driver for the Texas Instruments ThunderLAN
+* Written 2003-2003 by Timothy Legge <tlegge@rogers.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* Portions of this code (almost all) based on:
+* tlan.c: Linux ThunderLan Driver:
+*
+* by James Banks
+*
+* (C) 1997-1998 Caldera, Inc.
+* (C) 1998 James Banks
+* (C) 1999-2001 Torben Mathiasen
+* (C) 2002 Samuel Chessman
+*
+* REVISION HISTORY:
+* ================
+* v1.0 07-08-2003 timlegge Initial not quite working version
+*
+* Indent Style: indent -kr -i8
+***************************************************************************/
+
+/*
+#include <asm/io.h>
+#include <asm/types.h>
+#include <linux/netdevice.h>
+*/
+
+typedef unsigned char u8;
+typedef signed char s8;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned int u32;
+typedef signed int s32;
+ /*****************************************************************
+ * TLan Definitions
+ *
+ ****************************************************************/
+
+#define FALSE 0
+#define TRUE 1
+
+#define TLAN_MIN_FRAME_SIZE 64
+#define TLAN_MAX_FRAME_SIZE 1600
+
+#define TLAN_NUM_RX_LISTS 4
+#define TLAN_NUM_TX_LISTS 2
+
+#define TLAN_IGNORE 0
+#define TLAN_RECORD 1
+/*
+#define TLAN_DBG(lvl, format, args...) if (debug&lvl) printf("TLAN: " format, ##args );
+*/
+#define TLAN_DEBUG_GNRL 0x0001
+#define TLAN_DEBUG_TX 0x0002
+#define TLAN_DEBUG_RX 0x0004
+#define TLAN_DEBUG_LIST 0x0008
+#define TLAN_DEBUG_PROBE 0x0010
+
+#define TX_TIMEOUT (10*HZ) /* We need time for auto-neg */
+#define MAX_TLAN_BOARDS 8 /* Max number of boards installed at a time */
+
+
+ /*****************************************************************
+ * Device Identification Definitions
+ *
+ ****************************************************************/
+
+#define PCI_DEVICE_ID_NETELLIGENT_10_T2 0xB012
+#define PCI_DEVICE_ID_NETELLIGENT_10_100_WS_5100 0xB030
+#ifndef PCI_DEVICE_ID_OLICOM_OC2183
+#define PCI_DEVICE_ID_OLICOM_OC2183 0x0013
+#endif
+#ifndef PCI_DEVICE_ID_OLICOM_OC2325
+#define PCI_DEVICE_ID_OLICOM_OC2325 0x0012
+#endif
+#ifndef PCI_DEVICE_ID_OLICOM_OC2326
+#define PCI_DEVICE_ID_OLICOM_OC2326 0x0014
+#endif
+
+typedef struct tlan_adapter_entry {
+ u16 vendorId;
+ u16 deviceId;
+ char *deviceLabel;
+ u32 flags;
+ u16 addrOfs;
+} TLanAdapterEntry;
+
+#define TLAN_ADAPTER_NONE 0x00000000
+#define TLAN_ADAPTER_UNMANAGED_PHY 0x00000001
+#define TLAN_ADAPTER_BIT_RATE_PHY 0x00000002
+#define TLAN_ADAPTER_USE_INTERN_10 0x00000004
+#define TLAN_ADAPTER_ACTIVITY_LED 0x00000008
+
+#define TLAN_SPEED_DEFAULT 0
+#define TLAN_SPEED_10 10
+#define TLAN_SPEED_100 100
+
+#define TLAN_DUPLEX_DEFAULT 0
+#define TLAN_DUPLEX_HALF 1
+#define TLAN_DUPLEX_FULL 2
+
+
+
+ /*****************************************************************
+ * EISA Definitions
+ *
+ ****************************************************************/
+
+#define EISA_ID 0xc80 /* EISA ID Registers */
+#define EISA_ID0 0xc80 /* EISA ID Register 0 */
+#define EISA_ID1 0xc81 /* EISA ID Register 1 */
+#define EISA_ID2 0xc82 /* EISA ID Register 2 */
+#define EISA_ID3 0xc83 /* EISA ID Register 3 */
+#define EISA_CR 0xc84 /* EISA Control Register */
+#define EISA_REG0 0xc88 /* EISA Configuration Register 0 */
+#define EISA_REG1 0xc89 /* EISA Configuration Register 1 */
+#define EISA_REG2 0xc8a /* EISA Configuration Register 2 */
+#define EISA_REG3 0xc8f /* EISA Configuration Register 3 */
+#define EISA_APROM 0xc90 /* Ethernet Address PROM */
+
+
+
+ /*****************************************************************
+ * Rx/Tx List Definitions
+ *
+ ****************************************************************/
+
+#define TLAN_BUFFERS_PER_LIST 10
+#define TLAN_LAST_BUFFER 0x80000000
+#define TLAN_CSTAT_UNUSED 0x8000
+#define TLAN_CSTAT_FRM_CMP 0x4000
+#define TLAN_CSTAT_READY 0x3000
+#define TLAN_CSTAT_EOC 0x0800
+#define TLAN_CSTAT_RX_ERROR 0x0400
+#define TLAN_CSTAT_PASS_CRC 0x0200
+#define TLAN_CSTAT_DP_PR 0x0100
+
+
+
+
+
+
+ /*****************************************************************
+ * PHY definitions
+ *
+ ****************************************************************/
+
+#define TLAN_PHY_MAX_ADDR 0x1F
+#define TLAN_PHY_NONE 0x20
+
+
+
+ /*****************************************************************
+ * TLan Driver Timer Definitions
+ *
+ ****************************************************************/
+
+#define TLAN_TIMER_LINK_BEAT 1
+#define TLAN_TIMER_ACTIVITY 2
+#define TLAN_TIMER_PHY_PDOWN 3
+#define TLAN_TIMER_PHY_PUP 4
+#define TLAN_TIMER_PHY_RESET 5
+#define TLAN_TIMER_PHY_START_LINK 6
+#define TLAN_TIMER_PHY_FINISH_AN 7
+#define TLAN_TIMER_FINISH_RESET 8
+
+#define TLAN_TIMER_ACT_DELAY (HZ/10)
+
+
+
+
+ /*****************************************************************
+ * TLan Driver Eeprom Definitions
+ *
+ ****************************************************************/
+
+#define TLAN_EEPROM_ACK 0
+#define TLAN_EEPROM_STOP 1
+
+
+
+
+ /*****************************************************************
+ * Host Register Offsets and Contents
+ *
+ ****************************************************************/
+
+#define TLAN_HOST_CMD 0x00
+#define TLAN_HC_GO 0x80000000
+#define TLAN_HC_STOP 0x40000000
+#define TLAN_HC_ACK 0x20000000
+#define TLAN_HC_CS_MASK 0x1FE00000
+#define TLAN_HC_EOC 0x00100000
+#define TLAN_HC_RT 0x00080000
+#define TLAN_HC_NES 0x00040000
+#define TLAN_HC_AD_RST 0x00008000
+#define TLAN_HC_LD_TMR 0x00004000
+#define TLAN_HC_LD_THR 0x00002000
+#define TLAN_HC_REQ_INT 0x00001000
+#define TLAN_HC_INT_OFF 0x00000800
+#define TLAN_HC_INT_ON 0x00000400
+#define TLAN_HC_AC_MASK 0x000000FF
+#define TLAN_CH_PARM 0x04
+#define TLAN_DIO_ADR 0x08
+#define TLAN_DA_ADR_INC 0x8000
+#define TLAN_DA_RAM_ADR 0x4000
+#define TLAN_HOST_INT 0x0A
+#define TLAN_HI_IV_MASK 0x1FE0
+#define TLAN_HI_IT_MASK 0x001C
+#define TLAN_DIO_DATA 0x0C
+
+
+/* ThunderLAN Internal Register DIO Offsets */
+
+#define TLAN_NET_CMD 0x00
+#define TLAN_NET_CMD_NRESET 0x80
+#define TLAN_NET_CMD_NWRAP 0x40
+#define TLAN_NET_CMD_CSF 0x20
+#define TLAN_NET_CMD_CAF 0x10
+#define TLAN_NET_CMD_NOBRX 0x08
+#define TLAN_NET_CMD_DUPLEX 0x04
+#define TLAN_NET_CMD_TRFRAM 0x02
+#define TLAN_NET_CMD_TXPACE 0x01
+#define TLAN_NET_SIO 0x01
+#define TLAN_NET_SIO_MINTEN 0x80
+#define TLAN_NET_SIO_ECLOK 0x40
+#define TLAN_NET_SIO_ETXEN 0x20
+#define TLAN_NET_SIO_EDATA 0x10
+#define TLAN_NET_SIO_NMRST 0x08
+#define TLAN_NET_SIO_MCLK 0x04
+#define TLAN_NET_SIO_MTXEN 0x02
+#define TLAN_NET_SIO_MDATA 0x01
+#define TLAN_NET_STS 0x02
+#define TLAN_NET_STS_MIRQ 0x80
+#define TLAN_NET_STS_HBEAT 0x40
+#define TLAN_NET_STS_TXSTOP 0x20
+#define TLAN_NET_STS_RXSTOP 0x10
+#define TLAN_NET_STS_RSRVD 0x0F
+#define TLAN_NET_MASK 0x03
+#define TLAN_NET_MASK_MASK7 0x80
+#define TLAN_NET_MASK_MASK6 0x40
+#define TLAN_NET_MASK_MASK5 0x20
+#define TLAN_NET_MASK_MASK4 0x10
+#define TLAN_NET_MASK_RSRVD 0x0F
+#define TLAN_NET_CONFIG 0x04
+#define TLAN_NET_CFG_RCLK 0x8000
+#define TLAN_NET_CFG_TCLK 0x4000
+#define TLAN_NET_CFG_BIT 0x2000
+#define TLAN_NET_CFG_RXCRC 0x1000
+#define TLAN_NET_CFG_PEF 0x0800
+#define TLAN_NET_CFG_1FRAG 0x0400
+#define TLAN_NET_CFG_1CHAN 0x0200
+#define TLAN_NET_CFG_MTEST 0x0100
+#define TLAN_NET_CFG_PHY_EN 0x0080
+#define TLAN_NET_CFG_MSMASK 0x007F
+#define TLAN_MAN_TEST 0x06
+#define TLAN_DEF_VENDOR_ID 0x08
+#define TLAN_DEF_DEVICE_ID 0x0A
+#define TLAN_DEF_REVISION 0x0C
+#define TLAN_DEF_SUBCLASS 0x0D
+#define TLAN_DEF_MIN_LAT 0x0E
+#define TLAN_DEF_MAX_LAT 0x0F
+#define TLAN_AREG_0 0x10
+#define TLAN_AREG_1 0x16
+#define TLAN_AREG_2 0x1C
+#define TLAN_AREG_3 0x22
+#define TLAN_HASH_1 0x28
+#define TLAN_HASH_2 0x2C
+#define TLAN_GOOD_TX_FRMS 0x30
+#define TLAN_TX_UNDERUNS 0x33
+#define TLAN_GOOD_RX_FRMS 0x34
+#define TLAN_RX_OVERRUNS 0x37
+#define TLAN_DEFERRED_TX 0x38
+#define TLAN_CRC_ERRORS 0x3A
+#define TLAN_CODE_ERRORS 0x3B
+#define TLAN_MULTICOL_FRMS 0x3C
+#define TLAN_SINGLECOL_FRMS 0x3E
+#define TLAN_EXCESSCOL_FRMS 0x40
+#define TLAN_LATE_COLS 0x41
+#define TLAN_CARRIER_LOSS 0x42
+#define TLAN_ACOMMIT 0x43
+#define TLAN_LED_REG 0x44
+#define TLAN_LED_ACT 0x10
+#define TLAN_LED_LINK 0x01
+#define TLAN_BSIZE_REG 0x45
+#define TLAN_MAX_RX 0x46
+#define TLAN_INT_DIS 0x48
+#define TLAN_ID_TX_EOC 0x04
+#define TLAN_ID_RX_EOF 0x02
+#define TLAN_ID_RX_EOC 0x01
+
+
+
+/* ThunderLAN Interrupt Codes */
+
+#define TLAN_INT_NUMBER_OF_INTS 8
+
+#define TLAN_INT_NONE 0x0000
+#define TLAN_INT_TX_EOF 0x0001
+#define TLAN_INT_STAT_OVERFLOW 0x0002
+#define TLAN_INT_RX_EOF 0x0003
+#define TLAN_INT_DUMMY 0x0004
+#define TLAN_INT_TX_EOC 0x0005
+#define TLAN_INT_STATUS_CHECK 0x0006
+#define TLAN_INT_RX_EOC 0x0007
+
+
+
+/* ThunderLAN MII Registers */
+
+/* Generic MII/PHY Registers */
+
+#define MII_GEN_CTL 0x00
+#define MII_GC_RESET 0x8000
+#define MII_GC_LOOPBK 0x4000
+#define MII_GC_SPEEDSEL 0x2000
+#define MII_GC_AUTOENB 0x1000
+#define MII_GC_PDOWN 0x0800
+#define MII_GC_ISOLATE 0x0400
+#define MII_GC_AUTORSRT 0x0200
+#define MII_GC_DUPLEX 0x0100
+#define MII_GC_COLTEST 0x0080
+#define MII_GC_RESERVED 0x007F
+#define MII_GEN_STS 0x01
+#define MII_GS_100BT4 0x8000
+#define MII_GS_100BTXFD 0x4000
+#define MII_GS_100BTXHD 0x2000
+#define MII_GS_10BTFD 0x1000
+#define MII_GS_10BTHD 0x0800
+#define MII_GS_RESERVED 0x07C0
+#define MII_GS_AUTOCMPLT 0x0020
+#define MII_GS_RFLT 0x0010
+#define MII_GS_AUTONEG 0x0008
+#define MII_GS_LINK 0x0004
+#define MII_GS_JABBER 0x0002
+#define MII_GS_EXTCAP 0x0001
+#define MII_GEN_ID_HI 0x02
+#define MII_GEN_ID_LO 0x03
+#define MII_GIL_OUI 0xFC00
+#define MII_GIL_MODEL 0x03F0
+#define MII_GIL_REVISION 0x000F
+#define MII_AN_ADV 0x04
+#define MII_AN_LPA 0x05
+#define MII_AN_EXP 0x06
+
+/* ThunderLAN Specific MII/PHY Registers */
+
+#define TLAN_TLPHY_ID 0x10
+#define TLAN_TLPHY_CTL 0x11
+#define TLAN_TC_IGLINK 0x8000
+#define TLAN_TC_SWAPOL 0x4000
+#define TLAN_TC_AUISEL 0x2000
+#define TLAN_TC_SQEEN 0x1000
+#define TLAN_TC_MTEST 0x0800
+#define TLAN_TC_RESERVED 0x07F8
+#define TLAN_TC_NFEW 0x0004
+#define TLAN_TC_INTEN 0x0002
+#define TLAN_TC_TINT 0x0001
+#define TLAN_TLPHY_STS 0x12
+#define TLAN_TS_MINT 0x8000
+#define TLAN_TS_PHOK 0x4000
+#define TLAN_TS_POLOK 0x2000
+#define TLAN_TS_TPENERGY 0x1000
+#define TLAN_TS_RESERVED 0x0FFF
+#define TLAN_TLPHY_PAR 0x19
+#define TLAN_PHY_CIM_STAT 0x0020
+#define TLAN_PHY_SPEED_100 0x0040
+#define TLAN_PHY_DUPLEX_FULL 0x0080
+#define TLAN_PHY_AN_EN_STAT 0x0400
+
+/* National Sem. & Level1 PHY id's */
+#define NAT_SEM_ID1 0x2000
+#define NAT_SEM_ID2 0x5C01
+#define LEVEL1_ID1 0x7810
+#define LEVEL1_ID2 0x0000
+
+#define CIRC_INC( a, b ) if ( ++a >= b ) a = 0
+
+/* Routines to access internal registers. */
+
+inline u8 TLan_DioRead8(u16 base_addr, u16 internal_addr)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ return (inb((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x3)));
+
+} /* TLan_DioRead8 */
+
+
+
+
+inline u16 TLan_DioRead16(u16 base_addr, u16 internal_addr)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ return (inw((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x2)));
+
+} /* TLan_DioRead16 */
+
+
+
+
+inline u32 TLan_DioRead32(u16 base_addr, u16 internal_addr)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ return (inl(base_addr + TLAN_DIO_DATA));
+
+} /* TLan_DioRead32 */
+
+
+
+
+inline void TLan_DioWrite8(u16 base_addr, u16 internal_addr, u8 data)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ outb(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x3));
+
+}
+
+
+
+
+inline void TLan_DioWrite16(u16 base_addr, u16 internal_addr, u16 data)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ outw(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2));
+
+}
+
+
+
+
+inline void TLan_DioWrite32(u16 base_addr, u16 internal_addr, u32 data)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ outl(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2));
+
+}
+
+
+
+#if 0
+inline void TLan_ClearBit(u8 bit, u16 port)
+{
+ outb_p(inb_p(port) & ~bit, port);
+}
+
+
+
+
+inline int TLan_GetBit(u8 bit, u16 port)
+{
+ return ((int) (inb_p(port) & bit));
+}
+
+
+
+
+inline void TLan_SetBit(u8 bit, u16 port)
+{
+ outb_p(inb_p(port) | bit, port);
+}
+#endif
+
+#define TLan_ClearBit( bit, port ) outb_p(inb_p(port) & ~bit, port)
+#define TLan_GetBit( bit, port ) ((int) (inb_p(port) & bit))
+#define TLan_SetBit( bit, port ) outb_p(inb_p(port) | bit, port)
+
+#ifdef I_LIKE_A_FAST_HASH_FUNCTION
+/* given 6 bytes, view them as 8 6-bit numbers and return the XOR of those */
+/* the code below is about seven times as fast as the original code */
+inline u32 TLan_HashFunc(u8 * a)
+{
+ u8 hash;
+
+ hash = (a[0] ^ a[3]); /* & 077 */
+ hash ^= ((a[0] ^ a[3]) >> 6); /* & 003 */
+ hash ^= ((a[1] ^ a[4]) << 2); /* & 074 */
+ hash ^= ((a[1] ^ a[4]) >> 4); /* & 017 */
+ hash ^= ((a[2] ^ a[5]) << 4); /* & 060 */
+ hash ^= ((a[2] ^ a[5]) >> 2); /* & 077 */
+
+ return (hash & 077);
+}
+
+#else /* original code */
+
+inline u32 xor(u32 a, u32 b)
+{
+ return ((a && !b) || (!a && b));
+}
+
+#define XOR8( a, b, c, d, e, f, g, h ) xor( a, xor( b, xor( c, xor( d, xor( e, xor( f, xor( g, h ) ) ) ) ) ) )
+#define DA( a, bit ) ( ( (u8) a[bit/8] ) & ( (u8) ( 1 << bit%8 ) ) )
+
+inline u32 TLan_HashFunc(u8 * a)
+{
+ u32 hash;
+
+ hash =
+ XOR8(DA(a, 0), DA(a, 6), DA(a, 12), DA(a, 18), DA(a, 24),
+ DA(a, 30), DA(a, 36), DA(a, 42));
+ hash |=
+ XOR8(DA(a, 1), DA(a, 7), DA(a, 13), DA(a, 19), DA(a, 25),
+ DA(a, 31), DA(a, 37), DA(a, 43)) << 1;
+ hash |=
+ XOR8(DA(a, 2), DA(a, 8), DA(a, 14), DA(a, 20), DA(a, 26),
+ DA(a, 32), DA(a, 38), DA(a, 44)) << 2;
+ hash |=
+ XOR8(DA(a, 3), DA(a, 9), DA(a, 15), DA(a, 21), DA(a, 27),
+ DA(a, 33), DA(a, 39), DA(a, 45)) << 3;
+ hash |=
+ XOR8(DA(a, 4), DA(a, 10), DA(a, 16), DA(a, 22), DA(a, 28),
+ DA(a, 34), DA(a, 40), DA(a, 46)) << 4;
+ hash |=
+ XOR8(DA(a, 5), DA(a, 11), DA(a, 17), DA(a, 23), DA(a, 29),
+ DA(a, 35), DA(a, 41), DA(a, 47)) << 5;
+
+ return hash;
+
+}
+
+#endif /* I_LIKE_A_FAST_HASH_FUNCTION */
diff --git a/src/drivers/net/tulip.c b/src/drivers/net/tulip.c
new file mode 100644
index 00000000..23fca665
--- /dev/null
+++ b/src/drivers/net/tulip.c
@@ -0,0 +1,2082 @@
+/* -*- Mode:C; c-basic-offset:4; -*- */
+
+/*
+ Tulip and clone Etherboot Driver
+
+ By Marty Connor (mdc@thinguin.org)
+ Copyright (C) 2001 Entity Cyber, Inc.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ As of April 2001 this driver should support most tulip cards that
+ the Linux tulip driver supports because Donald Becker's Linux media
+ detection code is now included.
+
+ Based on Ken Yap's Tulip Etherboot Driver and Donald Becker's
+ Linux Tulip Driver. Supports N-Way speed auto-configuration on
+ MX98715, MX98715A and MX98725. Support inexpensive PCI 10/100 cards
+ based on the Macronix MX987x5 chip, such as the SOHOware Fast
+ model SFA110A, and the LinkSYS model LNE100TX. The NetGear
+ model FA310X, based on the LC82C168 chip is supported.
+ The TRENDnet TE100-PCIA NIC which uses a genuine Intel 21143-PD
+ chipset is supported. Also, Davicom DM9102's.
+
+ Documentation and source code used:
+ Source for Etherboot driver at
+ http://etherboot.sourceforge.net/
+ MX98715A Data Sheet and MX98715A Application Note
+ on http://www.macronix.com/ (PDF format files)
+ Source for Linux tulip driver at
+ http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
+
+ Adapted by Ken Yap from
+ FreeBSD netboot DEC 21143 driver
+ Author: David Sharp
+ date: Nov/98
+
+ Some code fragments were taken from verious places, Ken Yap's
+ etherboot, FreeBSD's if_de.c, and various Linux related files.
+ DEC's manuals for the 21143 and SROM format were very helpful.
+ The Linux de driver development page has a number of links to
+ useful related information. Have a look at:
+ ftp://cesdis.gsfc.nasa.gov/pub/linux/drivers/tulip-devel.html
+*/
+
+/*********************************************************************/
+/* Revision History */
+/*********************************************************************/
+
+/*
+ 07 Sep 2003 timlegge Multicast Support Added
+ 11 Apr 2001 mdc [patch to etherboot 4.7.24]
+ Major rewrite to include Linux tulip driver media detection
+ code. This driver should support a lot more cards now.
+ 16 Jul 2000 mdc 0.75b11
+ Added support for ADMtek 0985 Centaur-P, a "Comet" tulip clone
+ which is used on the LinkSYS LNE100TX v4.x cards. We already
+ support LNE100TX v2.0 cards, which use a different controller.
+ 04 Jul 2000 jam ?
+ Added test of status after receiving a packet from the card.
+ Also uncommented the tulip_disable routine. Stray packets
+ seemed to be causing problems.
+ 27 Apr 2000 njl ?
+ 29 Feb 2000 mdc 0.75b7
+ Increased reset delay to 3 seconds because Macronix cards seem to
+ need more reset time before card comes back to a usable state.
+ 26 Feb 2000 mdc 0.75b6
+ Added a 1 second delay after initializing the transmitter because
+ some cards seem to need the time or they drop the first packet
+ transmitted.
+ 23 Feb 2000 mdc 0.75b5
+ removed udelay code and used currticks() for more reliable delay
+ code in reset pause and sanity timeouts. Added function prototypes
+ and TX debugging code.
+ 21 Feb 2000 mdc patch to Etherboot 4.4.3
+ Incorporated patches from Bob Edwards and Paul Mackerras of
+ Linuxcare's OZLabs to deal with inefficiencies in tulip_transmit
+ and udelay. We now wait for packet transmission to complete
+ (or sanity timeout).
+ 04 Feb 2000 Robert.Edwards@anu.edu.au patch to Etherboot 4.4.2
+ patch to tulip.c that implements the automatic selection of the MII
+ interface on cards using the Intel/DEC 21143 reference design, in
+ particular, the TRENDnet TE100-PCIA NIC which uses a genuine Intel
+ 21143-PD chipset.
+ 11 Jan 2000 mdc 0.75b4
+ Added support for NetGear FA310TX card based on the LC82C168
+ chip. This should also support Lite-On LC82C168 boards.
+ Added simple MII support. Re-arranged code to better modularize
+ initializations.
+ 04 Dec 1999 mdc 0.75b3
+ Added preliminary support for LNE100TX PCI cards. Should work for
+ PNIC2 cards. No MII support, but single interface (RJ45) tulip
+ cards seem to not care.
+ 03 Dec 1999 mdc 0.75b2
+ Renamed from mx987x5 to tulip, merged in original tulip init code
+ from tulip.c to support other tulip compatible cards.
+ 02 Dec 1999 mdc 0.75b1
+ Released Beta MX987x5 Driver for code review and testing to netboot
+ and thinguin mailing lists.
+*/
+
+
+/*********************************************************************/
+/* Declarations */
+/*********************************************************************/
+
+#include "etherboot.h"
+#include "nic.h"
+#include "pci.h"
+
+/* User settable parameters */
+
+#undef TULIP_DEBUG
+#undef TULIP_DEBUG_WHERE
+#ifdef TULIP_DEBUG
+static int tulip_debug = 2; /* 1 normal messages, 0 quiet .. 7 verbose. */
+#endif
+
+#define TX_TIME_OUT 2*TICKS_PER_SEC
+
+typedef uint8_t u8;
+typedef int8_t s8;
+typedef uint16_t u16;
+typedef int16_t s16;
+typedef uint32_t u32;
+typedef int32_t s32;
+
+/* helpful macros if on a big_endian machine for changing byte order.
+ not strictly needed on Intel */
+#define get_unaligned(ptr) (*(ptr))
+#define put_unaligned(val, ptr) ((void)( *(ptr) = (val) ))
+#define get_u16(ptr) (*(u16 *)(ptr))
+#define virt_to_le32desc(addr) virt_to_bus(addr)
+
+#define TULIP_IOTYPE PCI_USES_MASTER | PCI_USES_IO | PCI_ADDR0
+#define TULIP_SIZE 0x80
+
+/* This is a mysterious value that can be written to CSR11 in the 21040 (only)
+ to support a pre-NWay full-duplex signaling mechanism using short frames.
+ No one knows what it should be, but if left at its default value some
+ 10base2(!) packets trigger a full-duplex-request interrupt. */
+#define FULL_DUPLEX_MAGIC 0x6969
+
+static const int csr0 = 0x01A00000 | 0x8000;
+
+/* The possible media types that can be set in options[] are: */
+#define MEDIA_MASK 31
+static const char * const medianame[32] = {
+ "10baseT", "10base2", "AUI", "100baseTx",
+ "10baseT-FDX", "100baseTx-FDX", "100baseT4", "100baseFx",
+ "100baseFx-FDX", "MII 10baseT", "MII 10baseT-FDX", "MII",
+ "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FDX", "MII 100baseT4",
+ "MII 100baseFx-HDX", "MII 100baseFx-FDX", "Home-PNA 1Mbps", "Invalid-19",
+};
+
+/* This much match tulip_tbl[]! Note 21142 == 21143. */
+enum tulip_chips {
+ DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3,
+ LC82C168, MX98713, MX98715, MX98725, AX88141, AX88140, PNIC2, COMET,
+ COMPEX9881, I21145, XIRCOM
+};
+
+enum pci_id_flags_bits {
+ /* Set PCI command register bits before calling probe1(). */
+ PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
+ /* Read and map the single following PCI BAR. */
+ PCI_ADDR0=0<<4, PCI_ADDR1=1<<4, PCI_ADDR2=2<<4, PCI_ADDR3=3<<4,
+ PCI_ADDR_64BITS=0x100, PCI_NO_ACPI_WAKE=0x200, PCI_NO_MIN_LATENCY=0x400,
+ PCI_UNUSED_IRQ=0x800,
+};
+
+struct pci_id_info {
+ char *name;
+ struct match_info {
+ u32 pci, pci_mask, subsystem, subsystem_mask;
+ u32 revision, revision_mask; /* Only 8 bits. */
+ } id;
+ enum pci_id_flags_bits pci_flags;
+ int io_size; /* Needed for I/O region check or ioremap(). */
+ int drv_flags; /* Driver use, intended as capability flags. */
+};
+
+static struct pci_id_info pci_id_tbl[] = {
+ { "Digital DC21040 Tulip", { 0x00021011, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 0x80, DC21040 },
+ { "Digital DC21041 Tulip", { 0x00141011, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 0x80, DC21041 },
+ { "Digital DS21140A Tulip", { 0x00091011, 0xffffffff, 0,0, 0x20,0xf0 },
+ TULIP_IOTYPE, 0x80, DC21140 },
+ { "Digital DS21140 Tulip", { 0x00091011, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 0x80, DC21140 },
+ { "Digital DS21143 Tulip", { 0x00191011, 0xffffffff, 0,0, 65,0xff },
+ TULIP_IOTYPE, TULIP_SIZE, DC21142 },
+ { "Digital DS21142 Tulip", { 0x00191011, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, TULIP_SIZE, DC21142 },
+ { "Kingston KNE110tx (PNIC)", { 0x000211AD, 0xffffffff, 0xf0022646, 0xffffffff, 0, 0 },
+ TULIP_IOTYPE, 256, LC82C168 },
+ { "Lite-On 82c168 PNIC", { 0x000211AD, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, LC82C168 },
+ { "Macronix 98713 PMAC", { 0x051210d9, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, MX98713 },
+ { "Macronix 98715 PMAC", { 0x053110d9, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, MX98715 },
+ { "Macronix 98725 PMAC", { 0x053110d9, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, MX98725 },
+ { "ASIX AX88141", { 0x1400125B, 0xffffffff, 0,0, 0x10, 0xf0 },
+ TULIP_IOTYPE, 128, AX88141 },
+ { "ASIX AX88140", { 0x1400125B, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 128, AX88140 },
+ { "Lite-On LC82C115 PNIC-II", { 0xc11511AD, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, PNIC2 },
+ { "ADMtek AN981 Comet", { 0x09811317, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, COMET },
+ { "ADMTek AN983 Comet", { 0x12161113, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, COMET },
+ { "ADMTek Comet AN983b", { 0x95111317, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, COMET },
+ { "ADMtek Centaur-P", { 0x09851317, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, COMET },
+ { "ADMtek Centaur-C", { 0x19851317, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, COMET },
+ { "Compex RL100-TX", { 0x988111F6, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 128, COMPEX9881 },
+ { "Intel 21145 Tulip", { 0x00398086, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 128, I21145 },
+ { "Xircom Tulip clone", { 0x0003115d, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 128, XIRCOM },
+ { "Davicom DM9102", { 0x91021282, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 0x80, DC21140 },
+ { "Davicom DM9100", { 0x91001282, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 0x80, DC21140 },
+ { "Macronix mxic-98715 (EN1217)", { 0x12171113, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, 256, MX98715 },
+ { "3Com 3cSOHO100B-TX (ADMtek Centuar)", { 0x930010b7, 0xffffffff, 0, 0, 0, 0 },
+ TULIP_IOTYPE, TULIP_SIZE, COMET },
+ { 0, { 0, 0, 0, 0, 0, 0 }, 0, 0, 0 },
+};
+
+enum tbl_flag {
+ HAS_MII=1, HAS_MEDIA_TABLE=2, CSR12_IN_SROM=4, ALWAYS_CHECK_MII=8,
+ HAS_PWRDWN=0x10, MC_HASH_ONLY=0x20, /* Hash-only multicast filter. */
+ HAS_PNICNWAY=0x80, HAS_NWAY=0x40, /* Uses internal NWay xcvr. */
+ HAS_INTR_MITIGATION=0x100, IS_ASIX=0x200, HAS_8023X=0x400,
+};
+
+/* Note: this table must match enum tulip_chips above. */
+static struct tulip_chip_table {
+ char *chip_name;
+ int flags;
+} tulip_tbl[] = {
+ { "Digital DC21040 Tulip", 0},
+ { "Digital DC21041 Tulip", HAS_MEDIA_TABLE | HAS_NWAY },
+ { "Digital DS21140 Tulip", HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM },
+ { "Digital DS21143 Tulip", HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII
+ | HAS_PWRDWN | HAS_NWAY | HAS_INTR_MITIGATION },
+ { "Lite-On 82c168 PNIC", HAS_MII | HAS_PNICNWAY },
+ { "Macronix 98713 PMAC", HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM },
+ { "Macronix 98715 PMAC", HAS_MEDIA_TABLE },
+ { "Macronix 98725 PMAC", HAS_MEDIA_TABLE },
+ { "ASIX AX88140", HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM
+ | MC_HASH_ONLY | IS_ASIX },
+ { "ASIX AX88141", HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY
+ | IS_ASIX },
+ { "Lite-On PNIC-II", HAS_MII | HAS_NWAY | HAS_8023X },
+ { "ADMtek Comet", HAS_MII | MC_HASH_ONLY },
+ { "Compex 9881 PMAC", HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM },
+ { "Intel DS21145 Tulip", HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII
+ | HAS_PWRDWN | HAS_NWAY },
+ { "Xircom tulip work-alike", HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII
+ | HAS_PWRDWN | HAS_NWAY },
+ { 0, 0 },
+};
+
+/* A full-duplex map for media types. */
+enum MediaIs {
+ MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8,
+ MediaIs100=16};
+
+static const char media_cap[32] =
+{0,0,0,16, 3,19,16,24, 27,4,7,5, 0,20,23,20, 20,31,0,0, };
+static u8 t21040_csr13[] = {2,0x0C,8,4, 4,0,0,0, 0,0,0,0, 4,0,0,0};
+
+/* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD */
+static u16 t21041_csr13[] = { 0xEF01, 0xEF09, 0xEF09, 0xEF01, 0xEF09, };
+static u16 t21041_csr14[] = { 0xFFFF, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, };
+static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
+
+/* not used
+static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, };
+*/
+static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, };
+/* not used
+static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
+*/
+
+/* Offsets to the Command and Status Registers, "CSRs". All accesses
+ must be longword instructions and quadword aligned. */
+enum tulip_offsets {
+ CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
+ CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
+ CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78, CSR16=0x80, CSR20=0xA0
+};
+
+/* The bits in the CSR5 status registers, mostly interrupt sources. */
+enum status_bits {
+ TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10,
+ NormalIntr=0x10000, AbnormalIntr=0x8000,
+ RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40,
+ TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01,
+};
+
+/* The configuration bits in CSR6. */
+enum csr6_mode_bits {
+ TxOn=0x2000, RxOn=0x0002, FullDuplex=0x0200,
+ AcceptBroadcast=0x0100, AcceptAllMulticast=0x0080,
+ AcceptAllPhys=0x0040, AcceptRunt=0x0008,
+};
+
+
+enum desc_status_bits {
+ DescOwnded=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300,
+};
+
+struct medialeaf {
+ u8 type;
+ u8 media;
+ unsigned char *leafdata;
+};
+
+struct mediatable {
+ u16 defaultmedia;
+ u8 leafcount, csr12dir; /* General purpose pin directions. */
+ unsigned has_mii:1, has_nonmii:1, has_reset:6;
+ u32 csr15dir, csr15val; /* 21143 NWay setting. */
+ struct medialeaf mleaf[0];
+};
+
+struct mediainfo {
+ struct mediainfo *next;
+ int info_type;
+ int index;
+ unsigned char *info;
+};
+
+/* EEPROM Address width definitions */
+#define EEPROM_ADDRLEN 6
+#define EEPROM_SIZE 128 /* 2 << EEPROM_ADDRLEN */
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5 << addr_len)
+#define EE_READ_CMD (6 << addr_len)
+#define EE_ERASE_CMD (7 << addr_len)
+
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */
+#define EE_CS 0x01 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
+#define EE_WRITE_0 0x01
+#define EE_WRITE_1 0x05
+#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
+#define EE_ENB (0x4800 | EE_CS)
+
+/* Delay between EEPROM clock transitions. Even at 33Mhz current PCI
+ implementations don't overrun the EEPROM clock. We add a bus
+ turn-around to insure that this remains true. */
+#define eeprom_delay() inl(ee_addr)
+
+/* Size of transmit and receive buffers */
+#define BUFLEN 1536
+
+/* Ring-wrap flag in length field, use for last ring entry.
+ 0x01000000 means chain on buffer2 address,
+ 0x02000000 means use the ring start address in CSR2/3.
+ Note: Some work-alike chips do not function correctly in chained mode.
+ The ASIX chip works only in chained mode.
+ Thus we indicate ring mode, but always write the 'next' field for
+ chained mode as well. */
+#define DESC_RING_WRAP 0x02000000
+
+/* transmit and receive descriptor format */
+struct tulip_rx_desc {
+ volatile u32 status;
+ u32 length;
+ u32 buffer1, buffer2;
+};
+
+struct tulip_tx_desc {
+ volatile u32 status;
+ u32 length;
+ u32 buffer1, buffer2;
+};
+
+/*********************************************************************/
+/* Global Storage */
+/*********************************************************************/
+
+static u32 ioaddr;
+
+/* Note: transmit and receive buffers must be longword aligned and
+ longword divisable */
+
+#define TX_RING_SIZE 2
+static struct tulip_tx_desc tx_ring[TX_RING_SIZE] __attribute__ ((aligned(4)));
+static unsigned char txb[BUFLEN] __attribute__ ((aligned(4)));
+
+#define RX_RING_SIZE 4
+static struct tulip_rx_desc rx_ring[RX_RING_SIZE] __attribute__ ((aligned(4)));
+static unsigned char rxb[RX_RING_SIZE * BUFLEN] __attribute__ ((aligned(4)));
+
+static struct tulip_private {
+ int cur_rx;
+ int chip_id; /* index into tulip_tbl[] */
+ int pci_id_idx; /* index into pci_id_tbl[] */
+ int revision;
+ int flags;
+ unsigned short vendor_id; /* PCI card vendor code */
+ unsigned short dev_id; /* PCI card device code */
+ unsigned char ehdr[ETH_HLEN]; /* buffer for ethernet header */
+ const char *nic_name;
+ unsigned int csr0, csr6; /* Current CSR0, CSR6 settings. */
+ unsigned int if_port;
+ unsigned int full_duplex; /* Full-duplex operation requested. */
+ unsigned int full_duplex_lock;
+ unsigned int medialock; /* Do not sense media type. */
+ unsigned int mediasense; /* Media sensing in progress. */
+ unsigned int nway, nwayset; /* 21143 internal NWay. */
+ unsigned int default_port;
+ unsigned char eeprom[EEPROM_SIZE]; /* Serial EEPROM contents. */
+ u8 media_table_storage[(sizeof(struct mediatable) + 32*sizeof(struct medialeaf))];
+ u16 sym_advertise, mii_advertise; /* NWay to-advertise. */
+ struct mediatable *mtable;
+ u16 lpar; /* 21143 Link partner ability. */
+ u16 advertising[4]; /* MII advertise, from SROM table. */
+ signed char phys[4], mii_cnt; /* MII device addresses. */
+ int cur_index; /* Current media index. */
+ int saved_if_port;
+} tpx;
+
+static struct tulip_private *tp;
+
+/* Known cards that have old-style EEPROMs.
+ Writing this table is described at
+ http://cesdis.gsfc.nasa.gov/linux/drivers/tulip-drivers/tulip-media.html */
+static struct fixups {
+ char *name;
+ unsigned char addr0, addr1, addr2;
+ u16 newtable[32]; /* Max length below. */
+} eeprom_fixups[] = {
+ {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c,
+ 0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }},
+ {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x041f,
+ 0x0000, 0x009E, /* 10baseT */
+ 0x0004, 0x009E, /* 10baseT-FD */
+ 0x0903, 0x006D, /* 100baseTx */
+ 0x0905, 0x006D, /* 100baseTx-FD */ }},
+ {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x063f,
+ 0x0107, 0x8021, /* 100baseFx */
+ 0x0108, 0x8021, /* 100baseFx-FD */
+ 0x0100, 0x009E, /* 10baseT */
+ 0x0104, 0x009E, /* 10baseT-FD */
+ 0x0103, 0x006D, /* 100baseTx */
+ 0x0105, 0x006D, /* 100baseTx-FD */ }},
+ {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0513,
+ 0x1001, 0x009E, /* 10base2, CSR12 0x10*/
+ 0x0000, 0x009E, /* 10baseT */
+ 0x0004, 0x009E, /* 10baseT-FD */
+ 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */
+ 0x0305, 0x006D, /* 100baseTx-FD CSR12 0x03 */}},
+ {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x051F,
+ 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */
+ 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */
+ 0x0B04, 0x009E, /* 10baseT-FD,CSR12 0x0B */
+ 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */
+ 0x1B05, 0x006D, /* 100baseTx-FD CSR12 0x1B */
+ }},
+ {0, 0, 0, 0, {}}};
+
+static const char * block_name[] = {"21140 non-MII", "21140 MII PHY",
+ "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"};
+
+
+/*********************************************************************/
+/* Function Prototypes */
+/*********************************************************************/
+static int mdio_read(struct nic *nic, int phy_id, int location);
+static void mdio_write(struct nic *nic, int phy_id, int location, int value);
+static int read_eeprom(unsigned long ioaddr, int location, int addr_len);
+static void parse_eeprom(struct nic *nic);
+static int tulip_probe(struct dev *dev, struct pci_device *pci);
+static void tulip_init_ring(struct nic *nic);
+static void tulip_reset(struct nic *nic);
+static void tulip_transmit(struct nic *nic, const char *d, unsigned int t,
+ unsigned int s, const char *p);
+static int tulip_poll(struct nic *nic, int retrieve);
+static void tulip_disable(struct dev *dev);
+static void nway_start(struct nic *nic);
+static void pnic_do_nway(struct nic *nic);
+static void select_media(struct nic *nic, int startup);
+static void init_media(struct nic *nic);
+static void start_link(struct nic *nic);
+static int tulip_check_duplex(struct nic *nic);
+
+static void tulip_wait(unsigned int nticks);
+
+#ifdef TULIP_DEBUG_WHERE
+static void whereami(const char *str);
+#endif
+
+#ifdef TULIP_DEBUG
+static void tulip_more(void);
+#endif
+
+
+/*********************************************************************/
+/* Utility Routines */
+/*********************************************************************/
+
+#ifdef TULIP_DEBUG_WHERE
+static void whereami (const char *str)
+{
+ printf("%s: %s\n", tp->nic_name, str);
+ /* sleep(2); */
+}
+#endif
+
+#ifdef TULIP_DEBUG
+static void tulip_more(void)
+{
+ printf("\n\n-- more --");
+ while (!iskey())
+ /* wait */;
+ getchar();
+ printf("\n\n");
+}
+#endif /* TULIP_DEBUG */
+
+static void tulip_wait(unsigned int nticks)
+{
+ unsigned int to = currticks() + nticks;
+ while (currticks() < to)
+ /* wait */ ;
+}
+
+
+/*********************************************************************/
+/* Media Descriptor Code */
+/*********************************************************************/
+
+/* MII transceiver control section.
+ Read and write the MII registers using software-generated serial
+ MDIO protocol. See the MII specifications or DP83840A data sheet
+ for details. */
+
+/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
+ met by back-to-back PCI I/O cycles, but we insert a delay to avoid
+ "overclocking" issues or future 66Mhz PCI. */
+#define mdio_delay() inl(mdio_addr)
+
+/* Read and write the MII registers using software-generated serial
+ MDIO protocol. It is just different enough from the EEPROM protocol
+ to not share code. The maxium data clock rate is 2.5 Mhz. */
+#define MDIO_SHIFT_CLK 0x10000
+#define MDIO_DATA_WRITE0 0x00000
+#define MDIO_DATA_WRITE1 0x20000
+#define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */
+#define MDIO_ENB_IN 0x40000
+#define MDIO_DATA_READ 0x80000
+
+/* MII transceiver control section.
+ Read and write the MII registers using software-generated serial
+ MDIO protocol. See the MII specifications or DP83840A data sheet
+ for details. */
+
+int mdio_read(struct nic *nic __unused, int phy_id, int location)
+{
+ int i;
+ int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+ int retval = 0;
+ long mdio_addr = ioaddr + CSR9;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("mdio_read\n");
+#endif
+
+ if (tp->chip_id == LC82C168) {
+ int i = 1000;
+ outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0);
+ inl(ioaddr + 0xA0);
+ inl(ioaddr + 0xA0);
+ while (--i > 0)
+ if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000))
+ return retval & 0xffff;
+ return 0xffff;
+ }
+
+ if (tp->chip_id == COMET) {
+ if (phy_id == 1) {
+ if (location < 7)
+ return inl(ioaddr + 0xB4 + (location<<2));
+ else if (location == 17)
+ return inl(ioaddr + 0xD0);
+ else if (location >= 29 && location <= 31)
+ return inl(ioaddr + 0xD4 + ((location-29)<<2));
+ }
+ return 0xffff;
+ }
+
+ /* Establish sync by sending at least 32 logic ones. */
+ for (i = 32; i >= 0; i--) {
+ outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
+ mdio_delay();
+ outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+ /* Shift the read command bits out. */
+ for (i = 15; i >= 0; i--) {
+ int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
+
+ outl(MDIO_ENB | dataval, mdio_addr);
+ mdio_delay();
+ outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 19; i > 0; i--) {
+ outl(MDIO_ENB_IN, mdio_addr);
+ mdio_delay();
+ retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
+ outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+ return (retval>>1) & 0xffff;
+}
+
+void mdio_write(struct nic *nic __unused, int phy_id, int location, int value)
+{
+ int i;
+ int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
+ long mdio_addr = ioaddr + CSR9;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("mdio_write\n");
+#endif
+
+ if (tp->chip_id == LC82C168) {
+ int i = 1000;
+ outl(cmd, ioaddr + 0xA0);
+ do
+ if ( ! (inl(ioaddr + 0xA0) & 0x80000000))
+ break;
+ while (--i > 0);
+ return;
+ }
+
+ if (tp->chip_id == COMET) {
+ if (phy_id != 1)
+ return;
+ if (location < 7)
+ outl(value, ioaddr + 0xB4 + (location<<2));
+ else if (location == 17)
+ outl(value, ioaddr + 0xD0);
+ else if (location >= 29 && location <= 31)
+ outl(value, ioaddr + 0xD4 + ((location-29)<<2));
+ return;
+ }
+
+ /* Establish sync by sending 32 logic ones. */
+ for (i = 32; i >= 0; i--) {
+ outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
+ mdio_delay();
+ outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+ /* Shift the command bits out. */
+ for (i = 31; i >= 0; i--) {
+ int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
+ outl(MDIO_ENB | dataval, mdio_addr);
+ mdio_delay();
+ outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+ /* Clear out extra bits. */
+ for (i = 2; i > 0; i--) {
+ outl(MDIO_ENB_IN, mdio_addr);
+ mdio_delay();
+ outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+}
+
+
+/*********************************************************************/
+/* EEPROM Reading Code */
+/*********************************************************************/
+/* EEPROM routines adapted from the Linux Tulip Code */
+/* Reading a serial EEPROM is a "bit" grungy, but we work our way
+ through:->.
+*/
+static int read_eeprom(unsigned long ioaddr, int location, int addr_len)
+{
+ int i;
+ unsigned short retval = 0;
+ long ee_addr = ioaddr + CSR9;
+ int read_cmd = location | EE_READ_CMD;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("read_eeprom\n");
+#endif
+
+ outl(EE_ENB & ~EE_CS, ee_addr);
+ outl(EE_ENB, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 4 + addr_len; i >= 0; i--) {
+ short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+ outl(EE_ENB | dataval, ee_addr);
+ eeprom_delay();
+ outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ }
+ outl(EE_ENB, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
+ outl(EE_ENB, ee_addr);
+ eeprom_delay();
+ }
+
+ /* Terminate the EEPROM access. */
+ outl(EE_ENB & ~EE_CS, ee_addr);
+ return retval;
+}
+
+
+/*********************************************************************/
+/* EEPROM Parsing Code */
+/*********************************************************************/
+static void parse_eeprom(struct nic *nic)
+{
+ unsigned char *p, *ee_data = tp->eeprom;
+ int new_advertise = 0;
+ int i;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("parse_eeprom\n");
+#endif
+
+ tp->mtable = 0;
+ /* Detect an old-style (SA only) EEPROM layout:
+ memcmp(ee_data, ee_data+16, 8). */
+ for (i = 0; i < 8; i ++)
+ if (ee_data[i] != ee_data[16+i])
+ break;
+ if (i >= 8) {
+ /* Do a fix-up based on the vendor half of the station address. */
+ for (i = 0; eeprom_fixups[i].name; i++) {
+ if (nic->node_addr[0] == eeprom_fixups[i].addr0
+ && nic->node_addr[1] == eeprom_fixups[i].addr1
+ && nic->node_addr[2] == eeprom_fixups[i].addr2) {
+ if (nic->node_addr[2] == 0xE8 && ee_data[0x1a] == 0x55)
+ i++; /* An Accton EN1207, not an outlaw Maxtech. */
+ memcpy(ee_data + 26, eeprom_fixups[i].newtable,
+ sizeof(eeprom_fixups[i].newtable));
+#ifdef TULIP_DEBUG
+ printf("%s: Old format EEPROM on '%s' board.\n%s: Using substitute media control info.\n",
+ tp->nic_name, eeprom_fixups[i].name, tp->nic_name);
+#endif
+ break;
+ }
+ }
+ if (eeprom_fixups[i].name == NULL) { /* No fixup found. */
+#ifdef TULIP_DEBUG
+ printf("%s: Old style EEPROM with no media selection information.\n",
+ tp->nic_name);
+#endif
+ return;
+ }
+ }
+
+ if (ee_data[19] > 1) {
+#ifdef TULIP_DEBUG
+ printf("%s: Multiport cards (%d ports) may not work correctly.\n",
+ tp->nic_name, ee_data[19]);
+#endif
+ }
+
+ p = (void *)ee_data + ee_data[27];
+
+ if (ee_data[27] == 0) { /* No valid media table. */
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1) {
+ printf("%s: No Valid Media Table. ee_data[27] = %hhX\n",
+ tp->nic_name, ee_data[27]);
+ }
+#endif
+ } else if (tp->chip_id == DC21041) {
+ int media = get_u16(p);
+ int count = p[2];
+ p += 3;
+
+ printf("%s: 21041 Media table, default media %hX (%s).\n",
+ tp->nic_name, media,
+ media & 0x0800 ? "Autosense" : medianame[media & 15]);
+ for (i = 0; i < count; i++) {
+ unsigned char media_block = *p++;
+ int media_code = media_block & MEDIA_MASK;
+ if (media_block & 0x40)
+ p += 6;
+ switch(media_code) {
+ case 0: new_advertise |= 0x0020; break;
+ case 4: new_advertise |= 0x0040; break;
+ }
+ printf("%s: 21041 media #%d, %s.\n",
+ tp->nic_name, media_code, medianame[media_code]);
+ }
+ } else {
+ unsigned char csr12dir = 0;
+ int count;
+ struct mediatable *mtable;
+ u16 media = get_u16(p);
+
+ p += 2;
+ if (tp->flags & CSR12_IN_SROM)
+ csr12dir = *p++;
+ count = *p++;
+
+ tp->mtable = mtable = (struct mediatable *)&tp->media_table_storage[0];
+
+ mtable->defaultmedia = media;
+ mtable->leafcount = count;
+ mtable->csr12dir = csr12dir;
+ mtable->has_nonmii = mtable->has_mii = mtable->has_reset = 0;
+ mtable->csr15dir = mtable->csr15val = 0;
+
+ printf("%s: EEPROM default media type %s.\n", tp->nic_name,
+ media & 0x0800 ? "Autosense" : medianame[media & MEDIA_MASK]);
+
+ for (i = 0; i < count; i++) {
+ struct medialeaf *leaf = &mtable->mleaf[i];
+
+ if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */
+ leaf->type = 0;
+ leaf->media = p[0] & 0x3f;
+ leaf->leafdata = p;
+ if ((p[2] & 0x61) == 0x01) /* Bogus, but Znyx boards do it. */
+ mtable->has_mii = 1;
+ p += 4;
+ } else {
+ switch(leaf->type = p[1]) {
+ case 5:
+ mtable->has_reset = i;
+ leaf->media = p[2] & 0x0f;
+ break;
+ case 1: case 3:
+ mtable->has_mii = 1;
+ leaf->media = 11;
+ break;
+ case 2:
+ if ((p[2] & 0x3f) == 0) {
+ u32 base15 = (p[2] & 0x40) ? get_u16(p + 7) : 0x0008;
+ u16 *p1 = (u16 *)(p + (p[2] & 0x40 ? 9 : 3));
+ mtable->csr15dir = (get_unaligned(p1 + 0)<<16) + base15;
+ mtable->csr15val = (get_unaligned(p1 + 1)<<16) + base15;
+ }
+ /* Fall through. */
+ case 0: case 4:
+ mtable->has_nonmii = 1;
+ leaf->media = p[2] & MEDIA_MASK;
+ switch (leaf->media) {
+ case 0: new_advertise |= 0x0020; break;
+ case 4: new_advertise |= 0x0040; break;
+ case 3: new_advertise |= 0x0080; break;
+ case 5: new_advertise |= 0x0100; break;
+ case 6: new_advertise |= 0x0200; break;
+ }
+ break;
+ default:
+ leaf->media = 19;
+ }
+ leaf->leafdata = p + 2;
+ p += (p[0] & 0x3f) + 1;
+ }
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1 && leaf->media == 11) {
+ unsigned char *bp = leaf->leafdata;
+ printf("%s: MII interface PHY %d, setup/reset sequences %d/%d long, capabilities %hhX %hhX.\n",
+ tp->nic_name, bp[0], bp[1], bp[2 + bp[1]*2],
+ bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]);
+ }
+#endif
+ printf("%s: Index #%d - Media %s (#%d) described "
+ "by a %s (%d) block.\n",
+ tp->nic_name, i, medianame[leaf->media], leaf->media,
+ leaf->type < 6 ? block_name[leaf->type] : "UNKNOWN",
+ leaf->type);
+ }
+ if (new_advertise)
+ tp->sym_advertise = new_advertise;
+ }
+}
+
+
+/*********************************************************************/
+/* tulip_init_ring - setup the tx and rx descriptors */
+/*********************************************************************/
+static void tulip_init_ring(struct nic *nic __unused)
+{
+ int i;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("tulip_init_ring\n");
+#endif
+
+ tp->cur_rx = 0;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ rx_ring[i].status = cpu_to_le32(0x80000000);
+ rx_ring[i].length = cpu_to_le32(BUFLEN);
+ rx_ring[i].buffer1 = virt_to_le32desc(&rxb[i * BUFLEN]);
+ rx_ring[i].buffer2 = virt_to_le32desc(&rx_ring[i+1]);
+ }
+ /* Mark the last entry as wrapping the ring. */
+ rx_ring[i-1].length = cpu_to_le32(DESC_RING_WRAP | BUFLEN);
+ rx_ring[i-1].buffer2 = virt_to_le32desc(&rx_ring[0]);
+
+ /* We only use 1 transmit buffer, but we use 2 descriptors so
+ transmit engines have somewhere to point to if they feel the need */
+
+ tx_ring[0].status = 0x00000000;
+ tx_ring[0].buffer1 = virt_to_le32desc(&txb[0]);
+ tx_ring[0].buffer2 = virt_to_le32desc(&tx_ring[1]);
+
+ /* this descriptor should never get used, since it will never be owned
+ by the machine (status will always == 0) */
+ tx_ring[1].status = 0x00000000;
+ tx_ring[1].buffer1 = virt_to_le32desc(&txb[0]);
+ tx_ring[1].buffer2 = virt_to_le32desc(&tx_ring[0]);
+
+ /* Mark the last entry as wrapping the ring, though this should never happen */
+ tx_ring[1].length = cpu_to_le32(DESC_RING_WRAP | BUFLEN);
+}
+
+
+static void set_rx_mode(struct nic *nic __unused) {
+ int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
+
+ tp->csr6 &= ~0x00D5;
+
+ /* !IFF_PROMISC */
+ tp->csr6 |= AcceptAllMulticast;
+ csr6 |= AcceptAllMulticast;
+
+ outl(csr6, ioaddr + CSR6);
+
+
+
+}
+
+/*********************************************************************/
+/* eth_reset - Reset adapter */
+/*********************************************************************/
+static void tulip_reset(struct nic *nic)
+{
+ int i;
+ unsigned long to;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("tulip_reset\n");
+#endif
+
+ /* Stop Tx and RX */
+ outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
+
+ /* On some chip revs we must set the MII/SYM port before the reset!? */
+ if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii)) {
+ outl(0x814C0000, ioaddr + CSR6);
+ }
+
+ /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
+ outl(0x00000001, ioaddr + CSR0);
+ tulip_wait(1);
+
+ /* turn off reset and set cache align=16lword, burst=unlimit */
+ outl(tp->csr0, ioaddr + CSR0);
+
+ /* Wait the specified 50 PCI cycles after a reset */
+ tulip_wait(1);
+
+ /* set up transmit and receive descriptors */
+ tulip_init_ring(nic);
+
+ if (tp->chip_id == PNIC2) {
+ u32 addr_high = (nic->node_addr[1]<<8) + (nic->node_addr[0]<<0);
+ /* This address setting does not appear to impact chip operation?? */
+ outl((nic->node_addr[5]<<8) + nic->node_addr[4] +
+ (nic->node_addr[3]<<24) + (nic->node_addr[2]<<16),
+ ioaddr + 0xB0);
+ outl(addr_high + (addr_high<<16), ioaddr + 0xB8);
+ }
+
+ /* MC_HASH_ONLY boards don't support setup packets */
+ if (tp->flags & MC_HASH_ONLY) {
+ u32 addr_low = cpu_to_le32(get_unaligned((u32 *)nic->node_addr));
+ u32 addr_high = cpu_to_le32(get_unaligned((u16 *)(nic->node_addr+4)));
+
+ /* clear multicast hash filters and setup MAC address filters */
+ if (tp->flags & IS_ASIX) {
+ outl(0, ioaddr + CSR13);
+ outl(addr_low, ioaddr + CSR14);
+ outl(1, ioaddr + CSR13);
+ outl(addr_high, ioaddr + CSR14);
+ outl(2, ioaddr + CSR13);
+ outl(0, ioaddr + CSR14);
+ outl(3, ioaddr + CSR13);
+ outl(0, ioaddr + CSR14);
+ } else if (tp->chip_id == COMET) {
+ outl(addr_low, ioaddr + 0xA4);
+ outl(addr_high, ioaddr + 0xA8);
+ outl(0, ioaddr + 0xAC);
+ outl(0, ioaddr + 0xB0);
+ }
+ } else {
+ /* for other boards we send a setup packet to initialize
+ the filters */
+ u32 tx_flags = 0x08000000 | 192;
+
+ /* construct perfect filter frame with mac address as first match
+ and broadcast address for all others */
+ for (i=0; i<192; i++)
+ txb[i] = 0xFF;
+ txb[0] = nic->node_addr[0];
+ txb[1] = nic->node_addr[1];
+ txb[4] = nic->node_addr[2];
+ txb[5] = nic->node_addr[3];
+ txb[8] = nic->node_addr[4];
+ txb[9] = nic->node_addr[5];
+
+ tx_ring[0].length = cpu_to_le32(tx_flags);
+ tx_ring[0].buffer1 = virt_to_le32desc(&txb[0]);
+ tx_ring[0].status = cpu_to_le32(0x80000000);
+ }
+
+ /* Point to rx and tx descriptors */
+ outl(virt_to_le32desc(&rx_ring[0]), ioaddr + CSR3);
+ outl(virt_to_le32desc(&tx_ring[0]), ioaddr + CSR4);
+
+ init_media(nic);
+
+ /* set the chip's operating mode (but don't turn on xmit and recv yet) */
+ outl((tp->csr6 & ~0x00002002), ioaddr + CSR6);
+
+ /* send setup packet for cards that support it */
+ if (!(tp->flags & MC_HASH_ONLY)) {
+ /* enable transmit wait for completion */
+ outl(tp->csr6 | 0x00002000, ioaddr + CSR6);
+ /* immediate transmit demand */
+ outl(0, ioaddr + CSR1);
+
+ to = currticks() + TX_TIME_OUT;
+ while ((tx_ring[0].status & 0x80000000) && (currticks() < to))
+ /* wait */ ;
+
+ if (currticks() >= to) {
+ printf ("%s: TX Setup Timeout.\n", tp->nic_name);
+ }
+ }
+
+ if (tp->chip_id == LC82C168)
+ tulip_check_duplex(nic);
+
+ set_rx_mode(nic);
+
+ /* enable transmit and receive */
+ outl(tp->csr6 | 0x00002002, ioaddr + CSR6);
+}
+
+
+/*********************************************************************/
+/* eth_transmit - Transmit a frame */
+/*********************************************************************/
+static void tulip_transmit(struct nic *nic, const char *d, unsigned int t,
+ unsigned int s, const char *p)
+{
+ u16 nstype;
+ u32 to;
+ u32 csr6 = inl(ioaddr + CSR6);
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("tulip_transmit\n");
+#endif
+
+ /* Disable Tx */
+ outl(csr6 & ~0x00002000, ioaddr + CSR6);
+
+ memcpy(txb, d, ETH_ALEN);
+ memcpy(txb + ETH_ALEN, nic->node_addr, ETH_ALEN);
+ nstype = htons((u16) t);
+ memcpy(txb + 2 * ETH_ALEN, (u8 *)&nstype, 2);
+ memcpy(txb + ETH_HLEN, p, s);
+
+ s += ETH_HLEN;
+ s &= 0x0FFF;
+
+ /* pad to minimum packet size */
+ while (s < ETH_ZLEN)
+ txb[s++] = '\0';
+
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: sending %d bytes ethtype %hX\n", tp->nic_name, s, t);
+#endif
+
+ /* setup the transmit descriptor */
+ /* 0x60000000 = no interrupt on completion */
+ tx_ring[0].length = cpu_to_le32(0x60000000 | s);
+ tx_ring[0].status = cpu_to_le32(0x80000000);
+
+ /* Point to transmit descriptor */
+ outl(virt_to_le32desc(&tx_ring[0]), ioaddr + CSR4);
+
+ /* Enable Tx */
+ outl(csr6 | 0x00002000, ioaddr + CSR6);
+ /* immediate transmit demand */
+ outl(0, ioaddr + CSR1);
+
+ to = currticks() + TX_TIME_OUT;
+ while ((tx_ring[0].status & 0x80000000) && (currticks() < to))
+ /* wait */ ;
+
+ if (currticks() >= to) {
+ printf ("TX Timeout!\n");
+ }
+
+ /* Disable Tx */
+ outl(csr6 & ~0x00002000, ioaddr + CSR6);
+}
+
+/*********************************************************************/
+/* eth_poll - Wait for a frame */
+/*********************************************************************/
+static int tulip_poll(struct nic *nic, int retrieve)
+{
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("tulip_poll\n");
+#endif
+
+ /* no packet waiting. packet still owned by NIC */
+ if (rx_ring[tp->cur_rx].status & 0x80000000)
+ return 0;
+
+ if ( ! retrieve ) return 1;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("tulip_poll got one\n");
+#endif
+
+ nic->packetlen = (rx_ring[tp->cur_rx].status & 0x3FFF0000) >> 16;
+
+ /* if we get a corrupted packet. throw it away and move on */
+ if (rx_ring[tp->cur_rx].status & 0x00008000) {
+ /* return the descriptor and buffer to receive ring */
+ rx_ring[tp->cur_rx].status = 0x80000000;
+ tp->cur_rx = (++tp->cur_rx) % RX_RING_SIZE;
+ return 0;
+ }
+
+ /* copy packet to working buffer */
+ memcpy(nic->packet, rxb + tp->cur_rx * BUFLEN, nic->packetlen);
+
+ /* return the descriptor and buffer to receive ring */
+ rx_ring[tp->cur_rx].status = 0x80000000;
+ tp->cur_rx = (++tp->cur_rx) % RX_RING_SIZE;
+
+ return 1;
+}
+
+/*********************************************************************/
+/* eth_disable - Disable the interface */
+/*********************************************************************/
+static void tulip_disable(struct dev *dev)
+{
+ struct nic *nic = (struct nic *)dev;
+#ifdef TULIP_DEBUG_WHERE
+ whereami("tulip_disable\n");
+#endif
+
+ /* merge reset and disable */
+ tulip_reset(nic);
+
+ /* disable interrupts */
+ outl(0x00000000, ioaddr + CSR7);
+
+ /* Stop the chip's Tx and Rx processes. */
+ outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
+
+ /* Clear the missed-packet counter. */
+ (volatile unsigned long)inl(ioaddr + CSR8);
+}
+
+/*********************************************************************/
+/*IRQ - Enable, Disable, or Force interrupts */
+/*********************************************************************/
+static void tulip_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/*********************************************************************/
+/* eth_probe - Look for an adapter */
+/*********************************************************************/
+static int tulip_probe(struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *)dev;
+ u32 i;
+ u8 chip_rev;
+ u8 ee_data[EEPROM_SIZE];
+ unsigned short sum;
+ int chip_idx;
+ static unsigned char last_phys_addr[ETH_ALEN] = {0x00, 'L', 'i', 'n', 'u', 'x'};
+
+ if (pci->ioaddr == 0)
+ return 0;
+
+ ioaddr = pci->ioaddr;
+ nic->ioaddr = pci->ioaddr & ~3;
+ nic->irqno = 0;
+
+ /* point to private storage */
+ tp = &tpx;
+
+ tp->vendor_id = pci->vendor;
+ tp->dev_id = pci->dev_id;
+ tp->nic_name = pci->name;
+
+ tp->if_port = 0;
+ tp->default_port = 0;
+
+ adjust_pci_device(pci);
+
+ /* disable interrupts */
+ outl(0x00000000, ioaddr + CSR7);
+
+ /* Stop the chip's Tx and Rx processes. */
+ outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
+
+ /* Clear the missed-packet counter. */
+ (volatile unsigned long)inl(ioaddr + CSR8);
+
+ printf("\n"); /* so we start on a fresh line */
+#ifdef TULIP_DEBUG_WHERE
+ whereami("tulip_probe\n");
+#endif
+
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf ("%s: Looking for Tulip Chip: Vendor=%hX Device=%hX\n", tp->nic_name,
+ tp->vendor_id, tp->dev_id);
+#endif
+
+ /* Figure out which chip we're dealing with */
+ i = 0;
+ chip_idx = -1;
+
+ while (pci_id_tbl[i].name) {
+ if ( (((u32) tp->dev_id << 16) | tp->vendor_id) ==
+ (pci_id_tbl[i].id.pci & pci_id_tbl[i].id.pci_mask) ) {
+ chip_idx = pci_id_tbl[i].drv_flags;
+ break;
+ }
+ i++;
+ }
+
+ if (chip_idx == -1) {
+ printf ("%s: Unknown Tulip Chip: Vendor=%hX Device=%hX\n", tp->nic_name,
+ tp->vendor_id, tp->dev_id);
+ return 0;
+ }
+
+ tp->pci_id_idx = i;
+ tp->flags = tulip_tbl[chip_idx].flags;
+
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1) {
+ printf ("%s: tp->pci_id_idx == %d, name == %s\n", tp->nic_name,
+ tp->pci_id_idx, pci_id_tbl[tp->pci_id_idx].name);
+ printf ("%s: chip_idx == %d, name == %s\n", tp->nic_name, chip_idx,
+ tulip_tbl[chip_idx].chip_name);
+ }
+#endif
+
+ /* Bring the 21041/21143 out of sleep mode.
+ Caution: Snooze mode does not work with some boards! */
+ if (tp->flags & HAS_PWRDWN)
+ pcibios_write_config_dword(pci->bus, pci->devfn, 0x40, 0x00000000);
+
+ if (inl(ioaddr + CSR5) == 0xFFFFFFFF) {
+ printf("%s: The Tulip chip at %X is not functioning.\n",
+ tp->nic_name, ioaddr);
+ return 0;
+ }
+
+ pcibios_read_config_byte(pci->bus, pci->devfn, PCI_REVISION, &chip_rev);
+
+ printf("%s: [chip: %s] rev %d at %hX\n", tp->nic_name,
+ tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr);
+ printf("%s: Vendor=%hX Device=%hX", tp->nic_name, tp->vendor_id, tp->dev_id);
+
+ if (chip_idx == DC21041 && inl(ioaddr + CSR9) & 0x8000) {
+ printf(" 21040 compatible mode.");
+ chip_idx = DC21040;
+ }
+
+ printf("\n");
+
+ /* The SROM/EEPROM interface varies dramatically. */
+ sum = 0;
+ if (chip_idx == DC21040) {
+ outl(0, ioaddr + CSR9); /* Reset the pointer with a dummy write. */
+ for (i = 0; i < ETH_ALEN; i++) {
+ int value, boguscnt = 100000;
+ do
+ value = inl(ioaddr + CSR9);
+ while (value < 0 && --boguscnt > 0);
+ nic->node_addr[i] = value;
+ sum += value & 0xff;
+ }
+ } else if (chip_idx == LC82C168) {
+ for (i = 0; i < 3; i++) {
+ int value, boguscnt = 100000;
+ outl(0x600 | i, ioaddr + 0x98);
+ do
+ value = inl(ioaddr + CSR9);
+ while (value < 0 && --boguscnt > 0);
+ put_unaligned(le16_to_cpu(value), ((u16*)nic->node_addr) + i);
+ sum += value & 0xffff;
+ }
+ } else if (chip_idx == COMET) {
+ /* No need to read the EEPROM. */
+ put_unaligned(inl(ioaddr + 0xA4), (u32 *)nic->node_addr);
+ put_unaligned(inl(ioaddr + 0xA8), (u16 *)(nic->node_addr + 4));
+ for (i = 0; i < ETH_ALEN; i ++)
+ sum += nic->node_addr[i];
+ } else {
+ /* A serial EEPROM interface, we read now and sort it out later. */
+ int sa_offset = 0;
+ int ee_addr_size = read_eeprom(ioaddr, 0xff, 8) & 0x40000 ? 8 : 6;
+
+ for (i = 0; i < sizeof(ee_data)/2; i++)
+ ((u16 *)ee_data)[i] =
+ le16_to_cpu(read_eeprom(ioaddr, i, ee_addr_size));
+
+ /* DEC now has a specification (see Notes) but early board makers
+ just put the address in the first EEPROM locations. */
+ /* This does memcmp(eedata, eedata+16, 8) */
+ for (i = 0; i < 8; i ++)
+ if (ee_data[i] != ee_data[16+i])
+ sa_offset = 20;
+ if (ee_data[0] == 0xff && ee_data[1] == 0xff && ee_data[2] == 0) {
+ sa_offset = 2; /* Grrr, damn Matrox boards. */
+ }
+ for (i = 0; i < ETH_ALEN; i ++) {
+ nic->node_addr[i] = ee_data[i + sa_offset];
+ sum += ee_data[i + sa_offset];
+ }
+ }
+ /* Lite-On boards have the address byte-swapped. */
+ if ((nic->node_addr[0] == 0xA0 || nic->node_addr[0] == 0xC0)
+ && nic->node_addr[1] == 0x00)
+ for (i = 0; i < ETH_ALEN; i+=2) {
+ char tmp = nic->node_addr[i];
+ nic->node_addr[i] = nic->node_addr[i+1];
+ nic->node_addr[i+1] = tmp;
+ }
+
+ if (sum == 0 || sum == ETH_ALEN*0xff) {
+ printf("%s: EEPROM not present!\n", tp->nic_name);
+ for (i = 0; i < ETH_ALEN-1; i++)
+ nic->node_addr[i] = last_phys_addr[i];
+ nic->node_addr[i] = last_phys_addr[i] + 1;
+ }
+
+ for (i = 0; i < ETH_ALEN; i++)
+ last_phys_addr[i] = nic->node_addr[i];
+
+ printf("%s: %! at ioaddr %hX\n", tp->nic_name, nic->node_addr, ioaddr);
+
+ tp->chip_id = chip_idx;
+ tp->revision = chip_rev;
+ tp->csr0 = csr0;
+
+ /* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles.
+ And the ASIX must have a burst limit or horrible things happen. */
+ if (chip_idx == DC21143 && chip_rev == 65)
+ tp->csr0 &= ~0x01000000;
+ else if (tp->flags & IS_ASIX)
+ tp->csr0 |= 0x2000;
+
+ if (media_cap[tp->default_port] & MediaIsMII) {
+ u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 };
+ tp->mii_advertise = media2advert[tp->default_port - 9];
+ tp->mii_advertise |= (tp->flags & HAS_8023X); /* Matching bits! */
+ }
+
+ /* This is logically part of the probe routine, but too complex
+ to write inline. */
+ if (tp->flags & HAS_MEDIA_TABLE) {
+ memcpy(tp->eeprom, ee_data, sizeof(tp->eeprom));
+ parse_eeprom(nic);
+ }
+
+ start_link(nic);
+
+ /* reset the device and make ready for tx and rx of packets */
+ tulip_reset(nic);
+
+ dev->disable = tulip_disable;
+ nic->poll = tulip_poll;
+ nic->transmit = tulip_transmit;
+ nic->irq = tulip_irq;
+
+ /* give the board a chance to reset before returning */
+ tulip_wait(4*TICKS_PER_SEC);
+
+ return 1;
+}
+
+static void start_link(struct nic *nic)
+{
+ int i;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("start_link\n");
+#endif
+
+ if ((tp->flags & ALWAYS_CHECK_MII) ||
+ (tp->mtable && tp->mtable->has_mii) ||
+ ( ! tp->mtable && (tp->flags & HAS_MII))) {
+ unsigned int phy, phy_idx;
+ if (tp->mtable && tp->mtable->has_mii) {
+ for (i = 0; i < tp->mtable->leafcount; i++)
+ if (tp->mtable->mleaf[i].media == 11) {
+ tp->cur_index = i;
+ tp->saved_if_port = tp->if_port;
+ select_media(nic, 2);
+ tp->if_port = tp->saved_if_port;
+ break;
+ }
+ }
+
+ /* Find the connected MII xcvrs. */
+ for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys);
+ phy++) {
+ int mii_status = mdio_read(nic, phy, 1);
+ if ((mii_status & 0x8301) == 0x8001 ||
+ ((mii_status & 0x8000) == 0 && (mii_status & 0x7800) != 0)) {
+ int mii_reg0 = mdio_read(nic, phy, 0);
+ int mii_advert = mdio_read(nic, phy, 4);
+ int to_advert;
+
+ if (tp->mii_advertise)
+ to_advert = tp->mii_advertise;
+ else if (tp->advertising[phy_idx])
+ to_advert = tp->advertising[phy_idx];
+ else /* Leave unchanged. */
+ tp->mii_advertise = to_advert = mii_advert;
+
+ tp->phys[phy_idx++] = phy;
+ printf("%s: MII transceiver %d config %hX status %hX advertising %hX.\n",
+ tp->nic_name, phy, mii_reg0, mii_status, mii_advert);
+ /* Fixup for DLink with miswired PHY. */
+ if (mii_advert != to_advert) {
+ printf("%s: Advertising %hX on PHY %d previously advertising %hX.\n",
+ tp->nic_name, to_advert, phy, mii_advert);
+ mdio_write(nic, phy, 4, to_advert);
+ }
+ /* Enable autonegotiation: some boards default to off. */
+ mdio_write(nic, phy, 0, mii_reg0 |
+ (tp->full_duplex ? 0x1100 : 0x1000) |
+ (media_cap[tp->default_port]&MediaIs100 ? 0x2000:0));
+ }
+ }
+ tp->mii_cnt = phy_idx;
+ if (tp->mtable && tp->mtable->has_mii && phy_idx == 0) {
+ printf("%s: ***WARNING***: No MII transceiver found!\n",
+ tp->nic_name);
+ tp->phys[0] = 1;
+ }
+ }
+
+ /* Reset the xcvr interface and turn on heartbeat. */
+ switch (tp->chip_id) {
+ case DC21040:
+ outl(0x00000000, ioaddr + CSR13);
+ outl(0x00000004, ioaddr + CSR13);
+ break;
+ case DC21041:
+ /* This is nway_start(). */
+ if (tp->sym_advertise == 0)
+ tp->sym_advertise = 0x0061;
+ outl(0x00000000, ioaddr + CSR13);
+ outl(0xFFFFFFFF, ioaddr + CSR14);
+ outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
+ outl(inl(ioaddr + CSR6) | 0x0200, ioaddr + CSR6);
+ outl(0x0000EF01, ioaddr + CSR13);
+ break;
+ case DC21140: default:
+ if (tp->mtable)
+ outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
+ break;
+ case DC21142:
+ case PNIC2:
+ if (tp->mii_cnt || media_cap[tp->if_port] & MediaIsMII) {
+ outl(0x82020000, ioaddr + CSR6);
+ outl(0x0000, ioaddr + CSR13);
+ outl(0x0000, ioaddr + CSR14);
+ outl(0x820E0000, ioaddr + CSR6);
+ } else
+ nway_start(nic);
+ break;
+ case LC82C168:
+ if ( ! tp->mii_cnt) {
+ tp->nway = 1;
+ tp->nwayset = 0;
+ outl(0x00420000, ioaddr + CSR6);
+ outl(0x30, ioaddr + CSR12);
+ outl(0x0001F078, ioaddr + 0xB8);
+ outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
+ }
+ break;
+ case MX98713: case COMPEX9881:
+ outl(0x00000000, ioaddr + CSR6);
+ outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */
+ outl(0x00000001, ioaddr + CSR13);
+ break;
+ case MX98715: case MX98725:
+ outl(0x01a80000, ioaddr + CSR6);
+ outl(0xFFFFFFFF, ioaddr + CSR14);
+ outl(0x00001000, ioaddr + CSR12);
+ break;
+ case COMET:
+ /* No initialization necessary. */
+ break;
+ }
+}
+
+static void nway_start(struct nic *nic __unused)
+{
+ int csr14 = ((tp->sym_advertise & 0x0780) << 9) |
+ ((tp->sym_advertise&0x0020)<<1) | 0xffbf;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("nway_start\n");
+#endif
+
+ tp->if_port = 0;
+ tp->nway = tp->mediasense = 1;
+ tp->nwayset = tp->lpar = 0;
+ if (tp->chip_id == PNIC2) {
+ tp->csr6 = 0x01000000 | (tp->sym_advertise & 0x0040 ? 0x0200 : 0);
+ return;
+ }
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: Restarting internal NWay autonegotiation, %X.\n",
+ tp->nic_name, csr14);
+#endif
+ outl(0x0001, ioaddr + CSR13);
+ outl(csr14, ioaddr + CSR14);
+ tp->csr6 = 0x82420000 | (tp->sym_advertise & 0x0040 ? 0x0200 : 0);
+ outl(tp->csr6, ioaddr + CSR6);
+ if (tp->mtable && tp->mtable->csr15dir) {
+ outl(tp->mtable->csr15dir, ioaddr + CSR15);
+ outl(tp->mtable->csr15val, ioaddr + CSR15);
+ } else if (tp->chip_id != PNIC2)
+ outw(0x0008, ioaddr + CSR15);
+ if (tp->chip_id == DC21041) /* Trigger NWAY. */
+ outl(0xEF01, ioaddr + CSR12);
+ else
+ outl(0x1301, ioaddr + CSR12);
+}
+
+static void init_media(struct nic *nic)
+{
+ int i;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("init_media\n");
+#endif
+
+ tp->saved_if_port = tp->if_port;
+ if (tp->if_port == 0)
+ tp->if_port = tp->default_port;
+
+ /* Allow selecting a default media. */
+ i = 0;
+ if (tp->mtable == NULL)
+ goto media_picked;
+ if (tp->if_port) {
+ int looking_for = media_cap[tp->if_port] & MediaIsMII ? 11 :
+ (tp->if_port == 12 ? 0 : tp->if_port);
+ for (i = 0; i < tp->mtable->leafcount; i++)
+ if (tp->mtable->mleaf[i].media == looking_for) {
+ printf("%s: Using user-specified media %s.\n",
+ tp->nic_name, medianame[tp->if_port]);
+ goto media_picked;
+ }
+ }
+ if ((tp->mtable->defaultmedia & 0x0800) == 0) {
+ int looking_for = tp->mtable->defaultmedia & 15;
+ for (i = 0; i < tp->mtable->leafcount; i++)
+ if (tp->mtable->mleaf[i].media == looking_for) {
+ printf("%s: Using EEPROM-set media %s.\n",
+ tp->nic_name, medianame[looking_for]);
+ goto media_picked;
+ }
+ }
+ /* Start sensing first non-full-duplex media. */
+ for (i = tp->mtable->leafcount - 1;
+ (media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--)
+ ;
+ media_picked:
+
+ tp->csr6 = 0;
+ tp->cur_index = i;
+ tp->nwayset = 0;
+
+ if (tp->if_port) {
+ if (tp->chip_id == DC21143 && media_cap[tp->if_port] & MediaIsMII) {
+ /* We must reset the media CSRs when we force-select MII mode. */
+ outl(0x0000, ioaddr + CSR13);
+ outl(0x0000, ioaddr + CSR14);
+ outl(0x0008, ioaddr + CSR15);
+ }
+ select_media(nic, 1);
+ return;
+ }
+ switch(tp->chip_id) {
+ case DC21041:
+ /* tp->nway = 1;*/
+ nway_start(nic);
+ break;
+ case DC21142:
+ if (tp->mii_cnt) {
+ select_media(nic, 1);
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: Using MII transceiver %d, status %hX.\n",
+ tp->nic_name, tp->phys[0], mdio_read(nic, tp->phys[0], 1));
+#endif
+ outl(0x82020000, ioaddr + CSR6);
+ tp->csr6 = 0x820E0000;
+ tp->if_port = 11;
+ outl(0x0000, ioaddr + CSR13);
+ outl(0x0000, ioaddr + CSR14);
+ } else
+ nway_start(nic);
+ break;
+ case PNIC2:
+ nway_start(nic);
+ break;
+ case LC82C168:
+ if (tp->mii_cnt) {
+ tp->if_port = 11;
+ tp->csr6 = 0x814C0000 | (tp->full_duplex ? 0x0200 : 0);
+ outl(0x0001, ioaddr + CSR15);
+ } else if (inl(ioaddr + CSR5) & TPLnkPass)
+ pnic_do_nway(nic);
+ else {
+ /* Start with 10mbps to do autonegotiation. */
+ outl(0x32, ioaddr + CSR12);
+ tp->csr6 = 0x00420000;
+ outl(0x0001B078, ioaddr + 0xB8);
+ outl(0x0201B078, ioaddr + 0xB8);
+ }
+ break;
+ case MX98713: case COMPEX9881:
+ tp->if_port = 0;
+ tp->csr6 = 0x01880000 | (tp->full_duplex ? 0x0200 : 0);
+ outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80);
+ break;
+ case MX98715: case MX98725:
+ /* Provided by BOLO, Macronix - 12/10/1998. */
+ tp->if_port = 0;
+ tp->csr6 = 0x01a80200;
+ outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80);
+ outl(0x11000 | inw(ioaddr + 0xa0), ioaddr + 0xa0);
+ break;
+ case COMET:
+ tp->if_port = 0;
+ tp->csr6 = 0x00040000;
+ break;
+ case AX88140: case AX88141:
+ tp->csr6 = tp->mii_cnt ? 0x00040100 : 0x00000100;
+ break;
+ default:
+ select_media(nic, 1);
+ }
+}
+
+static void pnic_do_nway(struct nic *nic __unused)
+{
+ u32 phy_reg = inl(ioaddr + 0xB8);
+ u32 new_csr6 = tp->csr6 & ~0x40C40200;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("pnic_do_nway\n");
+#endif
+
+ if (phy_reg & 0x78000000) { /* Ignore baseT4 */
+ if (phy_reg & 0x20000000) tp->if_port = 5;
+ else if (phy_reg & 0x40000000) tp->if_port = 3;
+ else if (phy_reg & 0x10000000) tp->if_port = 4;
+ else if (phy_reg & 0x08000000) tp->if_port = 0;
+ tp->nwayset = 1;
+ new_csr6 = (tp->if_port & 1) ? 0x01860000 : 0x00420000;
+ outl(0x32 | (tp->if_port & 1), ioaddr + CSR12);
+ if (tp->if_port & 1)
+ outl(0x1F868, ioaddr + 0xB8);
+ if (phy_reg & 0x30000000) {
+ tp->full_duplex = 1;
+ new_csr6 |= 0x00000200;
+ }
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: PNIC autonegotiated status %X, %s.\n",
+ tp->nic_name, phy_reg, medianame[tp->if_port]);
+#endif
+ if (tp->csr6 != new_csr6) {
+ tp->csr6 = new_csr6;
+ outl(tp->csr6 | 0x0002, ioaddr + CSR6); /* Restart Tx */
+ outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ }
+ }
+}
+
+/* Set up the transceiver control registers for the selected media type. */
+static void select_media(struct nic *nic, int startup)
+{
+ struct mediatable *mtable = tp->mtable;
+ u32 new_csr6;
+ int i;
+
+#ifdef TULIP_DEBUG_WHERE
+ whereami("select_media\n");
+#endif
+
+ if (mtable) {
+ struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index];
+ unsigned char *p = mleaf->leafdata;
+ switch (mleaf->type) {
+ case 0: /* 21140 non-MII xcvr. */
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: Using a 21140 non-MII transceiver"
+ " with control setting %hhX.\n",
+ tp->nic_name, p[1]);
+#endif
+ tp->if_port = p[0];
+ if (startup)
+ outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
+ outl(p[1], ioaddr + CSR12);
+ new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18);
+ break;
+ case 2: case 4: {
+ u16 setup[5];
+ u32 csr13val, csr14val, csr15dir, csr15val;
+ for (i = 0; i < 5; i++)
+ setup[i] = get_u16(&p[i*2 + 1]);
+
+ tp->if_port = p[0] & 15;
+ if (media_cap[tp->if_port] & MediaAlwaysFD)
+ tp->full_duplex = 1;
+
+ if (startup && mtable->has_reset) {
+ struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset];
+ unsigned char *rst = rleaf->leafdata;
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: Resetting the transceiver.\n",
+ tp->nic_name);
+#endif
+ for (i = 0; i < rst[0]; i++)
+ outl(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15);
+ }
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: 21143 non-MII %s transceiver control "
+ "%hX/%hX.\n",
+ tp->nic_name, medianame[tp->if_port], setup[0], setup[1]);
+#endif
+ if (p[0] & 0x40) { /* SIA (CSR13-15) setup values are provided. */
+ csr13val = setup[0];
+ csr14val = setup[1];
+ csr15dir = (setup[3]<<16) | setup[2];
+ csr15val = (setup[4]<<16) | setup[2];
+ outl(0, ioaddr + CSR13);
+ outl(csr14val, ioaddr + CSR14);
+ outl(csr15dir, ioaddr + CSR15); /* Direction */
+ outl(csr15val, ioaddr + CSR15); /* Data */
+ outl(csr13val, ioaddr + CSR13);
+ } else {
+ csr13val = 1;
+ csr14val = 0x0003FF7F;
+ csr15dir = (setup[0]<<16) | 0x0008;
+ csr15val = (setup[1]<<16) | 0x0008;
+ if (tp->if_port <= 4)
+ csr14val = t21142_csr14[tp->if_port];
+ if (startup) {
+ outl(0, ioaddr + CSR13);
+ outl(csr14val, ioaddr + CSR14);
+ }
+ outl(csr15dir, ioaddr + CSR15); /* Direction */
+ outl(csr15val, ioaddr + CSR15); /* Data */
+ if (startup) outl(csr13val, ioaddr + CSR13);
+ }
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: Setting CSR15 to %X/%X.\n",
+ tp->nic_name, csr15dir, csr15val);
+#endif
+ if (mleaf->type == 4)
+ new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18);
+ else
+ new_csr6 = 0x82420000;
+ break;
+ }
+ case 1: case 3: {
+ int phy_num = p[0];
+ int init_length = p[1];
+ u16 *misc_info;
+
+ tp->if_port = 11;
+ new_csr6 = 0x020E0000;
+ if (mleaf->type == 3) { /* 21142 */
+ u16 *init_sequence = (u16*)(p+2);
+ u16 *reset_sequence = &((u16*)(p+3))[init_length];
+ int reset_length = p[2 + init_length*2];
+ misc_info = reset_sequence + reset_length;
+ if (startup)
+ for (i = 0; i < reset_length; i++)
+ outl(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15);
+ for (i = 0; i < init_length; i++)
+ outl(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15);
+ } else {
+ u8 *init_sequence = p + 2;
+ u8 *reset_sequence = p + 3 + init_length;
+ int reset_length = p[2 + init_length];
+ misc_info = (u16*)(reset_sequence + reset_length);
+ if (startup) {
+ outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
+ for (i = 0; i < reset_length; i++)
+ outl(reset_sequence[i], ioaddr + CSR12);
+ }
+ for (i = 0; i < init_length; i++)
+ outl(init_sequence[i], ioaddr + CSR12);
+ }
+ tp->advertising[phy_num] = get_u16(&misc_info[1]) | 1;
+ if (startup < 2) {
+ if (tp->mii_advertise == 0)
+ tp->mii_advertise = tp->advertising[phy_num];
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: Advertising %hX on MII %d.\n",
+ tp->nic_name, tp->mii_advertise, tp->phys[phy_num]);
+#endif
+ mdio_write(nic, tp->phys[phy_num], 4, tp->mii_advertise);
+ }
+ break;
+ }
+ default:
+ printf("%s: Invalid media table selection %d.\n",
+ tp->nic_name, mleaf->type);
+ new_csr6 = 0x020E0000;
+ }
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: Using media type %s, CSR12 is %hhX.\n",
+ tp->nic_name, medianame[tp->if_port],
+ inl(ioaddr + CSR12) & 0xff);
+#endif
+ } else if (tp->chip_id == DC21041) {
+ int port = tp->if_port <= 4 ? tp->if_port : 0;
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: 21041 using media %s, CSR12 is %hX.\n",
+ tp->nic_name, medianame[port == 3 ? 12: port],
+ inl(ioaddr + CSR12));
+#endif
+ outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
+ outl(t21041_csr14[port], ioaddr + CSR14);
+ outl(t21041_csr15[port], ioaddr + CSR15);
+ outl(t21041_csr13[port], ioaddr + CSR13);
+ new_csr6 = 0x80020000;
+ } else if (tp->chip_id == LC82C168) {
+ if (startup && ! tp->medialock)
+ tp->if_port = tp->mii_cnt ? 11 : 0;
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: PNIC PHY status is %hX, media %s.\n",
+ tp->nic_name, inl(ioaddr + 0xB8), medianame[tp->if_port]);
+#endif
+ if (tp->mii_cnt) {
+ new_csr6 = 0x810C0000;
+ outl(0x0001, ioaddr + CSR15);
+ outl(0x0201B07A, ioaddr + 0xB8);
+ } else if (startup) {
+ /* Start with 10mbps to do autonegotiation. */
+ outl(0x32, ioaddr + CSR12);
+ new_csr6 = 0x00420000;
+ outl(0x0001B078, ioaddr + 0xB8);
+ outl(0x0201B078, ioaddr + 0xB8);
+ } else if (tp->if_port == 3 || tp->if_port == 5) {
+ outl(0x33, ioaddr + CSR12);
+ new_csr6 = 0x01860000;
+ /* Trigger autonegotiation. */
+ outl(startup ? 0x0201F868 : 0x0001F868, ioaddr + 0xB8);
+ } else {
+ outl(0x32, ioaddr + CSR12);
+ new_csr6 = 0x00420000;
+ outl(0x1F078, ioaddr + 0xB8);
+ }
+ } else if (tp->chip_id == DC21040) { /* 21040 */
+ /* Turn on the xcvr interface. */
+#ifdef TULIP_DEBUG
+ int csr12 = inl(ioaddr + CSR12);
+ if (tulip_debug > 1)
+ printf("%s: 21040 media type is %s, CSR12 is %hhX.\n",
+ tp->nic_name, medianame[tp->if_port], csr12);
+#endif
+ if (media_cap[tp->if_port] & MediaAlwaysFD)
+ tp->full_duplex = 1;
+ new_csr6 = 0x20000;
+ /* Set the full duplux match frame. */
+ outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11);
+ outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
+ if (t21040_csr13[tp->if_port] & 8) {
+ outl(0x0705, ioaddr + CSR14);
+ outl(0x0006, ioaddr + CSR15);
+ } else {
+ outl(0xffff, ioaddr + CSR14);
+ outl(0x0000, ioaddr + CSR15);
+ }
+ outl(0x8f01 | t21040_csr13[tp->if_port], ioaddr + CSR13);
+ } else { /* Unknown chip type with no media table. */
+ if (tp->default_port == 0)
+ tp->if_port = tp->mii_cnt ? 11 : 3;
+ if (media_cap[tp->if_port] & MediaIsMII) {
+ new_csr6 = 0x020E0000;
+ } else if (media_cap[tp->if_port] & MediaIsFx) {
+ new_csr6 = 0x028600000;
+ } else
+ new_csr6 = 0x038600000;
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: No media description table, assuming "
+ "%s transceiver, CSR12 %hhX.\n",
+ tp->nic_name, medianame[tp->if_port],
+ inl(ioaddr + CSR12));
+#endif
+ }
+
+ tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0);
+ return;
+}
+
+/*
+ Check the MII negotiated duplex and change the CSR6 setting if
+ required.
+ Return 0 if everything is OK.
+ Return < 0 if the transceiver is missing or has no link beat.
+*/
+static int tulip_check_duplex(struct nic *nic)
+{
+ unsigned int bmsr, lpa, negotiated, new_csr6;
+
+ bmsr = mdio_read(nic, tp->phys[0], 1);
+ lpa = mdio_read(nic, tp->phys[0], 5);
+
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: MII status %#x, Link partner report "
+ "%#x.\n", tp->nic_name, bmsr, lpa);
+#endif
+
+ if (bmsr == 0xffff)
+ return -2;
+ if ((bmsr & 4) == 0) {
+ int new_bmsr = mdio_read(nic, tp->phys[0], 1);
+ if ((new_bmsr & 4) == 0) {
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 1)
+ printf("%s: No link beat on the MII interface,"
+ " status %#x.\n", tp->nic_name,
+ new_bmsr);
+#endif
+ return -1;
+ }
+ }
+ tp->full_duplex = lpa & 0x140;
+
+ new_csr6 = tp->csr6;
+ negotiated = lpa & tp->advertising[0];
+
+ if(negotiated & 0x380) new_csr6 &= ~0x400000;
+ else new_csr6 |= 0x400000;
+ if (tp->full_duplex) new_csr6 |= 0x200;
+ else new_csr6 &= ~0x200;
+
+ if (new_csr6 != tp->csr6) {
+ tp->csr6 = new_csr6;
+
+#ifdef TULIP_DEBUG
+ if (tulip_debug > 0)
+ printf("%s: Setting %s-duplex based on MII"
+ "#%d link partner capability of %#x.\n",
+ tp->nic_name,
+ tp->full_duplex ? "full" : "half",
+ tp->phys[0], lpa);
+#endif
+ return 1;
+ }
+
+ return 0;
+}
+
+static struct pci_id tulip_nics[] = {
+PCI_ROM(0x1011, 0x0002, "dc21040", "Digital Tulip"),
+PCI_ROM(0x1011, 0x0009, "ds21140", "Digital Tulip Fast"),
+PCI_ROM(0x1011, 0x0014, "dc21041", "Digital Tulip+"),
+PCI_ROM(0x1011, 0x0019, "ds21142", "Digital Tulip 21142"),
+PCI_ROM(0x10b7, 0x9300, "3csoho100b-tx","3ComSOHO100B-TX"),
+PCI_ROM(0x10b9, 0x5261, "ali1563", "ALi 1563 integrated ethernet"),
+PCI_ROM(0x10d9, 0x0512, "mx98713", "Macronix MX987x3"),
+PCI_ROM(0x10d9, 0x0531, "mx98715", "Macronix MX987x5"),
+PCI_ROM(0x1113, 0x1217, "mxic-98715", "Macronix MX987x5"),
+PCI_ROM(0x11ad, 0xc115, "lc82c115", "LinkSys LNE100TX"),
+PCI_ROM(0x11ad, 0x0002, "82c168", "Netgear FA310TX"),
+PCI_ROM(0x1282, 0x9100, "dm9100", "Davicom 9100"),
+PCI_ROM(0x1282, 0x9102, "dm9102", "Davicom 9102"),
+PCI_ROM(0x1282, 0x9009, "dm9009", "Davicom 9009"),
+PCI_ROM(0x1282, 0x9132, "dm9132", "Davicom 9132"),
+PCI_ROM(0x1317, 0x0985, "centaur-p", "ADMtek Centaur-P"),
+PCI_ROM(0x1317, 0x0981, "an981", "ADMtek AN981 Comet"), /* ADMTek Centaur-P (stmicro) */
+PCI_ROM(0x1113, 0x1216, "an983", "ADMTek AN983 Comet"),
+PCI_ROM(0x1317, 0x9511, "an983b", "ADMTek Comet 983b"),
+PCI_ROM(0x1317, 0x1985, "centaur-c", "ADMTek Centaur-C"),
+PCI_ROM(0x8086, 0x0039, "intel21145", "Intel Tulip"),
+PCI_ROM(0x125b, 0x1400, "ax88140", "ASIX AX88140"),
+PCI_ROM(0x11f6, 0x9881, "rl100tx", "Compex RL100-TX"),
+PCI_ROM(0x115d, 0x0003, "xircomtulip", "Xircom Tulip"),
+PCI_ROM(0x104a, 0x0981, "tulip-0981", "Tulip 0x104a 0x0981"),
+PCI_ROM(0x104a, 0x2774, "tulip-2774", "Tulip 0x104a 0x2774"),
+PCI_ROM(0x1113, 0x9511, "tulip-9511", "Tulip 0x1113 0x9511"),
+PCI_ROM(0x1186, 0x1561, "tulip-1561", "Tulip 0x1186 0x1561"),
+PCI_ROM(0x1259, 0xa120, "tulip-a120", "Tulip 0x1259 0xa120"),
+PCI_ROM(0x13d1, 0xab02, "tulip-ab02", "Tulip 0x13d1 0xab02"),
+PCI_ROM(0x13d1, 0xab03, "tulip-ab03", "Tulip 0x13d1 0xab03"),
+PCI_ROM(0x13d1, 0xab08, "tulip-ab08", "Tulip 0x13d1 0xab08"),
+PCI_ROM(0x14f1, 0x1803, "lanfinity", "Conexant LANfinity"),
+PCI_ROM(0x1626, 0x8410, "tulip-8410", "Tulip 0x1626 0x8410"),
+PCI_ROM(0x1737, 0xab08, "tulip-1737-ab08","Tulip 0x1737 0xab08"),
+PCI_ROM(0x1737, 0xab09, "tulip-ab09", "Tulip 0x1737 0xab09"),
+};
+
+static struct pci_driver tulip_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "Tulip",
+ .probe = tulip_probe,
+ .ids = tulip_nics,
+ .id_count = sizeof(tulip_nics)/sizeof(tulip_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/tulip.txt b/src/drivers/net/tulip.txt
new file mode 100644
index 00000000..68b7b3b7
--- /dev/null
+++ b/src/drivers/net/tulip.txt
@@ -0,0 +1,53 @@
+This software may be used and distributed according to the terms of
+the GNU Public License, incorporated herein by reference.
+
+This is a tulip and clone driver for Etherboot. See the revision
+history in the tulip.c file for information on changes. This version
+of the driver incorporates changes from Bob Edwards and Paul Mackerras
+who cantributed changes to support the TRENDnet TE100-PCIA NIC which
+uses a genuine Intel 21143-PD chipset. There are also various code
+cleanups to make time-based activities more reliable.
+
+Of course you have to have all the usual Etherboot environment
+(bootp/dhcp/NFS) set up, and you need a Linux kernel with v0.91g
+(7.16.99) or later of the tulip.c driver compiled in to support some
+MX98715 based cards. That file is available at:
+
+ http://cesdis.gsfc.nasa.gov/linux/drivers/test/tulip.c
+
+NOTES
+
+I've tested this driver with a SOHOware Fast 10/100 Model SDA110A,
+a Linksys LNE100TX v2.0, and a Netgear FA310TX card, and it worked at
+both 10 and 100 mbits. Other cards based on the tulip family may work as
+well.
+
+These cards are about 20$US, are supported by Linux and now Etherboot,
+and being PCI, they auto-configure IRQ and IOADDR and auto-negotiate
+10/100 half/full duplex. It seems like a pretty good value compared to
+some of the pricier cards, and can lower the cost of building/adapting
+thin client workstations substantially while giving a considerable
+performance increase.
+
+On some PCI tulip clone chipsets (MX987x5, LC82C115, LC82C168) this driver
+lets the card choose the fastest speed it can negotiate with the peer
+device. On other cards, it chooses 10mbit half-duplex.
+
+I burned an AM27C256 (32KByte) EPROM with mx987x5.lzrom and it worked.
+According to the data sheet the MX98715A supports up to 64K (27C512)
+EPROMs,
+
+I've liberally commented the code and header files in the hope that it
+will help the next person who hacks the code or needs to support some
+tulip clone card, or wishes to add functionality.
+
+Anyway, please test this if you can on your tulip based card, and let
+me (mdc@thinguin.org) and the netboot list (netboot@baghira.han.de)
+know how things go. I also would appreciate code review by people who
+program. I'm a strong believer in "another set of eyes".
+
+Regards,
+
+Marty Connor
+mdc@thinguin.org
+http://www.thinguin.org/
diff --git a/src/drivers/net/via-rhine.c b/src/drivers/net/via-rhine.c
new file mode 100644
index 00000000..d5e4812e
--- /dev/null
+++ b/src/drivers/net/via-rhine.c
@@ -0,0 +1,1426 @@
+/* rhine.c:Fast Ethernet driver for Linux. */
+/*
+ Adapted 09-jan-2000 by Paolo Marini (paolom@prisma-eng.it)
+
+ originally written by Donald Becker.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License (GPL), incorporated herein by reference.
+ Drivers derived from this code also fall under the GPL and must retain
+ this authorship and copyright notice.
+
+ Under no circumstances are the authors responsible for
+ the proper functioning of this software, nor do the authors assume any
+ responsibility for damages incurred with its use.
+
+ This driver is designed for the VIA VT86C100A Rhine-II PCI Fast Ethernet
+ controller.
+
+*/
+
+static const char *version = "rhine.c v1.0.2 2004-10-29\n";
+
+/* A few user-configurable values. */
+
+// max time out delay time
+#define W_MAX_TIMEOUT 0x0FFFU
+
+/* Size of the in-memory receive ring. */
+#define RX_BUF_LEN_IDX 3 /* 0==8K, 1==16K, 2==32K, 3==64K */
+#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX)
+
+/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */
+#define TX_BUF_SIZE 1536
+#define RX_BUF_SIZE 1536
+
+/* PCI Tuning Parameters
+ Threshold is bytes transferred to chip before transmission starts. */
+#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */
+
+/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024. */
+#define RX_FIFO_THRESH 4 /* Rx buffer level before first PCI xfer. */
+#define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 bytes */
+#define TX_DMA_BURST 4
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT ((2000*HZ)/1000)
+
+#include "etherboot.h"
+#include "nic.h"
+#include "pci.h"
+#include "timer.h"
+
+/* define all ioaddr */
+
+#define byPAR0 ioaddr
+#define byRCR ioaddr + 6
+#define byTCR ioaddr + 7
+#define byCR0 ioaddr + 8
+#define byCR1 ioaddr + 9
+#define byISR0 ioaddr + 0x0c
+#define byISR1 ioaddr + 0x0d
+#define byIMR0 ioaddr + 0x0e
+#define byIMR1 ioaddr + 0x0f
+#define byMAR0 ioaddr + 0x10
+#define byMAR1 ioaddr + 0x11
+#define byMAR2 ioaddr + 0x12
+#define byMAR3 ioaddr + 0x13
+#define byMAR4 ioaddr + 0x14
+#define byMAR5 ioaddr + 0x15
+#define byMAR6 ioaddr + 0x16
+#define byMAR7 ioaddr + 0x17
+#define dwCurrentRxDescAddr ioaddr + 0x18
+#define dwCurrentTxDescAddr ioaddr + 0x1c
+#define dwCurrentRDSE0 ioaddr + 0x20
+#define dwCurrentRDSE1 ioaddr + 0x24
+#define dwCurrentRDSE2 ioaddr + 0x28
+#define dwCurrentRDSE3 ioaddr + 0x2c
+#define dwNextRDSE0 ioaddr + 0x30
+#define dwNextRDSE1 ioaddr + 0x34
+#define dwNextRDSE2 ioaddr + 0x38
+#define dwNextRDSE3 ioaddr + 0x3c
+#define dwCurrentTDSE0 ioaddr + 0x40
+#define dwCurrentTDSE1 ioaddr + 0x44
+#define dwCurrentTDSE2 ioaddr + 0x48
+#define dwCurrentTDSE3 ioaddr + 0x4c
+#define dwNextTDSE0 ioaddr + 0x50
+#define dwNextTDSE1 ioaddr + 0x54
+#define dwNextTDSE2 ioaddr + 0x58
+#define dwNextTDSE3 ioaddr + 0x5c
+#define dwCurrRxDMAPtr ioaddr + 0x60
+#define dwCurrTxDMAPtr ioaddr + 0x64
+#define byMPHY ioaddr + 0x6c
+#define byMIISR ioaddr + 0x6d
+#define byBCR0 ioaddr + 0x6e
+#define byBCR1 ioaddr + 0x6f
+#define byMIICR ioaddr + 0x70
+#define byMIIAD ioaddr + 0x71
+#define wMIIDATA ioaddr + 0x72
+#define byEECSR ioaddr + 0x74
+#define byTEST ioaddr + 0x75
+#define byGPIO ioaddr + 0x76
+#define byCFGA ioaddr + 0x78
+#define byCFGB ioaddr + 0x79
+#define byCFGC ioaddr + 0x7a
+#define byCFGD ioaddr + 0x7b
+#define wTallyCntMPA ioaddr + 0x7c
+#define wTallyCntCRC ioaddr + 0x7d
+#define bySTICKHW ioaddr + 0x83
+#define byWOLcrClr ioaddr + 0xA4
+#define byWOLcgClr ioaddr + 0xA7
+#define byPwrcsrClr ioaddr + 0xAC
+
+/*--------------------- Exioaddr Definitions -------------------------*/
+
+/*
+ * Bits in the RCR register
+ */
+
+#define RCR_RRFT2 0x80
+#define RCR_RRFT1 0x40
+#define RCR_RRFT0 0x20
+#define RCR_PROM 0x10
+#define RCR_AB 0x08
+#define RCR_AM 0x04
+#define RCR_AR 0x02
+#define RCR_SEP 0x01
+
+/*
+ * Bits in the TCR register
+ */
+
+#define TCR_RTSF 0x80
+#define TCR_RTFT1 0x40
+#define TCR_RTFT0 0x20
+#define TCR_OFSET 0x08
+#define TCR_LB1 0x04 /* loopback[1] */
+#define TCR_LB0 0x02 /* loopback[0] */
+
+/*
+ * Bits in the CR0 register
+ */
+
+#define CR0_RDMD 0x40 /* rx descriptor polling demand */
+#define CR0_TDMD 0x20 /* tx descriptor polling demand */
+#define CR0_TXON 0x10
+#define CR0_RXON 0x08
+#define CR0_STOP 0x04 /* stop NIC, default = 1 */
+#define CR0_STRT 0x02 /* start NIC */
+#define CR0_INIT 0x01 /* start init process */
+
+
+/*
+ * Bits in the CR1 register
+ */
+
+#define CR1_SFRST 0x80 /* software reset */
+#define CR1_RDMD1 0x40 /* RDMD1 */
+#define CR1_TDMD1 0x20 /* TDMD1 */
+#define CR1_KEYPAG 0x10 /* turn on par/key */
+#define CR1_DPOLL 0x08 /* disable rx/tx auto polling */
+#define CR1_FDX 0x04 /* full duplex mode */
+#define CR1_ETEN 0x02 /* early tx mode */
+#define CR1_EREN 0x01 /* early rx mode */
+
+/*
+ * Bits in the CR register
+ */
+
+#define CR_RDMD 0x0040 /* rx descriptor polling demand */
+#define CR_TDMD 0x0020 /* tx descriptor polling demand */
+#define CR_TXON 0x0010
+#define CR_RXON 0x0008
+#define CR_STOP 0x0004 /* stop NIC, default = 1 */
+#define CR_STRT 0x0002 /* start NIC */
+#define CR_INIT 0x0001 /* start init process */
+#define CR_SFRST 0x8000 /* software reset */
+#define CR_RDMD1 0x4000 /* RDMD1 */
+#define CR_TDMD1 0x2000 /* TDMD1 */
+#define CR_KEYPAG 0x1000 /* turn on par/key */
+#define CR_DPOLL 0x0800 /* disable rx/tx auto polling */
+#define CR_FDX 0x0400 /* full duplex mode */
+#define CR_ETEN 0x0200 /* early tx mode */
+#define CR_EREN 0x0100 /* early rx mode */
+
+/*
+ * Bits in the IMR0 register
+ */
+
+#define IMR0_CNTM 0x80
+#define IMR0_BEM 0x40
+#define IMR0_RUM 0x20
+#define IMR0_TUM 0x10
+#define IMR0_TXEM 0x08
+#define IMR0_RXEM 0x04
+#define IMR0_PTXM 0x02
+#define IMR0_PRXM 0x01
+
+/* define imrshadow */
+
+#define IMRShadow 0x5AFF
+
+/*
+ * Bits in the IMR1 register
+ */
+
+#define IMR1_INITM 0x80
+#define IMR1_SRCM 0x40
+#define IMR1_NBFM 0x10
+#define IMR1_PRAIM 0x08
+#define IMR1_RES0M 0x04
+#define IMR1_ETM 0x02
+#define IMR1_ERM 0x01
+
+/*
+ * Bits in the ISR register
+ */
+
+#define ISR_INITI 0x8000
+#define ISR_SRCI 0x4000
+#define ISR_ABTI 0x2000
+#define ISR_NORBF 0x1000
+#define ISR_PKTRA 0x0800
+#define ISR_RES0 0x0400
+#define ISR_ETI 0x0200
+#define ISR_ERI 0x0100
+#define ISR_CNT 0x0080
+#define ISR_BE 0x0040
+#define ISR_RU 0x0020
+#define ISR_TU 0x0010
+#define ISR_TXE 0x0008
+#define ISR_RXE 0x0004
+#define ISR_PTX 0x0002
+#define ISR_PRX 0x0001
+
+/*
+ * Bits in the ISR0 register
+ */
+
+#define ISR0_CNT 0x80
+#define ISR0_BE 0x40
+#define ISR0_RU 0x20
+#define ISR0_TU 0x10
+#define ISR0_TXE 0x08
+#define ISR0_RXE 0x04
+#define ISR0_PTX 0x02
+#define ISR0_PRX 0x01
+
+/*
+ * Bits in the ISR1 register
+ */
+
+#define ISR1_INITI 0x80
+#define ISR1_SRCI 0x40
+#define ISR1_NORBF 0x10
+#define ISR1_PKTRA 0x08
+#define ISR1_ETI 0x02
+#define ISR1_ERI 0x01
+
+/* ISR ABNORMAL CONDITION */
+
+#define ISR_ABNORMAL ISR_BE+ISR_RU+ISR_TU+ISR_CNT+ISR_NORBF+ISR_PKTRA
+
+/*
+ * Bits in the MIISR register
+ */
+
+#define MIISR_MIIERR 0x08
+#define MIISR_MRERR 0x04
+#define MIISR_LNKFL 0x02
+#define MIISR_SPEED 0x01
+
+/*
+ * Bits in the MIICR register
+ */
+
+#define MIICR_MAUTO 0x80
+#define MIICR_RCMD 0x40
+#define MIICR_WCMD 0x20
+#define MIICR_MDPM 0x10
+#define MIICR_MOUT 0x08
+#define MIICR_MDO 0x04
+#define MIICR_MDI 0x02
+#define MIICR_MDC 0x01
+
+/*
+ * Bits in the EECSR register
+ */
+
+#define EECSR_EEPR 0x80 /* eeprom programed status, 73h means programed */
+#define EECSR_EMBP 0x40 /* eeprom embeded programming */
+#define EECSR_AUTOLD 0x20 /* eeprom content reload */
+#define EECSR_DPM 0x10 /* eeprom direct programming */
+#define EECSR_CS 0x08 /* eeprom CS pin */
+#define EECSR_SK 0x04 /* eeprom SK pin */
+#define EECSR_DI 0x02 /* eeprom DI pin */
+#define EECSR_DO 0x01 /* eeprom DO pin */
+
+/*
+ * Bits in the BCR0 register
+ */
+
+#define BCR0_CRFT2 0x20
+#define BCR0_CRFT1 0x10
+#define BCR0_CRFT0 0x08
+#define BCR0_DMAL2 0x04
+#define BCR0_DMAL1 0x02
+#define BCR0_DMAL0 0x01
+
+/*
+ * Bits in the BCR1 register
+ */
+
+#define BCR1_CTSF 0x20
+#define BCR1_CTFT1 0x10
+#define BCR1_CTFT0 0x08
+#define BCR1_POT2 0x04
+#define BCR1_POT1 0x02
+#define BCR1_POT0 0x01
+
+/*
+ * Bits in the CFGA register
+ */
+
+#define CFGA_EELOAD 0x80 /* enable eeprom embeded and direct programming */
+#define CFGA_JUMPER 0x40
+#define CFGA_MTGPIO 0x08
+#define CFGA_T10EN 0x02
+#define CFGA_AUTO 0x01
+
+/*
+ * Bits in the CFGB register
+ */
+
+#define CFGB_PD 0x80
+#define CFGB_POLEN 0x02
+#define CFGB_LNKEN 0x01
+
+/*
+ * Bits in the CFGC register
+ */
+
+#define CFGC_M10TIO 0x80
+#define CFGC_M10POL 0x40
+#define CFGC_PHY1 0x20
+#define CFGC_PHY0 0x10
+#define CFGC_BTSEL 0x08
+#define CFGC_BPS2 0x04 /* bootrom select[2] */
+#define CFGC_BPS1 0x02 /* bootrom select[1] */
+#define CFGC_BPS0 0x01 /* bootrom select[0] */
+
+/*
+ * Bits in the CFGD register
+ */
+
+#define CFGD_GPIOEN 0x80
+#define CFGD_DIAG 0x40
+#define CFGD_MAGIC 0x10
+#define CFGD_RANDOM 0x08
+#define CFGD_CFDX 0x04
+#define CFGD_CEREN 0x02
+#define CFGD_CETEN 0x01
+
+/* Bits in RSR */
+#define RSR_RERR 0x00000001
+#define RSR_CRC 0x00000002
+#define RSR_FAE 0x00000004
+#define RSR_FOV 0x00000008
+#define RSR_LONG 0x00000010
+#define RSR_RUNT 0x00000020
+#define RSR_SERR 0x00000040
+#define RSR_BUFF 0x00000080
+#define RSR_EDP 0x00000100
+#define RSR_STP 0x00000200
+#define RSR_CHN 0x00000400
+#define RSR_PHY 0x00000800
+#define RSR_BAR 0x00001000
+#define RSR_MAR 0x00002000
+#define RSR_RXOK 0x00008000
+#define RSR_ABNORMAL RSR_RERR+RSR_LONG+RSR_RUNT
+
+/* Bits in TSR */
+#define TSR_NCR0 0x00000001
+#define TSR_NCR1 0x00000002
+#define TSR_NCR2 0x00000004
+#define TSR_NCR3 0x00000008
+#define TSR_COLS 0x00000010
+#define TSR_CDH 0x00000080
+#define TSR_ABT 0x00000100
+#define TSR_OWC 0x00000200
+#define TSR_CRS 0x00000400
+#define TSR_UDF 0x00000800
+#define TSR_TBUFF 0x00001000
+#define TSR_SERR 0x00002000
+#define TSR_JAB 0x00004000
+#define TSR_TERR 0x00008000
+#define TSR_ABNORMAL TSR_TERR+TSR_OWC+TSR_ABT+TSR_JAB+TSR_CRS
+#define TSR_OWN_BIT 0x80000000
+
+#define CB_DELAY_LOOP_WAIT 10 /* 10ms */
+/* enabled mask value of irq */
+
+#define W_IMR_MASK_VALUE 0x1BFF /* initial value of IMR */
+
+/* Ethernet address filter type */
+#define PKT_TYPE_DIRECTED 0x0001 /* obsolete, directed address is always accepted */
+#define PKT_TYPE_MULTICAST 0x0002
+#define PKT_TYPE_ALL_MULTICAST 0x0004
+#define PKT_TYPE_BROADCAST 0x0008
+#define PKT_TYPE_PROMISCUOUS 0x0020
+#define PKT_TYPE_LONG 0x2000
+#define PKT_TYPE_RUNT 0x4000
+#define PKT_TYPE_ERROR 0x8000 /* accept error packets, e.g. CRC error */
+
+/* Loopback mode */
+
+#define NIC_LB_NONE 0x00
+#define NIC_LB_INTERNAL 0x01
+#define NIC_LB_PHY 0x02 /* MII or Internal-10BaseT loopback */
+
+#define TX_RING_SIZE 2
+#define RX_RING_SIZE 2
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */
+
+#define PCI_REG_MODE3 0x53
+#define MODE3_MIION 0x04 /* in PCI_REG_MOD3 OF PCI space */
+
+enum rhine_revs {
+ VT86C100A = 0x00,
+ VTunknown0 = 0x20,
+ VT6102 = 0x40,
+ VT8231 = 0x50, /* Integrated MAC */
+ VT8233 = 0x60, /* Integrated MAC */
+ VT8235 = 0x74, /* Integrated MAC */
+ VT8237 = 0x78, /* Integrated MAC */
+ VTunknown1 = 0x7C,
+ VT6105 = 0x80,
+ VT6105_B0 = 0x83,
+ VT6105L = 0x8A,
+ VT6107 = 0x8C,
+ VTunknown2 = 0x8E,
+ VT6105M = 0x90,
+};
+
+/* Transmit and receive descriptors definition */
+
+struct rhine_tx_desc
+{
+ union VTC_tx_status_tag
+ {
+ struct
+ {
+ unsigned long ncro:1;
+ unsigned long ncr1:1;
+ unsigned long ncr2:1;
+ unsigned long ncr3:1;
+ unsigned long cols:1;
+ unsigned long reserve_1:2;
+ unsigned long cdh:1;
+ unsigned long abt:1;
+ unsigned long owc:1;
+ unsigned long crs:1;
+ unsigned long udf:1;
+ unsigned long tbuff:1;
+ unsigned long serr:1;
+ unsigned long jab:1;
+ unsigned long terr:1;
+ unsigned long reserve_2:15;
+ unsigned long own_bit:1;
+ }
+ bits;
+ unsigned long lw;
+ }
+ tx_status;
+
+ union VTC_tx_ctrl_tag
+ {
+ struct
+ {
+ unsigned long tx_buf_size:11;
+ unsigned long extend_tx_buf_size:4;
+ unsigned long chn:1;
+ unsigned long crc:1;
+ unsigned long reserve_1:4;
+ unsigned long stp:1;
+ unsigned long edp:1;
+ unsigned long ic:1;
+ unsigned long reserve_2:8;
+ }
+ bits;
+ unsigned long lw;
+ }
+ tx_ctrl;
+
+ unsigned long buf_addr_1:32;
+ unsigned long buf_addr_2:32;
+
+};
+
+struct rhine_rx_desc
+{
+ union VTC_rx_status_tag
+ {
+ struct
+ {
+ unsigned long rerr:1;
+ unsigned long crc_error:1;
+ unsigned long fae:1;
+ unsigned long fov:1;
+ unsigned long toolong:1;
+ unsigned long runt:1;
+ unsigned long serr:1;
+ unsigned long buff:1;
+ unsigned long edp:1;
+ unsigned long stp:1;
+ unsigned long chn:1;
+ unsigned long phy:1;
+ unsigned long bar:1;
+ unsigned long mar:1;
+ unsigned long reserve_1:1;
+ unsigned long rxok:1;
+ unsigned long frame_length:11;
+ unsigned long reverve_2:4;
+ unsigned long own_bit:1;
+ }
+ bits;
+ unsigned long lw;
+ }
+ rx_status;
+
+ union VTC_rx_ctrl_tag
+ {
+ struct
+ {
+ unsigned long rx_buf_size:11;
+ unsigned long extend_rx_buf_size:4;
+ unsigned long reserved_1:17;
+ }
+ bits;
+ unsigned long lw;
+ }
+ rx_ctrl;
+
+ unsigned long buf_addr_1:32;
+ unsigned long buf_addr_2:32;
+
+};
+
+
+/* The I/O extent. */
+#define rhine_TOTAL_SIZE 0x80
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry rhine_drv =
+ { "rhine", rhine_probe, rhine_TOTAL_SIZE, NULL };
+#endif
+
+static int rhine_debug = 1;
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This driver is designed for the VIA 86c100A Rhine-II PCI Fast Ethernet
+controller.
+
+II. Board-specific settings
+
+Boards with this chip are functional only in a bus-master PCI slot.
+
+Many operational settings are loaded from the EEPROM to the Config word at
+offset 0x78. This driver assumes that they are correct.
+If this driver is compiled to use PCI memory space operations the EEPROM
+must be configured to enable memory ops.
+
+III. Driver operation
+
+IIIa. Ring buffers
+
+This driver uses two statically allocated fixed-size descriptor lists
+formed into rings by a branch from the final descriptor to the beginning of
+the list. The ring sizes are set at compile time by RX/TX_RING_SIZE.
+
+IIIb/c. Transmit/Receive Structure
+
+This driver attempts to use a zero-copy receive and transmit scheme.
+
+Alas, all data buffers are required to start on a 32 bit boundary, so
+the driver must often copy transmit packets into bounce buffers.
+
+The driver allocates full frame size skbuffs for the Rx ring buffers at
+open() time and passes the skb->data field to the chip as receive data
+buffers. When an incoming frame is less than RX_COPYBREAK bytes long,
+a fresh skbuff is allocated and the frame is copied to the new skbuff.
+When the incoming frame is larger, the skbuff is passed directly up the
+protocol stack. Buffers consumed this way are replaced by newly allocated
+skbuffs in the last phase of netdev_rx().
+
+The RX_COPYBREAK value is chosen to trade-off the memory wasted by
+using a full-sized skbuff for small frames vs. the copying costs of larger
+frames. New boards are typically used in generously configured machines
+and the underfilled buffers have negligible impact compared to the benefit of
+a single allocation size, so the default value of zero results in never
+copying packets. When copying is done, the cost is usually mitigated by using
+a combined copy/checksum routine. Copying also preloads the cache, which is
+most useful with small frames.
+
+Since the VIA chips are only able to transfer data to buffers on 32 bit
+boundaries, the the IP header at offset 14 in an ethernet frame isn't
+longword aligned for further processing. Copying these unaligned buffers
+has the beneficial effect of 16-byte aligning the IP header.
+
+IIId. Synchronization
+
+The driver runs as two independent, single-threaded flows of control. One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag. The other thread is the interrupt handler, which is single
+threaded by the hardware and interrupt handling software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'lp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring. After reaping the stats, it marks the Tx queue entry as
+empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it
+clears both the tx_full and tbusy flags.
+
+IV. Notes
+
+IVb. References
+
+Preliminary VT86C100A manual from http://www.via.com.tw/
+http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html
+http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
+
+IVc. Errata
+
+The VT86C100A manual is not reliable information.
+The chip does not handle unaligned transmit or receive buffers, resulting
+in significant performance degradation for bounce buffer copies on transmit
+and unaligned IP headers on receive.
+The chip does not pad to minimum transmit length.
+
+*/
+
+/* The rest of these values should never change. */
+#define NUM_TX_DESC 2 /* Number of Tx descriptor registers. */
+
+static struct rhine_private
+{
+ char devname[8]; /* Used only for kernel debugging. */
+ const char *product_name;
+ struct rhine_rx_desc *rx_ring;
+ struct rhine_tx_desc *tx_ring;
+ char *rx_buffs[RX_RING_SIZE];
+ char *tx_buffs[TX_RING_SIZE];
+
+ /* temporary Rx buffers. */
+
+ int chip_id;
+ int chip_revision;
+ unsigned short ioaddr;
+ unsigned int cur_rx, cur_tx; /* The next free and used entries */
+ unsigned int dirty_rx, dirty_tx;
+ /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+ struct sk_buff *tx_skbuff[TX_RING_SIZE];
+ unsigned char mc_filter[8]; /* Current multicast filter. */
+ char phys[4]; /* MII device addresses. */
+ unsigned int tx_full:1; /* The Tx queue is full. */
+ unsigned int full_duplex:1; /* Full-duplex operation requested. */
+ unsigned int default_port:4; /* Last dev->if_port value. */
+ unsigned int media2:4; /* Secondary monitored media port. */
+ unsigned int medialock:1; /* Don't sense media type. */
+ unsigned int mediasense:1; /* Media sensing in progress. */
+}
+rhine;
+
+static void rhine_probe1 (struct nic *nic, struct pci_device *pci, int ioaddr,
+ int chip_id, int options);
+static int QueryAuto (int);
+static int ReadMII (int byMIIIndex, int);
+static void WriteMII (char, char, char, int);
+static void MIIDelay (void);
+static void rhine_init_ring (struct nic *dev);
+static void rhine_disable (struct dev *dev);
+static void rhine_reset (struct nic *nic);
+static int rhine_poll (struct nic *nic, int retreive);
+static void rhine_transmit (struct nic *nic, const char *d, unsigned int t,
+ unsigned int s, const char *p);
+static void reload_eeprom(int ioaddr);
+
+
+static void reload_eeprom(int ioaddr)
+{
+ int i;
+ outb(0x20, byEECSR);
+ /* Typically 2 cycles to reload. */
+ for (i = 0; i < 150; i++)
+ if (! (inb(byEECSR) & 0x20))
+ break;
+}
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void
+rhine_init_ring (struct nic *nic)
+{
+ struct rhine_private *tp = (struct rhine_private *) nic->priv_data;
+ int i;
+
+ tp->tx_full = 0;
+ tp->cur_rx = tp->cur_tx = 0;
+ tp->dirty_rx = tp->dirty_tx = 0;
+
+ for (i = 0; i < RX_RING_SIZE; i++)
+ {
+
+ tp->rx_ring[i].rx_status.bits.own_bit = 1;
+ tp->rx_ring[i].rx_ctrl.bits.rx_buf_size = 1536;
+
+ tp->rx_ring[i].buf_addr_1 = virt_to_bus (tp->rx_buffs[i]);
+ tp->rx_ring[i].buf_addr_2 = virt_to_bus (&tp->rx_ring[i + 1]);
+ /* printf("[%d]buf1=%hX,buf2=%hX",i,tp->rx_ring[i].buf_addr_1,tp->rx_ring[i].buf_addr_2); */
+ }
+ /* Mark the last entry as wrapping the ring. */
+ /* tp->rx_ring[i-1].rx_ctrl.bits.rx_buf_size =1518; */
+ tp->rx_ring[i - 1].buf_addr_2 = virt_to_bus (&tp->rx_ring[0]);
+ /*printf("[%d]buf1=%hX,buf2=%hX",i-1,tp->rx_ring[i-1].buf_addr_1,tp->rx_ring[i-1].buf_addr_2); */
+
+ /* The Tx buffer descriptor is filled in as needed, but we
+ do need to clear the ownership bit. */
+
+ for (i = 0; i < TX_RING_SIZE; i++)
+ {
+
+ tp->tx_ring[i].tx_status.lw = 0;
+ tp->tx_ring[i].tx_ctrl.lw = 0x00e08000;
+ tp->tx_ring[i].buf_addr_1 = virt_to_bus (tp->tx_buffs[i]);
+ tp->tx_ring[i].buf_addr_2 = virt_to_bus (&tp->tx_ring[i + 1]);
+ /* printf("[%d]buf1=%hX,buf2=%hX",i,tp->tx_ring[i].buf_addr_1,tp->tx_ring[i].buf_addr_2); */
+ }
+
+ tp->tx_ring[i - 1].buf_addr_2 = virt_to_bus (&tp->tx_ring[0]);
+ /* printf("[%d]buf1=%hX,buf2=%hX",i,tp->tx_ring[i-1].buf_addr_1,tp->tx_ring[i-1].buf_addr_2); */
+}
+
+int
+QueryAuto (int ioaddr)
+{
+ int byMIIIndex;
+ int MIIReturn;
+
+ int advertising,mii_reg5;
+ int negociated;
+
+ byMIIIndex = 0x04;
+ MIIReturn = ReadMII (byMIIIndex, ioaddr);
+ advertising=MIIReturn;
+
+ byMIIIndex = 0x05;
+ MIIReturn = ReadMII (byMIIIndex, ioaddr);
+ mii_reg5=MIIReturn;
+
+ negociated=mii_reg5 & advertising;
+
+ if ( (negociated & 0x100) || (negociated & 0x1C0) == 0x40 )
+ return 1;
+ else
+ return 0;
+
+}
+
+int
+ReadMII (int byMIIIndex, int ioaddr)
+{
+ int ReturnMII;
+ char byMIIAdrbak;
+ char byMIICRbak;
+ char byMIItemp;
+
+ byMIIAdrbak = inb (byMIIAD);
+ byMIICRbak = inb (byMIICR);
+ outb (byMIICRbak & 0x7f, byMIICR);
+ MIIDelay ();
+
+ outb (byMIIIndex, byMIIAD);
+ MIIDelay ();
+
+ outb (inb (byMIICR) | 0x40, byMIICR);
+
+ byMIItemp = inb (byMIICR);
+ byMIItemp = byMIItemp & 0x40;
+
+ load_timer2(2*TICKS_PER_MS);
+ while (byMIItemp != 0 && timer2_running())
+ {
+ byMIItemp = inb (byMIICR);
+ byMIItemp = byMIItemp & 0x40;
+ }
+ MIIDelay ();
+
+ ReturnMII = inw (wMIIDATA);
+
+ outb (byMIIAdrbak, byMIIAD);
+ outb (byMIICRbak, byMIICR);
+ MIIDelay ();
+
+ return (ReturnMII);
+
+}
+
+void
+WriteMII (char byMIISetByte, char byMIISetBit, char byMIIOP, int ioaddr)
+{
+ int ReadMIItmp;
+ int MIIMask;
+ char byMIIAdrbak;
+ char byMIICRbak;
+ char byMIItemp;
+
+
+ byMIIAdrbak = inb (byMIIAD);
+
+ byMIICRbak = inb (byMIICR);
+ outb (byMIICRbak & 0x7f, byMIICR);
+ MIIDelay ();
+ outb (byMIISetByte, byMIIAD);
+ MIIDelay ();
+
+ outb (inb (byMIICR) | 0x40, byMIICR);
+
+ byMIItemp = inb (byMIICR);
+ byMIItemp = byMIItemp & 0x40;
+
+ load_timer2(2*TICKS_PER_MS);
+ while (byMIItemp != 0 && timer2_running())
+ {
+ byMIItemp = inb (byMIICR);
+ byMIItemp = byMIItemp & 0x40;
+ }
+ MIIDelay ();
+
+ ReadMIItmp = inw (wMIIDATA);
+ MIIMask = 0x0001;
+ MIIMask = MIIMask << byMIISetBit;
+
+
+ if (byMIIOP == 0)
+ {
+ MIIMask = ~MIIMask;
+ ReadMIItmp = ReadMIItmp & MIIMask;
+ }
+ else
+ {
+ ReadMIItmp = ReadMIItmp | MIIMask;
+
+ }
+ outw (ReadMIItmp, wMIIDATA);
+ MIIDelay ();
+
+ outb (inb (byMIICR) | 0x20, byMIICR);
+ byMIItemp = inb (byMIICR);
+ byMIItemp = byMIItemp & 0x20;
+
+ load_timer2(2*TICKS_PER_MS);
+ while (byMIItemp != 0 && timer2_running())
+ {
+ byMIItemp = inb (byMIICR);
+ byMIItemp = byMIItemp & 0x20;
+ }
+ MIIDelay ();
+
+ outb (byMIIAdrbak & 0x7f, byMIIAD);
+ outb (byMIICRbak, byMIICR);
+ MIIDelay ();
+
+}
+
+void
+MIIDelay (void)
+{
+ int i;
+ for (i = 0; i < 0x7fff; i++)
+ {
+ inb (0x61);
+ inb (0x61);
+ inb (0x61);
+ inb (0x61);
+ }
+}
+
+/* Offsets to the device registers. */
+enum register_offsets {
+ StationAddr=0x00, RxConfig=0x06, TxConfig=0x07, ChipCmd=0x08,
+ IntrStatus=0x0C, IntrEnable=0x0E,
+ MulticastFilter0=0x10, MulticastFilter1=0x14,
+ RxRingPtr=0x18, TxRingPtr=0x1C, GFIFOTest=0x54,
+ MIIPhyAddr=0x6C, MIIStatus=0x6D, PCIBusConfig=0x6E,
+ MIICmd=0x70, MIIRegAddr=0x71, MIIData=0x72, MACRegEEcsr=0x74,
+ ConfigA=0x78, ConfigB=0x79, ConfigC=0x7A, ConfigD=0x7B,
+ RxMissed=0x7C, RxCRCErrs=0x7E, MiscCmd=0x81,
+ StickyHW=0x83, IntrStatus2=0x84, WOLcrClr=0xA4, WOLcgClr=0xA7,
+ PwrcsrClr=0xAC,
+};
+
+/* Bits in the interrupt status/mask registers. */
+enum intr_status_bits {
+ IntrRxDone=0x0001, IntrRxErr=0x0004, IntrRxEmpty=0x0020,
+ IntrTxDone=0x0002, IntrTxError=0x0008, IntrTxUnderrun=0x0210,
+ IntrPCIErr=0x0040,
+ IntrStatsMax=0x0080, IntrRxEarly=0x0100,
+ IntrRxOverflow=0x0400, IntrRxDropped=0x0800, IntrRxNoBuf=0x1000,
+ IntrTxAborted=0x2000, IntrLinkChange=0x4000,
+ IntrRxWakeUp=0x8000,
+ IntrNormalSummary=0x0003, IntrAbnormalSummary=0xC260,
+ IntrTxDescRace=0x080000, /* mapped from IntrStatus2 */
+ IntrTxErrSummary=0x082218,
+};
+#define DEFAULT_INTR (IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow | \
+ IntrRxDropped | IntrRxNoBuf)
+
+/***************************************************************************
+ IRQ - PXE IRQ Handler
+***************************************************************************/
+void rhine_irq ( struct nic *nic, irq_action_t action ) {
+ struct rhine_private *tp = (struct rhine_private *) nic->priv_data;
+ /* Enable interrupts by setting the interrupt mask. */
+ unsigned int intr_status;
+
+ switch ( action ) {
+ case DISABLE :
+ case ENABLE :
+ intr_status = inw(nic->ioaddr + IntrStatus);
+ /* On Rhine-II, Bit 3 indicates Tx descriptor write-back race. */
+
+ /* added comment by guard */
+ /* For supporting VT6107, please use revision id to recognize different chips in driver */
+ // if (tp->chip_id == 0x3065)
+ if( tp->chip_revision < 0x80 && tp->chip_revision >=0x40 )
+ intr_status |= inb(nic->ioaddr + IntrStatus2) << 16;
+ intr_status = (intr_status & ~DEFAULT_INTR);
+ if ( action == ENABLE )
+ intr_status = intr_status | DEFAULT_INTR;
+ outw(intr_status, nic->ioaddr + IntrEnable);
+ break;
+ case FORCE :
+ outw(0x0010, nic->ioaddr + 0x84);
+ break;
+ }
+}
+
+static int
+rhine_probe (struct dev *dev, struct pci_device *pci)
+{
+ struct nic *nic = (struct nic *)dev;
+ struct rhine_private *tp = (struct rhine_private *) nic->priv_data;
+ if (!pci->ioaddr)
+ return 0;
+ rhine_probe1 (nic, pci, pci->ioaddr, pci->dev_id, -1);
+
+ adjust_pci_device(pci);
+ rhine_reset (nic);
+
+ dev->disable = rhine_disable;
+ nic->poll = rhine_poll;
+ nic->transmit = rhine_transmit;
+ nic->irqno = pci->irq;
+ nic->irq = rhine_irq;
+ nic->ioaddr = tp->ioaddr;
+
+
+ return 1;
+}
+
+static void set_rx_mode(struct nic *nic __unused) {
+ struct rhine_private *tp = (struct rhine_private *) nic->priv_data;
+ unsigned char rx_mode;
+ int ioaddr = tp->ioaddr;
+
+ /* ! IFF_PROMISC */
+ outl(0xffffffff, byMAR0);
+ outl(0xffffffff, byMAR4);
+ rx_mode = 0x0C;
+
+ outb(0x60 /* thresh */ | rx_mode, byRCR );
+}
+
+static void
+rhine_probe1 (struct nic *nic, struct pci_device *pci, int ioaddr, int chip_id, int options)
+{
+ struct rhine_private *tp;
+ static int did_version = 0; /* Already printed version info. */
+ int i, ww;
+ unsigned int timeout;
+ int FDXFlag;
+ int byMIIvalue, LineSpeed, MIICRbak;
+ uint8_t revision_id;
+ unsigned char mode3_reg;
+
+ if (rhine_debug > 0 && did_version++ == 0)
+ printf (version);
+
+ // get revision id.
+ pci_read_config_byte(pci, PCI_REVISION, &revision_id);
+
+ /* D-Link provided reset code (with comment additions) */
+ if (revision_id >= 0x40) {
+ unsigned char byOrgValue;
+
+ if(rhine_debug > 0)
+ printf("Enabling Sticky Bit Workaround for Chip_id: 0x%hX\n"
+ , chip_id);
+ /* clear sticky bit before reset & read ethernet address */
+ byOrgValue = inb(bySTICKHW);
+ byOrgValue = byOrgValue & 0xFC;
+ outb(byOrgValue, bySTICKHW);
+
+ /* (bits written are cleared?) */
+ /* disable force PME-enable */
+ outb(0x80, byWOLcgClr);
+ /* disable power-event config bit */
+ outb(0xFF, byWOLcrClr);
+ /* clear power status (undocumented in vt6102 docs?) */
+ outb(0xFF, byPwrcsrClr);
+
+ }
+
+ /* Reset the chip to erase previous misconfiguration. */
+ outw(CR_SFRST, byCR0);
+ // if vt3043 delay after reset
+ if (revision_id <0x40) {
+ udelay(10000);
+ }
+ // polling till software reset complete
+ // W_MAX_TIMEOUT is the timeout period
+ for(ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ if ((inw(byCR0) & CR_SFRST) == 0)
+ break;
+ }
+
+ // issue AUTOLoad in EECSR to reload eeprom
+ outb(0x20, byEECSR );
+
+ // if vt3065 delay after reset
+ if (revision_id >=0x40) {
+ // delay 8ms to let MAC stable
+ mdelay(8);
+ /*
+ * for 3065D, EEPROM reloaded will cause bit 0 in MAC_REG_CFGA
+ * turned on. it makes MAC receive magic packet
+ * automatically. So, we turn it off. (D-Link)
+ */
+ outb(inb(byCFGA) & 0xFE, byCFGA);
+ }
+
+ /* turn on bit2 in PCI configuration register 0x53 , only for 3065*/
+ if (revision_id >= 0x40) {
+ pci_read_config_byte(pci, PCI_REG_MODE3, &mode3_reg);
+ pci_write_config_byte(pci, PCI_REG_MODE3, mode3_reg|MODE3_MIION);
+ }
+
+
+ /* back off algorithm ,disable the right-most 4-bit off CFGD*/
+ outb(inb(byCFGD) & (~(CFGD_RANDOM | CFGD_CFDX | CFGD_CEREN | CFGD_CETEN)), byCFGD);
+
+ /* reload eeprom */
+ reload_eeprom(ioaddr);
+
+ /* Perhaps this should be read from the EEPROM? */
+ for (i = 0; i < ETH_ALEN; i++)
+ nic->node_addr[i] = inb (byPAR0 + i);
+ printf ("IO address %hX Ethernet Address: %!\n", ioaddr, nic->node_addr);
+
+ /* restart MII auto-negotiation */
+ WriteMII (0, 9, 1, ioaddr);
+ printf ("Analyzing Media type,this will take several seconds........");
+ for (i = 0; i < 5; i++)
+ {
+ /* need to wait 1 millisecond - we will round it up to 50-100ms */
+ timeout = currticks() + 2;
+ for (timeout = currticks() + 2; currticks() < timeout;)
+ /* nothing */;
+ if (ReadMII (1, ioaddr) & 0x0020)
+ break;
+ }
+ printf ("OK\n");
+
+#if 0
+ /* JJM : for Debug */
+ printf("MII : Address %hhX ",inb(ioaddr+0x6c));
+ {
+ unsigned char st1,st2,adv1,adv2,l1,l2;
+
+ st1=ReadMII(1,ioaddr)>>8;
+ st2=ReadMII(1,ioaddr)&0xFF;
+ adv1=ReadMII(4,ioaddr)>>8;
+ adv2=ReadMII(4,ioaddr)&0xFF;
+ l1=ReadMII(5,ioaddr)>>8;
+ l2=ReadMII(5,ioaddr)&0xFF;
+ printf(" status 0x%hhX%hhX, advertising 0x%hhX%hhX, link 0x%hhX%hhX\n", st1,st2,adv1,adv2,l1,l2);
+ }
+#endif
+
+
+ /* query MII to know LineSpeed,duplex mode */
+ byMIIvalue = inb (ioaddr + 0x6d);
+ LineSpeed = byMIIvalue & MIISR_SPEED;
+ if (LineSpeed != 0) //JJM
+ {
+ printf ("Linespeed=10Mbs");
+ }
+ else
+ {
+ printf ("Linespeed=100Mbs");
+ }
+
+ FDXFlag = QueryAuto (ioaddr);
+ if (FDXFlag == 1)
+ {
+ printf (" Fullduplex\n");
+ outw (CR_FDX, byCR0);
+ }
+ else
+ {
+ printf (" Halfduplex\n");
+ }
+
+
+ /* set MII 10 FULL ON, only apply in vt3043 */
+ if(chip_id == 0x3043)
+ WriteMII (0x17, 1, 1, ioaddr);
+
+ /* turn on MII link change */
+ MIICRbak = inb (byMIICR);
+ outb (MIICRbak & 0x7F, byMIICR);
+ MIIDelay ();
+ outb (0x41, byMIIAD);
+ MIIDelay ();
+
+ /* while((inb(byMIIAD)&0x20)==0) ; */
+ outb (MIICRbak | 0x80, byMIICR);
+
+ nic->priv_data = &rhine;
+ tp = &rhine;
+ tp->chip_id = chip_id;
+ tp->ioaddr = ioaddr;
+ tp->phys[0] = -1;
+ tp->chip_revision = revision_id;
+
+ /* The lower four bits are the media type. */
+ if (options > 0)
+ {
+ tp->full_duplex = (options & 16) ? 1 : 0;
+ tp->default_port = options & 15;
+ if (tp->default_port)
+ tp->medialock = 1;
+ }
+ return;
+}
+
+static void
+rhine_disable (struct dev *dev)
+{
+ struct nic *nic = (struct nic *)dev;
+ struct rhine_private *tp = (struct rhine_private *) nic->priv_data;
+ int ioaddr = tp->ioaddr;
+
+ /* merge reset and disable */
+ rhine_reset(nic);
+
+ printf ("rhine disable\n");
+ /* Switch to loopback mode to avoid hardware races. */
+ writeb(0x60 | 0x01, byTCR);
+ /* Stop the chip's Tx and Rx processes. */
+ writew(CR_STOP, byCR0);
+}
+
+/**************************************************************************
+ETH_RESET - Reset adapter
+***************************************************************************/
+static void
+rhine_reset (struct nic *nic)
+{
+ struct rhine_private *tp = (struct rhine_private *) nic->priv_data;
+ int ioaddr = tp->ioaddr;
+ int i, j;
+ int FDXFlag, CRbak;
+ int rx_ring_tmp, rx_ring_tmp1;
+ int tx_ring_tmp, tx_ring_tmp1;
+ int rx_bufs_tmp, rx_bufs_tmp1;
+ int tx_bufs_tmp, tx_bufs_tmp1;
+
+ static char buf1[TX_RING_SIZE * PKT_BUF_SZ + 32];
+ static char buf2[RX_RING_SIZE * PKT_BUF_SZ + 32];
+ static char desc1[TX_RING_SIZE * sizeof (struct rhine_tx_desc) + 32];
+ static char desc2[RX_RING_SIZE * sizeof (struct rhine_rx_desc) + 32];
+
+ /* printf ("rhine_reset\n"); */
+ /* Soft reset the chip. */
+ /*outb(CmdReset, ioaddr + ChipCmd); */
+
+ tx_bufs_tmp = (int) buf1;
+ tx_ring_tmp = (int) desc1;
+ rx_bufs_tmp = (int) buf2;
+ rx_ring_tmp = (int) desc2;
+
+ /* tune RD TD 32 byte alignment */
+ rx_ring_tmp1 = (int) virt_to_bus ((char *) rx_ring_tmp);
+ j = (rx_ring_tmp1 + 32) & (~0x1f);
+ /* printf ("txring[%d]", j); */
+ tp->rx_ring = (struct rhine_rx_desc *) bus_to_virt (j);
+
+ tx_ring_tmp1 = (int) virt_to_bus ((char *) tx_ring_tmp);
+ j = (tx_ring_tmp1 + 32) & (~0x1f);
+ tp->tx_ring = (struct rhine_tx_desc *) bus_to_virt (j);
+ /* printf ("rxring[%X]", j); */
+
+
+ tx_bufs_tmp1 = (int) virt_to_bus ((char *) tx_bufs_tmp);
+ j = (int) (tx_bufs_tmp1 + 32) & (~0x1f);
+ tx_bufs_tmp = (int) bus_to_virt (j);
+ /* printf ("txb[%X]", j); */
+
+ rx_bufs_tmp1 = (int) virt_to_bus ((char *) rx_bufs_tmp);
+ j = (int) (rx_bufs_tmp1 + 32) & (~0x1f);
+ rx_bufs_tmp = (int) bus_to_virt (j);
+ /* printf ("rxb[%X][%X]", rx_bufs_tmp1, j); */
+
+ for (i = 0; i < RX_RING_SIZE; i++)
+ {
+ tp->rx_buffs[i] = (char *) rx_bufs_tmp;
+ /* printf("r[%X]",tp->rx_buffs[i]); */
+ rx_bufs_tmp += 1536;
+ }
+
+ for (i = 0; i < TX_RING_SIZE; i++)
+ {
+ tp->tx_buffs[i] = (char *) tx_bufs_tmp;
+ /* printf("t[%X]",tp->tx_buffs[i]); */
+ tx_bufs_tmp += 1536;
+ }
+
+ /* software reset */
+ outb (CR1_SFRST, byCR1);
+ MIIDelay ();
+
+ /* printf ("init ring"); */
+ rhine_init_ring (nic);
+ /*write TD RD Descriptor to MAC */
+ outl (virt_to_bus (tp->rx_ring), dwCurrentRxDescAddr);
+ outl (virt_to_bus (tp->tx_ring), dwCurrentTxDescAddr);
+
+ /* Setup Multicast */
+ set_rx_mode(nic);
+
+ /* set TCR RCR threshold to store and forward*/
+ outb (0x3e, byBCR0);
+ outb (0x38, byBCR1);
+ outb (0x2c, byRCR);
+ outb (0x60, byTCR);
+ /* Set Fulldupex */
+ FDXFlag = QueryAuto (ioaddr);
+ if (FDXFlag == 1)
+ {
+ outb (CFGD_CFDX, byCFGD);
+ outw (CR_FDX, byCR0);
+ }
+
+ /* KICK NIC to WORK */
+ CRbak = inw (byCR0);
+ CRbak = CRbak & 0xFFFB; /* not CR_STOP */
+ outw ((CRbak | CR_STRT | CR_TXON | CR_RXON | CR_DPOLL), byCR0);
+
+ /* disable all known interrupt */
+ outw (0, byIMR0);
+}
+/* Beware of PCI posted writes */
+#define IOSYNC do { readb(nic->ioaddr + StationAddr); } while (0)
+
+static int
+rhine_poll (struct nic *nic, int retreive)
+{
+ struct rhine_private *tp = (struct rhine_private *) nic->priv_data;
+ int rxstatus, good = 0;;
+
+ if (tp->rx_ring[tp->cur_rx].rx_status.bits.own_bit == 0)
+ {
+ unsigned int intr_status;
+ /* There is a packet ready */
+ if(!retreive)
+ return 1;
+
+ intr_status = inw(nic->ioaddr + IntrStatus);
+ /* On Rhine-II, Bit 3 indicates Tx descriptor write-back race. */
+#if 0
+ if (tp->chip_id == 0x3065)
+ intr_status |= inb(nic->ioaddr + IntrStatus2) << 16;
+#endif
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ if (intr_status & IntrTxDescRace)
+ outb(0x08, nic->ioaddr + IntrStatus2);
+ outw(intr_status & 0xffff, nic->ioaddr + IntrStatus);
+ IOSYNC;
+
+ rxstatus = tp->rx_ring[tp->cur_rx].rx_status.lw;
+ if ((rxstatus & 0x0300) != 0x0300)
+ {
+ printf("rhine_poll: bad status\n");
+ }
+ else if (rxstatus & (RSR_ABNORMAL))
+ {
+ printf ("rxerr[%X]\n", rxstatus);
+ }
+ else
+ good = 1;
+
+ if (good)
+ {
+ nic->packetlen = tp->rx_ring[tp->cur_rx].rx_status.bits.frame_length;
+ memcpy (nic->packet, tp->rx_buffs[tp->cur_rx], nic->packetlen);
+ /* printf ("Packet RXed\n"); */
+ }
+ tp->rx_ring[tp->cur_rx].rx_status.bits.own_bit = 1;
+ tp->cur_rx++;
+ tp->cur_rx = tp->cur_rx % RX_RING_SIZE;
+ }
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ outw(DEFAULT_INTR & ~IntrRxDone, nic->ioaddr + IntrStatus);
+
+ IOSYNC;
+
+ return good;
+}
+
+static void
+rhine_transmit (struct nic *nic,
+ const char *d, unsigned int t, unsigned int s, const char *p)
+{
+ struct rhine_private *tp = (struct rhine_private *) nic->priv_data;
+ int ioaddr = tp->ioaddr;
+ int entry;
+ unsigned char CR1bak;
+ unsigned char CR0bak;
+ unsigned int nstype;
+
+
+ /*printf ("rhine_transmit\n"); */
+ /* setup ethernet header */
+
+
+ /* Calculate the next Tx descriptor entry. */
+ entry = tp->cur_tx % TX_RING_SIZE;
+
+ memcpy (tp->tx_buffs[entry], d, ETH_ALEN); /* dst */
+ memcpy (tp->tx_buffs[entry] + ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */
+
+ nstype=htons(t);
+ memcpy(tp->tx_buffs[entry] + 2 * ETH_ALEN, (char*)&nstype, 2);
+
+ memcpy (tp->tx_buffs[entry] + ETH_HLEN, p, s);
+ s += ETH_HLEN;
+ while (s < ETH_ZLEN)
+ *((char *) tp->tx_buffs[entry] + (s++)) = 0;
+
+ tp->tx_ring[entry].tx_ctrl.bits.tx_buf_size = s;
+
+ tp->tx_ring[entry].tx_status.bits.own_bit = 1;
+
+
+ CR1bak = inb (byCR1);
+
+ CR1bak = CR1bak | CR1_TDMD1;
+ /*printf("tdsw=[%X]",tp->tx_ring[entry].tx_status.lw); */
+ /*printf("tdcw=[%X]",tp->tx_ring[entry].tx_ctrl.lw); */
+ /*printf("tdbuf1=[%X]",tp->tx_ring[entry].buf_addr_1); */
+ /*printf("tdbuf2=[%X]",tp->tx_ring[entry].buf_addr_2); */
+ /*printf("td1=[%X]",inl(dwCurrentTDSE0)); */
+ /*printf("td2=[%X]",inl(dwCurrentTDSE1)); */
+ /*printf("td3=[%X]",inl(dwCurrentTDSE2)); */
+ /*printf("td4=[%X]",inl(dwCurrentTDSE3)); */
+
+ outb (CR1bak, byCR1);
+ do
+ {
+ load_timer2(10*TICKS_PER_MS);
+ /* Wait until transmit is finished or timeout*/
+ while((tp->tx_ring[entry].tx_status.bits.own_bit !=0) && timer2_running())
+ ;
+
+ if(tp->tx_ring[entry].tx_status.bits.terr == 0)
+ break;
+
+ if(tp->tx_ring[entry].tx_status.bits.abt == 1)
+ {
+ // turn on TX
+ CR0bak = inb(byCR0);
+ CR0bak = CR0bak|CR_TXON;
+ outb(CR0bak,byCR0);
+ }
+ }while(0);
+ tp->cur_tx++;
+
+ /*outw(IMRShadow,byIMR0); */
+ /*dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE); */
+ /*tp->tx_skbuff[entry] = 0; */
+}
+
+static struct pci_id rhine_nics[] = {
+PCI_ROM(0x1106, 0x3065, "dlink-530tx", "VIA 6102"),
+PCI_ROM(0x1106, 0x3106, "via-rhine-6105", "VIA 6105"),
+PCI_ROM(0x1106, 0x3043, "dlink-530tx-old", "VIA 3043"), /* Rhine-I 86c100a */
+PCI_ROM(0x1106, 0x3053, "via6105m", "VIA 6105M"),
+PCI_ROM(0x1106, 0x6100, "via-rhine-old", "VIA 86C100A"), /* Rhine-II */
+};
+
+static struct pci_driver rhine_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "VIA 86C100",
+ .probe = rhine_probe,
+ .ids = rhine_nics,
+ .id_count = sizeof(rhine_nics)/sizeof(rhine_nics[0]),
+ .class = 0,
+};
+
+/* EOF via-rhine.c */
diff --git a/src/drivers/net/w89c840.c b/src/drivers/net/w89c840.c
new file mode 100644
index 00000000..27a81eda
--- /dev/null
+++ b/src/drivers/net/w89c840.c
@@ -0,0 +1,958 @@
+/*
+ * Etherboot - BOOTP/TFTP Bootstrap Program
+ *
+ * w89c840.c -- This file implements the winbond-840 driver for etherboot.
+ *
+ */
+
+/*
+ * Adapted by Igor V. Kovalenko
+ * -- <garrison@mail.ru>
+ * OR
+ * -- <iko@crec.mipt.ru>
+ * Initial adaptaion stage, including testing, completed 23 August 2000.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * date version by what
+ * Written: Aug 20 2000 V0.10 iko Initial revision.
+ * changes: Aug 22 2000 V0.90 iko Works!
+ * Aug 23 2000 V0.91 iko Cleanup, posted to etherboot
+ * maintainer.
+ * Aug 26 2000 V0.92 iko Fixed Rx ring handling.
+ * First Linux Kernel (TM)
+ * successfully loaded using
+ * this driver.
+ * Jan 07 2001 V0.93 iko Transmitter timeouts are handled
+ * using timer2 routines. Proposed
+ * by Ken Yap to eliminate CPU speed
+ * dependency.
+ * Dec 12 2003 V0.94 timlegge Fixed issues in 5.2, removed
+ * interrupt usage, enabled
+ * multicast support
+ *
+ * This is the etherboot driver for cards based on Winbond W89c840F chip.
+ *
+ * It was written from skeleton source, with Donald Becker's winbond-840.c
+ * kernel driver as a guideline. Mostly the w89c840 related definitions
+ * and the lower level routines have been cut-and-pasted into this source.
+ *
+ * Frankly speaking, about 90% of the code was obtained using cut'n'paste
+ * sequence :) while the remainder appeared while brainstorming
+ * Linux Kernel 2.4.0-testX source code. Thanks, Donald and Linus!
+ *
+ * There was a demand for using this card in a rather large
+ * remote boot environment at MSKP OVTI Lab of
+ * Moscow Institute for Physics and Technology (MIPT) -- http://www.mipt.ru/
+ * so you may count that for motivation.
+ *
+ */
+
+/*
+ * If you want to see debugging output then define W89C840_DEBUG
+ */
+
+/*
+#define W89C840_DEBUG
+*/
+
+/*
+ * Keep using IO_OPS for Etherboot driver!
+ */
+#define USE_IO_OPS
+
+#include "etherboot.h"
+#include "nic.h"
+#include "pci.h"
+#include "timer.h"
+
+static const char *w89c840_version = "driver Version 0.94 - December 12, 2003";
+
+typedef unsigned char u8;
+typedef signed char s8;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned int u32;
+typedef signed int s32;
+
+/* Linux support functions */
+#define virt_to_le32desc(addr) virt_to_bus(addr)
+#define le32desc_to_virt(addr) bus_to_virt(addr)
+
+/*
+#define cpu_to_le32(val) (val)
+#define le32_to_cpu(val) (val)
+*/
+
+/* Operational parameters that are set at compile time. */
+
+/* Keep the ring sizes a power of two for compile efficiency.
+ The compiler will convert <unsigned>'%'<2^N> into a bit mask.
+ Making the Tx ring too large decreases the effectiveness of channel
+ bonding and packet priority.
+ There are no ill effects from too-large receive rings. */
+#define TX_RING_SIZE 2
+#define RX_RING_SIZE 2
+
+/* The presumed FIFO size for working around the Tx-FIFO-overflow bug.
+ To avoid overflowing we don't queue again until we have room for a
+ full-size packet.
+ */
+#define TX_FIFO_SIZE (2048)
+#define TX_BUG_FIFO_LIMIT (TX_FIFO_SIZE-1514-16)
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (10*TICKS_PER_MS)
+
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
+
+/*
+ * Used to be this much CPU loops on Celeron@400 (?),
+ * now using real timer and TX_TIMEOUT!
+ * #define TX_LOOP_COUNT 10000000
+ */
+
+#if !defined(__OPTIMIZE__)
+#warning You must compile this file with the correct options!
+#warning See the last lines of the source file.
+#error You must compile this driver with "-O".
+#endif
+
+enum chip_capability_flags {CanHaveMII=1, HasBrokenTx=2};
+
+#ifdef USE_IO_OPS
+#define W840_FLAGS (PCI_USES_IO | PCI_ADDR0 | PCI_USES_MASTER)
+#else
+#define W840_FLAGS (PCI_USES_MEM | PCI_ADDR1 | PCI_USES_MASTER)
+#endif
+
+static u32 driver_flags = CanHaveMII | HasBrokenTx;
+
+/* This driver was written to use PCI memory space, however some x86 systems
+ work only with I/O space accesses. Pass -DUSE_IO_OPS to use PCI I/O space
+ accesses instead of memory space. */
+
+#ifdef USE_IO_OPS
+#undef readb
+#undef readw
+#undef readl
+#undef writeb
+#undef writew
+#undef writel
+#define readb inb
+#define readw inw
+#define readl inl
+#define writeb outb
+#define writew outw
+#define writel outl
+#endif
+
+/* Offsets to the Command and Status Registers, "CSRs".
+ While similar to the Tulip, these registers are longword aligned.
+ Note: It's not useful to define symbolic names for every register bit in
+ the device. The name can only partially document the semantics and make
+ the driver longer and more difficult to read.
+*/
+enum w840_offsets {
+ PCIBusCfg=0x00, TxStartDemand=0x04, RxStartDemand=0x08,
+ RxRingPtr=0x0C, TxRingPtr=0x10,
+ IntrStatus=0x14, NetworkConfig=0x18, IntrEnable=0x1C,
+ RxMissed=0x20, EECtrl=0x24, MIICtrl=0x24, BootRom=0x28, GPTimer=0x2C,
+ CurRxDescAddr=0x30, CurRxBufAddr=0x34, /* Debug use */
+ MulticastFilter0=0x38, MulticastFilter1=0x3C, StationAddr=0x40,
+ CurTxDescAddr=0x4C, CurTxBufAddr=0x50,
+};
+
+/* Bits in the interrupt status/enable registers. */
+/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */
+enum intr_status_bits {
+ NormalIntr=0x10000, AbnormalIntr=0x8000,
+ IntrPCIErr=0x2000, TimerInt=0x800,
+ IntrRxDied=0x100, RxNoBuf=0x80, IntrRxDone=0x40,
+ TxFIFOUnderflow=0x20, RxErrIntr=0x10,
+ TxIdle=0x04, IntrTxStopped=0x02, IntrTxDone=0x01,
+};
+
+/* Bits in the NetworkConfig register. */
+enum rx_mode_bits {
+ AcceptErr=0x80, AcceptRunt=0x40,
+ AcceptBroadcast=0x20, AcceptMulticast=0x10,
+ AcceptAllPhys=0x08, AcceptMyPhys=0x02,
+};
+
+enum mii_reg_bits {
+ MDIO_ShiftClk=0x10000, MDIO_DataIn=0x80000, MDIO_DataOut=0x20000,
+ MDIO_EnbOutput=0x40000, MDIO_EnbIn = 0x00000,
+};
+
+/* The Tulip Rx and Tx buffer descriptors. */
+struct w840_rx_desc {
+ s32 status;
+ s32 length;
+ u32 buffer1;
+ u32 next_desc;
+};
+
+struct w840_tx_desc {
+ s32 status;
+ s32 length;
+ u32 buffer1, buffer2; /* We use only buffer 1. */
+};
+
+/* Bits in network_desc.status */
+enum desc_status_bits {
+ DescOwn=0x80000000, DescEndRing=0x02000000, DescUseLink=0x01000000,
+ DescWholePkt=0x60000000, DescStartPkt=0x20000000, DescEndPkt=0x40000000,
+ DescIntr=0x80000000,
+};
+#define PRIV_ALIGN 15 /* Required alignment mask */
+#define PRIV_ALIGN_BYTES 32
+
+static struct winbond_private
+{
+ /* Descriptor rings first for alignment. */
+ struct w840_rx_desc rx_ring[RX_RING_SIZE];
+ struct w840_tx_desc tx_ring[TX_RING_SIZE];
+ struct net_device *next_module; /* Link for devices of this type. */
+ void *priv_addr; /* Unaligned address for kfree */
+ const char *product_name;
+ /* Frequently used values: keep some adjacent for cache effect. */
+ int chip_id, drv_flags;
+ struct pci_dev *pci_dev;
+ int csr6;
+ struct w840_rx_desc *rx_head_desc;
+ unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */
+ unsigned int rx_buf_sz; /* Based on MTU+slack. */
+ unsigned int cur_tx, dirty_tx;
+ int tx_q_bytes;
+ unsigned int tx_full:1; /* The Tx queue is full. */
+ /* These values are keep track of the transceiver/media in use. */
+ unsigned int full_duplex:1; /* Full-duplex operation requested. */
+ unsigned int duplex_lock:1;
+ unsigned int medialock:1; /* Do not sense media. */
+ unsigned int default_port:4; /* Last dev->if_port value. */
+ /* MII transceiver section. */
+ int mii_cnt; /* MII device addresses. */
+ u16 advertising; /* NWay media advertisement */
+ unsigned char phys[2]; /* MII device addresses. */
+} w840private __attribute__ ((aligned (PRIV_ALIGN_BYTES)));
+
+/* NIC specific static variables go here */
+
+static int ioaddr;
+static unsigned short eeprom [0x40];
+static char rx_packet[PKT_BUF_SZ * RX_RING_SIZE];
+static char tx_packet[PKT_BUF_SZ * TX_RING_SIZE];
+
+static int eeprom_read(long ioaddr, int location);
+static int mdio_read(int base_address, int phy_id, int location);
+#if 0
+static void mdio_write(int base_address, int phy_id, int location, int value);
+#endif
+
+static void check_duplex(void);
+static void set_rx_mode(void);
+static void init_ring(void);
+
+#if defined(W89C840_DEBUG)
+static void decode_interrupt(u32 intr_status)
+{
+ printf("Interrupt status: ");
+
+#define TRACE_INTR(_intr_) \
+ if (intr_status & (_intr_)) { printf (" " #_intr_); }
+
+ TRACE_INTR(NormalIntr);
+ TRACE_INTR(AbnormalIntr);
+ TRACE_INTR(IntrPCIErr);
+ TRACE_INTR(TimerInt);
+ TRACE_INTR(IntrRxDied);
+ TRACE_INTR(RxNoBuf);
+ TRACE_INTR(IntrRxDone);
+ TRACE_INTR(TxFIFOUnderflow);
+ TRACE_INTR(RxErrIntr);
+ TRACE_INTR(TxIdle);
+ TRACE_INTR(IntrTxStopped);
+ TRACE_INTR(IntrTxDone);
+
+ printf("\n");
+ /*sleep(1);*/
+}
+#endif
+
+/**************************************************************************
+w89c840_reset - Reset adapter
+***************************************************************************/
+static void w89c840_reset(struct nic *nic)
+{
+ int i;
+
+ /* Reset the chip to erase previous misconfiguration.
+ No hold time required! */
+ writel(0x00000001, ioaddr + PCIBusCfg);
+
+ init_ring();
+
+ writel(virt_to_bus(w840private.rx_ring), ioaddr + RxRingPtr);
+ writel(virt_to_bus(w840private.tx_ring), ioaddr + TxRingPtr);
+
+ for (i = 0; i < ETH_ALEN; i++)
+ writeb(nic->node_addr[i], ioaddr + StationAddr + i);
+
+ /* Initialize other registers. */
+ /* Configure the PCI bus bursts and FIFO thresholds.
+ 486: Set 8 longword cache alignment, 8 longword burst.
+ 586: Set 16 longword cache alignment, no burst limit.
+ Cache alignment bits 15:14 Burst length 13:8
+ 0000 <not allowed> 0000 align to cache 0800 8 longwords
+ 4000 8 longwords 0100 1 longword 1000 16 longwords
+ 8000 16 longwords 0200 2 longwords 2000 32 longwords
+ C000 32 longwords 0400 4 longwords
+ Wait the specified 50 PCI cycles after a reset by initializing
+ Tx and Rx queues and the address filter list. */
+
+ writel(0xE010, ioaddr + PCIBusCfg);
+
+ writel(0, ioaddr + RxStartDemand);
+ w840private.csr6 = 0x20022002;
+ check_duplex();
+ set_rx_mode();
+
+ /* Do not enable the interrupts Etherboot doesn't need them */
+/*
+ writel(0x1A0F5, ioaddr + IntrStatus);
+ writel(0x1A0F5, ioaddr + IntrEnable);
+*/
+#if defined(W89C840_DEBUG)
+ printf("winbond-840 : Done reset.\n");
+#endif
+}
+
+#if 0
+static void handle_intr(u32 intr_stat)
+{
+ if ((intr_stat & (NormalIntr|AbnormalIntr)) == 0) {
+ /* we are polling, do not return now */
+ /*return 0;*/
+ } else {
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ writel(intr_stat & 0x001ffff, ioaddr + IntrStatus);
+ }
+
+ if (intr_stat & AbnormalIntr) {
+ /* There was an abnormal interrupt */
+ printf("\n-=- Abnormal interrupt.\n");
+
+#if defined(W89C840_DEBUG)
+ decode_interrupt(intr_stat);
+#endif
+
+ if (intr_stat & RxNoBuf) {
+ /* There was an interrupt */
+ printf("-=- <=> No receive buffers available.\n");
+ writel(0, ioaddr + RxStartDemand);
+ }
+ }
+}
+#endif
+
+/**************************************************************************
+w89c840_poll - Wait for a frame
+***************************************************************************/
+static int w89c840_poll(struct nic *nic, int retrieve)
+{
+ /* return true if there's an ethernet packet ready to read */
+ /* nic->packet should contain data on return */
+ /* nic->packetlen should contain length of data */
+ int packet_received = 0;
+
+#if defined(W89C840_DEBUG)
+ u32 intr_status = readl(ioaddr + IntrStatus);
+#endif
+
+ do {
+ /* Code from netdev_rx(dev) */
+
+ int entry = w840private.cur_rx % RX_RING_SIZE;
+
+ struct w840_rx_desc *desc = w840private.rx_head_desc;
+ s32 status = desc->status;
+
+ if (status & DescOwn) {
+ /* DescOwn bit is still set, we should wait for RX to complete */
+ packet_received = 0;
+ break;
+ }
+
+ if ( !retrieve ) {
+ packet_received = 1;
+ break;
+ }
+
+ if ((status & 0x38008300) != 0x0300) {
+ if ((status & 0x38000300) != 0x0300) {
+ /* Ingore earlier buffers. */
+ if ((status & 0xffff) != 0x7fff) {
+ printf("winbond-840 : Oversized Ethernet frame spanned "
+ "multiple buffers, entry %d status %X !\n",
+ w840private.cur_rx, status);
+ }
+ } else if (status & 0x8000) {
+ /* There was a fatal error. */
+#if defined(W89C840_DEBUG)
+ printf("winbond-840 : Receive error, Rx status %X :", status);
+ if (status & 0x0890) {
+ printf(" RXLEN_ERROR");
+ }
+ if (status & 0x004C) {
+ printf(", FRAME_ERROR");
+ }
+ if (status & 0x0002) {
+ printf(", CRC_ERROR");
+ }
+ printf("\n");
+#endif
+
+ /* Simpy do a reset now... */
+ w89c840_reset(nic);
+
+ packet_received = 0;
+ break;
+ }
+ } else {
+ /* Omit the four octet CRC from the length. */
+ int pkt_len = ((status >> 16) & 0x7ff) - 4;
+
+#if defined(W89C840_DEBUG)
+ printf(" netdev_rx() normal Rx pkt ring %d length %d status %X\n", entry, pkt_len, status);
+#endif
+
+ nic->packetlen = pkt_len;
+
+ /* Check if the packet is long enough to accept without copying
+ to a minimally-sized skbuff. */
+
+ memcpy(nic->packet, le32desc_to_virt(w840private.rx_ring[entry].buffer1), pkt_len);
+ packet_received = 1;
+
+ /* Release buffer to NIC */
+ w840private.rx_ring[entry].status = DescOwn;
+
+#if defined(W89C840_DEBUG)
+ /* You will want this info for the initial debug. */
+ printf(" Rx data %hhX:%hhX:%hhX:%hhX:%hhX:"
+ "%hhX %hhX:%hhX:%hhX:%hhX:%hhX:%hhX %hhX%hhX "
+ "%hhX.%hhX.%hhX.%hhX.\n",
+ nic->packet[0], nic->packet[1], nic->packet[2], nic->packet[3],
+ nic->packet[4], nic->packet[5], nic->packet[6], nic->packet[7],
+ nic->packet[8], nic->packet[9], nic->packet[10],
+ nic->packet[11], nic->packet[12], nic->packet[13],
+ nic->packet[14], nic->packet[15], nic->packet[16],
+ nic->packet[17]);
+#endif
+
+ }
+
+ entry = (++w840private.cur_rx) % RX_RING_SIZE;
+ w840private.rx_head_desc = &w840private.rx_ring[entry];
+ } while (0);
+
+ return packet_received;
+}
+
+/**************************************************************************
+w89c840_transmit - Transmit a frame
+***************************************************************************/
+
+static void w89c840_transmit(
+ struct nic *nic,
+ const char *d, /* Destination */
+ unsigned int t, /* Type */
+ unsigned int s, /* size */
+ const char *p) /* Packet */
+{
+ /* send the packet to destination */
+ unsigned entry;
+ int transmit_status;
+
+ /* Caution: the write order is important here, set the field
+ with the "ownership" bits last. */
+
+ /* Fill in our transmit buffer */
+ entry = w840private.cur_tx % TX_RING_SIZE;
+
+ memcpy (tx_packet, d, ETH_ALEN); /* dst */
+ memcpy (tx_packet + ETH_ALEN, nic->node_addr, ETH_ALEN);/* src */
+
+ *((char *) tx_packet + 12) = t >> 8; /* type */
+ *((char *) tx_packet + 13) = t;
+
+ memcpy (tx_packet + ETH_HLEN, p, s);
+ s += ETH_HLEN;
+
+ while (s < ETH_ZLEN)
+ *((char *) tx_packet + ETH_HLEN + (s++)) = 0;
+
+ w840private.tx_ring[entry].buffer1 = virt_to_le32desc(tx_packet);
+
+ w840private.tx_ring[entry].length = (DescWholePkt | (u32) s);
+ if (entry >= TX_RING_SIZE-1) /* Wrap ring */
+ w840private.tx_ring[entry].length |= (DescIntr | DescEndRing);
+ w840private.tx_ring[entry].status = (DescOwn);
+ w840private.cur_tx++;
+
+ w840private.tx_q_bytes = (u16) s;
+ writel(0, ioaddr + TxStartDemand);
+
+ /* Work around horrible bug in the chip by marking the queue as full
+ when we do not have FIFO room for a maximum sized packet. */
+
+ if ((w840private.drv_flags & HasBrokenTx) && w840private.tx_q_bytes > TX_BUG_FIFO_LIMIT) {
+ /* Actually this is left to help finding error tails later in debugging...
+ * See Linux kernel driver in winbond-840.c for details.
+ */
+ w840private.tx_full = 1;
+ }
+
+#if defined(W89C840_DEBUG)
+ printf("winbond-840 : Transmit frame # %d size %d queued in slot %d.\n", w840private.cur_tx, s, entry);
+#endif
+
+ /* Now wait for TX to complete. */
+ transmit_status = w840private.tx_ring[entry].status;
+
+ load_timer2(TX_TIMEOUT);
+
+ {
+#if defined W89C840_DEBUG
+ u32 intr_stat = 0;
+#endif
+ while (1) {
+
+#if defined(W89C840_DEBUG)
+ decode_interrupt(intr_stat);
+#endif
+
+ while ( (transmit_status & DescOwn) && timer2_running()) {
+
+ transmit_status = w840private.tx_ring[entry].status;
+ }
+
+ break;
+ }
+ }
+
+ if ((transmit_status & DescOwn) == 0) {
+
+#if defined(W89C840_DEBUG)
+ printf("winbond-840 : transmission complete after wait loop iterations, status %X\n",
+ w840private.tx_ring[entry].status);
+#endif
+
+ return;
+ }
+
+ /* Transmit timed out... */
+
+ printf("winbond-840 : transmission TIMEOUT : status %X\n", w840private.tx_ring[entry].status);
+
+ return;
+}
+
+/**************************************************************************
+w89c840_disable - Turn off ethernet interface
+***************************************************************************/
+static void w89c840_disable(struct dev *dev)
+{
+ struct nic *nic = (struct nic *)dev;
+ /* merge reset and disable */
+ w89c840_reset(nic);
+
+ /* Don't know what to do to disable the board. Is this needed at all? */
+ /* Yes, a live NIC can corrupt the loaded memory later [Ken] */
+ /* Stop the chip's Tx and Rx processes. */
+ writel(w840private.csr6 &= ~0x20FA, ioaddr + NetworkConfig);
+}
+
+/**************************************************************************
+w89c840_irq - Enable, Disable, or Force interrupts
+***************************************************************************/
+static void w89c840_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+ switch ( action ) {
+ case DISABLE :
+ break;
+ case ENABLE :
+ break;
+ case FORCE :
+ break;
+ }
+}
+
+/**************************************************************************
+w89c840_probe - Look for an adapter, this routine's visible to the outside
+***************************************************************************/
+static int w89c840_probe(struct dev *dev, struct pci_device *p)
+{
+ struct nic *nic = (struct nic *)dev;
+ u16 sum = 0;
+ int i, j;
+ unsigned short value;
+
+ if (p->ioaddr == 0)
+ return 0;
+
+ ioaddr = p->ioaddr;
+ nic->ioaddr = p->ioaddr & ~3;
+ nic->irqno = 0;
+
+
+#if defined(W89C840_DEBUG)
+ printf("winbond-840: PCI bus %hhX device function %hhX: I/O address: %hX\n", p->bus, p->devfn, ioaddr);
+#endif
+
+ ioaddr = ioaddr & ~3; /* Mask the bit that says "this is an io addr" */
+
+#define PCI_DEVICE_ID_WINBOND2_89C840 0x0840
+#define PCI_DEVICE_ID_COMPEX_RL100ATX 0x2011
+
+ /* From Matt Hortman <mbhortman@acpthinclient.com> */
+ if (p->vendor == PCI_VENDOR_ID_WINBOND2
+ && p->dev_id == PCI_DEVICE_ID_WINBOND2_89C840) {
+
+ /* detected "Winbond W89c840 Fast Ethernet PCI NIC" */
+
+ } else if ( p->vendor == PCI_VENDOR_ID_COMPEX
+ && p->dev_id == PCI_DEVICE_ID_COMPEX_RL100ATX) {
+
+ /* detected "Compex RL100ATX Fast Ethernet PCI NIC" */
+
+ } else {
+ /* Gee, guess what? They missed again. */
+ printf("device ID : %X - is not a Compex RL100ATX NIC.\n", p->dev_id);
+ return 0;
+ }
+
+ printf(" %s\n", w89c840_version);
+
+ adjust_pci_device(p);
+
+ /* Ok. Got one. Read the eeprom. */
+ for (j = 0, i = 0; i < 0x40; i++) {
+ value = eeprom_read(ioaddr, i);
+ eeprom[i] = value;
+ sum += value;
+ }
+
+ for (i=0;i<ETH_ALEN;i++) {
+ nic->node_addr[i] = (eeprom[i/2] >> (8*(i&1))) & 0xff;
+ }
+ printf ("Ethernet addr: %!\n", nic->node_addr);
+
+#if defined(W89C840_DEBUG)
+ printf("winbond-840: EEPROM checksum %hX, got eeprom", sum);
+#endif
+
+ /* Reset the chip to erase previous misconfiguration.
+ No hold time required! */
+ writel(0x00000001, ioaddr + PCIBusCfg);
+
+ if (driver_flags & CanHaveMII) {
+ int phy, phy_idx = 0;
+ for (phy = 1; phy < 32 && phy_idx < 4; phy++) {
+ int mii_status = mdio_read(ioaddr, phy, 1);
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ w840private.phys[phy_idx++] = phy;
+ w840private.advertising = mdio_read(ioaddr, phy, 4);
+
+#if defined(W89C840_DEBUG)
+ printf("winbond-840 : MII PHY found at address %d, status "
+ "%X advertising %hX.\n", phy, mii_status, w840private.advertising);
+#endif
+
+ }
+ }
+
+ w840private.mii_cnt = phy_idx;
+
+ if (phy_idx == 0) {
+ printf("winbond-840 : MII PHY not found -- this device may not operate correctly.\n");
+ }
+ }
+
+ /* point to NIC specific routines */
+ dev->disable = w89c840_disable;
+ nic->poll = w89c840_poll;
+ nic->transmit = w89c840_transmit;
+ nic->irq = w89c840_irq;
+
+ w89c840_reset(nic);
+
+ return 1;
+}
+
+/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. These are
+ often serial bit streams generated by the host processor.
+ The example below is for the common 93c46 EEPROM, 64 16 bit words. */
+
+/* Delay between EEPROM clock transitions.
+ No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need
+ a delay. Note that pre-2.0.34 kernels had a cache-alignment bug that
+ made udelay() unreliable.
+ The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is
+ depricated.
+*/
+#define eeprom_delay(ee_addr) readl(ee_addr)
+
+enum EEPROM_Ctrl_Bits {
+ EE_ShiftClk=0x02, EE_Write0=0x801, EE_Write1=0x805,
+ EE_ChipSelect=0x801, EE_DataIn=0x08,
+};
+
+/* The EEPROM commands include the alway-set leading bit. */
+enum EEPROM_Cmds {
+ EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6),
+};
+
+static int eeprom_read(long addr, int location)
+{
+ int i;
+ int retval = 0;
+ int ee_addr = addr + EECtrl;
+ int read_cmd = location | EE_ReadCmd;
+ writel(EE_ChipSelect, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 10; i >= 0; i--) {
+ short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0;
+ writel(dataval, ee_addr);
+ eeprom_delay(ee_addr);
+ writel(dataval | EE_ShiftClk, ee_addr);
+ eeprom_delay(ee_addr);
+ }
+ writel(EE_ChipSelect, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ writel(EE_ChipSelect | EE_ShiftClk, ee_addr);
+ eeprom_delay(ee_addr);
+ retval = (retval << 1) | ((readl(ee_addr) & EE_DataIn) ? 1 : 0);
+ writel(EE_ChipSelect, ee_addr);
+ eeprom_delay(ee_addr);
+ }
+
+ /* Terminate the EEPROM access. */
+ writel(0, ee_addr);
+ return retval;
+}
+
+/* MII transceiver control section.
+ Read and write the MII registers using software-generated serial
+ MDIO protocol. See the MII specifications or DP83840A data sheet
+ for details.
+
+ The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
+ met by back-to-back 33Mhz PCI cycles. */
+#define mdio_delay(mdio_addr) readl(mdio_addr)
+
+/* Set iff a MII transceiver on any interface requires mdio preamble.
+ This only set with older tranceivers, so the extra
+ code size of a per-interface flag is not worthwhile. */
+static char mii_preamble_required = 1;
+
+#define MDIO_WRITE0 (MDIO_EnbOutput)
+#define MDIO_WRITE1 (MDIO_DataOut | MDIO_EnbOutput)
+
+/* Generate the preamble required for initial synchronization and
+ a few older transceivers. */
+static void mdio_sync(long mdio_addr)
+{
+ int bits = 32;
+
+ /* Establish sync by sending at least 32 logic ones. */
+ while (--bits >= 0) {
+ writel(MDIO_WRITE1, mdio_addr);
+ mdio_delay(mdio_addr);
+ writel(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+}
+
+static int mdio_read(int base_address, int phy_id, int location)
+{
+ long mdio_addr = base_address + MIICtrl;
+ int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+ int i, retval = 0;
+
+ if (mii_preamble_required)
+ mdio_sync(mdio_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 15; i >= 0; i--) {
+ int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+
+ writel(dataval, mdio_addr);
+ mdio_delay(mdio_addr);
+ writel(dataval | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 20; i > 0; i--) {
+ writel(MDIO_EnbIn, mdio_addr);
+ mdio_delay(mdio_addr);
+ retval = (retval << 1) | ((readl(mdio_addr) & MDIO_DataIn) ? 1 : 0);
+ writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ return (retval>>1) & 0xffff;
+}
+
+#if 0
+static void mdio_write(int base_address, int phy_id, int location, int value)
+{
+ long mdio_addr = base_address + MIICtrl;
+ int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
+ int i;
+
+ if (location == 4 && phy_id == w840private.phys[0])
+ w840private.advertising = value;
+
+ if (mii_preamble_required)
+ mdio_sync(mdio_addr);
+
+ /* Shift the command bits out. */
+ for (i = 31; i >= 0; i--) {
+ int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
+
+ writel(dataval, mdio_addr);
+ mdio_delay(mdio_addr);
+ writel(dataval | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ /* Clear out extra bits. */
+ for (i = 2; i > 0; i--) {
+ writel(MDIO_EnbIn, mdio_addr);
+ mdio_delay(mdio_addr);
+ writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
+ mdio_delay(mdio_addr);
+ }
+ return;
+}
+#endif
+
+static void check_duplex(void)
+{
+ int mii_reg5 = mdio_read(ioaddr, w840private.phys[0], 5);
+ int negotiated = mii_reg5 & w840private.advertising;
+ int duplex;
+
+ if (w840private.duplex_lock || mii_reg5 == 0xffff)
+ return;
+
+ duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
+ if (w840private.full_duplex != duplex) {
+ w840private.full_duplex = duplex;
+
+#if defined(W89C840_DEBUG)
+ printf("winbond-840 : Setting %s-duplex based on MII # %d negotiated capability %X\n",
+ duplex ? "full" : "half", w840private.phys[0], negotiated);
+#endif
+
+ w840private.csr6 &= ~0x200;
+ w840private.csr6 |= duplex ? 0x200 : 0;
+ }
+}
+
+static void set_rx_mode(void)
+{
+ u32 mc_filter[2]; /* Multicast hash filter */
+ u32 rx_mode;
+
+ /* Accept all multicasts from now on. */
+ memset(mc_filter, 0xff, sizeof(mc_filter));
+
+/*
+ * works OK with multicast enabled.
+ */
+
+ rx_mode = AcceptBroadcast | AcceptMyPhys | AcceptMulticast;
+
+ writel(mc_filter[0], ioaddr + MulticastFilter0);
+ writel(mc_filter[1], ioaddr + MulticastFilter1);
+ w840private.csr6 &= ~0x00F8;
+ w840private.csr6 |= rx_mode;
+ writel(w840private.csr6, ioaddr + NetworkConfig);
+
+#if defined(W89C840_DEBUG)
+ printf("winbond-840 : Done setting RX mode.\n");
+#endif
+}
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void init_ring(void)
+{
+ int i;
+ char * p;
+
+ w840private.tx_full = 0;
+ w840private.tx_q_bytes = w840private.cur_rx = w840private.cur_tx = 0;
+ w840private.dirty_rx = w840private.dirty_tx = 0;
+
+ w840private.rx_buf_sz = PKT_BUF_SZ;
+ w840private.rx_head_desc = &w840private.rx_ring[0];
+
+ /* Initial all Rx descriptors. Fill in the Rx buffers. */
+
+ p = &rx_packet[0];
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ w840private.rx_ring[i].length = w840private.rx_buf_sz;
+ w840private.rx_ring[i].status = 0;
+ w840private.rx_ring[i].next_desc = virt_to_le32desc(&w840private.rx_ring[i+1]);
+
+ w840private.rx_ring[i].buffer1 = virt_to_le32desc(p + (PKT_BUF_SZ * i));
+ w840private.rx_ring[i].status = DescOwn | DescIntr;
+ }
+
+ /* Mark the last entry as wrapping the ring. */
+ w840private.rx_ring[i-1].length |= DescEndRing;
+ w840private.rx_ring[i-1].next_desc = virt_to_le32desc(&w840private.rx_ring[0]);
+
+ w840private.dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ w840private.tx_ring[i].status = 0;
+ }
+ return;
+}
+
+
+static struct pci_id w89c840_nics[] = {
+PCI_ROM(0x1050, 0x0840, "winbond840", "Winbond W89C840F"),
+PCI_ROM(0x11f6, 0x2011, "compexrl100atx", "Compex RL100ATX"),
+};
+
+static struct pci_driver w89c840_driver __pci_driver = {
+ .type = NIC_DRIVER,
+ .name = "W89C840F",
+ .probe = w89c840_probe,
+ .ids = w89c840_nics,
+ .id_count = sizeof(w89c840_nics)/sizeof(w89c840_nics[0]),
+ .class = 0,
+};
diff --git a/src/drivers/net/wlan_compat.h b/src/drivers/net/wlan_compat.h
new file mode 100644
index 00000000..a4f75e3a
--- /dev/null
+++ b/src/drivers/net/wlan_compat.h
@@ -0,0 +1,575 @@
+/* src/include/wlan/wlan_compat.h
+*
+* Types and macros to aid in portability
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*/
+
+#ifndef _WLAN_COMPAT_H
+#define _WLAN_COMPAT_H
+
+/*=============================================================*/
+/*------ Establish Platform Identity --------------------------*/
+/*=============================================================*/
+/* Key macros: */
+/* WLAN_CPU_FAMILY */
+ #define WLAN_Ix86 1
+ #define WLAN_PPC 2
+ #define WLAN_Ix96 3
+ #define WLAN_ARM 4
+ #define WLAN_ALPHA 5
+ #define WLAN_MIPS 6
+ #define WLAN_HPPA 7
+/* WLAN_CPU_CORE */
+ #define WLAN_I386CORE 1
+ #define WLAN_PPCCORE 2
+ #define WLAN_I296 3
+ #define WLAN_ARMCORE 4
+ #define WLAN_ALPHACORE 5
+ #define WLAN_MIPSCORE 6
+ #define WLAN_HPPACORE 7
+/* WLAN_CPU_PART */
+ #define WLAN_I386PART 1
+ #define WLAN_MPC860 2
+ #define WLAN_MPC823 3
+ #define WLAN_I296SA 4
+ #define WLAN_PPCPART 5
+ #define WLAN_ARMPART 6
+ #define WLAN_ALPHAPART 7
+ #define WLAN_MIPSPART 8
+ #define WLAN_HPPAPART 9
+/* WLAN_SYSARCH */
+ #define WLAN_PCAT 1
+ #define WLAN_MBX 2
+ #define WLAN_RPX 3
+ #define WLAN_LWARCH 4
+ #define WLAN_PMAC 5
+ #define WLAN_SKIFF 6
+ #define WLAN_BITSY 7
+ #define WLAN_ALPHAARCH 7
+ #define WLAN_MIPSARCH 9
+ #define WLAN_HPPAARCH 10
+/* WLAN_OS */
+ #define WLAN_LINUX_KERNEL 1
+ #define WLAN_LINUX_USER 2
+/* WLAN_HOSTIF (generally set on the command line, not detected) */
+ #define WLAN_PCMCIA 1
+ #define WLAN_ISA 2
+ #define WLAN_PCI 3
+ #define WLAN_USB 4
+ #define WLAN_PLX 5
+
+/* Note: the PLX HOSTIF above refers to some vendors implementations for */
+/* PCI. It's a PLX chip that is a PCI to PCMCIA adapter, but it */
+/* isn't a real PCMCIA host interface adapter providing all the */
+/* card&socket services. */
+
+/* Lets try to figure out what we've got. Kernel mode or User mode? */
+#if defined(__KERNEL__)
+ #define WLAN_OS WLAN_LINUX_KERNEL
+#else
+ #define WLAN_OS WLAN_LINUX_USER
+#endif
+
+#ifdef __powerpc__
+#ifndef __ppc__
+#define __ppc__
+#endif
+#endif
+
+#if (defined(CONFIG_PPC) || defined(CONFIG_8xx))
+#ifndef __ppc__
+#define __ppc__
+#endif
+#endif
+
+#if defined(__KERNEL__)
+#if defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__)
+ #define WLAN_CPU_FAMILY WLAN_Ix86
+ #define WLAN_CPU_CORE WLAN_I386CORE
+ #define WLAN_CPU_PART WLAN_I386PART
+ #define WLAN_SYSARCH WLAN_PCAT
+#elif defined(__ppc__)
+ #define WLAN_CPU_FAMILY WLAN_PPC
+ #define WLAN_CPU_CORE WLAN_PPCCORE
+ #if defined(CONFIG_MBX)
+ #define WLAN_CPU_PART WLAN_MPC860
+ #define WLAN_SYSARCH WLAN_MBX
+ #elif defined(CONFIG_RPXLITE)
+ #define WLAN_CPU_PART WLAN_MPC823
+ #define WLAN_SYSARCH WLAN_RPX
+ #elif defined(CONFIG_RPXCLASSIC)
+ #define WLAN_CPU_PART WLAN_MPC860
+ #define WLAN_SYSARCH WLAN_RPX
+ #else
+ #define WLAN_CPU_PART WLAN_PPCPART
+ #define WLAN_SYSARCH WLAN_PMAC
+ #endif
+#elif defined(__arm__)
+ #define WLAN_CPU_FAMILY WLAN_ARM
+ #define WLAN_CPU_CORE WLAN_ARMCORE
+ #define WLAN_CPU_PART WLAN_ARM_PART
+ #define WLAN_SYSARCH WLAN_SKIFF
+#elif defined(__alpha__)
+ #define WLAN_CPU_FAMILY WLAN_ALPHA
+ #define WLAN_CPU_CORE WLAN_ALPHACORE
+ #define WLAN_CPU_PART WLAN_ALPHAPART
+ #define WLAN_SYSARCH WLAN_ALPHAARCH
+#elif defined(__mips__)
+ #define WLAN_CPU_FAMILY WLAN_MIPS
+ #define WLAN_CPU_CORE WLAN_MIPSCORE
+ #define WLAN_CPU_PART WLAN_MIPSPART
+ #define WLAN_SYSARCH WLAN_MIPSARCH
+#elif defined(__hppa__)
+ #define WLAN_CPU_FAMILY WLAN_HPPA
+ #define WLAN_CPU_CORE WLAN_HPPACORE
+ #define WLAN_CPU_PART WLAN_HPPAPART
+ #define WLAN_SYSARCH WLAN_HPPAARCH
+#else
+ #error "No CPU identified!"
+#endif
+#endif /* __KERNEL__ */
+
+/*
+ Some big endian machines implicitly do all I/O in little endian mode.
+
+ In particular:
+ Linux/PPC on PowerMacs (PCI)
+ Arm/Intel Xscale (PCI)
+
+ This may also affect PLX boards and other BE &| PPC platforms;
+ as new ones are discovered, add them below.
+*/
+
+#if (WLAN_HOSTIF == WLAN_PCI)
+#if ((WLAN_SYSARCH == WLAN_SKIFF) || (WLAN_SYSARCH == WLAN_PMAC))
+#define REVERSE_ENDIAN
+#endif
+#endif
+
+/*=============================================================*/
+/*------ Bit settings -----------------------------------------*/
+/*=============================================================*/
+
+#define BIT0 0x00000001
+#define BIT1 0x00000002
+#define BIT2 0x00000004
+#define BIT3 0x00000008
+#define BIT4 0x00000010
+#define BIT5 0x00000020
+#define BIT6 0x00000040
+#define BIT7 0x00000080
+#define BIT8 0x00000100
+#define BIT9 0x00000200
+#define BIT10 0x00000400
+#define BIT11 0x00000800
+#define BIT12 0x00001000
+#define BIT13 0x00002000
+#define BIT14 0x00004000
+#define BIT15 0x00008000
+#define BIT16 0x00010000
+#define BIT17 0x00020000
+#define BIT18 0x00040000
+#define BIT19 0x00080000
+#define BIT20 0x00100000
+#define BIT21 0x00200000
+#define BIT22 0x00400000
+#define BIT23 0x00800000
+#define BIT24 0x01000000
+#define BIT25 0x02000000
+#define BIT26 0x04000000
+#define BIT27 0x08000000
+#define BIT28 0x10000000
+#define BIT29 0x20000000
+#define BIT30 0x40000000
+#define BIT31 0x80000000
+
+typedef unsigned char UINT8;
+typedef unsigned short UINT16;
+typedef unsigned long UINT32;
+
+typedef signed char INT8;
+typedef signed short INT16;
+typedef signed long INT32;
+
+typedef unsigned int UINT;
+typedef signed int INT;
+
+typedef unsigned long long UINT64;
+typedef signed long long INT64;
+
+#define UINT8_MAX (0xffUL)
+#define UINT16_MAX (0xffffUL)
+#define UINT32_MAX (0xffffffffUL)
+
+#define INT8_MAX (0x7fL)
+#define INT16_MAX (0x7fffL)
+#define INT32_MAX (0x7fffffffL)
+
+/*=============================================================*/
+/*------ Compiler Portability Macros --------------------------*/
+/*=============================================================*/
+#define __WLAN_ATTRIB_PACK__ __attribute__ ((packed))
+#define __WLAN_PRAGMA_PACK1__
+#define __WLAN_PRAGMA_PACKDFLT__
+#define __WLAN_INLINE__ inline
+#define WLAN_MIN_ARRAY 0
+
+/*=============================================================*/
+/*------ OS Portability Macros --------------------------------*/
+/*=============================================================*/
+
+#ifndef WLAN_DBVAR
+#define WLAN_DBVAR wlan_debug
+#endif
+
+#if (WLAN_OS == WLAN_LINUX_KERNEL)
+ #define WLAN_LOG_ERROR0(x) printk(KERN_ERR "%s: " x , __FUNCTION__ );
+ #define WLAN_LOG_ERROR1(x,n) printk(KERN_ERR "%s: " x , __FUNCTION__ , (n));
+ #define WLAN_LOG_ERROR2(x,n1,n2) printk(KERN_ERR "%s: " x , __FUNCTION__ , (n1), (n2));
+ #define WLAN_LOG_ERROR3(x,n1,n2,n3) printk(KERN_ERR "%s: " x , __FUNCTION__, (n1), (n2), (n3));
+ #define WLAN_LOG_ERROR4(x,n1,n2,n3,n4) printk(KERN_ERR "%s: " x , __FUNCTION__, (n1), (n2), (n3), (n4));
+
+ #define WLAN_LOG_WARNING0(x) printk(KERN_WARNING "%s: " x , __FUNCTION__);
+ #define WLAN_LOG_WARNING1(x,n) printk(KERN_WARNING "%s: " x , __FUNCTION__, (n));
+ #define WLAN_LOG_WARNING2(x,n1,n2) printk(KERN_WARNING "%s: " x , __FUNCTION__, (n1), (n2));
+ #define WLAN_LOG_WARNING3(x,n1,n2,n3) printk(KERN_WARNING "%s: " x , __FUNCTION__, (n1), (n2), (n3));
+ #define WLAN_LOG_WARNING4(x,n1,n2,n3,n4) printk(KERN_WARNING "%s: " x , __FUNCTION__ , (n1), (n2), (n3), (n4));
+
+ #define WLAN_LOG_NOTICE0(x) printk(KERN_NOTICE "%s: " x , __FUNCTION__);
+ #define WLAN_LOG_NOTICE1(x,n) printk(KERN_NOTICE "%s: " x , __FUNCTION__, (n));
+ #define WLAN_LOG_NOTICE2(x,n1,n2) printk(KERN_NOTICE "%s: " x , __FUNCTION__, (n1), (n2));
+ #define WLAN_LOG_NOTICE3(x,n1,n2,n3) printk(KERN_NOTICE "%s: " x , __FUNCTION__, (n1), (n2), (n3));
+ #define WLAN_LOG_NOTICE4(x,n1,n2,n3,n4) printk(KERN_NOTICE "%s: " x , __FUNCTION__, (n1), (n2), (n3), (n4));
+
+ #define WLAN_LOG_INFO0(x) printk(KERN_INFO x);
+ #define WLAN_LOG_INFO1(x,n) printk(KERN_INFO x, (n));
+ #define WLAN_LOG_INFO2(x,n1,n2) printk(KERN_INFO x, (n1), (n2));
+ #define WLAN_LOG_INFO3(x,n1,n2,n3) printk(KERN_INFO x, (n1), (n2), (n3));
+ #define WLAN_LOG_INFO4(x,n1,n2,n3,n4) printk(KERN_INFO x, (n1), (n2), (n3), (n4));
+ #define WLAN_LOG_INFO5(x,n1,n2,n3,n4,n5) printk(KERN_INFO x, (n1), (n2), (n3), (n4), (n5));
+
+ #if defined(WLAN_INCLUDE_DEBUG)
+ #define WLAN_ASSERT(c) if ((!(c)) && WLAN_DBVAR >= 1) { \
+ WLAN_LOG_DEBUG0(1, "Assertion failure!\n"); }
+ #define WLAN_HEX_DUMP( l, x, p, n) if( WLAN_DBVAR >= (l) ){ \
+ int __i__; \
+ printk(KERN_DEBUG x ":"); \
+ for( __i__=0; __i__ < (n); __i__++) \
+ printk( " %02x", ((UINT8*)(p))[__i__]); \
+ printk("\n"); }
+
+ #define DBFENTER { if ( WLAN_DBVAR >= 4 ){ WLAN_LOG_DEBUG0(3,"Enter\n"); } }
+ #define DBFEXIT { if ( WLAN_DBVAR >= 4 ){ WLAN_LOG_DEBUG0(3,"Exit\n"); } }
+
+ #define WLAN_LOG_DEBUG0(l,x) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ );
+ #define WLAN_LOG_DEBUG1(l,x,n) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n));
+ #define WLAN_LOG_DEBUG2(l,x,n1,n2) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2));
+ #define WLAN_LOG_DEBUG3(l,x,n1,n2,n3) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2), (n3));
+ #define WLAN_LOG_DEBUG4(l,x,n1,n2,n3,n4) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2), (n3), (n4));
+ #define WLAN_LOG_DEBUG5(l,x,n1,n2,n3,n4,n5) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2), (n3), (n4), (n5));
+ #define WLAN_LOG_DEBUG6(l,x,n1,n2,n3,n4,n5,n6) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2), (n3), (n4), (n5), (n6));
+ #else
+ #define WLAN_ASSERT(c)
+ #define WLAN_HEX_DUMP( l, s, p, n)
+
+ #define DBFENTER
+ #define DBFEXIT
+
+ #define WLAN_LOG_DEBUG0(l, s)
+ #define WLAN_LOG_DEBUG1(l, s,n)
+ #define WLAN_LOG_DEBUG2(l, s,n1,n2)
+ #define WLAN_LOG_DEBUG3(l, s,n1,n2,n3)
+ #define WLAN_LOG_DEBUG4(l, s,n1,n2,n3,n4)
+ #define WLAN_LOG_DEBUG5(l, s,n1,n2,n3,n4,n5)
+ #endif
+#else
+ #define WLAN_LOG_ERROR0(s)
+ #define WLAN_LOG_ERROR1(s,n)
+ #define WLAN_LOG_ERROR2(s,n1,n2)
+ #define WLAN_LOG_ERROR3(s,n1,n2,n3)
+ #define WLAN_LOG_ERROR4(s,n1,n2,n3,n4)
+
+ #define WLAN_LOG_WARNING0(s)
+ #define WLAN_LOG_WARNING1(s,n)
+ #define WLAN_LOG_WARNING2(s,n1,n2)
+ #define WLAN_LOG_WARNING3(s,n1,n2,n3)
+ #define WLAN_LOG_WARNING4(s,n1,n2,n3,n4)
+
+ #define WLAN_LOG_NOTICE0(s)
+ #define WLAN_LOG_NOTICE1(s,n)
+ #define WLAN_LOG_NOTICE2(s,n1,n2)
+ #define WLAN_LOG_NOTICE3(s,n1,n2,n3)
+ #define WLAN_LOG_NOTICE4(s,n1,n2,n3,n4)
+
+ #define WLAN_ASSERT(c)
+ #define WLAN_HEX_DUMP( l, s, p, n)
+
+ #define DBFENTER
+ #define DBFEXIT
+
+ #define WLAN_LOG_INFO0(s)
+ #define WLAN_LOG_INFO1(s,n)
+ #define WLAN_LOG_INFO2(s,n1,n2)
+ #define WLAN_LOG_INFO3(s,n1,n2,n3)
+ #define WLAN_LOG_INFO4(s,n1,n2,n3,n4)
+ #define WLAN_LOG_INFO5(s,n1,n2,n3,n4,n5)
+
+ #define WLAN_LOG_DEBUG0(l, s)
+ #define WLAN_LOG_DEBUG1(l, s,n)
+ #define WLAN_LOG_DEBUG2(l, s,n1,n2)
+ #define WLAN_LOG_DEBUG3(l, s,n1,n2,n3)
+ #define WLAN_LOG_DEBUG4(l, s,n1,n2,n3,n4)
+ #define WLAN_LOG_DEBUG5(l, s,n1,n2,n3,n4,n5)
+#endif
+
+#define wlan_ms_per_tick (1000UL / (wlan_ticks_per_sec))
+#define wlan_ms_to_ticks(n) ( (n) / (wlan_ms_per_tick))
+#define wlan_tu2ticks(n) ( (n) / (wlan_ms_per_tick))
+#define WLAN_INT_DISABLE(n) { save_flags((n)); cli(); }
+#define WLAN_INT_ENABLE(n) { sti(); restore_flags((n)); }
+
+#ifdef CONFIG_MODVERSIONS
+#define MODVERSIONS 1
+#include <linux/modversions.h>
+#endif
+
+#ifdef CONFIG_SMP
+#define __SMP__ 1
+#endif
+
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17))
+#define CONFIG_NETLINK 1
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0))
+#define kfree_s(a, b) kfree((a))
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18))
+#ifndef init_waitqueue_head
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,0,16))
+#define init_waitqueue_head(p) (*(p) = NULL)
+#else
+#define init_waitqueue_head(p) init_waitqueue(p)
+#endif
+typedef struct wait_queue *wait_queue_head_t;
+typedef struct wait_queue wait_queue_t;
+#define set_current_state(b) { current->state = (b); mb(); }
+#define init_waitqueue_entry(a, b) { (a)->task = current; }
+#endif
+#endif
+
+#ifndef wait_event_interruptible_timeout
+// retval == 0; signal met; we're good.
+// retval < 0; interrupted by signal.
+// retval > 0; timed out.
+#define __wait_event_interruptible_timeout(wq, condition, timeout, ret) \
+do { \
+ int __ret = 0; \
+ if (!(condition)) { \
+ wait_queue_t __wait; \
+ unsigned long expire; \
+ init_waitqueue_entry(&__wait, current); \
+ \
+ expire = timeout + jiffies; \
+ add_wait_queue(&wq, &__wait); \
+ for (;;) { \
+ set_current_state(TASK_INTERRUPTIBLE); \
+ if (condition) \
+ break; \
+ if (jiffies > expire) { \
+ ret = jiffies - expire; \
+ break; \
+ } \
+ if (!signal_pending(current)) { \
+ schedule_timeout(timeout); \
+ continue; \
+ } \
+ ret = -ERESTARTSYS; \
+ break; \
+ } \
+ set_current_state(TASK_RUNNING); \
+ remove_wait_queue(&wq, &__wait); \
+ } \
+} while (0)
+
+#define wait_event_interruptible_timeout(wq, condition, timeout) \
+({ \
+ int __ret = 0; \
+ if (!(condition)) \
+ __wait_event_interruptible_timeout(wq, condition, \
+ timeout, __ret); \
+ __ret; \
+})
+
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,90))
+#define spin_lock(l) do { } while (0)
+#define spin_unlock(l) do { } while (0)
+#define spin_lock_irqsave(l,f) do { save_flags(f); cli(); } while (0)
+#define spin_unlock_irqrestore(l,f) do { restore_flags(f); } while (0)
+#define spin_lock_init(s) do { } while (0)
+#define spin_trylock(l) (1)
+typedef int spinlock_t;
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0))
+#ifdef CONFIG_SMP
+#define spin_is_locked(x) (*(volatile char *)(&(x)->lock) <= 0)
+#else
+#define spin_is_locked(l) (0)
+#endif
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,38))
+typedef struct device netdevice_t;
+#elif (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4))
+typedef struct net_device netdevice_t;
+#else
+#undef netdevice_t
+typedef struct net_device netdevice_t;
+#endif
+
+#ifdef WIRELESS_EXT
+#if (WIRELESS_EXT < 13)
+struct iw_request_info
+{
+ __u16 cmd; /* Wireless Extension command */
+ __u16 flags; /* More to come ;-) */
+};
+#endif
+#endif
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,18))
+#define MODULE_PARM(a,b) extern int __bogus_decl
+#define MODULE_AUTHOR(a) extern int __bogus_decl
+#define MODULE_DESCRIPTION(a) extern int __bogus_decl
+#define MODULE_SUPPORTED_DEVICE(a) extern int __bogus_decl
+#undef GET_USE_COUNT
+#define GET_USE_COUNT(m) mod_use_count_
+#endif
+
+#ifndef MODULE_LICENSE
+#define MODULE_LICENSE(m) extern int __bogus_decl
+#endif
+
+/* TODO: Do we care about this? */
+#ifndef MODULE_DEVICE_TABLE
+#define MODULE_DEVICE_TABLE(foo,bar)
+#endif
+
+#define wlan_minutes2ticks(a) ((a)*(wlan_ticks_per_sec * 60))
+#define wlan_seconds2ticks(a) ((a)*(wlan_ticks_per_sec))
+
+/*=============================================================*/
+/*------ Hardware Portability Macros --------------------------*/
+/*=============================================================*/
+
+#define ieee2host16(n) __le16_to_cpu(n)
+#define ieee2host32(n) __le32_to_cpu(n)
+#define host2ieee16(n) __cpu_to_le16(n)
+#define host2ieee32(n) __cpu_to_le32(n)
+
+#if (WLAN_CPU_FAMILY == WLAN_PPC)
+ #define wlan_inw(a) in_be16((unsigned short *)((a)+_IO_BASE))
+ #define wlan_inw_le16_to_cpu(a) inw((a))
+ #define wlan_outw(v,a) out_be16((unsigned short *)((a)+_IO_BASE), (v))
+ #define wlan_outw_cpu_to_le16(v,a) outw((v),(a))
+#else
+ #define wlan_inw(a) inw((a))
+ #define wlan_inw_le16_to_cpu(a) __cpu_to_le16(inw((a)))
+ #define wlan_outw(v,a) outw((v),(a))
+ #define wlan_outw_cpu_to_le16(v,a) outw(__cpu_to_le16((v)),(a))
+#endif
+
+/*=============================================================*/
+/*--- General Macros ------------------------------------------*/
+/*=============================================================*/
+
+#define wlan_max(a, b) (((a) > (b)) ? (a) : (b))
+#define wlan_min(a, b) (((a) < (b)) ? (a) : (b))
+
+#define wlan_isprint(c) (((c) > (0x19)) && ((c) < (0x7f)))
+
+#define wlan_hexchar(x) (((x) < 0x0a) ? ('0' + (x)) : ('a' + ((x) - 0x0a)))
+
+/* Create a string of printable chars from something that might not be */
+/* It's recommended that the str be 4*len + 1 bytes long */
+#define wlan_mkprintstr(buf, buflen, str, strlen) \
+{ \
+ int i = 0; \
+ int j = 0; \
+ memset(str, 0, (strlen)); \
+ for (i = 0; i < (buflen); i++) { \
+ if ( wlan_isprint((buf)[i]) ) { \
+ (str)[j] = (buf)[i]; \
+ j++; \
+ } else { \
+ (str)[j] = '\\'; \
+ (str)[j+1] = 'x'; \
+ (str)[j+2] = wlan_hexchar(((buf)[i] & 0xf0) >> 4); \
+ (str)[j+3] = wlan_hexchar(((buf)[i] & 0x0f)); \
+ j += 4; \
+ } \
+ } \
+}
+
+/*=============================================================*/
+/*--- Variables -----------------------------------------------*/
+/*=============================================================*/
+
+extern int wlan_debug;
+extern int wlan_ethconv; /* What's the default ethconv? */
+
+/*=============================================================*/
+/*--- Functions -----------------------------------------------*/
+/*=============================================================*/
+#endif /* _WLAN_COMPAT_H */
+