diff mbox series

[v5,07/13] wifi: ath12k: add support for fixed QMI firmware memory

Message ID 20250130043508.1885026-8-quic_rajkbhag@quicinc.com
State New
Headers show
Series wifi: ath12k: add Ath12k AHB driver support for IPQ5332 | expand

Commit Message

Raj Kumar Bhagat Jan. 30, 2025, 4:35 a.m. UTC
IPQ5332 firmware supports only fixed QMI firmware memory.

Hence, add support to read reserved fixed memory region from
device-tree and provide the reserved memory segments for
firmware to use during QMI firmware memory request.

Note that the ability to set the fixed memory will be introduced in
a subsequent patch. Currently, the flag remains unset by default,
ensuring that existing chipsets are unaffected.

Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.3.1-00130-QCAHKSWPL_SILICONZ-1
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.1.1-00210-QCAHKSWPL_SILICONZ-1

Signed-off-by: Raj Kumar Bhagat <quic_rajkbhag@quicinc.com>
---
 drivers/net/wireless/ath/ath12k/core.c |  24 ++++
 drivers/net/wireless/ath/ath12k/core.h |   3 +
 drivers/net/wireless/ath/ath12k/hw.c   |   4 +
 drivers/net/wireless/ath/ath12k/hw.h   |   1 +
 drivers/net/wireless/ath/ath12k/qmi.c  | 188 ++++++++++++++++++++++---
 5 files changed, 204 insertions(+), 16 deletions(-)

Comments

Raj Kumar Bhagat Feb. 3, 2025, 9:44 a.m. UTC | #1
On 1/30/2025 1:16 PM, Krzysztof Kozlowski wrote:
> On 30/01/2025 05:35, Raj Kumar Bhagat wrote:
>> @@ -2646,6 +2663,136 @@ static int ath12k_qmi_alloc_target_mem_chunk(struct ath12k_base *ab)
>>  	return ret;
>>  }
>>  
>> +static int ath12k_qmi_assign_target_mem_chunk(struct ath12k_base *ab)
>> +{
>> +	struct device_node *mem_node;
>> +	struct resource res, m3_res;
>> +	u32 bdf_start_addr;
>> +	int i, idx, ret;
>> +
>> +	for (i = 0, idx = 0; i < ab->qmi.mem_seg_count; i++) {
>> +		switch (ab->qmi.target_mem[i].type) {
>> +		case HOST_DDR_REGION_TYPE:
>> +			mem_node = ath12k_core_get_reserved_mem_by_name(ab, "q6-region");
> 
> 
> Why cannot you use existing API for reserved memory -
> of_reserved_mem_lookup()?
> 

The of_reserved_mem_lookup() requires reserved memory node to read the memory and
return in the structure "struct reserved_mem".

The of_reserved_mem_lookup() would be used after we get the reserved memory node
using the API - ath12k_core_get_reserved_mem_by_name(ab, "q6-region");

In next version we would use of_reserved_mem_lookup(), Something like below:
    mem_node = ath12k_core_get_reserved_mem_by_name(ab, "q6-region");
    struct reserved_mem res = of_reserved_mem_lookup(mem_node)
Krzysztof Kozlowski Feb. 3, 2025, 10:12 a.m. UTC | #2
On 03/02/2025 10:44, Raj Kumar Bhagat wrote:
> On 1/30/2025 1:16 PM, Krzysztof Kozlowski wrote:
>> On 30/01/2025 05:35, Raj Kumar Bhagat wrote:
>>> @@ -2646,6 +2663,136 @@ static int ath12k_qmi_alloc_target_mem_chunk(struct ath12k_base *ab)
>>>  	return ret;
>>>  }
>>>  
>>> +static int ath12k_qmi_assign_target_mem_chunk(struct ath12k_base *ab)
>>> +{
>>> +	struct device_node *mem_node;
>>> +	struct resource res, m3_res;
>>> +	u32 bdf_start_addr;
>>> +	int i, idx, ret;
>>> +
>>> +	for (i = 0, idx = 0; i < ab->qmi.mem_seg_count; i++) {
>>> +		switch (ab->qmi.target_mem[i].type) {
>>> +		case HOST_DDR_REGION_TYPE:
>>> +			mem_node = ath12k_core_get_reserved_mem_by_name(ab, "q6-region");
>>
>>
>> Why cannot you use existing API for reserved memory -
>> of_reserved_mem_lookup()?
>>
> 
> The of_reserved_mem_lookup() requires reserved memory node to read the memory and
> return in the structure "struct reserved_mem".
> 
> The of_reserved_mem_lookup() would be used after we get the reserved memory node
> using the API - ath12k_core_get_reserved_mem_by_name(ab, "q6-region");
> 
> In next version we would use of_reserved_mem_lookup(), Something like below:
>     mem_node = ath12k_core_get_reserved_mem_by_name(ab, "q6-region");

Then why do you need ath12k_core_get_reserved_mem_by_name() in the first
place? Just use of_reserved_mem_lookup() directly. Why do you need to
parse phandle before of_reserved_mem_lookup()?


>     struct reserved_mem res = of_reserved_mem_lookup(mem_node)


Best regards,
Krzysztof
Raj Kumar Bhagat Feb. 4, 2025, 9:06 a.m. UTC | #3
On 2/3/2025 3:42 PM, Krzysztof Kozlowski wrote:
> On 03/02/2025 10:44, Raj Kumar Bhagat wrote:
>> On 1/30/2025 1:16 PM, Krzysztof Kozlowski wrote:
>>> On 30/01/2025 05:35, Raj Kumar Bhagat wrote:
>>>> @@ -2646,6 +2663,136 @@ static int ath12k_qmi_alloc_target_mem_chunk(struct ath12k_base *ab)
>>>>  	return ret;
>>>>  }
>>>>  
>>>> +static int ath12k_qmi_assign_target_mem_chunk(struct ath12k_base *ab)
>>>> +{
>>>> +	struct device_node *mem_node;
>>>> +	struct resource res, m3_res;
>>>> +	u32 bdf_start_addr;
>>>> +	int i, idx, ret;
>>>> +
>>>> +	for (i = 0, idx = 0; i < ab->qmi.mem_seg_count; i++) {
>>>> +		switch (ab->qmi.target_mem[i].type) {
>>>> +		case HOST_DDR_REGION_TYPE:
>>>> +			mem_node = ath12k_core_get_reserved_mem_by_name(ab, "q6-region");
>>>
>>>
>>> Why cannot you use existing API for reserved memory -
>>> of_reserved_mem_lookup()?
>>>
>>
>> The of_reserved_mem_lookup() requires reserved memory node to read the memory and
>> return in the structure "struct reserved_mem".
>>
>> The of_reserved_mem_lookup() would be used after we get the reserved memory node
>> using the API - ath12k_core_get_reserved_mem_by_name(ab, "q6-region");
>>
>> In next version we would use of_reserved_mem_lookup(), Something like below:
>>     mem_node = ath12k_core_get_reserved_mem_by_name(ab, "q6-region");
> 
> Then why do you need ath12k_core_get_reserved_mem_by_name() in the first
> place? Just use of_reserved_mem_lookup() directly. Why do you need to
> parse phandle before of_reserved_mem_lookup()?
> 

Sorry, I'm having difficulty understanding this.
We have the WiFi node at ab->dev->of_node, but we don't have a node for the reserved-memory
'q6-region'. The of_reserved_mem_lookup() function requires the device node for 'q6-region'.

Could you please suggest how we can use of_reserved_mem_lookup() without obtaining the
'q6-region' node first.
Raj Kumar Bhagat Feb. 5, 2025, 9:59 a.m. UTC | #4
On 2/5/2025 2:37 PM, Krzysztof Kozlowski wrote:
> On 04/02/2025 10:06, Raj Kumar Bhagat wrote:
>> On 2/3/2025 3:42 PM, Krzysztof Kozlowski wrote:
>>> On 03/02/2025 10:44, Raj Kumar Bhagat wrote:
>>>> On 1/30/2025 1:16 PM, Krzysztof Kozlowski wrote:
>>>>> On 30/01/2025 05:35, Raj Kumar Bhagat wrote:
>>>>>> @@ -2646,6 +2663,136 @@ static int ath12k_qmi_alloc_target_mem_chunk(struct ath12k_base *ab)
>>>>>>  	return ret;
>>>>>>  }
>>>>>>  
>>>>>> +static int ath12k_qmi_assign_target_mem_chunk(struct ath12k_base *ab)
>>>>>> +{
>>>>>> +	struct device_node *mem_node;
>>>>>> +	struct resource res, m3_res;
>>>>>> +	u32 bdf_start_addr;
>>>>>> +	int i, idx, ret;
>>>>>> +
>>>>>> +	for (i = 0, idx = 0; i < ab->qmi.mem_seg_count; i++) {
>>>>>> +		switch (ab->qmi.target_mem[i].type) {
>>>>>> +		case HOST_DDR_REGION_TYPE:
>>>>>> +			mem_node = ath12k_core_get_reserved_mem_by_name(ab, "q6-region");
>>>>>
>>>>>
>>>>> Why cannot you use existing API for reserved memory -
>>>>> of_reserved_mem_lookup()?
>>>>>
>>>>
>>>> The of_reserved_mem_lookup() requires reserved memory node to read the memory and
>>>> return in the structure "struct reserved_mem".
>>>>
>>>> The of_reserved_mem_lookup() would be used after we get the reserved memory node
>>>> using the API - ath12k_core_get_reserved_mem_by_name(ab, "q6-region");
>>>>
>>>> In next version we would use of_reserved_mem_lookup(), Something like below:
>>>>     mem_node = ath12k_core_get_reserved_mem_by_name(ab, "q6-region");
>>>
>>> Then why do you need ath12k_core_get_reserved_mem_by_name() in the first
>>> place? Just use of_reserved_mem_lookup() directly. Why do you need to
>>> parse phandle before of_reserved_mem_lookup()?
>>>
>>
>> Sorry, I'm having difficulty understanding this.
>> We have the WiFi node at ab->dev->of_node, but we don't have a node for the reserved-memory
>> 'q6-region'. The of_reserved_mem_lookup() function requires the device node for 'q6-region'.
>>
>> Could you please suggest how we can use of_reserved_mem_lookup() without obtaining the
>> 'q6-region' node first.
> 
> 
> Hm, it seems you are not using it for this device, so indeed you need to
> parse phandle. You can still code it simpler -
> of_property_match_string() is not necessary and
> of_address_to_resource()+of_node_put()  could be in the
> ath12k_core_get_reserved_mem()
> 

Thanks, will make it simpler.
diff mbox series

Patch

diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index 60c077b016b4..cbe4d6274768 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -609,6 +609,30 @@  u32 ath12k_core_get_max_num_tids(struct ath12k_base *ab)
 	return TARGET_NUM_TIDS(SINGLE);
 }
 
+struct device_node *ath12k_core_get_reserved_mem_by_name(struct ath12k_base *ab,
+							 const char *name)
+{
+	struct device *dev = ab->dev;
+	struct device_node *mem_np;
+	int index;
+
+	index = of_property_match_string(dev->of_node, "memory-region-names", name);
+	if (index < 0) {
+		ath12k_dbg(ab, ATH12K_DBG_BOOT,
+			   "memory region %s not found\n", name);
+		return NULL;
+	}
+
+	mem_np = of_parse_phandle(dev->of_node, "memory-region", index);
+	if (!mem_np) {
+		ath12k_dbg(ab, ATH12K_DBG_BOOT,
+			   "failed to parse memory region %s\n", name);
+		return NULL;
+	}
+
+	return mem_np;
+}
+
 static void ath12k_core_stop(struct ath12k_base *ab)
 {
 	ath12k_core_stopped(ab);
diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
index 22ade838b99f..fa2eadd60c44 100644
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -239,6 +239,7 @@  enum ath12k_dev_flags {
 	ATH12K_FLAG_EXT_IRQ_ENABLED,
 	ATH12K_FLAG_QMI_FW_READY_COMPLETE,
 	ATH12K_FLAG_FTM_SEGMENTED,
+	ATH12K_FLAG_FIXED_MEM_REGION,
 };
 
 struct ath12k_tx_conf {
@@ -1203,6 +1204,8 @@  u32 ath12k_core_get_max_peers_per_radio(struct ath12k_base *ab);
 u32 ath12k_core_get_max_num_tids(struct ath12k_base *ab);
 
 void ath12k_core_hw_group_set_mlo_capable(struct ath12k_hw_group *ag);
+struct device_node *ath12k_core_get_reserved_mem_by_name(struct ath12k_base *ab,
+							 const char *name);
 
 static inline const char *ath12k_scan_state_str(enum ath12k_scan_state state)
 {
diff --git a/drivers/net/wireless/ath/ath12k/hw.c b/drivers/net/wireless/ath/ath12k/hw.c
index 91aecca566a4..b4d5651973b7 100644
--- a/drivers/net/wireless/ath/ath12k/hw.c
+++ b/drivers/net/wireless/ath/ath12k/hw.c
@@ -1322,6 +1322,7 @@  static const struct ath12k_hw_params ath12k_hw_params[] = {
 
 		.ce_ie_addr = NULL,
 		.ce_remap = NULL,
+		.bdf_addr_offset = 0,
 	},
 	{
 		.name = "wcn7850 hw2.0",
@@ -1406,6 +1407,7 @@  static const struct ath12k_hw_params ath12k_hw_params[] = {
 
 		.ce_ie_addr = NULL,
 		.ce_remap = NULL,
+		.bdf_addr_offset = 0,
 	},
 	{
 		.name = "qcn9274 hw2.0",
@@ -1486,6 +1488,7 @@  static const struct ath12k_hw_params ath12k_hw_params[] = {
 
 		.ce_ie_addr = NULL,
 		.ce_remap = NULL,
+		.bdf_addr_offset = 0,
 	},
 	{
 		.name = "ipq5332 hw1.0",
@@ -1561,6 +1564,7 @@  static const struct ath12k_hw_params ath12k_hw_params[] = {
 
 		.ce_ie_addr = &ath12k_ce_ie_addr_ipq5332,
 		.ce_remap = &ath12k_ce_remap_ipq5332,
+		.bdf_addr_offset = 0xC00000,
 	},
 };
 
diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h
index 6a75af093f31..5123d2f51865 100644
--- a/drivers/net/wireless/ath/ath12k/hw.h
+++ b/drivers/net/wireless/ath/ath12k/hw.h
@@ -229,6 +229,7 @@  struct ath12k_hw_params {
 
 	const struct ce_ie_addr *ce_ie_addr;
 	const struct ce_remap *ce_remap;
+	u32 bdf_addr_offset;
 };
 
 struct ath12k_hw_ops {
diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c
index 3d1f5309c962..d75769ae03bb 100644
--- a/drivers/net/wireless/ath/ath12k/qmi.c
+++ b/drivers/net/wireless/ath/ath12k/qmi.c
@@ -11,6 +11,8 @@ 
 #include "debug.h"
 #include <linux/of.h>
 #include <linux/firmware.h>
+#include <linux/of_address.h>
+#include <linux/ioport.h>
 
 #define SLEEP_CLOCK_SELECT_INTERNAL_BIT	0x02
 #define HOST_CSTATE_BIT			0x04
@@ -2384,7 +2386,8 @@  int ath12k_qmi_respond_fw_mem_request(struct ath12k_base *ab)
 	 * failure to firmware and firmware then request multiple blocks of
 	 * small chunk size memory.
 	 */
-	if (ab->qmi.target_mem_delayed) {
+	if (!test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags) &&
+	    ab->qmi.target_mem_delayed) {
 		delayed = true;
 		ath12k_dbg(ab, ATH12K_DBG_QMI, "qmi delays mem_request %d\n",
 			   ab->qmi.mem_seg_count);
@@ -2448,6 +2451,7 @@  static void ath12k_qmi_free_mlo_mem_chunk(struct ath12k_base *ab,
 {
 	struct ath12k_hw_group *ag = ab->ag;
 	struct target_mem_chunk *mlo_chunk;
+	bool fixed_mem;
 
 	lockdep_assert_held(&ag->mutex);
 
@@ -2459,8 +2463,13 @@  static void ath12k_qmi_free_mlo_mem_chunk(struct ath12k_base *ab,
 		return;
 	}
 
+	fixed_mem = test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags);
 	mlo_chunk = &ag->mlo_mem.chunk[idx];
-	if (mlo_chunk->v.addr) {
+
+	if (fixed_mem && mlo_chunk->v.ioaddr) {
+		iounmap(mlo_chunk->v.ioaddr);
+		mlo_chunk->v.ioaddr = NULL;
+	} else if (mlo_chunk->v.addr) {
 		dma_free_coherent(ab->dev,
 				  mlo_chunk->size,
 				  mlo_chunk->v.addr,
@@ -2470,7 +2479,10 @@  static void ath12k_qmi_free_mlo_mem_chunk(struct ath12k_base *ab,
 
 	mlo_chunk->paddr = 0;
 	mlo_chunk->size = 0;
-	chunk->v.addr = NULL;
+	if (fixed_mem)
+		chunk->v.ioaddr = NULL;
+	else
+		chunk->v.addr = NULL;
 	chunk->paddr = 0;
 	chunk->size = 0;
 }
@@ -2481,19 +2493,24 @@  static void ath12k_qmi_free_target_mem_chunk(struct ath12k_base *ab)
 	int i, mlo_idx;
 
 	for (i = 0, mlo_idx = 0; i < ab->qmi.mem_seg_count; i++) {
-		if (!ab->qmi.target_mem[i].v.addr)
-			continue;
-
 		if (ab->qmi.target_mem[i].type == MLO_GLOBAL_MEM_REGION_TYPE) {
 			ath12k_qmi_free_mlo_mem_chunk(ab,
 						      &ab->qmi.target_mem[i],
 						      mlo_idx++);
 		} else {
-			dma_free_coherent(ab->dev,
-					  ab->qmi.target_mem[i].prev_size,
-					  ab->qmi.target_mem[i].v.addr,
-					  ab->qmi.target_mem[i].paddr);
-			ab->qmi.target_mem[i].v.addr = NULL;
+			if (test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags) &&
+			    ab->qmi.target_mem[i].v.ioaddr) {
+				iounmap(ab->qmi.target_mem[i].v.ioaddr);
+				ab->qmi.target_mem[i].v.ioaddr = NULL;
+			} else {
+				if (!ab->qmi.target_mem[i].v.addr)
+					continue;
+				dma_free_coherent(ab->dev,
+						  ab->qmi.target_mem[i].prev_size,
+						  ab->qmi.target_mem[i].v.addr,
+						  ab->qmi.target_mem[i].paddr);
+				ab->qmi.target_mem[i].v.addr = NULL;
+			}
 		}
 	}
 
@@ -2646,6 +2663,136 @@  static int ath12k_qmi_alloc_target_mem_chunk(struct ath12k_base *ab)
 	return ret;
 }
 
+static int ath12k_qmi_assign_target_mem_chunk(struct ath12k_base *ab)
+{
+	struct device_node *mem_node;
+	struct resource res, m3_res;
+	u32 bdf_start_addr;
+	int i, idx, ret;
+
+	for (i = 0, idx = 0; i < ab->qmi.mem_seg_count; i++) {
+		switch (ab->qmi.target_mem[i].type) {
+		case HOST_DDR_REGION_TYPE:
+			mem_node = ath12k_core_get_reserved_mem_by_name(ab, "q6-region");
+			if (!mem_node || of_address_to_resource(mem_node, 0, &res)) {
+				ath12k_dbg(ab, ATH12K_DBG_QMI,
+					   "q6-region is not defined in device-tree\n");
+				ret = -ENODEV;
+				goto out;
+			}
+			of_node_put(mem_node);
+
+			if (res.end - res.start + 1 < ab->qmi.target_mem[i].size) {
+				ath12k_dbg(ab, ATH12K_DBG_QMI,
+					   "failed to assign mem type %d req size %d avail size %lld\n",
+					   ab->qmi.target_mem[i].type,
+					   ab->qmi.target_mem[i].size,
+					   (res.end - res.start + 1));
+				ret = -EINVAL;
+				goto out;
+			}
+
+			ab->qmi.target_mem[idx].paddr = res.start;
+			ab->qmi.target_mem[idx].v.ioaddr =
+				ioremap(ab->qmi.target_mem[idx].paddr,
+					ab->qmi.target_mem[i].size);
+			if (!ab->qmi.target_mem[idx].v.ioaddr) {
+				ret = -EIO;
+				goto out;
+			}
+			ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size;
+			ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type;
+			idx++;
+			break;
+		case BDF_MEM_REGION_TYPE:
+			mem_node = ath12k_core_get_reserved_mem_by_name(ab, "q6-region");
+			if (!mem_node || of_address_to_resource(mem_node, 0, &res)) {
+				ath12k_dbg(ab, ATH12K_DBG_QMI,
+					   "q6-region is not defined to calculate BDF mem in DT\n");
+				ret = -ENODEV;
+				goto out;
+			}
+			of_node_put(mem_node);
+
+			bdf_start_addr = res.start + ab->hw_params->bdf_addr_offset;
+			if (res.end - bdf_start_addr + 1 < ab->qmi.target_mem[i].size) {
+				ath12k_dbg(ab, ATH12K_DBG_QMI,
+					   "failed to assign mem type %d req size %d avail size %lld\n",
+					   ab->qmi.target_mem[i].type,
+					   ab->qmi.target_mem[i].size,
+					   (res.end - bdf_start_addr + 1));
+				ret = -EINVAL;
+				goto out;
+			}
+			ab->qmi.target_mem[idx].paddr = bdf_start_addr;
+			ab->qmi.target_mem[idx].v.ioaddr =
+				ioremap(ab->qmi.target_mem[idx].paddr,
+					ab->qmi.target_mem[i].size);
+			if (!ab->qmi.target_mem[idx].v.ioaddr) {
+				ret = -EIO;
+				goto out;
+			}
+			ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size;
+			ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type;
+			idx++;
+			break;
+		case CALDB_MEM_REGION_TYPE:
+			/* Cold boot calibration is not enabled in Ath12k. Hence,
+			 * assign paddr = 0.
+			 * Once cold boot calibration is enabled add support to
+			 * assign reserved memory from DT.
+			 */
+			ab->qmi.target_mem[idx].paddr = 0;
+			ab->qmi.target_mem[idx].v.ioaddr = NULL;
+			ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size;
+			ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type;
+			idx++;
+			break;
+		case M3_DUMP_REGION_TYPE:
+			mem_node = ath12k_core_get_reserved_mem_by_name(ab, "m3-dump");
+			if (!mem_node || of_address_to_resource(mem_node, 0, &m3_res)) {
+				ath12k_err(ab, "m3_dump not defined in device-tree\n");
+				ret = -EINVAL;
+				goto out;
+			}
+			of_node_put(mem_node);
+
+			if (m3_res.end - m3_res.start + 1 < ab->qmi.target_mem[i].size) {
+				ath12k_dbg(ab, ATH12K_DBG_QMI,
+					   "failed to assign mem type %d req size %d avail size %lld\n",
+					   ab->qmi.target_mem[i].type,
+					   ab->qmi.target_mem[i].size,
+					   (m3_res.end - m3_res.start + 1));
+				ret = -EINVAL;
+				goto out;
+			}
+
+			ab->qmi.target_mem[idx].paddr = m3_res.start;
+			ab->qmi.target_mem[idx].v.ioaddr =
+				ioremap(ab->qmi.target_mem[idx].paddr,
+					ab->qmi.target_mem[i].size);
+			if (!ab->qmi.target_mem[idx].v.ioaddr) {
+				ret = -EIO;
+				goto out;
+			}
+			ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size;
+			ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type;
+			idx++;
+			break;
+		default:
+			ath12k_warn(ab, "qmi ignore invalid mem req type %d\n",
+				    ab->qmi.target_mem[i].type);
+			break;
+		}
+	}
+	ab->qmi.mem_seg_count = idx;
+
+	return 0;
+out:
+	ath12k_qmi_free_target_mem_chunk(ab);
+	return ret;
+}
+
 /* clang stack usage explodes if this is inlined */
 static noinline_for_stack
 int ath12k_qmi_request_target_cap(struct ath12k_base *ab)
@@ -3480,11 +3627,20 @@  static void ath12k_qmi_msg_mem_request_cb(struct qmi_handle *qmi_hdl,
 			   msg->mem_seg[i].type, msg->mem_seg[i].size);
 	}
 
-	ret = ath12k_qmi_alloc_target_mem_chunk(ab);
-	if (ret) {
-		ath12k_warn(ab, "qmi failed to alloc target memory: %d\n",
-			    ret);
-		return;
+	if (test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags)) {
+		ret = ath12k_qmi_assign_target_mem_chunk(ab);
+		if (ret) {
+			ath12k_warn(ab, "failed to assign qmi target memory: %d\n",
+				    ret);
+			return;
+		}
+	} else {
+		ret = ath12k_qmi_alloc_target_mem_chunk(ab);
+		if (ret) {
+			ath12k_warn(ab, "qmi failed to alloc target memory: %d\n",
+				    ret);
+			return;
+		}
 	}
 
 	ath12k_qmi_driver_event_post(qmi, ATH12K_QMI_EVENT_REQUEST_MEM, NULL);