Commit ee13b500 authored by Manivannan Sadhasivam's avatar Manivannan Sadhasivam Committed by Bjorn Andersson

qcom: llcc/edac: Fix the base address used for accessing LLCC banks

The Qualcomm LLCC/EDAC drivers were using a fixed register stride for
accessing the (Control and Status Registers) CSRs of each LLCC bank.
This stride only works for some SoCs like SDM845 for which driver
support was initially added.

But the later SoCs use different register stride that vary between the
banks with holes in-between. So it is not possible to use a single register
stride for accessing the CSRs of each bank. By doing so could result in a
crash.

For fixing this issue, let's obtain the base address of each LLCC bank from
devicetree and get rid of the fixed stride. This also means, there is no
need to rely on reg-names property and the base addresses can be obtained
using the index.

First index is LLCC bank 0 and last index is LLCC broadcast. If the SoC
supports more than one bank, then those need to be defined in devicetree
for index from 1..N-1.
Reported-by: default avatarParikshit Pareek <quic_ppareek@quicinc.com>
Tested-by: default avatarLuca Weiss <luca.weiss@fairphone.com>
Tested-by: Steev Klimaszewski <steev@kali.org> # Thinkpad X13s
Tested-by: Andrew Halaney <ahalaney@redhat.com> # sa8540p-ride
Reviewed-by: default avatarBorislav Petkov (AMD) <bp@alien8.de>
Signed-off-by: default avatarManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: default avatarBjorn Andersson <andersson@kernel.org>
Link: https://lore.kernel.org/r/20230314080443.64635-13-manivannan.sadhasivam@linaro.org
parent 43aa006e
...@@ -213,7 +213,7 @@ dump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type) ...@@ -213,7 +213,7 @@ dump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type)
for (i = 0; i < reg_data.reg_cnt; i++) { for (i = 0; i < reg_data.reg_cnt; i++) {
synd_reg = reg_data.synd_reg + (i * 4); synd_reg = reg_data.synd_reg + (i * 4);
ret = regmap_read(drv->regmap, drv->offsets[bank] + synd_reg, ret = regmap_read(drv->regmaps[bank], synd_reg,
&synd_val); &synd_val);
if (ret) if (ret)
goto clear; goto clear;
...@@ -222,8 +222,7 @@ dump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type) ...@@ -222,8 +222,7 @@ dump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type)
reg_data.name, i, synd_val); reg_data.name, i, synd_val);
} }
ret = regmap_read(drv->regmap, ret = regmap_read(drv->regmaps[bank], reg_data.count_status_reg,
drv->offsets[bank] + reg_data.count_status_reg,
&err_cnt); &err_cnt);
if (ret) if (ret)
goto clear; goto clear;
...@@ -233,8 +232,7 @@ dump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type) ...@@ -233,8 +232,7 @@ dump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type)
edac_printk(KERN_CRIT, EDAC_LLCC, "%s: Error count: 0x%4x\n", edac_printk(KERN_CRIT, EDAC_LLCC, "%s: Error count: 0x%4x\n",
reg_data.name, err_cnt); reg_data.name, err_cnt);
ret = regmap_read(drv->regmap, ret = regmap_read(drv->regmaps[bank], reg_data.ways_status_reg,
drv->offsets[bank] + reg_data.ways_status_reg,
&err_ways); &err_ways);
if (ret) if (ret)
goto clear; goto clear;
...@@ -296,8 +294,7 @@ llcc_ecc_irq_handler(int irq, void *edev_ctl) ...@@ -296,8 +294,7 @@ llcc_ecc_irq_handler(int irq, void *edev_ctl)
/* Iterate over the banks and look for Tag RAM or Data RAM errors */ /* Iterate over the banks and look for Tag RAM or Data RAM errors */
for (i = 0; i < drv->num_banks; i++) { for (i = 0; i < drv->num_banks; i++) {
ret = regmap_read(drv->regmap, ret = regmap_read(drv->regmaps[i], DRP_INTERRUPT_STATUS,
drv->offsets[i] + DRP_INTERRUPT_STATUS,
&drp_error); &drp_error);
if (!ret && (drp_error & SB_ECC_ERROR)) { if (!ret && (drp_error & SB_ECC_ERROR)) {
...@@ -312,8 +309,7 @@ llcc_ecc_irq_handler(int irq, void *edev_ctl) ...@@ -312,8 +309,7 @@ llcc_ecc_irq_handler(int irq, void *edev_ctl)
if (!ret) if (!ret)
irq_rc = IRQ_HANDLED; irq_rc = IRQ_HANDLED;
ret = regmap_read(drv->regmap, ret = regmap_read(drv->regmaps[i], TRP_INTERRUPT_0_STATUS,
drv->offsets[i] + TRP_INTERRUPT_0_STATUS,
&trp_error); &trp_error);
if (!ret && (trp_error & SB_ECC_ERROR)) { if (!ret && (trp_error & SB_ECC_ERROR)) {
......
...@@ -62,8 +62,6 @@ ...@@ -62,8 +62,6 @@
#define LLCC_TRP_WRSC_CACHEABLE_EN 0x21f2c #define LLCC_TRP_WRSC_CACHEABLE_EN 0x21f2c
#define LLCC_TRP_ALGO_CFG8 0x21f30 #define LLCC_TRP_ALGO_CFG8 0x21f30
#define BANK_OFFSET_STRIDE 0x80000
#define LLCC_VERSION_2_0_0_0 0x02000000 #define LLCC_VERSION_2_0_0_0 0x02000000
#define LLCC_VERSION_2_1_0_0 0x02010000 #define LLCC_VERSION_2_1_0_0 0x02010000
#define LLCC_VERSION_4_1_0_0 0x04010000 #define LLCC_VERSION_4_1_0_0 0x04010000
...@@ -898,8 +896,8 @@ static int qcom_llcc_remove(struct platform_device *pdev) ...@@ -898,8 +896,8 @@ static int qcom_llcc_remove(struct platform_device *pdev)
return 0; return 0;
} }
static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, u8 index,
const char *name) const char *name)
{ {
void __iomem *base; void __iomem *base;
struct regmap_config llcc_regmap_config = { struct regmap_config llcc_regmap_config = {
...@@ -909,7 +907,7 @@ static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, ...@@ -909,7 +907,7 @@ static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev,
.fast_io = true, .fast_io = true,
}; };
base = devm_platform_ioremap_resource_byname(pdev, name); base = devm_platform_ioremap_resource(pdev, index);
if (IS_ERR(base)) if (IS_ERR(base))
return ERR_CAST(base); return ERR_CAST(base);
...@@ -927,6 +925,7 @@ static int qcom_llcc_probe(struct platform_device *pdev) ...@@ -927,6 +925,7 @@ static int qcom_llcc_probe(struct platform_device *pdev)
const struct llcc_slice_config *llcc_cfg; const struct llcc_slice_config *llcc_cfg;
u32 sz; u32 sz;
u32 version; u32 version;
struct regmap *regmap;
drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
if (!drv_data) { if (!drv_data) {
...@@ -934,21 +933,51 @@ static int qcom_llcc_probe(struct platform_device *pdev) ...@@ -934,21 +933,51 @@ static int qcom_llcc_probe(struct platform_device *pdev)
goto err; goto err;
} }
drv_data->regmap = qcom_llcc_init_mmio(pdev, "llcc_base"); /* Initialize the first LLCC bank regmap */
if (IS_ERR(drv_data->regmap)) { regmap = qcom_llcc_init_mmio(pdev, 0, "llcc0_base");
ret = PTR_ERR(drv_data->regmap); if (IS_ERR(regmap)) {
ret = PTR_ERR(regmap);
goto err; goto err;
} }
drv_data->bcast_regmap = cfg = of_device_get_match_data(&pdev->dev);
qcom_llcc_init_mmio(pdev, "llcc_broadcast_base");
ret = regmap_read(regmap, cfg->reg_offset[LLCC_COMMON_STATUS0], &num_banks);
if (ret)
goto err;
num_banks &= LLCC_LB_CNT_MASK;
num_banks >>= LLCC_LB_CNT_SHIFT;
drv_data->num_banks = num_banks;
drv_data->regmaps = devm_kcalloc(dev, num_banks, sizeof(*drv_data->regmaps), GFP_KERNEL);
if (!drv_data->regmaps) {
ret = -ENOMEM;
goto err;
}
drv_data->regmaps[0] = regmap;
/* Initialize rest of LLCC bank regmaps */
for (i = 1; i < num_banks; i++) {
char *base = kasprintf(GFP_KERNEL, "llcc%d_base", i);
drv_data->regmaps[i] = qcom_llcc_init_mmio(pdev, i, base);
if (IS_ERR(drv_data->regmaps[i])) {
ret = PTR_ERR(drv_data->regmaps[i]);
kfree(base);
goto err;
}
kfree(base);
}
drv_data->bcast_regmap = qcom_llcc_init_mmio(pdev, i, "llcc_broadcast_base");
if (IS_ERR(drv_data->bcast_regmap)) { if (IS_ERR(drv_data->bcast_regmap)) {
ret = PTR_ERR(drv_data->bcast_regmap); ret = PTR_ERR(drv_data->bcast_regmap);
goto err; goto err;
} }
cfg = of_device_get_match_data(&pdev->dev);
/* Extract version of the IP */ /* Extract version of the IP */
ret = regmap_read(drv_data->bcast_regmap, cfg->reg_offset[LLCC_COMMON_HW_INFO], ret = regmap_read(drv_data->bcast_regmap, cfg->reg_offset[LLCC_COMMON_HW_INFO],
&version); &version);
...@@ -957,15 +986,6 @@ static int qcom_llcc_probe(struct platform_device *pdev) ...@@ -957,15 +986,6 @@ static int qcom_llcc_probe(struct platform_device *pdev)
drv_data->version = version; drv_data->version = version;
ret = regmap_read(drv_data->regmap, cfg->reg_offset[LLCC_COMMON_STATUS0],
&num_banks);
if (ret)
goto err;
num_banks &= LLCC_LB_CNT_MASK;
num_banks >>= LLCC_LB_CNT_SHIFT;
drv_data->num_banks = num_banks;
llcc_cfg = cfg->sct_data; llcc_cfg = cfg->sct_data;
sz = cfg->size; sz = cfg->size;
...@@ -973,16 +993,6 @@ static int qcom_llcc_probe(struct platform_device *pdev) ...@@ -973,16 +993,6 @@ static int qcom_llcc_probe(struct platform_device *pdev)
if (llcc_cfg[i].slice_id > drv_data->max_slices) if (llcc_cfg[i].slice_id > drv_data->max_slices)
drv_data->max_slices = llcc_cfg[i].slice_id; drv_data->max_slices = llcc_cfg[i].slice_id;
drv_data->offsets = devm_kcalloc(dev, num_banks, sizeof(u32),
GFP_KERNEL);
if (!drv_data->offsets) {
ret = -ENOMEM;
goto err;
}
for (i = 0; i < num_banks; i++)
drv_data->offsets[i] = i * BANK_OFFSET_STRIDE;
drv_data->bitmap = devm_bitmap_zalloc(dev, drv_data->max_slices, drv_data->bitmap = devm_bitmap_zalloc(dev, drv_data->max_slices,
GFP_KERNEL); GFP_KERNEL);
if (!drv_data->bitmap) { if (!drv_data->bitmap) {
......
...@@ -120,7 +120,7 @@ struct llcc_edac_reg_offset { ...@@ -120,7 +120,7 @@ struct llcc_edac_reg_offset {
/** /**
* struct llcc_drv_data - Data associated with the llcc driver * struct llcc_drv_data - Data associated with the llcc driver
* @regmap: regmap associated with the llcc device * @regmaps: regmaps associated with the llcc device
* @bcast_regmap: regmap associated with llcc broadcast offset * @bcast_regmap: regmap associated with llcc broadcast offset
* @cfg: pointer to the data structure for slice configuration * @cfg: pointer to the data structure for slice configuration
* @edac_reg_offset: Offset of the LLCC EDAC registers * @edac_reg_offset: Offset of the LLCC EDAC registers
...@@ -129,12 +129,11 @@ struct llcc_edac_reg_offset { ...@@ -129,12 +129,11 @@ struct llcc_edac_reg_offset {
* @max_slices: max slices as read from device tree * @max_slices: max slices as read from device tree
* @num_banks: Number of llcc banks * @num_banks: Number of llcc banks
* @bitmap: Bit map to track the active slice ids * @bitmap: Bit map to track the active slice ids
* @offsets: Pointer to the bank offsets array
* @ecc_irq: interrupt for llcc cache error detection and reporting * @ecc_irq: interrupt for llcc cache error detection and reporting
* @version: Indicates the LLCC version * @version: Indicates the LLCC version
*/ */
struct llcc_drv_data { struct llcc_drv_data {
struct regmap *regmap; struct regmap **regmaps;
struct regmap *bcast_regmap; struct regmap *bcast_regmap;
const struct llcc_slice_config *cfg; const struct llcc_slice_config *cfg;
const struct llcc_edac_reg_offset *edac_reg_offset; const struct llcc_edac_reg_offset *edac_reg_offset;
...@@ -143,7 +142,6 @@ struct llcc_drv_data { ...@@ -143,7 +142,6 @@ struct llcc_drv_data {
u32 max_slices; u32 max_slices;
u32 num_banks; u32 num_banks;
unsigned long *bitmap; unsigned long *bitmap;
u32 *offsets;
int ecc_irq; int ecc_irq;
u32 version; u32 version;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment