mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
soc: qcom: llcc-qcom: get SCT descriptors from fw-populated memory
Retrieve System Cache Table (SCT) descriptors from a shared memory region populated by firmware. SCT initialization and programming are performed entirely by firmware outside of Linux. The LLCC driver only consumes the pre-initialized descriptor data and does not configure SCT itself. Support this mechanism for future SoCs that provide SCT programming via firmware. Signed-off-by: Francisco Munoz Ruiz <francisco.ruiz@oss.qualcomm.com> Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com> Link: https://lore.kernel.org/r/20260407-external_llcc_changes2set-v2-2-b5017ce2020b@oss.qualcomm.com Signed-off-by: Bjorn Andersson <andersson@kernel.org>
This commit is contained in:
parent
cb4a414bac
commit
ac23106a9b
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
|
|
@ -14,6 +13,7 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
@ -76,6 +76,12 @@
|
|||
#define LLCC_VERSION_4_1_0_0 0x04010000
|
||||
#define LLCC_VERSION_6_0_0_0 0X06000000
|
||||
|
||||
#define SLC_SCT_MEM_LAYOUT_VERSION1 1 /* SCT Memory layout version */
|
||||
#define SLC_SCT_DONE 0x00534354444f4e45 /* SCT programming OK */
|
||||
#define SLC_SCT_FAIL 0x005343544641494c /* SCT programming failed */
|
||||
#define SLC_SCT_NAME_LEN 15
|
||||
#define SLC_SCT_SLICE_ACT_ON_BOOT BIT(25)
|
||||
|
||||
/**
|
||||
* struct llcc_slice_config - Data associated with the llcc slice
|
||||
* @usecase_id: Unique id for the client's use case
|
||||
|
|
@ -143,6 +149,87 @@ struct llcc_slice_config {
|
|||
u32 parent_slice_id;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct slc_sct_error - Represents SCT error
|
||||
* @code: FW code status
|
||||
* @param: Holds the SCT programming error
|
||||
*/
|
||||
struct slc_sct_error {
|
||||
__le64 code;
|
||||
__le64 param;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* struct slc_sct_status - SCT programming status
|
||||
* @program_status: Indicates programming success or failure
|
||||
* @version: SCT mem layout version
|
||||
* @error: Error enum and its param
|
||||
*/
|
||||
struct slc_sct_status {
|
||||
__le64 program_status;
|
||||
/* Use the lower 8 bits */
|
||||
__le64 version;
|
||||
struct slc_sct_error error;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* struct slc_sct_details - SCT details
|
||||
* @revision: revision of the SCT table
|
||||
* @name: name of the SCT table
|
||||
*/
|
||||
struct slc_sct_details {
|
||||
u8 revision;
|
||||
char name[SLC_SCT_NAME_LEN];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* struct tcm_mem_info - SC TCM Shared memory details
|
||||
* @is_present: is TCM region present
|
||||
* @offset: offset of TCM shared memory details
|
||||
*/
|
||||
struct slc_tcm_mem_info {
|
||||
__le32 is_present;
|
||||
__le32 offset;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* struct slc_sct_slice_desc - Slice descriptor definition used in shmem
|
||||
* @slice_id: SCID of the slice
|
||||
* @usecase_id: Usecase ID of the slice
|
||||
* @slice_properties:
|
||||
* slice_size: Contains the slice descriptor size - 20 bit wide
|
||||
* rsvd: Reserved space - 4 bit wide
|
||||
* flags: Flags for descriptors - 3 bit wide
|
||||
* MPAM SCID: Bit 24
|
||||
* Activate on boot: Bit 25
|
||||
* Non-HLOS SCID: Bit 26
|
||||
* HWMutex: Ensures only one processor (CPU or MCU) at a time can
|
||||
* access the LLCC hardware resources - 5 bit wide
|
||||
*/
|
||||
struct slc_sct_slice_desc {
|
||||
__le16 slice_id;
|
||||
__le16 usecase_id;
|
||||
__le32 slice_properties;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* struct slc_sct_mem - Shared memory structure
|
||||
* @sct_status: Status of SCT programming
|
||||
* @sct_details: Sct revision and name details
|
||||
* @tcm_mem_info: TCM shared memory presence & offset info
|
||||
* @slice_descs_count: Number of slice desc present in SCT
|
||||
* @scid_max: Maximum no. of SCIDs supported
|
||||
* @slice_descs: Array of SCT slice desc
|
||||
*/
|
||||
struct slc_sct_mem {
|
||||
struct slc_sct_status sct_status;
|
||||
struct slc_sct_details sct_details;
|
||||
struct slc_tcm_mem_info tcm_mem_info;
|
||||
__le32 slice_descs_count;
|
||||
__le32 scid_max;
|
||||
struct slc_sct_slice_desc slice_descs[] __counted_by_le(slice_descs_count);
|
||||
} __packed;
|
||||
|
||||
struct qcom_llcc_config {
|
||||
const struct llcc_slice_config *sct_data;
|
||||
const u32 *reg_offset;
|
||||
|
|
@ -4141,6 +4228,15 @@ static const u32 llcc_v6_reg_offset[] = {
|
|||
[LLCC_TRP_WRS_CACHEABLE_EN] = 0x00042088,
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config hawi_sct_cfg[] = {
|
||||
{
|
||||
.sct_data = NULL,
|
||||
.size = 0,
|
||||
.reg_offset = llcc_v6_reg_offset,
|
||||
.edac_reg_offset = &llcc_v6_edac_reg_offset,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct qcom_llcc_config kaanapali_cfg[] = {
|
||||
{
|
||||
.sct_data = kaanapali_data,
|
||||
|
|
@ -4397,6 +4493,11 @@ static const struct qcom_llcc_config x1e80100_cfg[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config hawi_sct_cfgs = {
|
||||
.llcc_config = hawi_sct_cfg,
|
||||
.num_config = ARRAY_SIZE(hawi_sct_cfg),
|
||||
};
|
||||
|
||||
static const struct qcom_sct_config kaanapali_cfgs = {
|
||||
.llcc_config = kaanapali_cfg,
|
||||
.num_config = ARRAY_SIZE(kaanapali_cfg),
|
||||
|
|
@ -4533,23 +4634,20 @@ static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER;
|
|||
*/
|
||||
struct llcc_slice_desc *llcc_slice_getd(u32 uid)
|
||||
{
|
||||
const struct llcc_slice_config *cfg;
|
||||
u32 sz, i;
|
||||
|
||||
if (IS_ERR(drv_data))
|
||||
return ERR_CAST(drv_data);
|
||||
|
||||
cfg = drv_data->cfg;
|
||||
sz = drv_data->cfg_size;
|
||||
|
||||
for (i = 0; cfg && i < sz; i++, cfg++)
|
||||
if (cfg->usecase_id == uid)
|
||||
break;
|
||||
|
||||
if (i == sz)
|
||||
if (IS_ERR_OR_NULL(drv_data->desc))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return &drv_data->desc[i];
|
||||
for (u32 i = 0; i < drv_data->cfg_size; i++) {
|
||||
if (uid == drv_data->desc[i].uid)
|
||||
return &drv_data->desc[i];
|
||||
}
|
||||
|
||||
dev_err(drv_data->dev, "Failed to get slice desc for uid: %u\n", uid);
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(llcc_slice_getd);
|
||||
|
||||
|
|
@ -5029,6 +5127,12 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev,
|
|||
sz = drv_data->cfg_size;
|
||||
llcc_table = drv_data->cfg;
|
||||
|
||||
for (i = 0; i < sz; i++) {
|
||||
drv_data->desc[i].uid = llcc_table[i].usecase_id;
|
||||
drv_data->desc[i].slice_id = llcc_table[i].slice_id;
|
||||
drv_data->desc[i].slice_size = llcc_table[i].max_cap;
|
||||
}
|
||||
|
||||
if (drv_data->version >= LLCC_VERSION_6_0_0_0) {
|
||||
for (i = 0; i < sz; i++) {
|
||||
ret = _qcom_llcc_cfg_program_v6(&llcc_table[i], cfg);
|
||||
|
|
@ -5064,6 +5168,101 @@ static int qcom_llcc_get_cfg_index(struct platform_device *pdev, u8 *cfg_index,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_llcc_verify_fw_config(struct device *dev,
|
||||
const struct slc_sct_mem *slc_mem)
|
||||
{
|
||||
u64 program_status;
|
||||
|
||||
program_status = le64_to_cpu(slc_mem->sct_status.program_status);
|
||||
|
||||
if (program_status == SLC_SCT_DONE) {
|
||||
u32 desc_count = le32_to_cpu(slc_mem->slice_descs_count);
|
||||
u32 scid_max = le32_to_cpu(slc_mem->scid_max);
|
||||
|
||||
if (desc_count > scid_max) {
|
||||
dev_err(dev, "Descriptor count above max limit (%u > %u)\n",
|
||||
desc_count, scid_max);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
u8 revision = slc_mem->sct_details.revision;
|
||||
char name_buf[SLC_SCT_NAME_LEN];
|
||||
|
||||
memcpy(name_buf, slc_mem->sct_details.name,
|
||||
SLC_SCT_NAME_LEN - 1);
|
||||
name_buf[SLC_SCT_NAME_LEN - 1] = '\0';
|
||||
|
||||
dev_dbg(dev, "SCT init: desc_count=%u, rev=%u, name=%s\n",
|
||||
desc_count, revision, name_buf);
|
||||
|
||||
return 0;
|
||||
} else if (program_status == SLC_SCT_FAIL) {
|
||||
u8 version = (u8)(le64_to_cpu(slc_mem->sct_status.version));
|
||||
u64 code = le64_to_cpu(slc_mem->sct_status.error.code);
|
||||
u64 param = le64_to_cpu(slc_mem->sct_status.error.param);
|
||||
|
||||
if (version == SLC_SCT_MEM_LAYOUT_VERSION1) {
|
||||
dev_err(dev, "SCT init failed: code = %llu, param = %llu, version = 0x%x\n",
|
||||
code, param, version);
|
||||
} else {
|
||||
dev_err(dev, "Found unsupported version %u\n", version);
|
||||
}
|
||||
} else {
|
||||
dev_err(dev, "Unknown SCT Initialization error\n");
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int qcom_llcc_get_fw_config(struct platform_device *pdev)
|
||||
{
|
||||
const struct slc_sct_mem *slc_mem = NULL;
|
||||
const struct slc_sct_slice_desc *memslice;
|
||||
struct device *dev = &pdev->dev;
|
||||
u32 slice_properties;
|
||||
struct resource res;
|
||||
u32 i, sz;
|
||||
int ret;
|
||||
|
||||
ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res);
|
||||
if (ret) {
|
||||
dev_err(dev, "Unable to locate DT /reserved-memory resource\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
slc_mem = devm_memremap(dev, res.start, resource_size(&res), MEMREMAP_WB);
|
||||
if (!slc_mem) {
|
||||
dev_err(dev, "Failed to memremap SLC shared memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = qcom_llcc_verify_fw_config(dev, slc_mem);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sz = le32_to_cpu(slc_mem->slice_descs_count);
|
||||
|
||||
drv_data->desc = devm_kcalloc(dev, sz, sizeof(struct llcc_slice_desc),
|
||||
GFP_KERNEL);
|
||||
if (!drv_data->desc)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < sz; i++) {
|
||||
memslice = &slc_mem->slice_descs[i];
|
||||
drv_data->desc[i].slice_id = le16_to_cpu(memslice->slice_id);
|
||||
drv_data->desc[i].uid = le16_to_cpu(memslice->usecase_id);
|
||||
slice_properties = le32_to_cpu(memslice->slice_properties);
|
||||
/* Set refcount to 1 if FW already activated this descriptor */
|
||||
if (FIELD_GET(SLC_SCT_SLICE_ACT_ON_BOOT, slice_properties))
|
||||
refcount_set(&drv_data->desc[i].refcount, 1);
|
||||
}
|
||||
|
||||
drv_data->cfg = NULL;
|
||||
drv_data->cfg_size = sz;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qcom_llcc_remove(struct platform_device *pdev)
|
||||
{
|
||||
/* Set the global pointer to a error code to avoid referencing it */
|
||||
|
|
@ -5096,8 +5295,6 @@ static int qcom_llcc_probe(struct platform_device *pdev)
|
|||
struct platform_device *llcc_edac;
|
||||
const struct qcom_sct_config *cfgs;
|
||||
const struct qcom_llcc_config *cfg;
|
||||
const struct llcc_slice_config *llcc_cfg;
|
||||
u32 sz;
|
||||
u8 cfg_index;
|
||||
u32 version;
|
||||
struct regmap *regmap;
|
||||
|
|
@ -5190,32 +5387,31 @@ static int qcom_llcc_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
llcc_cfg = cfg->sct_data;
|
||||
sz = cfg->size;
|
||||
drv_data->desc = devm_kcalloc(dev, sz, sizeof(struct llcc_slice_desc), GFP_KERNEL);
|
||||
if (!drv_data->desc) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (i = 0; i < sz; i++) {
|
||||
drv_data->desc[i].slice_id = llcc_cfg[i].slice_id;
|
||||
drv_data->desc[i].slice_size = llcc_cfg[i].max_cap;
|
||||
refcount_set(&drv_data->desc[i].refcount, 0);
|
||||
}
|
||||
|
||||
drv_data->cfg = llcc_cfg;
|
||||
drv_data->cfg_size = sz;
|
||||
drv_data->edac_reg_offset = cfg->edac_reg_offset;
|
||||
drv_data->ecc_irq_configured = cfg->irq_configured;
|
||||
mutex_init(&drv_data->lock);
|
||||
platform_set_drvdata(pdev, drv_data);
|
||||
if (!cfg->size) {
|
||||
ret = qcom_llcc_get_fw_config(pdev);
|
||||
if (ret)
|
||||
goto err;
|
||||
} else {
|
||||
drv_data->cfg = cfg->sct_data;
|
||||
drv_data->cfg_size = cfg->size;
|
||||
drv_data->desc = devm_kcalloc(dev, cfg->size,
|
||||
sizeof(struct llcc_slice_desc), GFP_KERNEL);
|
||||
|
||||
ret = qcom_llcc_cfg_program(pdev, cfg);
|
||||
if (ret)
|
||||
goto err;
|
||||
if (!drv_data->desc) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = qcom_llcc_cfg_program(pdev, cfg);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
drv_data->ecc_irq = platform_get_irq_optional(pdev, 0);
|
||||
drv_data->edac_reg_offset = cfg->edac_reg_offset;
|
||||
drv_data->ecc_irq_configured = cfg->irq_configured;
|
||||
drv_data->dev = dev;
|
||||
|
||||
/*
|
||||
* On some platforms, the access to EDAC registers will be locked by
|
||||
|
|
@ -5231,6 +5427,8 @@ static int qcom_llcc_probe(struct platform_device *pdev)
|
|||
dev_err(dev, "Failed to register llcc edac driver\n");
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, drv_data);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
drv_data = ERR_PTR(-ENODEV);
|
||||
|
|
@ -5239,6 +5437,7 @@ static int qcom_llcc_probe(struct platform_device *pdev)
|
|||
|
||||
static const struct of_device_id qcom_llcc_of_match[] = {
|
||||
{ .compatible = "qcom,glymur-llcc", .data = &glymur_cfgs },
|
||||
{ .compatible = "qcom,hawi-llcc", .data = &hawi_sct_cfgs },
|
||||
{ .compatible = "qcom,ipq5424-llcc", .data = &ipq5424_cfgs},
|
||||
{ .compatible = "qcom,kaanapali-llcc", .data = &kaanapali_cfgs},
|
||||
{ .compatible = "qcom,qcs615-llcc", .data = &qcs615_cfgs},
|
||||
|
|
|
|||
|
|
@ -90,11 +90,13 @@
|
|||
/**
|
||||
* struct llcc_slice_desc - Cache slice descriptor
|
||||
* @slice_id: llcc slice id
|
||||
* @uid: Unique ID associated with the llcc device
|
||||
* @slice_size: Size allocated for the llcc slice
|
||||
* @refcount: Atomic counter to track activate/deactivate calls
|
||||
*/
|
||||
struct llcc_slice_desc {
|
||||
u32 slice_id;
|
||||
u32 uid;
|
||||
size_t slice_size;
|
||||
refcount_t refcount;
|
||||
};
|
||||
|
|
@ -147,6 +149,7 @@ struct llcc_edac_reg_offset {
|
|||
|
||||
/**
|
||||
* struct llcc_drv_data - Data associated with the llcc driver
|
||||
* @dev: device back-pointer for this llcc instance
|
||||
* @regmaps: regmaps associated with the llcc device
|
||||
* @bcast_regmap: regmap associated with llcc broadcast OR offset
|
||||
* @bcast_and_regmap: regmap associated with llcc broadcast AND offset
|
||||
|
|
@ -157,10 +160,11 @@ struct llcc_edac_reg_offset {
|
|||
* @num_banks: Number of llcc banks
|
||||
* @ecc_irq: interrupt for llcc cache error detection and reporting
|
||||
* @ecc_irq_configured: 'True' if firmware has already configured the irq propagation
|
||||
* @desc: Array pointer of pre-allocated LLCC slice descriptors
|
||||
* @version: Indicates the LLCC version
|
||||
* @desc: Array pointer of pre-allocated LLCC slice descriptors
|
||||
*/
|
||||
struct llcc_drv_data {
|
||||
struct device *dev;
|
||||
struct regmap **regmaps;
|
||||
struct regmap *bcast_regmap;
|
||||
struct regmap *bcast_and_regmap;
|
||||
|
|
@ -183,7 +187,7 @@ struct llcc_drv_data {
|
|||
struct llcc_slice_desc *llcc_slice_getd(u32 uid);
|
||||
|
||||
/**
|
||||
* llcc_slice_putd - llcc slice descritpor
|
||||
* llcc_slice_putd - llcc slice descriptor
|
||||
* @desc: Pointer to llcc slice descriptor
|
||||
*/
|
||||
void llcc_slice_putd(struct llcc_slice_desc *desc);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user