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

Vasanthakumar Thiagarajan <[email protected]>
Newsgroups org.infradead.lists.ath12k,org.kernel.vger.linux-wireless
Message-ID <[email protected]>

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 <[email protected]>
> ---
>   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 <[email protected]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.