ath11k: enable DP interrupt setup for QCA6390
authorCarl Huang <cjhuang@codeaurora.org>
Mon, 17 Aug 2020 10:31:52 +0000 (13:31 +0300)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 18 Aug 2020 09:46:47 +0000 (12:46 +0300)
QCA6390 uses MSI interrupt, so need to configure msi_add and
msi_data to dp srngs. As there are so many DP srngs, so need
to group them. Each group shares one MSI interrupt.

Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1
Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.1.0.1-01238-QCAHKSWPL_SILICONZ-2

Signed-off-by: Carl Huang <cjhuang@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/1597555891-26112-2-git-send-email-kvalo@codeaurora.org
drivers/net/wireless/ath/ath11k/core.c
drivers/net/wireless/ath/ath11k/dp.c
drivers/net/wireless/ath/ath11k/hw.c
drivers/net/wireless/ath/ath11k/hw.h
drivers/net/wireless/ath/ath11k/pci.c

index b946308..45d16f7 100644 (file)
@@ -49,7 +49,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
                .max_radios = 3,
                .bdf_addr = 0x4B0C0000,
                .hw_ops = &qca6390_ops,
-               .ring_mask = &ath11k_hw_ring_mask_ipq8074,
+               .ring_mask = &ath11k_hw_ring_mask_qca6390,
                .internal_sleep_clock = true,
                .regs = &qca6390_regs,
                .host_ce_config = ath11k_host_ce_config_qca6390,
index a3c4d36..ac92f34 100644 (file)
@@ -107,6 +107,113 @@ void ath11k_dp_srng_cleanup(struct ath11k_base *ab, struct dp_srng *ring)
        ring->vaddr_unaligned = NULL;
 }
 
+static int ath11k_dp_srng_find_ring_in_mask(int ring_num, const u8 *grp_mask)
+{
+       int ext_group_num;
+       u8 mask = 1 << ring_num;
+
+       for (ext_group_num = 0; ext_group_num < ATH11K_EXT_IRQ_GRP_NUM_MAX;
+            ext_group_num++) {
+               if (mask & grp_mask[ext_group_num])
+                       return ext_group_num;
+       }
+
+       return -ENOENT;
+}
+
+static int ath11k_dp_srng_calculate_msi_group(struct ath11k_base *ab,
+                                             enum hal_ring_type type, int ring_num)
+{
+       const u8 *grp_mask;
+
+       switch (type) {
+       case HAL_WBM2SW_RELEASE:
+               if (ring_num < 3) {
+                       grp_mask = &ab->hw_params.ring_mask->tx[0];
+               } else if (ring_num == 3) {
+                       grp_mask = &ab->hw_params.ring_mask->rx_wbm_rel[0];
+                       ring_num = 0;
+               } else {
+                       return -ENOENT;
+               }
+               break;
+       case HAL_REO_EXCEPTION:
+               grp_mask = &ab->hw_params.ring_mask->rx_err[0];
+               break;
+       case HAL_REO_DST:
+               grp_mask = &ab->hw_params.ring_mask->rx[0];
+               break;
+       case HAL_REO_STATUS:
+               grp_mask = &ab->hw_params.ring_mask->reo_status[0];
+               break;
+       case HAL_RXDMA_MONITOR_STATUS:
+       case HAL_RXDMA_MONITOR_DST:
+               grp_mask = &ab->hw_params.ring_mask->rx_mon_status[0];
+               break;
+       case HAL_RXDMA_DST:
+               grp_mask = &ab->hw_params.ring_mask->rxdma2host[0];
+               break;
+       case HAL_RXDMA_BUF:
+               grp_mask = &ab->hw_params.ring_mask->host2rxdma[0];
+               break;
+       case HAL_RXDMA_MONITOR_BUF:
+       case HAL_TCL_DATA:
+       case HAL_TCL_CMD:
+       case HAL_REO_CMD:
+       case HAL_SW2WBM_RELEASE:
+       case HAL_WBM_IDLE_LINK:
+       case HAL_TCL_STATUS:
+       case HAL_REO_REINJECT:
+       case HAL_CE_SRC:
+       case HAL_CE_DST:
+       case HAL_CE_DST_STATUS:
+       default:
+               return -ENOENT;
+       }
+
+       return ath11k_dp_srng_find_ring_in_mask(ring_num, grp_mask);
+}
+
+static void ath11k_dp_srng_msi_setup(struct ath11k_base *ab,
+                                    struct hal_srng_params *ring_params,
+                                    enum hal_ring_type type, int ring_num)
+{
+       int msi_group_number, msi_data_count;
+       u32 msi_data_start, msi_irq_start, addr_lo, addr_hi;
+       int ret;
+
+       ret = ath11k_get_user_msi_vector(ab, "DP",
+                                        &msi_data_count, &msi_data_start,
+                                        &msi_irq_start);
+       if (ret)
+               return;
+
+       msi_group_number = ath11k_dp_srng_calculate_msi_group(ab, type,
+                                                             ring_num);
+       if (msi_group_number < 0) {
+               ath11k_dbg(ab, ATH11K_DBG_PCI,
+                          "ring not part of an ext_group; ring_type: %d,ring_num %d",
+                          type, ring_num);
+               ring_params->msi_addr = 0;
+               ring_params->msi_data = 0;
+               return;
+       }
+
+       if (msi_group_number > msi_data_count) {
+               ath11k_dbg(ab, ATH11K_DBG_PCI,
+                          "multiple msi_groups share one msi, msi_group_num %d",
+                          msi_group_number);
+       }
+
+       ath11k_get_msi_address(ab, &addr_lo, &addr_hi);
+
+       ring_params->msi_addr = addr_lo;
+       ring_params->msi_addr |= (dma_addr_t)(((uint64_t)addr_hi) << 32);
+       ring_params->msi_data = (msi_group_number % msi_data_count)
+               + msi_data_start;
+       ring_params->flags |= HAL_SRNG_FLAGS_MSI_INTR;
+}
+
 int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring,
                         enum hal_ring_type type, int ring_num,
                         int mac_id, int num_entries)
