summaryrefslogtreecommitdiff
path: root/drivers/soc/qcom
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/soc/qcom')
-rw-r--r--drivers/soc/qcom/llcc-qcom.c481
-rw-r--r--drivers/soc/qcom/qcom-geni-se.c270
-rw-r--r--drivers/soc/qcom/qcom_pd_mapper.c8
-rw-r--r--drivers/soc/qcom/smem.c56
-rw-r--r--drivers/soc/qcom/socinfo.c23
-rw-r--r--drivers/soc/qcom/wcnss_ctrl.c11
6 files changed, 750 insertions, 99 deletions
diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c
index 0161ceec8842..8948b5fd42d2 100644
--- a/drivers/soc/qcom/llcc-qcom.c
+++ b/drivers/soc/qcom/llcc-qcom.c
@@ -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,10 +76,16 @@
#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
+ * struct llcc_slice_config - Data associated with the LLCC slice
* @usecase_id: Unique id for the client's use case
- * @slice_id: llcc slice id for each client
+ * @slice_id: LLCC slice id for each client
* @max_cap: The maximum capacity of the cache slice provided in KB
* @priority: Priority of the client used to select victim line for replacement
* @fixed_size: Boolean indicating if the slice has a fixed capacity
@@ -93,7 +99,7 @@
* slice: normal or TCM(Tightly Coupled Memory)
* @probe_target_ways: Determines what ways to probe for access hit. When
* configured to 1 only bonus and reserved ways are probed.
- * When configured to 0 all ways in llcc are probed.
+ * When configured to 0 all ways in LLCC are probed.
* @dis_cap_alloc: Disable capacity based allocation for a client
* @retain_on_pc: If this bit is set and client has maintained active vote
* then the ways assigned to this client are not flushed on power
@@ -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;
@@ -181,6 +268,171 @@ enum llcc_reg_offset {
LLCC_TRP_WRS_CACHEABLE_EN,
};
+static const struct llcc_slice_config eliza_data[] = {
+ {
+ .usecase_id = LLCC_CPUSS,
+ .slice_id = 1,
+ .max_cap = 896,
+ .bonus_ways = 0xfff,
+ .activate_on_init = true,
+ .write_scid_en = true,
+ .stale_en = true,
+ },
+ {
+ .usecase_id = LLCC_MDMHPFX,
+ .slice_id = 24,
+ .max_cap = 1024,
+ .priority = 5,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ },
+ {
+ .usecase_id = LLCC_VIDSC0,
+ .slice_id = 2,
+ .max_cap = 128,
+ .priority = 5,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ },
+ {
+ .usecase_id = LLCC_MDMHPGRW,
+ .slice_id = 25,
+ .max_cap = 1024,
+ .priority = 5,
+ .bonus_ways = 0xfff,
+ },
+ {
+ .usecase_id = LLCC_GPUHTW,
+ .slice_id = 11,
+ .max_cap = 256,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ },
+ {
+ .usecase_id = LLCC_GPU,
+ .slice_id = 9,
+ .max_cap = 896,
+ .priority = 1,
+ .bonus_ways = 0xfff,
+ .write_scid_cacheable_en = true,
+ },
+ {
+ .usecase_id = LLCC_MMUHWT,
+ .slice_id = 18,
+ .max_cap = 256,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ .activate_on_init = true,
+ },
+ {
+ .usecase_id = LLCC_MDMPNG,
+ .slice_id = 27,
+ .max_cap = 256,
+ .priority = 5,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ },
+ {
+ .usecase_id = LLCC_MODPE,
+ .slice_id = 29,
+ .max_cap = 256,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xf00,
+ .alloc_oneway_en = true,
+ },
+ {
+ .usecase_id = LLCC_WRCACHE,
+ .slice_id = 31,
+ .max_cap = 256,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ .activate_on_init = true,
+ },
+ {
+ .usecase_id = LLCC_LCPDARE,
+ .slice_id = 30,
+ .max_cap = 128,
+ .priority = 5,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ .activate_on_init = true,
+ .alloc_oneway_en = true,
+ },
+ {
+ .usecase_id = LLCC_ISLAND1,
+ .slice_id = 12,
+ .max_cap = 1280,
+ .priority = 7,
+ .fixed_size = true,
+ .res_ways = 0x3ff,
+ },
+ {
+ .usecase_id = LLCC_CAMOFE,
+ .slice_id = 33,
+ .max_cap = 1024,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ .stale_en = true,
+ .parent_slice_id = 13,
+ },
+ {
+ .usecase_id = LLCC_CAMRTIP,
+ .slice_id = 13,
+ .max_cap = 1024,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ .stale_en = true,
+ .parent_slice_id = 13,
+ },
+ {
+ .usecase_id = LLCC_CAMSRTIP,
+ .slice_id = 14,
+ .max_cap = 512,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ .stale_en = true,
+ .parent_slice_id = 13,
+ },
+ {
+ .usecase_id = LLCC_CAMRTRF,
+ .slice_id = 7,
+ .max_cap = 1024,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ .stale_en = true,
+ .parent_slice_id = 13,
+ },
+ {
+ .usecase_id = LLCC_CAMSRTRF,
+ .slice_id = 21,
+ .max_cap = 1024,
+ .priority = 1,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ .stale_en = true,
+ .parent_slice_id = 13,
+ },
+ {
+ .usecase_id = LLCC_CPUSSMPAM,
+ .slice_id = 6,
+ .max_cap = 512,
+ .priority = 0,
+ .fixed_size = true,
+ .bonus_ways = 0xfff,
+ .activate_on_init = true,
+ .write_scid_en = true,
+ .stale_en = true,
+ },
+};
+
static const struct llcc_slice_config glymur_data[] = {
{
.usecase_id = LLCC_CPUSS,
@@ -4141,6 +4393,24 @@ 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 eliza_cfg[] = {
+ {
+ .sct_data = eliza_data,
+ .size = ARRAY_SIZE(eliza_data),
+ .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 +4667,16 @@ 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 eliza_cfgs = {
+ .llcc_config = eliza_cfg,
+ .num_config = ARRAY_SIZE(eliza_cfg),
+};
+
static const struct qcom_sct_config kaanapali_cfgs = {
.llcc_config = kaanapali_cfg,
.num_config = ARRAY_SIZE(kaanapali_cfg),
@@ -4525,37 +4805,34 @@ static const struct qcom_sct_config x1e80100_cfgs = {
static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER;
/**
- * llcc_slice_getd - get llcc slice descriptor
+ * llcc_slice_getd - get LLCC slice descriptor
* @uid: usecase_id for the client
*
- * A pointer to llcc slice descriptor will be returned on success
+ * A pointer to LLCC slice descriptor will be returned on success
* and error pointer is returned on failure
*/
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;
+ if (IS_ERR_OR_NULL(drv_data->desc))
+ return ERR_PTR(-ENODEV);
- for (i = 0; cfg && i < sz; i++, cfg++)
- if (cfg->usecase_id == uid)
- break;
+ for (u32 i = 0; i < drv_data->cfg_size; i++) {
+ if (uid == drv_data->desc[i].uid)
+ return &drv_data->desc[i];
+ }
- if (i == sz)
- return ERR_PTR(-ENODEV);
+ dev_err(drv_data->dev, "Failed to get slice desc for uid: %u\n", uid);
- return &drv_data->desc[i];
+ return ERR_PTR(-EINVAL);
}
EXPORT_SYMBOL_GPL(llcc_slice_getd);
/**
- * llcc_slice_putd - llcc slice descriptor
- * @desc: Pointer to llcc slice descriptor
+ * llcc_slice_putd - LLCC slice descriptor
+ * @desc: Pointer to LLCC slice descriptor
*/
void llcc_slice_putd(struct llcc_slice_desc *desc)
{
@@ -4618,8 +4895,8 @@ static int llcc_update_act_ctrl(u32 sid,
}
/**
- * llcc_slice_activate - Activate the llcc slice
- * @desc: Pointer to llcc slice descriptor
+ * llcc_slice_activate - Activate the LLCC slice
+ * @desc: Pointer to LLCC slice descriptor
*
* A value of zero will be returned on success and a negative errno will
* be returned in error cases
@@ -4654,8 +4931,8 @@ int llcc_slice_activate(struct llcc_slice_desc *desc)
EXPORT_SYMBOL_GPL(llcc_slice_activate);
/**
- * llcc_slice_deactivate - Deactivate the llcc slice
- * @desc: Pointer to llcc slice descriptor
+ * llcc_slice_deactivate - Deactivate the LLCC slice
+ * @desc: Pointer to LLCC slice descriptor
*
* A value of zero will be returned on success and a negative errno will
* be returned in error cases
@@ -4691,7 +4968,7 @@ EXPORT_SYMBOL_GPL(llcc_slice_deactivate);
/**
* llcc_get_slice_id - return the slice id
- * @desc: Pointer to llcc slice descriptor
+ * @desc: Pointer to LLCC slice descriptor
*/
int llcc_get_slice_id(struct llcc_slice_desc *desc)
{
@@ -4704,7 +4981,7 @@ EXPORT_SYMBOL_GPL(llcc_get_slice_id);
/**
* llcc_get_slice_size - return the slice id
- * @desc: Pointer to llcc slice descriptor
+ * @desc: Pointer to LLCC slice descriptor
*/
size_t llcc_get_slice_size(struct llcc_slice_desc *desc)
{
@@ -4738,9 +5015,9 @@ static int _qcom_llcc_cfg_program(const struct llcc_slice_config *config,
/*
* LLCC instances can vary for each target.
* The SW writes to broadcast register which gets propagated
- * to each llcc instance (llcc0,.. llccN).
+ * to each LLCC instance (llcc0,.. llccN).
* Since the size of the memory is divided equally amongst the
- * llcc instances, we need to configure the max cap accordingly.
+ * LLCC instances, we need to configure the max cap accordingly.
*/
max_cap_cacheline = max_cap_cacheline / drv_data->num_banks;
max_cap_cacheline >>= CACHE_LINE_SIZE_SHIFT;
@@ -5029,6 +5306,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 +5347,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 (IS_ERR(slc_mem)) {
+ dev_err(dev, "Failed to memremap SLC shared memory\n");
+ return PTR_ERR(slc_mem);
+ }
+
+ 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 +5474,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 +5566,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;
- }
+ mutex_init(&drv_data->lock);
+ 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);
- 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);
+ if (!drv_data->desc) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = qcom_llcc_cfg_program(pdev, cfg);
+ if (ret)
+ goto err;
}
- drv_data->cfg = llcc_cfg;
- drv_data->cfg_size = sz;
+ 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;
- mutex_init(&drv_data->lock);
- platform_set_drvdata(pdev, drv_data);
-
- ret = qcom_llcc_cfg_program(pdev, cfg);
- if (ret)
- goto err;
-
- drv_data->ecc_irq = platform_get_irq_optional(pdev, 0);
+ drv_data->dev = dev;
/*
* On some platforms, the access to EDAC registers will be locked by
@@ -5228,9 +5603,11 @@ static int qcom_llcc_probe(struct platform_device *pdev)
"qcom_llcc_edac", -1, drv_data,
sizeof(*drv_data));
if (IS_ERR(llcc_edac))
- dev_err(dev, "Failed to register llcc edac driver\n");
+ dev_err(dev, "Failed to register LLCC EDAC driver\n");
}
+ platform_set_drvdata(pdev, drv_data);
+
return 0;
err:
drv_data = ERR_PTR(-ENODEV);
@@ -5238,7 +5615,9 @@ err:
}
static const struct of_device_id qcom_llcc_of_match[] = {
+ { .compatible = "qcom,eliza-llcc", .data = &eliza_cfgs },
{ .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},
diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
index cd1779b6a91a..15636a8dc907 100644
--- a/drivers/soc/qcom/qcom-geni-se.c
+++ b/drivers/soc/qcom/qcom-geni-se.c
@@ -19,6 +19,8 @@
#include <linux/of_platform.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
#include <linux/soc/qcom/geni-se.h>
/**
@@ -280,6 +282,12 @@ struct se_fw_hdr {
#define geni_setbits32(_addr, _v) writel(readl(_addr) | (_v), _addr)
#define geni_clrbits32(_addr, _v) writel(readl(_addr) & ~(_v), _addr)
+enum domain_idx {
+ DOMAIN_IDX_POWER,
+ DOMAIN_IDX_PERF,
+ DOMAIN_IDX_MAX
+};
+
/**
* geni_se_get_qup_hw_version() - Read the QUP wrapper Hardware version
* @se: Pointer to the corresponding serial engine.
@@ -582,6 +590,7 @@ static void geni_se_clks_off(struct geni_se *se)
clk_disable_unprepare(se->clk);
clk_bulk_disable_unprepare(wrapper->num_clks, wrapper->clks);
+ clk_disable_unprepare(se->core_clk);
}
/**
@@ -618,7 +627,18 @@ static int geni_se_clks_on(struct geni_se *se)
ret = clk_prepare_enable(se->clk);
if (ret)
- clk_bulk_disable_unprepare(wrapper->num_clks, wrapper->clks);
+ goto err_bulk_clks;
+
+ ret = clk_prepare_enable(se->core_clk);
+ if (ret)
+ goto err_se_clk;
+
+ return 0;
+
+err_se_clk:
+ clk_disable_unprepare(se->clk);
+err_bulk_clks:
+ clk_bulk_disable_unprepare(wrapper->num_clks, wrapper->clks);
return ret;
}
@@ -899,30 +919,32 @@ EXPORT_SYMBOL_GPL(geni_se_rx_dma_unprep);
int geni_icc_get(struct geni_se *se, const char *icc_ddr)
{
- int i, err;
- const char *icc_names[] = {"qup-core", "qup-config", icc_ddr};
+ struct geni_icc_path *icc_paths = se->icc_paths;
if (has_acpi_companion(se->dev))
return 0;
- for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++) {
- if (!icc_names[i])
- continue;
-
- se->icc_paths[i].path = devm_of_icc_get(se->dev, icc_names[i]);
- if (IS_ERR(se->icc_paths[i].path))
- goto err;
+ icc_paths[GENI_TO_CORE].path = devm_of_icc_get(se->dev, "qup-core");
+ if (IS_ERR(icc_paths[GENI_TO_CORE].path))
+ return dev_err_probe(se->dev, PTR_ERR(icc_paths[GENI_TO_CORE].path),
+ "Failed to get 'qup-core' ICC path\n");
+
+ icc_paths[CPU_TO_GENI].path = devm_of_icc_get(se->dev, "qup-config");
+ if (IS_ERR(icc_paths[CPU_TO_GENI].path))
+ return dev_err_probe(se->dev, PTR_ERR(icc_paths[CPU_TO_GENI].path),
+ "Failed to get 'qup-config' ICC path\n");
+
+ /* The DDR path is optional, depending on protocol and hw capabilities */
+ icc_paths[GENI_TO_DDR].path = devm_of_icc_get(se->dev, "qup-memory");
+ if (IS_ERR(icc_paths[GENI_TO_DDR].path)) {
+ if (PTR_ERR(icc_paths[GENI_TO_DDR].path) == -ENODATA)
+ icc_paths[GENI_TO_DDR].path = NULL;
+ else
+ return dev_err_probe(se->dev, PTR_ERR(icc_paths[GENI_TO_DDR].path),
+ "Failed to get 'qup-memory' ICC path\n");
}
return 0;
-
-err:
- err = PTR_ERR(se->icc_paths[i].path);
- if (err != -EPROBE_DEFER)
- dev_err_ratelimited(se->dev, "Failed to get ICC path '%s': %d\n",
- icc_names[i], err);
- return err;
-
}
EXPORT_SYMBOL_GPL(geni_icc_get);
@@ -944,6 +966,28 @@ int geni_icc_set_bw(struct geni_se *se)
}
EXPORT_SYMBOL_GPL(geni_icc_set_bw);
+/**
+ * geni_icc_set_bw_ab() - Set average bandwidth for all ICC paths and apply
+ * @se: Pointer to the concerned serial engine.
+ * @core_ab: Average bandwidth in kBps for GENI_TO_CORE path.
+ * @cfg_ab: Average bandwidth in kBps for CPU_TO_GENI path.
+ * @ddr_ab: Average bandwidth in kBps for GENI_TO_DDR path.
+ *
+ * Sets bandwidth values for all ICC paths and applies them. DDR path is
+ * optional and only set if it exists.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int geni_icc_set_bw_ab(struct geni_se *se, u32 core_ab, u32 cfg_ab, u32 ddr_ab)
+{
+ se->icc_paths[GENI_TO_CORE].avg_bw = core_ab;
+ se->icc_paths[CPU_TO_GENI].avg_bw = cfg_ab;
+ se->icc_paths[GENI_TO_DDR].avg_bw = ddr_ab;
+
+ return geni_icc_set_bw(se);
+}
+EXPORT_SYMBOL_GPL(geni_icc_set_bw_ab);
+
void geni_icc_set_tag(struct geni_se *se, u32 tag)
{
int i;
@@ -989,6 +1033,196 @@ int geni_icc_disable(struct geni_se *se)
EXPORT_SYMBOL_GPL(geni_icc_disable);
/**
+ * geni_se_resources_deactivate() - Deactivate GENI SE device resources
+ * @se: Pointer to the geni_se structure
+ *
+ * Deactivates device resources for power saving: OPP rate to 0, pin control
+ * to sleep state, turns off clocks, and disables interconnect. Skips ACPI devices.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int geni_se_resources_deactivate(struct geni_se *se)
+{
+ int ret;
+
+ if (has_acpi_companion(se->dev))
+ return 0;
+
+ if (se->has_opp)
+ dev_pm_opp_set_rate(se->dev, 0);
+
+ ret = pinctrl_pm_select_sleep_state(se->dev);
+ if (ret)
+ return ret;
+
+ geni_se_clks_off(se);
+
+ return geni_icc_disable(se);
+}
+EXPORT_SYMBOL_GPL(geni_se_resources_deactivate);
+
+/**
+ * geni_se_resources_activate() - Activate GENI SE device resources
+ * @se: Pointer to the geni_se structure
+ *
+ * Activates device resources for operation: enables interconnect, prepares clocks,
+ * and sets pin control to default state. Includes error cleanup. Skips ACPI devices.
+ *
+ * Unlike geni_se_resources_deactivate(), this function doesn't alter the
+ * connected genpds' performance states, which must be additionally handled.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int geni_se_resources_activate(struct geni_se *se)
+{
+ int ret;
+
+ if (has_acpi_companion(se->dev))
+ return 0;
+
+ ret = geni_icc_enable(se);
+ if (ret)
+ return ret;
+
+ ret = geni_se_clks_on(se);
+ if (ret)
+ goto out_icc_disable;
+
+ ret = pinctrl_pm_select_default_state(se->dev);
+ if (ret) {
+ geni_se_clks_off(se);
+ goto out_icc_disable;
+ }
+
+ return 0;
+
+out_icc_disable:
+ geni_icc_disable(se);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(geni_se_resources_activate);
+
+/**
+ * geni_se_set_perf_level() - Set performance level for GENI SE.
+ * @se: Pointer to the struct geni_se instance.
+ * @level: The desired performance level.
+ *
+ * Sets the performance level by directly calling dev_pm_opp_set_level
+ * on the performance device associated with the SE.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int geni_se_set_perf_level(struct geni_se *se, unsigned long level)
+{
+ return dev_pm_opp_set_level(se->pd_list->pd_devs[DOMAIN_IDX_PERF], level);
+}
+EXPORT_SYMBOL_GPL(geni_se_set_perf_level);
+
+/**
+ * geni_se_set_perf_opp() - Set performance OPP for GENI SE by frequency.
+ * @se: Pointer to the struct geni_se instance.
+ * @clk_freq: The requested clock frequency.
+ *
+ * Finds the nearest operating performance point (OPP) for the given
+ * clock frequency and applies it to the SE's performance device.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int geni_se_set_perf_opp(struct geni_se *se, unsigned long clk_freq)
+{
+ struct device *perf_dev = se->pd_list->pd_devs[DOMAIN_IDX_PERF];
+ struct dev_pm_opp *opp;
+ int ret;
+
+ opp = dev_pm_opp_find_freq_floor(perf_dev, &clk_freq);
+ if (IS_ERR(opp)) {
+ dev_err(se->dev, "failed to find opp for freq %lu\n", clk_freq);
+ return PTR_ERR(opp);
+ }
+
+ ret = dev_pm_opp_set_opp(perf_dev, opp);
+ dev_pm_opp_put(opp);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(geni_se_set_perf_opp);
+
+/**
+ * geni_se_domain_attach() - Attach power domains to a GENI SE device.
+ * @se: Pointer to the geni_se structure representing the GENI SE device.
+ *
+ * This function attaches the power domains ("power" and "perf") required
+ * in the SCMI auto-VM environment to the GENI Serial Engine device. It
+ * initializes se->pd_list with the attached domains.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+int geni_se_domain_attach(struct geni_se *se)
+{
+ struct dev_pm_domain_attach_data pd_data = {
+ .pd_flags = PD_FLAG_DEV_LINK_ON,
+ .pd_names = (const char*[]) { "power", "perf" },
+ .num_pd_names = 2,
+ };
+ int ret;
+
+ ret = devm_pm_domain_attach_list(se->dev,
+ &pd_data, &se->pd_list);
+ if (ret == 0)
+ return -ENODEV;
+ else if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(geni_se_domain_attach);
+
+/**
+ * geni_se_resources_init() - Initialize resources for a GENI SE device.
+ * @se: Pointer to the geni_se structure representing the GENI SE device.
+ *
+ * This function initializes various resources required by the GENI Serial Engine
+ * (SE) device, including clock resources (core and SE clocks), interconnect
+ * paths for communication.
+ * It retrieves optional and mandatory clock resources, adds an OF-based
+ * operating performance point (OPP) table, and sets up interconnect paths
+ * with default bandwidths. The function also sets a flag (`has_opp`) to
+ * indicate whether OPP support is available for the device.
+ *
+ * Return: 0 on success, or a negative errno on failure.
+ */
+int geni_se_resources_init(struct geni_se *se)
+{
+ int ret;
+
+ se->core_clk = devm_clk_get_optional(se->dev, "core");
+ if (IS_ERR(se->core_clk))
+ return dev_err_probe(se->dev, PTR_ERR(se->core_clk),
+ "Failed to get optional core clk\n");
+
+ se->clk = devm_clk_get(se->dev, "se");
+ if (IS_ERR(se->clk) && !has_acpi_companion(se->dev))
+ return dev_err_probe(se->dev, PTR_ERR(se->clk),
+ "Failed to get SE clk\n");
+
+ ret = devm_pm_opp_set_clkname(se->dev, "se");
+ if (ret)
+ return ret;
+
+ ret = devm_pm_opp_of_add_table(se->dev);
+ if (ret && ret != -ENODEV)
+ return dev_err_probe(se->dev, ret, "Failed to add OPP table\n");
+
+ se->has_opp = (ret == 0);
+
+ ret = geni_icc_get(se, "qup-memory");
+ if (ret)
+ return ret;
+
+ return geni_icc_set_bw_ab(se, GENI_DEFAULT_BW, GENI_DEFAULT_BW, GENI_DEFAULT_BW);
+}
+EXPORT_SYMBOL_GPL(geni_se_resources_init);
+
+/**
* geni_find_protocol_fw() - Locate and validate SE firmware for a protocol.
* @dev: Pointer to the device structure.
* @fw: Pointer to the firmware image.
diff --git a/drivers/soc/qcom/qcom_pd_mapper.c b/drivers/soc/qcom/qcom_pd_mapper.c
index 7bb14c20ab5d..b99718e25f2f 100644
--- a/drivers/soc/qcom/qcom_pd_mapper.c
+++ b/drivers/soc/qcom/qcom_pd_mapper.c
@@ -266,6 +266,12 @@ static const struct qcom_pdm_domain_data adsp_charger_pd = {
.services = { NULL },
};
+static const struct qcom_pdm_domain_data adsp_ois_pd = {
+ .domain = "msm/adsp/ois_pd",
+ .instance_id = 74,
+ .services = { NULL, },
+};
+
static const struct qcom_pdm_domain_data adsp_root_pd = {
.domain = "msm/adsp/root_pd",
.instance_id = 74,
@@ -370,6 +376,7 @@ static const struct qcom_pdm_domain_data *glymur_domains[] = {
static const struct qcom_pdm_domain_data *kaanapali_domains[] = {
&adsp_audio_pd,
+ &adsp_ois_pd,
&adsp_root_pd,
&adsp_sensor_pd,
&cdsp_root_pd,
@@ -581,6 +588,7 @@ static const struct of_device_id qcom_pdm_domains[] __maybe_unused = {
{ .compatible = "qcom,eliza", .data = sm8550_domains, },
{ .compatible = "qcom,apq8096", .data = msm8996_domains, },
{ .compatible = "qcom,glymur", .data = glymur_domains, },
+ { .compatible = "qcom,hawi", .data = kaanapali_domains, },
{ .compatible = "qcom,kaanapali", .data = kaanapali_domains, },
{ .compatible = "qcom,mahua", .data = glymur_domains, },
{ .compatible = "qcom,milos", .data = sm8550_domains, },
diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
index d5c94b47f431..afb21a778fe7 100644
--- a/drivers/soc/qcom/smem.c
+++ b/drivers/soc/qcom/smem.c
@@ -85,9 +85,6 @@
/* Processor/host identifier for the global partition */
#define SMEM_GLOBAL_HOST 0xfffe
-/* Max number of processors/hosts in a system */
-#define SMEM_HOST_COUNT 25
-
/**
* struct smem_proc_comm - proc_comm communication struct (legacy)
* @command: current command to be executed
@@ -282,7 +279,7 @@ struct qcom_smem {
struct platform_device *socinfo;
struct smem_ptable *ptable;
struct smem_partition global_partition;
- struct smem_partition partitions[SMEM_HOST_COUNT];
+ struct xarray partitions;
unsigned num_regions;
struct smem_region regions[] __counted_by(num_regions);
@@ -382,7 +379,7 @@ static struct qcom_smem *__smem = INIT_ERR_PTR(-EPROBE_DEFER);
int qcom_smem_bust_hwspin_lock_by_host(unsigned int host)
{
/* This function is for remote procs, so ignore SMEM_HOST_APPS */
- if (host == SMEM_HOST_APPS || host >= SMEM_HOST_COUNT)
+ if (host == SMEM_HOST_APPS || !xa_load(&__smem->partitions, host))
return -EINVAL;
return hwspin_lock_bust(__smem->hwlock, SMEM_HOST_ID_TO_HWSPINLOCK_ID(host));
@@ -530,8 +527,8 @@ int qcom_smem_alloc(unsigned host, unsigned item, size_t size)
if (ret)
return ret;
- if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
- part = &__smem->partitions[host];
+ part = xa_load(&__smem->partitions, host);
+ if (part) {
ret = qcom_smem_alloc_private(__smem, part, item, size);
} else if (__smem->global_partition.virt_base) {
part = &__smem->global_partition;
@@ -697,8 +694,8 @@ void *qcom_smem_get(unsigned host, unsigned item, size_t *size)
if (item >= __smem->item_count)
return ERR_PTR(-EINVAL);
- if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
- part = &__smem->partitions[host];
+ part = xa_load(&__smem->partitions, host);
+ if (part) {
ptr = qcom_smem_get_private(__smem, part, item, size);
} else if (__smem->global_partition.virt_base) {
part = &__smem->global_partition;
@@ -730,8 +727,8 @@ int qcom_smem_get_free_space(unsigned host)
if (IS_ERR(__smem))
return PTR_ERR(__smem);
- if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) {
- part = &__smem->partitions[host];
+ part = xa_load(&__smem->partitions, host);
+ if (part) {
phdr = part->virt_base;
ret = le32_to_cpu(phdr->offset_free_cached) -
le32_to_cpu(phdr->offset_free_uncached);
@@ -774,12 +771,11 @@ phys_addr_t qcom_smem_virt_to_phys(void *p)
{
struct smem_partition *part;
struct smem_region *area;
+ unsigned long index;
u64 offset;
u32 i;
- for (i = 0; i < SMEM_HOST_COUNT; i++) {
- part = &__smem->partitions[i];
-
+ xa_for_each(&__smem->partitions, index, part) {
if (addr_in_range(part->virt_base, part->size, p)) {
offset = p - part->virt_base;
@@ -1016,16 +1012,20 @@ static int
qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host)
{
struct smem_partition_header *header;
+ struct smem_partition *part;
struct smem_ptable_entry *entry;
struct smem_ptable *ptable;
u16 remote_host;
u16 host0, host1;
+ int ret;
int i;
ptable = qcom_smem_get_ptable(smem);
if (IS_ERR(ptable))
return PTR_ERR(ptable);
+ xa_init(&smem->partitions);
+
for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) {
entry = &ptable->entry[i];
if (!le32_to_cpu(entry->offset))
@@ -1042,12 +1042,7 @@ qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host)
else
continue;
- if (remote_host >= SMEM_HOST_COUNT) {
- dev_err(smem->dev, "bad host %u\n", remote_host);
- return -EINVAL;
- }
-
- if (smem->partitions[remote_host].virt_base) {
+ if (xa_load(&smem->partitions, remote_host)) {
dev_err(smem->dev, "duplicate host %u\n", remote_host);
return -EINVAL;
}
@@ -1056,11 +1051,20 @@ qcom_smem_enumerate_partitions(struct qcom_smem *smem, u16 local_host)
if (!header)
return -EINVAL;
- smem->partitions[remote_host].virt_base = (void __iomem *)header;
- smem->partitions[remote_host].phys_base = smem->regions[0].aux_base +
- le32_to_cpu(entry->offset);
- smem->partitions[remote_host].size = le32_to_cpu(entry->size);
- smem->partitions[remote_host].cacheline = le32_to_cpu(entry->cacheline);
+ part = devm_kzalloc(smem->dev, sizeof(struct smem_partition), GFP_KERNEL);
+ if (!part)
+ return -ENOMEM;
+
+ part->virt_base = (void __iomem *)header;
+ part->phys_base = smem->regions[0].aux_base + le32_to_cpu(entry->offset);
+ part->size = le32_to_cpu(entry->size);
+ part->cacheline = le32_to_cpu(entry->cacheline);
+
+ ret = xa_insert(&smem->partitions, remote_host, part, GFP_KERNEL);
+ if (ret) {
+ dev_err(smem->dev, "fail to insert host %u\n", remote_host);
+ return ret;
+ }
}
return 0;
@@ -1229,7 +1233,6 @@ static int qcom_smem_probe(struct platform_device *pdev)
return -EINVAL;
}
- BUILD_BUG_ON(SMEM_HOST_APPS >= SMEM_HOST_COUNT);
ret = qcom_smem_enumerate_partitions(smem, SMEM_HOST_APPS);
if (ret < 0 && ret != -ENOENT)
return ret;
@@ -1249,6 +1252,7 @@ static void qcom_smem_remove(struct platform_device *pdev)
{
platform_device_unregister(__smem->socinfo);
+ xa_destroy(&__smem->partitions);
/* Set to -EPROBE_DEFER to signal unprobed state */
__smem = ERR_PTR(-EPROBE_DEFER);
}
diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c
index 8ffd903ebddb..af418adad7aa 100644
--- a/drivers/soc/qcom/socinfo.c
+++ b/drivers/soc/qcom/socinfo.c
@@ -188,7 +188,19 @@ static const char *const pmic_models[] = {
[80] = "PM7550",
[82] = "PMC8380",
[83] = "SMB2360",
+ [86] = "PM8750B",
+ [87] = "PMD8028",
+ [88] = "PMC1020H",
+ [89] = "PMIV0104",
+ [90] = "PMIV0102",
[91] = "PMIV0108",
+ [92] = "PMK8850",
+ [93] = "PMH0101",
+ [94] = "PMAU0102",
+ [95] = "SMB2370",
+ [96] = "PMH0104",
+ [97] = "PMH0110",
+ [98] = "PMCX0102",
};
struct socinfo_params {
@@ -519,6 +531,7 @@ static const struct soc_id soc_id[] = {
{ qcom_board_id(IPQ5424) },
{ qcom_board_id(QCM6690) },
{ qcom_board_id(QCS6690) },
+ { qcom_board_id(SM7750) },
{ qcom_board_id(SM8850) },
{ qcom_board_id(IPQ5404) },
{ qcom_board_id(QCS9100) },
@@ -526,13 +539,23 @@ static const struct soc_id soc_id[] = {
{ qcom_board_id(QCS8275) },
{ qcom_board_id(QCS9075) },
{ qcom_board_id(QCS615) },
+ { qcom_board_id(SA8797P) },
{ qcom_board_id(CQ7790M) },
{ qcom_board_id(CQ7790S) },
+ { qcom_board_id(CQ2390M) },
+ { qcom_board_id(CQ2390S) },
+ { qcom_board_id(IQ2390S) },
{ qcom_board_id(IPQ5200) },
{ qcom_board_id(IPQ5210) },
{ qcom_board_id(QCF2200) },
{ qcom_board_id(QCF3200) },
{ qcom_board_id(QCF3210) },
+ { qcom_board_id(IPQ9620) },
+ { qcom_board_id(IPQ9650) },
+ { qcom_board_id(IPQ9610) },
+ { qcom_board_id(IPQ9630) },
+ { qcom_board_id(IPQ9640) },
+ { qcom_board_id(IPQ9670) },
};
static const char *socinfo_machine(struct device *dev, unsigned int id)
diff --git a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c
index ffb31a049d4a..942e11feba65 100644
--- a/drivers/soc/qcom/wcnss_ctrl.c
+++ b/drivers/soc/qcom/wcnss_ctrl.c
@@ -221,8 +221,10 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc)
left = fw->size;
req = kzalloc_flex(*req, fragment, NV_FRAGMENT_SIZE);
- if (!req)
- return -ENOMEM;
+ if (!req) {
+ ret = -ENOMEM;
+ goto release_fw;
+ }
req->frag_size = NV_FRAGMENT_SIZE;
req->hdr.type = WCNSS_DOWNLOAD_NV_REQ;
@@ -243,7 +245,7 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc)
ret = rpmsg_send(wcnss->channel, req, req->hdr.len);
if (ret < 0) {
dev_err(dev, "failed to send smd packet\n");
- goto release_fw;
+ goto release_req;
}
/* Increment for next fragment */
@@ -262,9 +264,10 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc)
ret = 0;
}
+release_req:
+ kfree(req);
release_fw:
release_firmware(fw);
- kfree(req);
return ret;
}