Merge ../powerpc-merge
[powerpc.git] / drivers / net / sky2.c
index 9308b3b..cae2edf 100644 (file)
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-/*
- * TOTEST
- *     - speed setting
- *     - suspend/resume
- */
-
 #include <linux/config.h>
 #include <linux/crc32.h>
 #include <linux/kernel.h>
@@ -57,7 +51,7 @@
 #include "sky2.h"
 
 #define DRV_NAME               "sky2"
-#define DRV_VERSION            "0.12"
+#define DRV_VERSION            "0.15"
 #define PFX                    DRV_NAME " "
 
 /*
@@ -102,6 +96,10 @@ static int copybreak __read_mostly = 256;
 module_param(copybreak, int, 0);
 MODULE_PARM_DESC(copybreak, "Receive copy threshold");
 
+static int disable_msi = 0;
+module_param(disable_msi, int, 0);
+MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)");
+
 static const struct pci_device_id sky2_id_table[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) },
        { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) },
@@ -198,7 +196,7 @@ static int sky2_set_power_state(struct sky2_hw *hw, pci_power_t state)
        sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
 
        pci_read_config_word(hw->pdev, hw->pm_cap + PCI_PM_PMC, &power_control);
-       vaux = (sky2_read8(hw, B0_CTST) & Y2_VAUX_AVAIL) &&
+       vaux = (sky2_read16(hw, B0_CTST) & Y2_VAUX_AVAIL) &&
                (power_control & PCI_PM_CAP_PME_D3cold);
 
        pci_read_config_word(hw->pdev, hw->pm_cap + PCI_PM_CTRL, &power_control);
@@ -704,7 +702,7 @@ static inline struct sky2_tx_le *get_tx_le(struct sky2_port *sky2)
  * This is a workaround code taken from SysKonnect sk98lin driver
  * to deal with chip bug on Yukon EC rev 0 in the wraparound case.
  */
-static inline void sky2_put_idx(struct sky2_hw *hw, unsigned q,
+static void sky2_put_idx(struct sky2_hw *hw, unsigned q,
                                u16 idx, u16 *last, u16 size)
 {
        wmb();
@@ -749,7 +747,7 @@ static inline u32 high32(dma_addr_t a)
 }
 
 /* Build description to hardware about buffer */
-static inline void sky2_rx_add(struct sky2_port *sky2, dma_addr_t map)
+static void sky2_rx_add(struct sky2_port *sky2, dma_addr_t map)
 {
        struct sky2_rx_le *le;
        u32 hi = high32(map);
@@ -889,13 +887,13 @@ static void sky2_vlan_rx_register(struct net_device *dev, struct vlan_group *grp
        struct sky2_hw *hw = sky2->hw;
        u16 port = sky2->port;
 
-       spin_lock(&sky2->tx_lock);
+       spin_lock_bh(&sky2->tx_lock);
 
        sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), RX_VLAN_STRIP_ON);
        sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_VLAN_TAG_ON);
        sky2->vlgrp = grp;
 
-       spin_unlock(&sky2->tx_lock);
+       spin_unlock_bh(&sky2->tx_lock);
 }
 
 static void sky2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
@@ -904,14 +902,14 @@ static void sky2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
        struct sky2_hw *hw = sky2->hw;
        u16 port = sky2->port;
 
-       spin_lock(&sky2->tx_lock);
+       spin_lock_bh(&sky2->tx_lock);
 
        sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), RX_VLAN_STRIP_OFF);
        sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_VLAN_TAG_OFF);
        if (sky2->vlgrp)
                sky2->vlgrp->vlan_devices[vid] = NULL;
 
-       spin_unlock(&sky2->tx_lock);
+       spin_unlock_bh(&sky2->tx_lock);
 }
 #endif
 
@@ -1080,7 +1078,7 @@ static inline int tx_avail(const struct sky2_port *sky2)
 }
 
 /* Estimate of number of transmit list elements required */