@@ -136,6 +243,7 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring,
        params.ring_base_vaddr = ring->vaddr;
        params.ring_base_paddr = ring->paddr;
        params.num_entries = num_entries;
+       ath11k_dp_srng_msi_setup(ab, &params, type, ring_num + mac_id);
 
        switch (type) {
        case HAL_REO_DST:
index 338b784..9737b9f 100644 (file)
@@ -208,6 +208,43 @@ const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_ipq8074 = {
        },
 };
 
+const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qca6390 = {
+       .tx  = {
+               ATH11K_TX_RING_MASK_0,
+               ATH11K_TX_RING_MASK_1,
+               ATH11K_TX_RING_MASK_2,
+       },
+       .rx_mon_status = {
+               0, 0, 0, 0,
+               ATH11K_RX_MON_STATUS_RING_MASK_0,
+               ATH11K_RX_MON_STATUS_RING_MASK_1,
+               ATH11K_RX_MON_STATUS_RING_MASK_2,
+       },
+       .rx = {
+               0, 0, 0, 0, 0, 0, 0,
+               ATH11K_RX_RING_MASK_0,
+               ATH11K_RX_RING_MASK_1,
+               ATH11K_RX_RING_MASK_2,
+               ATH11K_RX_RING_MASK_3,
+       },
+       .rx_err = {
+               ATH11K_RX_ERR_RING_MASK_0,
+       },
+       .rx_wbm_rel = {
+               ATH11K_RX_WBM_REL_RING_MASK_0,
+       },
+       .reo_status = {
+               ATH11K_REO_STATUS_RING_MASK_0,
+       },
+       .rxdma2host = {
+               ATH11K_RXDMA2HOST_RING_MASK_0,
+               ATH11K_RXDMA2HOST_RING_MASK_1,
+               ATH11K_RXDMA2HOST_RING_MASK_2,
+       },
+       .host2rxdma = {
+       },
+};
+
 const struct ath11k_hw_regs ipq8074_regs = {
        /* SW2TCL(x) R0 ring configuration address */
        .hal_tcl1_ring_base_lsb = 0x00000510,
index 8db9534..1c91762 100644 (file)
@@ -158,6 +158,7 @@ extern const struct ath11k_hw_ops ipq6018_ops;
 extern const struct ath11k_hw_ops qca6390_ops;
 
 extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_ipq8074;
+extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qca6390;
 
 static inline
 int ath11k_hw_get_mac_from_pdev_id(struct ath11k_hw_params *hw,
index 2b5a8d3..6a1e74f 100644 (file)
@@ -389,6 +389,20 @@ static int ath11k_get_user_msi_assignment(struct ath11k_base *ab, char *user_nam
                                                  base_vector);
 }
 
+static void ath11k_pci_free_ext_irq(struct ath11k_base *ab)
+{
+       int i, j;
+
+       for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
+               struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
+
+               for (j = 0; j < irq_grp->num_irq; j++)
+                       free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp);
+
+               netif_napi_del(&irq_grp->napi);
+       }
+}
+
 static void ath11k_pci_free_irq(struct ath11k_base *ab)
 {
        int i, irq_idx;
@@ -399,6 +413,8 @@ static void ath11k_pci_free_irq(struct ath11k_base *ab)
                irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;
                free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]);
        }
+
+       ath11k_pci_free_ext_irq(ab);
 }
 
 static void ath11k_pci_ce_irq_enable(struct ath11k_base *ab, u16 ce_id)
@@ -461,6 +477,159 @@ static irqreturn_t ath11k_pci_ce_interrupt_handler(int irq, void *arg)
        return IRQ_HANDLED;
 }
 
+static void ath11k_pci_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp)
+{
+       int i;
+
+       for (i = 0; i < irq_grp->num_irq; i++)
+               disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
+}
+
+static void __ath11k_pci_ext_irq_disable(struct ath11k_base *sc)
+{
+       int i;
+
+       for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
+               struct ath11k_ext_irq_grp *irq_grp = &sc->ext_irq_grp[i];
+
+               ath11k_pci_ext_grp_disable(irq_grp);
+
+               napi_synchronize(&irq_grp->napi);
+               napi_disable(&irq_grp->napi);
+       }
+}
+
+static void ath11k_pci_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp)
+{
+       int i;
+
+       for (i = 0; i < irq_grp->num_irq; i++)
+               enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
+}
+
+static void ath11k_pci_ext_irq_enable(struct ath11k_base *ab)
+{
+       int i;
+
+       for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
+               struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
+
+               napi_enable(&irq_grp->napi);
+               ath11k_pci_ext_grp_enable(irq_grp);
+       }
+}
+
+static void ath11k_pci_sync_ext_irqs(struct ath11k_base *ab)
+{
+       int i, j, irq_idx;
+
+       for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
+               struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
+
+               for (j = 0; j < irq_grp->num_irq; j++) {
+                       irq_idx = irq_grp->irqs[j];
+                       synchronize_irq(ab->irq_num[irq_idx]);
+               }
+       }
+}
+
+static void ath11k_pci_ext_irq_disable(struct ath11k_base *ab)
+{
+       __ath11k_pci_ext_irq_disable(ab);
+       ath11k_pci_sync_ext_irqs(ab);
+}
+
+static int ath11k_pci_ext_grp_napi_poll(struct napi_struct *napi, int budget)
+{
+       struct ath11k_ext_irq_grp *irq_grp = container_of(napi,
+                                               struct ath11k_ext_irq_grp,
+                                               napi);
+       struct ath11k_base *ab = irq_grp->ab;
+       int work_done;
+
+       work_done = ath11k_dp_service_srng(ab, irq_grp, budget);
+       if (work_done < budget) {
+               napi_complete_done(napi, work_done);
+               ath11k_pci_ext_grp_enable(irq_grp);
+       }
+
+       if (work_done > budget)
+               work_done = budget;
+
+       return work_done;
+}
+
+static irqreturn_t ath11k_pci_ext_interrupt_handler(int irq, void *arg)
+{
+       struct ath11k_ext_irq_grp *irq_grp = arg;
+
+       ath11k_dbg(irq_grp->ab, ATH11K_DBG_PCI, "ext irq:%d\n", irq);
+
+       ath11k_pci_ext_grp_disable(irq_grp);
+
+       napi_schedule(&irq_grp->napi);
+
+       return IRQ_HANDLED;
+}
+
+static int ath11k_pci_ext_irq_config(struct ath11k_base *ab)
+{
+       int i, j, ret, num_vectors = 0;
+       u32 user_base_data = 0, base_vector = 0;
+
+       ath11k_pci_get_user_msi_assignment(ath11k_pci_priv(ab), "DP",
+                                          &num_vectors, &user_base_data,
+                                          &base_vector);
+
+       for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
+               struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
+               u32 num_irq = 0;
+
+               irq_grp->ab = ab;
+               irq_grp->grp_id = i;
+               init_dummy_netdev(&irq_grp->napi_ndev);
+               netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi,
+                              ath11k_pci_ext_grp_napi_poll, NAPI_POLL_WEIGHT);
+
+               if (ab->hw_params.ring_mask->tx[i] ||
+                   ab->hw_params.ring_mask->rx[i] ||
+                   ab->hw_params.ring_mask->rx_err[i] ||
+                   ab->hw_params.ring_mask->rx_wbm_rel[i] ||
+                   ab->hw_params.ring_mask->reo_status[i] ||
+                   ab->hw_params.ring_mask->rxdma2host[i] ||
+                   ab->hw_params.ring_mask->host2rxdma[i] ||
+                   ab->hw_params.ring_mask->rx_mon_status[i]) {
+                       num_irq = 1;
+               }
+
+               irq_grp->num_irq = num_irq;
+               irq_grp->irqs[0] = base_vector + i;
+
+               for (j = 0; j < irq_grp->num_irq; j++) {
+                       int irq_idx = irq_grp->irqs[j];
+                       int vector = (i % num_vectors) + base_vector;
+                       int irq = ath11k_pci_get_msi_irq(ab->dev, vector);
+
+                       ab->irq_num[irq_idx] = irq;
+
+                       ath11k_dbg(ab, ATH11K_DBG_PCI,
+                                  "irq:%d group:%d\n", irq, i);
+                       ret = request_irq(irq, ath11k_pci_ext_interrupt_handler,
+                                         IRQF_SHARED,
+                                         "DP_EXT_IRQ", irq_grp);
+                       if (ret) {
+                               ath11k_err(ab, "failed request irq %d: %d\n",
+                                          vector, ret);
+                               return ret;
+                       }
+
+                       disable_irq_nosync(ab->irq_num[irq_idx]);
+               }
+       }
+
+       return 0;
+}
+
 static int ath11k_pci_config_irq(struct ath11k_base *ab)
 {
        struct ath11k_ce_pipe *ce_pipe;
@@ -503,6 +672,10 @@ static int ath11k_pci_config_irq(struct ath11k_base *ab)
                ath11k_pci_ce_irq_disable(ab, i);
        }
 
+       ret = ath11k_pci_ext_irq_config(ab);
+       if (ret)
+               return ret;
+
        return 0;
 }
 
@@ -757,6 +930,8 @@ static const struct ath11k_hif_ops ath11k_pci_hif_ops = {
        .write32 = ath11k_pci_write32,
        .power_down = ath11k_pci_power_down,
        .power_up = ath11k_pci_power_up,
+       .irq_enable = ath11k_pci_ext_irq_enable,
+       .irq_disable = ath11k_pci_ext_irq_disable,
        .get_msi_address =  ath11k_pci_get_msi_address,
        .get_user_msi_vector = ath11k_get_user_msi_assignment,
        .map_service_to_pipe = ath11k_pci_map_service_to_pipe,