#include "etherboot.h"
#include "nic.h"
#include "pci.h"
#include "timer.h"
#include "sis900.h"
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;
static BufferDesc txd;
static BufferDesc rxd[NUM_RX_DESC];
static unsigned char txb[TX_BUF_SIZE];
static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE];
#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}
};
#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, 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},
{"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;
static struct pci_id pci_isa_bridge_list[] = {
{ 0x1039, 0x0008,
"SIS 85C503/5513 PCI to ISA bridge"},
};
struct pci_driver sis_bridge_driver = {
.type = BRIDGE_DRIVER,
.name = "",
.probe = 0,
.ids = pci_isa_bridge_list,
.id_count = sizeof(pci_isa_bridge_list)/sizeof(pci_isa_bridge_list[0]),
.class = 0,
};
static int sis900_probe(struct dev *dev, 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);
#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);
static void sis900_disable(struct dev *dev);
static void sis900_irq(struct nic *nic, irq_action_t action);
static int sis900_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic)
{
u16 signature;
int i;
signature = (u16) sis900_read_eeprom( EEPROMSignature);
if (signature == 0xffff || signature == 0x0000) {
printf ("sis900_probe: Error EERPOM read %hX\n", signature);
return 0;
}
for (i = 0; i < 3; i++)
((u16 *)(nic->node_addr))[i] = sis900_read_eeprom(i+EEPROMMACAddr);
return 1;
}
static int sis96x_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic)
{
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) {
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;
}
static int sis630e_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic)
{
u8 reg;
int i;
struct pci_device p[1];
memset(p, 0, sizeof(p));
do {
find_pci(BRIDGE_DRIVER, p);
} while(p->driver && p->driver != &sis_bridge_driver);
if (!p->driver)
return 0;
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;
}
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);
outl(rfcrSave & ~RFEN, rfcr + ioaddr);
for (i = 0 ; i < 3 ; i++) {
outl((i << RFADDR_shift), ioaddr + rfcr);
*( ((u16 *)nic->node_addr) + i) = inw(ioaddr + rfdr);
}
outl(rfcrSave | RFEN, rfcr + ioaddr);
return 1;
}
static int sis900_probe(struct dev *dev, struct pci_device *pci)
{
struct nic *nic = (struct nic *)dev;
int i;
int found=0;
int phy_addr;
u8 revision;
int ret;
if (pci->ioaddr == 0)
return 0;
nic->irqno = 0;
nic->ioaddr = pci->ioaddr & ~3;
ioaddr = pci->ioaddr & ~3;
vendor = pci->vendor;
dev_id = pci->dev_id;
pcibios_write_config_dword(pci->bus, pci->devfn, 0x40, 0x00000000);
adjust_pci_device(pci);
ret = 0;
pcibios_read_config_byte(pci->bus,pci->devfn, PCI_REVISION, &revision);
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;
}
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);
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)
continue;
phy_id0 = sis900_mdio_read(phy_addr, MII_PHY_ID0);
phy_id1 = sis900_mdio_read(phy_addr, MII_PHY_ID1);
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 0;
}
cur_phy = mii.phy_addr;
printf("sis900_probe: Using %s as default\n", mii.chip_info->name);
sis900_init(nic);
dev->disable = sis900_disable;
nic->poll = sis900_poll;
nic->transmit = sis900_transmit;
nic->irq = sis900_irq;
return 1;
}
#define eeprom_delay() inl(ee_addr)
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();
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();
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();
}
outl(0, ee_addr);
eeprom_delay();
outl(EECLK, ee_addr);
return (retval);
}
#define sis900_mdio_delay() inl(mdio_addr)
static void sis900_mdio_idle(long mdio_addr)
{
outl(MDIO | MDDIR, mdio_addr);
sis900_mdio_delay();
outl(MDIO | MDDIR | MDC, mdio_addr);
}
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();
}
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);
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();
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();
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
static void
sis900_init(struct nic *nic)
{
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);
}
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);
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);
}
static void
sis900_init_rxfilter(struct nic *nic)
{
u32 rfcrSave;
int i;
rfcrSave = inl(rfcr + ioaddr);
outl(rfcrSave & ~RFEN, rfcr + ioaddr);
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));
}
outl(rfcrSave | RFEN, rfcr + ioaddr);
}
static void
sis900_init_txd(struct nic *nic __unused)
{
txd.link = (u32) 0;
txd.cmdsts = (u32) 0;
txd.bufptr = virt_to_bus(&txb[0]);
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));
}
static void
sis900_init_rxd(struct nic *nic __unused)
{
int i;
cur_rx = 0;
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);
}
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));
}
static void sis900_set_rx_mode(struct nic *nic __unused)
{
int i, table_entries;
u32 rx_mode;
u16 mc_filter[16] = {0};
if((pci_revision == SIS635A_900_REV) || (pci_revision == SIS900B_900_REV))
table_entries = 16;
else
table_entries = 8;
rx_mode = RFAAB | RFAAM;
for (i = 0; i < table_entries; i++)
mc_filter[i] = 0xffff;
for (i = 0; i < table_entries; i++) {
outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr);
outl(mc_filter[i], ioaddr + rfdr);
}
outl(RFEN | rx_mode, ioaddr + rfcr);
return;
}
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);
}
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;
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;
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");
}
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) {
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 {
*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");
}
}
static void ics1893_read_mode(struct nic *nic __unused, int phy_addr, int *speed, int *duplex)
{
int i = 0;
u32 status;
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");
}
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");
}
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");
}
static void
sis900_transmit(struct nic *nic,
const char *d,
unsigned int t,
unsigned int s,
const char *p)
{
u32 to, nstype;
u32 tx_status;
outl(TxDIS | inl(ioaddr + cr), ioaddr + cr);
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);
while (s < ETH_ZLEN)
txb[s++] = '\0';
txd.bufptr = virt_to_bus(&txb[0]);
txd.cmdsts = (u32) OWN | s;
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;
while ((((volatile u32) tx_status=txd.cmdsts) & OWN) && (currticks() < to))
;
if (currticks() >= to) {
printf("sis900_transmit: TX Timeout! Tx status %X.\n", tx_status);
}
if (tx_status & (ABORT | UNDERRUN | OWCOLL)) {
printf("sis900_transmit: Transmit error, Tx status %X.\n", tx_status);
}
outl(0, ioaddr + imr);
}
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)) {
printf("sis900_poll: Corrupted packet received, buffer status = %X\n",
rx_status);
retstat = 0;
} else {
memcpy(nic->packet, (rxb + cur_rx*RX_BUF_SIZE), nic->packetlen);
retstat = 1;
}
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;
outl(RxENA | inl(ioaddr + cr), ioaddr + cr);
return retstat;
}
static void
sis900_disable(struct dev *dev)
{
struct nic *nic = (struct nic *)dev;
sis900_init(nic);
outl(0, ioaddr + imr);
outl(0, ioaddr + ier);
outl(RxDIS | TxDIS | inl(ioaddr + cr), ioaddr + cr);
}
static void
sis900_irq(struct nic *nic __unused, irq_action_t action __unused)
{
switch ( action ) {
case DISABLE :
break;
case ENABLE :
break;
case FORCE :
break;
}
}
static struct pci_id sis900_nics[] = {
PCI_ROM(0x1039, 0x0900, "sis900", "SIS900"),
PCI_ROM(0x1039, 0x7016, "sis7016", "SIS7016"),
};
struct pci_driver sis900_driver = {
.type = NIC_DRIVER,
.name = "SIS900",
.probe = sis900_probe,
.ids = sis900_nics,
.id_count = sizeof(sis900_nics)/sizeof(sis900_nics[0]),
.class = 0,
};