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