diff mbox series

[RFT] ath11k: pci: support platforms with one MSI vector

Message ID 1605121102-14352-1-git-send-email-kvalo@codeaurora.org (mailing list archive)
State RFC
Delegated to: Kalle Valo
Headers show
Series [RFT] ath11k: pci: support platforms with one MSI vector | expand

Commit Message

Kalle Valo Nov. 11, 2020, 6:58 p.m. UTC
From: Carl Huang <cjhuang@codeaurora.org>

Dell XPS 13 9310 has only one MSI vector available for QCA6390 device and ath11k fails with:

ath11k_pci 0000:56:00.0: failed to get 32 MSI vectors, only -28 available
ath11k_pci 0000:56:00.0: failed to enable msi: -28
ath11k_pci: probe of 0000:56:00.0 failed with error -28

This is a proof of concept patch for getting ath11k to work with QCA6390 using
only one MSI vector, not the final solution. Testing feedback more than
welcome. The patch applies to v5.10-rc2.

The idea here is to add a flag to indicate whether this ISR can be called
or not as all the ISR handlers are registered with IRQF_SHARED when ath11k can
only request 1 vector. This needs to be refined later.

In some scenarios, kernel crashed when the interrupt migration happens, so add
IRQF_NOBALANCING.

Also add debug messages to ath11k_qmi_respond_fw_mem_request() for helping to
debug mysterious firmware initialisation timeouts:

ath11k_pci 0000:05:00.0: qmi failed memory request, err = -110
ath11k_pci 0000:05:00.0: qmi failed to respond fw mem req:-110

If that happens, one way to workaround the problem is to revert this commit:

7fef431be9c9 mm/page_alloc: place pages to tail in __free_pages_core()

Link: https://lore.kernel.org/linux-pci/87mtzxkus5.fsf@nanos.tec.linutronix.de/
Link: http://lists.infradead.org/pipermail/ath11k/2020-November/000550.html
Signed-off-by: Carl Huang <cjhuang@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
 drivers/bus/mhi/core/init.c            |   4 +-
 drivers/net/wireless/ath/ath11k/ce.c   |   6 +-
 drivers/net/wireless/ath/ath11k/core.h |   1 +
 drivers/net/wireless/ath/ath11k/dp.c   |   8 +-
 drivers/net/wireless/ath/ath11k/hif.h  |   1 +
 drivers/net/wireless/ath/ath11k/mhi.c  |  11 +-
 drivers/net/wireless/ath/ath11k/pci.c  | 178 +++++++++++++++++++++++++++------
 drivers/net/wireless/ath/ath11k/pci.h  |   1 +
 drivers/net/wireless/ath/ath11k/qmi.c  |   4 +
 9 files changed, 174 insertions(+), 40 deletions(-)

Comments

