| /* |
| Tulip and clone Etherboot Driver |
| By Marty Connor (mdc@thinguin.org) |
| This software may be used and distributed according to the terms |
| of the GNU Public License, incorporated herein by reference. |
| |
| 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 */ |
| /*********************************************************************/ |
| |
| /* |
| 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" |
| #include "cards.h" |
| |
| #undef TULIP_DEBUG |
| #undef TULIP_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 tulip device */ |
| 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 |
| }; |
| |
| #define DEC_21142_CSR6_TTM 0x00400000 /* Transmit Threshold Mode */ |
| #define DEC_21142_CSR6_HBD 0x00080000 /* Heartbeat Disable */ |
| #define DEC_21142_CSR6_PS 0x00040000 /* Port Select */ |
| |
| /* EEPROM Address width definitions */ |
| #define EEPROM_ADDRLEN 6 |
| #define EEPROM_SIZE 128 /* 2 << EEPROM_ADDRLEN */ |
| |
| /* 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) |
| |
| /* 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 */ |
| #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 */ |
| static struct txdesc txd __attribute__ ((aligned(4))); |
| #ifndef USE_INTERNAL_BUFFER |
| #define txb ((char *)0x10000 - BUFLEN) |
| #else |
| static unsigned char txb[BUFLEN] __attribute__ ((aligned(4))); |
| #endif |
| |
| /* receive descriptor(s) and buffer(s) */ |
| #define NRXD 4 |
| static struct rxdesc rxd[NRXD] __attribute__ ((aligned(4))); |
| #ifndef USE_INTERNAL_BUFFER |
| #define rxb ((char *)0x10000 - NRXD * BUFLEN - BUFLEN) |
| #else |
| static unsigned char rxb[NRXD * BUFLEN] __attribute__ ((aligned(4))); |
| #endif |
| static int rxd_tail; |
| |
| /* buffer for ethernet header */ |
| static unsigned char ehdr[ETHER_HDR_SIZE]; |
| |
| |
| /*********************************************************************/ |
| /* Function Prototypes */ |
| /*********************************************************************/ |
| static void whereami(const char *str); |
| static int lc82c168_mdio_read(int phy_id, int location); |
| static void lc82c168_mdio_write(int phy_id, int location, int value); |
| static void lc82c168_do_mii(); |
| static int read_eeprom(unsigned long ioaddr, int location, int addr_len); |
| struct nic *tulip_probe(struct nic *nic, unsigned short *io_addrs, |
| 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); |
| static void tulip_disable(struct nic *nic); |
| static void whereami (const char *str); |
| #ifdef TULIP_DEBUG |
| static void tulip_more(void); |
| #endif /* TULIP_DEBUG */ |
| static void tulip_wait(unsigned int nticks); |
| |
| |
| /*********************************************************************/ |
| /* Utility Routines */ |
| /*********************************************************************/ |
| |
| static inline void whereami (const char *str) |
| { |
| #ifdef TULIP_DEBUG_WHERE |
| printf("%s\n", str); |
| /* sleep(2); */ |
| #endif |
| } |
| |
| #ifdef TULIP_DEBUG |
| static void tulip_more() |
| { |
| 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 */ |
| /*********************************************************************/ |
| static int lc82c168_mdio_read(int phy_id, int location) |
| { |
| int i = 1000; |
| int retval = 0; |
| |
| whereami("mdio_read\n"); |
| |
| 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; |
| if (i == 0) printf("mdio read timeout!\n"); |
| return 0xFFFF; |
| } |
| |
| static void lc82c168_mdio_write(int phy_id, int location, int value) |
| { |
| int i = 1000; |
| int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; |
| |
| whereami("mdio_write\n"); |
| |
| outl(cmd, ioaddr + 0xA0); |
| do |
| if ( ! (inl(ioaddr + 0xA0) & 0x80000000)) |
| break; |
| while (--i > 0); |
| if (i == 0) printf("mdio write timeout!\n"); |
| return; |
| } |
| |
| static void lc82c168_do_mii(void) |
| { |
| int phy, phy_idx; |
| |
| whereami("do_mii\n"); |
| |
| for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < 4; phy++) { |
| |
| int mii_status = lc82c168_mdio_read(phy, 1); |
| |
| if ((mii_status & 0x8301) == 0x8001 || |
| ((mii_status & 0x8000) == 0 && (mii_status & 0x7800) != 0)) { |
| |
| int mii_reg0 = lc82c168_mdio_read(phy, 0); |
| int mii_advert = lc82c168_mdio_read(phy, 4); |
| int mii_reg4 = ((mii_status >> 6) & 0x01E1) | 1; |
| |
| phy_idx++; |
| printf("%s: MII trcvr #%d " |
| "config %x status %x advertising %x reg4 %x.\n", |
| "LC82C168", phy, mii_reg0, mii_status, mii_advert, mii_reg4); |
| |
| lc82c168_mdio_write(phy, 0, mii_reg0 | 0x1000); |
| if (mii_advert != mii_reg4) |
| lc82c168_mdio_write(phy, 4, mii_reg4); |
| } |
| } |
| #ifdef TULIP_DEBUG |
| printf("mii_cnt = %d\n", phy_idx); |
| #endif |
| |
| } |
| |
| |
| /*********************************************************************/ |
| /* 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; |
| } |
| |
| /*********************************************************************/ |
| /* tulip_init_ring - setup the tx and rx descriptors */ |
| /*********************************************************************/ |
| static void tulip_init_ring(struct nic *nic) |
| { |
| int i; |
| |
| /* setup the transmit descriptor */ |
| txd.buf1addr = &txb[0]; |
| txd.buf2addr = &txb[0]; /* just in case */ |
| txd.buf1sz = 192; /* setup packet must be 192 bytes */ |
| txd.buf2sz = 0; |
| txd.control = 0x028; /* setup packet + TER */ |
| txd.status = 0x80000000; /* give ownership to device */ |
| |
| /* 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 = &rxb[i * BUFLEN]; |
| rxd[i].buf2addr = 0; /* not used */ |
| rxd[i].buf1sz = BUFLEN; |
| rxd[i].buf2sz = 0; /* not used */ |
| rxd[i].control = 0x0; |
| rxd[i].status = 0x80000000; /* give ownership to device */ |
| } |
| |
| /* Set Receive end of ring on last descriptor */ |
| rxd[NRXD - 1].control = 0x008; |
| rxd_tail = 0; |
| } |
| |
| /*********************************************************************/ |
| /* eth_reset - Reset adapter */ |
| /*********************************************************************/ |
| static void tulip_reset(struct nic *nic) |
| { |
| unsigned long to, csr6; |
| u32 addr_low, addr_high; |
| |
| whereami("tulip_reset\n"); |
| |
| /* Stop Tx and RX */ |
| outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6); |
| |
| if (vendor == PCI_VENDOR_ID_MACRONIX && dev_id == PCI_DEVICE_ID_MX987x5) { |
| /* set up 10-BASE-T Control Port */ |
| outl(0xFFFFFFFF, ioaddr + CSR14); |
| /* set up 10-BASE-T Status Port */ |
| outl(0x00001000, ioaddr + CSR12); |
| /* Set Operation Control Register (CSR6) for MX987x5 |
| to allow N-Way Active Speed selection, and |
| start the chip's Tx to process setup frame. |
| While it is possible to force speed selection, |
| this is probably more useful most of the time. |
| */ |
| outl(0x01A80200, ioaddr + CSR6); |
| |
| } else if (vendor == PCI_VENDOR_ID_LINKSYS && dev_id == PCI_DEVICE_ID_LC82C115) { |
| /* This is MX987x5 init code. It seems to work for the LNE100TX |
| but should be replaced when we figure out the right way |
| to do this initialization |
| */ |
| outl(0xFFFFFFFF, ioaddr + CSR14); |
| outl(0x00001000, ioaddr + CSR12); |
| outl(0x01A80200, ioaddr + CSR6); |
| |
| } else if (vendor == PCI_VENDOR_ID_LINKSYS && dev_id == PCI_DEVICE_ID_DEC_TULIP) { |
| |
| lc82c168_do_mii(); |
| |
| } else if (vendor == PCI_VENDOR_ID_DEC && dev_id == PCI_DEVICE_ID_DEC_21142) { |
| /* nothing */ |
| |
| } else if (vendor == PCI_VENDOR_ID_ADMTEK && dev_id == PCI_DEVICE_ID_ADMTEK_0985) { |
| /* nothing */ |
| |
| } else { |
| /* If we don't know what to do with the card, set to 10Mbps half-duplex */ |
| |
| outl(0x00000000, ioaddr + CSR13); |
| outl(0x7F3F0000, ioaddr + CSR14); |
| outl(0x08000008, ioaddr + CSR15); |
| outl(0x00000000, ioaddr + CSR13); |
| outl(0x00000001, ioaddr + CSR13); |
| outl(0x02404000, ioaddr + CSR6); |
| outl(0x08AF0008, ioaddr + CSR15); |
| outl(0x00050008, ioaddr + CSR15); |
| |
| } |
| |
| /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ |
| outl(0x00000001, ioaddr + CSR0); |
| |
| tulip_wait(3*TICKS_PER_SEC); |
| |
| /* turn off reset and set cache align=16lword, burst=unlimit */ |
| outl(0x01A08000, ioaddr + CSR0); |
| |
| /* set up transmit and receive descriptors */ |
| tulip_init_ring(nic); |
| |
| /* set up multicast hash address for Comet (ADKTEK 0985) */ |
| /* possibly not needed for Etherboot, but seems to do no harm -mdc */ |
| if (vendor == PCI_VENDOR_ID_ADMTEK && dev_id == PCI_DEVICE_ID_ADMTEK_0985) { |
| addr_low = nic->node_addr[0] + (nic->node_addr[1] << 8) |
| + (nic->node_addr[2] << 16) + (nic->node_addr[3] << 24); |
| addr_high = nic->node_addr[4] + (nic->node_addr[5] << 8); |
| outl(addr_low, ioaddr + 0xA4); |
| outl(addr_high, ioaddr + 0xA8); |
| outl(0, ioaddr + 0xAC); |
| outl(0, ioaddr + 0xB0); |
| } |
| |
| /* Point to receive descriptor */ |
| outl((unsigned long)&rxd[0], ioaddr + CSR3); |
| outl((unsigned long)&txd , ioaddr + CSR4); |
| |
| csr6 = 0x02404000; |
| |
| /* Chip specific init code */ |
| |
| if (vendor == PCI_VENDOR_ID_MACRONIX && dev_id == PCI_DEVICE_ID_MX987x5) { |
| csr6 = 0x01880200; |
| /* Set CSR16 and CSR20 to values that allow device modification */ |
| outl(0x0B3C0000 | inl(ioaddr + CSR16), ioaddr + CSR16); |
| outl(0x00011000 | inl(ioaddr + CSR20), ioaddr + CSR20); |
| |
| } else if (vendor == PCI_VENDOR_ID_LINKSYS && dev_id == PCI_DEVICE_ID_LC82C115) { |
| /* This is MX987x5 init code. It seems to work for the LNE100TX |
| but should be replaced when we figure out the right way |
| to do this initialization. |
| */ |
| csr6 = 0x01880200; |
| outl(0x0B3C0000 | inl(ioaddr + CSR16), ioaddr + CSR16); |
| outl(0x00011000 | inl(ioaddr + CSR20), ioaddr + CSR20); |
| |
| } else if (vendor == PCI_VENDOR_ID_LINKSYS && dev_id == PCI_DEVICE_ID_DEC_TULIP) { |
| |
| csr6 = 0x814C0000; |
| outl(0x00000001, ioaddr + CSR15); |
| |
| } else if (vendor == PCI_VENDOR_ID_DEC && dev_id == PCI_DEVICE_ID_DEC_21142) { |
| /* check SROM for evidence of an MII interface */ |
| /* get Controller_0 Info Leaf Offset from SROM - assume already in ee_data */ |
| int offset = ee_data [27] + (ee_data [28] << 8); |
| |
| /* check offset range and if we have an extended type 3 Info Block */ |
| if ((offset >= 30) && (offset < 120) && (ee_data [offset + 3] > 128) && |
| (ee_data [offset + 4] == 3)) { |
| /* must have an MII interface - disable heartbeat, select port etc. */ |
| csr6 |= (DEC_21142_CSR6_HBD | DEC_21142_CSR6_PS); |
| csr6 &= ~(DEC_21142_CSR6_TTM); |
| } |
| } else if (vendor == PCI_VENDOR_ID_DAVICOM && dev_id == PCI_DEVICE_ID_DM9102){ |
| /* setup CR12 */ |
| outl(0x180, ioaddr + CSR12); /* Let bit 7 output port */ |
| outl(0x80, ioaddr + CSR12); /* RESET DM9102 phyxcer */ |
| outl(0x0, ioaddr + CSR12); /* Clear RESET signal */ |
| |
| } else if (vendor == PCI_VENDOR_ID_ADMTEK && dev_id == PCI_DEVICE_ID_ADMTEK_0985) { |
| /* nothing */ |
| |
| } |
| |
| /* set the chip's operating mode */ |
| outl(csr6, ioaddr + CSR6); |
| |
| /* Start Tx */ |
| outl(inl(ioaddr + CSR6) | 0x00002000, ioaddr + CSR6); |
| /* immediate transmit demand */ |
| outl(0, ioaddr + CSR1); |
| |
| to = currticks() + TX_TIME_OUT; |
| while ((txd.status & 0x80000000) && (currticks() < to)) |
| /* wait */ ; |
| |
| if (currticks() >= to) { |
| printf ("TX Setup Timeout!\n"); |
| } |
| |
| #ifdef TULIP_DEBUG |
| printf("txd.status = %X\n", txd.status); |
| printf("ticks = %d\n", currticks() - (to - TX_TIME_OUT)); |
| tulip_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 tulip_transmit(struct nic *nic, const char *d, unsigned int t, |
| unsigned int s, const char *p) |
| { |
| unsigned long to; |
| |
| whereami("tulip_transmit\n"); |
| |
| /* Stop Tx */ |
| outl(inl(ioaddr + CSR6) & ~0x00002000, ioaddr + CSR6); |
| |
| /* setup ethernet header */ |
| memcpy(ehdr, d, ETHER_ADDR_SIZE); |
| memcpy(&ehdr[ETHER_ADDR_SIZE], nic->node_addr, ETHER_ADDR_SIZE); |
| ehdr[ETHER_ADDR_SIZE*2] = (t >> 8) & 0xFF; |
| ehdr[ETHER_ADDR_SIZE*2+1] = t & 0xFF; |
| |
| /* setup the transmit descriptor */ |
| txd.buf1addr = &ehdr[0]; /* ethernet header */ |
| txd.buf1sz = ETHER_HDR_SIZE; |
| txd.buf2addr = p; /* packet to transmit */ |
| txd.buf2sz = s; |
| txd.control = 0x00000188; /* LS+FS+TER */ |
| txd.status = 0x80000000; /* give ownership to device */ |
| |
| /* Point to transmit descriptor */ |
| outl((unsigned long)&txd, ioaddr + CSR4); |
| |
| /* Start Tx */ |
| outl(inl(ioaddr + CSR6) | 0x00002000, ioaddr + CSR6); |
| |
| /* immediate transmit demand */ |
| outl(0, ioaddr + CSR1); |
| |
| to = currticks() + TX_TIME_OUT; |
| while ((txd.status & 0x80000000) && (currticks() < to)) |
| /* wait */ ; |
| |
| if (currticks() >= to) { |
| printf ("TX Timeout!\n"); |
| } |
| |
| } |
| |
| /*********************************************************************/ |
| /* eth_poll - Wait for a frame */ |
| /*********************************************************************/ |
| static int tulip_poll(struct nic *nic) |
| { |
| whereami("tulip_poll\n"); |
| |
| if (rxd[rxd_tail].status & 0x80000000) |
| return 0; |
| |
| whereami("tulip_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 tulip_disable(struct nic *nic) |
| { |
| whereami("tulip_disable\n"); |
| |
| /* 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_probe - Look for an adapter */ |
| /*********************************************************************/ |
| struct nic *tulip_probe(struct nic *nic, unsigned short *io_addrs, |
| struct pci_device *pci) |
| { |
| unsigned int i; |
| u32 l1, l2; |
| |
| whereami("tulip_probe\n"); |
| |
| if (io_addrs == 0 || *io_addrs == 0) |
| return 0; |
| |
| vendor = pci->vendor; |
| dev_id = pci->dev_id; |
| ioaddr = *io_addrs; |
| |
| /* wakeup chip */ |
| pcibios_write_config_dword(0, 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 */ |
| |
| /* Hardware Address retrieval method for LC82C168 */ |
| if (vendor == PCI_VENDOR_ID_LINKSYS && dev_id == PCI_DEVICE_ID_DEC_TULIP) { |
| 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); |
| nic->node_addr[i*2] = (u8)((value >> 8) & 0xFF); |
| nic->node_addr[i*2 + 1] = (u8)( value & 0xFF); |
| } |
| } else if (vendor == PCI_VENDOR_ID_ADMTEK && |
| dev_id == PCI_DEVICE_ID_ADMTEK_0985) { |
| l1 = inl(ioaddr + 0xA4); |
| l2 = inl(ioaddr + 0xA8); |
| nic->node_addr[0] = (l1 ) & 0xFF; |
| nic->node_addr[1] = (l1 >> 8) & 0xFF; |
| nic->node_addr[2] = (l1 >> 16) & 0xFF; |
| nic->node_addr[3] = (l1 >> 24) & 0xFF; |
| nic->node_addr[4] = (l2 ) & 0xFF; |
| nic->node_addr[5] = (l2 >> 8) & 0xFF; |
| } else { |
| /* 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<6; i++) |
| nic->node_addr[i] = ee_data[20+i]; |
| } |
| |
| printf("Tulip %b:%b:%b:%b:%b:%b at ioaddr 0x%x\n", |
| nic->node_addr[0],nic->node_addr[1],nic->node_addr[2],nic->node_addr[3], |
| nic->node_addr[4],nic->node_addr[5],ioaddr); |
| |
| /* initialize device */ |
| tulip_reset(nic); |
| |
| nic->reset = tulip_reset; |
| nic->poll = tulip_poll; |
| nic->transmit = tulip_transmit; |
| nic->disable = tulip_disable; |
| |
| return nic; |
| } |