rtlwifi: add MSI interrupts mode support
authorAdam Lee <adam.lee@canonical.com>
Fri, 28 Mar 2014 03:36:18 +0000 (11:36 +0800)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 31 Mar 2014 17:47:41 +0000 (13:47 -0400)
Add MSI interrupts mode support, enable it when submodules' msi_support
flag is true, also could fallback to pin-based interrupts mode if MSI
interrupts mode fails.

RealTek's policy(on modules which work well with MSI interrupts mode) is:

> If the platform supports both MSI and pin-based, use MSI.
> If the platform supports MSI only, use MSI.
> If the platform supports pin-based only, use pin-based.

Also as RealTek's testing results, RTL8188EE and RTL8723BE work well
with both MSI mode and pin-based mode fallback.

Signed-off-by: Adam Lee <adam.lee@canonical.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/rtlwifi/pci.c

index 7d711708d2f367a7766b8c8e7825a6bed72c62e5..dae55257f0e8bde44fb772173f7d573fceab6d65 100644 (file)
@@ -1853,6 +1853,65 @@ static bool _rtl_pci_find_adapter(struct pci_dev *pdev,
        return true;
 }
 
+static int rtl_pci_intr_mode_msi(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+       struct rtl_pci *rtlpci = rtl_pcidev(pcipriv);
+       int ret;
+
+       ret = pci_enable_msi(rtlpci->pdev);
+       if (ret < 0)
+               return ret;
+
+       ret = request_irq(rtlpci->pdev->irq, &_rtl_pci_interrupt,
+                         IRQF_SHARED, KBUILD_MODNAME, hw);
+       if (ret < 0) {
+               pci_disable_msi(rtlpci->pdev);
+               return ret;
+       }
+
+       rtlpci->using_msi = true;
+
+       RT_TRACE(rtlpriv, COMP_INIT|COMP_INTR, DBG_DMESG,
+                "MSI Interrupt Mode!\n");
+       return 0;
+}
+
+static int rtl_pci_intr_mode_legacy(struct ieee80211_hw *hw)
+{
+       struct rtl_priv *rtlpriv = rtl_priv(hw);
+       struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+       struct rtl_pci *rtlpci = rtl_pcidev(pcipriv);
+       int ret;
+
+       ret = request_irq(rtlpci->pdev->irq, &_rtl_pci_interrupt,
+                         IRQF_SHARED, KBUILD_MODNAME, hw);
+       if (ret < 0)
+               return ret;
+
+       rtlpci->using_msi = false;
+       RT_TRACE(rtlpriv, COMP_INIT|COMP_INTR, DBG_DMESG,
+                "Pin-based Interrupt Mode!\n");
+       return 0;
+}
+
+static int rtl_pci_intr_mode_decide(struct ieee80211_hw *hw)
+{
+       struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+       struct rtl_pci *rtlpci = rtl_pcidev(pcipriv);
+       int ret;
+
+       if (rtlpci->msi_support) {
+               ret = rtl_pci_intr_mode_msi(hw);
+               if (ret < 0)
+                       ret = rtl_pci_intr_mode_legacy(hw);
+       } else {
+               ret = rtl_pci_intr_mode_legacy(hw);
+       }
+       return ret;
+}
+
 int rtl_pci_probe(struct pci_dev *pdev,
                            const struct pci_device_id *id)
 {
@@ -1995,8 +2054,7 @@ int rtl_pci_probe(struct pci_dev *pdev,
        }
 
        rtlpci = rtl_pcidev(pcipriv);
-       err = request_irq(rtlpci->pdev->irq, &_rtl_pci_interrupt,
-                         IRQF_SHARED, KBUILD_MODNAME, hw);
+       err = rtl_pci_intr_mode_decide(hw);
        if (err) {
                RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
                         "%s: failed to register IRQ handler\n",
@@ -2064,6 +2122,9 @@ void rtl_pci_disconnect(struct pci_dev *pdev)
                rtlpci->irq_alloc = 0;
        }
 
+       if (rtlpci->using_msi)
+               pci_disable_msi(rtlpci->pdev);
+
        list_del(&rtlpriv->list);
        if (rtlpriv->io.pci_mem_start != 0) {
                pci_iounmap(pdev, (void __iomem *)rtlpriv->io.pci_mem_start);