[PATCH] soc: qcom: llcc: Add per slice counter and common llcc slice descriptor

From: Unnathi Chalicheemala
Date: Tue Oct 08 2024 - 15:46:53 EST


Introduce atomic per-slice counter to keep track of the
activate/deactivate count per slice and a common llcc slice
descriptor to maintain accurate count.

In case a client driver calls llcc_slice_getd more than once,
it will get the same descriptor for given use case. And if two
client drivers are voting for same slice, this count variable
will help track enable/disable of slice accurately.

Signed-off-by: Unnathi Chalicheemala <quic_uchalich@xxxxxxxxxxx>
---
drivers/soc/qcom/llcc-qcom.c | 50 +++++++++++++++++++++---------
include/linux/soc/qcom/llcc-qcom.h | 4 +++
2 files changed, 40 insertions(+), 14 deletions(-)

diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c
index 8fa4ffd3a9b5..0cb4fd18fd2c 100644
--- a/drivers/soc/qcom/llcc-qcom.c
+++ b/drivers/soc/qcom/llcc-qcom.c
@@ -813,7 +813,6 @@ 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;
- struct llcc_slice_desc *desc;
u32 sz, count;

if (IS_ERR(drv_data))
@@ -826,17 +825,10 @@ struct llcc_slice_desc *llcc_slice_getd(u32 uid)
if (cfg->usecase_id == uid)
break;

- if (count == sz || !cfg)
+ if (count == sz || !cfg || IS_ERR_OR_NULL(drv_data->desc))
return ERR_PTR(-ENODEV);

- desc = kzalloc(sizeof(*desc), GFP_KERNEL);
- if (!desc)
- return ERR_PTR(-ENOMEM);
-
- desc->slice_id = cfg->slice_id;
- desc->slice_size = cfg->max_cap;
-
- return desc;
+ return &drv_data->desc[count];
}
EXPORT_SYMBOL_GPL(llcc_slice_getd);

@@ -847,7 +839,8 @@ EXPORT_SYMBOL_GPL(llcc_slice_getd);
void llcc_slice_putd(struct llcc_slice_desc *desc)
{
if (!IS_ERR_OR_NULL(desc))
- kfree(desc);
+ WARN(atomic_read(&desc->refcount), " Slice %d is still active\n",
+ desc->slice_id);
}
EXPORT_SYMBOL_GPL(llcc_slice_putd);

@@ -923,6 +916,12 @@ int llcc_slice_activate(struct llcc_slice_desc *desc)
return -EINVAL;

mutex_lock(&drv_data->lock);
+ if ((atomic_read(&desc->refcount)) >= 1) {
+ atomic_inc_return(&desc->refcount);
+ mutex_unlock(&drv_data->lock);
+ return 0;
+ }
+
if (test_bit(desc->slice_id, drv_data->bitmap)) {
mutex_unlock(&drv_data->lock);
return 0;
@@ -937,6 +936,7 @@ int llcc_slice_activate(struct llcc_slice_desc *desc)
return ret;
}

+ atomic_inc_return(&desc->refcount);
__set_bit(desc->slice_id, drv_data->bitmap);
mutex_unlock(&drv_data->lock);

@@ -963,6 +963,12 @@ int llcc_slice_deactivate(struct llcc_slice_desc *desc)
return -EINVAL;

mutex_lock(&drv_data->lock);
+ if ((atomic_read(&desc->refcount)) > 1) {
+ atomic_dec_return(&desc->refcount);
+ mutex_unlock(&drv_data->lock);
+ return 0;
+ }
+
if (!test_bit(desc->slice_id, drv_data->bitmap)) {
mutex_unlock(&drv_data->lock);
return 0;
@@ -976,6 +982,7 @@ int llcc_slice_deactivate(struct llcc_slice_desc *desc)
return ret;
}

+ atomic_dec_return(&desc->refcount);
__clear_bit(desc->slice_id, drv_data->bitmap);
mutex_unlock(&drv_data->lock);

@@ -1020,7 +1027,7 @@ static int _qcom_llcc_cfg_program(const struct llcc_slice_config *config,
u32 attr1_val;
u32 attr0_val;
u32 max_cap_cacheline;
- struct llcc_slice_desc desc;
+ struct llcc_slice_desc *desc;

attr1_val = config->cache_mode;
attr1_val |= config->probe_target_ways << ATTR1_PROBE_TARGET_WAYS_SHIFT;
@@ -1165,8 +1172,11 @@ static int _qcom_llcc_cfg_program(const struct llcc_slice_config *config,
}

if (config->activate_on_init) {
- desc.slice_id = config->slice_id;
- ret = llcc_slice_activate(&desc);
+ desc = llcc_slice_getd(config->usecase_id);
+ if (PTR_ERR_OR_ZERO(desc))
+ return -EINVAL;
+
+ ret = llcc_slice_activate(desc);
}

return ret;
@@ -1183,6 +1193,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].slice_id = llcc_table[i].slice_id;
+ drv_data->desc[i].slice_size = llcc_table[i].max_cap;
+ atomic_set(&drv_data->desc[i].refcount, 0);
+ }
+
for (i = 0; i < sz; i++) {
ret = _qcom_llcc_cfg_program(&llcc_table[i], cfg);
if (ret)
@@ -1331,6 +1347,12 @@ static int qcom_llcc_probe(struct platform_device *pdev)
llcc_cfg = cfg->sct_data;
sz = cfg->size;

+ drv_data->desc = devm_kzalloc(dev, sizeof(struct llcc_slice_desc) * sz, GFP_KERNEL);
+ if (IS_ERR_OR_NULL(drv_data->desc)) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
for (i = 0; i < sz; i++)
if (llcc_cfg[i].slice_id > drv_data->max_slices)
drv_data->max_slices = llcc_cfg[i].slice_id;
diff --git a/include/linux/soc/qcom/llcc-qcom.h b/include/linux/soc/qcom/llcc-qcom.h
index 9e9f528b1370..5eca861e2837 100644
--- a/include/linux/soc/qcom/llcc-qcom.h
+++ b/include/linux/soc/qcom/llcc-qcom.h
@@ -60,10 +60,12 @@
* struct llcc_slice_desc - Cache slice descriptor
* @slice_id: llcc slice id
* @slice_size: Size allocated for the llcc slice
+ * @refcount: Counter to track activate/deactivate slice count
*/
struct llcc_slice_desc {
u32 slice_id;
size_t slice_size;
+ atomic_t refcount;
};

/**
@@ -126,6 +128,7 @@ struct llcc_edac_reg_offset {
* @bitmap: Bit map to track the active slice ids
* @ecc_irq: interrupt for llcc cache error detection and reporting
* @version: Indicates the LLCC version
+ * @desc: Array pointer of llcc_slice_desc
*/
struct llcc_drv_data {
struct regmap **regmaps;
@@ -140,6 +143,7 @@ struct llcc_drv_data {
unsigned long *bitmap;
int ecc_irq;
u32 version;
+ struct llcc_slice_desc *desc;
};

#if IS_ENABLED(CONFIG_QCOM_LLCC)
--
2.34.1