]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/net/sis190.c
[PATCH] sis190: the size of the Rx buffer is constrained
[linux-2.6-omap-h63xx.git] / drivers / net / sis190.c
index ff4f24e5f59c664f5ad20edc78ec53ec6b6db4f1..2229698debbd11d02052573e28d6c14241d3f4db 100644 (file)
@@ -5,7 +5,8 @@
    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.
@@ -68,6 +69,7 @@
 #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 {
@@ -212,6 +223,24 @@ enum _DescStatusBit {
        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;
@@ -324,27 +353,24 @@ static int __mdio_read(struct net_device *dev, int phy_id, int reg)
        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;
 }
 
@@ -375,7 +401,7 @@ static inline void sis190_give_to_asic(struct RxDesc *desc, u32 rx_buf_sz)
        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);
 }
@@ -783,8 +809,8 @@ static void sis190_hw_start(struct net_device *dev)
        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();
 
@@ -899,6 +925,11 @@ static void sis190_set_rxbufsize(struct sis190_private *tp,
        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)
@@ -1205,6 +1236,10 @@ static void sis190_tx_timeout(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);
 
@@ -1219,6 +1254,125 @@ static void sis190_tx_timeout(struct net_device *dev)
        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, &reg);
+
+        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);
@@ -1334,7 +1488,7 @@ static int __devinit sis190_init_one(struct pci_dev *pdev,
        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");
@@ -1350,18 +1504,11 @@ static int __devinit sis190_init_one(struct pci_dev *pdev,
        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);
 
@@ -1382,10 +1529,8 @@ static int __devinit sis190_init_one(struct pci_dev *pdev,
 
        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);
 
@@ -1402,6 +1547,10 @@ static int __devinit sis190_init_one(struct pci_dev *pdev,
        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)