420 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2018, The Linux Foundation. All rights reserved.4 */5 6#include <linux/edac.h>7#include <linux/interrupt.h>8#include <linux/kernel.h>9#include <linux/of.h>10#include <linux/platform_device.h>11#include <linux/regmap.h>12#include <linux/soc/qcom/llcc-qcom.h>13 14#include "edac_mc.h"15#include "edac_device.h"16 17#define EDAC_LLCC "qcom_llcc"18 19#define LLCC_ERP_PANIC_ON_UE 120 21#define TRP_SYN_REG_CNT 622#define DRP_SYN_REG_CNT 823 24#define LLCC_LB_CNT_MASK GENMASK(31, 28)25#define LLCC_LB_CNT_SHIFT 2826 27/* Mask and shift macros */28#define ECC_DB_ERR_COUNT_MASK GENMASK(4, 0)29#define ECC_DB_ERR_WAYS_MASK GENMASK(31, 16)30#define ECC_DB_ERR_WAYS_SHIFT BIT(4)31 32#define ECC_SB_ERR_COUNT_MASK GENMASK(23, 16)33#define ECC_SB_ERR_COUNT_SHIFT BIT(4)34#define ECC_SB_ERR_WAYS_MASK GENMASK(15, 0)35 36#define SB_ECC_ERROR BIT(0)37#define DB_ECC_ERROR BIT(1)38 39#define DRP_TRP_INT_CLEAR GENMASK(1, 0)40#define DRP_TRP_CNT_CLEAR GENMASK(1, 0)41 42#define SB_ERROR_THRESHOLD 0x143#define SB_ERROR_THRESHOLD_SHIFT 2444#define SB_DB_TRP_INTERRUPT_ENABLE 0x345#define TRP0_INTERRUPT_ENABLE 0x146#define DRP0_INTERRUPT_ENABLE BIT(6)47#define SB_DB_DRP_INTERRUPT_ENABLE 0x348 49#define ECC_POLL_MSEC 500050 51enum {52 LLCC_DRAM_CE = 0,53 LLCC_DRAM_UE,54 LLCC_TRAM_CE,55 LLCC_TRAM_UE,56};57 58static const struct llcc_edac_reg_data edac_reg_data[] = {59 [LLCC_DRAM_CE] = {60 .name = "DRAM Single-bit",61 .reg_cnt = DRP_SYN_REG_CNT,62 .count_mask = ECC_SB_ERR_COUNT_MASK,63 .ways_mask = ECC_SB_ERR_WAYS_MASK,64 .count_shift = ECC_SB_ERR_COUNT_SHIFT,65 },66 [LLCC_DRAM_UE] = {67 .name = "DRAM Double-bit",68 .reg_cnt = DRP_SYN_REG_CNT,69 .count_mask = ECC_DB_ERR_COUNT_MASK,70 .ways_mask = ECC_DB_ERR_WAYS_MASK,71 .ways_shift = ECC_DB_ERR_WAYS_SHIFT,72 },73 [LLCC_TRAM_CE] = {74 .name = "TRAM Single-bit",75 .reg_cnt = TRP_SYN_REG_CNT,76 .count_mask = ECC_SB_ERR_COUNT_MASK,77 .ways_mask = ECC_SB_ERR_WAYS_MASK,78 .count_shift = ECC_SB_ERR_COUNT_SHIFT,79 },80 [LLCC_TRAM_UE] = {81 .name = "TRAM Double-bit",82 .reg_cnt = TRP_SYN_REG_CNT,83 .count_mask = ECC_DB_ERR_COUNT_MASK,84 .ways_mask = ECC_DB_ERR_WAYS_MASK,85 .ways_shift = ECC_DB_ERR_WAYS_SHIFT,86 },87};88 89static int qcom_llcc_core_setup(struct llcc_drv_data *drv, struct regmap *llcc_bcast_regmap)90{91 u32 sb_err_threshold;92 int ret;93 94 /*95 * Configure interrupt enable registers such that Tag, Data RAM related96 * interrupts are propagated to interrupt controller for servicing97 */98 ret = regmap_update_bits(llcc_bcast_regmap, drv->edac_reg_offset->cmn_interrupt_2_enable,99 TRP0_INTERRUPT_ENABLE,100 TRP0_INTERRUPT_ENABLE);101 if (ret)102 return ret;103 104 ret = regmap_update_bits(llcc_bcast_regmap, drv->edac_reg_offset->trp_interrupt_0_enable,105 SB_DB_TRP_INTERRUPT_ENABLE,106 SB_DB_TRP_INTERRUPT_ENABLE);107 if (ret)108 return ret;109 110 sb_err_threshold = (SB_ERROR_THRESHOLD << SB_ERROR_THRESHOLD_SHIFT);111 ret = regmap_write(llcc_bcast_regmap, drv->edac_reg_offset->drp_ecc_error_cfg,112 sb_err_threshold);113 if (ret)114 return ret;115 116 ret = regmap_update_bits(llcc_bcast_regmap, drv->edac_reg_offset->cmn_interrupt_2_enable,117 DRP0_INTERRUPT_ENABLE,118 DRP0_INTERRUPT_ENABLE);119 if (ret)120 return ret;121 122 ret = regmap_write(llcc_bcast_regmap, drv->edac_reg_offset->drp_interrupt_enable,123 SB_DB_DRP_INTERRUPT_ENABLE);124 return ret;125}126 127/* Clear the error interrupt and counter registers */128static int129qcom_llcc_clear_error_status(int err_type, struct llcc_drv_data *drv)130{131 int ret;132 133 switch (err_type) {134 case LLCC_DRAM_CE:135 case LLCC_DRAM_UE:136 ret = regmap_write(drv->bcast_regmap,137 drv->edac_reg_offset->drp_interrupt_clear,138 DRP_TRP_INT_CLEAR);139 if (ret)140 return ret;141 142 ret = regmap_write(drv->bcast_regmap,143 drv->edac_reg_offset->drp_ecc_error_cntr_clear,144 DRP_TRP_CNT_CLEAR);145 if (ret)146 return ret;147 break;148 case LLCC_TRAM_CE:149 case LLCC_TRAM_UE:150 ret = regmap_write(drv->bcast_regmap,151 drv->edac_reg_offset->trp_interrupt_0_clear,152 DRP_TRP_INT_CLEAR);153 if (ret)154 return ret;155 156 ret = regmap_write(drv->bcast_regmap,157 drv->edac_reg_offset->trp_ecc_error_cntr_clear,158 DRP_TRP_CNT_CLEAR);159 if (ret)160 return ret;161 break;162 default:163 ret = -EINVAL;164 edac_printk(KERN_CRIT, EDAC_LLCC, "Unexpected error type: %d\n",165 err_type);166 }167 return ret;168}169 170struct qcom_llcc_syn_regs {171 u32 synd_reg;172 u32 count_status_reg;173 u32 ways_status_reg;174};175 176static void get_reg_offsets(struct llcc_drv_data *drv, int err_type,177 struct qcom_llcc_syn_regs *syn_regs)178{179 const struct llcc_edac_reg_offset *edac_reg_offset = drv->edac_reg_offset;180 181 switch (err_type) {182 case LLCC_DRAM_CE:183 syn_regs->synd_reg = edac_reg_offset->drp_ecc_sb_err_syn0;184 syn_regs->count_status_reg = edac_reg_offset->drp_ecc_error_status1;185 syn_regs->ways_status_reg = edac_reg_offset->drp_ecc_error_status0;186 break;187 case LLCC_DRAM_UE:188 syn_regs->synd_reg = edac_reg_offset->drp_ecc_db_err_syn0;189 syn_regs->count_status_reg = edac_reg_offset->drp_ecc_error_status1;190 syn_regs->ways_status_reg = edac_reg_offset->drp_ecc_error_status0;191 break;192 case LLCC_TRAM_CE:193 syn_regs->synd_reg = edac_reg_offset->trp_ecc_sb_err_syn0;194 syn_regs->count_status_reg = edac_reg_offset->trp_ecc_error_status1;195 syn_regs->ways_status_reg = edac_reg_offset->trp_ecc_error_status0;196 break;197 case LLCC_TRAM_UE:198 syn_regs->synd_reg = edac_reg_offset->trp_ecc_db_err_syn0;199 syn_regs->count_status_reg = edac_reg_offset->trp_ecc_error_status1;200 syn_regs->ways_status_reg = edac_reg_offset->trp_ecc_error_status0;201 break;202 }203}204 205/* Dump Syndrome registers data for Tag RAM, Data RAM bit errors*/206static int207dump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type)208{209 struct llcc_edac_reg_data reg_data = edac_reg_data[err_type];210 struct qcom_llcc_syn_regs regs = { };211 int err_cnt, err_ways, ret, i;212 u32 synd_reg, synd_val;213 214 get_reg_offsets(drv, err_type, ®s);215 216 for (i = 0; i < reg_data.reg_cnt; i++) {217 synd_reg = regs.synd_reg + (i * 4);218 ret = regmap_read(drv->regmaps[bank], synd_reg,219 &synd_val);220 if (ret)221 goto clear;222 223 edac_printk(KERN_CRIT, EDAC_LLCC, "%s: ECC_SYN%d: 0x%8x\n",224 reg_data.name, i, synd_val);225 }226 227 ret = regmap_read(drv->regmaps[bank], regs.count_status_reg,228 &err_cnt);229 if (ret)230 goto clear;231 232 err_cnt &= reg_data.count_mask;233 err_cnt >>= reg_data.count_shift;234 edac_printk(KERN_CRIT, EDAC_LLCC, "%s: Error count: 0x%4x\n",235 reg_data.name, err_cnt);236 237 ret = regmap_read(drv->regmaps[bank], regs.ways_status_reg,238 &err_ways);239 if (ret)240 goto clear;241 242 err_ways &= reg_data.ways_mask;243 err_ways >>= reg_data.ways_shift;244 245 edac_printk(KERN_CRIT, EDAC_LLCC, "%s: Error ways: 0x%4x\n",246 reg_data.name, err_ways);247 248clear:249 return qcom_llcc_clear_error_status(err_type, drv);250}251 252static int253dump_syn_reg(struct edac_device_ctl_info *edev_ctl, int err_type, u32 bank)254{255 struct llcc_drv_data *drv = edev_ctl->dev->platform_data;256 int ret;257 258 ret = dump_syn_reg_values(drv, bank, err_type);259 if (ret)260 return ret;261 262 switch (err_type) {263 case LLCC_DRAM_CE:264 edac_device_handle_ce(edev_ctl, 0, bank,265 "LLCC Data RAM correctable Error");266 break;267 case LLCC_DRAM_UE:268 edac_device_handle_ue(edev_ctl, 0, bank,269 "LLCC Data RAM uncorrectable Error");270 break;271 case LLCC_TRAM_CE:272 edac_device_handle_ce(edev_ctl, 0, bank,273 "LLCC Tag RAM correctable Error");274 break;275 case LLCC_TRAM_UE:276 edac_device_handle_ue(edev_ctl, 0, bank,277 "LLCC Tag RAM uncorrectable Error");278 break;279 default:280 ret = -EINVAL;281 edac_printk(KERN_CRIT, EDAC_LLCC, "Unexpected error type: %d\n",282 err_type);283 }284 285 return ret;286}287 288static irqreturn_t llcc_ecc_irq_handler(int irq, void *edev_ctl)289{290 struct edac_device_ctl_info *edac_dev_ctl = edev_ctl;291 struct llcc_drv_data *drv = edac_dev_ctl->dev->platform_data;292 irqreturn_t irq_rc = IRQ_NONE;293 u32 drp_error, trp_error, i;294 int ret;295 296 /* Iterate over the banks and look for Tag RAM or Data RAM errors */297 for (i = 0; i < drv->num_banks; i++) {298 ret = regmap_read(drv->regmaps[i], drv->edac_reg_offset->drp_interrupt_status,299 &drp_error);300 301 if (!ret && (drp_error & SB_ECC_ERROR)) {302 edac_printk(KERN_CRIT, EDAC_LLCC,303 "Single Bit Error detected in Data RAM\n");304 ret = dump_syn_reg(edev_ctl, LLCC_DRAM_CE, i);305 } else if (!ret && (drp_error & DB_ECC_ERROR)) {306 edac_printk(KERN_CRIT, EDAC_LLCC,307 "Double Bit Error detected in Data RAM\n");308 ret = dump_syn_reg(edev_ctl, LLCC_DRAM_UE, i);309 }310 if (!ret)311 irq_rc = IRQ_HANDLED;312 313 ret = regmap_read(drv->regmaps[i], drv->edac_reg_offset->trp_interrupt_0_status,314 &trp_error);315 316 if (!ret && (trp_error & SB_ECC_ERROR)) {317 edac_printk(KERN_CRIT, EDAC_LLCC,318 "Single Bit Error detected in Tag RAM\n");319 ret = dump_syn_reg(edev_ctl, LLCC_TRAM_CE, i);320 } else if (!ret && (trp_error & DB_ECC_ERROR)) {321 edac_printk(KERN_CRIT, EDAC_LLCC,322 "Double Bit Error detected in Tag RAM\n");323 ret = dump_syn_reg(edev_ctl, LLCC_TRAM_UE, i);324 }325 if (!ret)326 irq_rc = IRQ_HANDLED;327 }328 329 return irq_rc;330}331 332static void llcc_ecc_check(struct edac_device_ctl_info *edev_ctl)333{334 llcc_ecc_irq_handler(0, edev_ctl);335}336 337static int qcom_llcc_edac_probe(struct platform_device *pdev)338{339 struct llcc_drv_data *llcc_driv_data = pdev->dev.platform_data;340 struct edac_device_ctl_info *edev_ctl;341 struct device *dev = &pdev->dev;342 int ecc_irq;343 int rc;344 345 if (!llcc_driv_data->ecc_irq_configured) {346 rc = qcom_llcc_core_setup(llcc_driv_data, llcc_driv_data->bcast_regmap);347 if (rc)348 return rc;349 }350 351 /* Allocate edac control info */352 edev_ctl = edac_device_alloc_ctl_info(0, "qcom-llcc", 1, "bank",353 llcc_driv_data->num_banks, 1,354 edac_device_alloc_index());355 356 if (!edev_ctl)357 return -ENOMEM;358 359 edev_ctl->dev = dev;360 edev_ctl->mod_name = dev_name(dev);361 edev_ctl->dev_name = dev_name(dev);362 edev_ctl->ctl_name = "llcc";363 edev_ctl->panic_on_ue = LLCC_ERP_PANIC_ON_UE;364 365 /* Check if LLCC driver has passed ECC IRQ */366 ecc_irq = llcc_driv_data->ecc_irq;367 if (ecc_irq > 0) {368 /* Use interrupt mode if IRQ is available */369 rc = devm_request_irq(dev, ecc_irq, llcc_ecc_irq_handler,370 IRQF_TRIGGER_HIGH, "llcc_ecc", edev_ctl);371 if (!rc) {372 edac_op_state = EDAC_OPSTATE_INT;373 goto irq_done;374 }375 }376 377 /* Fall back to polling mode otherwise */378 edev_ctl->poll_msec = ECC_POLL_MSEC;379 edev_ctl->edac_check = llcc_ecc_check;380 edac_op_state = EDAC_OPSTATE_POLL;381 382irq_done:383 rc = edac_device_add_device(edev_ctl);384 if (rc) {385 edac_device_free_ctl_info(edev_ctl);386 return rc;387 }388 389 platform_set_drvdata(pdev, edev_ctl);390 391 return rc;392}393 394static void qcom_llcc_edac_remove(struct platform_device *pdev)395{396 struct edac_device_ctl_info *edev_ctl = dev_get_drvdata(&pdev->dev);397 398 edac_device_del_device(edev_ctl->dev);399 edac_device_free_ctl_info(edev_ctl);400}401 402static const struct platform_device_id qcom_llcc_edac_id_table[] = {403 { .name = "qcom_llcc_edac" },404 {}405};406MODULE_DEVICE_TABLE(platform, qcom_llcc_edac_id_table);407 408static struct platform_driver qcom_llcc_edac_driver = {409 .probe = qcom_llcc_edac_probe,410 .remove_new = qcom_llcc_edac_remove,411 .driver = {412 .name = "qcom_llcc_edac",413 },414 .id_table = qcom_llcc_edac_id_table,415};416module_platform_driver(qcom_llcc_edac_driver);417 418MODULE_DESCRIPTION("QCOM EDAC driver");419MODULE_LICENSE("GPL v2");420