-static inline unsigned tx_le_req(const struct sk_buff *skb)
+static unsigned tx_le_req(const struct sk_buff *skb)
 {
        unsigned count;
 
@@ -1116,6 +1114,10 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
        u16 mss;
        u8 ctrl;
 
+       /* No BH disabling for tx_lock here.  We are running in BH disabled
+        * context and TX reclaim runs via poll inside of a software
+        * interrupt, and no related locks in IRQ processing.
+        */
        if (!spin_trylock(&sky2->tx_lock))
                return NETDEV_TX_LOCKED;
 
@@ -1308,17 +1310,17 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done)
                dev_kfree_skb_any(skb);
        }
 
-       spin_lock(&sky2->tx_lock);
        sky2->tx_cons = put;
        if (netif_queue_stopped(dev) && tx_avail(sky2) > MAX_SKB_TX_LE)
                netif_wake_queue(dev);
-       spin_unlock(&sky2->tx_lock);
 }
 
 /* Cleanup all untransmitted buffers, assume transmitter not running */
 static void sky2_tx_clean(struct sky2_port *sky2)
 {
+       spin_lock_bh(&sky2->tx_lock);
        sky2_tx_complete(sky2, sky2->tx_prod);
+       spin_unlock_bh(&sky2->tx_lock);
 }
 
 /* Network shutdown */
@@ -1608,28 +1610,40 @@ out:
        local_irq_enable();
 }
 
+
+/* Transmit timeout is only called if we are running, carries is up
+ * and tx queue is full (stopped).
+ */
 static void sky2_tx_timeout(struct net_device *dev)
 {
        struct sky2_port *sky2 = netdev_priv(dev);
        struct sky2_hw *hw = sky2->hw;
        unsigned txq = txqaddr[sky2->port];
+       u16 ridx;
+
+       /* Maybe we just missed an status interrupt */
+       spin_lock(&sky2->tx_lock);
+       ridx = sky2_read16(hw,
+                          sky2->port == 0 ? STAT_TXA1_RIDX : STAT_TXA2_RIDX);
+       sky2_tx_complete(sky2, ridx);
+       spin_unlock(&sky2->tx_lock);
+
+       if (!netif_queue_stopped(dev)) {
+               if (net_ratelimit())
+                       pr_info(PFX "transmit interrupt missed? recovered\n");
+               return;
+       }
 
        if (netif_msg_timer(sky2))
                printk(KERN_ERR PFX "%s: tx timeout\n", dev->name);
 
-       netif_stop_queue(dev);
-
        sky2_write32(hw, Q_ADDR(txq, Q_CSR), BMU_STOP);
-       sky2_read32(hw, Q_ADDR(txq, Q_CSR));
-
        sky2_write32(hw, Y2_QADDR(txq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET);
 
        sky2_tx_clean(sky2);
 
        sky2_qset(hw, txq);
        sky2_prefetch_init(hw, txq, sky2->tx_le_map, TX_RING_SIZE - 1);
-
-       netif_wake_queue(dev);
 }
 
 
@@ -1792,13 +1806,16 @@ error:
  */
 #define TX_NO_STATUS   0xffff
 
-static inline void sky2_tx_check(struct sky2_hw *hw, int port, u16 last)
+static void sky2_tx_check(struct sky2_hw *hw, int port, u16 last)
 {
        if (last != TX_NO_STATUS) {
                struct net_device *dev = hw->dev[port];
                if (dev && netif_running(dev)) {
                        struct sky2_port *sky2 = netdev_priv(dev);
+
+                       spin_lock(&sky2->tx_lock);
                        sky2_tx_complete(sky2, last);
+                       spin_unlock(&sky2->tx_lock);
                }
        }
 }
@@ -1815,6 +1832,8 @@ static int sky2_poll(struct net_device *dev0, int *budget)
        u16 hwidx;
        u16 tx_done[2] = { TX_NO_STATUS, TX_NO_STATUS };
 
+       sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ);
+
        hwidx = sky2_read16(hw, STAT_PUT_IDX);
        BUG_ON(hwidx >= STATUS_RING_SIZE);
        rmb();
@@ -1894,12 +1913,10 @@ static int sky2_poll(struct net_device *dev0, int *budget)
        }
 
 exit_loop:
