--- /dev/null
+From a5951389e58d2e816eed3dbec5877de9327fd881 Mon Sep 17 00:00:00 2001
+From: Douglas Anderson <dianders@chromium.org>
+Date: Tue, 7 Jan 2025 12:06:02 -0800
+Subject: arm64: errata: Add newer ARM cores to the spectre_bhb_loop_affected() lists
+
+From: Douglas Anderson <dianders@chromium.org>
+
+commit a5951389e58d2e816eed3dbec5877de9327fd881 upstream.
+
+When comparing to the ARM list [1], it appears that several ARM cores
+were missing from the lists in spectre_bhb_loop_affected(). Add them.
+
+NOTE: for some of these cores it may not matter since other ways of
+clearing the BHB may be used (like the CLRBHB instruction or ECBHB),
+but it still seems good to have all the info from ARM's whitepaper
+included.
+
+[1] https://developer.arm.com/Arm%20Security%20Center/Spectre-BHB
+
+Fixes: 558c303c9734 ("arm64: Mitigate spectre style branch history side channels")
+Cc: stable@vger.kernel.org
+Signed-off-by: Douglas Anderson <dianders@chromium.org>
+Reviewed-by: James Morse <james.morse@arm.com>
+Link: https://lore.kernel.org/r/20250107120555.v4.5.I4a9a527e03f663040721c5401c41de587d015c82@changeid
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/arm64/kernel/proton-pack.c | 15 ++++++++++++++-
+ 1 file changed, 14 insertions(+), 1 deletion(-)
+
+--- a/arch/arm64/kernel/proton-pack.c
++++ b/arch/arm64/kernel/proton-pack.c
+@@ -876,6 +876,14 @@ static u8 spectre_bhb_loop_affected(void
+ {
+ u8 k = 0;
+
++ static const struct midr_range spectre_bhb_k132_list[] = {
++ MIDR_ALL_VERSIONS(MIDR_CORTEX_X3),
++ MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V2),
++ };
++ static const struct midr_range spectre_bhb_k38_list[] = {
++ MIDR_ALL_VERSIONS(MIDR_CORTEX_A715),
++ MIDR_ALL_VERSIONS(MIDR_CORTEX_A720),
++ };
+ static const struct midr_range spectre_bhb_k32_list[] = {
+ MIDR_ALL_VERSIONS(MIDR_CORTEX_A78),
+ MIDR_ALL_VERSIONS(MIDR_CORTEX_A78AE),
+@@ -889,6 +897,7 @@ static u8 spectre_bhb_loop_affected(void
+ };
+ static const struct midr_range spectre_bhb_k24_list[] = {
+ MIDR_ALL_VERSIONS(MIDR_CORTEX_A76),
++ MIDR_ALL_VERSIONS(MIDR_CORTEX_A76AE),
+ MIDR_ALL_VERSIONS(MIDR_CORTEX_A77),
+ MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N1),
+ MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_4XX_GOLD),
+@@ -904,7 +913,11 @@ static u8 spectre_bhb_loop_affected(void
+ {},
+ };
+
+- if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k32_list))
++ if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k132_list))
++ k = 132;
++ else if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k38_list))
++ k = 38;
++ else if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k32_list))
+ k = 32;
+ else if (is_midr_in_range_list(read_cpuid_id(), spectre_bhb_k24_list))
+ k = 24;
--- /dev/null
+From 55c85fa7579dc2e3f5399ef5bad67a44257c1a48 Mon Sep 17 00:00:00 2001
+From: Yi Liu <yi.l.liu@intel.com>
+Date: Wed, 5 Mar 2025 19:48:42 -0800
+Subject: iommufd: Fail replace if device has not been attached
+
+From: Yi Liu <yi.l.liu@intel.com>
+
+commit 55c85fa7579dc2e3f5399ef5bad67a44257c1a48 upstream.
+
+The current implementation of iommufd_device_do_replace() implicitly
+assumes that the input device has already been attached. However, there
+is no explicit check to verify this assumption. If another device within
+the same group has been attached, the replace operation might succeed,
+but the input device itself may not have been attached yet.
+
+As a result, the input device might not be tracked in the
+igroup->device_list, and its reserved IOVA might not be added. Despite
+this, the caller might incorrectly assume that the device has been
+successfully replaced, which could lead to unexpected behavior or errors.
+
+To address this issue, add a check to ensure that the input device has
+been attached before proceeding with the replace operation. This check
+will help maintain the integrity of the device tracking system and prevent
+potential issues arising from incorrect assumptions about the device's
+attachment status.
+
+Fixes: e88d4ec154a8 ("iommufd: Add iommufd_device_replace()")
+Link: https://patch.msgid.link/r/20250306034842.5950-1-yi.l.liu@intel.com
+Cc: stable@vger.kernel.org
+Reviewed-by: Kevin Tian <kevin.tian@intel.com>
+Signed-off-by: Yi Liu <yi.l.liu@intel.com>
+Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iommu/iommufd/device.c | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/drivers/iommu/iommufd/device.c
++++ b/drivers/iommu/iommufd/device.c
+@@ -354,6 +354,17 @@ iommufd_device_attach_reserved_iova(stru
+
+ /* The device attach/detach/replace helpers for attach_handle */
+
++/* Check if idev is attached to igroup->hwpt */
++static bool iommufd_device_is_attached(struct iommufd_device *idev)
++{
++ struct iommufd_device *cur;
++
++ list_for_each_entry(cur, &idev->igroup->device_list, group_item)
++ if (cur == idev)
++ return true;
++ return false;
++}
++
+ static int iommufd_hwpt_attach_device(struct iommufd_hw_pagetable *hwpt,
+ struct iommufd_device *idev)
+ {
+@@ -592,6 +603,11 @@ iommufd_device_do_replace(struct iommufd
+ rc = -EINVAL;
+ goto err_unlock;
+ }
++
++ if (!iommufd_device_is_attached(idev)) {
++ rc = -EINVAL;
++ goto err_unlock;
++ }
+
+ if (hwpt == igroup->hwpt) {
+ mutex_unlock(&idev->igroup->lock);
--- /dev/null
+From fb21b1568adaa76af7a8c853f37c60fba8b28661 Mon Sep 17 00:00:00 2001
+From: Nicolin Chen <nicolinc@nvidia.com>
+Date: Mon, 3 Feb 2025 21:00:54 -0800
+Subject: iommufd: Make attach_handle generic than fault specific
+
+From: Nicolin Chen <nicolinc@nvidia.com>
+
+commit fb21b1568adaa76af7a8c853f37c60fba8b28661 upstream.
+
+"attach_handle" was added exclusively for the iommufd_fault_iopf_handler()
+used by IOPF/PRI use cases. Now, both the MSI and PASID series require to
+reuse the attach_handle for non-fault cases.
+
+Add a set of new attach/detach/replace helpers that does the attach_handle
+allocation/releasing/replacement in the common path and also handles those
+fault specific routines such as iopf enabling/disabling and auto response.
+
+This covers both non-fault and fault cases in a clean way, replacing those
+inline helpers in the header. The following patch will clean up those old
+helpers in the fault.c file.
+
+Link: https://patch.msgid.link/r/32687df01c02291d89986a9fca897bbbe2b10987.1738645017.git.nicolinc@nvidia.com
+Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
+Reviewed-by: Yi Liu <yi.l.liu@intel.com>
+Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iommu/iommufd/device.c | 105 ++++++++++++++++++++++++++++++++
+ drivers/iommu/iommufd/fault.c | 8 +-
+ drivers/iommu/iommufd/iommufd_private.h | 33 +---------
+ 3 files changed, 113 insertions(+), 33 deletions(-)
+
+--- a/drivers/iommu/iommufd/device.c
++++ b/drivers/iommu/iommufd/device.c
+@@ -352,6 +352,111 @@ iommufd_device_attach_reserved_iova(stru
+ return 0;
+ }
+
++/* The device attach/detach/replace helpers for attach_handle */
++
++static int iommufd_hwpt_attach_device(struct iommufd_hw_pagetable *hwpt,
++ struct iommufd_device *idev)
++{
++ struct iommufd_attach_handle *handle;
++ int rc;
++
++ lockdep_assert_held(&idev->igroup->lock);
++
++ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
++ if (!handle)
++ return -ENOMEM;
++
++ if (hwpt->fault) {
++ rc = iommufd_fault_iopf_enable(idev);
++ if (rc)
++ goto out_free_handle;
++ }
++
++ handle->idev = idev;
++ rc = iommu_attach_group_handle(hwpt->domain, idev->igroup->group,
++ &handle->handle);
++ if (rc)
++ goto out_disable_iopf;
++
++ return 0;
++
++out_disable_iopf:
++ if (hwpt->fault)
++ iommufd_fault_iopf_disable(idev);
++out_free_handle:
++ kfree(handle);
++ return rc;
++}
++
++static struct iommufd_attach_handle *
++iommufd_device_get_attach_handle(struct iommufd_device *idev)
++{
++ struct iommu_attach_handle *handle;
++
++ lockdep_assert_held(&idev->igroup->lock);
++
++ handle =
++ iommu_attach_handle_get(idev->igroup->group, IOMMU_NO_PASID, 0);
++ if (IS_ERR(handle))
++ return NULL;
++ return to_iommufd_handle(handle);
++}
++
++static void iommufd_hwpt_detach_device(struct iommufd_hw_pagetable *hwpt,
++ struct iommufd_device *idev)
++{
++ struct iommufd_attach_handle *handle;
++
++ handle = iommufd_device_get_attach_handle(idev);
++ iommu_detach_group_handle(hwpt->domain, idev->igroup->group);
++ if (hwpt->fault) {
++ iommufd_auto_response_faults(hwpt, handle);
++ iommufd_fault_iopf_disable(idev);
++ }
++ kfree(handle);
++}
++
++static int iommufd_hwpt_replace_device(struct iommufd_device *idev,
++ struct iommufd_hw_pagetable *hwpt,
++ struct iommufd_hw_pagetable *old)
++{
++ struct iommufd_attach_handle *handle, *old_handle =
++ iommufd_device_get_attach_handle(idev);
++ int rc;
++
++ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
++ if (!handle)
++ return -ENOMEM;
++
++ if (hwpt->fault && !old->fault) {
++ rc = iommufd_fault_iopf_enable(idev);
++ if (rc)
++ goto out_free_handle;
++ }
++
++ handle->idev = idev;
++ rc = iommu_replace_group_handle(idev->igroup->group, hwpt->domain,
++ &handle->handle);
++ if (rc)
++ goto out_disable_iopf;
++
++ if (old->fault) {
++ iommufd_auto_response_faults(hwpt, old_handle);
++ if (!hwpt->fault)
++ iommufd_fault_iopf_disable(idev);
++ }
++ kfree(old_handle);
++
++ return 0;
++
++out_disable_iopf:
++ if (hwpt->fault && !old->fault)
++ iommufd_fault_iopf_disable(idev);
++out_free_handle:
++ kfree(handle);
++ return rc;
++}
++
+ int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
+ struct iommufd_device *idev)
+ {
+--- a/drivers/iommu/iommufd/fault.c
++++ b/drivers/iommu/iommufd/fault.c
+@@ -17,7 +17,7 @@
+ #include "../iommu-priv.h"
+ #include "iommufd_private.h"
+
+-static int iommufd_fault_iopf_enable(struct iommufd_device *idev)
++int iommufd_fault_iopf_enable(struct iommufd_device *idev)
+ {
+ struct device *dev = idev->dev;
+ int ret;
+@@ -50,7 +50,7 @@ static int iommufd_fault_iopf_enable(str
+ return ret;
+ }
+
+-static void iommufd_fault_iopf_disable(struct iommufd_device *idev)
++void iommufd_fault_iopf_disable(struct iommufd_device *idev)
+ {
+ mutex_lock(&idev->iopf_lock);
+ if (!WARN_ON(idev->iopf_enabled == 0)) {
+@@ -98,8 +98,8 @@ int iommufd_fault_domain_attach_dev(stru
+ return ret;
+ }
+
+-static void iommufd_auto_response_faults(struct iommufd_hw_pagetable *hwpt,
+- struct iommufd_attach_handle *handle)
++void iommufd_auto_response_faults(struct iommufd_hw_pagetable *hwpt,
++ struct iommufd_attach_handle *handle)
+ {
+ struct iommufd_fault *fault = hwpt->fault;
+ struct iopf_group *group, *next;
+--- a/drivers/iommu/iommufd/iommufd_private.h
++++ b/drivers/iommu/iommufd/iommufd_private.h
+@@ -504,35 +504,10 @@ int iommufd_fault_domain_replace_dev(str
+ struct iommufd_hw_pagetable *hwpt,
+ struct iommufd_hw_pagetable *old);
+
+-static inline int iommufd_hwpt_attach_device(struct iommufd_hw_pagetable *hwpt,
+- struct iommufd_device *idev)
+-{
+- if (hwpt->fault)
+- return iommufd_fault_domain_attach_dev(hwpt, idev);
+-
+- return iommu_attach_group(hwpt->domain, idev->igroup->group);
+-}
+-
+-static inline void iommufd_hwpt_detach_device(struct iommufd_hw_pagetable *hwpt,
+- struct iommufd_device *idev)
+-{
+- if (hwpt->fault) {
+- iommufd_fault_domain_detach_dev(hwpt, idev);
+- return;
+- }
+-
+- iommu_detach_group(hwpt->domain, idev->igroup->group);
+-}
+-
+-static inline int iommufd_hwpt_replace_device(struct iommufd_device *idev,
+- struct iommufd_hw_pagetable *hwpt,
+- struct iommufd_hw_pagetable *old)
+-{
+- if (old->fault || hwpt->fault)
+- return iommufd_fault_domain_replace_dev(idev, hwpt, old);
+-
+- return iommu_group_replace_domain(idev->igroup->group, hwpt->domain);
+-}
++int iommufd_fault_iopf_enable(struct iommufd_device *idev);
++void iommufd_fault_iopf_disable(struct iommufd_device *idev);
++void iommufd_auto_response_faults(struct iommufd_hw_pagetable *hwpt,
++ struct iommufd_attach_handle *handle);
+
+ static inline struct iommufd_viommu *
+ iommufd_get_viommu(struct iommufd_ucmd *ucmd, u32 id)
spi-fsl-qspi-fix-double-cleanup-in-probe-error-path.patch
thermal-drivers-mediatek-lvts-disable-monitor-mode-during-suspend.patch
thermal-drivers-mediatek-lvts-disable-stage-3-thermal-threshold.patch
+wifi-ath11k-update-channel-list-in-worker-when-wait-flag-is-set.patch
+arm64-errata-add-newer-arm-cores-to-the-spectre_bhb_loop_affected-lists.patch
+iommufd-make-attach_handle-generic-than-fault-specific.patch
+iommufd-fail-replace-if-device-has-not-been-attached.patch
--- /dev/null
+From 02aae8e2f957adc1b15b6b8055316f8a154ac3f5 Mon Sep 17 00:00:00 2001
+From: Wen Gong <quic_wgong@quicinc.com>
+Date: Fri, 17 Jan 2025 14:17:37 +0800
+Subject: wifi: ath11k: update channel list in worker when wait flag is set
+
+From: Wen Gong <quic_wgong@quicinc.com>
+
+commit 02aae8e2f957adc1b15b6b8055316f8a154ac3f5 upstream.
+
+With previous patch "wifi: ath11k: move update channel list from update
+reg worker to reg notifier", ath11k_reg_update_chan_list() will be
+called during reg_process_self_managed_hint().
+
+reg_process_self_managed_hint() will hold rtnl_lock all the time.
+But ath11k_reg_update_chan_list() may increase the occupation time of
+rtnl_lock, because when wait flag is set, wait_for_completion_timeout()
+will be called during 11d/hw scan.
+
+Should minimize the occupation time of rtnl_lock as much as possible
+to avoid interfering with rest of the system. So move the update channel
+list operation to a new worker, so that wait_for_completion_timeout()
+won't be called and will not increase the occupation time of rtnl_lock.
+
+Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3
+
+Signed-off-by: Wen Gong <quic_wgong@quicinc.com>
+Co-developed-by: Kang Yang <quic_kangyang@quicinc.com>
+Signed-off-by: Kang Yang <quic_kangyang@quicinc.com>
+Reviewed-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
+Link: https://patch.msgid.link/20250117061737.1921-3-quic_kangyang@quicinc.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Cc: Mario Limonciello <superm1@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/ath/ath11k/core.c | 1
+ drivers/net/wireless/ath/ath11k/core.h | 5 +
+ drivers/net/wireless/ath/ath11k/mac.c | 14 +++++
+ drivers/net/wireless/ath/ath11k/reg.c | 85 ++++++++++++++++++++++-----------
+ drivers/net/wireless/ath/ath11k/reg.h | 3 -
+ drivers/net/wireless/ath/ath11k/wmi.h | 1
+ 6 files changed, 81 insertions(+), 28 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath11k/core.c
++++ b/drivers/net/wireless/ath/ath11k/core.c
+@@ -2056,6 +2056,7 @@ void ath11k_core_halt(struct ath11k *ar)
+ ath11k_mac_scan_finish(ar);
+ ath11k_mac_peer_cleanup_all(ar);
+ cancel_delayed_work_sync(&ar->scan.timeout);
++ cancel_work_sync(&ar->channel_update_work);
+ cancel_work_sync(&ar->regd_update_work);
+ cancel_work_sync(&ab->update_11d_work);
+
+--- a/drivers/net/wireless/ath/ath11k/core.h
++++ b/drivers/net/wireless/ath/ath11k/core.h
+@@ -685,7 +685,7 @@ struct ath11k {
+ struct mutex conf_mutex;
+ /* protects the radio specific data like debug stats, ppdu_stats_info stats,
+ * vdev_stop_status info, scan data, ath11k_sta info, ath11k_vif info,
+- * channel context data, survey info, test mode data.
++ * channel context data, survey info, test mode data, channel_update_queue.
+ */
+ spinlock_t data_lock;
+
+@@ -743,6 +743,9 @@ struct ath11k {
+ struct completion bss_survey_done;
+
+ struct work_struct regd_update_work;
++ struct work_struct channel_update_work;
++ /* protected with data_lock */
++ struct list_head channel_update_queue;
+
+ struct work_struct wmi_mgmt_tx_work;
+ struct sk_buff_head wmi_mgmt_tx_queue;
+--- a/drivers/net/wireless/ath/ath11k/mac.c
++++ b/drivers/net/wireless/ath/ath11k/mac.c
+@@ -6283,6 +6283,7 @@ static void ath11k_mac_op_stop(struct ie
+ {
+ struct ath11k *ar = hw->priv;
+ struct htt_ppdu_stats_info *ppdu_stats, *tmp;
++ struct scan_chan_list_params *params;
+ int ret;
+
+ ath11k_mac_drain_tx(ar);
+@@ -6298,6 +6299,7 @@ static void ath11k_mac_op_stop(struct ie
+ mutex_unlock(&ar->conf_mutex);
+
+ cancel_delayed_work_sync(&ar->scan.timeout);
++ cancel_work_sync(&ar->channel_update_work);
+ cancel_work_sync(&ar->regd_update_work);
+ cancel_work_sync(&ar->ab->update_11d_work);
+
+@@ -6307,10 +6309,19 @@ static void ath11k_mac_op_stop(struct ie
+ }
+
+ spin_lock_bh(&ar->data_lock);
++
+ list_for_each_entry_safe(ppdu_stats, tmp, &ar->ppdu_stats_info, list) {
+ list_del(&ppdu_stats->list);
+ kfree(ppdu_stats);
+ }
++
++ while ((params = list_first_entry_or_null(&ar->channel_update_queue,
++ struct scan_chan_list_params,
++ list))) {
++ list_del(¶ms->list);
++ kfree(params);
++ }
++
+ spin_unlock_bh(&ar->data_lock);
+
+ rcu_assign_pointer(ar->ab->pdevs_active[ar->pdev_idx], NULL);
+@@ -10014,6 +10025,7 @@ static const struct wiphy_iftype_ext_cap
+
+ static void __ath11k_mac_unregister(struct ath11k *ar)
+ {
++ cancel_work_sync(&ar->channel_update_work);
+ cancel_work_sync(&ar->regd_update_work);
+
+ ieee80211_unregister_hw(ar->hw);
+@@ -10413,6 +10425,8 @@ int ath11k_mac_allocate(struct ath11k_ba
+ init_completion(&ar->thermal.wmi_sync);
+
+ INIT_DELAYED_WORK(&ar->scan.timeout, ath11k_scan_timeout_work);
++ INIT_WORK(&ar->channel_update_work, ath11k_regd_update_chan_list_work);
++ INIT_LIST_HEAD(&ar->channel_update_queue);
+ INIT_WORK(&ar->regd_update_work, ath11k_regd_update_work);
+
+ INIT_WORK(&ar->wmi_mgmt_tx_work, ath11k_mgmt_over_wmi_tx_work);
+--- a/drivers/net/wireless/ath/ath11k/reg.c
++++ b/drivers/net/wireless/ath/ath11k/reg.c
+@@ -124,32 +124,7 @@ int ath11k_reg_update_chan_list(struct a
+ struct channel_param *ch;
+ enum nl80211_band band;
+ int num_channels = 0;
+- int i, ret, left;
+-
+- if (wait && ar->state_11d != ATH11K_11D_IDLE) {
+- left = wait_for_completion_timeout(&ar->completed_11d_scan,
+- ATH11K_SCAN_TIMEOUT_HZ);
+- if (!left) {
+- ath11k_dbg(ar->ab, ATH11K_DBG_REG,
+- "failed to receive 11d scan complete: timed out\n");
+- ar->state_11d = ATH11K_11D_IDLE;
+- }
+- ath11k_dbg(ar->ab, ATH11K_DBG_REG,
+- "11d scan wait left time %d\n", left);
+- }
+-
+- if (wait &&
+- (ar->scan.state == ATH11K_SCAN_STARTING ||
+- ar->scan.state == ATH11K_SCAN_RUNNING)) {
+- left = wait_for_completion_timeout(&ar->scan.completed,
+- ATH11K_SCAN_TIMEOUT_HZ);
+- if (!left)
+- ath11k_dbg(ar->ab, ATH11K_DBG_REG,
+- "failed to receive hw scan complete: timed out\n");
+-
+- ath11k_dbg(ar->ab, ATH11K_DBG_REG,
+- "hw scan wait left time %d\n", left);
+- }
++ int i, ret = 0;
+
+ if (ar->state == ATH11K_STATE_RESTARTING)
+ return 0;
+@@ -231,6 +206,16 @@ int ath11k_reg_update_chan_list(struct a
+ }
+ }
+
++ if (wait) {
++ spin_lock_bh(&ar->data_lock);
++ list_add_tail(¶ms->list, &ar->channel_update_queue);
++ spin_unlock_bh(&ar->data_lock);
++
++ queue_work(ar->ab->workqueue, &ar->channel_update_work);
++
++ return 0;
++ }
++
+ ret = ath11k_wmi_send_scan_chan_list_cmd(ar, params);
+ kfree(params);
+
+@@ -811,6 +796,54 @@ ret:
+ return new_regd;
+ }
+
++void ath11k_regd_update_chan_list_work(struct work_struct *work)
++{
++ struct ath11k *ar = container_of(work, struct ath11k,
++ channel_update_work);
++ struct scan_chan_list_params *params;
++ struct list_head local_update_list;
++ int left;
++
++ INIT_LIST_HEAD(&local_update_list);
++
++ spin_lock_bh(&ar->data_lock);
++ list_splice_tail_init(&ar->channel_update_queue, &local_update_list);
++ spin_unlock_bh(&ar->data_lock);
++
++ while ((params = list_first_entry_or_null(&local_update_list,
++ struct scan_chan_list_params,
++ list))) {
++ if (ar->state_11d != ATH11K_11D_IDLE) {
++ left = wait_for_completion_timeout(&ar->completed_11d_scan,
++ ATH11K_SCAN_TIMEOUT_HZ);
++ if (!left) {
++ ath11k_dbg(ar->ab, ATH11K_DBG_REG,
++ "failed to receive 11d scan complete: timed out\n");
++ ar->state_11d = ATH11K_11D_IDLE;
++ }
++
++ ath11k_dbg(ar->ab, ATH11K_DBG_REG,
++ "reg 11d scan wait left time %d\n", left);
++ }
++
++ if ((ar->scan.state == ATH11K_SCAN_STARTING ||
++ ar->scan.state == ATH11K_SCAN_RUNNING)) {
++ left = wait_for_completion_timeout(&ar->scan.completed,
++ ATH11K_SCAN_TIMEOUT_HZ);
++ if (!left)
++ ath11k_dbg(ar->ab, ATH11K_DBG_REG,
++ "failed to receive hw scan complete: timed out\n");
++
++ ath11k_dbg(ar->ab, ATH11K_DBG_REG,
++ "reg hw scan wait left time %d\n", left);
++ }
++
++ ath11k_wmi_send_scan_chan_list_cmd(ar, params);
++ list_del(¶ms->list);
++ kfree(params);
++ }
++}
++
+ static bool ath11k_reg_is_world_alpha(char *alpha)
+ {
+ if (alpha[0] == '0' && alpha[1] == '0')
+--- a/drivers/net/wireless/ath/ath11k/reg.h
++++ b/drivers/net/wireless/ath/ath11k/reg.h
+@@ -1,7 +1,7 @@
+ /* SPDX-License-Identifier: BSD-3-Clause-Clear */
+ /*
+ * Copyright (c) 2019 The Linux Foundation. All rights reserved.
+- * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+ #ifndef ATH11K_REG_H
+@@ -33,6 +33,7 @@ void ath11k_reg_init(struct ath11k *ar);
+ void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info);
+ void ath11k_reg_free(struct ath11k_base *ab);
+ void ath11k_regd_update_work(struct work_struct *work);
++void ath11k_regd_update_chan_list_work(struct work_struct *work);
+ struct ieee80211_regdomain *
+ ath11k_reg_build_regd(struct ath11k_base *ab,
+ struct cur_regulatory_info *reg_info, bool intersect,
+--- a/drivers/net/wireless/ath/ath11k/wmi.h
++++ b/drivers/net/wireless/ath/ath11k/wmi.h
+@@ -3817,6 +3817,7 @@ struct wmi_stop_scan_cmd {
+ };
+
+ struct scan_chan_list_params {
++ struct list_head list;
+ u32 pdev_id;
+ u16 nallchans;
+ struct channel_param ch_param[];