mirror of
https://git.savannah.gnu.org/git/grub.git
synced 2026-10-23 23:59:22 +00:00
Update the netboot stuff to Etherboot-5.0.5. * configure.in (--enable-3c590): Removed. This was a mistake. (--enable-davicom): New option. (--enable-eepro): Likewise. (--enable-natsemi): Likewise. (--enable-ni5010): Likewise. (--enable-sis900): Likewise. (--enable-w89c840): Likewise. (--enable-3c509-hack): Removed. (--enable-ns8390-force-16bit): Likewise. * netboot/Makefile.am (libdrivers_a_SOURCES): Added timer.c and timer.h. (EXTRA_libdrivers_a_SOURCES): Added davicom.c, eepro.c, fa311.c, natsemi.c, ni5010.c, sis900.c, sis900.h, tlan.c and w89c840.c. (EXTRA_DIST): Added sis900.txt. (3c595_drivers): Remove 3c590.o from this. (davicom_drivers): New variable. (eepro_drivers): Likewise. (natsemi_drivers): Likewise. (ni5010_drivers): Likewise. (sis900_drivers): Likewise. (w89c840_drivers): Likewise. (3c590_o_CFLAGS): Removed. (davicom_o_CFLAGS): New variable. (eepro_o_CFLAGS): Likewise. (natsemi_o_CFLAGS): Likewise. (ni5010_o_CFLAGS): Likewise. (sis900_o_CFLAGS): Likewise. (w89c840_o_CFLAGS): Likewise. * netboot/davicom.c: New file, from Etherboot-5.0.5. * netboot/eepro.c: Likewise. * netboot/natsemi.c: Likewise. * netboot/ni5010.c: Likewise. * netboot/sis900.c: Likewise. * netboot/sis900.h: Likewise. * netboot/sis900.txt: Likewise. * netboot/timer.c: Likewise. * netboot/timer.h: Likewise. * netboot/w89c840.c: Likewise. * netboot/fa311.c: Likewise. * netboot/tlan.c: Likewise. * netboot/3c509.c: Copied from Etherboot-5.0.5. * netboot/3c509.h: Likewise. * netboot/3c595.c: Likewise. * netboot/3c90x.c: Likewise. * netboot/3c90x.txt: Likewise. * netboot/cards.h: Likewise. * netboot/cs89x0.c: Likewise. * netboot/depca.c: Likewise. * netboot/eepro100.c: Likewise. * netboot/epic100.c: Likewise. * netboot/i82586.c: Likewise. * netboot/lance.c: Likewise. * netboot/linux-asm-string.h: Likewise. * netboot/nic.h: Likewise. * netboot/ns8390.c: Likewise. * netboot/ns8390.h: Likewise. * netboot/otulip.c: Likewise. * netboot/pci.h: Likewise. * netboot/rtl8139.c: Likewise. * netboot/sk_g16.c: Likewise. * netboot/smc9000.c: Likewise. * netboot/tiara.c: Likewise. * netboot/tulip.c: Likewise. * netboot/via-rhine.c: Likewise. * netboot/config.c: Applied a diff between Etherboot-4.6.18 and Etherboot-5.0.5 manually. * netboot/main.c: Likewise. * netboot/pci.c: Likewise. * netboot/etherboot.h: Rewritten mostly from scratch, based on the same file in Etherboot-5.0.5. * netboot/misc.c: Likewise. * netboot/osdep.h: Likewise. * netboot/fsys_tftp.c (GRUB): Defined. (buf_fill): Use rfc2131_sleep_interval instead of rfc951_sleep. * stage2/builtins.c [SUPPORT_NETBOOT] (GRUB): Defined. (boot_func) [SUPPORT_NETBOOT]: Call cleanup_net. * stage2/cmdline.c [SUPPORT_DISKLESS] (GRUB): Defined. * stage2/common.c [SUPPORT_DISKLESS] (GRUB): Likewise.
1035 lines
27 KiB
C
1035 lines
27 KiB
C
/* -*- Mode:C; c-basic-offset:4; -*- */
|
|
|
|
/*
|
|
sis900.c: An SiS 900/7016 PCI Fast Ethernet driver for Etherboot
|
|
Copyright (C) 2001 Entity Cyber, Inc.
|
|
|
|
Revision: 1.0 March 1, 2001
|
|
|
|
Author: Marty Connor (mdc@thinguin.org)
|
|
|
|
Adapted from a Linux driver which was written by Donald Becker
|
|
and modified by Ollie Lho and Chin-Shan Li of SiS Corporation.
|
|
Rewritten for Etherboot by Marty Connor.
|
|
|
|
This software may be used and distributed according to the terms
|
|
of the GNU Public License (GPL), incorporated herein by reference.
|
|
|
|
References:
|
|
SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support,
|
|
preliminary Rev. 1.0 Jan. 14, 1998
|
|
SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support,
|
|
preliminary Rev. 1.0 Nov. 10, 1998
|
|
SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution,
|
|
preliminary Rev. 1.0 Jan. 18, 1998
|
|
http://www.sis.com.tw/support/databook.htm */
|
|
|
|
/* Revision History */
|
|
|
|
/*
|
|
01 March 2001 mdc 1.0
|
|
Initial Release. Tested with PCI based sis900 card and ThinkNIC
|
|
computer.
|
|
20 March 2001 P.Koegel
|
|
added support for sis630e and PHY ICS1893 and RTL8201
|
|
Testet with SIS730S chipset + ICS1893
|
|
*/
|
|
|
|
|
|
/* Includes */
|
|
|
|
#include "etherboot.h"
|
|
#include "nic.h"
|
|
#include "pci.h"
|
|
#include "cards.h"
|
|
|
|
#include "sis900.h"
|
|
|
|
/* Globals */
|
|
|
|
static int sis900_debug = 0;
|
|
|
|
static unsigned short vendor, dev_id;
|
|
static unsigned long ioaddr;
|
|
|
|
static unsigned int cur_phy;
|
|
|
|
static unsigned int cur_rx;
|
|
|
|
static BufferDesc txd;
|
|
static BufferDesc rxd[NUM_RX_DESC];
|
|
|
|
#ifdef USE_LOWMEM_BUFFER
|
|
#define txb ((char *)0x10000 - TX_BUF_SIZE)
|
|
#define rxb ((char *)0x10000 - NUM_RX_DESC*RX_BUF_SIZE - TX_BUF_SIZE)
|
|
#else
|
|
static unsigned char txb[TX_BUF_SIZE];
|
|
static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE];
|
|
#endif
|
|
|
|
static struct mac_chip_info {
|
|
const char *name;
|
|
u16 vendor_id, device_id, flags;
|
|
int io_size;
|
|
} mac_chip_table[] = {
|
|
{ "SiS 900 PCI Fast Ethernet", PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS900,
|
|
PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE},
|
|
{ "SiS 7016 PCI Fast Ethernet",PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS7016,
|
|
PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE},
|
|
{0,0,0,0,0} /* 0 terminated list. */
|
|
};
|
|
|
|
static void sis900_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex);
|
|
static void amd79c901_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex);
|
|
static void ics1893_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex);
|
|
static void rtl8201_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex);
|
|
|
|
static struct mii_chip_info {
|
|
const char * name;
|
|
u16 phy_id0;
|
|
u16 phy_id1;
|
|
void (*read_mode) (struct nic *nic, int phy_addr, int *speed, int *duplex);
|
|
} mii_chip_table[] = {
|
|
{"SiS 900 Internal MII PHY", 0x001d, 0x8000, sis900_read_mode},
|
|
{"SiS 7014 Physical Layer Solution", 0x0016, 0xf830,sis900_read_mode},
|
|
{"AMD 79C901 10BASE-T PHY", 0x0000, 0x35b9, amd79c901_read_mode},
|
|
{"AMD 79C901 HomePNA PHY", 0x0000, 0x35c8, amd79c901_read_mode},
|
|
{"ICS 1893 Integrated PHYceiver" , 0x0015, 0xf441,ics1893_read_mode},
|
|
{"RTL 8201 10/100Mbps Phyceiver" , 0x0000, 0x8201,rtl8201_read_mode},
|
|
{0,0,0,0}
|
|
};
|
|
|
|
static struct mii_phy {
|
|
struct mii_phy * next;
|
|
struct mii_chip_info * chip_info;
|
|
int phy_addr;
|
|
u16 status;
|
|
} mii;
|
|
|
|
|
|
// PCI to ISA bridge for SIS640E access
|
|
static struct pci_device pci_isa_bridge_list[] = {
|
|
{ 0x1039, 0x0008,
|
|
"SIS 85C503/5513 PCI to ISA bridge", 0, 0, 0, 0},
|
|
{0, 0, NULL, 0, 0, 0, 0}
|
|
};
|
|
|
|
/* Function Prototypes */
|
|
|
|
struct nic *sis900_probe(struct nic *nic, unsigned short *io_addrs, struct pci_device *pci);
|
|
|
|
static u16 sis900_read_eeprom(int location);
|
|
static void sis900_mdio_reset(long mdio_addr);
|
|
static void sis900_mdio_idle(long mdio_addr);
|
|
static u16 sis900_mdio_read(int phy_id, int location);
|
|
static void sis900_mdio_write(int phy_id, int location, int val);
|
|
|
|
static void sis900_init(struct nic *nic);
|
|
|
|
static void sis900_reset(struct nic *nic);
|
|
|
|
static void sis900_init_rxfilter(struct nic *nic);
|
|
static void sis900_init_txd(struct nic *nic);
|
|
static void sis900_init_rxd(struct nic *nic);
|
|
static void sis900_set_rx_mode(struct nic *nic);
|
|
static void sis900_check_mode(struct nic *nic);
|
|
|
|
static void sis900_transmit(struct nic *nic, const char *d,
|
|
unsigned int t, unsigned int s, const char *p);
|
|
static int sis900_poll(struct nic *nic);
|
|
|
|
static void sis900_disable(struct nic *nic);
|
|
|
|
/**
|
|
* sis900_get_mac_addr: - Get MAC address for stand alone SiS900 model
|
|
* @pci_dev: the sis900 pci device
|
|
* @net_dev: the net device to get address for
|
|
*
|
|
* Older SiS900 and friends, use EEPROM to store MAC address.
|
|
* MAC address is read from read_eeprom() into @net_dev->dev_addr.
|
|
*/
|
|
|
|
static int sis900_get_mac_addr(struct pci_device * pci_dev , struct nic *nic)
|
|
{
|
|
u16 signature;
|
|
int i;
|
|
|
|
/* check to see if we have sane EEPROM */
|
|
signature = (u16) sis900_read_eeprom( EEPROMSignature);
|
|
if (signature == 0xffff || signature == 0x0000) {
|
|
printf ("sis900_probe: Error EERPOM read %hX\n", signature);
|
|
return 0;
|
|
}
|
|
|
|
/* get MAC address from EEPROM */
|
|
for (i = 0; i < 3; i++)
|
|
((u16 *)(nic->node_addr))[i] = sis900_read_eeprom(i+EEPROMMACAddr);
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* sis630e_get_mac_addr: - Get MAC address for SiS630E model
|
|
* @pci_dev: the sis900 pci device
|
|
* @net_dev: the net device to get address for
|
|
*
|
|
* SiS630E model, use APC CMOS RAM to store MAC address.
|
|
* APC CMOS RAM is accessed through ISA bridge.
|
|
* MAC address is read into @net_dev->dev_addr.
|
|
*/
|
|
|
|
static int sis630e_get_mac_addr(struct pci_device * pci_dev, struct nic *nic)
|
|
{
|
|
u8 reg;
|
|
int i;
|
|
struct pci_device *p;
|
|
|
|
// find PCI to ISA bridge
|
|
eth_pci_init(pci_isa_bridge_list);
|
|
|
|
/* the firts entry in this list should contain bus/devfn */
|
|
p = pci_isa_bridge_list;
|
|
|
|
pcibios_read_config_byte(p->bus,p->devfn, 0x48, ®);
|
|
pcibios_write_config_byte(p->bus,p->devfn, 0x48, reg | 0x40);
|
|
|
|
for (i = 0; i < ETH_ALEN; i++)
|
|
{
|
|
outb(0x09 + i, 0x70);
|
|
((u8 *)(nic->node_addr))[i] = inb(0x71);
|
|
}
|
|
pcibios_write_config_byte(p->bus,p->devfn, 0x48, reg & ~0x40);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Function: sis900_probe
|
|
*
|
|
* Description: initializes initializes the NIC, retrieves the
|
|
* MAC address of the card, and sets up some globals required by
|
|
* other routines.
|
|
*
|
|
* Side effects:
|
|
* leaves the ioaddress of the sis900 chip in the variable ioaddr.
|
|
* leaves the sis900 initialized, and ready to recieve packets.
|
|
*
|
|
* Returns: struct nic *: pointer to NIC data structure
|
|
*/
|
|
|
|
struct nic *sis900_probe(struct nic *nic, unsigned short *io_addrs, struct pci_device *pci)
|
|
{
|
|
int i;
|
|
int found=0;
|
|
int phy_addr;
|
|
u16 signature;
|
|
u8 revision;
|
|
int ret;
|
|
|
|
if (io_addrs == 0 || *io_addrs == 0)
|
|
return NULL;
|
|
|
|
ioaddr = *io_addrs & ~3;
|
|
vendor = pci->vendor;
|
|
dev_id = pci->dev_id;
|
|
|
|
/* wakeup chip */
|
|
pcibios_write_config_dword(pci->bus, pci->devfn, 0x40, 0x00000000);
|
|
|
|
adjust_pci_device(pci);
|
|
|
|
/* get MAC address */
|
|
ret = 0;
|
|
pcibios_read_config_byte(pci->bus,pci->devfn, PCI_REVISION, &revision);
|
|
if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV)
|
|
ret = sis630e_get_mac_addr(pci, nic);
|
|
else if (revision == SIS630S_900_REV)
|
|
ret = sis630e_get_mac_addr(pci, nic);
|
|
else
|
|
ret = sis900_get_mac_addr(pci, nic);
|
|
|
|
if (ret == 0)
|
|
{
|
|
printf ("sis900_probe: Error MAC address not found\n");
|
|
return NULL;
|
|
}
|
|
|
|
printf("\nsis900_probe: MAC addr %! at ioaddr %#hX\n",
|
|
nic->node_addr, ioaddr);
|
|
printf("sis900_probe: Vendor:%#hX Device:%#hX\n", vendor, dev_id);
|
|
|
|
/* probe for mii transceiver */
|
|
/* search for total of 32 possible mii phy addresses */
|
|
|
|
found = 0;
|
|
for (phy_addr = 0; phy_addr < 32; phy_addr++) {
|
|
u16 mii_status;
|
|
u16 phy_id0, phy_id1;
|
|
|
|
mii_status = sis900_mdio_read(phy_addr, MII_STATUS);
|
|
if (mii_status == 0xffff || mii_status == 0x0000)
|
|
/* the mii is not accessable, try next one */
|
|
continue;
|
|
|
|
phy_id0 = sis900_mdio_read(phy_addr, MII_PHY_ID0);
|
|
phy_id1 = sis900_mdio_read(phy_addr, MII_PHY_ID1);
|
|
|
|
/* search our mii table for the current mii */
|
|
for (i = 0; mii_chip_table[i].phy_id1; i++) {
|
|
|
|
if (phy_id0 == mii_chip_table[i].phy_id0) {
|
|
|
|
printf("sis900_probe: %s transceiver found at address %d.\n",
|
|
mii_chip_table[i].name, phy_addr);
|
|
|
|
mii.chip_info = &mii_chip_table[i];
|
|
mii.phy_addr = phy_addr;
|
|
mii.status = sis900_mdio_read(phy_addr, MII_STATUS);
|
|
mii.next = NULL;
|
|
|
|
found=1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (found == 0) {
|
|
printf("sis900_probe: No MII transceivers found!\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Arbitrarily select the last PHY found as current PHY */
|
|
cur_phy = mii.phy_addr;
|
|
printf("sis900_probe: Using %s as default\n", mii.chip_info->name);
|
|
|
|
/* initialize device */
|
|
sis900_init(nic);
|
|
|
|
nic->reset = sis900_init;
|
|
nic->poll = sis900_poll;
|
|
nic->transmit = sis900_transmit;
|
|
nic->disable = sis900_disable;
|
|
|
|
return nic;
|
|
}
|
|
|
|
|
|
/*
|
|
* EEPROM Routines: These functions read and write to EEPROM for
|
|
* retrieving the MAC address and other configuration information about
|
|
* the card.
|
|
*/
|
|
|
|
/* Delay between EEPROM clock transitions. */
|
|
#define eeprom_delay() inl(ee_addr)
|
|
|
|
|
|
/* Function: sis900_read_eeprom
|
|
*
|
|
* Description: reads and returns a given location from EEPROM
|
|
*
|
|
* Arguments: int location: requested EEPROM location
|
|
*
|
|
* Returns: u16: contents of requested EEPROM location
|
|
*
|
|
*/
|
|
|
|
/* Read Serial EEPROM through EEPROM Access Register, Note that location is
|
|
in word (16 bits) unit */
|
|
static u16 sis900_read_eeprom(int location)
|
|
{
|
|
int i;
|
|
u16 retval = 0;
|
|
long ee_addr = ioaddr + mear;
|
|
u32 read_cmd = location | EEread;
|
|
|
|
outl(0, ee_addr);
|
|
eeprom_delay();
|
|
outl(EECLK, ee_addr);
|
|
eeprom_delay();
|
|
|
|
/* Shift the read command (9) bits out. */
|
|
for (i = 8; i >= 0; i--) {
|
|
u32 dataval = (read_cmd & (1 << i)) ? EEDI | EECS : EECS;
|
|
outl(dataval, ee_addr);
|
|
eeprom_delay();
|
|
outl(dataval | EECLK, ee_addr);
|
|
eeprom_delay();
|
|
}
|
|
outb(EECS, ee_addr);
|
|
eeprom_delay();
|
|
|
|
/* read the 16-bits data in */
|
|
for (i = 16; i > 0; i--) {
|
|
outl(EECS, ee_addr);
|
|
eeprom_delay();
|
|
outl(EECS | EECLK, ee_addr);
|
|
eeprom_delay();
|
|
retval = (retval << 1) | ((inl(ee_addr) & EEDO) ? 1 : 0);
|
|
eeprom_delay();
|
|
}
|
|
|
|
/* Terminate the EEPROM access. */
|
|
outl(0, ee_addr);
|
|
eeprom_delay();
|
|
outl(EECLK, ee_addr);
|
|
|
|
return (retval);
|
|
}
|
|
|
|
#define sis900_mdio_delay() inl(mdio_addr)
|
|
|
|
|
|
/*
|
|
Read and write the MII management registers using software-generated
|
|
serial MDIO protocol. Note that the command bits and data bits are
|
|
send out seperately
|
|
*/
|
|
|
|
static void sis900_mdio_idle(long mdio_addr)
|
|
{
|
|
outl(MDIO | MDDIR, mdio_addr);
|
|
sis900_mdio_delay();
|
|
outl(MDIO | MDDIR | MDC, mdio_addr);
|
|
}
|
|
|
|
/* Syncronize the MII management interface by shifting 32 one bits out. */
|
|
static void sis900_mdio_reset(long mdio_addr)
|
|
{
|
|
int i;
|
|
|
|
for (i = 31; i >= 0; i--) {
|
|
outl(MDDIR | MDIO, mdio_addr);
|
|
sis900_mdio_delay();
|
|
outl(MDDIR | MDIO | MDC, mdio_addr);
|
|
sis900_mdio_delay();
|
|
}
|
|
return;
|
|
}
|
|
|
|
static u16 sis900_mdio_read(int phy_id, int location)
|
|
{
|
|
long mdio_addr = ioaddr + mear;
|
|
int mii_cmd = MIIread|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
|
|
u16 retval = 0;
|
|
int i;
|
|
|
|
sis900_mdio_reset(mdio_addr);
|
|
sis900_mdio_idle(mdio_addr);
|
|
|
|
for (i = 15; i >= 0; i--) {
|
|
int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR;
|
|
outl(dataval, mdio_addr);
|
|
sis900_mdio_delay();
|
|
outl(dataval | MDC, mdio_addr);
|
|
sis900_mdio_delay();
|
|
}
|
|
|
|
/* Read the 16 data bits. */
|
|
for (i = 16; i > 0; i--) {
|
|
outl(0, mdio_addr);
|
|
sis900_mdio_delay();
|
|
retval = (retval << 1) | ((inl(mdio_addr) & MDIO) ? 1 : 0);
|
|
outl(MDC, mdio_addr);
|
|
sis900_mdio_delay();
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
static void sis900_mdio_write(int phy_id, int location, int value)
|
|
{
|
|
long mdio_addr = ioaddr + mear;
|
|
int mii_cmd = MIIwrite|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
|
|
int i;
|
|
|
|
sis900_mdio_reset(mdio_addr);
|
|
sis900_mdio_idle(mdio_addr);
|
|
|
|
/* Shift the command bits out. */
|
|
for (i = 15; i >= 0; i--) {
|
|
int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR;
|
|
outb(dataval, mdio_addr);
|
|
sis900_mdio_delay();
|
|
outb(dataval | MDC, mdio_addr);
|
|
sis900_mdio_delay();
|
|
}
|
|
sis900_mdio_delay();
|
|
|
|
/* Shift the value bits out. */
|
|
for (i = 15; i >= 0; i--) {
|
|
int dataval = (value & (1 << i)) ? MDDIR | MDIO : MDDIR;
|
|
outl(dataval, mdio_addr);
|
|
sis900_mdio_delay();
|
|
outl(dataval | MDC, mdio_addr);
|
|
sis900_mdio_delay();
|
|
}
|
|
sis900_mdio_delay();
|
|
|
|
/* Clear out extra bits. */
|
|
for (i = 2; i > 0; i--) {
|
|
outb(0, mdio_addr);
|
|
sis900_mdio_delay();
|
|
outb(MDC, mdio_addr);
|
|
sis900_mdio_delay();
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
/* Function: sis900_init
|
|
*
|
|
* Description: resets the ethernet controller chip and various
|
|
* data structures required for sending and receiving packets.
|
|
*
|
|
* Arguments: struct nic *nic: NIC data structure
|
|
*
|
|
* returns: void.
|
|
*/
|
|
|
|
static void
|
|
sis900_init(struct nic *nic)
|
|
{
|
|
/* Soft reset the chip. */
|
|
sis900_reset(nic);
|
|
|
|
sis900_init_rxfilter(nic);
|
|
|
|
sis900_init_txd(nic);
|
|
sis900_init_rxd(nic);
|
|
|
|
sis900_set_rx_mode(nic);
|
|
|
|
sis900_check_mode(nic);
|
|
|
|
outl(RxENA, ioaddr + cr);
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: sis900_reset
|
|
*
|
|
* Description: disables interrupts and soft resets the controller chip
|
|
*
|
|
* Arguments: struct nic *nic: NIC data structure
|
|
*
|
|
* Returns: void.
|
|
*/
|
|
|
|
static void
|
|
sis900_reset(struct nic *nic)
|
|
{
|
|
int i = 0;
|
|
u32 status = TxRCMP | RxRCMP;
|
|
|
|
outl(0, ioaddr + ier);
|
|
outl(0, ioaddr + imr);
|
|
outl(0, ioaddr + rfcr);
|
|
|
|
outl(RxRESET | TxRESET | RESET, ioaddr + cr);
|
|
|
|
/* Check that the chip has finished the reset. */
|
|
while (status && (i++ < 1000)) {
|
|
status ^= (inl(isr + ioaddr) & status);
|
|
}
|
|
outl(PESEL, ioaddr + cfg);
|
|
}
|
|
|
|
|
|
/* Function: sis_init_rxfilter
|
|
*
|
|
* Description: sets receive filter address to our MAC address
|
|
*
|
|
* Arguments: struct nic *nic: NIC data structure
|
|
*
|
|
* returns: void.
|
|
*/
|
|
|
|
static void
|
|
sis900_init_rxfilter(struct nic *nic)
|
|
{
|
|
u32 rfcrSave;
|
|
int i;
|
|
|
|
rfcrSave = inl(rfcr + ioaddr);
|
|
|
|
/* disable packet filtering before setting filter */
|
|
outl(rfcrSave & ~RFEN, rfcr);
|
|
|
|
/* load MAC addr to filter data register */
|
|
for (i = 0 ; i < 3 ; i++) {
|
|
u32 w;
|
|
|
|
w = (u32) *((u16 *)(nic->node_addr)+i);
|
|
outl((i << RFADDR_shift), ioaddr + rfcr);
|
|
outl(w, ioaddr + rfdr);
|
|
|
|
if (sis900_debug > 0)
|
|
printf("sis900_init_rxfilter: Receive Filter Addrss[%d]=%X\n",
|
|
i, inl(ioaddr + rfdr));
|
|
}
|
|
|
|
/* enable packet filitering */
|
|
outl(rfcrSave | RFEN, rfcr + ioaddr);
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: sis_init_txd
|
|
*
|
|
* Description: initializes the Tx descriptor
|
|
*
|
|
* Arguments: struct nic *nic: NIC data structure
|
|
*
|
|
* returns: void.
|
|
*/
|
|
|
|
static void
|
|
sis900_init_txd(struct nic *nic)
|
|
{
|
|
txd.link = (u32) 0;
|
|
txd.cmdsts = (u32) 0;
|
|
txd.bufptr = (u32) &txb[0];
|
|
|
|
/* load Transmit Descriptor Register */
|
|
outl((u32) &txd, ioaddr + txdp);
|
|
if (sis900_debug > 0)
|
|
printf("sis900_init_txd: TX descriptor register loaded with: %X\n",
|
|
inl(ioaddr + txdp));
|
|
}
|
|
|
|
|
|
/* Function: sis_init_rxd
|
|
*
|
|
* Description: initializes the Rx descriptor ring
|
|
*
|
|
* Arguments: struct nic *nic: NIC data structure
|
|
*
|
|
* Returns: void.
|
|
*/
|
|
|
|
static void
|
|
sis900_init_rxd(struct nic *nic)
|
|
{
|
|
int i;
|
|
|
|
cur_rx = 0;
|
|
|
|
/* init RX descriptor */
|
|
for (i = 0; i < NUM_RX_DESC; i++) {
|
|
rxd[i].link = (i+1 < NUM_RX_DESC) ? (u32) &rxd[i+1] : (u32) &rxd[0];
|
|
rxd[i].cmdsts = (u32) RX_BUF_SIZE;
|
|
rxd[i].bufptr = (u32) &rxb[i*RX_BUF_SIZE];
|
|
if (sis900_debug > 0)
|
|
printf("sis900_init_rxd: rxd[%d]=%X link=%X cmdsts=%X bufptr=%X\n",
|
|
i, &rxd[i], rxd[i].link, rxd[i].cmdsts, rxd[i].bufptr);
|
|
}
|
|
|
|
/* load Receive Descriptor Register */
|
|
outl((u32) &rxd[0], ioaddr + rxdp);
|
|
|
|
if (sis900_debug > 0)
|
|
printf("sis900_init_rxd: RX descriptor register loaded with: %X\n",
|
|
inl(ioaddr + rxdp));
|
|
|
|
}
|
|
|
|
|
|
/* Function: sis_init_rxd
|
|
*
|
|
* Description:
|
|
* sets the receive mode to accept all broadcast packets and packets
|
|
* with our MAC address, and reject all multicast packets.
|
|
*
|
|
* Arguments: struct nic *nic: NIC data structure
|
|
*
|
|
* Returns: void.
|
|
*/
|
|
|
|
static void sis900_set_rx_mode(struct nic *nic)
|
|
{
|
|
int i;
|
|
|
|
/* Configure Multicast Hash Table in Receive Filter
|
|
to reject all MCAST packets */
|
|
for (i = 0; i < 8; i++) {
|
|
/* why plus 0x04? That makes the correct value for hash table. */
|
|
outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr);
|
|
outl((u32)(0x0), ioaddr + rfdr);
|
|
}
|
|
/* Accept Broadcast packets, destination addresses that match
|
|
our MAC address */
|
|
outl(RFEN | RFAAB, ioaddr + rfcr);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* Function: sis900_check_mode
|
|
*
|
|
* Description: checks the state of transmit and receive
|
|
* parameters on the NIC, and updates NIC registers to match
|
|
*
|
|
* Arguments: struct nic *nic: NIC data structure
|
|
*
|
|
* Returns: void.
|
|
*/
|
|
|
|
static void
|
|
sis900_check_mode (struct nic *nic)
|
|
{
|
|
int speed, duplex;
|
|
u32 tx_flags = 0, rx_flags = 0;
|
|
|
|
mii.chip_info->read_mode(nic, cur_phy, &speed, &duplex);
|
|
|
|
tx_flags = TxATP | (TX_DMA_BURST << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift);
|
|
rx_flags = RX_DMA_BURST << RxMXDMA_shift;
|
|
|
|
if (speed == HW_SPEED_HOME || speed == HW_SPEED_10_MBPS) {
|
|
rx_flags |= (RxDRNT_10 << RxDRNT_shift);
|
|
tx_flags |= (TxDRNT_10 << TxDRNT_shift);
|
|
}
|
|
else {
|
|
rx_flags |= (RxDRNT_100 << RxDRNT_shift);
|
|
tx_flags |= (TxDRNT_100 << TxDRNT_shift);
|
|
}
|
|
|
|
if (duplex == FDX_CAPABLE_FULL_SELECTED) {
|
|
tx_flags |= (TxCSI | TxHBI);
|
|
rx_flags |= RxATX;
|
|
}
|
|
|
|
outl (tx_flags, ioaddr + txcfg);
|
|
outl (rx_flags, ioaddr + rxcfg);
|
|
}
|
|
|
|
|
|
/* Function: sis900_read_mode
|
|
*
|
|
* Description: retrieves and displays speed and duplex
|
|
* parameters from the NIC
|
|
*
|
|
* Arguments: struct nic *nic: NIC data structure
|
|
*
|
|
* Returns: void.
|
|
*/
|
|
|
|
static void
|
|
sis900_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex)
|
|
{
|
|
int i = 0;
|
|
u32 status;
|
|
|
|
/* STSOUT register is Latched on Transition, read operation updates it */
|
|
while (i++ < 2)
|
|
status = sis900_mdio_read(phy_addr, MII_STSOUT);
|
|
|
|
if (status & MII_STSOUT_SPD)
|
|
*speed = HW_SPEED_100_MBPS;
|
|
else
|
|
*speed = HW_SPEED_10_MBPS;
|
|
|
|
if (status & MII_STSOUT_DPLX)
|
|
*duplex = FDX_CAPABLE_FULL_SELECTED;
|
|
else
|
|
*duplex = FDX_CAPABLE_HALF_SELECTED;
|
|
|
|
if (status & MII_STSOUT_LINK_FAIL)
|
|
printf("sis900_read_mode: Media Link Off\n");
|
|
else
|
|
printf("sis900_read_mode: Media Link On %s %s-duplex \n",
|
|
*speed == HW_SPEED_100_MBPS ?
|
|
"100mbps" : "10mbps",
|
|
*duplex == FDX_CAPABLE_FULL_SELECTED ?
|
|
"full" : "half");
|
|
}
|
|
|
|
|
|
/* Function: amd79c901_read_mode
|
|
*
|
|
* Description: retrieves and displays speed and duplex
|
|
* parameters from the NIC
|
|
*
|
|
* Arguments: struct nic *nic: NIC data structure
|
|
*
|
|
* Returns: void.
|
|
*/
|
|
|
|
static void
|
|
amd79c901_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex)
|
|
{
|
|
int i;
|
|
u16 status;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
status = sis900_mdio_read(phy_addr, MII_STATUS);
|
|
|
|
if (status & MII_STAT_CAN_AUTO) {
|
|
/* 10BASE-T PHY */
|
|
for (i = 0; i < 2; i++)
|
|
status = sis900_mdio_read(phy_addr, MII_STATUS_SUMMARY);
|
|
if (status & MII_STSSUM_SPD)
|
|
*speed = HW_SPEED_100_MBPS;
|
|
else
|
|
*speed = HW_SPEED_10_MBPS;
|
|
if (status & MII_STSSUM_DPLX)
|
|
*duplex = FDX_CAPABLE_FULL_SELECTED;
|
|
else
|
|
*duplex = FDX_CAPABLE_HALF_SELECTED;
|
|
|
|
if (status & MII_STSSUM_LINK)
|
|
printf("amd79c901_read_mode: Media Link On %s %s-duplex \n",
|
|
*speed == HW_SPEED_100_MBPS ?
|
|
"100mbps" : "10mbps",
|
|
*duplex == FDX_CAPABLE_FULL_SELECTED ?
|
|
"full" : "half");
|
|
else
|
|
printf("amd79c901_read_mode: Media Link Off\n");
|
|
}
|
|
else {
|
|
/* HomePNA */
|
|
*speed = HW_SPEED_HOME;
|
|
*duplex = FDX_CAPABLE_HALF_SELECTED;
|
|
if (status & MII_STAT_LINK)
|
|
printf("amd79c901_read_mode:Media Link On 1mbps half-duplex \n");
|
|
else
|
|
printf("amd79c901_read_mode: Media Link Off\n");
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* ics1893_read_mode: - read media mode for ICS1893 PHY
|
|
* @net_dev: the net device to read mode for
|
|
* @phy_addr: mii phy address
|
|
* @speed: the transmit speed to be determined
|
|
* @duplex: the duplex mode to be determined
|
|
*
|
|
* ICS1893 PHY use Quick Poll Detailed Status register
|
|
* to determine the speed and duplex mode for sis900
|
|
*/
|
|
|
|
static void ics1893_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex)
|
|
{
|
|
int i = 0;
|
|
u32 status;
|
|
|
|
/* MII_QPDSTS is Latched, read twice in succession will reflect the current state */
|
|
for (i = 0; i < 2; i++)
|
|
status = sis900_mdio_read(phy_addr, MII_QPDSTS);
|
|
|
|
if (status & MII_STSICS_SPD)
|
|
*speed = HW_SPEED_100_MBPS;
|
|
else
|
|
*speed = HW_SPEED_10_MBPS;
|
|
|
|
if (status & MII_STSICS_DPLX)
|
|
*duplex = FDX_CAPABLE_FULL_SELECTED;
|
|
else
|
|
*duplex = FDX_CAPABLE_HALF_SELECTED;
|
|
|
|
if (status & MII_STSICS_LINKSTS)
|
|
printf("ics1893_read_mode: Media Link On %s %s-duplex \n",
|
|
*speed == HW_SPEED_100_MBPS ?
|
|
"100mbps" : "10mbps",
|
|
*duplex == FDX_CAPABLE_FULL_SELECTED ?
|
|
"full" : "half");
|
|
else
|
|
printf("ics1893_read_mode: Media Link Off\n");
|
|
}
|
|
|
|
/**
|
|
* rtl8201_read_mode: - read media mode for rtl8201 phy
|
|
* @nic: the net device to read mode for
|
|
* @phy_addr: mii phy address
|
|
* @speed: the transmit speed to be determined
|
|
* @duplex: the duplex mode to be determined
|
|
*
|
|
* read MII_STATUS register from rtl8201 phy
|
|
* to determine the speed and duplex mode for sis900
|
|
*/
|
|
|
|
static void rtl8201_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex)
|
|
{
|
|
u32 status;
|
|
|
|
status = sis900_mdio_read(phy_addr, MII_STATUS);
|
|
|
|
if (status & MII_STAT_CAN_TX_FDX) {
|
|
*speed = HW_SPEED_100_MBPS;
|
|
*duplex = FDX_CAPABLE_FULL_SELECTED;
|
|
}
|
|
else if (status & MII_STAT_CAN_TX) {
|
|
*speed = HW_SPEED_100_MBPS;
|
|
*duplex = FDX_CAPABLE_HALF_SELECTED;
|
|
}
|
|
else if (status & MII_STAT_CAN_T_FDX) {
|
|
*speed = HW_SPEED_10_MBPS;
|
|
*duplex = FDX_CAPABLE_FULL_SELECTED;
|
|
}
|
|
else if (status & MII_STAT_CAN_T) {
|
|
*speed = HW_SPEED_10_MBPS;
|
|
*duplex = FDX_CAPABLE_HALF_SELECTED;
|
|
}
|
|
|
|
if (status & MII_STAT_LINK)
|
|
printf("rtl8201_read_mode: Media Link On %s %s-duplex \n",
|
|
*speed == HW_SPEED_100_MBPS ?
|
|
"100mbps" : "10mbps",
|
|
*duplex == FDX_CAPABLE_FULL_SELECTED ?
|
|
"full" : "half");
|
|
else
|
|
printf("rtl9201_read_config_mode: Media Link Off\n");
|
|
}
|
|
|
|
/* Function: sis900_transmit
|
|
*
|
|
* Description: transmits a packet and waits for completion or timeout.
|
|
*
|
|
* Arguments: char d[6]: destination ethernet address.
|
|
* unsigned short t: ethernet protocol type.
|
|
* unsigned short s: size of the data-part of the packet.
|
|
* char *p: the data for the packet.
|
|
*
|
|
* Returns: void.
|
|
*/
|
|
|
|
static void
|
|
sis900_transmit(struct nic *nic,
|
|
const char *d, /* Destination */
|
|
unsigned int t, /* Type */
|
|
unsigned int s, /* size */
|
|
const char *p) /* Packet */
|
|
{
|
|
u32 status, to, nstype;
|
|
u32 tx_status;
|
|
|
|
/* Stop the transmitter */
|
|
outl(TxDIS, ioaddr + cr);
|
|
|
|
/* load Transmit Descriptor Register */
|
|
outl((u32) &txd, ioaddr + txdp);
|
|
if (sis900_debug > 1)
|
|
printf("sis900_transmit: TX descriptor register loaded with: %X\n",
|
|
inl(ioaddr + txdp));
|
|
|
|
memcpy(txb, d, ETH_ALEN);
|
|
memcpy(txb + ETH_ALEN, nic->node_addr, ETH_ALEN);
|
|
nstype = htons(t);
|
|
memcpy(txb + 2 * ETH_ALEN, (char*)&nstype, 2);
|
|
memcpy(txb + ETH_HLEN, p, s);
|
|
|
|
s += ETH_HLEN;
|
|
s &= DSIZE;
|
|
|
|
if (sis900_debug > 1)
|
|
printf("sis900_transmit: sending %d bytes ethtype %hX\n", (int) s, t);
|
|
|
|
/* pad to minimum packet size */
|
|
while (s < ETH_ZLEN)
|
|
txb[s++] = '\0';
|
|
|
|
/* set the transmit buffer descriptor and enable Transmit State Machine */
|
|
txd.bufptr = (u32) &txb[0];
|
|
txd.cmdsts = (u32) OWN | s;
|
|
|
|
/* restart the transmitter */
|
|
outl(TxENA, ioaddr + cr);
|
|
|
|
if (sis900_debug > 1)
|
|
printf("sis900_transmit: Queued Tx packet size %d.\n", (int) s);
|
|
|
|
to = currticks() + TX_TIMEOUT;
|
|
|
|
while ((((volatile u32) tx_status=txd.cmdsts) & OWN) && (currticks() < to))
|
|
/* wait */ ;
|
|
|
|
if (currticks() >= to) {
|
|
printf("sis900_transmit: TX Timeout! Tx status %X.\n", tx_status);
|
|
}
|
|
|
|
if (tx_status & (ABORT | UNDERRUN | OWCOLL)) {
|
|
/* packet unsuccessfully transmited */
|
|
printf("sis900_transmit: Transmit error, Tx status %X.\n", tx_status);
|
|
}
|
|
/* Disable interrupts by clearing the interrupt mask. */
|
|
outl(0, ioaddr + imr);
|
|
}
|
|
|
|
|
|
/* Function: sis900_poll
|
|
*
|
|
* Description: checks for a received packet and returns it if found.
|
|
*
|
|
* Arguments: struct nic *nic: NIC data structure
|
|
*
|
|
* Returns: 1 if a packet was recieved.
|
|
* 0 if no pacet was recieved.
|
|
*
|
|
* Side effects:
|
|
* Returns (copies) the packet to the array nic->packet.
|
|
* Returns the length of the packet in nic->packetlen.
|
|
*/
|
|
|
|
static int
|
|
sis900_poll(struct nic *nic)
|
|
{
|
|
u32 rx_status = rxd[cur_rx].cmdsts;
|
|
int retstat = 0;
|
|
|
|
if (sis900_debug > 2)
|
|
printf("sis900_poll: cur_rx:%d, status:%X\n", cur_rx, rx_status);
|
|
|
|
if (!(rx_status & OWN))
|
|
return retstat;
|
|
|
|
if (sis900_debug > 1)
|
|
printf("sis900_poll: got a packet: cur_rx:%d, status:%X\n",
|
|
cur_rx, rx_status);
|
|
|
|
nic->packetlen = (rx_status & DSIZE) - CRC_SIZE;
|
|
|
|
if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) {
|
|
/* corrupted packet received */
|
|
printf("sis900_poll: Corrupted packet received, buffer status = %X\n",
|
|
rx_status);
|
|
retstat = 0;
|
|
} else {
|
|
/* give packet to higher level routine */
|
|
memcpy(nic->packet, (rxb + cur_rx*RX_BUF_SIZE), nic->packetlen);
|
|
retstat = 1;
|
|
}
|
|
|
|
/* return the descriptor and buffer to receive ring */
|
|
rxd[cur_rx].cmdsts = RX_BUF_SIZE;
|
|
rxd[cur_rx].bufptr = (u32) &rxb[cur_rx*RX_BUF_SIZE];
|
|
|
|
if (++cur_rx == NUM_RX_DESC)
|
|
cur_rx = 0;
|
|
|
|
/* re-enable the potentially idle receive state machine */
|
|
outl(RxENA , ioaddr + cr);
|
|
|
|
return retstat;
|
|
}
|
|
|
|
|
|
/* Function: sis900_disable
|
|
*
|
|
* Description: Turns off interrupts and stops Tx and Rx engines
|
|
*
|
|
* Arguments: struct nic *nic: NIC data structure
|
|
*
|
|
* Returns: void.
|
|
*/
|
|
|
|
static void
|
|
sis900_disable(struct nic *nic)
|
|
{
|
|
/* Disable interrupts by clearing the interrupt mask. */
|
|
outl(0, ioaddr + imr);
|
|
outl(0, ioaddr + ier);
|
|
|
|
/* Stop the chip's Tx and Rx Status Machine */
|
|
outl(RxDIS | TxDIS, ioaddr + cr);
|
|
}
|