[PATCH ath-next] wifi: ath12k: add support to load shared firmware on multiPD

Vasanthakumar Thiagarajan vasanthakumar.thiagarajan at oss.qualcomm.com
Tue Aug 11 02:08:28 PDT 2026



On 8/11/2026 11:13 AM, Aaradhana Sahu wrote:
> IPQ5332 platform uses a MultiPD architecture where multiple firmware
> instances share common read-only code segments. The driver currently
> loads only the per-UserPD firmware, which leaves the shared read-only
> firmware unloaded.
> 
> Add support for loading the shared read-only firmware once before the
> UserPDs are powered up. The shared segment contains common code that is
> identical across all UserPDs and reduces the total firmware memory
> requirement.
> 
> Store the reserved memory mapping in struct ath12k_ahb_rproc_info so it can
> be shared by all UserPDs and mapped only once. Keep the mapping valid
> across UserPD power cycles, and unmap it when the last UserPD is removed.
> 
> Shut down the shared firmware from the rproc deconfiguration path when the
> last UserPD is removed.
> 
> Also, pass NULL for the reloc_base parameter because the driver uses fixed
> memory regions configured through device tree and does not need the
> adjusted physical address after relocation.
> 
> Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1
> 
> Signed-off-by: Aaradhana Sahu <aaradhana.sahu at oss.qualcomm.com>
> ---
>   drivers/net/wireless/ath/ath12k/ahb.c       | 166 +++++++++++++++++---
>   drivers/net/wireless/ath/ath12k/ahb.h       |   9 ++
>   drivers/net/wireless/ath/ath12k/wifi7/ahb.c |   3 +
>   3 files changed, 157 insertions(+), 21 deletions(-)
> 
> diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c
> index 0fc55c9169e1..3985f4e1c067 100644
> --- a/drivers/net/wireless/ath/ath12k/ahb.c
> +++ b/drivers/net/wireless/ath/ath12k/ahb.c
> @@ -347,35 +347,60 @@ static void ath12k_ahb_stop(struct ath12k_base *ab)
>   	ath12k_ce_cleanup_pipes(ab);
>   }
>   
> +static int ath12k_ahb_get_fw_load_region(struct ath12k_base *ab)
> +{
> +	struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
> +	struct ath12k_ahb_rproc_info *rproc_info = ab_ahb->rproc_info;
> +	struct device *dev = ab->dev;
> +	struct resource res;
> +	int ret;
> +
> +	if (rproc_info->mem_region)
> +		return 0;
> +
> +	ret = of_reserved_mem_region_to_resource_byname(dev->of_node, "q6-region", &res);
> +	if (ret)
> +		return ret;
> +
> +	rproc_info->mem_phys = res.start;
> +	rproc_info->mem_size = resource_size(&res);
> +	rproc_info->mem_region = memremap(rproc_info->mem_phys, rproc_info->mem_size,
> +					  MEMREMAP_WC);
> +	if (!rproc_info->mem_region) {
> +		ath12k_err(ab, "unable to map memory region: %pa+%zx\n",
> +			   &res.start, rproc_info->mem_size);
> +		rproc_info->mem_phys = 0;
> +		rproc_info->mem_size = 0;
> +		return -ENOMEM;
> +	}
> +
> +	return 0;
> +}
> +
> +static void ath12k_ahb_put_fw_load_region(struct ath12k_ahb_rproc_info *rproc_info)
> +{
> +	memunmap(rproc_info->mem_region);
> +	rproc_info->mem_region = NULL;
> +	rproc_info->mem_phys = 0;
> +	rproc_info->mem_size = 0;
> +}
> +
>   static int ath12k_ahb_power_up(struct ath12k_base *ab)
>   {
>   	struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab);
> +	struct ath12k_ahb_rproc_info *rproc_info = ab_ahb->rproc_info;
>   	char fw_name[ATH12K_USERPD_FW_NAME_LEN];
>   	char fw2_name[ATH12K_USERPD_FW_NAME_LEN];
>   	struct device *dev = ab->dev;
>   	const struct firmware *fw, *fw2;
>   	unsigned long time_left;
> -	phys_addr_t mem_phys;
> -	struct resource res;
> -	void *mem_region;
> -	size_t mem_size;
>   	u32 pasid;
>   	int ret;
>   
> -	ret = of_reserved_mem_region_to_resource_byname(dev->of_node, "q6-region",
> -							&res);
> +	ret = ath12k_ahb_get_fw_load_region(ab);
>   	if (ret)
>   		return ret;
>   
> -	mem_phys = res.start;
> -	mem_size = resource_size(&res);
> -	mem_region = devm_memremap(dev, mem_phys, mem_size, MEMREMAP_WC);
> -	if (IS_ERR(mem_region)) {
> -		ath12k_err(ab, "unable to map memory region: %pa+%zx\n",
> -			   &res.start, mem_size);
> -		return PTR_ERR(mem_region);
> -	}
> -
>   	snprintf(fw_name, sizeof(fw_name), "%s/%s/%s%d%s", ATH12K_FW_DIR,
>   		 ab->hw_params->fw.dir, ATH12K_AHB_FW_PREFIX, ab_ahb->userpd_id,
>   		 ATH12K_AHB_FW_SUFFIX);
> @@ -400,11 +425,13 @@ static int ath12k_ahb_power_up(struct ath12k_base *ab)
>   
>   	/* Load FW image to a reserved memory location */
>   	if (ab_ahb->scm_auth_enabled)
> -		ret = qcom_mdt_load(dev, fw, fw_name, pasid, mem_region,
> -				    mem_phys, mem_size, &mem_phys);
> +		ret = qcom_mdt_load(dev, fw, fw_name, pasid, rproc_info->mem_region,
> +				    rproc_info->mem_phys, rproc_info->mem_size,
> +				    NULL);
>   	else
> -		ret = qcom_mdt_load_no_init(dev, fw, fw_name, mem_region,
> -					    mem_phys, mem_size, &mem_phys);
> +		ret = qcom_mdt_load_no_init(dev, fw, fw_name, rproc_info->mem_region,
> +					    rproc_info->mem_phys, rproc_info->mem_size,
> +					    NULL);
>   	if (ret) {
>   		ath12k_err(ab, "Failed to load MDT segments: %d\n", ret);
>   		goto err_fw;
> @@ -428,8 +455,9 @@ static int ath12k_ahb_power_up(struct ath12k_base *ab)
>   		goto err_fw2;
>   	}
>   
> -	ret = qcom_mdt_load_no_init(dev, fw2, fw2_name, mem_region, mem_phys,
> -				    mem_size, &mem_phys);
> +	ret = qcom_mdt_load_no_init(dev, fw2, fw2_name, rproc_info->mem_region,
> +				    rproc_info->mem_phys, rproc_info->mem_size,
> +				    NULL);
>   	if (ret) {
>   		ath12k_err(ab, "Failed to load MDT segments: %d\n", ret);
>   		goto err_fw2;
> @@ -877,6 +905,7 @@ static struct ath12k_ahb_rproc_info *ath12k_ahb_rproc_info_alloc(struct ath12k_b
>   	rproc_info->rootpd_booted_by_driver = false;
>   	rproc_info->userpd[ab_ahb->userpd_id - 1] = ab_ahb;
>   	rproc_info->num_userpd = 1;
> +	rproc_info->shared_fw_loaded = false;
>   	init_completion(&rproc_info->rootpd_ready);
>   	ab_ahb->rproc_info = rproc_info;
>   
> @@ -961,6 +990,76 @@ static int ath12k_ahb_boot_root_pd(struct ath12k_base *ab)
>   	return 0;
>   }
>   
> +static int ath12k_ahb_load_auth_shared_fw(struct ath12k_base *ab,
> +					  struct ath12k_ahb_rproc_info *rproc_info,
> +					  const char *fw_name, u32 pasid)
> +{
> +	int ret;
> +

nit: move this empty line below after the declaration part.

> +	const struct firmware *fw __free(firmware) = NULL;


With that nit addressed

Reviewed-by: Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan at oss.qualcomm.com>



More information about the ath12k mailing list