kernel test robot Nov. 12, 2020, 6:46 a.m. UTC | #1
Hi Kalle,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v5.10-rc3 next-20201111]
[cannot apply to ath6kl/ath-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Kalle-Valo/ath11k-pci-support-platforms-with-one-MSI-vector/20201112-030041
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git eccc876724927ff3b9ff91f36f7b6b159e948f0c
config: m68k-allmodconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/e2c4c01cee2e8ce50345b8f70f192921a4875e18
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Kalle-Valo/ath11k-pci-support-platforms-with-one-MSI-vector/20201112-030041
        git checkout e2c4c01cee2e8ce50345b8f70f192921a4875e18
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=m68k 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   In file included from include/linux/kernel.h:11,
                    from include/linux/list.h:9,
                    from include/linux/mutex.h:15,
                    from drivers/net/wireless/ath/ath11k/qmi.h:9,
                    from drivers/net/wireless/ath/ath11k/qmi.c:8:
   include/linux/scatterlist.h: In function 'sg_set_buf':
   arch/m68k/include/asm/page_mm.h:169:49: warning: ordered comparison of pointer with null pointer [-Wextra]
     169 | #define virt_addr_valid(kaddr) ((void *)(kaddr) >= (void *)PAGE_OFFSET && (void *)(kaddr) < high_memory)
         |                                                 ^~
   include/linux/compiler.h:78:42: note: in definition of macro 'unlikely'
      78 | # define unlikely(x) __builtin_expect(!!(x), 0)
         |                                          ^
   include/linux/scatterlist.h:143:2: note: in expansion of macro 'BUG_ON'
     143 |  BUG_ON(!virt_addr_valid(buf));
         |  ^~~~~~
   include/linux/scatterlist.h:143:10: note: in expansion of macro 'virt_addr_valid'
     143 |  BUG_ON(!virt_addr_valid(buf));
         |          ^~~~~~~~~~~~~~~
   drivers/net/wireless/ath/ath11k/qmi.c: In function 'ath11k_qmi_respond_fw_mem_request':
>> drivers/net/wireless/ath/ath11k/qmi.c:1678:42: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 4 has type 'dma_addr_t' {aka 'unsigned int'} [-Wformat=]
    1678 |    ath11k_info(ab, "req mem_seg[%d] 0x%llx %u %u\n", i,
         |                                       ~~~^
         |                                          |
         |                                          long long unsigned int
         |                                       %x
    1679 |         ab->qmi.target_mem[i].paddr,
         |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~       
         |                              |
         |                              dma_addr_t {aka unsigned int}

vim +1678 drivers/net/wireless/ath/ath11k/qmi.c

  1648	
  1649	static int ath11k_qmi_respond_fw_mem_request(struct ath11k_base *ab)
  1650	{
  1651		struct qmi_wlanfw_respond_mem_req_msg_v01 *req;
  1652		struct qmi_wlanfw_respond_mem_resp_msg_v01 resp;
  1653		struct qmi_txn txn = {};
  1654		int ret = 0, i;
  1655	
  1656		req = kzalloc(sizeof(*req), GFP_KERNEL);
  1657		if (!req)
  1658			return -ENOMEM;
  1659	
  1660		memset(&resp, 0, sizeof(resp));
  1661	
  1662		/* For QCA6390 by default FW requests a block of ~4M contiguous
  1663		 * DMA memory, it's hard to allocate from OS. So host returns
  1664		 * failure to FW and FW will then request mulitple blocks of small
  1665		 * chunk size memory.
  1666		 */
  1667		if (!ab->bus_params.fixed_mem_region && ab->qmi.mem_seg_count <= 2) {
  1668			ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi delays mem_request %d\n",
  1669				   ab->qmi.mem_seg_count);
  1670			memset(req, 0, sizeof(*req));
  1671		} else {
  1672			req->mem_seg_len = ab->qmi.mem_seg_count;
  1673	
  1674			for (i = 0; i < req->mem_seg_len ; i++) {
  1675				req->mem_seg[i].addr = ab->qmi.target_mem[i].paddr;
  1676				req->mem_seg[i].size = ab->qmi.target_mem[i].size;
  1677				req->mem_seg[i].type = ab->qmi.target_mem[i].type;
> 1678				ath11k_info(ab, "req mem_seg[%d] 0x%llx %u %u\n", i,
  1679					    ab->qmi.target_mem[i].paddr,
  1680					    ab->qmi.target_mem[i].size,
  1681					    ab->qmi.target_mem[i].type);
  1682			}
  1683		}
  1684	
  1685		ret = qmi_txn_init(&ab->qmi.handle, &txn,
  1686				   qmi_wlanfw_respond_mem_resp_msg_v01_ei, &resp);
  1687		if (ret < 0)
  1688			goto out;
  1689	
  1690		ret = qmi_send_request(&ab->qmi.handle, NULL, &txn,
  1691				       QMI_WLANFW_RESPOND_MEM_REQ_V01,
  1692				       QMI_WLANFW_RESPOND_MEM_REQ_MSG_V01_MAX_LEN,
  1693				       qmi_wlanfw_respond_mem_req_msg_v01_ei, req);
  1694		if (ret < 0) {
  1695			ath11k_warn(ab, "qmi failed to respond memory request, err = %d\n",
  1696				    ret);
  1697			goto out;
  1698		}
  1699	
  1700		ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS));
  1701		if (ret < 0) {
  1702			ath11k_warn(ab, "qmi failed memory request, err = %d\n", ret);
  1703			goto out;
  1704		}
  1705	
  1706		if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
  1707			ath11k_warn(ab, "Respond mem req failed, result: %d, err: %d\n",
  1708				    resp.resp.result, resp.resp.error);
  1709			ret = -EINVAL;
  1710			goto out;
  1711		}
  1712	out:
  1713		kfree(req);
  1714		return ret;
  1715	}
  1716	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff mbox series

