[PATCH v2] wifi: ath12k: restore country code during resume

Stian Knudsen stian at pokerfj.es
Sun Jul 26 05:39:00 PDT 2026


On Friday, 24 July 2026 04:51:43 Central European Summer Time Baochen Qiang 
wrote:
> On 7/24/2026 8:15 AM, Jeff Johnson wrote:
> > On 7/20/2026 5:09 AM, Stian Knudsen wrote:
> >> The country code configured before suspend is lost after resume:
> >> the device is powered down in suspend_late and powered back up in
> >> resume_early, so firmware reboots with its default regulatory
> >> settings and the previously set country code is no longer applied.
> >> 
> >> On WCN7850 the firmware comes back in the world regulatory domain
> >> (country 00) and takes ~7 seconds to rediscover the country code
> >> from AP beacons, part of a ~16 second total delay before wifi
> >> reconnects after resume.
> >> 
> >> Restore it by resending WMI_SET_CURRENT_COUNTRY_CMDID during resume
> >> if a country code was set before suspend, i.e. when ar->alpha2 is
> >> valid. This follows the same approach as ath11k commit 7f0343b7b871
> >> ("wifi: ath11k: restore country code during resume").
> >> 
> >> Note that only single_pdev_only devices support suspend/resume (see
> >> ath12k_core_continue_suspend_resume()), so handling the first and
> >> only pdev is sufficient.
> >> 
> >> Tested-on: WCN7850 hw2.0 PCI
> >> WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3
> >> 
> >> Fixes: 8d5f4da8d70b ("wifi: ath12k: support suspend/resume")
> >> Suggested-by: Baochen Qiang <baochen.qiang at oss.qualcomm.com>
> >> Link: https://lore.kernel.org/r/-_iDJ_M5RrqACWB0qmtleg@pokerfj.es
> >> Signed-off-by: Stian Knudsen <stian at pokerfj.es>
> >> ---
> >> v2:
> >> - Add Fixes tag, per Baochen's review comment.
> >> - Link to v1:
> >> https://lore.kernel.org/r/20260717130254.36732-1-stian@pokerfj.es ---
> >> 
> >>  drivers/net/wireless/ath/ath12k/core.c | 21 +++++++++++++++++++++
> >>  1 file changed, 21 insertions(+)
> >> 
> >> diff --git a/drivers/net/wireless/ath/ath12k/core.c
> >> b/drivers/net/wireless/ath/ath12k/core.c ---
> >> a/drivers/net/wireless/ath/ath12k/core.c
> >> +++ b/drivers/net/wireless/ath/ath12k/core.c
> >> @@ -197,6 +197,7 @@ EXPORT_SYMBOL(ath12k_core_resume_early);
> >> 
> >>  int ath12k_core_resume(struct ath12k_base *ab)
> >>  {
> >> 
> >> +	struct ath12k *ar;
> >> 
> >>  	long time_left;
> >>  	int ret;
> >> 
> >> @@ -211,6 +212,26 @@ int ath12k_core_resume(struct ath12k_base *ab)
> >> 
> >>  		return -ETIMEDOUT;
> >>  	
> >>  	}
> >> 
> >> +	/* So far only single_pdev_only devices can reach here,
> >> +	 * so it is valid to handle the first, and the only, pdev.
> >> +	 */
> >> +	ar = ab->pdevs[0].ar;
> >> +	if (ab->hw_params->current_cc_support &&
> >> +	    ar->alpha2[0] != 0 && ar->alpha2[1] != 0) {
> >> +		struct wmi_set_current_country_arg arg = {};
> >> +
> >> +		memcpy(&arg.alpha2, ar->alpha2, 2);
> >> +
> >> +		reinit_completion(&ar->regd_update_completed);
> >> +
> >> +		ret = ath12k_wmi_send_set_current_country_cmd(ar, &arg);
> >> +		if (ret) {
> >> +			ath12k_warn(ab, "failed to set country code during 
resume: %d\n",
> >> +				    ret);
> >> +			return ret;
> >> +		}
> > 
> > Baochen, can you remind me why we don't need to wait for completion here?
> > (the ath11k change doesn't wait)
> 
> Technically we will wait, but asynchronously instead of synchronously. It
> works in a way:
> 
> 1. reinit completion
> 2. send new cc to firmware, proceed with no sync wait.
> 3. in reg channel list event host process reg rules, queue regd update work
> and signals completion.
> 4. regd update work calls ath12k_regd_update() where we wait for the
> completion.
> 
> But I am feeling this is not a good design. As you can see,
> ath12k_regd_update() is reachable via the regd update work, which is queued
> in reg chan list event. However it is the event itself signals the
> completion. That means ath12k_regd_update() never blocks, the whole
> reinit-wait thing is a no-ops.
> 

To be honest, I didn't walk that code path when I wrote the patch. I added 
reinit_completion() because it's what the existing call sites already do 
(ath13k_update_11d() and the reg_notifier path in reg.c, and the ath11k commit 
this was ported from), not because I'd verified it synchronizes anything.
I went through the code path, and came to the same conclusion as Baochen's 
explanation: ath12k_reg_handle_chan_list() queues regd_update_work before the 
event handler calls complete_all() a few lines later in the same function 
(wmi.c), so by the time the queued work reaches its 
wait_for_completion_timeout() in ath12k_regd_update(), the completion is 
already signaled. I don't think it's blocking anything meaningful.

Since it's not limited to this patch, happy to either add a comment here once 
you settle on how to describe it, or drop the reinit_completion() call from my 
patch entirely if you'd rather not repeat it to a fourth call until it's 
cleaned up.
Whichever you prefer for v3.

> > If that is legit, perhaps we should document it with a comment since
> > reinit_completion() without waiting for it seems strange
> > 
> >> +	}
> >> +
> >> 
> >>  	return 0;
> >>  
> >>  }
> >>  EXPORT_SYMBOL(ath12k_core_resume);
> >> 
> >> --
> >> 2.50.0







More information about the ath12k mailing list