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@xxxxxxxxxxx> --- 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(-) 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); -- 2.34.1