Patch

diff --git a/drivers/bus/mhi/core/init.c b/drivers/bus/mhi/core/init.c
index 0ffdebde8265..d182ffdbaf64 100644
--- a/drivers/bus/mhi/core/init.c
+++ b/drivers/bus/mhi/core/init.c
@@ -153,7 +153,7 @@  int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl)
 	/* Setup BHI_INTVEC IRQ */
 	ret = request_threaded_irq(mhi_cntrl->irq[0], mhi_intvec_handler,
 				   mhi_intvec_threaded_handler,
-				   IRQF_SHARED | IRQF_NO_SUSPEND,
+				   IRQF_SHARED | IRQF_NOBALANCING,
 				   "bhi", mhi_cntrl);
 	if (ret)
 		return ret;
@@ -171,7 +171,7 @@  int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl)
 
 		ret = request_irq(mhi_cntrl->irq[mhi_event->irq],
 				  mhi_irq_handler,
-				  IRQF_SHARED | IRQF_NO_SUSPEND,
+				  IRQF_SHARED | IRQF_NOBALANCING,
 				  "mhi", mhi_event);
 		if (ret) {
 			dev_err(dev, "Error requesting irq:%d for ev:%d\n",
diff --git a/drivers/net/wireless/ath/ath11k/ce.c b/drivers/net/wireless/ath/ath11k/ce.c
index 9d730f8ac816..46c717a344c3 100644
--- a/drivers/net/wireless/ath/ath11k/ce.c
+++ b/drivers/net/wireless/ath/ath11k/ce.c
@@ -459,20 +459,22 @@  static void ath11k_ce_srng_msi_ring_params_setup(struct ath11k_base *ab, u32 ce_
 	u32 msi_irq_start;
 	u32 addr_lo;
 	u32 addr_hi;
+	u32 vectors_32_capability;
 	int ret;
 
 	ret = ath11k_get_user_msi_vector(ab, "CE",
 					 &msi_data_count, &msi_data_start,
 					 &msi_irq_start);
-
 	if (ret)
 		return;
+	vectors_32_capability = ab->hif.ops->is_32_vecs_support(ab);
 
 	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 = (ce_id % msi_data_count) + msi_data_start;
+	ring_params->msi_data = vectors_32_capability ?
+		(ce_id % msi_data_count) + msi_data_start : msi_data_start;
 	ring_params->flags |= HAL_SRNG_FLAGS_MSI_INTR;
 }
 
diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
index 18b97420f0d8..0de6b2cd8c75 100644
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -683,6 +683,7 @@  struct ath11k_base {
 	bool wmi_ready;
 	u32 wlan_init_status;
 	int irq_num[ATH11K_IRQ_NUM_MAX];
+	int irq_enable_flag[ATH11K_IRQ_NUM_MAX];
 	struct ath11k_ext_irq_grp ext_irq_grp[ATH11K_EXT_IRQ_GRP_NUM_MAX];
 	struct napi_struct *napi;
 	struct ath11k_targ_cap target_caps;
diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c
index 59dd185a0cfc..b7288dc47199 100644
--- a/drivers/net/wireless/ath/ath11k/dp.c
+++ b/drivers/net/wireless/ath/ath11k/dp.c
@@ -180,13 +180,15 @@  static void ath11k_dp_srng_msi_setup(struct ath11k_base *ab,
 {
 	int msi_group_number, msi_data_count;
 	u32 msi_data_start, msi_irq_start, addr_lo, addr_hi;
-	int ret;
+	u32 vectors_32_capability;
+	int ret = -EINVAL;
 
 	ret = ath11k_get_user_msi_vector(ab, "DP",
 					 &msi_data_count, &msi_data_start,
 					 &msi_irq_start);
 	if (ret)
 		return;
+	vectors_32_capability = ab->hif.ops->is_32_vecs_support(ab);
 
 	msi_group_number = ath11k_dp_srng_calculate_msi_group(ab, type,
 							      ring_num);
@@ -209,8 +211,8 @@  static void ath11k_dp_srng_msi_setup(struct ath11k_base *ab,
 
 	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->msi_data = vectors_32_capability ?
+		(msi_group_number % msi_data_count) + msi_data_start : msi_data_start;
 	ring_params->flags |= HAL_SRNG_FLAGS_MSI_INTR;
 }
 
diff --git a/drivers/net/wireless/ath/ath11k/hif.h b/drivers/net/wireless/ath/ath11k/hif.h
index dbe5568916e8..b6bde194a390 100644
--- a/drivers/net/wireless/ath/ath11k/hif.h
+++ b/drivers/net/wireless/ath/ath11k/hif.h
@@ -24,6 +24,7 @@  struct ath11k_hif_ops {
 				   u32 *base_vector);
 	void (*get_msi_address)(struct ath11k_base *ab, u32 *msi_addr_lo,
 				u32 *msi_addr_hi);
+	int (*is_32_vecs_support)(struct ath11k_base *ab);
 };
 
 static inline int ath11k_hif_start(struct ath11k_base *sc)
diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c
index aded9a719d51..80d7dd18034d 100644
--- a/drivers/net/wireless/ath/ath11k/mhi.c
+++ b/drivers/net/wireless/ath/ath11k/mhi.c
@@ -156,14 +156,17 @@  static int ath11k_mhi_get_msi(struct ath11k_pci *ab_pci)
 {
 	struct ath11k_base *ab = ab_pci->ab;
 	u32 user_base_data, base_vector;
+	u32 vectors_32_capability;
 	int ret, num_vectors, i;
 	int *irq;
+	unsigned int msi_data;
 
 	ret = ath11k_pci_get_user_msi_assignment(ab_pci,
 						 "MHI", &num_vectors,
 						 &user_base_data, &base_vector);
 	if (ret)
 		return ret;
+	vectors_32_capability = ab_pci->vectors_32_capability;
 
 	ath11k_dbg(ab, ATH11K_DBG_PCI, "Number of assigned MSI for MHI is %d, base vector is %d\n",
 		   num_vectors, base_vector);
@@ -172,9 +175,13 @@  static int ath11k_mhi_get_msi(struct ath11k_pci *ab_pci)
 	if (!irq)
 		return -ENOMEM;
 
-	for (i = 0; i < num_vectors; i++)
+	for (i = 0; i < num_vectors; i++) {
+		msi_data = vectors_32_capability ?
+			   i + base_vector : base_vector;
+
 		irq[i] = ath11k_pci_get_msi_irq(ab->dev,
-						base_vector + i);
+						msi_data);
+	}
 
 	ab_pci->mhi_ctrl->irq = irq;
 	ab_pci->mhi_ctrl->nr_irqs = num_vectors;
diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c
index d7eb6b7160bb..a7a98bdc0c5f 100644
--- a/drivers/net/wireless/ath/ath11k/pci.c
+++ b/drivers/net/wireless/ath/ath11k/pci.c
@@ -17,6 +17,7 @@ 
 #define ATH11K_PCI_DMA_MASK		32
 
 #define ATH11K_PCI_IRQ_CE0_OFFSET		3
+#define ATH11K_PCI_IRQ_DP_OFFSET		14
 
 #define WINDOW_ENABLE_BIT		0x40000000
 #define WINDOW_REG_ADDRESS		0x310c
@@ -265,12 +266,17 @@  static void ath11k_pci_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo,
 				       u32 *msi_addr_hi)
 {
 	struct pci_dev *pci_dev = to_pci_dev(ab->dev);
+	u16 is_64bits;
 
 	pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_LO,
 			      msi_addr_lo);
+	pci_read_config_word(pci_dev, pci_dev->msi_cap + PCI_MSI_FLAGS, &is_64bits);
 
-	pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_HI,
-			      msi_addr_hi);
+	if (is_64bits & PCI_MSI_FLAGS_64BIT)
+		pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_HI,
+				      msi_addr_hi);
+	else
+		*msi_addr_hi = 0;
 }
 
 int ath11k_pci_get_user_msi_assignment(struct ath11k_pci *ab_pci, char *user_name,
@@ -278,14 +284,15 @@  int ath11k_pci_get_user_msi_assignment(struct ath11k_pci *ab_pci, char *user_nam
 				       u32 *base_vector)
 {
 	struct ath11k_base *ab = ab_pci->ab;
+	u32 msi_32_cap = ab_pci->vectors_32_capability;
 	int idx;
 
 	for (idx = 0; idx < msi_config.total_users; idx++) {
 		if (strcmp(user_name, msi_config.users[idx].name) == 0) {
 			*num_vectors = msi_config.users[idx].num_vectors;
-			*user_base_data = msi_config.users[idx].base_vector
-				+ ab_pci->msi_ep_base_data;
-			*base_vector = msi_config.users[idx].base_vector;
+			*base_vector = msi_32_cap ?
+				  msi_config.users[idx].base_vector : 0;
+			*user_base_data = *base_vector + ab_pci->msi_ep_base_data;
 
 			ath11k_dbg(ab, ATH11K_DBG_PCI, "Assign MSI to user: %s, num_vectors: %d, user_base_data: %u, base_vector: %u\n",
 				   user_name, *num_vectors, *user_base_data,
@@ -339,20 +346,38 @@  static void ath11k_pci_free_irq(struct ath11k_base *ab)
 	ath11k_pci_free_ext_irq(ab);
 }
 
+static void ath11k_pci_set_irq_enable_flag(struct ath11k_base *ab, u32 irq_idx, int flag)
+{
+	ab->irq_enable_flag[irq_idx] = flag;
+}
+
 static void ath11k_pci_ce_irq_enable(struct ath11k_base *ab, u16 ce_id)
 {
 	u32 irq_idx;
+	u32 vecs_32_cap;
 
-	irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id;
-	enable_irq(ab->irq_num[irq_idx]);
+	vecs_32_cap = ath11k_pci_priv(ab)->vectors_32_capability;
+	irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET  + ce_id;
+
+	if (vecs_32_cap)
+		enable_irq(ab->irq_num[irq_idx]);
+
+	ath11k_pci_set_irq_enable_flag(ab, irq_idx, 1);
 }
 
 static void ath11k_pci_ce_irq_disable(struct ath11k_base *ab, u16 ce_id)
 {
 	u32 irq_idx;
+	u32 vecs_32_cap;
 
+	vecs_32_cap = ath11k_pci_priv(ab)->vectors_32_capability;
 	irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id;
-	disable_irq_nosync(ab->irq_num[irq_idx]);
+
+	/* Cannot disable the irq when using one msi interrupt */
+	if (vecs_32_cap)
+		disable_irq_nosync(ab->irq_num[irq_idx]);
+
+	ath11k_pci_set_irq_enable_flag(ab, irq_idx, 0);
 }
 
 static void ath11k_pci_ce_irqs_disable(struct ath11k_base *ab)
@@ -382,18 +407,28 @@  static void ath11k_pci_sync_ce_irqs(struct ath11k_base *ab)
 
 static void ath11k_pci_ce_tasklet(unsigned long data)
 {
+
 	struct ath11k_ce_pipe *ce_pipe = (struct ath11k_ce_pipe *)data;
+	int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num;
 
 	ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num);
 
-	ath11k_pci_ce_irq_enable(ce_pipe->ab, ce_pipe->pipe_num);
+	enable_irq(ce_pipe->ab->irq_num[irq_idx]);
 }
 
 static irqreturn_t ath11k_pci_ce_interrupt_handler(int irq, void *arg)
 {
 	struct ath11k_ce_pipe *ce_pipe = arg;
+	struct ath11k_base *ab = ce_pipe->ab;
+	int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num;
+
+	disable_irq_nosync(ab->irq_num[irq_idx]);
+
+	if (!ab->irq_enable_flag[irq_idx]) {
+		enable_irq(ab->irq_num[irq_idx]);
+		return IRQ_HANDLED;
+	}
 
-	ath11k_pci_ce_irq_disable(ce_pipe->ab, ce_pipe->pipe_num);
 	tasklet_schedule(&ce_pipe->intr_tq);
 
 	return IRQ_HANDLED;
@@ -402,9 +437,16 @@  static irqreturn_t ath11k_pci_ce_interrupt_handler(int irq, void *arg)
 static void ath11k_pci_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp)
 {
 	int i;
+	u32 vecs_32_cap;
 
-	for (i = 0; i < irq_grp->num_irq; i++)
-		disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
+	vecs_32_cap = ath11k_pci_priv(irq_grp->ab)->vectors_32_capability;
+
+	for (i = 0; i < irq_grp->num_irq; i++) {
+		if (vecs_32_cap)
+			disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
+
+		ath11k_pci_set_irq_enable_flag(irq_grp->ab, irq_grp->irqs[i], 0);
+	}
 }
 
 static void __ath11k_pci_ext_irq_disable(struct ath11k_base *sc)
@@ -424,9 +466,15 @@  static void __ath11k_pci_ext_irq_disable(struct ath11k_base *sc)
 static void ath11k_pci_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp)
 {
 	int i;
+	u32 vecs_32_cap;
 
-	for (i = 0; i < irq_grp->num_irq; i++)
-		enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
+	vecs_32_cap = ath11k_pci_priv(irq_grp->ab)->vectors_32_capability;
+
+	for (i = 0; i < irq_grp->num_irq; i++) {
+		if (vecs_32_cap)
+			enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
+		ath11k_pci_set_irq_enable_flag(irq_grp->ab, irq_grp->irqs[i], 1);
+	}
 }
 
 static void ath11k_pci_ext_irq_enable(struct ath11k_base *ab)
@@ -468,11 +516,13 @@  static int ath11k_pci_ext_grp_napi_poll(struct napi_struct *napi, int budget)
 						napi);
 	struct ath11k_base *ab = irq_grp->ab;
 	int work_done;
+	int i;
 
 	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);
