ath10k: split up pci irq code
authorMichal Kazior <michal.kazior@tieto.com>
Mon, 25 Nov 2013 13:06:21 +0000 (14:06 +0100)
committerKalle Valo <kvalo@qca.qualcomm.com>
Wed, 27 Nov 2013 14:45:32 +0000 (16:45 +0200)
Hardware waits until host signals whether it has
chosen MSI(-X) or shared legacy interrupts. It is
not required for the driver to register interrupt
handlers immediately.

This patch prepares the pci irq code for more
changes.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/pci.c

index 12fb12e..957fc59 100644 (file)
@@ -55,8 +55,10 @@ static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info);
 static void ath10k_pci_stop_ce(struct ath10k *ar);
 static int ath10k_pci_device_reset(struct ath10k *ar);
 static int ath10k_pci_wait_for_target_init(struct ath10k *ar);
-static int ath10k_pci_start_intr(struct ath10k *ar);
-static void ath10k_pci_stop_intr(struct ath10k *ar);
+static int ath10k_pci_init_irq(struct ath10k *ar);
+static int ath10k_pci_deinit_irq(struct ath10k *ar);
+static int ath10k_pci_request_irq(struct ath10k *ar);
+static void ath10k_pci_free_irq(struct ath10k *ar);
 
 static const struct ce_attr host_ce_config_wlan[] = {
        /* CE0: host->target HTC control and raw streams */
@@ -1354,7 +1356,7 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
        ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
 
        /* Irqs are never explicitly re-enabled. They are implicitly re-enabled
-        * by ath10k_pci_start_intr(). */
+        * by upon power_up. */
        ath10k_pci_disable_irqs(ar);
 
        ath10k_pci_stop_ce(ar);
@@ -1900,34 +1902,40 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar)
                goto err_ce;
        }
 
-       ret = ath10k_pci_start_intr(ar);
+       ret = ath10k_pci_init_irq(ar);
        if (ret) {
-               ath10k_err("failed to start interrupt handling: %d\n", ret);
+               ath10k_err("failed to init irqs: %d\n", ret);
                goto err_ce;
        }
 
+       ret = ath10k_pci_request_irq(ar);
+       if (ret) {
+               ath10k_err("failed to request irqs: %d\n", ret);
+               goto err_deinit_irq;
+       }
+
        ret = ath10k_pci_wait_for_target_init(ar);
        if (ret) {
                ath10k_err("failed to wait for target to init: %d\n", ret);
-               goto err_irq;
+               goto err_free_irq;
        }
 
        ret = ath10k_ce_enable_err_irq(ar);
        if (ret) {
                ath10k_err("failed to enable CE error irq: %d\n", ret);
-               goto err_irq;
+               goto err_free_irq;
        }
 
        ret = ath10k_pci_init_config(ar);
        if (ret) {
                ath10k_err("failed to setup init config: %d\n", ret);
-               goto err_irq;
+               goto err_free_irq;
        }
 
        ret = ath10k_pci_wake_target_cpu(ar);
        if (ret) {
                ath10k_err("could not wake up target CPU: %d\n", ret);
-               goto err_irq;
+               goto err_free_irq;
        }
 
        ath10k_pci_start_bmi(ar);
@@ -1944,11 +1952,12 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar)
 
        return 0;
 
-err_irq:
-       ath10k_ce_disable_interrupts(ar);
-       ath10k_pci_stop_intr(ar);
+err_free_irq:
+       ath10k_pci_free_irq(ar);
        ath10k_pci_kill_tasklet(ar);
        ath10k_pci_device_reset(ar);
+err_deinit_irq:
+       ath10k_pci_deinit_irq(ar);
 err_ce:
        ath10k_pci_ce_deinit(ar);
 err_ps:
@@ -1962,7 +1971,8 @@ static void ath10k_pci_hif_power_down(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 
-       ath10k_pci_stop_intr(ar);
+       ath10k_pci_free_irq(ar);
+       ath10k_pci_deinit_irq(ar);
        ath10k_pci_device_reset(ar);
 
        ath10k_pci_ce_deinit(ar);
@@ -2152,24 +2162,17 @@ static void ath10k_pci_tasklet(unsigned long data)
        }
 }
 
-static int ath10k_pci_start_intr_msix(struct ath10k *ar, int num)
+static int ath10k_pci_request_irq_msix(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       int ret;
-       int i;
-
-       ret = pci_enable_msi_block(ar_pci->pdev, num);
-       if (ret)
-               return ret;
+       int ret, i;
 
        ret = request_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW,
                          ath10k_pci_msi_fw_handler,
                          IRQF_SHARED, "ath10k_pci", ar);
        if (ret) {
-               ath10k_warn("request_irq(%d) failed %d\n",
+               ath10k_warn("failed to request MSI-X fw irq %d: %d\n",
                            ar_pci->pdev->irq + MSI_ASSIGN_FW, ret);
-
-               pci_disable_msi(ar_pci->pdev);
                return ret;
        }
 
@@ -2178,45 +2181,38 @@ static int ath10k_pci_start_intr_msix(struct ath10k *ar, int num)
                                  ath10k_pci_per_engine_handler,
                                  IRQF_SHARED, "ath10k_pci", ar);
                if (ret) {
-                       ath10k_warn("request_irq(%d) failed %d\n",
+                       ath10k_warn("failed to request MSI-X ce irq %d: %d\n",
                                    ar_pci->pdev->irq + i, ret);
 
                        for (i--; i >= MSI_ASSIGN_CE_INITIAL; i--)
                                free_irq(ar_pci->pdev->irq + i, ar);
 
                        free_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW, ar);
-                       pci_disable_msi(ar_pci->pdev);
                        return ret;
                }
        }
 
-       ath10k_dbg(ATH10K_DBG_BOOT,
-                  "MSI-X interrupt handling (%d intrs)\n", num);
        return 0;
 }
 
-static int ath10k_pci_start_intr_msi(struct ath10k *ar)
+static int ath10k_pci_request_irq_msi(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        int ret;
 
-       ret = pci_enable_msi(ar_pci->pdev);
-       if (ret < 0)
-               return ret;
-
        ret = request_irq(ar_pci->pdev->irq,
                          ath10k_pci_interrupt_handler,
                          IRQF_SHARED, "ath10k_pci", ar);
-       if (ret < 0) {
-               pci_disable_msi(ar_pci->pdev);
+       if (ret) {
+               ath10k_warn("failed to request MSI irq %d: %d\n",
+                           ar_pci->pdev->irq, ret);
                return ret;
        }
 
-       ath10k_dbg(ATH10K_DBG_BOOT, "MSI interrupt handling\n");
        return 0;
 }
 
-static int ath10k_pci_start_intr_legacy(struct ath10k *ar)
+static int ath10k_pci_request_irq_legacy(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        int ret;
@@ -2224,99 +2220,140 @@ static int ath10k_pci_start_intr_legacy(struct ath10k *ar)
        ret = request_irq(ar_pci->pdev->irq,
                          ath10k_pci_interrupt_handler,
                          IRQF_SHARED, "ath10k_pci", ar);
-       if (ret < 0)
-               return ret;
-
-       ret = ath10k_pci_wake(ar);
        if (ret) {
-               free_irq(ar_pci->pdev->irq, ar);
-               ath10k_err("failed to wake up target: %d\n", ret);
+               ath10k_warn("failed to request legacy irq %d: %d\n",
+                           ar_pci->pdev->irq, ret);
                return ret;
        }
 
-       /*
-        * A potential race occurs here: The CORE_BASE write
-        * depends on target correctly decoding AXI address but
-        * host won't know when target writes BAR to CORE_CTRL.
-        * This write might get lost if target has NOT written BAR.
-        * For now, fix the race by repeating the write in below
-        * synchronization checking.
-        */
-       iowrite32(PCIE_INTR_FIRMWARE_MASK |
-                 PCIE_INTR_CE_MASK_ALL,
-                 ar_pci->mem + (SOC_CORE_BASE_ADDRESS |
-                                PCIE_INTR_ENABLE_ADDRESS));
-
-       ath10k_pci_sleep(ar);
-       ath10k_dbg(ATH10K_DBG_BOOT, "legacy interrupt handling\n");
        return 0;
 }
 
-static int ath10k_pci_start_intr(struct ath10k *ar)
+static int ath10k_pci_request_irq(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+       switch (ar_pci->num_msi_intrs) {
+       case 0:
+               return ath10k_pci_request_irq_legacy(ar);
+       case 1:
+               return ath10k_pci_request_irq_msi(ar);
+       case MSI_NUM_REQUEST:
+               return ath10k_pci_request_irq_msix(ar);
+       }
+
+       ath10k_warn("unknown irq configuration upon request\n");
+       return -EINVAL;
+}
+
+static void ath10k_pci_free_irq(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       int num = MSI_NUM_REQUEST;
-       int ret;
        int i;
 
-       tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long) ar);
+       /* There's at least one interrupt irregardless whether its legacy INTR
+        * or MSI or MSI-X */
+       for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++)
+               free_irq(ar_pci->pdev->irq + i, ar);
+}
+
+static void ath10k_pci_init_irq_tasklets(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int i;
+
+       tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long)ar);
        tasklet_init(&ar_pci->msi_fw_err, ath10k_msi_err_tasklet,
-                    (unsigned long) ar);
+                    (unsigned long)ar);
 
        for (i = 0; i < CE_COUNT; i++) {
                ar_pci->pipe_info[i].ar_pci = ar_pci;
-               tasklet_init(&ar_pci->pipe_info[i].intr,
-                            ath10k_pci_ce_tasklet,
+               tasklet_init(&ar_pci->pipe_info[i].intr, ath10k_pci_ce_tasklet,
                             (unsigned long)&ar_pci->pipe_info[i]);
        }
+}
+
+static int ath10k_pci_init_irq(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret;
+
+       ath10k_pci_init_irq_tasklets(ar);
 
        if (!test_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features))
-               num = 1;
+               goto msi;
 
-       if (num > 1) {
-               ret = ath10k_pci_start_intr_msix(ar, num);
-               if (ret == 0)
-                       goto exit;
+       /* Try MSI-X */
+       ar_pci->num_msi_intrs = MSI_NUM_REQUEST;
+       ret = pci_enable_msi_block(ar_pci->pdev, ar_pci->num_msi_intrs);
+       if (ret == 0)
+               return 0;
+       if (ret > 0)
+               pci_disable_msi(ar_pci->pdev);
 
-               ath10k_dbg(ATH10K_DBG_BOOT,
-                          "MSI-X didn't succeed (%d), trying MSI\n", ret);
-               num = 1;
-       }
+msi:
+       /* Try MSI */
+       ar_pci->num_msi_intrs = 1;
+       ret = pci_enable_msi(ar_pci->pdev);
+       if (ret == 0)
+               return 0;
 
-       if (num == 1) {
-               ret = ath10k_pci_start_intr_msi(ar);
-               if (ret == 0)
-                       goto exit;
+       /* Try legacy irq
+        *
+        * A potential race occurs here: The CORE_BASE write
+        * depends on target correctly decoding AXI address but
+        * host won't know when target writes BAR to CORE_CTRL.
+        * This write might get lost if target has NOT written BAR.
+        * For now, fix the race by repeating the write in below
+        * synchronization checking. */
+       ar_pci->num_msi_intrs = 0;
 
-               ath10k_dbg(ATH10K_DBG_BOOT,
-                          "MSI didn't succeed (%d), trying legacy INTR\n",
-                          ret);
-               num = 0;
+       ret = ath10k_pci_wake(ar);
+       if (ret) {
+               ath10k_warn("failed to wake target: %d\n", ret);
+               return ret;
        }
 
-       ret = ath10k_pci_start_intr_legacy(ar);
+       ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS,
+                          PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL);
+       ath10k_pci_sleep(ar);
+
+       return 0;
+}
+
+static int ath10k_pci_deinit_irq_legacy(struct ath10k *ar)
+{
+       int ret;
+
+       ret = ath10k_pci_wake(ar);
        if (ret) {
-               ath10k_warn("Failed to start legacy interrupts: %d\n", ret);
+               ath10k_warn("failed to wake target: %d\n", ret);
                return ret;
        }
 
-exit:
-       ar_pci->num_msi_intrs = num;
-       return ret;
+       ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS,
+                          0);
+       ath10k_pci_sleep(ar);
+
+       return 0;
 }
 
-static void ath10k_pci_stop_intr(struct ath10k *ar)
+static int ath10k_pci_deinit_irq(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-       int i;
 
-       /* There's at least one interrupt irregardless whether its legacy INTR
-        * or MSI or MSI-X */
-       for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++)
-               free_irq(ar_pci->pdev->irq + i, ar);
-
-       if (ar_pci->num_msi_intrs > 0)
+       switch (ar_pci->num_msi_intrs) {
+       case 0:
+               return ath10k_pci_deinit_irq_legacy(ar);
+       case 1:
+               /* fall-through */
+       case MSI_NUM_REQUEST:
                pci_disable_msi(ar_pci->pdev);
+               return 0;
+       }
+
+       ath10k_warn("unknown irq configuration upon deinit\n");
+       return -EINVAL;
 }
 
 static int ath10k_pci_wait_for_target_init(struct ath10k *ar)