-       sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ);
-
        sky2_tx_check(hw, 0, tx_done[0]);
        sky2_tx_check(hw, 1, tx_done[1]);
 
-       if (sky2_read16(hw, STAT_PUT_IDX) == hw->st_idx) {
+       if (likely(work_done < to_do)) {
                /* need to restart TX timer */
                if (is_ec_a1(hw)) {
                        sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_STOP);
@@ -2122,14 +2139,12 @@ static inline u32 sky2_clk2us(const struct sky2_hw *hw, u32 clk)
 
 static int sky2_reset(struct sky2_hw *hw)
 {
-       u32 ctst;
        u16 status;
        u8 t8, pmd_type;
-       int i;
-
-       ctst = sky2_read32(hw, B0_CTST);
+       int i, err;
 
        sky2_write8(hw, B0_CTST, CS_RST_CLR);
+
        hw->chip_id = sky2_read8(hw, B2_CHIP_ID);
        if (hw->chip_id < CHIP_ID_YUKON_XL || hw->chip_id > CHIP_ID_YUKON_FE) {
                printk(KERN_ERR PFX "%s: unsupported chip type 0x%x\n",
@@ -2137,12 +2152,6 @@ static int sky2_reset(struct sky2_hw *hw)
                return -EOPNOTSUPP;
        }
 
-       /* ring for status responses */
-       hw->st_le = pci_alloc_consistent(hw->pdev, STATUS_LE_BYTES,
-                                        &hw->st_dma);
-       if (!hw->st_le)
-               return -ENOMEM;
-
        /* disable ASF */
        if (hw->chip_id <= CHIP_ID_YUKON_EC) {
                sky2_write8(hw, B28_Y2_ASF_STAT_CMD, Y2_ASF_RESET);
@@ -2154,19 +2163,24 @@ static int sky2_reset(struct sky2_hw *hw)
        sky2_write8(hw, B0_CTST, CS_RST_CLR);
 
        /* clear PCI errors, if any */
-       pci_read_config_word(hw->pdev, PCI_STATUS, &status);
+       err = pci_read_config_word(hw->pdev, PCI_STATUS, &status);
+       if (err)
+               goto pci_err;
+
        sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
-       pci_write_config_word(hw->pdev, PCI_STATUS,
-                             status | PCI_STATUS_ERROR_BITS);
+       err = pci_write_config_word(hw->pdev, PCI_STATUS,
+                                   status | PCI_STATUS_ERROR_BITS);
+       if (err)
+               goto pci_err;
 
        sky2_write8(hw, B0_CTST, CS_MRST_CLR);
 
        /* clear any PEX errors */
-       if (is_pciex(hw)) {
-               u16 lstat;
-               pci_write_config_dword(hw->pdev, PEX_UNC_ERR_STAT,
-                                      0xffffffffUL);
-               pci_read_config_word(hw->pdev, PEX_LNK_STAT, &lstat);
+       if (pci_find_capability(hw->pdev, PCI_CAP_ID_EXP)) {
+               err = pci_write_config_dword(hw->pdev, PEX_UNC_ERR_STAT,
+                                                0xffffffffUL);
+               if (err)
+                       goto pci_err;
        }
 
        pmd_type = sky2_read8(hw, B2_PMD_TYP);
@@ -2278,9 +2292,17 @@ static int sky2_reset(struct sky2_hw *hw)
        sky2_write8(hw, STAT_ISR_TIMER_CTRL, TIM_START);
 
        return 0;
+
+pci_err:
+       /* This is to catch a BIOS bug workaround where
+        * mmconfig table doesn't have other buses.
+        */
+       printk(KERN_ERR PFX "%s: can't access PCI config space\n",
+              pci_name(hw->pdev));
+       return err;
 }
 
-static inline u32 sky2_supported_modes(const struct sky2_hw *hw)
+static u32 sky2_supported_modes(const struct sky2_hw *hw)
 {
        u32 modes;
        if (hw->copper) {
@@ -2532,19 +2554,24 @@ static struct net_device_stats *sky2_get_stats(struct net_device *dev)
 static int sky2_set_mac_address(struct net_device *dev, void *p)
 {
        struct sky2_port *sky2 = netdev_priv(dev);
-       struct sockaddr *addr = p;
+       struct sky2_hw *hw = sky2->hw;
+       unsigned port = sky2->port;
+       const struct sockaddr *addr = p;
 
        if (!is_valid_ether_addr(addr->sa_data))
                return -EADDRNOTAVAIL;
 
        memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
-       memcpy_toio(sky2->hw->regs + B2_MAC_1 + sky2->port * 8,
+       memcpy_toio(hw->regs + B2_MAC_1 + port * 8,
                    dev->dev_addr, ETH_ALEN);
-       memcpy_toio(sky2->hw->regs + B2_MAC_2 + sky2->port * 8,
+       memcpy_toio(hw->regs + B2_MAC_2 + port * 8,
                    dev->dev_addr, ETH_ALEN);
 
-       if (netif_running(dev))
-               sky2_phy_reinit(sky2);
+       /* virtual address for data */
+       gma_set_addr(hw, port, GM_SRC_ADDR_2L, dev->dev_addr);
+
+       /* physical address: used for pause frames */
+       gma_set_addr(hw, port, GM_SRC_ADDR_1L, dev->dev_addr);
 
        return 0;
 }
@@ -2824,7 +2851,7 @@ static int sky2_set_coalesce(struct net_device *dev,
        if (ecmd->rx_coalesce_usecs_irq == 0)
                sky2_write8(hw, STAT_ISR_TIMER_CTRL, TIM_STOP);
        else {
-               sky2_write32(hw, STAT_TX_TIMER_INI,
+               sky2_write32(hw, STAT_ISR_TIMER_INI,
                             sky2_us2clk(hw, ecmd->rx_coalesce_usecs_irq));
                sky2_write8(hw, STAT_ISR_TIMER_CTRL, TIM_START);
        }
@@ -3025,7 +3052,7 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw,
        return dev;
 }
 
-static inline void sky2_show_addr(struct net_device *dev)
+static void __devinit sky2_show_addr(struct net_device *dev)
 {
        const struct sky2_port *sky2 = netdev_priv(dev);
 
@@ -3036,6 +3063,61 @@ static inline void sky2_show_addr(struct net_device *dev)
                       dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
 }
 
+/* Handle software interrupt used during MSI test */
+static irqreturn_t __devinit sky2_test_intr(int irq, void *dev_id,
+                                           struct pt_regs *regs)
+{
+       struct sky2_hw *hw = dev_id;
+       u32 status = sky2_read32(hw, B0_Y2_SP_ISRC2);
+
+       if (status == 0)
+               return IRQ_NONE;
+
+       if (status & Y2_IS_IRQ_SW) {
+               sky2_write8(hw, B0_CTST, CS_CL_SW_IRQ);
+               hw->msi = 1;
+       }
+       sky2_write32(hw, B0_Y2_SP_ICR, 2);
+
+       sky2_read32(hw, B0_IMSK);
+       return IRQ_HANDLED;
+}
+
+/* Test interrupt path by forcing a a software IRQ */
+static int __devinit sky2_test_msi(struct sky2_hw *hw)
+{
+       struct pci_dev *pdev = hw->pdev;
+       int i, err;
+
+       sky2_write32(hw, B0_IMSK, Y2_IS_IRQ_SW);
+
+       err = request_irq(pdev->irq, sky2_test_intr, SA_SHIRQ, DRV_NAME, hw);
+       if (err) {
+               printk(KERN_ERR PFX "%s: cannot assign irq %d\n",
+                      pci_name(pdev), pdev->irq);
+               return err;
+       }
+
+       sky2_write8(hw, B0_CTST, CS_ST_SW_IRQ);
+       wmb();
+
+       for (i = 0; i < 10; i++) {
+               barrier();
+               if (hw->msi)
+                       goto found;
+               mdelay(1);
+       }
+
+       err = -EOPNOTSUPP;
+       sky2_write8(hw, B0_CTST, CS_CL_SW_IRQ);
+ found:
+       sky2_write32(hw, B0_IMSK, 0);
+
+       free_irq(pdev->irq, hw);
+
+       return err;
+}
+
 static int __devinit sky2_probe(struct pci_dev *pdev,
                                const struct pci_device_id *ent)
 {
@@ -3099,14 +3181,13 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
 #endif
 
        err = -ENOMEM;
-       hw = kmalloc(sizeof(*hw), GFP_KERNEL);
+       hw = kzalloc(sizeof(*hw), GFP_KERNEL);
        if (!hw) {
                printk(KERN_ERR PFX "%s: cannot allocate hardware struct\n",
                       pci_name(pdev));
                goto err_out_free_regions;
        }
 
-       memset(hw, 0, sizeof(*hw));
        hw->pdev = pdev;
 
        hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000);
@@ -3117,6 +3198,12 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
        }
        hw->pm_cap = pm_cap;
 
+       /* ring for status responses */
+       hw->st_le = pci_alloc_consistent(hw->pdev, STATUS_LE_BYTES,
+                                        &hw->st_dma);
+       if (!hw->st_le)
+               goto err_out_iounmap;
+
        err = sky2_reset(hw);
        if (err)
                goto err_out_iounmap;
@@ -3151,7 +3238,22 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
                }
        }
 
-       err = request_irq(pdev->irq, sky2_intr, SA_SHIRQ, DRV_NAME, hw);
+       if (!disable_msi && pci_enable_msi(pdev) == 0) {
+               err = sky2_test_msi(hw);
+               if (err == -EOPNOTSUPP) {
+                       /* MSI test failed, go back to INTx mode */
+                       printk(KERN_WARNING PFX "%s: No interrupt was generated using MSI, "
+                              "switching to INTx mode. Please report this failure to "
+                              "the PCI maintainer and include system chipset information.\n",
+                              pci_name(pdev));
+                       pci_disable_msi(pdev);
+               }
+               else if (err)
+                       goto err_out_unregister;
+       }
+
+       err = request_irq(pdev->irq, sky2_intr, SA_SHIRQ | SA_SAMPLE_RANDOM,
+                         DRV_NAME, hw);
        if (err) {
                printk(KERN_ERR PFX "%s: cannot assign irq %d\n",
                       pci_name(pdev), pdev->irq);
@@ -3166,6 +3268,8 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
        return 0;
 
 err_out_unregister:
+       if (hw->msi)
+               pci_disable_msi(pdev);
        if (dev1) {
                unregister_netdev(dev1);
                free_netdev(dev1);
@@ -3208,6 +3312,8 @@ static void __devexit sky2_remove(struct pci_dev *pdev)
        sky2_read8(hw, B0_CTST);
 
        free_irq(pdev->irq, hw);
+       if (hw->msi)
+               pci_disable_msi(pdev);
        pci_free_consistent(pdev, STATUS_LE_BYTES, hw->st_le, hw->st_dma);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
@@ -3245,25 +3351,33 @@ static int sky2_suspend(struct pci_dev *pdev, pm_message_t state)
 static int sky2_resume(struct pci_dev *pdev)
 {
        struct sky2_hw *hw = pci_get_drvdata(pdev);
-       int i;
+       int i, err;
 
        pci_restore_state(pdev);
        pci_enable_wake(pdev, PCI_D0, 0);
-       sky2_set_power_state(hw, PCI_D0);
+       err = sky2_set_power_state(hw, PCI_D0);
+       if (err)
+               goto out;
 
-       sky2_reset(hw);
+       err = sky2_reset(hw);
+       if (err)
+               goto out;
 
        for (i = 0; i < 2; i++) {
                struct net_device *dev = hw->dev[i];
-               if (dev) {
-                       if (netif_running(dev)) {
-                               netif_device_attach(dev);
-                               if (sky2_up(dev))
-                                       dev_close(dev);
+               if (dev && netif_running(dev)) {
+                       netif_device_attach(dev);
+                       err = sky2_up(dev);
+                       if (err) {
+                               printk(KERN_ERR PFX "%s: could not up: %d\n",
+                                      dev->name, err);
+                               dev_close(dev);
+                               break;
                        }
                }
        }
-       return 0;
+out:
+       return err;
 }
 #endif