+		for (i = 0; i < irq_grp->num_irq; i++)
+			enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
 	}
 
 	if (work_done > budget)
@@ -484,10 +534,16 @@  static int ath11k_pci_ext_grp_napi_poll(struct napi_struct *napi, int budget)
 static irqreturn_t ath11k_pci_ext_interrupt_handler(int irq, void *arg)
 {
 	struct ath11k_ext_irq_grp *irq_grp = arg;
+	int i;
 
-	ath11k_dbg(irq_grp->ab, ATH11K_DBG_PCI, "ext irq:%d\n", irq);
+	for (i = 0; i < irq_grp->num_irq; i++)
+		disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
 
-	ath11k_pci_ext_grp_disable(irq_grp);
+	if (!irq_grp->ab->irq_enable_flag[irq_grp->irqs[0]]) {
+		for (i = 0; i < irq_grp->num_irq; i++)
+			enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
+		return IRQ_HANDLED;
+	}
 
 	napi_schedule(&irq_grp->napi);
 
@@ -498,6 +554,7 @@  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;
+	u32 vecs_32_cap;
 
 	ret = ath11k_pci_get_user_msi_assignment(ath11k_pci_priv(ab), "DP",
 						 &num_vectors,
@@ -506,6 +563,8 @@  static int ath11k_pci_ext_irq_config(struct ath11k_base *ab)
 	if (ret < 0)
 		return ret;
 
+	vecs_32_cap = ath11k_pci_priv(ab)->vectors_32_capability;
+
 	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;
@@ -528,11 +587,12 @@  static int ath11k_pci_ext_irq_config(struct ath11k_base *ab)
 		}
 
 		irq_grp->num_irq = num_irq;
-		irq_grp->irqs[0] = base_vector + i;
+		irq_grp->irqs[0] = ATH11K_PCI_IRQ_DP_OFFSET + 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 vector = vecs_32_cap ?
+				(i % num_vectors) + base_vector : base_vector;
 			int irq = ath11k_pci_get_msi_irq(ab->dev, vector);
 
 			ab->irq_num[irq_idx] = irq;
@@ -540,7 +600,7 @@  static int ath11k_pci_ext_irq_config(struct ath11k_base *ab)
 			ath11k_dbg(ab, ATH11K_DBG_PCI,
 				   "irq:%d group:%d\n", irq, i);
 			ret = request_irq(irq, ath11k_pci_ext_interrupt_handler,
-					  IRQF_SHARED,
+					  IRQF_SHARED | IRQF_NOBALANCING,
 					  "DP_EXT_IRQ", irq_grp);
 			if (ret) {
 				ath11k_err(ab, "failed request irq %d: %d\n",
@@ -548,7 +608,11 @@  static int ath11k_pci_ext_irq_config(struct ath11k_base *ab)
 				return ret;
 			}
 
-			disable_irq_nosync(ab->irq_num[irq_idx]);
+			/* balance irq_enable */
+			if (vecs_32_cap)
+				disable_irq_nosync(ab->irq_num[irq_idx]);
+
+			ath11k_pci_set_irq_enable_flag(ab, irq_idx, 0);
 		}
 	}
 
