From 4c0d5a0b719bf0b69b733e4aeea3ed45d2b21216 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 14 Apr 2005 12:23:12 +0000 Subject: [PATCH] Split 3c509.c into 3c509.9 and 3c529.c, with shared code in 3c5x9.c. Probe mechanisms now use standard EISA and MCA bus methods, with 3c509.c defining a custom ISA bus to handle the 3c509 contention resolution logic. --- src/drivers/net/3c509.c | 865 +++++++++++----------------------------- src/drivers/net/3c509.h | 12 +- src/drivers/net/3c529.c | 49 +++ src/drivers/net/3c5x9.c | 413 +++++++++++++++++++ 4 files changed, 700 insertions(+), 639 deletions(-) create mode 100644 src/drivers/net/3c529.c create mode 100644 src/drivers/net/3c5x9.c diff --git a/src/drivers/net/3c509.c b/src/drivers/net/3c509.c index cd0d5456..e35bd8b8 100644 --- a/src/drivers/net/3c509.c +++ b/src/drivers/net/3c509.c @@ -1,355 +1,92 @@ -/************************************************************************** -ETHERBOOT - BOOTP/TFTP Bootstrap Program +/* + * Split out into 3c509.c and 3c5x9.c, to make it possible to build a + * 3c529 module without including ISA, ISAPnP and EISA code. + * + */ -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 "eisa.h" #include "isa.h" +#include "dev.h" +#include "io.h" #include "timer.h" +#include "string.h" +#include "etherboot.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) + * 3c509 cards have their own method of contention resolution; this + * effectively defines another bus type. * */ -struct el3_mca_adapters_struct { - const char *name; - int id; + +/* + * A physical t509 device + * + */ +struct t509_device { + char *magic; /* must be first */ + struct dev *dev; + uint16_t id_port; + uint16_t ioaddr; + unsigned char current_tag; }; -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 }, + +/* + * A t509 driver + * + */ +struct t509_driver { + char *name; }; -#endif -/************************************************************************** -ETH_RESET - Reset adapter -***************************************************************************/ -static void t509_reset(struct nic *nic) -{ - int i; +/* + * Ensure that there is sufficient space in the shared dev_bus + * structure for a struct pci_device. + * + */ +DEV_BUS( struct t509_device, t509_dev ); +static char t509_magic[0]; /* guaranteed unique symbol */ - /*********************************************************** - 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); +/* + * Find a port that can be used for contention select + * + * Called only once, so inlined for efficiency. + * + */ +static inline int find_id_port ( struct t509_device *t509 ) { + for ( t509->id_port = EP_ID_PORT_START ; + t509->id_port < EP_ID_PORT_END ; + t509->id_port += EP_ID_PORT_INC ) { + outb ( 0x00, t509->id_port ); + outb ( 0xff, t509->id_port ); + if ( inb ( t509->id_port ) & 0x01 ) { + /* Found a suitable port */ + return 1; } - 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); + /* No id port available */ + return 0; } /* - * get_e: gets a 16 bits word from the EEPROM. we must have set the window - * before + * Send ID sequence to the ID port + * + * Called only once, so inlined for efficiency. + * */ -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 inline void send_id_sequence ( struct t509_device *t509 ) { + unsigned short lrs_state, i; + + outb ( 0x00, t509->id_port ); + outb ( 0x00, t509->id_port ); + lrs_state = 0xff; + for ( i = 0; i < 255; i++ ) { + outb ( lrs_state, t509->id_port ); + lrs_state <<= 1; + lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state; + } } -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 @@ -362,314 +99,170 @@ send_ID_sequence(int port) * 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) -{ +static uint16_t id_read_eeprom ( struct t509_device *t509, int offset ) { int i, data = 0; - outb(0x80 + offset, id_port); + + outb ( 0x80 + offset, t509->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); + + for ( i = 0; i < 16; i++ ) { + data = ( data << 1 ) | ( inw ( t509->id_port ) & 1 ); + } + return data; } -static void __t509_disable(void) -{ - outb(0xc0, EP_ID_PORT); -} - -static void t509_disable ( struct nic *nic ) { - /* 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= 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; -static struct nic_operations t509_operations; -static struct nic_operations t509_operations = { - .connect = dummy_connect, - .poll = t509_poll, - .transmit = t509_transmit, - .irq = t509_irq, - .disable = t509_disable, -}; - nic->nic_op = &t509_operations; - - /* 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, -}; -ISA_ROM("3c509","3c509, ISA/EISA"); -#endif - -#ifdef INCLUDE_3C529 -static struct isa_driver t529_driver __isa_driver = { - .type = NIC_DRIVER, - .name = "3C529", - .probe = t529_probe, - .ioaddrs = 0, -}; -ISA_ROM("3c529","3c529 == MCA 3c509"); -#endif - /* - * Local variables: - * c-basic-offset: 8 - * End: + * Find the next t509 device + * + * Called only once, so inlined for efficiency. + * */ +static inline int fill_t509_device ( struct t509_device *t509 ) { + int i; + uint16_t iobase; + + /* + * If this is the start of the scan, find an id_port and clear + * all tag registers. Otherwise, tell already-found NICs not + * to respond. + * + */ + if ( t509->current_tag == 0 ) { + if ( ! find_id_port ( t509 ) ) { + DBG ( "No ID port available for contention select\n" ); + return 0; + } + outb ( 0xd0, t509->id_port ); + } else { + outb ( 0xd8, t509->id_port ) ; + } + + /* Send the ID sequence */ + send_id_sequence ( t509 ); + + /* Check the manufacturer ID */ + if ( id_read_eeprom ( t509, EEPROM_MFG_ID ) != MFG_ID ) { + /* No more t509 devices */ + return 0; + } + + /* Do contention select by reading the MAC address */ + for ( i = 0 ; i < 3 ; i++ ) { + id_read_eeprom ( t509, i ); + } + + /* By now, only one device will be left active. Get its I/O + * address, tag and activate the adaptor. Tagging will + * prevent it taking part in the next scan, enabling us to see + * the next device. + */ + iobase = id_read_eeprom ( t509, EEPROM_ADDR_CFG ); + t509->ioaddr = 0x200 + ( ( iobase & 0x1f ) << 4 ); + outb ( ++t509->current_tag, t509->id_port ); /* tag */ + outb ( ( 0xe0 | iobase ), t509->id_port ); /* activate */ + + return 1; +} + +/* + * Obtain a struct t509_device * from a struct dev * + * + * If dev has not previously been used for a PCI device scan, blank + * out struct t509_device + */ +static struct t509_device * t509_device ( struct dev *dev ) { + struct t509_device *t509 = dev->bus; + + if ( t509->magic != t509_magic ) { + memset ( t509, 0, sizeof ( *t509 ) ); + t509->magic = t509_magic; + } + t509->dev = dev; + return t509; +} + +/* + * Find a t509 device matching the specified driver. ("Matching the + * specified driver" is, in this case, a no-op, but we want to + * preserve the common bus API). + * + */ +static int find_t509_device ( struct t509_device *t509, + struct t509_driver *driver ) { + /* Find the next t509 device */ + if ( ! fill_t509_device ( t509 ) ) + return 0; + + /* Fill in dev structure, if present */ + if ( t509->dev ) { + t509->dev->name = driver->name; + t509->dev->devid.bus_type = ISA_BUS_TYPE; + t509->dev->devid.vendor_id = MFG_ID; + t509->dev->devid.device_id = PROD_ID; + } + + return 1; +} + +/* + * The ISA probe function + * + */ +static struct t509_driver el3_t509_driver = { "3c509 (ISA)" }; + +static int el3_t509_probe ( struct dev *dev ) { + struct nic *nic = nic_device ( dev ); + struct t509_device *t509 = t509_device ( dev ); + + if ( ! find_t509_device ( t509, &el3_t509_driver ) ) + return 0; + + nic->ioaddr = t509->ioaddr; + nic->irqno = 0; + printf ( "3C5x9 board on ISA at %#hx - ", nic->ioaddr ); + + /* Hand off to generic t5x9 probe routine */ + return t5x9_probe ( nic, ISA_PROD_ID ( PROD_ID ), ISA_PROD_ID_MASK ); +} + +BOOT_DRIVER ( "3c509", el3_t509_probe ); + +/* + * The 3c509 driver also supports EISA cards + * + */ +static struct eisa_id el3_eisa_adapters[] = { + { "3Com 3c509 EtherLink III (EISA)", MFG_ID, PROD_ID }, +}; + +static struct eisa_driver el3_eisa_driver = + EISA_DRIVER ( "3c509 (EISA)", el3_eisa_adapters ); + +static int el3_eisa_probe ( struct dev *dev ) { + struct nic *nic = nic_device ( dev ); + struct eisa_device *eisa = eisa_device ( dev ); + + if ( ! find_eisa_device ( eisa, &el3_eisa_driver ) ) + return 0; + + enable_eisa_device ( eisa ); + nic->ioaddr = eisa->ioaddr; + nic->irqno = 0; + printf ( "3C5x9 board on EISA at %#hx - ", nic->ioaddr ); + + /* Hand off to generic t5x9 probe routine */ + return t5x9_probe ( nic, ISA_PROD_ID ( PROD_ID ), ISA_PROD_ID_MASK ); +} + +BOOT_DRIVER ( "3c509 (EISA)", el3_eisa_probe ); + +/* + * We currently build both ISA and EISA support into a single ROM + * image, though there's no reason why this couldn't be split to + * reduce code size; just split this .c file into two in the obvious + * place. + * + */ +ISA_ROM ( "3c509","3c509, ISA/EISA" ); + diff --git a/src/drivers/net/3c509.h b/src/drivers/net/3c509.h index 72f8e445..1f29f1e5 100644 --- a/src/drivers/net/3c509.h +++ b/src/drivers/net/3c509.h @@ -49,9 +49,9 @@ #define MAX_EEPROMBUSY 1000 #define EP_LAST_TAG 0xd7 #define EP_MAX_BOARDS 16 -#ifndef EP_ID_PORT -#define EP_ID_PORT 0x100 -#endif +#define EP_ID_PORT_START 0x110 +#define EP_ID_PORT_INC 0x10 +#define EP_ID_PORT_END 0x200 /* * Commands to read/write EEPROM trough EEPROM command register (Window 0, @@ -374,6 +374,12 @@ #define RX_BYTES_MASK (unsigned short) (0x07ff) +/* + * Function shared between 3c509.c and 3c529.c + */ +extern int t5x9_probe ( struct nic *nic, + uint16_t prod_id_check, uint16_t prod_id_mask ); + /* * Local variables: diff --git a/src/drivers/net/3c529.c b/src/drivers/net/3c529.c new file mode 100644 index 00000000..8faf3bf5 --- /dev/null +++ b/src/drivers/net/3c529.c @@ -0,0 +1,49 @@ +/* + * Split out from 3c509.c to make build process more sane + * + */ + +#include "etherboot.h" +#include "mca.h" +#include "isa.h" +#include "nic.h" +#include "3c509.h" + +/* + * 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) + * + */ +static struct mca_id 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 }, +}; + +static struct mca_driver t529_driver + = MCA_DRIVER ( "3c529", el3_mca_adapters ); + +ISA_ROM( "3c529", "3c529 == MCA 3c509" ); + +static int t529_probe ( struct dev *dev ) { + struct nic *nic = nic_device ( dev ); + struct mca_device *mca = mca_device ( dev ); + + if ( ! find_mca_device ( mca, &t529_driver ) ) + return 0; + + /* Retrieve NIC parameters from MCA device parameters */ + nic->ioaddr = ( ( mca->pos[4] & 0xfc ) | 0x02 ) << 8; + nic->irqno = mca->pos[5] & 0x0f; + printf ( "%s board found on MCA at %#hx IRQ %d -", + dev->name, nic->ioaddr, nic->irqno ); + + /* Hand off to generic t5x9 probe routine */ + return t5x9_probe ( nic, MCA_ID ( mca ), 0xffff ); +} + +BOOT_DRIVER ( "3c529", t529_probe ); diff --git a/src/drivers/net/3c5x9.c b/src/drivers/net/3c5x9.c new file mode 100644 index 00000000..414c385e --- /dev/null +++ b/src/drivers/net/3c5x9.c @@ -0,0 +1,413 @@ +/************************************************************************** +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 enum { none, bnc, utp } connector = none; /* for 3C509 */ + +/************************************************************************** +ETH_RESET - Reset adapter +***************************************************************************/ +static void t509_disable ( struct nic *nic ) { + /* stop card */ + outw(RX_DISABLE, nic->ioaddr + EP_COMMAND); + outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND); + while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS) + ; + outw(TX_DISABLE, nic->ioaddr + EP_COMMAND); + outw(STOP_TRANSCEIVER, nic->ioaddr + EP_COMMAND); + udelay(1000); + outw(RX_RESET, nic->ioaddr + EP_COMMAND); + outw(TX_RESET, nic->ioaddr + EP_COMMAND); + outw(C_INTR_LATCH, nic->ioaddr + EP_COMMAND); + outw(SET_RD_0_MASK, nic->ioaddr + EP_COMMAND); + outw(SET_INTR_MASK, nic->ioaddr + EP_COMMAND); + outw(SET_RX_FILTER, nic->ioaddr + EP_COMMAND); + + /* + * wait for reset to complete + */ + while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS) + ; + + GO_WINDOW(nic->ioaddr,0); + + /* Disable the card */ + outw(0, nic->ioaddr + EP_W0_CONFIG_CTRL); + + /* Configure IRQ to none */ + outw(SET_IRQ(0), nic->ioaddr + EP_W0_RESOURCE_CFG); +} + +static void t509_enable ( struct nic *nic ) { + int i; + + /* Enable the card */ + GO_WINDOW(nic->ioaddr,0); + outw(ENABLE_DRQ_IRQ, nic->ioaddr + EP_W0_CONFIG_CTRL); + + GO_WINDOW(nic->ioaddr,2); + + /* Reload the ether_addr. */ + for (i = 0; i < ETH_ALEN; i++) + outb(nic->node_addr[i], nic->ioaddr + EP_W2_ADDR_0 + i); + + outw(RX_RESET, nic->ioaddr + EP_COMMAND); + outw(TX_RESET, nic->ioaddr + EP_COMMAND); + + /* Window 1 is operating window */ + GO_WINDOW(nic->ioaddr,1); + for (i = 0; i < 31; i++) + inb(nic->ioaddr + EP_W1_TX_STATUS); + + /* get rid of stray intr's */ + outw(ACK_INTR | 0xff, nic->ioaddr + EP_COMMAND); + + outw(SET_RD_0_MASK | S_5_INTS, nic->ioaddr + EP_COMMAND); + + outw(SET_INTR_MASK, nic->ioaddr + EP_COMMAND); + + outw(SET_RX_FILTER | FIL_GROUP | FIL_INDIVIDUAL | FIL_BRDCST, + nic->ioaddr + EP_COMMAND); + + /* configure BNC */ + if (connector == bnc) { + outw(START_TRANSCEIVER, nic->ioaddr + EP_COMMAND); + udelay(1000); + } + /* configure UTP */ + else if (connector == utp) { + GO_WINDOW(nic->ioaddr,4); + outw(ENABLE_UTP, nic->ioaddr + EP_W4_MEDIA_TYPE); + sleep(2); /* Give time for media to negotiate */ + GO_WINDOW(nic->ioaddr,1); + } + + /* start transceiver and receiver */ + outw(RX_ENABLE, nic->ioaddr + EP_COMMAND); + outw(TX_ENABLE, nic->ioaddr + EP_COMMAND); + + /* set early threshold for minimal packet length */ + outw(SET_RX_EARLY_THRESH | ETH_ZLEN, nic->ioaddr + EP_COMMAND); + outw(SET_TX_START_THRESH | 16, nic->ioaddr + EP_COMMAND); +} + +static void t509_reset ( struct nic *nic ) { + t509_disable ( nic ); + t509_enable ( nic ); +} + +/************************************************************************** +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(nic->ioaddr + EP_W1_TX_STATUS)) & TXS_COMPLETE ) { + if (status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) { + outw(TX_RESET, nic->ioaddr + EP_COMMAND); + outw(TX_ENABLE, nic->ioaddr + EP_COMMAND); + } + outb(0x0, nic->ioaddr + EP_W1_TX_STATUS); + } + + while (inw(nic->ioaddr + EP_W1_FREE_TX) < (unsigned short)len + pad + 4) + ; /* no room in FIFO */ + + outw(len, nic->ioaddr + EP_W1_TX_PIO_WR_1); + outw(0x0, nic->ioaddr + EP_W1_TX_PIO_WR_1); /* Second dword meaningless */ + + /* write packet */ + outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, d, ETH_ALEN/2); + outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2); + outw(t, nic->ioaddr + EP_W1_TX_PIO_WR_1); + outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, p, s / 2); + if (s & 1) + outb(*(p+s - 1), nic->ioaddr + EP_W1_TX_PIO_WR_1); + + while (pad--) + outb(0, nic->ioaddr + EP_W1_TX_PIO_WR_1); /* Padding */ + + /* wait for Tx complete */ + while((inw(nic->ioaddr + 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(nic->ioaddr + 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), nic->ioaddr + EP_COMMAND); + outw(C_INTR_LATCH, nic->ioaddr + EP_COMMAND); + + return 0; + } + + status = inw(nic->ioaddr + EP_W1_RX_STATUS); +#ifdef EDEBUG + printf("*%hX*",status); +#endif + + if (status & ERR_RX) { + outw(RX_DISCARD_TOP_PACK, nic->ioaddr + 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(nic->ioaddr + EP_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2); + if(rx_fifo & 1) + nic->packet[rx_fifo-1]=inb(nic->ioaddr + EP_W1_RX_PIO_RD_1); + nic->packetlen=rx_fifo; + + while(1) { + status = inw(nic->ioaddr + EP_W1_RX_STATUS); +#ifdef EDEBUG + printf("*%hX*",status); +#endif + rx_fifo = status & RX_BYTES_MASK; + if(rx_fifo>0) { + insw(nic->ioaddr + 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(nic->ioaddr + 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, nic->ioaddr + EP_COMMAND); + while (inw(nic->ioaddr + 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); +} + +/************************************************************************** +ETH_IRQ - interrupt handling +***************************************************************************/ +static void t509_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +/************************************************************************* + 3Com 509 - specific routines +**************************************************************************/ + +static int eeprom_rdy ( uint16_t ioaddr ) { + int i; + + for (i = 0; is_eeprom_busy(ioaddr) && 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. + */ +static int get_e ( uint16_t ioaddr, int offset ) { + GO_WINDOW(ioaddr,0); + if (!eeprom_rdy(ioaddr)) + return (0xffff); + outw(EEPROM_CMD_RD | offset, ioaddr + EP_W0_EEPROM_COMMAND); + if (!eeprom_rdy(ioaddr)) + return (0xffff); + return (inw(ioaddr + EP_W0_EEPROM_DATA)); +} + +struct nic_operations t509_operations = { + .connect = dummy_connect, + .poll = t509_poll, + .transmit = t509_transmit, + .irq = t509_irq, + .disable = t509_disable, +}; + +/************************************************************************** +ETH_PROBE - Look for an adapter +***************************************************************************/ +int t5x9_probe ( struct nic *nic, + uint16_t prod_id_check, uint16_t prod_id_mask ) { + uint16_t prod_id; + int i,j; + unsigned short *p; + + /* Check product ID */ + prod_id = get_e ( nic->ioaddr, EEPROM_PROD_ID ); + if ( ( prod_id & prod_id_mask ) != prod_id_check ) { + printf ( "EEPROM Product ID is incorrect (%hx & %hx != %hx)\n", + prod_id, prod_id_mask, prod_id_check ); + return 0; + } + + /* test for presence of connectors */ + GO_WINDOW(nic->ioaddr,0); + i = inw(nic->ioaddr + EP_W0_CONFIG_CTRL); + j = (inw(nic->ioaddr + 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"); + return 0; + } + break; + case 1: + if (i & IS_AUI) { + printf("10base5\n"); + } else { + printf("10base5 not present\n"); + return 0; + } + break; + case 3: + if (i & IS_BNC) { + printf("10base2\n"); + connector = bnc; + } else { + printf("10base2 not present\n"); + return 0; + } + break; + default: + printf("unknown connector\n"); + return 0; + } + + /* + * Read the station address from the eeprom + */ + p = (unsigned short *) nic->node_addr; + for (i = 0; i < ETH_ALEN / 2; i++) { + p[i] = htons(get_e(nic->ioaddr,i)); + GO_WINDOW(nic->ioaddr,2); + outw(ntohs(p[i]), nic->ioaddr + EP_W2_ADDR_0 + (i * 2)); + } + printf("Ethernet address: %!\n", nic->node_addr); + t509_reset(nic); + + nic->nic_op = &t509_operations; + return 1; + +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */