mirror of
https://github.com/torvalds/linux.git
synced 2026-09-28 12:02:03 +02:00
wifi: ath12k: add support for fixed QMI firmware memory
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>
This commit is contained in:
parent
12070392be
commit
6757079c58
|
|
@ -612,6 +612,31 @@ u32 ath12k_core_get_max_num_tids(struct ath12k_base *ab)
|
||||||
return TARGET_NUM_TIDS(SINGLE);
|
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)
|
static void ath12k_core_stop(struct ath12k_base *ab)
|
||||||
{
|
{
|
||||||
ath12k_core_stopped(ab);
|
ath12k_core_stopped(ab);
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
#include <linux/dmi.h>
|
#include <linux/dmi.h>
|
||||||
#include <linux/ctype.h>
|
#include <linux/ctype.h>
|
||||||
#include <linux/firmware.h>
|
#include <linux/firmware.h>
|
||||||
|
#include <linux/of_reserved_mem.h>
|
||||||
#include <linux/panic_notifier.h>
|
#include <linux/panic_notifier.h>
|
||||||
#include <linux/average.h>
|
#include <linux/average.h>
|
||||||
#include "qmi.h"
|
#include "qmi.h"
|
||||||
|
|
@ -239,6 +240,7 @@ enum ath12k_dev_flags {
|
||||||
ATH12K_FLAG_EXT_IRQ_ENABLED,
|
ATH12K_FLAG_EXT_IRQ_ENABLED,
|
||||||
ATH12K_FLAG_QMI_FW_READY_COMPLETE,
|
ATH12K_FLAG_QMI_FW_READY_COMPLETE,
|
||||||
ATH12K_FLAG_FTM_SEGMENTED,
|
ATH12K_FLAG_FTM_SEGMENTED,
|
||||||
|
ATH12K_FLAG_FIXED_MEM_REGION,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ath12k_tx_conf {
|
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_bcn_free(struct list_head *head);
|
||||||
void ath12k_fw_stats_free(struct ath12k_fw_stats *stats);
|
void ath12k_fw_stats_free(struct ath12k_fw_stats *stats);
|
||||||
void ath12k_fw_stats_reset(struct ath12k *ar);
|
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)
|
static inline const char *ath12k_scan_state_str(enum ath12k_scan_state state)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1322,6 +1322,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = {
|
||||||
|
|
||||||
.ce_ie_addr = NULL,
|
.ce_ie_addr = NULL,
|
||||||
.ce_remap = NULL,
|
.ce_remap = NULL,
|
||||||
|
.bdf_addr_offset = 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.name = "wcn7850 hw2.0",
|
.name = "wcn7850 hw2.0",
|
||||||
|
|
@ -1406,6 +1407,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = {
|
||||||
|
|
||||||
.ce_ie_addr = NULL,
|
.ce_ie_addr = NULL,
|
||||||
.ce_remap = NULL,
|
.ce_remap = NULL,
|
||||||
|
.bdf_addr_offset = 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.name = "qcn9274 hw2.0",
|
.name = "qcn9274 hw2.0",
|
||||||
|
|
@ -1486,6 +1488,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = {
|
||||||
|
|
||||||
.ce_ie_addr = NULL,
|
.ce_ie_addr = NULL,
|
||||||
.ce_remap = NULL,
|
.ce_remap = NULL,
|
||||||
|
.bdf_addr_offset = 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.name = "ipq5332 hw1.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_ie_addr = &ath12k_ce_ie_addr_ipq5332,
|
||||||
.ce_remap = &ath12k_ce_remap_ipq5332,
|
.ce_remap = &ath12k_ce_remap_ipq5332,
|
||||||
|
.bdf_addr_offset = 0xC00000,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -229,6 +229,7 @@ struct ath12k_hw_params {
|
||||||
|
|
||||||
const struct ce_ie_addr *ce_ie_addr;
|
const struct ce_ie_addr *ce_ie_addr;
|
||||||
const struct ce_remap *ce_remap;
|
const struct ce_remap *ce_remap;
|
||||||
|
u32 bdf_addr_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ath12k_hw_ops {
|
struct ath12k_hw_ops {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/firmware.h>
|
#include <linux/firmware.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/ioport.h>
|
||||||
|
|
||||||
#define SLEEP_CLOCK_SELECT_INTERNAL_BIT 0x02
|
#define SLEEP_CLOCK_SELECT_INTERNAL_BIT 0x02
|
||||||
#define HOST_CSTATE_BIT 0x04
|
#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
|
* failure to firmware and firmware then request multiple blocks of
|
||||||
* small chunk size memory.
|
* 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;
|
delayed = true;
|
||||||
ath12k_dbg(ab, ATH12K_DBG_QMI, "qmi delays mem_request %d\n",
|
ath12k_dbg(ab, ATH12K_DBG_QMI, "qmi delays mem_request %d\n",
|
||||||
ab->qmi.mem_seg_count);
|
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 ath12k_hw_group *ag = ab->ag;
|
||||||
struct target_mem_chunk *mlo_chunk;
|
struct target_mem_chunk *mlo_chunk;
|
||||||
|
bool fixed_mem;
|
||||||
|
|
||||||
lockdep_assert_held(&ag->mutex);
|
lockdep_assert_held(&ag->mutex);
|
||||||
|
|
||||||
|
|
@ -2453,8 +2457,13 @@ static void ath12k_qmi_free_mlo_mem_chunk(struct ath12k_base *ab,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fixed_mem = test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags);
|
||||||
mlo_chunk = &ag->mlo_mem.chunk[idx];
|
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,
|
dma_free_coherent(ab->dev,
|
||||||
mlo_chunk->size,
|
mlo_chunk->size,
|
||||||
mlo_chunk->v.addr,
|
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->paddr = 0;
|
||||||
mlo_chunk->size = 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->paddr = 0;
|
||||||
chunk->size = 0;
|
chunk->size = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -2475,19 +2487,24 @@ static void ath12k_qmi_free_target_mem_chunk(struct ath12k_base *ab)
|
||||||
int i, mlo_idx;
|
int i, mlo_idx;
|
||||||
|
|
||||||
for (i = 0, mlo_idx = 0; i < ab->qmi.mem_seg_count; i++) {
|
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) {
|
if (ab->qmi.target_mem[i].type == MLO_GLOBAL_MEM_REGION_TYPE) {
|
||||||
ath12k_qmi_free_mlo_mem_chunk(ab,
|
ath12k_qmi_free_mlo_mem_chunk(ab,
|
||||||
&ab->qmi.target_mem[i],
|
&ab->qmi.target_mem[i],
|
||||||
mlo_idx++);
|
mlo_idx++);
|
||||||
} else {
|
} else {
|
||||||
dma_free_coherent(ab->dev,
|
if (test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags) &&
|
||||||
ab->qmi.target_mem[i].prev_size,
|
ab->qmi.target_mem[i].v.ioaddr) {
|
||||||
ab->qmi.target_mem[i].v.addr,
|
iounmap(ab->qmi.target_mem[i].v.ioaddr);
|
||||||
ab->qmi.target_mem[i].paddr);
|
ab->qmi.target_mem[i].v.ioaddr = NULL;
|
||||||
ab->qmi.target_mem[i].v.addr = 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 @@ static int ath12k_qmi_alloc_target_mem_chunk(struct ath12k_base *ab)
|
||||||
return ret;
|
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 */
|
/* clang stack usage explodes if this is inlined */
|
||||||
static noinline_for_stack
|
static noinline_for_stack
|
||||||
int ath12k_qmi_request_target_cap(struct ath12k_base *ab)
|
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);
|
msg->mem_seg[i].type, msg->mem_seg[i].size);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ath12k_qmi_alloc_target_mem_chunk(ab);
|
if (test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags)) {
|
||||||
if (ret) {
|
ret = ath12k_qmi_assign_target_mem_chunk(ab);
|
||||||
ath12k_warn(ab, "qmi failed to alloc target memory: %d\n",
|
if (ret) {
|
||||||
ret);
|
ath12k_warn(ab, "failed to assign qmi target memory: %d\n",
|
||||||
return;
|
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);
|
ath12k_qmi_driver_event_post(qmi, ATH12K_QMI_EVENT_REQUEST_MEM, NULL);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user