@@ -561,6 +625,7 @@  static int ath11k_pci_config_irq(struct ath11k_base *ab)
 	u32 msi_data_start;
 	u32 msi_data_count;
 	u32 msi_irq_start;
+	u32 vecs_32_cap;
 	unsigned int msi_data;
 	int irq, i, ret, irq_idx;
 
@@ -570,9 +635,13 @@  static int ath11k_pci_config_irq(struct ath11k_base *ab)
 	if (ret)
 		return ret;
 
+	vecs_32_cap = ath11k_pci_priv(ab)->vectors_32_capability;
+
 	/* Configure CE irqs */
 	for (i = 0; i < ab->hw_params.ce_count; i++) {
-		msi_data = (i % msi_data_count) + msi_irq_start;
+		msi_data = vecs_32_cap ?
+			   (i % msi_data_count) + msi_irq_start : msi_irq_start;
+
 		irq = ath11k_pci_get_msi_irq(ab->dev, msi_data);
 		ce_pipe = &ab->ce.ce_pipe[i];
 
@@ -585,7 +654,7 @@  static int ath11k_pci_config_irq(struct ath11k_base *ab)
 			     (unsigned long)ce_pipe);
 
 		ret = request_irq(irq, ath11k_pci_ce_interrupt_handler,
-				  IRQF_SHARED, irq_name[irq_idx],
+				  IRQF_SHARED | IRQF_NOBALANCING, irq_name[irq_idx],
 				  ce_pipe);
 		if (ret) {
 			ath11k_err(ab, "failed to request irq %d: %d\n",
@@ -641,14 +710,19 @@  static int ath11k_pci_enable_msi(struct ath11k_pci *ab_pci)
 					    msi_config.total_vectors,
 					    msi_config.total_vectors,
 					    PCI_IRQ_MSI);
-	if (num_vectors != msi_config.total_vectors) {
-		ath11k_err(ab, "failed to get %d MSI vectors, only %d available",
-			   msi_config.total_vectors, num_vectors);
-
-		if (num_vectors >= 0)
-			return -EINVAL;
-		else
-			return num_vectors;
+
+	if (num_vectors == msi_config.total_vectors) {
+		ab_pci->vectors_32_capability = 1;
+	} else {
+		ab_pci->vectors_32_capability = 0;
+		num_vectors = pci_alloc_irq_vectors(ab_pci->pdev,
+						    1,
+						    1,
+						    PCI_IRQ_MSI);
+		if (num_vectors < 0) {
+			ret = -EINVAL;
+			goto reset_msi_config;
+		}
 	}
 
 	msi_desc = irq_get_msi_desc(ab_pci->pdev->irq);
@@ -658,6 +732,8 @@  static int ath11k_pci_enable_msi(struct ath11k_pci *ab_pci)
 		goto free_msi_vector;
 	}
 
+	ath11k_info(ab, "MSI vectors: %d", num_vectors);
+
 	ab_pci->msi_ep_base_data = msi_desc->msg.data;
 
 	ath11k_dbg(ab, ATH11K_DBG_PCI, "msi base data is %d\n", ab_pci->msi_ep_base_data);
@@ -667,6 +743,7 @@  static int ath11k_pci_enable_msi(struct ath11k_pci *ab_pci)
 free_msi_vector:
 	pci_free_irq_vectors(ab_pci->pdev);
 
+reset_msi_config:
 	return ret;
 }
 
