Copyright (c) 2003, 2004 Jeff Garzik <jgarzik@pobox.com>
Copyright (c) 2003, 2004, 2005 Francois Romieu <romieu@fr.zoreil.com>
- Based on r8169.c, tg3.c, 8139cp.c, skge.c and probably even epic100.c.
+ Based on r8169.c, tg3.c, 8139cp.c, skge.c, epic100.c and SiS 190/191
+ genuine driver.
This software may be used and distributed according to the terms of
the GNU General Public License (GPL), incorporated herein by reference.
#define TX_RING_BYTES (NUM_TX_DESC * sizeof(struct TxDesc))
#define RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc))
#define RX_BUF_SIZE 1536
+#define RX_BUF_MASK 0xfff8
#define SIS190_REGS_SIZE 0x80
#define SIS190_TX_TIMEOUT (6*HZ)
enum sis190_registers {
TxControl = 0x00,
TxDescStartAddr = 0x04,
- TxNextDescAddr = 0x0c, // unused
+ rsv0 = 0x08, // reserved
+ TxSts = 0x0c, // unused (Control/Status)
RxControl = 0x10,
RxDescStartAddr = 0x14,
- RxNextDescAddr = 0x1c, // unused
+ rsv1 = 0x18, // reserved
+ RxSts = 0x1c, // unused
IntrStatus = 0x20,
IntrMask = 0x24,
IntrControl = 0x28,
- IntrTimer = 0x2c, // unused
- PMControl = 0x30, // unused
+ IntrTimer = 0x2c, // unused (Interupt Timer)
+ PMControl = 0x30, // unused (Power Mgmt Control/Status)
+ rsv2 = 0x34, // reserved
ROMControl = 0x38,
ROMInterface = 0x3c,
StationControl = 0x40,
GMIIControl = 0x44,
+ GIoCR = 0x48, // unused (GMAC IO Compensation)
+ GIoCtrl = 0x4c, // unused (GMAC IO Control)
TxMacControl = 0x50,
+ TxLimit = 0x54, // unused (Tx MAC Timer/TryLimit)
+ RGDelay = 0x58, // unused (RGMII Tx Internal Delay)
+ rsv3 = 0x5c, // reserved
RxMacControl = 0x60,
RxMacAddr = 0x62,
RxHashTable = 0x68,
// Undocumented = 0x6c,
- RxWakeOnLan = 0x70,
- // Undocumented = 0x74,
- RxMPSControl = 0x78, // unused
+ RxWolCtrl = 0x70,
+ RxWolData = 0x74, // unused (Rx WOL Data Access)
+ RxMPSControl = 0x78, // unused (Rx MPS Control)
+ rsv4 = 0x7c, // reserved
};
enum sis190_register_content {
RxSizeMask = 0x0000ffff
};
+enum sis190_eeprom_access_register_bits {
+ EECS = 0x00000001, // unused
+ EECLK = 0x00000002, // unused
+ EEDO = 0x00000008, // unused
+ EEDI = 0x00000004, // unused
+ EEREQ = 0x00000080,
+ EEROP = 0x00000200,
+ EEWOP = 0x00000100 // unused
+};
+
+/* EEPROM Addresses */
+enum sis190_eeprom_address {
+ EEPROMSignature = 0x00,
+ EEPROMCLK = 0x01, // unused
+ EEPROMInfo = 0x02,
+ EEPROMMACAddr = 0x03
+};
+
struct sis190_private {
void __iomem *mmio_addr;
struct pci_dev *pci_dev;
return mdio_read(tp->mmio_addr, reg);
}
-static int sis190_read_eeprom(void __iomem *ioaddr, u32 reg)
+static u16 __devinit sis190_read_eeprom(void __iomem *ioaddr, u32 reg)
{
+ u16 data = 0xffff;
unsigned int i;
- u16 data;
- u32 val;
if (!(SIS_R32(ROMControl) & 0x0002))
return 0;
- val = (0x0080 | (0x2 << 8) | (reg << 10));
-
- SIS_W32(ROMInterface, val);
+ SIS_W32(ROMInterface, EEREQ | EEROP | (reg << 10));
for (i = 0; i < 200; i++) {
- if (!(SIS_R32(ROMInterface) & 0x0080))
+ if (!(SIS_R32(ROMInterface) & EEREQ)) {
+ data = (SIS_R32(ROMInterface) & 0xffff0000) >> 16;
break;
+ }
msleep(1);
}
- data = (u16) ((SIS_R32(ROMInterface) & 0xffff0000) >> 16);
-
return data;
}
u32 eor = le32_to_cpu(desc->size) & RingEnd;
desc->PSize = 0x0;
- desc->size = cpu_to_le32(rx_buf_sz | eor);
+ desc->size = cpu_to_le32((rx_buf_sz & RX_BUF_MASK) | eor);
wmb();
desc->status = cpu_to_le32(OWNbit | INTbit);
}
SIS_W16(RxMacControl, 0x02);
SIS_W32(RxHashTable, 0x0);
SIS_W32(0x6c, 0x0);
- SIS_W32(RxWakeOnLan, 0x0);
- SIS_W32(0x74, 0x0);
+ SIS_W32(RxWolCtrl, 0x0);
+ SIS_W32(RxWolData, 0x0);
SIS_PCI_COMMIT();
unsigned int mtu = dev->mtu;
tp->rx_buf_sz = (mtu > RX_BUF_SIZE) ? mtu + ETH_HLEN + 8 : RX_BUF_SIZE;
+ /* RxDesc->size has a licence to kill the lower bits */
+ if (tp->rx_buf_sz & 0x07) {
+ tp->rx_buf_sz += 8;
+ tp->rx_buf_sz &= RX_BUF_MASK;
+ }
}
static int sis190_open(struct net_device *dev)
if (tmp8 & CmdTxEnb)
SIS_W8(TxControl, tmp8 & ~CmdTxEnb);
+
+ net_tx_err(tp, KERN_INFO "%s: Transmit timeout, status %08x %08x.\n",
+ dev->name, SIS_R32(TxControl), SIS_R32(TxSts));
+
/* Disable interrupts by clearing the interrupt mask. */
SIS_W32(IntrMask, 0x0000);
netif_wake_queue(dev);
}
+static int __devinit sis190_get_mac_addr_from_eeprom(struct pci_dev *pdev,
+ struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ u16 sig;
+ int i;
+
+ net_probe(tp, KERN_INFO "%s: Read MAC address from EEPROM\n",
+ pci_name(pdev));
+
+ /* Check to see if there is a sane EEPROM */
+ sig = (u16) sis190_read_eeprom(ioaddr, EEPROMSignature);
+
+ if ((sig == 0xffff) || (sig == 0x0000)) {
+ net_probe(tp, KERN_INFO "%s: Error EEPROM read %x.\n",
+ pci_name(pdev), sig);
+ return -EIO;
+ }
+
+ /* Get MAC address from EEPROM */
+ for (i = 0; i < MAC_ADDR_LEN / 2; i++) {
+ u16 w = sis190_read_eeprom(ioaddr, EEPROMMACAddr + i);
+
+ ((u16 *)dev->dev_addr)[0] = le16_to_cpu(w);
+ }
+
+ return 0;
+}
+
+/**
+ * sis190_get_mac_addr_from_apc - Get MAC address for SiS965 model
+ * @pdev: PCI device
+ * @dev: network device to get address for
+ *
+ * SiS965 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 __devinit sis190_get_mac_addr_from_apc(struct pci_dev *pdev,
+ struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+ struct pci_dev *isa_bridge;
+ u8 reg, tmp8;
+ int i;
+
+ net_probe(tp, KERN_INFO "%s: Read MAC address from APC.\n",
+ pci_name(pdev));
+
+ isa_bridge = pci_get_device(PCI_VENDOR_ID_SI, 0x0965, NULL);
+ if (!isa_bridge) {
+ net_probe(tp, KERN_INFO "%s: Can not find ISA bridge.\n",
+ pci_name(pdev));
+ return -EIO;
+ }
+
+ /* Enable port 78h & 79h to access APC Registers. */
+ pci_read_config_byte(isa_bridge, 0x48, &tmp8);
+ reg = (tmp8 & ~0x02);
+ pci_write_config_byte(isa_bridge, 0x48, reg);
+ udelay(50);
+ pci_read_config_byte(isa_bridge, 0x48, ®);
+
+ for (i = 0; i < MAC_ADDR_LEN; i++) {
+ outb(0x9 + i, 0x78);
+ dev->dev_addr[i] = inb(0x79);
+ }
+
+ outb(0x12, 0x78);
+ reg = inb(0x79);
+
+ /* Restore the value to ISA Bridge */
+ pci_write_config_byte(isa_bridge, 0x48, tmp8);
+ pci_dev_put(isa_bridge);
+
+ return 0;
+}
+
+/**
+ * sis190_init_rxfilter - Initialize the Rx filter
+ * @dev: network device to initialize
+ *
+ * Set receive filter address to our MAC address
+ * and enable packet filtering.
+ */
+static inline void sis190_init_rxfilter(struct net_device *dev)
+{
+ struct sis190_private *tp = netdev_priv(dev);
+ void __iomem *ioaddr = tp->mmio_addr;
+ u16 ctl;
+ int i;
+
+ ctl = SIS_R16(RxMacControl);
+ /*
+ * Disable packet filtering before setting filter.
+ * Note: SiS's driver writes 32 bits but RxMacControl is 16 bits
+ * only and followed by RxMacAddr (6 bytes). Strange. -- FR
+ */
+ SIS_W16(RxMacControl, ctl & ~0x0f00);
+
+ for (i = 0; i < MAC_ADDR_LEN; i++)
+ SIS_W8(RxMacAddr + i, dev->dev_addr[i]);
+
+ SIS_W16(RxMacControl, ctl);
+ SIS_PCI_COMMIT();
+}
+
+static int sis190_get_mac_addr(struct pci_dev *pdev, struct net_device *dev)
+{
+ u8 from;
+
+ pci_read_config_byte(pdev, 0x73, &from);
+
+ return (from & 0x00000001) ?
+ sis190_get_mac_addr_from_apc(pdev, dev) :
+ sis190_get_mac_addr_from_eeprom(pdev, dev);
+}
+
static void sis190_set_speed_auto(struct net_device *dev)
{
struct sis190_private *tp = netdev_priv(dev);
struct sis190_private *tp;
struct net_device *dev;
void __iomem *ioaddr;
- int i, rc;
+ int rc;
if (!printed_version) {
net_drv(&debug, KERN_INFO SIS190_DRIVER_NAME " loaded.\n");
tp = netdev_priv(dev);
ioaddr = tp->mmio_addr;
- /* Get MAC address */
- /* Read node address from the EEPROM */
+ rc = sis190_get_mac_addr(pdev, dev);
+ if (rc < 0)
+ goto err_release_board;
- if (SIS_R32(ROMControl) & 0x4) {
- for (i = 0; i < 3; i++) {
- SIS_W16(RxMacAddr + 2*i,
- sis190_read_eeprom(ioaddr, 3 + i));
- }
- }
-
- for (i = 0; i < MAC_ADDR_LEN; i++)
- dev->dev_addr[i] = SIS_R8(RxMacAddr + i);
+ sis190_init_rxfilter(dev);
INIT_WORK(&tp->phy_task, sis190_phy_task, dev);
spin_lock_init(&tp->lock);
rc = register_netdev(dev);
- if (rc < 0) {
- sis190_release_board(pdev);
- goto out;
- }
+ if (rc < 0)
+ goto err_release_board;
pci_set_drvdata(pdev, dev);
sis190_set_speed_auto(dev);
out:
return rc;
+
+err_release_board:
+ sis190_release_board(pdev);
+ goto out;
}
static void __devexit sis190_remove_one(struct pci_dev *pdev)