--- /dev/null
+From stable+bounces-10188-greg=kroah.com@vger.kernel.org Mon Jan 8 16:37:56 2024
+From: Amit Pundir <amit.pundir@linaro.org>
+Date: Mon, 8 Jan 2024 21:07:35 +0530
+Subject: ath10k: Add interrupt summary based CE processing
+To: Greg KH <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>, Douglas Anderson <dianders@chromium.org>, Rakesh Pillai <pillair@codeaurora.org>
+Cc: Yongqin Liu <yongqin.liu@linaro.org>, Stable <stable@vger.kernel.org>, Kalle Valo <kvalo@codeaurora.org>
+Message-ID: <20240108153737.3538218-3-amit.pundir@linaro.org>
+
+From: Rakesh Pillai <pillair@codeaurora.org>
+
+[ Upstream commit b92aba35d39d10d8a6bdf2495172fd490c598b4a ]
+
+Currently the NAPI processing loops through all
+the copy engines and processes a particular copy
+engine is the copy completion is set for that copy
+engine. The host driver is not supposed to access
+any copy engine register after clearing the interrupt
+status register.
+
+This might result in kernel crash like the one below
+[ 1159.220143] Call trace:
+[ 1159.220170] ath10k_snoc_read32+0x20/0x40 [ath10k_snoc]
+[ 1159.220193] ath10k_ce_per_engine_service_any+0x78/0x130 [ath10k_core]
+[ 1159.220203] ath10k_snoc_napi_poll+0x38/0x8c [ath10k_snoc]
+[ 1159.220270] net_rx_action+0x100/0x3b0
+[ 1159.220312] __do_softirq+0x164/0x30c
+[ 1159.220345] run_ksoftirqd+0x2c/0x64
+[ 1159.220380] smpboot_thread_fn+0x1b0/0x288
+[ 1159.220405] kthread+0x11c/0x12c
+[ 1159.220423] ret_from_fork+0x10/0x18
+
+To avoid such a scenario, we generate an interrupt
+summary by reading the copy completion for all the
+copy engine before actually processing any of them.
+This will avoid reading the interrupt status register
+for any CE after the interrupt status is cleared.
+
+Tested-on: WCN3990 hw1.0 SNOC WLAN.HL.3.1-01040-QCAHLSWMTPLZ-1
+
+Signed-off-by: Rakesh Pillai <pillair@codeaurora.org>
+Reviewed-by: Douglas Anderson <dianders@chromium.org>
+Tested-by: Douglas Anderson <dianders@chromium.org>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+Link: https://lore.kernel.org/r/1593193967-29897-1-git-send-email-pillair@codeaurora.org
+Stable-dep-of: 170c75d43a77 ("ath10k: Don't touch the CE interrupt registers after power up")
+Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/ath/ath10k/ce.c | 63 +++++++++++++++++++++--------------
+ drivers/net/wireless/ath/ath10k/ce.h | 5 +-
+ 2 files changed, 42 insertions(+), 26 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath10k/ce.c
++++ b/drivers/net/wireless/ath/ath10k/ce.c
+@@ -481,15 +481,38 @@ static inline void ath10k_ce_engine_int_
+ ath10k_ce_write32(ar, ce_ctrl_addr + wm_regs->addr, mask);
+ }
+
+-static inline bool ath10k_ce_engine_int_status_check(struct ath10k *ar,
+- u32 ce_ctrl_addr,
+- unsigned int mask)
++static bool ath10k_ce_engine_int_status_check(struct ath10k *ar, u32 ce_ctrl_addr,
++ unsigned int mask)
+ {
+ struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs;
+
+ return ath10k_ce_read32(ar, ce_ctrl_addr + wm_regs->addr) & mask;
+ }
+
++u32 ath10k_ce_gen_interrupt_summary(struct ath10k *ar)
++{
++ struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs;
++ struct ath10k_ce_pipe *ce_state;
++ struct ath10k_ce *ce;
++ u32 irq_summary = 0;
++ u32 ctrl_addr;
++ u32 ce_id;
++
++ ce = ath10k_ce_priv(ar);
++
++ for (ce_id = 0; ce_id < CE_COUNT; ce_id++) {
++ ce_state = &ce->ce_states[ce_id];
++ ctrl_addr = ce_state->ctrl_addr;
++ if (ath10k_ce_engine_int_status_check(ar, ctrl_addr,
++ wm_regs->cc_mask)) {
++ irq_summary |= BIT(ce_id);
++ }
++ }
++
++ return irq_summary;
++}
++EXPORT_SYMBOL(ath10k_ce_gen_interrupt_summary);
++
+ /*
+ * Guts of ath10k_ce_send.
+ * The caller takes responsibility for any needed locking.
+@@ -1308,32 +1331,24 @@ void ath10k_ce_per_engine_service(struct
+ struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs;
+ u32 ctrl_addr = ce_state->ctrl_addr;
+
+- spin_lock_bh(&ce->ce_lock);
+-
+- if (ath10k_ce_engine_int_status_check(ar, ctrl_addr,
+- wm_regs->cc_mask)) {
+- /* Clear before handling */
+- ath10k_ce_engine_int_status_clear(ar, ctrl_addr,
+- wm_regs->cc_mask);
+-
+- spin_unlock_bh(&ce->ce_lock);
+-
+- if (ce_state->recv_cb)
+- ce_state->recv_cb(ce_state);
+-
+- if (ce_state->send_cb)
+- ce_state->send_cb(ce_state);
+-
+- spin_lock_bh(&ce->ce_lock);
+- }
+-
+ /*
++ * Clear before handling
++ *
+ * Misc CE interrupts are not being handled, but still need
+ * to be cleared.
++ *
++ * NOTE: When the last copy engine interrupt is cleared the
++ * hardware will go to sleep. Once this happens any access to
++ * the CE registers can cause a hardware fault.
+ */
+- ath10k_ce_engine_int_status_clear(ar, ctrl_addr, wm_regs->wm_mask);
++ ath10k_ce_engine_int_status_clear(ar, ctrl_addr,
++ wm_regs->cc_mask | wm_regs->wm_mask);
+
+- spin_unlock_bh(&ce->ce_lock);
++ if (ce_state->recv_cb)
++ ce_state->recv_cb(ce_state);
++
++ if (ce_state->send_cb)
++ ce_state->send_cb(ce_state);
+ }
+ EXPORT_SYMBOL(ath10k_ce_per_engine_service);
+
+--- a/drivers/net/wireless/ath/ath10k/ce.h
++++ b/drivers/net/wireless/ath/ath10k/ce.h
+@@ -259,6 +259,8 @@ int ath10k_ce_disable_interrupts(struct
+ void ath10k_ce_enable_interrupts(struct ath10k *ar);
+ void ath10k_ce_dump_registers(struct ath10k *ar,
+ struct ath10k_fw_crash_data *crash_data);
++
++u32 ath10k_ce_gen_interrupt_summary(struct ath10k *ar);
+ void ath10k_ce_alloc_rri(struct ath10k *ar);
+ void ath10k_ce_free_rri(struct ath10k *ar);
+
+@@ -369,7 +371,6 @@ static inline u32 ath10k_ce_base_address
+ (((x) & CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK) >> \
+ CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB)
+ #define CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS 0x0000
+-#define CE_INTERRUPT_SUMMARY (GENMASK(CE_COUNT_MAX - 1, 0))
+
+ static inline u32 ath10k_ce_interrupt_summary(struct ath10k *ar)
+ {
+@@ -380,7 +381,7 @@ static inline u32 ath10k_ce_interrupt_su
+ ce->bus_ops->read32((ar), CE_WRAPPER_BASE_ADDRESS +
+ CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS));
+ else
+- return CE_INTERRUPT_SUMMARY;
++ return ath10k_ce_gen_interrupt_summary(ar);
+ }
+
+ /* Host software's Copy Engine configuration. */
--- /dev/null
+From stable+bounces-10191-greg=kroah.com@vger.kernel.org Mon Jan 8 16:37:58 2024
+From: Amit Pundir <amit.pundir@linaro.org>
+Date: Mon, 8 Jan 2024 21:07:37 +0530
+Subject: ath10k: Get rid of "per_ce_irq" hw param
+To: Greg KH <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>, Douglas Anderson <dianders@chromium.org>, Rakesh Pillai <pillair@codeaurora.org>
+Cc: Yongqin Liu <yongqin.liu@linaro.org>, Stable <stable@vger.kernel.org>, Brian Norris <briannorris@chromium.org>, Kalle Valo <kvalo@codeaurora.org>
+Message-ID: <20240108153737.3538218-5-amit.pundir@linaro.org>
+
+From: Douglas Anderson <dianders@chromium.org>
+
+[ Upstream commit 7f86551665121931ecd6d327e019e7a69782bfcd ]
+
+As of the patch ("ath10k: Keep track of which interrupts fired, don't
+poll them") we now have no users of this hardware parameter. Remove
+it.
+
+Suggested-by: Brian Norris <briannorris@chromium.org>
+Signed-off-by: Douglas Anderson <dianders@chromium.org>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+Link: https://lore.kernel.org/r/20200709082024.v2.2.I083faa4e62e69f863311c89ae5eb28ec5a229b70@changeid
+Stable-dep-of: 170c75d43a77 ("ath10k: Don't touch the CE interrupt registers after power up")
+Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/ath/ath10k/core.c | 13 -------------
+ drivers/net/wireless/ath/ath10k/hw.h | 3 ---
+ 2 files changed, 16 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath10k/core.c
++++ b/drivers/net/wireless/ath/ath10k/core.c
+@@ -118,7 +118,6 @@ static const struct ath10k_hw_params ath
+ .num_wds_entries = 0x20,
+ .target_64bit = false,
+ .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+- .per_ce_irq = false,
+ .shadow_reg_support = false,
+ .rri_on_ddr = false,
+ .hw_filter_reset_required = true,
+@@ -154,7 +153,6 @@ static const struct ath10k_hw_params ath
+ .num_wds_entries = 0x20,
+ .target_64bit = false,
+ .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+- .per_ce_irq = false,
+ .shadow_reg_support = false,
+ .rri_on_ddr = false,
+ .hw_filter_reset_required = true,
+@@ -217,7 +215,6 @@ static const struct ath10k_hw_params ath
+ .num_wds_entries = 0x20,
+ .target_64bit = false,
+ .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+- .per_ce_irq = false,
+ .shadow_reg_support = false,
+ .rri_on_ddr = false,
+ .hw_filter_reset_required = true,
+@@ -252,7 +249,6 @@ static const struct ath10k_hw_params ath
+ .num_wds_entries = 0x20,
+ .target_64bit = false,
+ .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+- .per_ce_irq = false,
+ .shadow_reg_support = false,
+ .rri_on_ddr = false,
+ .hw_filter_reset_required = true,
+@@ -287,7 +283,6 @@ static const struct ath10k_hw_params ath
+ .num_wds_entries = 0x20,
+ .target_64bit = false,
+ .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+- .per_ce_irq = false,
+ .shadow_reg_support = false,
+ .rri_on_ddr = false,
+ .hw_filter_reset_required = true,
+@@ -325,7 +320,6 @@ static const struct ath10k_hw_params ath
+ .num_wds_entries = 0x20,
+ .target_64bit = false,
+ .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+- .per_ce_irq = false,
+ .shadow_reg_support = false,
+ .rri_on_ddr = false,
+ .hw_filter_reset_required = true,
+@@ -366,7 +360,6 @@ static const struct ath10k_hw_params ath
+ .num_wds_entries = 0x20,
+ .target_64bit = false,
+ .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+- .per_ce_irq = false,
+ .shadow_reg_support = false,
+ .rri_on_ddr = false,
+ .hw_filter_reset_required = true,
+@@ -414,7 +407,6 @@ static const struct ath10k_hw_params ath
+ .num_wds_entries = 0x20,
+ .target_64bit = false,
+ .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+- .per_ce_irq = false,
+ .shadow_reg_support = false,
+ .rri_on_ddr = false,
+ .hw_filter_reset_required = true,
+@@ -459,7 +451,6 @@ static const struct ath10k_hw_params ath
+ .num_wds_entries = 0x20,
+ .target_64bit = false,
+ .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+- .per_ce_irq = false,
+ .shadow_reg_support = false,
+ .rri_on_ddr = false,
+ .hw_filter_reset_required = true,
+@@ -494,7 +485,6 @@ static const struct ath10k_hw_params ath
+ .num_wds_entries = 0x20,
+ .target_64bit = false,
+ .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+- .per_ce_irq = false,
+ .shadow_reg_support = false,
+ .rri_on_ddr = false,
+ .hw_filter_reset_required = true,
+@@ -531,7 +521,6 @@ static const struct ath10k_hw_params ath
+ .num_wds_entries = 0x20,
+ .target_64bit = false,
+ .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+- .per_ce_irq = false,
+ .shadow_reg_support = false,
+ .rri_on_ddr = false,
+ .hw_filter_reset_required = true,
+@@ -573,7 +562,6 @@ static const struct ath10k_hw_params ath
+ .num_wds_entries = 0x20,
+ .target_64bit = false,
+ .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL,
+- .per_ce_irq = false,
+ .shadow_reg_support = false,
+ .rri_on_ddr = false,
+ .hw_filter_reset_required = true,
+@@ -601,7 +589,6 @@ static const struct ath10k_hw_params ath
+ .num_wds_entries = TARGET_HL_TLV_NUM_WDS_ENTRIES,
+ .target_64bit = true,
+ .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL_DUAL_MAC,
+- .per_ce_irq = true,
+ .shadow_reg_support = true,
+ .rri_on_ddr = true,
+ .hw_filter_reset_required = false,
+--- a/drivers/net/wireless/ath/ath10k/hw.h
++++ b/drivers/net/wireless/ath/ath10k/hw.h
+@@ -590,9 +590,6 @@ struct ath10k_hw_params {
+ /* Target rx ring fill level */
+ u32 rx_ring_fill_level;
+
+- /* target supporting per ce IRQ */
+- bool per_ce_irq;
+-
+ /* target supporting shadow register for ce write */
+ bool shadow_reg_support;
+
--- /dev/null
+From stable+bounces-10189-greg=kroah.com@vger.kernel.org Mon Jan 8 16:37:55 2024
+From: Amit Pundir <amit.pundir@linaro.org>
+Date: Mon, 8 Jan 2024 21:07:36 +0530
+Subject: ath10k: Keep track of which interrupts fired, don't poll them
+To: Greg KH <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>, Douglas Anderson <dianders@chromium.org>, Rakesh Pillai <pillair@codeaurora.org>
+Cc: Yongqin Liu <yongqin.liu@linaro.org>, Stable <stable@vger.kernel.org>, Brian Norris <briannorris@chromium.org>, Kalle Valo <kvalo@codeaurora.org>
+Message-ID: <20240108153737.3538218-4-amit.pundir@linaro.org>
+
+From: Douglas Anderson <dianders@chromium.org>
+
+[ Upstream commit d66d24ac300cf41c6b88367fc9b4b6348679273d ]
+
+If we have a per CE (Copy Engine) IRQ then we have no summary
+register. Right now the code generates a summary register by
+iterating over all copy engines and seeing if they have an interrupt
+pending.
+
+This has a problem. Specifically if _none_ if the Copy Engines have
+an interrupt pending then they might go into low power mode and
+reading from their address space will cause a full system crash. This
+was seen to happen when two interrupts went off at nearly the same
+time. Both were handled by a single call of ath10k_snoc_napi_poll()
+but, because there were two interrupts handled and thus two calls to
+napi_schedule() there was still a second call to
+ath10k_snoc_napi_poll() which ran with no interrupts pending.
+
+Instead of iterating over all the copy engines, let's just keep track
+of the IRQs that fire. Then we can effectively generate our own
+summary without ever needing to read the Copy Engines.
+
+Tested-on: WCN3990 SNOC WLAN.HL.3.2.2-00490-QCAHLSWMTPL-1
+
+Signed-off-by: Douglas Anderson <dianders@chromium.org>
+Reviewed-by: Rakesh Pillai <pillair@codeaurora.org>
+Reviewed-by: Brian Norris <briannorris@chromium.org>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+Link: https://lore.kernel.org/r/20200709082024.v2.1.I4d2f85ffa06f38532631e864a3125691ef5ffe06@changeid
+Stable-dep-of: 170c75d43a77 ("ath10k: Don't touch the CE interrupt registers after power up")
+Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/ath/ath10k/ce.c | 84 ++++++++++++---------------------
+ drivers/net/wireless/ath/ath10k/ce.h | 14 ++---
+ drivers/net/wireless/ath/ath10k/snoc.c | 19 +++++--
+ drivers/net/wireless/ath/ath10k/snoc.h | 1
+ 4 files changed, 52 insertions(+), 66 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath10k/ce.c
++++ b/drivers/net/wireless/ath/ath10k/ce.c
+@@ -481,38 +481,6 @@ static inline void ath10k_ce_engine_int_
+ ath10k_ce_write32(ar, ce_ctrl_addr + wm_regs->addr, mask);
+ }
+
+-static bool ath10k_ce_engine_int_status_check(struct ath10k *ar, u32 ce_ctrl_addr,
+- unsigned int mask)
+-{
+- struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs;
+-
+- return ath10k_ce_read32(ar, ce_ctrl_addr + wm_regs->addr) & mask;
+-}
+-
+-u32 ath10k_ce_gen_interrupt_summary(struct ath10k *ar)
+-{
+- struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs;
+- struct ath10k_ce_pipe *ce_state;
+- struct ath10k_ce *ce;
+- u32 irq_summary = 0;
+- u32 ctrl_addr;
+- u32 ce_id;
+-
+- ce = ath10k_ce_priv(ar);
+-
+- for (ce_id = 0; ce_id < CE_COUNT; ce_id++) {
+- ce_state = &ce->ce_states[ce_id];
+- ctrl_addr = ce_state->ctrl_addr;
+- if (ath10k_ce_engine_int_status_check(ar, ctrl_addr,
+- wm_regs->cc_mask)) {
+- irq_summary |= BIT(ce_id);
+- }
+- }
+-
+- return irq_summary;
+-}
+-EXPORT_SYMBOL(ath10k_ce_gen_interrupt_summary);
+-
+ /*
+ * Guts of ath10k_ce_send.
+ * The caller takes responsibility for any needed locking.
+@@ -1399,45 +1367,55 @@ static void ath10k_ce_per_engine_handler
+ ath10k_ce_watermark_intr_disable(ar, ctrl_addr);
+ }
+
+-int ath10k_ce_disable_interrupts(struct ath10k *ar)
++void ath10k_ce_disable_interrupt(struct ath10k *ar, int ce_id)
+ {
+ struct ath10k_ce *ce = ath10k_ce_priv(ar);
+ struct ath10k_ce_pipe *ce_state;
+ u32 ctrl_addr;
+- int ce_id;
+
+- for (ce_id = 0; ce_id < CE_COUNT; ce_id++) {
+- ce_state = &ce->ce_states[ce_id];
+- if (ce_state->attr_flags & CE_ATTR_POLL)
+- continue;
++ ce_state = &ce->ce_states[ce_id];
++ if (ce_state->attr_flags & CE_ATTR_POLL)
++ return;
+
+- ctrl_addr = ath10k_ce_base_address(ar, ce_id);
++ ctrl_addr = ath10k_ce_base_address(ar, ce_id);
+
+- ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr);
+- ath10k_ce_error_intr_disable(ar, ctrl_addr);
+- ath10k_ce_watermark_intr_disable(ar, ctrl_addr);
+- }
++ ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr);
++ ath10k_ce_error_intr_disable(ar, ctrl_addr);
++ ath10k_ce_watermark_intr_disable(ar, ctrl_addr);
++}
++EXPORT_SYMBOL(ath10k_ce_disable_interrupt);
+
+- return 0;
++void ath10k_ce_disable_interrupts(struct ath10k *ar)
++{
++ int ce_id;
++
++ for (ce_id = 0; ce_id < CE_COUNT; ce_id++)
++ ath10k_ce_disable_interrupt(ar, ce_id);
+ }
+ EXPORT_SYMBOL(ath10k_ce_disable_interrupts);
+
+-void ath10k_ce_enable_interrupts(struct ath10k *ar)
++void ath10k_ce_enable_interrupt(struct ath10k *ar, int ce_id)
+ {
+ struct ath10k_ce *ce = ath10k_ce_priv(ar);
+- int ce_id;
+ struct ath10k_ce_pipe *ce_state;
+
++ ce_state = &ce->ce_states[ce_id];
++ if (ce_state->attr_flags & CE_ATTR_POLL)
++ return;
++
++ ath10k_ce_per_engine_handler_adjust(ce_state);
++}
++EXPORT_SYMBOL(ath10k_ce_enable_interrupt);
++
++void ath10k_ce_enable_interrupts(struct ath10k *ar)
++{
++ int ce_id;
++
+ /* Enable interrupts for copy engine that
+ * are not using polling mode.
+ */
+- for (ce_id = 0; ce_id < CE_COUNT; ce_id++) {
+- ce_state = &ce->ce_states[ce_id];
+- if (ce_state->attr_flags & CE_ATTR_POLL)
+- continue;
+-
+- ath10k_ce_per_engine_handler_adjust(ce_state);
+- }
++ for (ce_id = 0; ce_id < CE_COUNT; ce_id++)
++ ath10k_ce_enable_interrupt(ar, ce_id);
+ }
+ EXPORT_SYMBOL(ath10k_ce_enable_interrupts);
+
+--- a/drivers/net/wireless/ath/ath10k/ce.h
++++ b/drivers/net/wireless/ath/ath10k/ce.h
+@@ -255,12 +255,13 @@ int ath10k_ce_cancel_send_next(struct at
+ /*==================CE Interrupt Handlers====================*/
+ void ath10k_ce_per_engine_service_any(struct ath10k *ar);
+ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id);
+-int ath10k_ce_disable_interrupts(struct ath10k *ar);
++void ath10k_ce_disable_interrupt(struct ath10k *ar, int ce_id);
++void ath10k_ce_disable_interrupts(struct ath10k *ar);
++void ath10k_ce_enable_interrupt(struct ath10k *ar, int ce_id);
+ void ath10k_ce_enable_interrupts(struct ath10k *ar);
+ void ath10k_ce_dump_registers(struct ath10k *ar,
+ struct ath10k_fw_crash_data *crash_data);
+
+-u32 ath10k_ce_gen_interrupt_summary(struct ath10k *ar);
+ void ath10k_ce_alloc_rri(struct ath10k *ar);
+ void ath10k_ce_free_rri(struct ath10k *ar);
+
+@@ -376,12 +377,9 @@ static inline u32 ath10k_ce_interrupt_su
+ {
+ struct ath10k_ce *ce = ath10k_ce_priv(ar);
+
+- if (!ar->hw_params.per_ce_irq)
+- return CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET(
+- ce->bus_ops->read32((ar), CE_WRAPPER_BASE_ADDRESS +
+- CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS));
+- else
+- return ath10k_ce_gen_interrupt_summary(ar);
++ return CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET(
++ ce->bus_ops->read32((ar), CE_WRAPPER_BASE_ADDRESS +
++ CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS));
+ }
+
+ /* Host software's Copy Engine configuration. */
+--- a/drivers/net/wireless/ath/ath10k/snoc.c
++++ b/drivers/net/wireless/ath/ath10k/snoc.c
+@@ -3,6 +3,7 @@
+ * Copyright (c) 2018 The Linux Foundation. All rights reserved.
+ */
+
++#include <linux/bits.h>
+ #include <linux/clk.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+@@ -927,6 +928,7 @@ static int ath10k_snoc_hif_start(struct
+ {
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
++ bitmap_clear(ar_snoc->pending_ce_irqs, 0, CE_COUNT_MAX);
+ napi_enable(&ar->napi);
+ ath10k_snoc_irq_enable(ar);
+ ath10k_snoc_rx_post(ar);
+@@ -1166,7 +1168,9 @@ static irqreturn_t ath10k_snoc_per_engin
+ return IRQ_HANDLED;
+ }
+
+- ath10k_snoc_irq_disable(ar);
++ ath10k_ce_disable_interrupt(ar, ce_id);
++ set_bit(ce_id, ar_snoc->pending_ce_irqs);
++
+ napi_schedule(&ar->napi);
+
+ return IRQ_HANDLED;
+@@ -1175,20 +1179,25 @@ static irqreturn_t ath10k_snoc_per_engin
+ static int ath10k_snoc_napi_poll(struct napi_struct *ctx, int budget)
+ {
+ struct ath10k *ar = container_of(ctx, struct ath10k, napi);
++ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+ int done = 0;
++ int ce_id;
+
+ if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) {
+ napi_complete(ctx);
+ return done;
+ }
+
+- ath10k_ce_per_engine_service_any(ar);
++ for (ce_id = 0; ce_id < CE_COUNT; ce_id++)
++ if (test_and_clear_bit(ce_id, ar_snoc->pending_ce_irqs)) {
++ ath10k_ce_per_engine_service(ar, ce_id);
++ ath10k_ce_enable_interrupt(ar, ce_id);
++ }
++
+ done = ath10k_htt_txrx_compl_task(ar, budget);
+
+- if (done < budget) {
++ if (done < budget)
+ napi_complete(ctx);
+- ath10k_snoc_irq_enable(ar);
+- }
+
+ return done;
+ }
+--- a/drivers/net/wireless/ath/ath10k/snoc.h
++++ b/drivers/net/wireless/ath/ath10k/snoc.h
+@@ -81,6 +81,7 @@ struct ath10k_snoc {
+ struct ath10k_clk_info *clk;
+ struct ath10k_qmi *qmi;
+ unsigned long flags;
++ DECLARE_BITMAP(pending_ce_irqs, CE_COUNT_MAX);
+ };
+
+ static inline struct ath10k_snoc *ath10k_snoc_priv(struct ath10k *ar)
--- /dev/null
+From stable+bounces-10185-greg=kroah.com@vger.kernel.org Mon Jan 8 16:37:49 2024
+From: Amit Pundir <amit.pundir@linaro.org>
+Date: Mon, 8 Jan 2024 21:07:34 +0530
+Subject: ath10k: Wait until copy complete is actually done before completing
+To: Greg KH <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>, Douglas Anderson <dianders@chromium.org>, Rakesh Pillai <pillair@codeaurora.org>
+Cc: Yongqin Liu <yongqin.liu@linaro.org>, Stable <stable@vger.kernel.org>, Kalle Valo <kvalo@codeaurora.org>
+Message-ID: <20240108153737.3538218-2-amit.pundir@linaro.org>
+
+From: Douglas Anderson <dianders@chromium.org>
+
+[ Upstream commit 8f9ed93d09a97444733d492a3bbf66bcb786a777 ]
+
+On wcn3990 we have "per_ce_irq = true". That makes the
+ath10k_ce_interrupt_summary() function always return 0xfff. The
+ath10k_ce_per_engine_service_any() function will see this and think
+that _all_ copy engines have an interrupt. Without checking, the
+ath10k_ce_per_engine_service() assumes that if it's called that the
+"copy complete" (cc) interrupt fired. This combination seems bad.
+
+Let's add a check to make sure that the "copy complete" interrupt
+actually fired in ath10k_ce_per_engine_service().
+
+This might fix a hard-to-reproduce failure where it appears that the
+copy complete handlers run before the copy is really complete.
+Specifically a symptom was that we were seeing this on a Qualcomm
+sc7180 board:
+ arm-smmu 15000000.iommu: Unhandled context fault:
+ fsr=0x402, iova=0x7fdd45780, fsynr=0x30003, cbfrsynra=0xc1, cb=10
+
+Even on platforms that don't have wcn3990 this still seems like it
+would be a sane thing to do. Specifically the current IRQ handler
+comments indicate that there might be other misc interrupt sources
+firing that need to be cleared. If one of those sources was the one
+that caused the IRQ handler to be called it would also be important to
+double-check that the interrupt we cared about actually fired.
+
+Tested-on: WCN3990 SNOC WLAN.HL.3.2.2-00490-QCAHLSWMTPL-1
+
+Signed-off-by: Douglas Anderson <dianders@chromium.org>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+Link: https://lore.kernel.org/r/20200609082015.1.Ife398994e5a0a6830e4d4a16306ef36e0144e7ba@changeid
+Stable-dep-of: 170c75d43a77 ("ath10k: Don't touch the CE interrupt registers after power up")
+Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/ath/ath10k/ce.c | 30 +++++++++++++++++++++---------
+ 1 file changed, 21 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath10k/ce.c
++++ b/drivers/net/wireless/ath/ath10k/ce.c
+@@ -481,6 +481,15 @@ static inline void ath10k_ce_engine_int_
+ ath10k_ce_write32(ar, ce_ctrl_addr + wm_regs->addr, mask);
+ }
+
++static inline bool ath10k_ce_engine_int_status_check(struct ath10k *ar,
++ u32 ce_ctrl_addr,
++ unsigned int mask)
++{
++ struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs;
++
++ return ath10k_ce_read32(ar, ce_ctrl_addr + wm_regs->addr) & mask;
++}
++
+ /*
+ * Guts of ath10k_ce_send.
+ * The caller takes responsibility for any needed locking.
+@@ -1301,19 +1310,22 @@ void ath10k_ce_per_engine_service(struct
+
+ spin_lock_bh(&ce->ce_lock);
+
+- /* Clear the copy-complete interrupts that will be handled here. */
+- ath10k_ce_engine_int_status_clear(ar, ctrl_addr,
+- wm_regs->cc_mask);
++ if (ath10k_ce_engine_int_status_check(ar, ctrl_addr,
++ wm_regs->cc_mask)) {
++ /* Clear before handling */
++ ath10k_ce_engine_int_status_clear(ar, ctrl_addr,
++ wm_regs->cc_mask);
+
+- spin_unlock_bh(&ce->ce_lock);
++ spin_unlock_bh(&ce->ce_lock);
+
+- if (ce_state->recv_cb)
+- ce_state->recv_cb(ce_state);
++ if (ce_state->recv_cb)
++ ce_state->recv_cb(ce_state);
+
+- if (ce_state->send_cb)
+- ce_state->send_cb(ce_state);
++ if (ce_state->send_cb)
++ ce_state->send_cb(ce_state);
+
+- spin_lock_bh(&ce->ce_lock);
++ spin_lock_bh(&ce->ce_lock);
++ }
+
+ /*
+ * Misc CE interrupts are not being handled, but still need
mmc-rpmb-fixes-pause-retune-on-all-rpmb-partitions.patch
mmc-core-cancel-delayed-work-before-releasing-host.patch
mmc-sdhci-sprd-fix-emmc-init-failure-after-hw-reset.patch
+ath10k-wait-until-copy-complete-is-actually-done-before-completing.patch
+ath10k-add-interrupt-summary-based-ce-processing.patch
+ath10k-keep-track-of-which-interrupts-fired-don-t-poll-them.patch
+ath10k-get-rid-of-per_ce_irq-hw-param.patch