]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: ath12k: add support for fixed QMI firmware memory
authorRaj Kumar Bhagat <quic_rajkbhag@quicinc.com>
Fri, 21 Mar 2025 10:52:44 +0000 (16:22 +0530)
committerJeff Johnson <jeff.johnson@oss.qualcomm.com>
Tue, 25 Mar 2025 14:55:44 +0000 (07:55 -0700)
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

Reviewed-by: Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan@oss.qualcomm.com>
Signed-off-by: Raj Kumar Bhagat <quic_rajkbhag@quicinc.com>
Link: https://patch.msgid.link/20250321-ath12k-ahb-v12-7-bb389ed76ae5@quicinc.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
drivers/net/wireless/ath/ath12k/core.c
drivers/net/wireless/ath/ath12k/core.h
drivers/net/wireless/ath/ath12k/hw.c
drivers/net/wireless/ath/ath12k/hw.h
drivers/net/wireless/ath/ath12k/qmi.c

index 301dea381841c285d91228d5382a0c6af5e1bdaa..ecac5497e36e340a93c1af271ba60a802ce3e568 100644 (file)
@@ -612,6 +612,31 @@ u32 ath12k_core_get_max_num_tids(struct ath12k_base *ab)
        return TARGET_NUM_TIDS(SINGLE);
 }
 
+struct reserved_mem *ath12k_core_get_reserved_mem(struct ath12k_base *ab,
+                                                 int index)
+{
+       struct device *dev = ab->dev;
+       struct reserved_mem *rmem;
+       struct device_node *node;
+
+       node = of_parse_phandle(dev->of_node, "memory-region", index);
+       if (!node) {
+               ath12k_dbg(ab, ATH12K_DBG_BOOT,
+                          "failed to parse memory-region for index %d\n", index);
+               return NULL;
+       }
+
+       rmem = of_reserved_mem_lookup(node);
+       of_node_put(node);
+       if (!rmem) {
+               ath12k_dbg(ab, ATH12K_DBG_BOOT,
+                          "unable to get memory-region for index %d\n", index);
+               return NULL;
+       }
+
+       return rmem;
+}
+
 static void ath12k_core_stop(struct ath12k_base *ab)
 {
        ath12k_core_stopped(ab);
index b080870b39a05812916b9859c5e9df9fcc79534d..3f03e70913cc875ad26037503f90279a67ac360a 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/dmi.h>
 #include <linux/ctype.h>
 #include <linux/firmware.h>
+#include <linux/of_reserved_mem.h>
 #include <linux/panic_notifier.h>
 #include <linux/average.h>
 #include "qmi.h"
@@ -239,6 +240,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 {
@@ -1224,6 +1226,8 @@ void ath12k_fw_stats_init(struct ath12k *ar);
 void ath12k_fw_stats_bcn_free(struct list_head *head);
 void ath12k_fw_stats_free(struct ath12k_fw_stats *stats);
 void ath12k_fw_stats_reset(struct ath12k *ar);
+struct reserved_mem *ath12k_core_get_reserved_mem(struct ath12k_base *ab,
+                                                 int index);
 
 static inline const char *ath12k_scan_state_str(enum ath12k_scan_state state)
 {
index 91aecca566a4b3b3927f019c4b0b66e2663ce975..b4d5651973b76ceb6ec4edc2b5c3ab3fd884c5f8 100644 (file)
@@ -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,
        },
 };
 
index 6a75af093f319364f5930830c0c15e50906b9f78..5123d2f51865db325e43a6277cc2917136182bf8 100644 (file)
@@ -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 {
index 6978200fb79a755925f6e3e1d6428f4572be1f5e..7df6c6eebed6855ab42519610962d2b21825d9ec 100644 (file)
@@ -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
@@ -2378,7 +2380,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);
@@ -2442,6 +2445,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);
 
@@ -2453,8 +2457,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,
@@ -2464,7 +2473,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;
 }
@@ -2475,19 +2487,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;
+                       }
                }
        }
 
@@ -2640,6 +2657,130 @@ err:
        return ret;
 }
 
+static int ath12k_qmi_assign_target_mem_chunk(struct ath12k_base *ab)
+{
+       struct reserved_mem *rmem;
+       size_t avail_rmem_size;
+       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:
+                       rmem = ath12k_core_get_reserved_mem(ab, 0);
+                       if (!rmem) {
+                               ret = -ENODEV;
+                               goto out;
+                       }
+
+                       avail_rmem_size = rmem->size;
+                       if (avail_rmem_size < ab->qmi.target_mem[i].size) {
+                               ath12k_dbg(ab, ATH12K_DBG_QMI,
+                                          "failed to assign mem type %u req size %u avail size %zu\n",
+                                          ab->qmi.target_mem[i].type,
+                                          ab->qmi.target_mem[i].size,
+                                          avail_rmem_size);
+                               ret = -EINVAL;
+                               goto out;
+                       }
+
+                       ab->qmi.target_mem[idx].paddr = rmem->base;
+                       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:
+                       rmem = ath12k_core_get_reserved_mem(ab, 0);
+                       if (!rmem) {
+                               ret = -ENODEV;
+                               goto out;
+                       }
+
+                       avail_rmem_size = rmem->size - ab->hw_params->bdf_addr_offset;
+                       if (avail_rmem_size < ab->qmi.target_mem[i].size) {
+                               ath12k_dbg(ab, ATH12K_DBG_QMI,
+                                          "failed to assign mem type %u req size %u avail size %zu\n",
+                                          ab->qmi.target_mem[i].type,
+                                          ab->qmi.target_mem[i].size,
+                                          avail_rmem_size);
+                               ret = -EINVAL;
+                               goto out;
+                       }
+                       ab->qmi.target_mem[idx].paddr =
+                               rmem->base + ab->hw_params->bdf_addr_offset;
+                       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:
+                       rmem = ath12k_core_get_reserved_mem(ab, 1);
+                       if (!rmem) {
+                               ret = -EINVAL;
+                               goto out;
+                       }
+
+                       avail_rmem_size = rmem->size;
+                       if (avail_rmem_size < ab->qmi.target_mem[i].size) {
+                               ath12k_dbg(ab, ATH12K_DBG_QMI,
+                                          "failed to assign mem type %u req size %u avail size %zu\n",
+                                          ab->qmi.target_mem[i].type,
+                                          ab->qmi.target_mem[i].size,
+                                          avail_rmem_size);
+                               ret = -EINVAL;
+                               goto out;
+                       }
+
+                       ab->qmi.target_mem[idx].paddr = rmem->base;
+                       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 %u\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)
@@ -3483,11 +3624,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);