18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2018, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/edac.h> 78c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/of.h> 108c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 118c2ecf20Sopenharmony_ci#include <linux/regmap.h> 128c2ecf20Sopenharmony_ci#include <linux/soc/qcom/llcc-qcom.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "edac_mc.h" 158c2ecf20Sopenharmony_ci#include "edac_device.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define EDAC_LLCC "qcom_llcc" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define LLCC_ERP_PANIC_ON_UE 1 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define TRP_SYN_REG_CNT 6 228c2ecf20Sopenharmony_ci#define DRP_SYN_REG_CNT 8 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define LLCC_COMMON_STATUS0 0x0003000c 258c2ecf20Sopenharmony_ci#define LLCC_LB_CNT_MASK GENMASK(31, 28) 268c2ecf20Sopenharmony_ci#define LLCC_LB_CNT_SHIFT 28 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Single & double bit syndrome register offsets */ 298c2ecf20Sopenharmony_ci#define TRP_ECC_SB_ERR_SYN0 0x0002304c 308c2ecf20Sopenharmony_ci#define TRP_ECC_DB_ERR_SYN0 0x00020370 318c2ecf20Sopenharmony_ci#define DRP_ECC_SB_ERR_SYN0 0x0004204c 328c2ecf20Sopenharmony_ci#define DRP_ECC_DB_ERR_SYN0 0x00042070 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* Error register offsets */ 358c2ecf20Sopenharmony_ci#define TRP_ECC_ERROR_STATUS1 0x00020348 368c2ecf20Sopenharmony_ci#define TRP_ECC_ERROR_STATUS0 0x00020344 378c2ecf20Sopenharmony_ci#define DRP_ECC_ERROR_STATUS1 0x00042048 388c2ecf20Sopenharmony_ci#define DRP_ECC_ERROR_STATUS0 0x00042044 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* TRP, DRP interrupt register offsets */ 418c2ecf20Sopenharmony_ci#define DRP_INTERRUPT_STATUS 0x00041000 428c2ecf20Sopenharmony_ci#define TRP_INTERRUPT_0_STATUS 0x00020480 438c2ecf20Sopenharmony_ci#define DRP_INTERRUPT_CLEAR 0x00041008 448c2ecf20Sopenharmony_ci#define DRP_ECC_ERROR_CNTR_CLEAR 0x00040004 458c2ecf20Sopenharmony_ci#define TRP_INTERRUPT_0_CLEAR 0x00020484 468c2ecf20Sopenharmony_ci#define TRP_ECC_ERROR_CNTR_CLEAR 0x00020440 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* Mask and shift macros */ 498c2ecf20Sopenharmony_ci#define ECC_DB_ERR_COUNT_MASK GENMASK(4, 0) 508c2ecf20Sopenharmony_ci#define ECC_DB_ERR_WAYS_MASK GENMASK(31, 16) 518c2ecf20Sopenharmony_ci#define ECC_DB_ERR_WAYS_SHIFT BIT(4) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define ECC_SB_ERR_COUNT_MASK GENMASK(23, 16) 548c2ecf20Sopenharmony_ci#define ECC_SB_ERR_COUNT_SHIFT BIT(4) 558c2ecf20Sopenharmony_ci#define ECC_SB_ERR_WAYS_MASK GENMASK(15, 0) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define SB_ECC_ERROR BIT(0) 588c2ecf20Sopenharmony_ci#define DB_ECC_ERROR BIT(1) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define DRP_TRP_INT_CLEAR GENMASK(1, 0) 618c2ecf20Sopenharmony_ci#define DRP_TRP_CNT_CLEAR GENMASK(1, 0) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* Config registers offsets*/ 648c2ecf20Sopenharmony_ci#define DRP_ECC_ERROR_CFG 0x00040000 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* Tag RAM, Data RAM interrupt register offsets */ 678c2ecf20Sopenharmony_ci#define CMN_INTERRUPT_0_ENABLE 0x0003001c 688c2ecf20Sopenharmony_ci#define CMN_INTERRUPT_2_ENABLE 0x0003003c 698c2ecf20Sopenharmony_ci#define TRP_INTERRUPT_0_ENABLE 0x00020488 708c2ecf20Sopenharmony_ci#define DRP_INTERRUPT_ENABLE 0x0004100c 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define SB_ERROR_THRESHOLD 0x1 738c2ecf20Sopenharmony_ci#define SB_ERROR_THRESHOLD_SHIFT 24 748c2ecf20Sopenharmony_ci#define SB_DB_TRP_INTERRUPT_ENABLE 0x3 758c2ecf20Sopenharmony_ci#define TRP0_INTERRUPT_ENABLE 0x1 768c2ecf20Sopenharmony_ci#define DRP0_INTERRUPT_ENABLE BIT(6) 778c2ecf20Sopenharmony_ci#define SB_DB_DRP_INTERRUPT_ENABLE 0x3 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cienum { 808c2ecf20Sopenharmony_ci LLCC_DRAM_CE = 0, 818c2ecf20Sopenharmony_ci LLCC_DRAM_UE, 828c2ecf20Sopenharmony_ci LLCC_TRAM_CE, 838c2ecf20Sopenharmony_ci LLCC_TRAM_UE, 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic const struct llcc_edac_reg_data edac_reg_data[] = { 878c2ecf20Sopenharmony_ci [LLCC_DRAM_CE] = { 888c2ecf20Sopenharmony_ci .name = "DRAM Single-bit", 898c2ecf20Sopenharmony_ci .synd_reg = DRP_ECC_SB_ERR_SYN0, 908c2ecf20Sopenharmony_ci .count_status_reg = DRP_ECC_ERROR_STATUS1, 918c2ecf20Sopenharmony_ci .ways_status_reg = DRP_ECC_ERROR_STATUS0, 928c2ecf20Sopenharmony_ci .reg_cnt = DRP_SYN_REG_CNT, 938c2ecf20Sopenharmony_ci .count_mask = ECC_SB_ERR_COUNT_MASK, 948c2ecf20Sopenharmony_ci .ways_mask = ECC_SB_ERR_WAYS_MASK, 958c2ecf20Sopenharmony_ci .count_shift = ECC_SB_ERR_COUNT_SHIFT, 968c2ecf20Sopenharmony_ci }, 978c2ecf20Sopenharmony_ci [LLCC_DRAM_UE] = { 988c2ecf20Sopenharmony_ci .name = "DRAM Double-bit", 998c2ecf20Sopenharmony_ci .synd_reg = DRP_ECC_DB_ERR_SYN0, 1008c2ecf20Sopenharmony_ci .count_status_reg = DRP_ECC_ERROR_STATUS1, 1018c2ecf20Sopenharmony_ci .ways_status_reg = DRP_ECC_ERROR_STATUS0, 1028c2ecf20Sopenharmony_ci .reg_cnt = DRP_SYN_REG_CNT, 1038c2ecf20Sopenharmony_ci .count_mask = ECC_DB_ERR_COUNT_MASK, 1048c2ecf20Sopenharmony_ci .ways_mask = ECC_DB_ERR_WAYS_MASK, 1058c2ecf20Sopenharmony_ci .ways_shift = ECC_DB_ERR_WAYS_SHIFT, 1068c2ecf20Sopenharmony_ci }, 1078c2ecf20Sopenharmony_ci [LLCC_TRAM_CE] = { 1088c2ecf20Sopenharmony_ci .name = "TRAM Single-bit", 1098c2ecf20Sopenharmony_ci .synd_reg = TRP_ECC_SB_ERR_SYN0, 1108c2ecf20Sopenharmony_ci .count_status_reg = TRP_ECC_ERROR_STATUS1, 1118c2ecf20Sopenharmony_ci .ways_status_reg = TRP_ECC_ERROR_STATUS0, 1128c2ecf20Sopenharmony_ci .reg_cnt = TRP_SYN_REG_CNT, 1138c2ecf20Sopenharmony_ci .count_mask = ECC_SB_ERR_COUNT_MASK, 1148c2ecf20Sopenharmony_ci .ways_mask = ECC_SB_ERR_WAYS_MASK, 1158c2ecf20Sopenharmony_ci .count_shift = ECC_SB_ERR_COUNT_SHIFT, 1168c2ecf20Sopenharmony_ci }, 1178c2ecf20Sopenharmony_ci [LLCC_TRAM_UE] = { 1188c2ecf20Sopenharmony_ci .name = "TRAM Double-bit", 1198c2ecf20Sopenharmony_ci .synd_reg = TRP_ECC_DB_ERR_SYN0, 1208c2ecf20Sopenharmony_ci .count_status_reg = TRP_ECC_ERROR_STATUS1, 1218c2ecf20Sopenharmony_ci .ways_status_reg = TRP_ECC_ERROR_STATUS0, 1228c2ecf20Sopenharmony_ci .reg_cnt = TRP_SYN_REG_CNT, 1238c2ecf20Sopenharmony_ci .count_mask = ECC_DB_ERR_COUNT_MASK, 1248c2ecf20Sopenharmony_ci .ways_mask = ECC_DB_ERR_WAYS_MASK, 1258c2ecf20Sopenharmony_ci .ways_shift = ECC_DB_ERR_WAYS_SHIFT, 1268c2ecf20Sopenharmony_ci }, 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int qcom_llcc_core_setup(struct regmap *llcc_bcast_regmap) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci u32 sb_err_threshold; 1328c2ecf20Sopenharmony_ci int ret; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* 1358c2ecf20Sopenharmony_ci * Configure interrupt enable registers such that Tag, Data RAM related 1368c2ecf20Sopenharmony_ci * interrupts are propagated to interrupt controller for servicing 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci ret = regmap_update_bits(llcc_bcast_regmap, CMN_INTERRUPT_2_ENABLE, 1398c2ecf20Sopenharmony_ci TRP0_INTERRUPT_ENABLE, 1408c2ecf20Sopenharmony_ci TRP0_INTERRUPT_ENABLE); 1418c2ecf20Sopenharmony_ci if (ret) 1428c2ecf20Sopenharmony_ci return ret; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci ret = regmap_update_bits(llcc_bcast_regmap, TRP_INTERRUPT_0_ENABLE, 1458c2ecf20Sopenharmony_ci SB_DB_TRP_INTERRUPT_ENABLE, 1468c2ecf20Sopenharmony_ci SB_DB_TRP_INTERRUPT_ENABLE); 1478c2ecf20Sopenharmony_ci if (ret) 1488c2ecf20Sopenharmony_ci return ret; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci sb_err_threshold = (SB_ERROR_THRESHOLD << SB_ERROR_THRESHOLD_SHIFT); 1518c2ecf20Sopenharmony_ci ret = regmap_write(llcc_bcast_regmap, DRP_ECC_ERROR_CFG, 1528c2ecf20Sopenharmony_ci sb_err_threshold); 1538c2ecf20Sopenharmony_ci if (ret) 1548c2ecf20Sopenharmony_ci return ret; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ret = regmap_update_bits(llcc_bcast_regmap, CMN_INTERRUPT_2_ENABLE, 1578c2ecf20Sopenharmony_ci DRP0_INTERRUPT_ENABLE, 1588c2ecf20Sopenharmony_ci DRP0_INTERRUPT_ENABLE); 1598c2ecf20Sopenharmony_ci if (ret) 1608c2ecf20Sopenharmony_ci return ret; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci ret = regmap_write(llcc_bcast_regmap, DRP_INTERRUPT_ENABLE, 1638c2ecf20Sopenharmony_ci SB_DB_DRP_INTERRUPT_ENABLE); 1648c2ecf20Sopenharmony_ci return ret; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* Clear the error interrupt and counter registers */ 1688c2ecf20Sopenharmony_cistatic int 1698c2ecf20Sopenharmony_ciqcom_llcc_clear_error_status(int err_type, struct llcc_drv_data *drv) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci int ret = 0; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci switch (err_type) { 1748c2ecf20Sopenharmony_ci case LLCC_DRAM_CE: 1758c2ecf20Sopenharmony_ci case LLCC_DRAM_UE: 1768c2ecf20Sopenharmony_ci ret = regmap_write(drv->bcast_regmap, DRP_INTERRUPT_CLEAR, 1778c2ecf20Sopenharmony_ci DRP_TRP_INT_CLEAR); 1788c2ecf20Sopenharmony_ci if (ret) 1798c2ecf20Sopenharmony_ci return ret; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci ret = regmap_write(drv->bcast_regmap, DRP_ECC_ERROR_CNTR_CLEAR, 1828c2ecf20Sopenharmony_ci DRP_TRP_CNT_CLEAR); 1838c2ecf20Sopenharmony_ci if (ret) 1848c2ecf20Sopenharmony_ci return ret; 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci case LLCC_TRAM_CE: 1878c2ecf20Sopenharmony_ci case LLCC_TRAM_UE: 1888c2ecf20Sopenharmony_ci ret = regmap_write(drv->bcast_regmap, TRP_INTERRUPT_0_CLEAR, 1898c2ecf20Sopenharmony_ci DRP_TRP_INT_CLEAR); 1908c2ecf20Sopenharmony_ci if (ret) 1918c2ecf20Sopenharmony_ci return ret; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci ret = regmap_write(drv->bcast_regmap, TRP_ECC_ERROR_CNTR_CLEAR, 1948c2ecf20Sopenharmony_ci DRP_TRP_CNT_CLEAR); 1958c2ecf20Sopenharmony_ci if (ret) 1968c2ecf20Sopenharmony_ci return ret; 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci default: 1998c2ecf20Sopenharmony_ci ret = -EINVAL; 2008c2ecf20Sopenharmony_ci edac_printk(KERN_CRIT, EDAC_LLCC, "Unexpected error type: %d\n", 2018c2ecf20Sopenharmony_ci err_type); 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci return ret; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/* Dump Syndrome registers data for Tag RAM, Data RAM bit errors*/ 2078c2ecf20Sopenharmony_cistatic int 2088c2ecf20Sopenharmony_cidump_syn_reg_values(struct llcc_drv_data *drv, u32 bank, int err_type) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct llcc_edac_reg_data reg_data = edac_reg_data[err_type]; 2118c2ecf20Sopenharmony_ci int err_cnt, err_ways, ret, i; 2128c2ecf20Sopenharmony_ci u32 synd_reg, synd_val; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci for (i = 0; i < reg_data.reg_cnt; i++) { 2158c2ecf20Sopenharmony_ci synd_reg = reg_data.synd_reg + (i * 4); 2168c2ecf20Sopenharmony_ci ret = regmap_read(drv->regmap, drv->offsets[bank] + synd_reg, 2178c2ecf20Sopenharmony_ci &synd_val); 2188c2ecf20Sopenharmony_ci if (ret) 2198c2ecf20Sopenharmony_ci goto clear; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci edac_printk(KERN_CRIT, EDAC_LLCC, "%s: ECC_SYN%d: 0x%8x\n", 2228c2ecf20Sopenharmony_ci reg_data.name, i, synd_val); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ret = regmap_read(drv->regmap, 2268c2ecf20Sopenharmony_ci drv->offsets[bank] + reg_data.count_status_reg, 2278c2ecf20Sopenharmony_ci &err_cnt); 2288c2ecf20Sopenharmony_ci if (ret) 2298c2ecf20Sopenharmony_ci goto clear; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci err_cnt &= reg_data.count_mask; 2328c2ecf20Sopenharmony_ci err_cnt >>= reg_data.count_shift; 2338c2ecf20Sopenharmony_ci edac_printk(KERN_CRIT, EDAC_LLCC, "%s: Error count: 0x%4x\n", 2348c2ecf20Sopenharmony_ci reg_data.name, err_cnt); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci ret = regmap_read(drv->regmap, 2378c2ecf20Sopenharmony_ci drv->offsets[bank] + reg_data.ways_status_reg, 2388c2ecf20Sopenharmony_ci &err_ways); 2398c2ecf20Sopenharmony_ci if (ret) 2408c2ecf20Sopenharmony_ci goto clear; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci err_ways &= reg_data.ways_mask; 2438c2ecf20Sopenharmony_ci err_ways >>= reg_data.ways_shift; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci edac_printk(KERN_CRIT, EDAC_LLCC, "%s: Error ways: 0x%4x\n", 2468c2ecf20Sopenharmony_ci reg_data.name, err_ways); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ciclear: 2498c2ecf20Sopenharmony_ci return qcom_llcc_clear_error_status(err_type, drv); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic int 2538c2ecf20Sopenharmony_cidump_syn_reg(struct edac_device_ctl_info *edev_ctl, int err_type, u32 bank) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct llcc_drv_data *drv = edev_ctl->dev->platform_data; 2568c2ecf20Sopenharmony_ci int ret; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci ret = dump_syn_reg_values(drv, bank, err_type); 2598c2ecf20Sopenharmony_ci if (ret) 2608c2ecf20Sopenharmony_ci return ret; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci switch (err_type) { 2638c2ecf20Sopenharmony_ci case LLCC_DRAM_CE: 2648c2ecf20Sopenharmony_ci edac_device_handle_ce(edev_ctl, 0, bank, 2658c2ecf20Sopenharmony_ci "LLCC Data RAM correctable Error"); 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci case LLCC_DRAM_UE: 2688c2ecf20Sopenharmony_ci edac_device_handle_ue(edev_ctl, 0, bank, 2698c2ecf20Sopenharmony_ci "LLCC Data RAM uncorrectable Error"); 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci case LLCC_TRAM_CE: 2728c2ecf20Sopenharmony_ci edac_device_handle_ce(edev_ctl, 0, bank, 2738c2ecf20Sopenharmony_ci "LLCC Tag RAM correctable Error"); 2748c2ecf20Sopenharmony_ci break; 2758c2ecf20Sopenharmony_ci case LLCC_TRAM_UE: 2768c2ecf20Sopenharmony_ci edac_device_handle_ue(edev_ctl, 0, bank, 2778c2ecf20Sopenharmony_ci "LLCC Tag RAM uncorrectable Error"); 2788c2ecf20Sopenharmony_ci break; 2798c2ecf20Sopenharmony_ci default: 2808c2ecf20Sopenharmony_ci ret = -EINVAL; 2818c2ecf20Sopenharmony_ci edac_printk(KERN_CRIT, EDAC_LLCC, "Unexpected error type: %d\n", 2828c2ecf20Sopenharmony_ci err_type); 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return ret; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic irqreturn_t 2898c2ecf20Sopenharmony_cillcc_ecc_irq_handler(int irq, void *edev_ctl) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci struct edac_device_ctl_info *edac_dev_ctl = edev_ctl; 2928c2ecf20Sopenharmony_ci struct llcc_drv_data *drv = edac_dev_ctl->dev->platform_data; 2938c2ecf20Sopenharmony_ci irqreturn_t irq_rc = IRQ_NONE; 2948c2ecf20Sopenharmony_ci u32 drp_error, trp_error, i; 2958c2ecf20Sopenharmony_ci int ret; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* Iterate over the banks and look for Tag RAM or Data RAM errors */ 2988c2ecf20Sopenharmony_ci for (i = 0; i < drv->num_banks; i++) { 2998c2ecf20Sopenharmony_ci ret = regmap_read(drv->regmap, 3008c2ecf20Sopenharmony_ci drv->offsets[i] + DRP_INTERRUPT_STATUS, 3018c2ecf20Sopenharmony_ci &drp_error); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (!ret && (drp_error & SB_ECC_ERROR)) { 3048c2ecf20Sopenharmony_ci edac_printk(KERN_CRIT, EDAC_LLCC, 3058c2ecf20Sopenharmony_ci "Single Bit Error detected in Data RAM\n"); 3068c2ecf20Sopenharmony_ci ret = dump_syn_reg(edev_ctl, LLCC_DRAM_CE, i); 3078c2ecf20Sopenharmony_ci } else if (!ret && (drp_error & DB_ECC_ERROR)) { 3088c2ecf20Sopenharmony_ci edac_printk(KERN_CRIT, EDAC_LLCC, 3098c2ecf20Sopenharmony_ci "Double Bit Error detected in Data RAM\n"); 3108c2ecf20Sopenharmony_ci ret = dump_syn_reg(edev_ctl, LLCC_DRAM_UE, i); 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci if (!ret) 3138c2ecf20Sopenharmony_ci irq_rc = IRQ_HANDLED; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci ret = regmap_read(drv->regmap, 3168c2ecf20Sopenharmony_ci drv->offsets[i] + TRP_INTERRUPT_0_STATUS, 3178c2ecf20Sopenharmony_ci &trp_error); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (!ret && (trp_error & SB_ECC_ERROR)) { 3208c2ecf20Sopenharmony_ci edac_printk(KERN_CRIT, EDAC_LLCC, 3218c2ecf20Sopenharmony_ci "Single Bit Error detected in Tag RAM\n"); 3228c2ecf20Sopenharmony_ci ret = dump_syn_reg(edev_ctl, LLCC_TRAM_CE, i); 3238c2ecf20Sopenharmony_ci } else if (!ret && (trp_error & DB_ECC_ERROR)) { 3248c2ecf20Sopenharmony_ci edac_printk(KERN_CRIT, EDAC_LLCC, 3258c2ecf20Sopenharmony_ci "Double Bit Error detected in Tag RAM\n"); 3268c2ecf20Sopenharmony_ci ret = dump_syn_reg(edev_ctl, LLCC_TRAM_UE, i); 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci if (!ret) 3298c2ecf20Sopenharmony_ci irq_rc = IRQ_HANDLED; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return irq_rc; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int qcom_llcc_edac_probe(struct platform_device *pdev) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct llcc_drv_data *llcc_driv_data = pdev->dev.platform_data; 3388c2ecf20Sopenharmony_ci struct edac_device_ctl_info *edev_ctl; 3398c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 3408c2ecf20Sopenharmony_ci int ecc_irq; 3418c2ecf20Sopenharmony_ci int rc; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci rc = qcom_llcc_core_setup(llcc_driv_data->bcast_regmap); 3448c2ecf20Sopenharmony_ci if (rc) 3458c2ecf20Sopenharmony_ci return rc; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* Allocate edac control info */ 3488c2ecf20Sopenharmony_ci edev_ctl = edac_device_alloc_ctl_info(0, "qcom-llcc", 1, "bank", 3498c2ecf20Sopenharmony_ci llcc_driv_data->num_banks, 1, 3508c2ecf20Sopenharmony_ci NULL, 0, 3518c2ecf20Sopenharmony_ci edac_device_alloc_index()); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (!edev_ctl) 3548c2ecf20Sopenharmony_ci return -ENOMEM; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci edev_ctl->dev = dev; 3578c2ecf20Sopenharmony_ci edev_ctl->mod_name = dev_name(dev); 3588c2ecf20Sopenharmony_ci edev_ctl->dev_name = dev_name(dev); 3598c2ecf20Sopenharmony_ci edev_ctl->ctl_name = "llcc"; 3608c2ecf20Sopenharmony_ci edev_ctl->panic_on_ue = LLCC_ERP_PANIC_ON_UE; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci rc = edac_device_add_device(edev_ctl); 3638c2ecf20Sopenharmony_ci if (rc) 3648c2ecf20Sopenharmony_ci goto out_mem; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, edev_ctl); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* Request for ecc irq */ 3698c2ecf20Sopenharmony_ci ecc_irq = llcc_driv_data->ecc_irq; 3708c2ecf20Sopenharmony_ci if (ecc_irq < 0) { 3718c2ecf20Sopenharmony_ci rc = -ENODEV; 3728c2ecf20Sopenharmony_ci goto out_dev; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci rc = devm_request_irq(dev, ecc_irq, llcc_ecc_irq_handler, 3758c2ecf20Sopenharmony_ci IRQF_TRIGGER_HIGH, "llcc_ecc", edev_ctl); 3768c2ecf20Sopenharmony_ci if (rc) 3778c2ecf20Sopenharmony_ci goto out_dev; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return rc; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ciout_dev: 3828c2ecf20Sopenharmony_ci edac_device_del_device(edev_ctl->dev); 3838c2ecf20Sopenharmony_ciout_mem: 3848c2ecf20Sopenharmony_ci edac_device_free_ctl_info(edev_ctl); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return rc; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic int qcom_llcc_edac_remove(struct platform_device *pdev) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct edac_device_ctl_info *edev_ctl = dev_get_drvdata(&pdev->dev); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci edac_device_del_device(edev_ctl->dev); 3948c2ecf20Sopenharmony_ci edac_device_free_ctl_info(edev_ctl); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return 0; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic struct platform_driver qcom_llcc_edac_driver = { 4008c2ecf20Sopenharmony_ci .probe = qcom_llcc_edac_probe, 4018c2ecf20Sopenharmony_ci .remove = qcom_llcc_edac_remove, 4028c2ecf20Sopenharmony_ci .driver = { 4038c2ecf20Sopenharmony_ci .name = "qcom_llcc_edac", 4048c2ecf20Sopenharmony_ci }, 4058c2ecf20Sopenharmony_ci}; 4068c2ecf20Sopenharmony_cimodule_platform_driver(qcom_llcc_edac_driver); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("QCOM EDAC driver"); 4098c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 410