@@ -675,6 +752,32 @@  static void ath11k_pci_disable_msi(struct ath11k_pci *ab_pci)
 	pci_free_irq_vectors(ab_pci->pdev);
 }
 
+static int ath11k_pci_config_msi_data(struct ath11k_pci *ab_pci)
+{
+	struct msi_desc *msi_desc;
+	int ret;
+
+	msi_desc = irq_get_msi_desc(ab_pci->pdev->irq);
+
+	if (!msi_desc) {
+		ath11k_err(ab_pci->ab, "msi_desc is NULL!\n");
+		ret = -EINVAL;
+		goto free_msi_vector;
+	}
+
+	ab_pci->msi_ep_base_data = msi_desc->msg.data;
+
+	ath11k_dbg(ab_pci->ab, ATH11K_DBG_PCI, "msi base data is %d\n",
+		   ab_pci->msi_ep_base_data);
+
+	return 0;
+
+ free_msi_vector:
+	pci_free_irq_vectors(ab_pci->pdev);
+
+	return ret;
+}
+
 static int ath11k_pci_claim(struct ath11k_pci *ab_pci, struct pci_dev *pdev)
 {
 	struct ath11k_base *ab = ab_pci->ab;
@@ -862,6 +965,11 @@  static int ath11k_pci_map_service_to_pipe(struct ath11k_base *ab, u16 service_id
 	return 0;
 }
 
+static int ath11k_pci_is_32_vectors_support(struct ath11k_base *ab)
+{
+	return ath11k_pci_priv(ab)->vectors_32_capability;
+}
+
 static const struct ath11k_hif_ops ath11k_pci_hif_ops = {
 	.start = ath11k_pci_start,
 	.stop = ath11k_pci_stop,
@@ -874,6 +982,7 @@  static const struct ath11k_hif_ops ath11k_pci_hif_ops = {
 	.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,
+	.is_32_vecs_support = ath11k_pci_is_32_vectors_support,
 };
 
 static int ath11k_pci_probe(struct pci_dev *pdev,
@@ -972,6 +1081,13 @@  static int ath11k_pci_probe(struct pci_dev *pdev,
 		goto err_ce_free;
 	}
 
+	/* Get the correct msi_data after request_irq() to avoid spurious interrupt */
+	ret = ath11k_pci_config_msi_data(ab_pci);
+	if (ret) {
+		ath11k_err(ab, "failed to config msi_data: %d\n", ret);
+		goto err_ce_free;
+	}
+
 	ret = ath11k_core_init(ab);
 	if (ret) {
 		ath11k_err(ab, "failed to init core: %d\n", ret);
diff --git a/drivers/net/wireless/ath/ath11k/pci.h b/drivers/net/wireless/ath/ath11k/pci.h
index 43562f774a37..e165e8f0029f 100644
--- a/drivers/net/wireless/ath/ath11k/pci.h
+++ b/drivers/net/wireless/ath/ath11k/pci.h
@@ -46,6 +46,7 @@  struct ath11k_pci {
 	u16 dev_id;
 	char amss_path[100];
 	u32 msi_ep_base_data;
+	u32 vectors_32_capability;
 	struct mhi_controller *mhi_ctrl;
 	unsigned long mhi_state;
 	u32 register_window;
diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c
index c2b165158225..5ac7e2685518 100644
--- a/drivers/net/wireless/ath/ath11k/qmi.c
+++ b/drivers/net/wireless/ath/ath11k/qmi.c
@@ -1675,6 +1675,10 @@  static int ath11k_qmi_respond_fw_mem_request(struct ath11k_base *ab)
 			req->mem_seg[i].addr = ab->qmi.target_mem[i].paddr;
 			req->mem_seg[i].size = ab->qmi.target_mem[i].size;
 			req->mem_seg[i].type = ab->qmi.target_mem[i].type;
+			ath11k_info(ab, "req mem_seg[%d] 0x%llx %u %u\n", i,
+				    ab->qmi.target_mem[i].paddr,
+				    ab->qmi.target_mem[i].size,
+				    ab->qmi.target_mem[i].type);
 		}
 	}