2005-03-08 18:53:11 +00:00
|
|
|
|
/* -*- 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 */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
07 Dec 2003 timlegge - Enabled Multicast Support
|
|
|
|
|
06 Dec 2003 timlegge - Fixed relocation issue in 5.2
|
|
|
|
|
04 Jan 2002 Chien-Yu Chen, Doug Ambrisko, Marty Connor Patch to Etherboot 5.0.5
|
|
|
|
|
Added support for the SiS 630ET plus various bug fixes from linux kernel
|
|
|
|
|
source 2.4.17.
|
|
|
|
|
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"
|
2006-04-24 15:42:49 +00:00
|
|
|
|
#include <gpxe/pci.h>
|
2005-03-08 18:53:11 +00:00
|
|
|
|
#include "timer.h"
|
|
|
|
|
|
|
|
|
|
#include "sis900.h"
|
|
|
|
|
|
|
|
|
|
/* Globals */
|
|
|
|
|
|
2005-04-13 01:01:33 +00:00
|
|
|
|
static struct nic_operations sis900_operations;
|
|
|
|
|
|
2005-03-08 18:53:11 +00:00
|
|
|
|
static int sis900_debug = 0;
|
|
|
|
|
|
|
|
|
|
static unsigned short vendor, dev_id;
|
|
|
|
|
static unsigned long ioaddr;
|
|
|
|
|
static u8 pci_revision;
|
|
|
|
|
|
|
|
|
|
static unsigned int cur_phy;
|
|
|
|
|
|
|
|
|
|
static unsigned int cur_rx;
|
|
|
|
|
|
2005-05-03 11:29:33 +00:00
|
|
|
|
struct {
|
|
|
|
|
BufferDesc txd;
|
|
|
|
|
BufferDesc rxd[NUM_RX_DESC];
|
|
|
|
|
unsigned char txb[TX_BUF_SIZE];
|
|
|
|
|
unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE];
|
|
|
|
|
} sis900_bufs __shared;
|
|
|
|
|
#define txd sis900_bufs.txd
|
|
|
|
|
#define rxd sis900_bufs.rxd
|
|
|
|
|
#define txb sis900_bufs.txb
|
|
|
|
|
#define rxb sis900_bufs.rxb
|
2005-03-08 18:53:11 +00:00
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
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. */
|
|
|
|
|
};
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
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 void vt6103_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, 0x6B70, amd79c901_read_mode},
|
|
|
|
|
{"AMD 79C901 HomePNA PHY", 0x0000, 0x6B90, amd79c901_read_mode},
|
|
|
|
|
{"ICS 1893 Integrated PHYceiver" , 0x0015, 0xf440,ics1893_read_mode},
|
|
|
|
|
// {"NS 83851 PHY",0x2000, 0x5C20, MIX },
|
|
|
|
|
{"RTL 8201 10/100Mbps Phyceiver" , 0x0000, 0x8200,rtl8201_read_mode},
|
|
|
|
|
{"VIA 6103 10/100Mbps Phyceiver", 0x0101, 0x8f20,vt6103_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_id pci_isa_bridge_list[] = {
|
|
|
|
|
{ 0x1039, 0x0008,
|
|
|
|
|
"SIS 85C503/5513 PCI to ISA bridge"},
|
|
|
|
|
};
|
|
|
|
|
|
2005-04-26 12:19:39 +00:00
|
|
|
|
PCI_DRIVER ( sis_bridge_pci_driver, pci_isa_bridge_list, PCI_NO_CLASS );
|
2005-03-08 18:53:11 +00:00
|
|
|
|
|
2005-04-22 16:24:21 +00:00
|
|
|
|
static struct device_driver sis_bridge_driver = {
|
|
|
|
|
.name = "SIS ISA bridge",
|
|
|
|
|
.bus_driver = &pci_driver,
|
|
|
|
|
.bus_driver_info = ( struct bus_driver_info * ) &sis_bridge_pci_driver,
|
|
|
|
|
};
|
|
|
|
|
|
2005-03-08 18:53:11 +00:00
|
|
|
|
/* Function Prototypes */
|
|
|
|
|
|
2005-04-21 19:20:39 +00:00
|
|
|
|
static int sis900_probe(struct nic *nic,struct pci_device *pci);
|
2005-03-08 18:53:11 +00:00
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
#if 0
|
|
|
|
|
static void sis900_mdio_write(int phy_id, int location, int val);
|
|
|
|
|
#endif
|
|
|
|
|
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, int retrieve);
|
|
|
|
|
|
2005-04-21 19:20:39 +00:00
|
|
|
|
static void sis900_disable(struct nic *nic, struct pci_device *pci);
|
2005-03-08 18:53:11 +00:00
|
|
|
|
|
|
|
|
|
static void sis900_irq(struct nic *nic, irq_action_t action);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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 __unused, 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* sis96x_get_mac_addr: - Get MAC address for SiS962 or SiS963 model
|
|
|
|
|
* @pci_dev: the sis900 pci device
|
|
|
|
|
* @net_dev: the net device to get address for
|
|
|
|
|
*
|
|
|
|
|
* SiS962 or SiS963 model, use EEPROM to store MAC address. And EEPROM
|
|
|
|
|
* is shared by
|
|
|
|
|
* LAN and 1394. When access EEPROM, send EEREQ signal to hardware first
|
|
|
|
|
* and wait for EEGNT. If EEGNT is ON, EEPROM is permitted to be access
|
|
|
|
|
* by LAN, otherwise is not. After MAC address is read from EEPROM, send
|
|
|
|
|
* EEDONE signal to refuse EEPROM access by LAN.
|
|
|
|
|
* The EEPROM map of SiS962 or SiS963 is different to SiS900.
|
|
|
|
|
* The signature field in SiS962 or SiS963 spec is meaningless.
|
|
|
|
|
* MAC address is read into @net_dev->dev_addr.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int sis96x_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic)
|
|
|
|
|
{
|
|
|
|
|
/* long ioaddr = net_dev->base_addr; */
|
|
|
|
|
long ee_addr = ioaddr + mear;
|
|
|
|
|
u32 waittime = 0;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
printf("Alternate function\n");
|
|
|
|
|
|
|
|
|
|
outl(EEREQ, ee_addr);
|
|
|
|
|
while(waittime < 2000) {
|
|
|
|
|
if(inl(ee_addr) & EEGNT) {
|
|
|
|
|
|
|
|
|
|
/* get MAC address from EEPROM */
|
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
|
((u16 *)(nic->node_addr))[i] = sis900_read_eeprom(i+EEPROMMACAddr);
|
|
|
|
|
|
|
|
|
|
outl(EEDONE, ee_addr);
|
|
|
|
|
return 1;
|
|
|
|
|
} else {
|
|
|
|
|
udelay(1);
|
|
|
|
|
waittime ++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
outl(EEDONE, ee_addr);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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 __unused, struct nic *nic)
|
|
|
|
|
{
|
|
|
|
|
u8 reg;
|
|
|
|
|
int i;
|
2005-04-22 16:24:21 +00:00
|
|
|
|
struct bus_loc bus_loc;
|
|
|
|
|
union {
|
|
|
|
|
struct bus_dev bus_dev;
|
|
|
|
|
struct pci_device isa_bridge;
|
|
|
|
|
} u;
|
2005-03-08 18:53:11 +00:00
|
|
|
|
|
|
|
|
|
/* find PCI to ISA bridge */
|
2005-04-22 16:24:21 +00:00
|
|
|
|
memset(&bus_loc, 0, sizeof(bus_loc));
|
|
|
|
|
if ( ! find_by_driver ( &bus_loc, &u.bus_dev, &sis_bridge_driver, 0 ) )
|
2005-03-08 18:53:11 +00:00
|
|
|
|
return 0;
|
|
|
|
|
|
2005-04-22 16:24:21 +00:00
|
|
|
|
pci_read_config_byte(&u.isa_bridge, 0x48, ®);
|
|
|
|
|
pci_write_config_byte(&u.isa_bridge, 0x48, reg | 0x40);
|
2005-03-08 18:53:11 +00:00
|
|
|
|
|
|
|
|
|
for (i = 0; i < ETH_ALEN; i++)
|
|
|
|
|
{
|
|
|
|
|
outb(0x09 + i, 0x70);
|
|
|
|
|
((u8 *)(nic->node_addr))[i] = inb(0x71);
|
|
|
|
|
}
|
2005-04-22 16:24:21 +00:00
|
|
|
|
pci_write_config_byte(&u.isa_bridge, 0x48, reg & ~0x40);
|
2005-03-08 18:53:11 +00:00
|
|
|
|
|
|
|
|
|
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 sis635_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic)
|
|
|
|
|
{
|
|
|
|
|
u32 rfcrSave;
|
|
|
|
|
u32 i;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rfcrSave = inl(rfcr + ioaddr);
|
|
|
|
|
|
|
|
|
|
outl(rfcrSave | RELOAD, ioaddr + cr);
|
|
|
|
|
outl(0, ioaddr + cr);
|
|
|
|
|
|
|
|
|
|
/* disable packet filtering before setting filter */
|
|
|
|
|
outl(rfcrSave & ~RFEN, rfcr + ioaddr);
|
|
|
|
|
|
|
|
|
|
/* load MAC addr to filter data register */
|
|
|
|
|
for (i = 0 ; i < 3 ; i++) {
|
|
|
|
|
outl((i << RFADDR_shift), ioaddr + rfcr);
|
|
|
|
|
*( ((u16 *)nic->node_addr) + i) = inw(ioaddr + rfdr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* enable packet filitering */
|
|
|
|
|
outl(rfcrSave | RFEN, rfcr + ioaddr);
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
*/
|
|
|
|
|
|
2005-04-21 19:20:39 +00:00
|
|
|
|
static int sis900_probe ( struct nic *nic, struct pci_device *pci ) {
|
|
|
|
|
|
2005-03-08 18:53:11 +00:00
|
|
|
|
int i;
|
|
|
|
|
int found=0;
|
|
|
|
|
int phy_addr;
|
|
|
|
|
u8 revision;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
if (pci->ioaddr == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
nic->irqno = 0;
|
2005-04-21 19:20:39 +00:00
|
|
|
|
pci_fill_nic ( nic, pci );
|
2005-04-13 01:01:33 +00:00
|
|
|
|
nic->ioaddr = pci->ioaddr;
|
|
|
|
|
ioaddr = pci->ioaddr;
|
2005-04-22 16:24:21 +00:00
|
|
|
|
vendor = pci->vendor_id;
|
|
|
|
|
dev_id = pci->device_id;
|
2005-03-08 18:53:11 +00:00
|
|
|
|
|
|
|
|
|
/* wakeup chip */
|
2005-04-12 18:13:59 +00:00
|
|
|
|
pci_write_config_dword(pci, 0x40, 0x00000000);
|
2005-03-08 18:53:11 +00:00
|
|
|
|
|
|
|
|
|
adjust_pci_device(pci);
|
|
|
|
|
|
|
|
|
|
/* get MAC address */
|
|
|
|
|
ret = 0;
|
2005-04-12 18:13:59 +00:00
|
|
|
|
pci_read_config_byte(pci, PCI_REVISION, &revision);
|
2005-03-08 18:53:11 +00:00
|
|
|
|
|
|
|
|
|
/* save for use later in sis900_reset() */
|
|
|
|
|
pci_revision = revision;
|
|
|
|
|
|
|
|
|
|
if (revision == SIS630E_900_REV)
|
|
|
|
|
ret = sis630e_get_mac_addr(pci, nic);
|
|
|
|
|
else if ((revision > 0x81) && (revision <= 0x90))
|
|
|
|
|
ret = sis635_get_mac_addr(pci, nic);
|
|
|
|
|
else if (revision == SIS96x_900_REV)
|
|
|
|
|
ret = sis96x_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 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 630ET : set the mii access mode as software-mode */
|
|
|
|
|
if (revision == SIS630ET_900_REV)
|
|
|
|
|
outl(ACCESSMODE | inl(ioaddr + cr), ioaddr + cr);
|
|
|
|
|
|
|
|
|
|
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) &&
|
|
|
|
|
((phy_id1 & 0xFFF0) == mii_chip_table[i].phy_id1)){
|
|
|
|
|
|
|
|
|
|
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 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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);
|
2005-04-13 00:02:40 +00:00
|
|
|
|
nic->nic_op = &sis900_operations;
|
2005-03-08 18:53:11 +00:00
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2005-04-12 18:13:59 +00:00
|
|
|
|
|
|
|
|
|
|
2005-03-08 18:53:11 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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(EECS, 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();
|
|
|
|
|
}
|
|
|
|
|
outl(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();
|
|
|
|
|
}
|
|
|
|
|
outl(0x00, mdio_addr);
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
outl(0x00, mdio_addr);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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| inl(ioaddr + cr), 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 __unused)
|
|
|
|
|
{
|
|
|
|
|
int i = 0;
|
|
|
|
|
u32 status = TxRCMP | RxRCMP;
|
|
|
|
|
|
|
|
|
|
outl(0, ioaddr + ier);
|
|
|
|
|
outl(0, ioaddr + imr);
|
|
|
|
|
outl(0, ioaddr + rfcr);
|
|
|
|
|
|
|
|
|
|
outl(RxRESET | TxRESET | RESET | inl(ioaddr + cr), ioaddr + cr);
|
|
|
|
|
|
|
|
|
|
/* Check that the chip has finished the reset. */
|
|
|
|
|
while (status && (i++ < 1000)) {
|
|
|
|
|
status ^= (inl(isr + ioaddr) & status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( (pci_revision >= SIS635A_900_REV) || (pci_revision == SIS900B_900_REV) )
|
|
|
|
|
outl(PESEL | RND_CNT, ioaddr + cfg);
|
|
|
|
|
else
|
|
|
|
|
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 + ioaddr);
|
|
|
|
|
|
|
|
|
|
/* 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 __unused)
|
|
|
|
|
{
|
|
|
|
|
txd.link = (u32) 0;
|
|
|
|
|
txd.cmdsts = (u32) 0;
|
|
|
|
|
txd.bufptr = virt_to_bus(&txb[0]);
|
|
|
|
|
|
|
|
|
|
/* load Transmit Descriptor Register */
|
|
|
|
|
outl(virt_to_bus(&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 __unused)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
cur_rx = 0;
|
|
|
|
|
|
|
|
|
|
/* init RX descriptor */
|
|
|
|
|
for (i = 0; i < NUM_RX_DESC; i++) {
|
|
|
|
|
rxd[i].link = virt_to_bus((i+1 < NUM_RX_DESC) ? &rxd[i+1] : &rxd[0]);
|
|
|
|
|
rxd[i].cmdsts = (u32) RX_BUF_SIZE;
|
|
|
|
|
rxd[i].bufptr = virt_to_bus(&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(virt_to_bus(&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 __unused)
|
|
|
|
|
{
|
|
|
|
|
int i, table_entries;
|
|
|
|
|
u32 rx_mode;
|
|
|
|
|
u16 mc_filter[16] = {0}; /* 256/128 bits multicast hash table */
|
|
|
|
|
|
|
|
|
|
if((pci_revision == SIS635A_900_REV) || (pci_revision == SIS900B_900_REV))
|
|
|
|
|
table_entries = 16;
|
|
|
|
|
else
|
|
|
|
|
table_entries = 8;
|
|
|
|
|
|
|
|
|
|
/* accept all multicast packet */
|
|
|
|
|
rx_mode = RFAAB | RFAAM;
|
|
|
|
|
for (i = 0; i < table_entries; i++)
|
|
|
|
|
mc_filter[i] = 0xffff;
|
|
|
|
|
|
|
|
|
|
/* update Multicast Hash Table in Receive Filter */
|
|
|
|
|
for (i = 0; i < table_entries; i++) {
|
|
|
|
|
/* why plus 0x04? That makes the correct value for hash table. */
|
|
|
|
|
outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr);
|
|
|
|
|
outl(mc_filter[i], ioaddr + rfdr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Accept Broadcast and multicast packets, destination addresses that match
|
|
|
|
|
our MAC address */
|
|
|
|
|
outl(RFEN | rx_mode, 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);
|
|
|
|
|
|
|
|
|
|
if( inl(ioaddr + cfg) & EDB_MASTER_EN ) {
|
|
|
|
|
tx_flags = TxATP | (DMA_BURST_64 << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift);
|
|
|
|
|
rx_flags = DMA_BURST_64 << RxMXDMA_shift;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
tx_flags = TxATP | (DMA_BURST_512 << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift);
|
|
|
|
|
rx_flags = DMA_BURST_512 << 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 __unused, int phy_addr, int *speed, int *duplex)
|
|
|
|
|
{
|
|
|
|
|
int i = 0;
|
|
|
|
|
u32 status;
|
|
|
|
|
u16 phy_id0, phy_id1;
|
|
|
|
|
|
|
|
|
|
/* STSOUT register is Latched on Transition, read operation updates it */
|
|
|
|
|
while (i++ < 2)
|
|
|
|
|
status = sis900_mdio_read(phy_addr, MII_STSOUT);
|
|
|
|
|
|
|
|
|
|
*speed = HW_SPEED_10_MBPS;
|
|
|
|
|
*duplex = FDX_CAPABLE_HALF_SELECTED;
|
|
|
|
|
|
|
|
|
|
if (status & (MII_NWAY_TX | MII_NWAY_TX_FDX))
|
|
|
|
|
*speed = HW_SPEED_100_MBPS;
|
|
|
|
|
if (status & ( MII_NWAY_TX_FDX | MII_NWAY_T_FDX))
|
|
|
|
|
*duplex = FDX_CAPABLE_FULL_SELECTED;
|
|
|
|
|
|
|
|
|
|
/* Workaround for Realtek RTL8201 PHY issue */
|
|
|
|
|
phy_id0 = sis900_mdio_read(phy_addr, MII_PHY_ID0);
|
|
|
|
|
phy_id1 = sis900_mdio_read(phy_addr, MII_PHY_ID1);
|
|
|
|
|
if((phy_id0 == 0x0000) && ((phy_id1 & 0xFFF0) == 0x8200)){
|
|
|
|
|
if(sis900_mdio_read(phy_addr, MII_CONTROL) & MII_CNTL_FDX)
|
|
|
|
|
*duplex = FDX_CAPABLE_FULL_SELECTED;
|
|
|
|
|
if(sis900_mdio_read(phy_addr, 0x0019) & 0x01)
|
|
|
|
|
*speed = HW_SPEED_100_MBPS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 __unused, 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 __unused, 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 __unused, 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("rtl8201_read_config_mode: Media Link Off\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* vt6103_read_mode: - read media mode for vt6103 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 vt6103_read_mode(struct nic *nic __unused, 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("vt6103_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("vt6103_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 to, nstype;
|
2005-05-23 19:30:37 +00:00
|
|
|
|
volatile u32 tx_status;
|
2005-03-08 18:53:11 +00:00
|
|
|
|
|
|
|
|
|
/* Stop the transmitter */
|
|
|
|
|
outl(TxDIS | inl(ioaddr + cr), ioaddr + cr);
|
|
|
|
|
|
|
|
|
|
/* load Transmit Descriptor Register */
|
|
|
|
|
outl(virt_to_bus(&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 = virt_to_bus(&txb[0]);
|
|
|
|
|
txd.cmdsts = (u32) OWN | s;
|
|
|
|
|
|
|
|
|
|
/* restart the transmitter */
|
|
|
|
|
outl(TxENA | inl(ioaddr + cr), ioaddr + cr);
|
|
|
|
|
|
|
|
|
|
if (sis900_debug > 1)
|
|
|
|
|
printf("sis900_transmit: Queued Tx packet size %d.\n", (int) s);
|
|
|
|
|
|
|
|
|
|
to = currticks() + TX_TIMEOUT;
|
|
|
|
|
|
2005-05-23 19:30:37 +00:00
|
|
|
|
while (((tx_status=txd.cmdsts) & OWN) && (currticks() < to))
|
2005-03-08 18:53:11 +00:00
|
|
|
|
/* 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, int retrieve)
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
if ( ! retrieve ) return 1;
|
|
|
|
|
|
|
|
|
|
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 = virt_to_bus(&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 | inl(ioaddr + cr), 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
|
2005-04-21 19:20:39 +00:00
|
|
|
|
sis900_disable ( struct nic *nic, struct pci_device *pci __unused ) {
|
|
|
|
|
nic_disable ( nic );
|
2005-03-08 18:53:11 +00:00
|
|
|
|
/* merge reset and disable */
|
|
|
|
|
sis900_init(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 | inl(ioaddr + cr), ioaddr + cr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Function: sis900_irq
|
|
|
|
|
*
|
|
|
|
|
* Description: Enable, Disable, or Force, interrupts
|
|
|
|
|
*
|
|
|
|
|
* Arguments: struct nic *nic: NIC data structure
|
|
|
|
|
* irq_action_t action: Requested action
|
|
|
|
|
*
|
|
|
|
|
* Returns: void.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
sis900_irq(struct nic *nic __unused, irq_action_t action __unused)
|
|
|
|
|
{
|
|
|
|
|
switch ( action ) {
|
|
|
|
|
case DISABLE :
|
|
|
|
|
break;
|
|
|
|
|
case ENABLE :
|
|
|
|
|
break;
|
|
|
|
|
case FORCE :
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-04-13 01:01:33 +00:00
|
|
|
|
static struct nic_operations sis900_operations = {
|
|
|
|
|
.connect = dummy_connect,
|
|
|
|
|
.poll = sis900_poll,
|
|
|
|
|
.transmit = sis900_transmit,
|
|
|
|
|
.irq = sis900_irq,
|
|
|
|
|
};
|
|
|
|
|
|
2005-03-08 18:53:11 +00:00
|
|
|
|
static struct pci_id sis900_nics[] = {
|
|
|
|
|
PCI_ROM(0x1039, 0x0900, "sis900", "SIS900"),
|
|
|
|
|
PCI_ROM(0x1039, 0x7016, "sis7016", "SIS7016"),
|
|
|
|
|
};
|
|
|
|
|
|
2005-04-26 12:19:39 +00:00
|
|
|
|
PCI_DRIVER ( sis900_driver, sis900_nics, PCI_NO_CLASS );
|
2005-04-12 23:05:00 +00:00
|
|
|
|
|
2005-04-21 19:20:39 +00:00
|
|
|
|
DRIVER ( "SIS900", nic_driver, pci_driver, sis900_driver,
|
|
|
|
|
sis900_probe, sis900_disable );
|