mirror of
https://git.savannah.gnu.org/git/grub.git
synced 2026-10-21 14:59:34 +00:00
update the network support to etherboot-4.5.8.
This commit is contained in:
1 parent
26fef18cb8
commit
5ff25f9b8f
51 files changed
+5395
-3479
No files matched your search
+605
-286
@@ -1,380 +1,699 @@
|
||||
/*
|
||||
Etherboot DEC Tulip driver
|
||||
adapted by Ken Yap from
|
||||
/*
|
||||
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.
|
||||
|
||||
FreeBSD netboot DEC 21143 driver
|
||||
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.
|
||||
|
||||
Author: David Sharp
|
||||
date: Nov/98
|
||||
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
|
||||
|
||||
Known to work on DEC DE500 using 21143-PC chipset.
|
||||
Even on cards with the same chipset there can be
|
||||
incompatablity problems with the way media selection
|
||||
and status LED settings are done. See comments below.
|
||||
|
||||
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
|
||||
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 */
|
||||
/*********************************************************************/
|
||||
|
||||
/*
|
||||
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 "tulip.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 short ioaddr;
|
||||
static unsigned int *membase;
|
||||
static unsigned long devfn;
|
||||
static unsigned char srom[1024];
|
||||
static unsigned long ioaddr;
|
||||
|
||||
#define BUFLEN 1600 /* must be longword divisable */
|
||||
/* buffers must be longword aligned */
|
||||
/* Note: transmit and receive buffers must be longword aligned and
|
||||
longword divisable */
|
||||
|
||||
/* transmit descriptor and buffer */
|
||||
static struct txrxdesc txd;
|
||||
static unsigned char txb[BUFLEN];
|
||||
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 txrxdesc rxd[NRXD];
|
||||
static int rxd_tail = 0;
|
||||
static unsigned char rxb[NRXD][BUFLEN];
|
||||
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;
|
||||
|
||||
static unsigned char ehdr[ETHER_HDR_SIZE]; /* buffer for ethernet header */
|
||||
/* buffer for ethernet header */
|
||||
static unsigned char ehdr[ETHER_HDR_SIZE];
|
||||
|
||||
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 };
|
||||
|
||||
/*********************************************************************/
|
||||
/* 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 */
|
||||
/*********************************************************************/
|
||||
|
||||
/***************************************************************************/
|
||||
/* 21143 specific stuff */
|
||||
/***************************************************************************/
|
||||
|
||||
/* XXX assume 33MHz PCI bus, this is not very accurate and should be
|
||||
used only with gross over estimations of required delay times unless
|
||||
you tune UADJUST to your specific processor and I/O subsystem */
|
||||
|
||||
#define UADJUST 870
|
||||
static void udelay(unsigned long usec) {
|
||||
unsigned long i;
|
||||
for (i=((usec*UADJUST)/33)+1; i>0; i--) (void) TULIP_CSR_READ(csr_0);
|
||||
static inline void whereami (const char *str)
|
||||
{
|
||||
#ifdef TULIP_DEBUG_WHERE
|
||||
printf("%s\n", str);
|
||||
/* sleep(2); */
|
||||
#endif
|
||||
}
|
||||
|
||||
/* The following srom related code was taken from FreeBSD's if_de.c */
|
||||
/* with minor alterations to make it work here. the Linux code is */
|
||||
/* better but this was easier to use */
|
||||
|
||||
static void delay_300ns()
|
||||
#ifdef TULIP_DEBUG
|
||||
static void tulip_more()
|
||||
{
|
||||
int idx;
|
||||
for (idx = (300 / 33) + 1; idx > 0; idx--)
|
||||
(void) TULIP_CSR_READ(csr_busmode);
|
||||
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 */ ;
|
||||
}
|
||||
|
||||
#define EMIT do { TULIP_CSR_WRITE(csr_srom_mii, csr); delay_300ns(); } while (0)
|
||||
|
||||
static void srom_idle()
|
||||
|
||||
/*********************************************************************/
|
||||
/* Media Descriptor Code */
|
||||
/*********************************************************************/
|
||||
static int lc82c168_mdio_read(int phy_id, int location)
|
||||
{
|
||||
unsigned bit, csr;
|
||||
int i = 1000;
|
||||
int retval = 0;
|
||||
|
||||
csr = SROMSEL ; EMIT;
|
||||
csr = SROMSEL | SROMRD; EMIT;
|
||||
csr ^= SROMCS; EMIT;
|
||||
csr ^= SROMCLKON; EMIT;
|
||||
/*
|
||||
* Write 25 cycles of 0 which will force the SROM to be idle.
|
||||
*/
|
||||
for (bit = 3 + SROM_BITWIDTH + 16; bit > 0; bit--) {
|
||||
csr ^= SROMCLKOFF; EMIT; /* clock low; data not valid */
|
||||
csr ^= SROMCLKON; EMIT; /* clock high; data valid */
|
||||
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);
|
||||
}
|
||||
csr ^= SROMCLKOFF; EMIT;
|
||||
csr ^= SROMCS; EMIT;
|
||||
csr = 0; EMIT;
|
||||
}
|
||||
#ifdef TULIP_DEBUG
|
||||
printf("mii_cnt = %d\n", phy_idx);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
static void srom_read()
|
||||
|
||||
/*********************************************************************/
|
||||
/* 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)
|
||||
{
|
||||
unsigned idx;
|
||||
const unsigned bitwidth = SROM_BITWIDTH;
|
||||
const unsigned cmdmask = (SROMCMD_RD << bitwidth);
|
||||
const unsigned msb = 1 << (bitwidth + 3 - 1);
|
||||
unsigned lastidx = (1 << bitwidth) - 1;
|
||||
int i;
|
||||
unsigned short retval = 0;
|
||||
long ee_addr = ioaddr + CSR9;
|
||||
int read_cmd = location | EE_READ_CMD;
|
||||
|
||||
srom_idle();
|
||||
whereami("read_eeprom\n");
|
||||
|
||||
for (idx = 0; idx <= lastidx; idx++) {
|
||||
unsigned lastbit, data, bits, bit, csr;
|
||||
csr = SROMSEL ; EMIT;
|
||||
csr = SROMSEL | SROMRD; EMIT;
|
||||
csr ^= SROMCSON; EMIT;
|
||||
csr ^= SROMCLKON; EMIT;
|
||||
outl(EE_ENB & ~EE_CS, ee_addr);
|
||||
outl(EE_ENB, ee_addr);
|
||||
|
||||
lastbit = 0;
|
||||
for (bits = idx|cmdmask, bit = bitwidth + 3; bit > 0; bit--, bits <<= 1)
|
||||
{
|
||||
const unsigned thisbit = bits & msb;
|
||||
csr ^= SROMCLKOFF; EMIT; /* clock low; data not valid */
|
||||
if (thisbit != lastbit) {
|
||||
csr ^= SROMDOUT; EMIT; /* clock low; invert data */
|
||||
} else {
|
||||
EMIT;
|
||||
}
|
||||
csr ^= SROMCLKON; EMIT; /* clock high; data valid */
|
||||
lastbit = thisbit;
|
||||
}
|
||||
csr ^= SROMCLKOFF; EMIT;
|
||||
/* 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 (data = 0, bits = 0; bits < 16; bits++) {
|
||||
data <<= 1;
|
||||
csr ^= SROMCLKON; EMIT; /* clock high; data valid */
|
||||
data |= TULIP_CSR_READ(csr_srom_mii) & SROMDIN ? 1 : 0;
|
||||
csr ^= SROMCLKOFF; EMIT; /* clock low; data not valid */
|
||||
}
|
||||
srom[idx*2] = data & 0xFF;
|
||||
srom[idx*2+1] = data >> 8;
|
||||
csr = SROMSEL | SROMRD; EMIT;
|
||||
csr = 0; EMIT;
|
||||
}
|
||||
srom_idle();
|
||||
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;
|
||||
|
||||
/**************************************************************************
|
||||
ETH_RESET - Reset adapter
|
||||
***************************************************************************/
|
||||
/* 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)
|
||||
{
|
||||
int x,cnt=2;
|
||||
unsigned long csr6,csr15;
|
||||
unsigned long to, csr6;
|
||||
|
||||
outl(0x00000001, ioaddr + CSR0);
|
||||
udelay(1000);
|
||||
/* turn off reset and set cache align=16lword, burst=unlimit */
|
||||
outl(0x01A08000, ioaddr + CSR0);
|
||||
whereami("tulip_reset\n");
|
||||
|
||||
/* for some reason the media selection does not take
|
||||
the first time se it is repeated. */
|
||||
/* Stop Tx and RX */
|
||||
outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
|
||||
|
||||
while(cnt--) {
|
||||
/* stop TX,RX processes */
|
||||
if (cnt == 1)
|
||||
outl(0x32404000, ioaddr + CSR6);
|
||||
else
|
||||
outl(0x32000040, 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);
|
||||
|
||||
/* XXX - media selection is vendor specific and hard coded right
|
||||
here. This should be fixed to use the hints in the SROM and
|
||||
allow media selection by the user at runtime. MII support
|
||||
should also be added. Support for chips other than the
|
||||
21143 should be added here as well */
|
||||
} 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);
|
||||
|
||||
/* start set to 10Mbps half-duplex */
|
||||
} else if (vendor == PCI_VENDOR_ID_LINKSYS && dev_id == PCI_DEVICE_ID_DEC_TULIP) {
|
||||
|
||||
/* setup SIA */
|
||||
outl(0x0, ioaddr + CSR13); /* reset SIA */
|
||||
outl(0x7f3f, ioaddr + CSR14);
|
||||
outl(0x8000008, ioaddr + CSR15);
|
||||
outl(0x0, ioaddr + CSR13);
|
||||
outl(0x1, ioaddr + CSR13);
|
||||
outl(0x2404000, ioaddr + CSR6);
|
||||
lc82c168_do_mii();
|
||||
|
||||
/* initalize GP */
|
||||
outl(0x8af0008, ioaddr + CSR15);
|
||||
outl(0x50008, ioaddr + CSR15);
|
||||
} else if (vendor == PCI_VENDOR_ID_DEC && dev_id == PCI_DEVICE_ID_DEC_21142) {
|
||||
/* nothing */
|
||||
} else {
|
||||
/* If we don't know what to do with the card, set to 10Mbps half-duplex */
|
||||
|
||||
/* end 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);
|
||||
|
||||
if (vendor == PCI_VENDOR_ID_MACRONIX && dev_id == PCI_DEVICE_ID_MX987x5) {
|
||||
/* do stuff for MX98715 */
|
||||
outl(0x01a80000, ioaddr + CSR6);
|
||||
outl(0xFFFFFFFF, ioaddr + CSR14);
|
||||
outl(0x00001000, ioaddr + CSR12);
|
||||
}
|
||||
|
||||
outl(0x0, ioaddr + CSR7); /* disable interrupts */
|
||||
}
|
||||
|
||||
/* construct setup packet which is used by the 21143 to
|
||||
program its CAM to recognize interesting MAC addresses */
|
||||
/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
|
||||
outl(0x00000001, ioaddr + CSR0);
|
||||
|
||||
memset(&txd, 0, sizeof(struct txrxdesc));
|
||||
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 = 0x020; /* setup packet */
|
||||
txd.status = 0x80000000; /* give ownership to 21143 */
|
||||
tulip_wait(3*TICKS_PER_SEC);
|
||||
|
||||
/* construct perfect filter frame */
|
||||
/* with mac address as first match */
|
||||
/* and broadcast address for all others */
|
||||
/* turn off reset and set cache align=16lword, burst=unlimit */
|
||||
outl(0x01A08000, ioaddr + CSR0);
|
||||
|
||||
for(x=0;x<192;x++) txb[x] = 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];
|
||||
outl((unsigned long)&txd, ioaddr + CSR4); /* set xmit buf */
|
||||
outl(0x2406000, ioaddr + CSR6); /* start transmiter */
|
||||
/* set up transmit and receive descriptors */
|
||||
tulip_init_ring(nic);
|
||||
|
||||
udelay(50000); /* wait for the setup packet to be processed */
|
||||
/* Point to receive descriptor */
|
||||
outl((unsigned long)&rxd[0], ioaddr + CSR3);
|
||||
outl((unsigned long)&txd , ioaddr + CSR4);
|
||||
|
||||
}
|
||||
csr6 = 0x02404000;
|
||||
|
||||
/* setup receive descriptor */
|
||||
{
|
||||
int x;
|
||||
for(x=0;x<NRXD;x++) {
|
||||
memset(&rxd[x], 0, sizeof(struct txrxdesc));
|
||||
rxd[x].buf1addr = &rxb[x][0];
|
||||
rxd[x].buf2addr = 0; /* not used */
|
||||
rxd[x].buf1sz = BUFLEN;
|
||||
rxd[x].buf2sz = 0; /* not used */
|
||||
rxd[x].control = 0x0;
|
||||
rxd[x].status = 0x80000000; /* give ownership it to 21143 */
|
||||
}
|
||||
rxd[NRXD - 1].control = 0x008; /* Set Receive end of ring on la
|
||||
st descriptor */
|
||||
rxd_tail = 0;
|
||||
}
|
||||
/* Chip specific init code */
|
||||
|
||||
/* tell DC211XX where to find rx descriptor list */
|
||||
outl((unsigned long)&rxd[0], ioaddr + CSR3);
|
||||
/* start the receiver */
|
||||
outl(0x2406002, ioaddr + CSR6);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 const char padmap[] = {
|
||||
0, 3, 2, 1};
|
||||
|
||||
static void tulip_transmit(struct nic *nic, char *d, unsigned int t, unsigned int s, char *p)
|
||||
|
||||
/*********************************************************************/
|
||||
/* 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 int len;
|
||||
int pad;
|
||||
int status;
|
||||
unsigned char c;
|
||||
unsigned long time;
|
||||
unsigned long to;
|
||||
|
||||
/* setup ethernet header */
|
||||
whereami("tulip_transmit\n");
|
||||
|
||||
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;
|
||||
/* Stop Tx */
|
||||
outl(inl(ioaddr + CSR6) & ~0x00002000, ioaddr + CSR6);
|
||||
|
||||
/* setup the transmit descriptor */
|
||||
/* 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;
|
||||
|
||||
memset(&txd, 0, sizeof(struct txrxdesc));
|
||||
/* 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 */
|
||||
|
||||
txd.buf1addr = &ehdr[0]; /* ethernet header */
|
||||
txd.buf1sz = ETHER_HDR_SIZE;
|
||||
/* Point to transmit descriptor */
|
||||
outl((unsigned long)&txd, ioaddr + CSR4);
|
||||
|
||||
txd.buf2addr = p; /* packet to transmit */
|
||||
txd.buf2sz = s;
|
||||
/* Start Tx */
|
||||
outl(inl(ioaddr + CSR6) | 0x00002000, ioaddr + CSR6);
|
||||
|
||||
txd.control = 0x188; /* LS+FS+TER */
|
||||
/* immediate transmit demand */
|
||||
outl(0, ioaddr + CSR1);
|
||||
|
||||
txd.status = 0x80000000; /* give it to 21143 */
|
||||
to = currticks() + TX_TIME_OUT;
|
||||
while ((txd.status & 0x80000000) && (currticks() < to))
|
||||
/* wait */ ;
|
||||
|
||||
outl(inl(ioaddr + CSR6) & ~0x00004000, ioaddr + CSR6);
|
||||
outl((unsigned long)&txd, ioaddr + CSR4);
|
||||
outl(inl(ioaddr + CSR6) | 0x00004000, ioaddr + CSR6);
|
||||
|
||||
/* Wait for transmit to complete before returning. not well tested.
|
||||
|
||||
time = currticks();
|
||||
while(txd.status & 0x80000000) {
|
||||
if (currticks() - time > 20) {
|
||||
printf("transmit timeout.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
if (currticks() >= to) {
|
||||
printf ("TX Timeout!\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
ETH_POLL - Wait for a frame
|
||||
***************************************************************************/
|
||||
|
||||
/*********************************************************************/
|
||||
/* eth_poll - Wait for a frame */
|
||||
/*********************************************************************/
|
||||
static int tulip_poll(struct nic *nic)
|
||||
{
|
||||
/* common variables */
|
||||
unsigned short type = 0;
|
||||
int x;
|
||||
whereami("tulip_poll\n");
|
||||
|
||||
if (rxd[rxd_tail].status & 0x80000000) return 0;
|
||||
if (rxd[rxd_tail].status & 0x80000000)
|
||||
return 0;
|
||||
|
||||
nic->packetlen = (rxd[rxd_tail].status & 0x3FFF0000) >> 16;
|
||||
whereami("tulip_poll got one\n");
|
||||
|
||||
/* 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 */
|
||||
nic->packetlen = (rxd[rxd_tail].status & 0x3FFF0000) >> 16;
|
||||
|
||||
memcpy(nic->packet, &rxb[rxd_tail][0], nic->packetlen);
|
||||
/* 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 */
|
||||
|
||||
/* return the descriptor and buffer to recieve ring */
|
||||
rxd[rxd_tail].status = 0x80000000;
|
||||
rxd_tail++;
|
||||
if (rxd_tail == NRXD) rxd_tail = 0;
|
||||
memcpy(nic->packet, rxb + rxd_tail * BUFLEN, nic->packetlen);
|
||||
|
||||
return 1;
|
||||
/* 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)
|
||||
{
|
||||
/* nothing for the moment */
|
||||
whereami("tulip_disable\n");
|
||||
|
||||
/* The other Etherboot drivers don't seem to do anything here,
|
||||
so for now, we will not either */
|
||||
#if 0
|
||||
/* 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);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*********************************************************************/
|
||||
/* eth_probe - Look for an adapter */
|
||||
/*********************************************************************/
|
||||
struct nic *tulip_probe(struct nic *nic, unsigned short *io_addrs,
|
||||
struct pci_device *pci)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/**************************************************************************
|
||||
ETH_PROBE - Look for an adapter
|
||||
***************************************************************************/
|
||||
struct nic *tulip_probe(struct nic *nic, unsigned short *io_addrs, struct pci_device *pci)
|
||||
{
|
||||
int i;
|
||||
whereami("tulip_probe\n");
|
||||
|
||||
if (io_addrs == 0 || *io_addrs == 0)
|
||||
return (0);
|
||||
vendor = pci->vendor;
|
||||
dev_id = pci->dev_id;
|
||||
ioaddr = *io_addrs;
|
||||
membase = (unsigned int *)pci->membase;
|
||||
if (io_addrs == 0 || *io_addrs == 0)
|
||||
return 0;
|
||||
|
||||
/* wakeup chip */
|
||||
pcibios_write_config_dword(0,pci->devfn,0x40,0x00000000);
|
||||
vendor = pci->vendor;
|
||||
dev_id = pci->dev_id;
|
||||
ioaddr = *io_addrs;
|
||||
|
||||
/* Stop the chip's Tx and Rx processes. */
|
||||
/* outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6); */
|
||||
/* Clear the missed-packet counter. */
|
||||
/* (volatile int)inl(ioaddr + CSR8); */
|
||||
/* wakeup chip */
|
||||
pcibios_write_config_dword(0, pci->devfn, 0x40, 0x00000000);
|
||||
|
||||
srom_read();
|
||||
/* Stop the chip's Tx and Rx processes. */
|
||||
outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
|
||||
|
||||
for (i=0; i < 6; i++)
|
||||
nic->node_addr[i] = srom[20+i];
|
||||
/* Clear the missed-packet counter. */
|
||||
(volatile unsigned long)inl(ioaddr + CSR8);
|
||||
|
||||
printf("Tulip %b:%b:%b:%b:%b:%b at ioaddr 0x%x\n",
|
||||
srom[20],srom[21],srom[22],srom[23],srom[24],srom[25],
|
||||
ioaddr);
|
||||
/* Get MAC Address */
|
||||
|
||||
tulip_reset(nic);
|
||||
/* 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 {
|
||||
/* 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));
|
||||
|
||||
nic->reset = tulip_reset;
|
||||
nic->poll = tulip_poll;
|
||||
nic->transmit = tulip_transmit;
|
||||
nic->disable = tulip_disable;
|
||||
return nic;
|
||||
/* 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;
|
||||
}
|
||||
Reference in new issue
Block a user