It is currently limited to 0x8136 and 0x8168. 8169sb/8110sb ought to
handle it as well where they support MSI.

Includes unregister_netdev() fix from Bernhard Walle <[EMAIL PROTECTED]>
against BUG_ON(irq_has_action(dev->first_msi_irq)) (2007/02/24).

Signed-off-by: Francois Romieu <[EMAIL PROTECTED]>
Fixed-by: Bernhard Walle <[EMAIL PROTECTED]>
Cc: Edward Hsu <[EMAIL PROTECTED]>
---
 drivers/net/r8169.c |  102 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 97 insertions(+), 5 deletions(-)

diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c
index 7a0c5e7..e6dbfc9 100644
--- a/drivers/net/r8169.c
+++ b/drivers/net/r8169.c
@@ -250,6 +250,9 @@ enum rtl_register_content {
        CmdTxEnb        = 0x04,
        RxBufEmpty      = 0x01,
 
+       /* TXPoll register p.5 */
+       FSWInt          = 1,            /* Forced software interrupt */
+
        /* Cfg9346Bits */
        Cfg9346_Lock    = 0x00,
        Cfg9346_Unlock  = 0xc0,
@@ -271,6 +274,7 @@ enum rtl_register_content {
        TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */
 
        /* Config1 register p.24 */
+       MSIEnable       = (1 << 5),     /* Enable Message Signaled Interrupt */
        PMEnable        = (1 << 0),     /* Power Management Enable */
 
        /* Config2 register p. 25 */
@@ -414,6 +418,7 @@ struct rtl8169_private {
        unsigned int (*link_ok)(void __iomem *);
        struct delayed_work task;
        unsigned wol_enabled : 1;
+       unsigned msi : 1;
 };
 
 MODULE_AUTHOR("Realtek and the Linux r8169 crew <netdev@vger.kernel.org>");
@@ -1410,12 +1415,85 @@ static int rtl8169_ioctl(struct net_device *dev, struct 
ifreq *ifr, int cmd)
        return -EOPNOTSUPP;
 }
 
+static irqreturn_t __devinit rtl_test_intr(int irq, void *dev_instance)
+{
+       struct rtl8169_private *tp = dev_instance;
+       void __iomem *ioaddr = tp->mmio_addr;
+       u32 status;
+
+       status = RTL_R16(IntrStatus);
+       if ((status == 0xffff) || !status)
+               return IRQ_NONE;
+
+       if (status & SWInt) {
+               tp->msi = 1;
+               smp_wmb();
+               RTL_W16(IntrStatus, SWInt);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int __devinit rtl_try_msi(struct rtl8169_private *tp)
+{
+       struct pci_dev *pdev = tp->pci_dev;
+       void __iomem *ioaddr = tp->mmio_addr;
+       unsigned int i;
+       int rc;
+
+       rc = pci_enable_msi(pdev);
+       if (rc < 0)
+               return 0;
+
+       RTL_W8(Config2, RTL_R8(Config2) | MSIEnable);
+
+       rc = request_irq(pdev->irq, rtl_test_intr, 0, pci_name(pdev), tp);
+       if (rc < 0)
+               goto err_disable_msi_0;
+
+       RTL_W16(IntrMask, SWInt);
+       RTL_W8(TxPoll, FSWInt);
+       /* Commit */
+       RTL_R8(IntrMask);
+
+       for (i = 0; i < 100; i++) {
+               smp_rmb();
+               if (tp->msi)
+                       break;
+               msleep(10);
+       }
+
+       RTL_W16(IntrStatus, SWInt);
+       RTL_W16(IntrMask, 0);
+       RTL_R8(IntrMask);
+
+       free_irq(pdev->irq, tp);
+
+       smp_rmb();
+
+       if (!tp->msi && netif_msg_drv(tp))
+               goto err_status_ok_1;
+
+       tp->dev->irq = pdev->irq;
+out:
+       return rc;
+
+err_status_ok_1:
+       printk(KERN_INFO "%s: no MSI. Back to INTx.\n", pci_name(pdev));
+       rc = 0;
+err_disable_msi_0:
+       RTL_W8(Config2, RTL_R8(Config2) & ~MSIEnable);
+       pci_disable_msi(pdev);
+       goto out;
+}
+
 static const struct rtl_cfg_info {
        void (*hw_start)(struct net_device *);
        unsigned int region;
        unsigned int align;
        u16 intr_event;
        u16 napi_event;
+       unsigned msi : 1;
 } rtl_cfg_infos [] = {
        [RTL_CFG_0] = {
                .hw_start       = rtl_hw_start_8169,
@@ -1423,7 +1501,8 @@ static const struct rtl_cfg_info {
                .align          = 2,
                .intr_event     = SYSErr | LinkChg | RxOverflow |
                                  RxFIFOOver | TxErr | TxOK | RxOK | RxErr,
-               .napi_event     = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow
+               .napi_event     = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow,
+               .msi            = 0
        },
        [RTL_CFG_1] = {
                .hw_start       = rtl_hw_start_8168,
@@ -1431,7 +1510,8 @@ static const struct rtl_cfg_info {
                .align          = 8,
                .intr_event     = SYSErr | LinkChg | RxOverflow |
                                  TxErr | TxOK | RxOK | RxErr,
-               .napi_event     = TxErr | TxOK | RxOK | RxOverflow
+               .napi_event     = TxErr | TxOK | RxOK | RxOverflow,
+               .msi            = 1
        },
        [RTL_CFG_2] = {
                .hw_start       = rtl_hw_start_8101,
@@ -1439,7 +1519,8 @@ static const struct rtl_cfg_info {
                .align          = 8,
                .intr_event     = SYSErr | LinkChg | RxOverflow | PCSTimeout |
                                  RxFIFOOver | TxErr | TxOK | RxOK | RxErr,
-               .napi_event     = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow
+               .napi_event     = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow,
+               .msi            = 1
        }
 };
 
@@ -1663,6 +1744,12 @@ rtl8169_init_one(struct pci_dev *pdev, const struct 
pci_device_id *ent)
        if (rc < 0)
                goto err_out_unmap_5;
 
+       if (cfg->msi) {
+               rc = rtl_try_msi(tp);
+               if (rc < 0)
+                       goto err_out_unregister_netdev_6;
+       }
+
        pci_set_drvdata(pdev, dev);
 
        if (netif_msg_probe(tp)) {
@@ -1682,6 +1769,8 @@ rtl8169_init_one(struct pci_dev *pdev, const struct 
pci_device_id *ent)
 out:
        return rc;
 
+err_out_unregister_netdev_6:
+       unregister_netdev(dev);
 err_out_unmap_5:
        iounmap(ioaddr);
 err_out_free_res_4:
@@ -1703,6 +1792,9 @@ static void __devexit rtl8169_remove_one(struct pci_dev 
*pdev)
        flush_scheduled_work();
 
        unregister_netdev(dev);
+       if (tp->msi)
+               pci_disable_msi(pdev);
+       tp->msi = 0;
        rtl8169_release_board(pdev, dev, tp->mmio_addr);
        pci_set_drvdata(pdev, NULL);
 }
@@ -1746,8 +1838,8 @@ static int rtl8169_open(struct net_device *dev)
 
        smp_mb();
 
-       retval = request_irq(dev->irq, rtl8169_interrupt, IRQF_SHARED,
-                            dev->name, dev);
+       retval = request_irq(dev->irq, rtl8169_interrupt,
+                            tp->msi ? 0 : IRQF_SHARED, dev->name, dev);
        if (retval < 0)
                goto err_release_ring_2;
 
-- 
1.4.4.2
-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to