mirror of
https://git.savannah.gnu.org/git/grub.git
synced 2026-10-21 14:59:34 +00:00
750 lines
25 KiB
C
750 lines
25 KiB
C
/*
|
|
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;
|
|
}
|