18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * APM X-Gene SoC EDAC (error detection and correction)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2015, Applied Micro Circuits Corporation
68c2ecf20Sopenharmony_ci * Author: Feng Kan <fkan@apm.com>
78c2ecf20Sopenharmony_ci *         Loc Ho <lho@apm.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/ctype.h>
118c2ecf20Sopenharmony_ci#include <linux/edac.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/of.h>
168c2ecf20Sopenharmony_ci#include <linux/of_address.h>
178c2ecf20Sopenharmony_ci#include <linux/regmap.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "edac_module.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define EDAC_MOD_STR			"xgene_edac"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/* Global error configuration status registers (CSR) */
248c2ecf20Sopenharmony_ci#define PCPHPERRINTSTS			0x0000
258c2ecf20Sopenharmony_ci#define PCPHPERRINTMSK			0x0004
268c2ecf20Sopenharmony_ci#define  MCU_CTL_ERR_MASK		BIT(12)
278c2ecf20Sopenharmony_ci#define  IOB_PA_ERR_MASK		BIT(11)
288c2ecf20Sopenharmony_ci#define  IOB_BA_ERR_MASK		BIT(10)
298c2ecf20Sopenharmony_ci#define  IOB_XGIC_ERR_MASK		BIT(9)
308c2ecf20Sopenharmony_ci#define  IOB_RB_ERR_MASK		BIT(8)
318c2ecf20Sopenharmony_ci#define  L3C_UNCORR_ERR_MASK		BIT(5)
328c2ecf20Sopenharmony_ci#define  MCU_UNCORR_ERR_MASK		BIT(4)
338c2ecf20Sopenharmony_ci#define  PMD3_MERR_MASK			BIT(3)
348c2ecf20Sopenharmony_ci#define  PMD2_MERR_MASK			BIT(2)
358c2ecf20Sopenharmony_ci#define  PMD1_MERR_MASK			BIT(1)
368c2ecf20Sopenharmony_ci#define  PMD0_MERR_MASK			BIT(0)
378c2ecf20Sopenharmony_ci#define PCPLPERRINTSTS			0x0008
388c2ecf20Sopenharmony_ci#define PCPLPERRINTMSK			0x000C
398c2ecf20Sopenharmony_ci#define  CSW_SWITCH_TRACE_ERR_MASK	BIT(2)
408c2ecf20Sopenharmony_ci#define  L3C_CORR_ERR_MASK		BIT(1)
418c2ecf20Sopenharmony_ci#define  MCU_CORR_ERR_MASK		BIT(0)
428c2ecf20Sopenharmony_ci#define MEMERRINTSTS			0x0010
438c2ecf20Sopenharmony_ci#define MEMERRINTMSK			0x0014
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistruct xgene_edac {
468c2ecf20Sopenharmony_ci	struct device		*dev;
478c2ecf20Sopenharmony_ci	struct regmap		*csw_map;
488c2ecf20Sopenharmony_ci	struct regmap		*mcba_map;
498c2ecf20Sopenharmony_ci	struct regmap		*mcbb_map;
508c2ecf20Sopenharmony_ci	struct regmap		*efuse_map;
518c2ecf20Sopenharmony_ci	struct regmap		*rb_map;
528c2ecf20Sopenharmony_ci	void __iomem		*pcp_csr;
538c2ecf20Sopenharmony_ci	spinlock_t		lock;
548c2ecf20Sopenharmony_ci	struct dentry           *dfs;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	struct list_head	mcus;
578c2ecf20Sopenharmony_ci	struct list_head	pmds;
588c2ecf20Sopenharmony_ci	struct list_head	l3s;
598c2ecf20Sopenharmony_ci	struct list_head	socs;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	struct mutex		mc_lock;
628c2ecf20Sopenharmony_ci	int			mc_active_mask;
638c2ecf20Sopenharmony_ci	int			mc_registered_mask;
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic void xgene_edac_pcp_rd(struct xgene_edac *edac, u32 reg, u32 *val)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	*val = readl(edac->pcp_csr + reg);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void xgene_edac_pcp_clrbits(struct xgene_edac *edac, u32 reg,
728c2ecf20Sopenharmony_ci				   u32 bits_mask)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	u32 val;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	spin_lock(&edac->lock);
778c2ecf20Sopenharmony_ci	val = readl(edac->pcp_csr + reg);
788c2ecf20Sopenharmony_ci	val &= ~bits_mask;
798c2ecf20Sopenharmony_ci	writel(val, edac->pcp_csr + reg);
808c2ecf20Sopenharmony_ci	spin_unlock(&edac->lock);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic void xgene_edac_pcp_setbits(struct xgene_edac *edac, u32 reg,
848c2ecf20Sopenharmony_ci				   u32 bits_mask)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	u32 val;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	spin_lock(&edac->lock);
898c2ecf20Sopenharmony_ci	val = readl(edac->pcp_csr + reg);
908c2ecf20Sopenharmony_ci	val |= bits_mask;
918c2ecf20Sopenharmony_ci	writel(val, edac->pcp_csr + reg);
928c2ecf20Sopenharmony_ci	spin_unlock(&edac->lock);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/* Memory controller error CSR */
968c2ecf20Sopenharmony_ci#define MCU_MAX_RANK			8
978c2ecf20Sopenharmony_ci#define MCU_RANK_STRIDE			0x40
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci#define MCUGECR				0x0110
1008c2ecf20Sopenharmony_ci#define  MCU_GECR_DEMANDUCINTREN_MASK	BIT(0)
1018c2ecf20Sopenharmony_ci#define  MCU_GECR_BACKUCINTREN_MASK	BIT(1)
1028c2ecf20Sopenharmony_ci#define  MCU_GECR_CINTREN_MASK		BIT(2)
1038c2ecf20Sopenharmony_ci#define  MUC_GECR_MCUADDRERREN_MASK	BIT(9)
1048c2ecf20Sopenharmony_ci#define MCUGESR				0x0114
1058c2ecf20Sopenharmony_ci#define  MCU_GESR_ADDRNOMATCH_ERR_MASK	BIT(7)
1068c2ecf20Sopenharmony_ci#define  MCU_GESR_ADDRMULTIMATCH_ERR_MASK	BIT(6)
1078c2ecf20Sopenharmony_ci#define  MCU_GESR_PHYP_ERR_MASK		BIT(3)
1088c2ecf20Sopenharmony_ci#define MCUESRR0			0x0314
1098c2ecf20Sopenharmony_ci#define  MCU_ESRR_MULTUCERR_MASK	BIT(3)
1108c2ecf20Sopenharmony_ci#define  MCU_ESRR_BACKUCERR_MASK	BIT(2)
1118c2ecf20Sopenharmony_ci#define  MCU_ESRR_DEMANDUCERR_MASK	BIT(1)
1128c2ecf20Sopenharmony_ci#define  MCU_ESRR_CERR_MASK		BIT(0)
1138c2ecf20Sopenharmony_ci#define MCUESRRA0			0x0318
1148c2ecf20Sopenharmony_ci#define MCUEBLRR0			0x031c
1158c2ecf20Sopenharmony_ci#define  MCU_EBLRR_ERRBANK_RD(src)	(((src) & 0x00000007) >> 0)
1168c2ecf20Sopenharmony_ci#define MCUERCRR0			0x0320
1178c2ecf20Sopenharmony_ci#define  MCU_ERCRR_ERRROW_RD(src)	(((src) & 0xFFFF0000) >> 16)
1188c2ecf20Sopenharmony_ci#define  MCU_ERCRR_ERRCOL_RD(src)	((src) & 0x00000FFF)
1198c2ecf20Sopenharmony_ci#define MCUSBECNT0			0x0324
1208c2ecf20Sopenharmony_ci#define MCU_SBECNT_COUNT(src)		((src) & 0xFFFF)
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci#define CSW_CSWCR			0x0000
1238c2ecf20Sopenharmony_ci#define  CSW_CSWCR_DUALMCB_MASK		BIT(0)
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci#define MCBADDRMR			0x0000
1268c2ecf20Sopenharmony_ci#define  MCBADDRMR_MCU_INTLV_MODE_MASK	BIT(3)
1278c2ecf20Sopenharmony_ci#define  MCBADDRMR_DUALMCU_MODE_MASK	BIT(2)
1288c2ecf20Sopenharmony_ci#define  MCBADDRMR_MCB_INTLV_MODE_MASK	BIT(1)
1298c2ecf20Sopenharmony_ci#define  MCBADDRMR_ADDRESS_MODE_MASK	BIT(0)
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistruct xgene_edac_mc_ctx {
1328c2ecf20Sopenharmony_ci	struct list_head	next;
1338c2ecf20Sopenharmony_ci	char			*name;
1348c2ecf20Sopenharmony_ci	struct mem_ctl_info	*mci;
1358c2ecf20Sopenharmony_ci	struct xgene_edac	*edac;
1368c2ecf20Sopenharmony_ci	void __iomem		*mcu_csr;
1378c2ecf20Sopenharmony_ci	u32			mcu_id;
1388c2ecf20Sopenharmony_ci};
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic ssize_t xgene_edac_mc_err_inject_write(struct file *file,
1418c2ecf20Sopenharmony_ci					      const char __user *data,
1428c2ecf20Sopenharmony_ci					      size_t count, loff_t *ppos)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci = file->private_data;
1458c2ecf20Sopenharmony_ci	struct xgene_edac_mc_ctx *ctx = mci->pvt_info;
1468c2ecf20Sopenharmony_ci	int i;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	for (i = 0; i < MCU_MAX_RANK; i++) {
1498c2ecf20Sopenharmony_ci		writel(MCU_ESRR_MULTUCERR_MASK | MCU_ESRR_BACKUCERR_MASK |
1508c2ecf20Sopenharmony_ci		       MCU_ESRR_DEMANDUCERR_MASK | MCU_ESRR_CERR_MASK,
1518c2ecf20Sopenharmony_ci		       ctx->mcu_csr + MCUESRRA0 + i * MCU_RANK_STRIDE);
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci	return count;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic const struct file_operations xgene_edac_mc_debug_inject_fops = {
1578c2ecf20Sopenharmony_ci	.open = simple_open,
1588c2ecf20Sopenharmony_ci	.write = xgene_edac_mc_err_inject_write,
1598c2ecf20Sopenharmony_ci	.llseek = generic_file_llseek,
1608c2ecf20Sopenharmony_ci};
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic void xgene_edac_mc_create_debugfs_node(struct mem_ctl_info *mci)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_EDAC_DEBUG))
1658c2ecf20Sopenharmony_ci		return;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	if (!mci->debugfs)
1688c2ecf20Sopenharmony_ci		return;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	edac_debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
1718c2ecf20Sopenharmony_ci				 &xgene_edac_mc_debug_inject_fops);
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic void xgene_edac_mc_check(struct mem_ctl_info *mci)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	struct xgene_edac_mc_ctx *ctx = mci->pvt_info;
1778c2ecf20Sopenharmony_ci	unsigned int pcp_hp_stat;
1788c2ecf20Sopenharmony_ci	unsigned int pcp_lp_stat;
1798c2ecf20Sopenharmony_ci	u32 reg;
1808c2ecf20Sopenharmony_ci	u32 rank;
1818c2ecf20Sopenharmony_ci	u32 bank;
1828c2ecf20Sopenharmony_ci	u32 count;
1838c2ecf20Sopenharmony_ci	u32 col_row;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	xgene_edac_pcp_rd(ctx->edac, PCPHPERRINTSTS, &pcp_hp_stat);
1868c2ecf20Sopenharmony_ci	xgene_edac_pcp_rd(ctx->edac, PCPLPERRINTSTS, &pcp_lp_stat);
1878c2ecf20Sopenharmony_ci	if (!((MCU_UNCORR_ERR_MASK & pcp_hp_stat) ||
1888c2ecf20Sopenharmony_ci	      (MCU_CTL_ERR_MASK & pcp_hp_stat) ||
1898c2ecf20Sopenharmony_ci	      (MCU_CORR_ERR_MASK & pcp_lp_stat)))
1908c2ecf20Sopenharmony_ci		return;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	for (rank = 0; rank < MCU_MAX_RANK; rank++) {
1938c2ecf20Sopenharmony_ci		reg = readl(ctx->mcu_csr + MCUESRR0 + rank * MCU_RANK_STRIDE);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci		/* Detect uncorrectable memory error */
1968c2ecf20Sopenharmony_ci		if (reg & (MCU_ESRR_DEMANDUCERR_MASK |
1978c2ecf20Sopenharmony_ci			   MCU_ESRR_BACKUCERR_MASK)) {
1988c2ecf20Sopenharmony_ci			/* Detected uncorrectable memory error */
1998c2ecf20Sopenharmony_ci			edac_mc_chipset_printk(mci, KERN_ERR, "X-Gene",
2008c2ecf20Sopenharmony_ci				"MCU uncorrectable error at rank %d\n", rank);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci			edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
2038c2ecf20Sopenharmony_ci				1, 0, 0, 0, 0, 0, -1, mci->ctl_name, "");
2048c2ecf20Sopenharmony_ci		}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci		/* Detect correctable memory error */
2078c2ecf20Sopenharmony_ci		if (reg & MCU_ESRR_CERR_MASK) {
2088c2ecf20Sopenharmony_ci			bank = readl(ctx->mcu_csr + MCUEBLRR0 +
2098c2ecf20Sopenharmony_ci				     rank * MCU_RANK_STRIDE);
2108c2ecf20Sopenharmony_ci			col_row = readl(ctx->mcu_csr + MCUERCRR0 +
2118c2ecf20Sopenharmony_ci					rank * MCU_RANK_STRIDE);
2128c2ecf20Sopenharmony_ci			count = readl(ctx->mcu_csr + MCUSBECNT0 +
2138c2ecf20Sopenharmony_ci				      rank * MCU_RANK_STRIDE);
2148c2ecf20Sopenharmony_ci			edac_mc_chipset_printk(mci, KERN_WARNING, "X-Gene",
2158c2ecf20Sopenharmony_ci				"MCU correctable error at rank %d bank %d column %d row %d count %d\n",
2168c2ecf20Sopenharmony_ci				rank, MCU_EBLRR_ERRBANK_RD(bank),
2178c2ecf20Sopenharmony_ci				MCU_ERCRR_ERRCOL_RD(col_row),
2188c2ecf20Sopenharmony_ci				MCU_ERCRR_ERRROW_RD(col_row),
2198c2ecf20Sopenharmony_ci				MCU_SBECNT_COUNT(count));
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci			edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
2228c2ecf20Sopenharmony_ci				1, 0, 0, 0, 0, 0, -1, mci->ctl_name, "");
2238c2ecf20Sopenharmony_ci		}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		/* Clear all error registers */
2268c2ecf20Sopenharmony_ci		writel(0x0, ctx->mcu_csr + MCUEBLRR0 + rank * MCU_RANK_STRIDE);
2278c2ecf20Sopenharmony_ci		writel(0x0, ctx->mcu_csr + MCUERCRR0 + rank * MCU_RANK_STRIDE);
2288c2ecf20Sopenharmony_ci		writel(0x0, ctx->mcu_csr + MCUSBECNT0 +
2298c2ecf20Sopenharmony_ci		       rank * MCU_RANK_STRIDE);
2308c2ecf20Sopenharmony_ci		writel(reg, ctx->mcu_csr + MCUESRR0 + rank * MCU_RANK_STRIDE);
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* Detect memory controller error */
2348c2ecf20Sopenharmony_ci	reg = readl(ctx->mcu_csr + MCUGESR);
2358c2ecf20Sopenharmony_ci	if (reg) {
2368c2ecf20Sopenharmony_ci		if (reg & MCU_GESR_ADDRNOMATCH_ERR_MASK)
2378c2ecf20Sopenharmony_ci			edac_mc_chipset_printk(mci, KERN_WARNING, "X-Gene",
2388c2ecf20Sopenharmony_ci				"MCU address miss-match error\n");
2398c2ecf20Sopenharmony_ci		if (reg & MCU_GESR_ADDRMULTIMATCH_ERR_MASK)
2408c2ecf20Sopenharmony_ci			edac_mc_chipset_printk(mci, KERN_WARNING, "X-Gene",
2418c2ecf20Sopenharmony_ci				"MCU address multi-match error\n");
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		writel(reg, ctx->mcu_csr + MCUGESR);
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic void xgene_edac_mc_irq_ctl(struct mem_ctl_info *mci, bool enable)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	struct xgene_edac_mc_ctx *ctx = mci->pvt_info;
2508c2ecf20Sopenharmony_ci	unsigned int val;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (edac_op_state != EDAC_OPSTATE_INT)
2538c2ecf20Sopenharmony_ci		return;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	mutex_lock(&ctx->edac->mc_lock);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	/*
2588c2ecf20Sopenharmony_ci	 * As there is only single bit for enable error and interrupt mask,
2598c2ecf20Sopenharmony_ci	 * we must only enable top level interrupt after all MCUs are
2608c2ecf20Sopenharmony_ci	 * registered. Otherwise, if there is an error and the corresponding
2618c2ecf20Sopenharmony_ci	 * MCU has not registered, the interrupt will never get cleared. To
2628c2ecf20Sopenharmony_ci	 * determine all MCU have registered, we will keep track of active
2638c2ecf20Sopenharmony_ci	 * MCUs and registered MCUs.
2648c2ecf20Sopenharmony_ci	 */
2658c2ecf20Sopenharmony_ci	if (enable) {
2668c2ecf20Sopenharmony_ci		/* Set registered MCU bit */
2678c2ecf20Sopenharmony_ci		ctx->edac->mc_registered_mask |= 1 << ctx->mcu_id;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci		/* Enable interrupt after all active MCU registered */
2708c2ecf20Sopenharmony_ci		if (ctx->edac->mc_registered_mask ==
2718c2ecf20Sopenharmony_ci		    ctx->edac->mc_active_mask) {
2728c2ecf20Sopenharmony_ci			/* Enable memory controller top level interrupt */
2738c2ecf20Sopenharmony_ci			xgene_edac_pcp_clrbits(ctx->edac, PCPHPERRINTMSK,
2748c2ecf20Sopenharmony_ci					       MCU_UNCORR_ERR_MASK |
2758c2ecf20Sopenharmony_ci					       MCU_CTL_ERR_MASK);
2768c2ecf20Sopenharmony_ci			xgene_edac_pcp_clrbits(ctx->edac, PCPLPERRINTMSK,
2778c2ecf20Sopenharmony_ci					       MCU_CORR_ERR_MASK);
2788c2ecf20Sopenharmony_ci		}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		/* Enable MCU interrupt and error reporting */
2818c2ecf20Sopenharmony_ci		val = readl(ctx->mcu_csr + MCUGECR);
2828c2ecf20Sopenharmony_ci		val |= MCU_GECR_DEMANDUCINTREN_MASK |
2838c2ecf20Sopenharmony_ci		       MCU_GECR_BACKUCINTREN_MASK |
2848c2ecf20Sopenharmony_ci		       MCU_GECR_CINTREN_MASK |
2858c2ecf20Sopenharmony_ci		       MUC_GECR_MCUADDRERREN_MASK;
2868c2ecf20Sopenharmony_ci		writel(val, ctx->mcu_csr + MCUGECR);
2878c2ecf20Sopenharmony_ci	} else {
2888c2ecf20Sopenharmony_ci		/* Disable MCU interrupt */
2898c2ecf20Sopenharmony_ci		val = readl(ctx->mcu_csr + MCUGECR);
2908c2ecf20Sopenharmony_ci		val &= ~(MCU_GECR_DEMANDUCINTREN_MASK |
2918c2ecf20Sopenharmony_ci			 MCU_GECR_BACKUCINTREN_MASK |
2928c2ecf20Sopenharmony_ci			 MCU_GECR_CINTREN_MASK |
2938c2ecf20Sopenharmony_ci			 MUC_GECR_MCUADDRERREN_MASK);
2948c2ecf20Sopenharmony_ci		writel(val, ctx->mcu_csr + MCUGECR);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci		/* Disable memory controller top level interrupt */
2978c2ecf20Sopenharmony_ci		xgene_edac_pcp_setbits(ctx->edac, PCPHPERRINTMSK,
2988c2ecf20Sopenharmony_ci				       MCU_UNCORR_ERR_MASK | MCU_CTL_ERR_MASK);
2998c2ecf20Sopenharmony_ci		xgene_edac_pcp_setbits(ctx->edac, PCPLPERRINTMSK,
3008c2ecf20Sopenharmony_ci				       MCU_CORR_ERR_MASK);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci		/* Clear registered MCU bit */
3038c2ecf20Sopenharmony_ci		ctx->edac->mc_registered_mask &= ~(1 << ctx->mcu_id);
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->edac->mc_lock);
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic int xgene_edac_mc_is_active(struct xgene_edac_mc_ctx *ctx, int mc_idx)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	unsigned int reg;
3128c2ecf20Sopenharmony_ci	u32 mcu_mask;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (regmap_read(ctx->edac->csw_map, CSW_CSWCR, &reg))
3158c2ecf20Sopenharmony_ci		return 0;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	if (reg & CSW_CSWCR_DUALMCB_MASK) {
3188c2ecf20Sopenharmony_ci		/*
3198c2ecf20Sopenharmony_ci		 * Dual MCB active - Determine if all 4 active or just MCU0
3208c2ecf20Sopenharmony_ci		 * and MCU2 active
3218c2ecf20Sopenharmony_ci		 */
3228c2ecf20Sopenharmony_ci		if (regmap_read(ctx->edac->mcbb_map, MCBADDRMR, &reg))
3238c2ecf20Sopenharmony_ci			return 0;
3248c2ecf20Sopenharmony_ci		mcu_mask = (reg & MCBADDRMR_DUALMCU_MODE_MASK) ? 0xF : 0x5;
3258c2ecf20Sopenharmony_ci	} else {
3268c2ecf20Sopenharmony_ci		/*
3278c2ecf20Sopenharmony_ci		 * Single MCB active - Determine if MCU0/MCU1 or just MCU0
3288c2ecf20Sopenharmony_ci		 * active
3298c2ecf20Sopenharmony_ci		 */
3308c2ecf20Sopenharmony_ci		if (regmap_read(ctx->edac->mcba_map, MCBADDRMR, &reg))
3318c2ecf20Sopenharmony_ci			return 0;
3328c2ecf20Sopenharmony_ci		mcu_mask = (reg & MCBADDRMR_DUALMCU_MODE_MASK) ? 0x3 : 0x1;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/* Save active MC mask if hasn't set already */
3368c2ecf20Sopenharmony_ci	if (!ctx->edac->mc_active_mask)
3378c2ecf20Sopenharmony_ci		ctx->edac->mc_active_mask = mcu_mask;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	return (mcu_mask & (1 << mc_idx)) ? 1 : 0;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic int xgene_edac_mc_add(struct xgene_edac *edac, struct device_node *np)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci;
3458c2ecf20Sopenharmony_ci	struct edac_mc_layer layers[2];
3468c2ecf20Sopenharmony_ci	struct xgene_edac_mc_ctx tmp_ctx;
3478c2ecf20Sopenharmony_ci	struct xgene_edac_mc_ctx *ctx;
3488c2ecf20Sopenharmony_ci	struct resource res;
3498c2ecf20Sopenharmony_ci	int rc;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	memset(&tmp_ctx, 0, sizeof(tmp_ctx));
3528c2ecf20Sopenharmony_ci	tmp_ctx.edac = edac;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	if (!devres_open_group(edac->dev, xgene_edac_mc_add, GFP_KERNEL))
3558c2ecf20Sopenharmony_ci		return -ENOMEM;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	rc = of_address_to_resource(np, 0, &res);
3588c2ecf20Sopenharmony_ci	if (rc < 0) {
3598c2ecf20Sopenharmony_ci		dev_err(edac->dev, "no MCU resource address\n");
3608c2ecf20Sopenharmony_ci		goto err_group;
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci	tmp_ctx.mcu_csr = devm_ioremap_resource(edac->dev, &res);
3638c2ecf20Sopenharmony_ci	if (IS_ERR(tmp_ctx.mcu_csr)) {
3648c2ecf20Sopenharmony_ci		dev_err(edac->dev, "unable to map MCU resource\n");
3658c2ecf20Sopenharmony_ci		rc = PTR_ERR(tmp_ctx.mcu_csr);
3668c2ecf20Sopenharmony_ci		goto err_group;
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	/* Ignore non-active MCU */
3708c2ecf20Sopenharmony_ci	if (of_property_read_u32(np, "memory-controller", &tmp_ctx.mcu_id)) {
3718c2ecf20Sopenharmony_ci		dev_err(edac->dev, "no memory-controller property\n");
3728c2ecf20Sopenharmony_ci		rc = -ENODEV;
3738c2ecf20Sopenharmony_ci		goto err_group;
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci	if (!xgene_edac_mc_is_active(&tmp_ctx, tmp_ctx.mcu_id)) {
3768c2ecf20Sopenharmony_ci		rc = -ENODEV;
3778c2ecf20Sopenharmony_ci		goto err_group;
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
3818c2ecf20Sopenharmony_ci	layers[0].size = 4;
3828c2ecf20Sopenharmony_ci	layers[0].is_virt_csrow = true;
3838c2ecf20Sopenharmony_ci	layers[1].type = EDAC_MC_LAYER_CHANNEL;
3848c2ecf20Sopenharmony_ci	layers[1].size = 2;
3858c2ecf20Sopenharmony_ci	layers[1].is_virt_csrow = false;
3868c2ecf20Sopenharmony_ci	mci = edac_mc_alloc(tmp_ctx.mcu_id, ARRAY_SIZE(layers), layers,
3878c2ecf20Sopenharmony_ci			    sizeof(*ctx));
3888c2ecf20Sopenharmony_ci	if (!mci) {
3898c2ecf20Sopenharmony_ci		rc = -ENOMEM;
3908c2ecf20Sopenharmony_ci		goto err_group;
3918c2ecf20Sopenharmony_ci	}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	ctx = mci->pvt_info;
3948c2ecf20Sopenharmony_ci	*ctx = tmp_ctx;		/* Copy over resource value */
3958c2ecf20Sopenharmony_ci	ctx->name = "xgene_edac_mc_err";
3968c2ecf20Sopenharmony_ci	ctx->mci = mci;
3978c2ecf20Sopenharmony_ci	mci->pdev = &mci->dev;
3988c2ecf20Sopenharmony_ci	mci->ctl_name = ctx->name;
3998c2ecf20Sopenharmony_ci	mci->dev_name = ctx->name;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_RDDR2 | MEM_FLAG_RDDR3 |
4028c2ecf20Sopenharmony_ci			 MEM_FLAG_DDR | MEM_FLAG_DDR2 | MEM_FLAG_DDR3;
4038c2ecf20Sopenharmony_ci	mci->edac_ctl_cap = EDAC_FLAG_SECDED;
4048c2ecf20Sopenharmony_ci	mci->edac_cap = EDAC_FLAG_SECDED;
4058c2ecf20Sopenharmony_ci	mci->mod_name = EDAC_MOD_STR;
4068c2ecf20Sopenharmony_ci	mci->ctl_page_to_phys = NULL;
4078c2ecf20Sopenharmony_ci	mci->scrub_cap = SCRUB_FLAG_HW_SRC;
4088c2ecf20Sopenharmony_ci	mci->scrub_mode = SCRUB_HW_SRC;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	if (edac_op_state == EDAC_OPSTATE_POLL)
4118c2ecf20Sopenharmony_ci		mci->edac_check = xgene_edac_mc_check;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (edac_mc_add_mc(mci)) {
4148c2ecf20Sopenharmony_ci		dev_err(edac->dev, "edac_mc_add_mc failed\n");
4158c2ecf20Sopenharmony_ci		rc = -EINVAL;
4168c2ecf20Sopenharmony_ci		goto err_free;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	xgene_edac_mc_create_debugfs_node(mci);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	list_add(&ctx->next, &edac->mcus);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	xgene_edac_mc_irq_ctl(mci, true);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	devres_remove_group(edac->dev, xgene_edac_mc_add);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	dev_info(edac->dev, "X-Gene EDAC MC registered\n");
4288c2ecf20Sopenharmony_ci	return 0;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cierr_free:
4318c2ecf20Sopenharmony_ci	edac_mc_free(mci);
4328c2ecf20Sopenharmony_cierr_group:
4338c2ecf20Sopenharmony_ci	devres_release_group(edac->dev, xgene_edac_mc_add);
4348c2ecf20Sopenharmony_ci	return rc;
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cistatic int xgene_edac_mc_remove(struct xgene_edac_mc_ctx *mcu)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	xgene_edac_mc_irq_ctl(mcu->mci, false);
4408c2ecf20Sopenharmony_ci	edac_mc_del_mc(&mcu->mci->dev);
4418c2ecf20Sopenharmony_ci	edac_mc_free(mcu->mci);
4428c2ecf20Sopenharmony_ci	return 0;
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci/* CPU L1/L2 error CSR */
4468c2ecf20Sopenharmony_ci#define MAX_CPU_PER_PMD				2
4478c2ecf20Sopenharmony_ci#define CPU_CSR_STRIDE				0x00100000
4488c2ecf20Sopenharmony_ci#define CPU_L2C_PAGE				0x000D0000
4498c2ecf20Sopenharmony_ci#define CPU_MEMERR_L2C_PAGE			0x000E0000
4508c2ecf20Sopenharmony_ci#define CPU_MEMERR_CPU_PAGE			0x000F0000
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci#define MEMERR_CPU_ICFECR_PAGE_OFFSET		0x0000
4538c2ecf20Sopenharmony_ci#define MEMERR_CPU_ICFESR_PAGE_OFFSET		0x0004
4548c2ecf20Sopenharmony_ci#define  MEMERR_CPU_ICFESR_ERRWAY_RD(src)	(((src) & 0xFF000000) >> 24)
4558c2ecf20Sopenharmony_ci#define  MEMERR_CPU_ICFESR_ERRINDEX_RD(src)	(((src) & 0x003F0000) >> 16)
4568c2ecf20Sopenharmony_ci#define  MEMERR_CPU_ICFESR_ERRINFO_RD(src)	(((src) & 0x0000FF00) >> 8)
4578c2ecf20Sopenharmony_ci#define  MEMERR_CPU_ICFESR_ERRTYPE_RD(src)	(((src) & 0x00000070) >> 4)
4588c2ecf20Sopenharmony_ci#define  MEMERR_CPU_ICFESR_MULTCERR_MASK	BIT(2)
4598c2ecf20Sopenharmony_ci#define  MEMERR_CPU_ICFESR_CERR_MASK		BIT(0)
4608c2ecf20Sopenharmony_ci#define MEMERR_CPU_LSUESR_PAGE_OFFSET		0x000c
4618c2ecf20Sopenharmony_ci#define  MEMERR_CPU_LSUESR_ERRWAY_RD(src)	(((src) & 0xFF000000) >> 24)
4628c2ecf20Sopenharmony_ci#define  MEMERR_CPU_LSUESR_ERRINDEX_RD(src)	(((src) & 0x003F0000) >> 16)
4638c2ecf20Sopenharmony_ci#define  MEMERR_CPU_LSUESR_ERRINFO_RD(src)	(((src) & 0x0000FF00) >> 8)
4648c2ecf20Sopenharmony_ci#define  MEMERR_CPU_LSUESR_ERRTYPE_RD(src)	(((src) & 0x00000070) >> 4)
4658c2ecf20Sopenharmony_ci#define  MEMERR_CPU_LSUESR_MULTCERR_MASK	BIT(2)
4668c2ecf20Sopenharmony_ci#define  MEMERR_CPU_LSUESR_CERR_MASK		BIT(0)
4678c2ecf20Sopenharmony_ci#define MEMERR_CPU_LSUECR_PAGE_OFFSET		0x0008
4688c2ecf20Sopenharmony_ci#define MEMERR_CPU_MMUECR_PAGE_OFFSET		0x0010
4698c2ecf20Sopenharmony_ci#define MEMERR_CPU_MMUESR_PAGE_OFFSET		0x0014
4708c2ecf20Sopenharmony_ci#define  MEMERR_CPU_MMUESR_ERRWAY_RD(src)	(((src) & 0xFF000000) >> 24)
4718c2ecf20Sopenharmony_ci#define  MEMERR_CPU_MMUESR_ERRINDEX_RD(src)	(((src) & 0x007F0000) >> 16)
4728c2ecf20Sopenharmony_ci#define  MEMERR_CPU_MMUESR_ERRINFO_RD(src)	(((src) & 0x0000FF00) >> 8)
4738c2ecf20Sopenharmony_ci#define  MEMERR_CPU_MMUESR_ERRREQSTR_LSU_MASK	BIT(7)
4748c2ecf20Sopenharmony_ci#define  MEMERR_CPU_MMUESR_ERRTYPE_RD(src)	(((src) & 0x00000070) >> 4)
4758c2ecf20Sopenharmony_ci#define  MEMERR_CPU_MMUESR_MULTCERR_MASK	BIT(2)
4768c2ecf20Sopenharmony_ci#define  MEMERR_CPU_MMUESR_CERR_MASK		BIT(0)
4778c2ecf20Sopenharmony_ci#define MEMERR_CPU_ICFESRA_PAGE_OFFSET		0x0804
4788c2ecf20Sopenharmony_ci#define MEMERR_CPU_LSUESRA_PAGE_OFFSET		0x080c
4798c2ecf20Sopenharmony_ci#define MEMERR_CPU_MMUESRA_PAGE_OFFSET		0x0814
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci#define MEMERR_L2C_L2ECR_PAGE_OFFSET		0x0000
4828c2ecf20Sopenharmony_ci#define MEMERR_L2C_L2ESR_PAGE_OFFSET		0x0004
4838c2ecf20Sopenharmony_ci#define  MEMERR_L2C_L2ESR_ERRSYN_RD(src)	(((src) & 0xFF000000) >> 24)
4848c2ecf20Sopenharmony_ci#define  MEMERR_L2C_L2ESR_ERRWAY_RD(src)	(((src) & 0x00FC0000) >> 18)
4858c2ecf20Sopenharmony_ci#define  MEMERR_L2C_L2ESR_ERRCPU_RD(src)	(((src) & 0x00020000) >> 17)
4868c2ecf20Sopenharmony_ci#define  MEMERR_L2C_L2ESR_ERRGROUP_RD(src)	(((src) & 0x0000E000) >> 13)
4878c2ecf20Sopenharmony_ci#define  MEMERR_L2C_L2ESR_ERRACTION_RD(src)	(((src) & 0x00001C00) >> 10)
4888c2ecf20Sopenharmony_ci#define  MEMERR_L2C_L2ESR_ERRTYPE_RD(src)	(((src) & 0x00000300) >> 8)
4898c2ecf20Sopenharmony_ci#define  MEMERR_L2C_L2ESR_MULTUCERR_MASK	BIT(3)
4908c2ecf20Sopenharmony_ci#define  MEMERR_L2C_L2ESR_MULTICERR_MASK	BIT(2)
4918c2ecf20Sopenharmony_ci#define  MEMERR_L2C_L2ESR_UCERR_MASK		BIT(1)
4928c2ecf20Sopenharmony_ci#define  MEMERR_L2C_L2ESR_ERR_MASK		BIT(0)
4938c2ecf20Sopenharmony_ci#define MEMERR_L2C_L2EALR_PAGE_OFFSET		0x0008
4948c2ecf20Sopenharmony_ci#define CPUX_L2C_L2RTOCR_PAGE_OFFSET		0x0010
4958c2ecf20Sopenharmony_ci#define MEMERR_L2C_L2EAHR_PAGE_OFFSET		0x000c
4968c2ecf20Sopenharmony_ci#define CPUX_L2C_L2RTOSR_PAGE_OFFSET		0x0014
4978c2ecf20Sopenharmony_ci#define  MEMERR_L2C_L2RTOSR_MULTERR_MASK	BIT(1)
4988c2ecf20Sopenharmony_ci#define  MEMERR_L2C_L2RTOSR_ERR_MASK		BIT(0)
4998c2ecf20Sopenharmony_ci#define CPUX_L2C_L2RTOALR_PAGE_OFFSET		0x0018
5008c2ecf20Sopenharmony_ci#define CPUX_L2C_L2RTOAHR_PAGE_OFFSET		0x001c
5018c2ecf20Sopenharmony_ci#define MEMERR_L2C_L2ESRA_PAGE_OFFSET		0x0804
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci/*
5048c2ecf20Sopenharmony_ci * Processor Module Domain (PMD) context - Context for a pair of processsors.
5058c2ecf20Sopenharmony_ci * Each PMD consists of 2 CPUs and a shared L2 cache. Each CPU consists of
5068c2ecf20Sopenharmony_ci * its own L1 cache.
5078c2ecf20Sopenharmony_ci */
5088c2ecf20Sopenharmony_cistruct xgene_edac_pmd_ctx {
5098c2ecf20Sopenharmony_ci	struct list_head	next;
5108c2ecf20Sopenharmony_ci	struct device		ddev;
5118c2ecf20Sopenharmony_ci	char			*name;
5128c2ecf20Sopenharmony_ci	struct xgene_edac	*edac;
5138c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *edac_dev;
5148c2ecf20Sopenharmony_ci	void __iomem		*pmd_csr;
5158c2ecf20Sopenharmony_ci	u32			pmd;
5168c2ecf20Sopenharmony_ci	int			version;
5178c2ecf20Sopenharmony_ci};
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic void xgene_edac_pmd_l1_check(struct edac_device_ctl_info *edac_dev,
5208c2ecf20Sopenharmony_ci				    int cpu_idx)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
5238c2ecf20Sopenharmony_ci	void __iomem *pg_f;
5248c2ecf20Sopenharmony_ci	u32 val;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	pg_f = ctx->pmd_csr + cpu_idx * CPU_CSR_STRIDE + CPU_MEMERR_CPU_PAGE;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	val = readl(pg_f + MEMERR_CPU_ICFESR_PAGE_OFFSET);
5298c2ecf20Sopenharmony_ci	if (!val)
5308c2ecf20Sopenharmony_ci		goto chk_lsu;
5318c2ecf20Sopenharmony_ci	dev_err(edac_dev->dev,
5328c2ecf20Sopenharmony_ci		"CPU%d L1 memory error ICF 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X\n",
5338c2ecf20Sopenharmony_ci		ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val,
5348c2ecf20Sopenharmony_ci		MEMERR_CPU_ICFESR_ERRWAY_RD(val),
5358c2ecf20Sopenharmony_ci		MEMERR_CPU_ICFESR_ERRINDEX_RD(val),
5368c2ecf20Sopenharmony_ci		MEMERR_CPU_ICFESR_ERRINFO_RD(val));
5378c2ecf20Sopenharmony_ci	if (val & MEMERR_CPU_ICFESR_CERR_MASK)
5388c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "One or more correctable error\n");
5398c2ecf20Sopenharmony_ci	if (val & MEMERR_CPU_ICFESR_MULTCERR_MASK)
5408c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Multiple correctable error\n");
5418c2ecf20Sopenharmony_ci	switch (MEMERR_CPU_ICFESR_ERRTYPE_RD(val)) {
5428c2ecf20Sopenharmony_ci	case 1:
5438c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "L1 TLB multiple hit\n");
5448c2ecf20Sopenharmony_ci		break;
5458c2ecf20Sopenharmony_ci	case 2:
5468c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Way select multiple hit\n");
5478c2ecf20Sopenharmony_ci		break;
5488c2ecf20Sopenharmony_ci	case 3:
5498c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Physical tag parity error\n");
5508c2ecf20Sopenharmony_ci		break;
5518c2ecf20Sopenharmony_ci	case 4:
5528c2ecf20Sopenharmony_ci	case 5:
5538c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "L1 data parity error\n");
5548c2ecf20Sopenharmony_ci		break;
5558c2ecf20Sopenharmony_ci	case 6:
5568c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "L1 pre-decode parity error\n");
5578c2ecf20Sopenharmony_ci		break;
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	/* Clear any HW errors */
5618c2ecf20Sopenharmony_ci	writel(val, pg_f + MEMERR_CPU_ICFESR_PAGE_OFFSET);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	if (val & (MEMERR_CPU_ICFESR_CERR_MASK |
5648c2ecf20Sopenharmony_ci		   MEMERR_CPU_ICFESR_MULTCERR_MASK))
5658c2ecf20Sopenharmony_ci		edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_cichk_lsu:
5688c2ecf20Sopenharmony_ci	val = readl(pg_f + MEMERR_CPU_LSUESR_PAGE_OFFSET);
5698c2ecf20Sopenharmony_ci	if (!val)
5708c2ecf20Sopenharmony_ci		goto chk_mmu;
5718c2ecf20Sopenharmony_ci	dev_err(edac_dev->dev,
5728c2ecf20Sopenharmony_ci		"CPU%d memory error LSU 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X\n",
5738c2ecf20Sopenharmony_ci		ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val,
5748c2ecf20Sopenharmony_ci		MEMERR_CPU_LSUESR_ERRWAY_RD(val),
5758c2ecf20Sopenharmony_ci		MEMERR_CPU_LSUESR_ERRINDEX_RD(val),
5768c2ecf20Sopenharmony_ci		MEMERR_CPU_LSUESR_ERRINFO_RD(val));
5778c2ecf20Sopenharmony_ci	if (val & MEMERR_CPU_LSUESR_CERR_MASK)
5788c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "One or more correctable error\n");
5798c2ecf20Sopenharmony_ci	if (val & MEMERR_CPU_LSUESR_MULTCERR_MASK)
5808c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Multiple correctable error\n");
5818c2ecf20Sopenharmony_ci	switch (MEMERR_CPU_LSUESR_ERRTYPE_RD(val)) {
5828c2ecf20Sopenharmony_ci	case 0:
5838c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Load tag error\n");
5848c2ecf20Sopenharmony_ci		break;
5858c2ecf20Sopenharmony_ci	case 1:
5868c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Load data error\n");
5878c2ecf20Sopenharmony_ci		break;
5888c2ecf20Sopenharmony_ci	case 2:
5898c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "WSL multihit error\n");
5908c2ecf20Sopenharmony_ci		break;
5918c2ecf20Sopenharmony_ci	case 3:
5928c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Store tag error\n");
5938c2ecf20Sopenharmony_ci		break;
5948c2ecf20Sopenharmony_ci	case 4:
5958c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
5968c2ecf20Sopenharmony_ci			"DTB multihit from load pipeline error\n");
5978c2ecf20Sopenharmony_ci		break;
5988c2ecf20Sopenharmony_ci	case 5:
5998c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
6008c2ecf20Sopenharmony_ci			"DTB multihit from store pipeline error\n");
6018c2ecf20Sopenharmony_ci		break;
6028c2ecf20Sopenharmony_ci	}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	/* Clear any HW errors */
6058c2ecf20Sopenharmony_ci	writel(val, pg_f + MEMERR_CPU_LSUESR_PAGE_OFFSET);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	if (val & (MEMERR_CPU_LSUESR_CERR_MASK |
6088c2ecf20Sopenharmony_ci		   MEMERR_CPU_LSUESR_MULTCERR_MASK))
6098c2ecf20Sopenharmony_ci		edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_cichk_mmu:
6128c2ecf20Sopenharmony_ci	val = readl(pg_f + MEMERR_CPU_MMUESR_PAGE_OFFSET);
6138c2ecf20Sopenharmony_ci	if (!val)
6148c2ecf20Sopenharmony_ci		return;
6158c2ecf20Sopenharmony_ci	dev_err(edac_dev->dev,
6168c2ecf20Sopenharmony_ci		"CPU%d memory error MMU 0x%08X Way 0x%02X Index 0x%02X Info 0x%02X %s\n",
6178c2ecf20Sopenharmony_ci		ctx->pmd * MAX_CPU_PER_PMD + cpu_idx, val,
6188c2ecf20Sopenharmony_ci		MEMERR_CPU_MMUESR_ERRWAY_RD(val),
6198c2ecf20Sopenharmony_ci		MEMERR_CPU_MMUESR_ERRINDEX_RD(val),
6208c2ecf20Sopenharmony_ci		MEMERR_CPU_MMUESR_ERRINFO_RD(val),
6218c2ecf20Sopenharmony_ci		val & MEMERR_CPU_MMUESR_ERRREQSTR_LSU_MASK ? "LSU" : "ICF");
6228c2ecf20Sopenharmony_ci	if (val & MEMERR_CPU_MMUESR_CERR_MASK)
6238c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "One or more correctable error\n");
6248c2ecf20Sopenharmony_ci	if (val & MEMERR_CPU_MMUESR_MULTCERR_MASK)
6258c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Multiple correctable error\n");
6268c2ecf20Sopenharmony_ci	switch (MEMERR_CPU_MMUESR_ERRTYPE_RD(val)) {
6278c2ecf20Sopenharmony_ci	case 0:
6288c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Stage 1 UTB hit error\n");
6298c2ecf20Sopenharmony_ci		break;
6308c2ecf20Sopenharmony_ci	case 1:
6318c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Stage 1 UTB miss error\n");
6328c2ecf20Sopenharmony_ci		break;
6338c2ecf20Sopenharmony_ci	case 2:
6348c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Stage 1 UTB allocate error\n");
6358c2ecf20Sopenharmony_ci		break;
6368c2ecf20Sopenharmony_ci	case 3:
6378c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "TMO operation single bank error\n");
6388c2ecf20Sopenharmony_ci		break;
6398c2ecf20Sopenharmony_ci	case 4:
6408c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Stage 2 UTB error\n");
6418c2ecf20Sopenharmony_ci		break;
6428c2ecf20Sopenharmony_ci	case 5:
6438c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Stage 2 UTB miss error\n");
6448c2ecf20Sopenharmony_ci		break;
6458c2ecf20Sopenharmony_ci	case 6:
6468c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Stage 2 UTB allocate error\n");
6478c2ecf20Sopenharmony_ci		break;
6488c2ecf20Sopenharmony_ci	case 7:
6498c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "TMO operation multiple bank error\n");
6508c2ecf20Sopenharmony_ci		break;
6518c2ecf20Sopenharmony_ci	}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	/* Clear any HW errors */
6548c2ecf20Sopenharmony_ci	writel(val, pg_f + MEMERR_CPU_MMUESR_PAGE_OFFSET);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_cistatic void xgene_edac_pmd_l2_check(struct edac_device_ctl_info *edac_dev)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
6628c2ecf20Sopenharmony_ci	void __iomem *pg_d;
6638c2ecf20Sopenharmony_ci	void __iomem *pg_e;
6648c2ecf20Sopenharmony_ci	u32 val_hi;
6658c2ecf20Sopenharmony_ci	u32 val_lo;
6668c2ecf20Sopenharmony_ci	u32 val;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	/* Check L2 */
6698c2ecf20Sopenharmony_ci	pg_e = ctx->pmd_csr + CPU_MEMERR_L2C_PAGE;
6708c2ecf20Sopenharmony_ci	val = readl(pg_e + MEMERR_L2C_L2ESR_PAGE_OFFSET);
6718c2ecf20Sopenharmony_ci	if (!val)
6728c2ecf20Sopenharmony_ci		goto chk_l2c;
6738c2ecf20Sopenharmony_ci	val_lo = readl(pg_e + MEMERR_L2C_L2EALR_PAGE_OFFSET);
6748c2ecf20Sopenharmony_ci	val_hi = readl(pg_e + MEMERR_L2C_L2EAHR_PAGE_OFFSET);
6758c2ecf20Sopenharmony_ci	dev_err(edac_dev->dev,
6768c2ecf20Sopenharmony_ci		"PMD%d memory error L2C L2ESR 0x%08X @ 0x%08X.%08X\n",
6778c2ecf20Sopenharmony_ci		ctx->pmd, val, val_hi, val_lo);
6788c2ecf20Sopenharmony_ci	dev_err(edac_dev->dev,
6798c2ecf20Sopenharmony_ci		"ErrSyndrome 0x%02X ErrWay 0x%02X ErrCpu %d ErrGroup 0x%02X ErrAction 0x%02X\n",
6808c2ecf20Sopenharmony_ci		MEMERR_L2C_L2ESR_ERRSYN_RD(val),
6818c2ecf20Sopenharmony_ci		MEMERR_L2C_L2ESR_ERRWAY_RD(val),
6828c2ecf20Sopenharmony_ci		MEMERR_L2C_L2ESR_ERRCPU_RD(val),
6838c2ecf20Sopenharmony_ci		MEMERR_L2C_L2ESR_ERRGROUP_RD(val),
6848c2ecf20Sopenharmony_ci		MEMERR_L2C_L2ESR_ERRACTION_RD(val));
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	if (val & MEMERR_L2C_L2ESR_ERR_MASK)
6878c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "One or more correctable error\n");
6888c2ecf20Sopenharmony_ci	if (val & MEMERR_L2C_L2ESR_MULTICERR_MASK)
6898c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Multiple correctable error\n");
6908c2ecf20Sopenharmony_ci	if (val & MEMERR_L2C_L2ESR_UCERR_MASK)
6918c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "One or more uncorrectable error\n");
6928c2ecf20Sopenharmony_ci	if (val & MEMERR_L2C_L2ESR_MULTUCERR_MASK)
6938c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Multiple uncorrectable error\n");
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	switch (MEMERR_L2C_L2ESR_ERRTYPE_RD(val)) {
6968c2ecf20Sopenharmony_ci	case 0:
6978c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Outbound SDB parity error\n");
6988c2ecf20Sopenharmony_ci		break;
6998c2ecf20Sopenharmony_ci	case 1:
7008c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Inbound SDB parity error\n");
7018c2ecf20Sopenharmony_ci		break;
7028c2ecf20Sopenharmony_ci	case 2:
7038c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Tag ECC error\n");
7048c2ecf20Sopenharmony_ci		break;
7058c2ecf20Sopenharmony_ci	case 3:
7068c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Data ECC error\n");
7078c2ecf20Sopenharmony_ci		break;
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	/* Clear any HW errors */
7118c2ecf20Sopenharmony_ci	writel(val, pg_e + MEMERR_L2C_L2ESR_PAGE_OFFSET);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	if (val & (MEMERR_L2C_L2ESR_ERR_MASK |
7148c2ecf20Sopenharmony_ci		   MEMERR_L2C_L2ESR_MULTICERR_MASK))
7158c2ecf20Sopenharmony_ci		edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
7168c2ecf20Sopenharmony_ci	if (val & (MEMERR_L2C_L2ESR_UCERR_MASK |
7178c2ecf20Sopenharmony_ci		   MEMERR_L2C_L2ESR_MULTUCERR_MASK))
7188c2ecf20Sopenharmony_ci		edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_cichk_l2c:
7218c2ecf20Sopenharmony_ci	/* Check if any memory request timed out on L2 cache */
7228c2ecf20Sopenharmony_ci	pg_d = ctx->pmd_csr + CPU_L2C_PAGE;
7238c2ecf20Sopenharmony_ci	val = readl(pg_d + CPUX_L2C_L2RTOSR_PAGE_OFFSET);
7248c2ecf20Sopenharmony_ci	if (val) {
7258c2ecf20Sopenharmony_ci		val_lo = readl(pg_d + CPUX_L2C_L2RTOALR_PAGE_OFFSET);
7268c2ecf20Sopenharmony_ci		val_hi = readl(pg_d + CPUX_L2C_L2RTOAHR_PAGE_OFFSET);
7278c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
7288c2ecf20Sopenharmony_ci			"PMD%d L2C error L2C RTOSR 0x%08X @ 0x%08X.%08X\n",
7298c2ecf20Sopenharmony_ci			ctx->pmd, val, val_hi, val_lo);
7308c2ecf20Sopenharmony_ci		writel(val, pg_d + CPUX_L2C_L2RTOSR_PAGE_OFFSET);
7318c2ecf20Sopenharmony_ci	}
7328c2ecf20Sopenharmony_ci}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_cistatic void xgene_edac_pmd_check(struct edac_device_ctl_info *edac_dev)
7358c2ecf20Sopenharmony_ci{
7368c2ecf20Sopenharmony_ci	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
7378c2ecf20Sopenharmony_ci	unsigned int pcp_hp_stat;
7388c2ecf20Sopenharmony_ci	int i;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	xgene_edac_pcp_rd(ctx->edac, PCPHPERRINTSTS, &pcp_hp_stat);
7418c2ecf20Sopenharmony_ci	if (!((PMD0_MERR_MASK << ctx->pmd) & pcp_hp_stat))
7428c2ecf20Sopenharmony_ci		return;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	/* Check CPU L1 error */
7458c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_CPU_PER_PMD; i++)
7468c2ecf20Sopenharmony_ci		xgene_edac_pmd_l1_check(edac_dev, i);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	/* Check CPU L2 error */
7498c2ecf20Sopenharmony_ci	xgene_edac_pmd_l2_check(edac_dev);
7508c2ecf20Sopenharmony_ci}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_cistatic void xgene_edac_pmd_cpu_hw_cfg(struct edac_device_ctl_info *edac_dev,
7538c2ecf20Sopenharmony_ci				      int cpu)
7548c2ecf20Sopenharmony_ci{
7558c2ecf20Sopenharmony_ci	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
7568c2ecf20Sopenharmony_ci	void __iomem *pg_f = ctx->pmd_csr + cpu * CPU_CSR_STRIDE +
7578c2ecf20Sopenharmony_ci			     CPU_MEMERR_CPU_PAGE;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	/*
7608c2ecf20Sopenharmony_ci	 * Enable CPU memory error:
7618c2ecf20Sopenharmony_ci	 *  MEMERR_CPU_ICFESRA, MEMERR_CPU_LSUESRA, and MEMERR_CPU_MMUESRA
7628c2ecf20Sopenharmony_ci	 */
7638c2ecf20Sopenharmony_ci	writel(0x00000301, pg_f + MEMERR_CPU_ICFECR_PAGE_OFFSET);
7648c2ecf20Sopenharmony_ci	writel(0x00000301, pg_f + MEMERR_CPU_LSUECR_PAGE_OFFSET);
7658c2ecf20Sopenharmony_ci	writel(0x00000101, pg_f + MEMERR_CPU_MMUECR_PAGE_OFFSET);
7668c2ecf20Sopenharmony_ci}
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_cistatic void xgene_edac_pmd_hw_cfg(struct edac_device_ctl_info *edac_dev)
7698c2ecf20Sopenharmony_ci{
7708c2ecf20Sopenharmony_ci	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
7718c2ecf20Sopenharmony_ci	void __iomem *pg_d = ctx->pmd_csr + CPU_L2C_PAGE;
7728c2ecf20Sopenharmony_ci	void __iomem *pg_e = ctx->pmd_csr + CPU_MEMERR_L2C_PAGE;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	/* Enable PMD memory error - MEMERR_L2C_L2ECR and L2C_L2RTOCR */
7758c2ecf20Sopenharmony_ci	writel(0x00000703, pg_e + MEMERR_L2C_L2ECR_PAGE_OFFSET);
7768c2ecf20Sopenharmony_ci	/* Configure L2C HW request time out feature if supported */
7778c2ecf20Sopenharmony_ci	if (ctx->version > 1)
7788c2ecf20Sopenharmony_ci		writel(0x00000119, pg_d + CPUX_L2C_L2RTOCR_PAGE_OFFSET);
7798c2ecf20Sopenharmony_ci}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_cistatic void xgene_edac_pmd_hw_ctl(struct edac_device_ctl_info *edac_dev,
7828c2ecf20Sopenharmony_ci				  bool enable)
7838c2ecf20Sopenharmony_ci{
7848c2ecf20Sopenharmony_ci	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
7858c2ecf20Sopenharmony_ci	int i;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	/* Enable PMD error interrupt */
7888c2ecf20Sopenharmony_ci	if (edac_dev->op_state == OP_RUNNING_INTERRUPT) {
7898c2ecf20Sopenharmony_ci		if (enable)
7908c2ecf20Sopenharmony_ci			xgene_edac_pcp_clrbits(ctx->edac, PCPHPERRINTMSK,
7918c2ecf20Sopenharmony_ci					       PMD0_MERR_MASK << ctx->pmd);
7928c2ecf20Sopenharmony_ci		else
7938c2ecf20Sopenharmony_ci			xgene_edac_pcp_setbits(ctx->edac, PCPHPERRINTMSK,
7948c2ecf20Sopenharmony_ci					       PMD0_MERR_MASK << ctx->pmd);
7958c2ecf20Sopenharmony_ci	}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	if (enable) {
7988c2ecf20Sopenharmony_ci		xgene_edac_pmd_hw_cfg(edac_dev);
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci		/* Two CPUs per a PMD */
8018c2ecf20Sopenharmony_ci		for (i = 0; i < MAX_CPU_PER_PMD; i++)
8028c2ecf20Sopenharmony_ci			xgene_edac_pmd_cpu_hw_cfg(edac_dev, i);
8038c2ecf20Sopenharmony_ci	}
8048c2ecf20Sopenharmony_ci}
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_cistatic ssize_t xgene_edac_pmd_l1_inject_ctrl_write(struct file *file,
8078c2ecf20Sopenharmony_ci						   const char __user *data,
8088c2ecf20Sopenharmony_ci						   size_t count, loff_t *ppos)
8098c2ecf20Sopenharmony_ci{
8108c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *edac_dev = file->private_data;
8118c2ecf20Sopenharmony_ci	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
8128c2ecf20Sopenharmony_ci	void __iomem *cpux_pg_f;
8138c2ecf20Sopenharmony_ci	int i;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_CPU_PER_PMD; i++) {
8168c2ecf20Sopenharmony_ci		cpux_pg_f = ctx->pmd_csr + i * CPU_CSR_STRIDE +
8178c2ecf20Sopenharmony_ci			    CPU_MEMERR_CPU_PAGE;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci		writel(MEMERR_CPU_ICFESR_MULTCERR_MASK |
8208c2ecf20Sopenharmony_ci		       MEMERR_CPU_ICFESR_CERR_MASK,
8218c2ecf20Sopenharmony_ci		       cpux_pg_f + MEMERR_CPU_ICFESRA_PAGE_OFFSET);
8228c2ecf20Sopenharmony_ci		writel(MEMERR_CPU_LSUESR_MULTCERR_MASK |
8238c2ecf20Sopenharmony_ci		       MEMERR_CPU_LSUESR_CERR_MASK,
8248c2ecf20Sopenharmony_ci		       cpux_pg_f + MEMERR_CPU_LSUESRA_PAGE_OFFSET);
8258c2ecf20Sopenharmony_ci		writel(MEMERR_CPU_MMUESR_MULTCERR_MASK |
8268c2ecf20Sopenharmony_ci		       MEMERR_CPU_MMUESR_CERR_MASK,
8278c2ecf20Sopenharmony_ci		       cpux_pg_f + MEMERR_CPU_MMUESRA_PAGE_OFFSET);
8288c2ecf20Sopenharmony_ci	}
8298c2ecf20Sopenharmony_ci	return count;
8308c2ecf20Sopenharmony_ci}
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_cistatic ssize_t xgene_edac_pmd_l2_inject_ctrl_write(struct file *file,
8338c2ecf20Sopenharmony_ci						   const char __user *data,
8348c2ecf20Sopenharmony_ci						   size_t count, loff_t *ppos)
8358c2ecf20Sopenharmony_ci{
8368c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *edac_dev = file->private_data;
8378c2ecf20Sopenharmony_ci	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
8388c2ecf20Sopenharmony_ci	void __iomem *pg_e = ctx->pmd_csr + CPU_MEMERR_L2C_PAGE;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	writel(MEMERR_L2C_L2ESR_MULTUCERR_MASK |
8418c2ecf20Sopenharmony_ci	       MEMERR_L2C_L2ESR_MULTICERR_MASK |
8428c2ecf20Sopenharmony_ci	       MEMERR_L2C_L2ESR_UCERR_MASK |
8438c2ecf20Sopenharmony_ci	       MEMERR_L2C_L2ESR_ERR_MASK,
8448c2ecf20Sopenharmony_ci	       pg_e + MEMERR_L2C_L2ESRA_PAGE_OFFSET);
8458c2ecf20Sopenharmony_ci	return count;
8468c2ecf20Sopenharmony_ci}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_cistatic const struct file_operations xgene_edac_pmd_debug_inject_fops[] = {
8498c2ecf20Sopenharmony_ci	{
8508c2ecf20Sopenharmony_ci	.open = simple_open,
8518c2ecf20Sopenharmony_ci	.write = xgene_edac_pmd_l1_inject_ctrl_write,
8528c2ecf20Sopenharmony_ci	.llseek = generic_file_llseek, },
8538c2ecf20Sopenharmony_ci	{
8548c2ecf20Sopenharmony_ci	.open = simple_open,
8558c2ecf20Sopenharmony_ci	.write = xgene_edac_pmd_l2_inject_ctrl_write,
8568c2ecf20Sopenharmony_ci	.llseek = generic_file_llseek, },
8578c2ecf20Sopenharmony_ci	{ }
8588c2ecf20Sopenharmony_ci};
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_cistatic void
8618c2ecf20Sopenharmony_cixgene_edac_pmd_create_debugfs_nodes(struct edac_device_ctl_info *edac_dev)
8628c2ecf20Sopenharmony_ci{
8638c2ecf20Sopenharmony_ci	struct xgene_edac_pmd_ctx *ctx = edac_dev->pvt_info;
8648c2ecf20Sopenharmony_ci	struct dentry *dbgfs_dir;
8658c2ecf20Sopenharmony_ci	char name[10];
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_EDAC_DEBUG) || !ctx->edac->dfs)
8688c2ecf20Sopenharmony_ci		return;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	snprintf(name, sizeof(name), "PMD%d", ctx->pmd);
8718c2ecf20Sopenharmony_ci	dbgfs_dir = edac_debugfs_create_dir_at(name, ctx->edac->dfs);
8728c2ecf20Sopenharmony_ci	if (!dbgfs_dir)
8738c2ecf20Sopenharmony_ci		return;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	edac_debugfs_create_file("l1_inject_ctrl", S_IWUSR, dbgfs_dir, edac_dev,
8768c2ecf20Sopenharmony_ci				 &xgene_edac_pmd_debug_inject_fops[0]);
8778c2ecf20Sopenharmony_ci	edac_debugfs_create_file("l2_inject_ctrl", S_IWUSR, dbgfs_dir, edac_dev,
8788c2ecf20Sopenharmony_ci				 &xgene_edac_pmd_debug_inject_fops[1]);
8798c2ecf20Sopenharmony_ci}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_cistatic int xgene_edac_pmd_available(u32 efuse, int pmd)
8828c2ecf20Sopenharmony_ci{
8838c2ecf20Sopenharmony_ci	return (efuse & (1 << pmd)) ? 0 : 1;
8848c2ecf20Sopenharmony_ci}
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_cistatic int xgene_edac_pmd_add(struct xgene_edac *edac, struct device_node *np,
8878c2ecf20Sopenharmony_ci			      int version)
8888c2ecf20Sopenharmony_ci{
8898c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *edac_dev;
8908c2ecf20Sopenharmony_ci	struct xgene_edac_pmd_ctx *ctx;
8918c2ecf20Sopenharmony_ci	struct resource res;
8928c2ecf20Sopenharmony_ci	char edac_name[10];
8938c2ecf20Sopenharmony_ci	u32 pmd;
8948c2ecf20Sopenharmony_ci	int rc;
8958c2ecf20Sopenharmony_ci	u32 val;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	if (!devres_open_group(edac->dev, xgene_edac_pmd_add, GFP_KERNEL))
8988c2ecf20Sopenharmony_ci		return -ENOMEM;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	/* Determine if this PMD is disabled */
9018c2ecf20Sopenharmony_ci	if (of_property_read_u32(np, "pmd-controller", &pmd)) {
9028c2ecf20Sopenharmony_ci		dev_err(edac->dev, "no pmd-controller property\n");
9038c2ecf20Sopenharmony_ci		rc = -ENODEV;
9048c2ecf20Sopenharmony_ci		goto err_group;
9058c2ecf20Sopenharmony_ci	}
9068c2ecf20Sopenharmony_ci	rc = regmap_read(edac->efuse_map, 0, &val);
9078c2ecf20Sopenharmony_ci	if (rc)
9088c2ecf20Sopenharmony_ci		goto err_group;
9098c2ecf20Sopenharmony_ci	if (!xgene_edac_pmd_available(val, pmd)) {
9108c2ecf20Sopenharmony_ci		rc = -ENODEV;
9118c2ecf20Sopenharmony_ci		goto err_group;
9128c2ecf20Sopenharmony_ci	}
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	snprintf(edac_name, sizeof(edac_name), "l2c%d", pmd);
9158c2ecf20Sopenharmony_ci	edac_dev = edac_device_alloc_ctl_info(sizeof(*ctx),
9168c2ecf20Sopenharmony_ci					      edac_name, 1, "l2c", 1, 2, NULL,
9178c2ecf20Sopenharmony_ci					      0, edac_device_alloc_index());
9188c2ecf20Sopenharmony_ci	if (!edac_dev) {
9198c2ecf20Sopenharmony_ci		rc = -ENOMEM;
9208c2ecf20Sopenharmony_ci		goto err_group;
9218c2ecf20Sopenharmony_ci	}
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	ctx = edac_dev->pvt_info;
9248c2ecf20Sopenharmony_ci	ctx->name = "xgene_pmd_err";
9258c2ecf20Sopenharmony_ci	ctx->pmd = pmd;
9268c2ecf20Sopenharmony_ci	ctx->edac = edac;
9278c2ecf20Sopenharmony_ci	ctx->edac_dev = edac_dev;
9288c2ecf20Sopenharmony_ci	ctx->ddev = *edac->dev;
9298c2ecf20Sopenharmony_ci	ctx->version = version;
9308c2ecf20Sopenharmony_ci	edac_dev->dev = &ctx->ddev;
9318c2ecf20Sopenharmony_ci	edac_dev->ctl_name = ctx->name;
9328c2ecf20Sopenharmony_ci	edac_dev->dev_name = ctx->name;
9338c2ecf20Sopenharmony_ci	edac_dev->mod_name = EDAC_MOD_STR;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	rc = of_address_to_resource(np, 0, &res);
9368c2ecf20Sopenharmony_ci	if (rc < 0) {
9378c2ecf20Sopenharmony_ci		dev_err(edac->dev, "no PMD resource address\n");
9388c2ecf20Sopenharmony_ci		goto err_free;
9398c2ecf20Sopenharmony_ci	}
9408c2ecf20Sopenharmony_ci	ctx->pmd_csr = devm_ioremap_resource(edac->dev, &res);
9418c2ecf20Sopenharmony_ci	if (IS_ERR(ctx->pmd_csr)) {
9428c2ecf20Sopenharmony_ci		dev_err(edac->dev,
9438c2ecf20Sopenharmony_ci			"devm_ioremap_resource failed for PMD resource address\n");
9448c2ecf20Sopenharmony_ci		rc = PTR_ERR(ctx->pmd_csr);
9458c2ecf20Sopenharmony_ci		goto err_free;
9468c2ecf20Sopenharmony_ci	}
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	if (edac_op_state == EDAC_OPSTATE_POLL)
9498c2ecf20Sopenharmony_ci		edac_dev->edac_check = xgene_edac_pmd_check;
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	xgene_edac_pmd_create_debugfs_nodes(edac_dev);
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	rc = edac_device_add_device(edac_dev);
9548c2ecf20Sopenharmony_ci	if (rc > 0) {
9558c2ecf20Sopenharmony_ci		dev_err(edac->dev, "edac_device_add_device failed\n");
9568c2ecf20Sopenharmony_ci		rc = -ENOMEM;
9578c2ecf20Sopenharmony_ci		goto err_free;
9588c2ecf20Sopenharmony_ci	}
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	if (edac_op_state == EDAC_OPSTATE_INT)
9618c2ecf20Sopenharmony_ci		edac_dev->op_state = OP_RUNNING_INTERRUPT;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	list_add(&ctx->next, &edac->pmds);
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	xgene_edac_pmd_hw_ctl(edac_dev, 1);
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	devres_remove_group(edac->dev, xgene_edac_pmd_add);
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	dev_info(edac->dev, "X-Gene EDAC PMD%d registered\n", ctx->pmd);
9708c2ecf20Sopenharmony_ci	return 0;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_cierr_free:
9738c2ecf20Sopenharmony_ci	edac_device_free_ctl_info(edac_dev);
9748c2ecf20Sopenharmony_cierr_group:
9758c2ecf20Sopenharmony_ci	devres_release_group(edac->dev, xgene_edac_pmd_add);
9768c2ecf20Sopenharmony_ci	return rc;
9778c2ecf20Sopenharmony_ci}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_cistatic int xgene_edac_pmd_remove(struct xgene_edac_pmd_ctx *pmd)
9808c2ecf20Sopenharmony_ci{
9818c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *edac_dev = pmd->edac_dev;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	xgene_edac_pmd_hw_ctl(edac_dev, 0);
9848c2ecf20Sopenharmony_ci	edac_device_del_device(edac_dev->dev);
9858c2ecf20Sopenharmony_ci	edac_device_free_ctl_info(edac_dev);
9868c2ecf20Sopenharmony_ci	return 0;
9878c2ecf20Sopenharmony_ci}
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci/* L3 Error device */
9908c2ecf20Sopenharmony_ci#define L3C_ESR				(0x0A * 4)
9918c2ecf20Sopenharmony_ci#define  L3C_ESR_DATATAG_MASK		BIT(9)
9928c2ecf20Sopenharmony_ci#define  L3C_ESR_MULTIHIT_MASK		BIT(8)
9938c2ecf20Sopenharmony_ci#define  L3C_ESR_UCEVICT_MASK		BIT(6)
9948c2ecf20Sopenharmony_ci#define  L3C_ESR_MULTIUCERR_MASK	BIT(5)
9958c2ecf20Sopenharmony_ci#define  L3C_ESR_MULTICERR_MASK		BIT(4)
9968c2ecf20Sopenharmony_ci#define  L3C_ESR_UCERR_MASK		BIT(3)
9978c2ecf20Sopenharmony_ci#define  L3C_ESR_CERR_MASK		BIT(2)
9988c2ecf20Sopenharmony_ci#define  L3C_ESR_UCERRINTR_MASK		BIT(1)
9998c2ecf20Sopenharmony_ci#define  L3C_ESR_CERRINTR_MASK		BIT(0)
10008c2ecf20Sopenharmony_ci#define L3C_ECR				(0x0B * 4)
10018c2ecf20Sopenharmony_ci#define  L3C_ECR_UCINTREN		BIT(3)
10028c2ecf20Sopenharmony_ci#define  L3C_ECR_CINTREN		BIT(2)
10038c2ecf20Sopenharmony_ci#define  L3C_UCERREN			BIT(1)
10048c2ecf20Sopenharmony_ci#define  L3C_CERREN			BIT(0)
10058c2ecf20Sopenharmony_ci#define L3C_ELR				(0x0C * 4)
10068c2ecf20Sopenharmony_ci#define  L3C_ELR_ERRSYN(src)		((src & 0xFF800000) >> 23)
10078c2ecf20Sopenharmony_ci#define  L3C_ELR_ERRWAY(src)		((src & 0x007E0000) >> 17)
10088c2ecf20Sopenharmony_ci#define  L3C_ELR_AGENTID(src)		((src & 0x0001E000) >> 13)
10098c2ecf20Sopenharmony_ci#define  L3C_ELR_ERRGRP(src)		((src & 0x00000F00) >> 8)
10108c2ecf20Sopenharmony_ci#define  L3C_ELR_OPTYPE(src)		((src & 0x000000F0) >> 4)
10118c2ecf20Sopenharmony_ci#define  L3C_ELR_PADDRHIGH(src)		(src & 0x0000000F)
10128c2ecf20Sopenharmony_ci#define L3C_AELR			(0x0D * 4)
10138c2ecf20Sopenharmony_ci#define L3C_BELR			(0x0E * 4)
10148c2ecf20Sopenharmony_ci#define  L3C_BELR_BANK(src)		(src & 0x0000000F)
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_cistruct xgene_edac_dev_ctx {
10178c2ecf20Sopenharmony_ci	struct list_head	next;
10188c2ecf20Sopenharmony_ci	struct device		ddev;
10198c2ecf20Sopenharmony_ci	char			*name;
10208c2ecf20Sopenharmony_ci	struct xgene_edac	*edac;
10218c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *edac_dev;
10228c2ecf20Sopenharmony_ci	int			edac_idx;
10238c2ecf20Sopenharmony_ci	void __iomem		*dev_csr;
10248c2ecf20Sopenharmony_ci	int			version;
10258c2ecf20Sopenharmony_ci};
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci/*
10288c2ecf20Sopenharmony_ci * Version 1 of the L3 controller has broken single bit correctable logic for
10298c2ecf20Sopenharmony_ci * certain error syndromes. Log them as uncorrectable in that case.
10308c2ecf20Sopenharmony_ci */
10318c2ecf20Sopenharmony_cistatic bool xgene_edac_l3_promote_to_uc_err(u32 l3cesr, u32 l3celr)
10328c2ecf20Sopenharmony_ci{
10338c2ecf20Sopenharmony_ci	if (l3cesr & L3C_ESR_DATATAG_MASK) {
10348c2ecf20Sopenharmony_ci		switch (L3C_ELR_ERRSYN(l3celr)) {
10358c2ecf20Sopenharmony_ci		case 0x13C:
10368c2ecf20Sopenharmony_ci		case 0x0B4:
10378c2ecf20Sopenharmony_ci		case 0x007:
10388c2ecf20Sopenharmony_ci		case 0x00D:
10398c2ecf20Sopenharmony_ci		case 0x00E:
10408c2ecf20Sopenharmony_ci		case 0x019:
10418c2ecf20Sopenharmony_ci		case 0x01A:
10428c2ecf20Sopenharmony_ci		case 0x01C:
10438c2ecf20Sopenharmony_ci		case 0x04E:
10448c2ecf20Sopenharmony_ci		case 0x041:
10458c2ecf20Sopenharmony_ci			return true;
10468c2ecf20Sopenharmony_ci		}
10478c2ecf20Sopenharmony_ci	} else if (L3C_ELR_ERRWAY(l3celr) == 9)
10488c2ecf20Sopenharmony_ci		return true;
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	return false;
10518c2ecf20Sopenharmony_ci}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_cistatic void xgene_edac_l3_check(struct edac_device_ctl_info *edac_dev)
10548c2ecf20Sopenharmony_ci{
10558c2ecf20Sopenharmony_ci	struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
10568c2ecf20Sopenharmony_ci	u32 l3cesr;
10578c2ecf20Sopenharmony_ci	u32 l3celr;
10588c2ecf20Sopenharmony_ci	u32 l3caelr;
10598c2ecf20Sopenharmony_ci	u32 l3cbelr;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	l3cesr = readl(ctx->dev_csr + L3C_ESR);
10628c2ecf20Sopenharmony_ci	if (!(l3cesr & (L3C_ESR_UCERR_MASK | L3C_ESR_CERR_MASK)))
10638c2ecf20Sopenharmony_ci		return;
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	if (l3cesr & L3C_ESR_UCERR_MASK)
10668c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "L3C uncorrectable error\n");
10678c2ecf20Sopenharmony_ci	if (l3cesr & L3C_ESR_CERR_MASK)
10688c2ecf20Sopenharmony_ci		dev_warn(edac_dev->dev, "L3C correctable error\n");
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	l3celr = readl(ctx->dev_csr + L3C_ELR);
10718c2ecf20Sopenharmony_ci	l3caelr = readl(ctx->dev_csr + L3C_AELR);
10728c2ecf20Sopenharmony_ci	l3cbelr = readl(ctx->dev_csr + L3C_BELR);
10738c2ecf20Sopenharmony_ci	if (l3cesr & L3C_ESR_MULTIHIT_MASK)
10748c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "L3C multiple hit error\n");
10758c2ecf20Sopenharmony_ci	if (l3cesr & L3C_ESR_UCEVICT_MASK)
10768c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
10778c2ecf20Sopenharmony_ci			"L3C dropped eviction of line with error\n");
10788c2ecf20Sopenharmony_ci	if (l3cesr & L3C_ESR_MULTIUCERR_MASK)
10798c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "L3C multiple uncorrectable error\n");
10808c2ecf20Sopenharmony_ci	if (l3cesr & L3C_ESR_DATATAG_MASK)
10818c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
10828c2ecf20Sopenharmony_ci			"L3C data error syndrome 0x%X group 0x%X\n",
10838c2ecf20Sopenharmony_ci			L3C_ELR_ERRSYN(l3celr), L3C_ELR_ERRGRP(l3celr));
10848c2ecf20Sopenharmony_ci	else
10858c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
10868c2ecf20Sopenharmony_ci			"L3C tag error syndrome 0x%X Way of Tag 0x%X Agent ID 0x%X Operation type 0x%X\n",
10878c2ecf20Sopenharmony_ci			L3C_ELR_ERRSYN(l3celr), L3C_ELR_ERRWAY(l3celr),
10888c2ecf20Sopenharmony_ci			L3C_ELR_AGENTID(l3celr), L3C_ELR_OPTYPE(l3celr));
10898c2ecf20Sopenharmony_ci	/*
10908c2ecf20Sopenharmony_ci	 * NOTE: Address [41:38] in L3C_ELR_PADDRHIGH(l3celr).
10918c2ecf20Sopenharmony_ci	 *       Address [37:6] in l3caelr. Lower 6 bits are zero.
10928c2ecf20Sopenharmony_ci	 */
10938c2ecf20Sopenharmony_ci	dev_err(edac_dev->dev, "L3C error address 0x%08X.%08X bank %d\n",
10948c2ecf20Sopenharmony_ci		L3C_ELR_PADDRHIGH(l3celr) << 6 | (l3caelr >> 26),
10958c2ecf20Sopenharmony_ci		(l3caelr & 0x3FFFFFFF) << 6, L3C_BELR_BANK(l3cbelr));
10968c2ecf20Sopenharmony_ci	dev_err(edac_dev->dev,
10978c2ecf20Sopenharmony_ci		"L3C error status register value 0x%X\n", l3cesr);
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	/* Clear L3C error interrupt */
11008c2ecf20Sopenharmony_ci	writel(0, ctx->dev_csr + L3C_ESR);
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	if (ctx->version <= 1 &&
11038c2ecf20Sopenharmony_ci	    xgene_edac_l3_promote_to_uc_err(l3cesr, l3celr)) {
11048c2ecf20Sopenharmony_ci		edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
11058c2ecf20Sopenharmony_ci		return;
11068c2ecf20Sopenharmony_ci	}
11078c2ecf20Sopenharmony_ci	if (l3cesr & L3C_ESR_CERR_MASK)
11088c2ecf20Sopenharmony_ci		edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
11098c2ecf20Sopenharmony_ci	if (l3cesr & L3C_ESR_UCERR_MASK)
11108c2ecf20Sopenharmony_ci		edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
11118c2ecf20Sopenharmony_ci}
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_cistatic void xgene_edac_l3_hw_init(struct edac_device_ctl_info *edac_dev,
11148c2ecf20Sopenharmony_ci				  bool enable)
11158c2ecf20Sopenharmony_ci{
11168c2ecf20Sopenharmony_ci	struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
11178c2ecf20Sopenharmony_ci	u32 val;
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	val = readl(ctx->dev_csr + L3C_ECR);
11208c2ecf20Sopenharmony_ci	val |= L3C_UCERREN | L3C_CERREN;
11218c2ecf20Sopenharmony_ci	/* On disable, we just disable interrupt but keep error enabled */
11228c2ecf20Sopenharmony_ci	if (edac_dev->op_state == OP_RUNNING_INTERRUPT) {
11238c2ecf20Sopenharmony_ci		if (enable)
11248c2ecf20Sopenharmony_ci			val |= L3C_ECR_UCINTREN | L3C_ECR_CINTREN;
11258c2ecf20Sopenharmony_ci		else
11268c2ecf20Sopenharmony_ci			val &= ~(L3C_ECR_UCINTREN | L3C_ECR_CINTREN);
11278c2ecf20Sopenharmony_ci	}
11288c2ecf20Sopenharmony_ci	writel(val, ctx->dev_csr + L3C_ECR);
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	if (edac_dev->op_state == OP_RUNNING_INTERRUPT) {
11318c2ecf20Sopenharmony_ci		/* Enable/disable L3 error top level interrupt */
11328c2ecf20Sopenharmony_ci		if (enable) {
11338c2ecf20Sopenharmony_ci			xgene_edac_pcp_clrbits(ctx->edac, PCPHPERRINTMSK,
11348c2ecf20Sopenharmony_ci					       L3C_UNCORR_ERR_MASK);
11358c2ecf20Sopenharmony_ci			xgene_edac_pcp_clrbits(ctx->edac, PCPLPERRINTMSK,
11368c2ecf20Sopenharmony_ci					       L3C_CORR_ERR_MASK);
11378c2ecf20Sopenharmony_ci		} else {
11388c2ecf20Sopenharmony_ci			xgene_edac_pcp_setbits(ctx->edac, PCPHPERRINTMSK,
11398c2ecf20Sopenharmony_ci					       L3C_UNCORR_ERR_MASK);
11408c2ecf20Sopenharmony_ci			xgene_edac_pcp_setbits(ctx->edac, PCPLPERRINTMSK,
11418c2ecf20Sopenharmony_ci					       L3C_CORR_ERR_MASK);
11428c2ecf20Sopenharmony_ci		}
11438c2ecf20Sopenharmony_ci	}
11448c2ecf20Sopenharmony_ci}
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_cistatic ssize_t xgene_edac_l3_inject_ctrl_write(struct file *file,
11478c2ecf20Sopenharmony_ci					       const char __user *data,
11488c2ecf20Sopenharmony_ci					       size_t count, loff_t *ppos)
11498c2ecf20Sopenharmony_ci{
11508c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *edac_dev = file->private_data;
11518c2ecf20Sopenharmony_ci	struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	/* Generate all errors */
11548c2ecf20Sopenharmony_ci	writel(0xFFFFFFFF, ctx->dev_csr + L3C_ESR);
11558c2ecf20Sopenharmony_ci	return count;
11568c2ecf20Sopenharmony_ci}
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_cistatic const struct file_operations xgene_edac_l3_debug_inject_fops = {
11598c2ecf20Sopenharmony_ci	.open = simple_open,
11608c2ecf20Sopenharmony_ci	.write = xgene_edac_l3_inject_ctrl_write,
11618c2ecf20Sopenharmony_ci	.llseek = generic_file_llseek
11628c2ecf20Sopenharmony_ci};
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_cistatic void
11658c2ecf20Sopenharmony_cixgene_edac_l3_create_debugfs_nodes(struct edac_device_ctl_info *edac_dev)
11668c2ecf20Sopenharmony_ci{
11678c2ecf20Sopenharmony_ci	struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
11688c2ecf20Sopenharmony_ci	struct dentry *dbgfs_dir;
11698c2ecf20Sopenharmony_ci	char name[10];
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_EDAC_DEBUG) || !ctx->edac->dfs)
11728c2ecf20Sopenharmony_ci		return;
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	snprintf(name, sizeof(name), "l3c%d", ctx->edac_idx);
11758c2ecf20Sopenharmony_ci	dbgfs_dir = edac_debugfs_create_dir_at(name, ctx->edac->dfs);
11768c2ecf20Sopenharmony_ci	if (!dbgfs_dir)
11778c2ecf20Sopenharmony_ci		return;
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	debugfs_create_file("l3_inject_ctrl", S_IWUSR, dbgfs_dir, edac_dev,
11808c2ecf20Sopenharmony_ci			    &xgene_edac_l3_debug_inject_fops);
11818c2ecf20Sopenharmony_ci}
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_cistatic int xgene_edac_l3_add(struct xgene_edac *edac, struct device_node *np,
11848c2ecf20Sopenharmony_ci			     int version)
11858c2ecf20Sopenharmony_ci{
11868c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *edac_dev;
11878c2ecf20Sopenharmony_ci	struct xgene_edac_dev_ctx *ctx;
11888c2ecf20Sopenharmony_ci	struct resource res;
11898c2ecf20Sopenharmony_ci	void __iomem *dev_csr;
11908c2ecf20Sopenharmony_ci	int edac_idx;
11918c2ecf20Sopenharmony_ci	int rc = 0;
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	if (!devres_open_group(edac->dev, xgene_edac_l3_add, GFP_KERNEL))
11948c2ecf20Sopenharmony_ci		return -ENOMEM;
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	rc = of_address_to_resource(np, 0, &res);
11978c2ecf20Sopenharmony_ci	if (rc < 0) {
11988c2ecf20Sopenharmony_ci		dev_err(edac->dev, "no L3 resource address\n");
11998c2ecf20Sopenharmony_ci		goto err_release_group;
12008c2ecf20Sopenharmony_ci	}
12018c2ecf20Sopenharmony_ci	dev_csr = devm_ioremap_resource(edac->dev, &res);
12028c2ecf20Sopenharmony_ci	if (IS_ERR(dev_csr)) {
12038c2ecf20Sopenharmony_ci		dev_err(edac->dev,
12048c2ecf20Sopenharmony_ci			"devm_ioremap_resource failed for L3 resource address\n");
12058c2ecf20Sopenharmony_ci		rc = PTR_ERR(dev_csr);
12068c2ecf20Sopenharmony_ci		goto err_release_group;
12078c2ecf20Sopenharmony_ci	}
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	edac_idx = edac_device_alloc_index();
12108c2ecf20Sopenharmony_ci	edac_dev = edac_device_alloc_ctl_info(sizeof(*ctx),
12118c2ecf20Sopenharmony_ci					      "l3c", 1, "l3c", 1, 0, NULL, 0,
12128c2ecf20Sopenharmony_ci					      edac_idx);
12138c2ecf20Sopenharmony_ci	if (!edac_dev) {
12148c2ecf20Sopenharmony_ci		rc = -ENOMEM;
12158c2ecf20Sopenharmony_ci		goto err_release_group;
12168c2ecf20Sopenharmony_ci	}
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	ctx = edac_dev->pvt_info;
12198c2ecf20Sopenharmony_ci	ctx->dev_csr = dev_csr;
12208c2ecf20Sopenharmony_ci	ctx->name = "xgene_l3_err";
12218c2ecf20Sopenharmony_ci	ctx->edac_idx = edac_idx;
12228c2ecf20Sopenharmony_ci	ctx->edac = edac;
12238c2ecf20Sopenharmony_ci	ctx->edac_dev = edac_dev;
12248c2ecf20Sopenharmony_ci	ctx->ddev = *edac->dev;
12258c2ecf20Sopenharmony_ci	ctx->version = version;
12268c2ecf20Sopenharmony_ci	edac_dev->dev = &ctx->ddev;
12278c2ecf20Sopenharmony_ci	edac_dev->ctl_name = ctx->name;
12288c2ecf20Sopenharmony_ci	edac_dev->dev_name = ctx->name;
12298c2ecf20Sopenharmony_ci	edac_dev->mod_name = EDAC_MOD_STR;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	if (edac_op_state == EDAC_OPSTATE_POLL)
12328c2ecf20Sopenharmony_ci		edac_dev->edac_check = xgene_edac_l3_check;
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	xgene_edac_l3_create_debugfs_nodes(edac_dev);
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	rc = edac_device_add_device(edac_dev);
12378c2ecf20Sopenharmony_ci	if (rc > 0) {
12388c2ecf20Sopenharmony_ci		dev_err(edac->dev, "failed edac_device_add_device()\n");
12398c2ecf20Sopenharmony_ci		rc = -ENOMEM;
12408c2ecf20Sopenharmony_ci		goto err_ctl_free;
12418c2ecf20Sopenharmony_ci	}
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	if (edac_op_state == EDAC_OPSTATE_INT)
12448c2ecf20Sopenharmony_ci		edac_dev->op_state = OP_RUNNING_INTERRUPT;
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	list_add(&ctx->next, &edac->l3s);
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	xgene_edac_l3_hw_init(edac_dev, 1);
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	devres_remove_group(edac->dev, xgene_edac_l3_add);
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	dev_info(edac->dev, "X-Gene EDAC L3 registered\n");
12538c2ecf20Sopenharmony_ci	return 0;
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_cierr_ctl_free:
12568c2ecf20Sopenharmony_ci	edac_device_free_ctl_info(edac_dev);
12578c2ecf20Sopenharmony_cierr_release_group:
12588c2ecf20Sopenharmony_ci	devres_release_group(edac->dev, xgene_edac_l3_add);
12598c2ecf20Sopenharmony_ci	return rc;
12608c2ecf20Sopenharmony_ci}
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_cistatic int xgene_edac_l3_remove(struct xgene_edac_dev_ctx *l3)
12638c2ecf20Sopenharmony_ci{
12648c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *edac_dev = l3->edac_dev;
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	xgene_edac_l3_hw_init(edac_dev, 0);
12678c2ecf20Sopenharmony_ci	edac_device_del_device(l3->edac->dev);
12688c2ecf20Sopenharmony_ci	edac_device_free_ctl_info(edac_dev);
12698c2ecf20Sopenharmony_ci	return 0;
12708c2ecf20Sopenharmony_ci}
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci/* SoC error device */
12738c2ecf20Sopenharmony_ci#define IOBAXIS0TRANSERRINTSTS		0x0000
12748c2ecf20Sopenharmony_ci#define  IOBAXIS0_M_ILLEGAL_ACCESS_MASK	BIT(1)
12758c2ecf20Sopenharmony_ci#define  IOBAXIS0_ILLEGAL_ACCESS_MASK	BIT(0)
12768c2ecf20Sopenharmony_ci#define IOBAXIS0TRANSERRINTMSK		0x0004
12778c2ecf20Sopenharmony_ci#define IOBAXIS0TRANSERRREQINFOL	0x0008
12788c2ecf20Sopenharmony_ci#define IOBAXIS0TRANSERRREQINFOH	0x000c
12798c2ecf20Sopenharmony_ci#define  REQTYPE_RD(src)		(((src) & BIT(0)))
12808c2ecf20Sopenharmony_ci#define  ERRADDRH_RD(src)		(((src) & 0xffc00000) >> 22)
12818c2ecf20Sopenharmony_ci#define IOBAXIS1TRANSERRINTSTS		0x0010
12828c2ecf20Sopenharmony_ci#define IOBAXIS1TRANSERRINTMSK		0x0014
12838c2ecf20Sopenharmony_ci#define IOBAXIS1TRANSERRREQINFOL	0x0018
12848c2ecf20Sopenharmony_ci#define IOBAXIS1TRANSERRREQINFOH	0x001c
12858c2ecf20Sopenharmony_ci#define IOBPATRANSERRINTSTS		0x0020
12868c2ecf20Sopenharmony_ci#define  IOBPA_M_REQIDRAM_CORRUPT_MASK	BIT(7)
12878c2ecf20Sopenharmony_ci#define  IOBPA_REQIDRAM_CORRUPT_MASK	BIT(6)
12888c2ecf20Sopenharmony_ci#define  IOBPA_M_TRANS_CORRUPT_MASK	BIT(5)
12898c2ecf20Sopenharmony_ci#define  IOBPA_TRANS_CORRUPT_MASK	BIT(4)
12908c2ecf20Sopenharmony_ci#define  IOBPA_M_WDATA_CORRUPT_MASK	BIT(3)
12918c2ecf20Sopenharmony_ci#define  IOBPA_WDATA_CORRUPT_MASK	BIT(2)
12928c2ecf20Sopenharmony_ci#define  IOBPA_M_RDATA_CORRUPT_MASK	BIT(1)
12938c2ecf20Sopenharmony_ci#define  IOBPA_RDATA_CORRUPT_MASK	BIT(0)
12948c2ecf20Sopenharmony_ci#define IOBBATRANSERRINTSTS		0x0030
12958c2ecf20Sopenharmony_ci#define  M_ILLEGAL_ACCESS_MASK		BIT(15)
12968c2ecf20Sopenharmony_ci#define  ILLEGAL_ACCESS_MASK		BIT(14)
12978c2ecf20Sopenharmony_ci#define  M_WIDRAM_CORRUPT_MASK		BIT(13)
12988c2ecf20Sopenharmony_ci#define  WIDRAM_CORRUPT_MASK		BIT(12)
12998c2ecf20Sopenharmony_ci#define  M_RIDRAM_CORRUPT_MASK		BIT(11)
13008c2ecf20Sopenharmony_ci#define  RIDRAM_CORRUPT_MASK		BIT(10)
13018c2ecf20Sopenharmony_ci#define  M_TRANS_CORRUPT_MASK		BIT(9)
13028c2ecf20Sopenharmony_ci#define  TRANS_CORRUPT_MASK		BIT(8)
13038c2ecf20Sopenharmony_ci#define  M_WDATA_CORRUPT_MASK		BIT(7)
13048c2ecf20Sopenharmony_ci#define  WDATA_CORRUPT_MASK		BIT(6)
13058c2ecf20Sopenharmony_ci#define  M_RBM_POISONED_REQ_MASK	BIT(5)
13068c2ecf20Sopenharmony_ci#define  RBM_POISONED_REQ_MASK		BIT(4)
13078c2ecf20Sopenharmony_ci#define  M_XGIC_POISONED_REQ_MASK	BIT(3)
13088c2ecf20Sopenharmony_ci#define  XGIC_POISONED_REQ_MASK		BIT(2)
13098c2ecf20Sopenharmony_ci#define  M_WRERR_RESP_MASK		BIT(1)
13108c2ecf20Sopenharmony_ci#define  WRERR_RESP_MASK		BIT(0)
13118c2ecf20Sopenharmony_ci#define IOBBATRANSERRREQINFOL		0x0038
13128c2ecf20Sopenharmony_ci#define IOBBATRANSERRREQINFOH		0x003c
13138c2ecf20Sopenharmony_ci#define  REQTYPE_F2_RD(src)		((src) & BIT(0))
13148c2ecf20Sopenharmony_ci#define  ERRADDRH_F2_RD(src)		(((src) & 0xffc00000) >> 22)
13158c2ecf20Sopenharmony_ci#define IOBBATRANSERRCSWREQID		0x0040
13168c2ecf20Sopenharmony_ci#define XGICTRANSERRINTSTS		0x0050
13178c2ecf20Sopenharmony_ci#define  M_WR_ACCESS_ERR_MASK		BIT(3)
13188c2ecf20Sopenharmony_ci#define  WR_ACCESS_ERR_MASK		BIT(2)
13198c2ecf20Sopenharmony_ci#define  M_RD_ACCESS_ERR_MASK		BIT(1)
13208c2ecf20Sopenharmony_ci#define  RD_ACCESS_ERR_MASK		BIT(0)
13218c2ecf20Sopenharmony_ci#define XGICTRANSERRINTMSK		0x0054
13228c2ecf20Sopenharmony_ci#define XGICTRANSERRREQINFO		0x0058
13238c2ecf20Sopenharmony_ci#define  REQTYPE_MASK			BIT(26)
13248c2ecf20Sopenharmony_ci#define  ERRADDR_RD(src)		((src) & 0x03ffffff)
13258c2ecf20Sopenharmony_ci#define GLBL_ERR_STS			0x0800
13268c2ecf20Sopenharmony_ci#define  MDED_ERR_MASK			BIT(3)
13278c2ecf20Sopenharmony_ci#define  DED_ERR_MASK			BIT(2)
13288c2ecf20Sopenharmony_ci#define  MSEC_ERR_MASK			BIT(1)
13298c2ecf20Sopenharmony_ci#define  SEC_ERR_MASK			BIT(0)
13308c2ecf20Sopenharmony_ci#define GLBL_SEC_ERRL			0x0810
13318c2ecf20Sopenharmony_ci#define GLBL_SEC_ERRH			0x0818
13328c2ecf20Sopenharmony_ci#define GLBL_MSEC_ERRL			0x0820
13338c2ecf20Sopenharmony_ci#define GLBL_MSEC_ERRH			0x0828
13348c2ecf20Sopenharmony_ci#define GLBL_DED_ERRL			0x0830
13358c2ecf20Sopenharmony_ci#define GLBL_DED_ERRLMASK		0x0834
13368c2ecf20Sopenharmony_ci#define GLBL_DED_ERRH			0x0838
13378c2ecf20Sopenharmony_ci#define GLBL_DED_ERRHMASK		0x083c
13388c2ecf20Sopenharmony_ci#define GLBL_MDED_ERRL			0x0840
13398c2ecf20Sopenharmony_ci#define GLBL_MDED_ERRLMASK		0x0844
13408c2ecf20Sopenharmony_ci#define GLBL_MDED_ERRH			0x0848
13418c2ecf20Sopenharmony_ci#define GLBL_MDED_ERRHMASK		0x084c
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci/* IO Bus Registers */
13448c2ecf20Sopenharmony_ci#define RBCSR				0x0000
13458c2ecf20Sopenharmony_ci#define STICKYERR_MASK			BIT(0)
13468c2ecf20Sopenharmony_ci#define RBEIR				0x0008
13478c2ecf20Sopenharmony_ci#define AGENT_OFFLINE_ERR_MASK		BIT(30)
13488c2ecf20Sopenharmony_ci#define UNIMPL_RBPAGE_ERR_MASK		BIT(29)
13498c2ecf20Sopenharmony_ci#define WORD_ALIGNED_ERR_MASK		BIT(28)
13508c2ecf20Sopenharmony_ci#define PAGE_ACCESS_ERR_MASK		BIT(27)
13518c2ecf20Sopenharmony_ci#define WRITE_ACCESS_MASK		BIT(26)
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_cistatic const char * const soc_mem_err_v1[] = {
13548c2ecf20Sopenharmony_ci	"10GbE0",
13558c2ecf20Sopenharmony_ci	"10GbE1",
13568c2ecf20Sopenharmony_ci	"Security",
13578c2ecf20Sopenharmony_ci	"SATA45",
13588c2ecf20Sopenharmony_ci	"SATA23/ETH23",
13598c2ecf20Sopenharmony_ci	"SATA01/ETH01",
13608c2ecf20Sopenharmony_ci	"USB1",
13618c2ecf20Sopenharmony_ci	"USB0",
13628c2ecf20Sopenharmony_ci	"QML",
13638c2ecf20Sopenharmony_ci	"QM0",
13648c2ecf20Sopenharmony_ci	"QM1 (XGbE01)",
13658c2ecf20Sopenharmony_ci	"PCIE4",
13668c2ecf20Sopenharmony_ci	"PCIE3",
13678c2ecf20Sopenharmony_ci	"PCIE2",
13688c2ecf20Sopenharmony_ci	"PCIE1",
13698c2ecf20Sopenharmony_ci	"PCIE0",
13708c2ecf20Sopenharmony_ci	"CTX Manager",
13718c2ecf20Sopenharmony_ci	"OCM",
13728c2ecf20Sopenharmony_ci	"1GbE",
13738c2ecf20Sopenharmony_ci	"CLE",
13748c2ecf20Sopenharmony_ci	"AHBC",
13758c2ecf20Sopenharmony_ci	"PktDMA",
13768c2ecf20Sopenharmony_ci	"GFC",
13778c2ecf20Sopenharmony_ci	"MSLIM",
13788c2ecf20Sopenharmony_ci	"10GbE2",
13798c2ecf20Sopenharmony_ci	"10GbE3",
13808c2ecf20Sopenharmony_ci	"QM2 (XGbE23)",
13818c2ecf20Sopenharmony_ci	"IOB",
13828c2ecf20Sopenharmony_ci	"unknown",
13838c2ecf20Sopenharmony_ci	"unknown",
13848c2ecf20Sopenharmony_ci	"unknown",
13858c2ecf20Sopenharmony_ci	"unknown",
13868c2ecf20Sopenharmony_ci};
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_cistatic void xgene_edac_iob_gic_report(struct edac_device_ctl_info *edac_dev)
13898c2ecf20Sopenharmony_ci{
13908c2ecf20Sopenharmony_ci	struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
13918c2ecf20Sopenharmony_ci	u32 err_addr_lo;
13928c2ecf20Sopenharmony_ci	u32 err_addr_hi;
13938c2ecf20Sopenharmony_ci	u32 reg;
13948c2ecf20Sopenharmony_ci	u32 info;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	/* GIC transaction error interrupt */
13978c2ecf20Sopenharmony_ci	reg = readl(ctx->dev_csr + XGICTRANSERRINTSTS);
13988c2ecf20Sopenharmony_ci	if (!reg)
13998c2ecf20Sopenharmony_ci		goto chk_iob_err;
14008c2ecf20Sopenharmony_ci	dev_err(edac_dev->dev, "XGIC transaction error\n");
14018c2ecf20Sopenharmony_ci	if (reg & RD_ACCESS_ERR_MASK)
14028c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "XGIC read size error\n");
14038c2ecf20Sopenharmony_ci	if (reg & M_RD_ACCESS_ERR_MASK)
14048c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Multiple XGIC read size error\n");
14058c2ecf20Sopenharmony_ci	if (reg & WR_ACCESS_ERR_MASK)
14068c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "XGIC write size error\n");
14078c2ecf20Sopenharmony_ci	if (reg & M_WR_ACCESS_ERR_MASK)
14088c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Multiple XGIC write size error\n");
14098c2ecf20Sopenharmony_ci	info = readl(ctx->dev_csr + XGICTRANSERRREQINFO);
14108c2ecf20Sopenharmony_ci	dev_err(edac_dev->dev, "XGIC %s access @ 0x%08X (0x%08X)\n",
14118c2ecf20Sopenharmony_ci		info & REQTYPE_MASK ? "read" : "write", ERRADDR_RD(info),
14128c2ecf20Sopenharmony_ci		info);
14138c2ecf20Sopenharmony_ci	writel(reg, ctx->dev_csr + XGICTRANSERRINTSTS);
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_cichk_iob_err:
14168c2ecf20Sopenharmony_ci	/* IOB memory error */
14178c2ecf20Sopenharmony_ci	reg = readl(ctx->dev_csr + GLBL_ERR_STS);
14188c2ecf20Sopenharmony_ci	if (!reg)
14198c2ecf20Sopenharmony_ci		return;
14208c2ecf20Sopenharmony_ci	if (reg & SEC_ERR_MASK) {
14218c2ecf20Sopenharmony_ci		err_addr_lo = readl(ctx->dev_csr + GLBL_SEC_ERRL);
14228c2ecf20Sopenharmony_ci		err_addr_hi = readl(ctx->dev_csr + GLBL_SEC_ERRH);
14238c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
14248c2ecf20Sopenharmony_ci			"IOB single-bit correctable memory at 0x%08X.%08X error\n",
14258c2ecf20Sopenharmony_ci			err_addr_lo, err_addr_hi);
14268c2ecf20Sopenharmony_ci		writel(err_addr_lo, ctx->dev_csr + GLBL_SEC_ERRL);
14278c2ecf20Sopenharmony_ci		writel(err_addr_hi, ctx->dev_csr + GLBL_SEC_ERRH);
14288c2ecf20Sopenharmony_ci	}
14298c2ecf20Sopenharmony_ci	if (reg & MSEC_ERR_MASK) {
14308c2ecf20Sopenharmony_ci		err_addr_lo = readl(ctx->dev_csr + GLBL_MSEC_ERRL);
14318c2ecf20Sopenharmony_ci		err_addr_hi = readl(ctx->dev_csr + GLBL_MSEC_ERRH);
14328c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
14338c2ecf20Sopenharmony_ci			"IOB multiple single-bit correctable memory at 0x%08X.%08X error\n",
14348c2ecf20Sopenharmony_ci			err_addr_lo, err_addr_hi);
14358c2ecf20Sopenharmony_ci		writel(err_addr_lo, ctx->dev_csr + GLBL_MSEC_ERRL);
14368c2ecf20Sopenharmony_ci		writel(err_addr_hi, ctx->dev_csr + GLBL_MSEC_ERRH);
14378c2ecf20Sopenharmony_ci	}
14388c2ecf20Sopenharmony_ci	if (reg & (SEC_ERR_MASK | MSEC_ERR_MASK))
14398c2ecf20Sopenharmony_ci		edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	if (reg & DED_ERR_MASK) {
14428c2ecf20Sopenharmony_ci		err_addr_lo = readl(ctx->dev_csr + GLBL_DED_ERRL);
14438c2ecf20Sopenharmony_ci		err_addr_hi = readl(ctx->dev_csr + GLBL_DED_ERRH);
14448c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
14458c2ecf20Sopenharmony_ci			"IOB double-bit uncorrectable memory at 0x%08X.%08X error\n",
14468c2ecf20Sopenharmony_ci			err_addr_lo, err_addr_hi);
14478c2ecf20Sopenharmony_ci		writel(err_addr_lo, ctx->dev_csr + GLBL_DED_ERRL);
14488c2ecf20Sopenharmony_ci		writel(err_addr_hi, ctx->dev_csr + GLBL_DED_ERRH);
14498c2ecf20Sopenharmony_ci	}
14508c2ecf20Sopenharmony_ci	if (reg & MDED_ERR_MASK) {
14518c2ecf20Sopenharmony_ci		err_addr_lo = readl(ctx->dev_csr + GLBL_MDED_ERRL);
14528c2ecf20Sopenharmony_ci		err_addr_hi = readl(ctx->dev_csr + GLBL_MDED_ERRH);
14538c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
14548c2ecf20Sopenharmony_ci			"Multiple IOB double-bit uncorrectable memory at 0x%08X.%08X error\n",
14558c2ecf20Sopenharmony_ci			err_addr_lo, err_addr_hi);
14568c2ecf20Sopenharmony_ci		writel(err_addr_lo, ctx->dev_csr + GLBL_MDED_ERRL);
14578c2ecf20Sopenharmony_ci		writel(err_addr_hi, ctx->dev_csr + GLBL_MDED_ERRH);
14588c2ecf20Sopenharmony_ci	}
14598c2ecf20Sopenharmony_ci	if (reg & (DED_ERR_MASK | MDED_ERR_MASK))
14608c2ecf20Sopenharmony_ci		edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
14618c2ecf20Sopenharmony_ci}
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_cistatic void xgene_edac_rb_report(struct edac_device_ctl_info *edac_dev)
14648c2ecf20Sopenharmony_ci{
14658c2ecf20Sopenharmony_ci	struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
14668c2ecf20Sopenharmony_ci	u32 err_addr_lo;
14678c2ecf20Sopenharmony_ci	u32 err_addr_hi;
14688c2ecf20Sopenharmony_ci	u32 reg;
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci	/* If the register bus resource isn't available, just skip it */
14718c2ecf20Sopenharmony_ci	if (!ctx->edac->rb_map)
14728c2ecf20Sopenharmony_ci		goto rb_skip;
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci	/*
14758c2ecf20Sopenharmony_ci	 * Check RB access errors
14768c2ecf20Sopenharmony_ci	 * 1. Out of range
14778c2ecf20Sopenharmony_ci	 * 2. Un-implemented page
14788c2ecf20Sopenharmony_ci	 * 3. Un-aligned access
14798c2ecf20Sopenharmony_ci	 * 4. Offline slave IP
14808c2ecf20Sopenharmony_ci	 */
14818c2ecf20Sopenharmony_ci	if (regmap_read(ctx->edac->rb_map, RBCSR, &reg))
14828c2ecf20Sopenharmony_ci		return;
14838c2ecf20Sopenharmony_ci	if (reg & STICKYERR_MASK) {
14848c2ecf20Sopenharmony_ci		bool write;
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "IOB bus access error(s)\n");
14878c2ecf20Sopenharmony_ci		if (regmap_read(ctx->edac->rb_map, RBEIR, &reg))
14888c2ecf20Sopenharmony_ci			return;
14898c2ecf20Sopenharmony_ci		write = reg & WRITE_ACCESS_MASK ? 1 : 0;
14908c2ecf20Sopenharmony_ci		if (reg & AGENT_OFFLINE_ERR_MASK)
14918c2ecf20Sopenharmony_ci			dev_err(edac_dev->dev,
14928c2ecf20Sopenharmony_ci				"IOB bus %s access to offline agent error\n",
14938c2ecf20Sopenharmony_ci				write ? "write" : "read");
14948c2ecf20Sopenharmony_ci		if (reg & UNIMPL_RBPAGE_ERR_MASK)
14958c2ecf20Sopenharmony_ci			dev_err(edac_dev->dev,
14968c2ecf20Sopenharmony_ci				"IOB bus %s access to unimplemented page error\n",
14978c2ecf20Sopenharmony_ci				write ? "write" : "read");
14988c2ecf20Sopenharmony_ci		if (reg & WORD_ALIGNED_ERR_MASK)
14998c2ecf20Sopenharmony_ci			dev_err(edac_dev->dev,
15008c2ecf20Sopenharmony_ci				"IOB bus %s word aligned access error\n",
15018c2ecf20Sopenharmony_ci				write ? "write" : "read");
15028c2ecf20Sopenharmony_ci		if (reg & PAGE_ACCESS_ERR_MASK)
15038c2ecf20Sopenharmony_ci			dev_err(edac_dev->dev,
15048c2ecf20Sopenharmony_ci				"IOB bus %s to page out of range access error\n",
15058c2ecf20Sopenharmony_ci				write ? "write" : "read");
15068c2ecf20Sopenharmony_ci		if (regmap_write(ctx->edac->rb_map, RBEIR, 0))
15078c2ecf20Sopenharmony_ci			return;
15088c2ecf20Sopenharmony_ci		if (regmap_write(ctx->edac->rb_map, RBCSR, 0))
15098c2ecf20Sopenharmony_ci			return;
15108c2ecf20Sopenharmony_ci	}
15118c2ecf20Sopenharmony_cirb_skip:
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci	/* IOB Bridge agent transaction error interrupt */
15148c2ecf20Sopenharmony_ci	reg = readl(ctx->dev_csr + IOBBATRANSERRINTSTS);
15158c2ecf20Sopenharmony_ci	if (!reg)
15168c2ecf20Sopenharmony_ci		return;
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci	dev_err(edac_dev->dev, "IOB bridge agent (BA) transaction error\n");
15198c2ecf20Sopenharmony_ci	if (reg & WRERR_RESP_MASK)
15208c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "IOB BA write response error\n");
15218c2ecf20Sopenharmony_ci	if (reg & M_WRERR_RESP_MASK)
15228c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
15238c2ecf20Sopenharmony_ci			"Multiple IOB BA write response error\n");
15248c2ecf20Sopenharmony_ci	if (reg & XGIC_POISONED_REQ_MASK)
15258c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "IOB BA XGIC poisoned write error\n");
15268c2ecf20Sopenharmony_ci	if (reg & M_XGIC_POISONED_REQ_MASK)
15278c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
15288c2ecf20Sopenharmony_ci			"Multiple IOB BA XGIC poisoned write error\n");
15298c2ecf20Sopenharmony_ci	if (reg & RBM_POISONED_REQ_MASK)
15308c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "IOB BA RBM poisoned write error\n");
15318c2ecf20Sopenharmony_ci	if (reg & M_RBM_POISONED_REQ_MASK)
15328c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
15338c2ecf20Sopenharmony_ci			"Multiple IOB BA RBM poisoned write error\n");
15348c2ecf20Sopenharmony_ci	if (reg & WDATA_CORRUPT_MASK)
15358c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "IOB BA write error\n");
15368c2ecf20Sopenharmony_ci	if (reg & M_WDATA_CORRUPT_MASK)
15378c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Multiple IOB BA write error\n");
15388c2ecf20Sopenharmony_ci	if (reg & TRANS_CORRUPT_MASK)
15398c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "IOB BA transaction error\n");
15408c2ecf20Sopenharmony_ci	if (reg & M_TRANS_CORRUPT_MASK)
15418c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Multiple IOB BA transaction error\n");
15428c2ecf20Sopenharmony_ci	if (reg & RIDRAM_CORRUPT_MASK)
15438c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
15448c2ecf20Sopenharmony_ci			"IOB BA RDIDRAM read transaction ID error\n");
15458c2ecf20Sopenharmony_ci	if (reg & M_RIDRAM_CORRUPT_MASK)
15468c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
15478c2ecf20Sopenharmony_ci			"Multiple IOB BA RDIDRAM read transaction ID error\n");
15488c2ecf20Sopenharmony_ci	if (reg & WIDRAM_CORRUPT_MASK)
15498c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
15508c2ecf20Sopenharmony_ci			"IOB BA RDIDRAM write transaction ID error\n");
15518c2ecf20Sopenharmony_ci	if (reg & M_WIDRAM_CORRUPT_MASK)
15528c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
15538c2ecf20Sopenharmony_ci			"Multiple IOB BA RDIDRAM write transaction ID error\n");
15548c2ecf20Sopenharmony_ci	if (reg & ILLEGAL_ACCESS_MASK)
15558c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
15568c2ecf20Sopenharmony_ci			"IOB BA XGIC/RB illegal access error\n");
15578c2ecf20Sopenharmony_ci	if (reg & M_ILLEGAL_ACCESS_MASK)
15588c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
15598c2ecf20Sopenharmony_ci			"Multiple IOB BA XGIC/RB illegal access error\n");
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci	err_addr_lo = readl(ctx->dev_csr + IOBBATRANSERRREQINFOL);
15628c2ecf20Sopenharmony_ci	err_addr_hi = readl(ctx->dev_csr + IOBBATRANSERRREQINFOH);
15638c2ecf20Sopenharmony_ci	dev_err(edac_dev->dev, "IOB BA %s access at 0x%02X.%08X (0x%08X)\n",
15648c2ecf20Sopenharmony_ci		REQTYPE_F2_RD(err_addr_hi) ? "read" : "write",
15658c2ecf20Sopenharmony_ci		ERRADDRH_F2_RD(err_addr_hi), err_addr_lo, err_addr_hi);
15668c2ecf20Sopenharmony_ci	if (reg & WRERR_RESP_MASK)
15678c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "IOB BA requestor ID 0x%08X\n",
15688c2ecf20Sopenharmony_ci			readl(ctx->dev_csr + IOBBATRANSERRCSWREQID));
15698c2ecf20Sopenharmony_ci	writel(reg, ctx->dev_csr + IOBBATRANSERRINTSTS);
15708c2ecf20Sopenharmony_ci}
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_cistatic void xgene_edac_pa_report(struct edac_device_ctl_info *edac_dev)
15738c2ecf20Sopenharmony_ci{
15748c2ecf20Sopenharmony_ci	struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
15758c2ecf20Sopenharmony_ci	u32 err_addr_lo;
15768c2ecf20Sopenharmony_ci	u32 err_addr_hi;
15778c2ecf20Sopenharmony_ci	u32 reg;
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci	/* IOB Processing agent transaction error interrupt */
15808c2ecf20Sopenharmony_ci	reg = readl(ctx->dev_csr + IOBPATRANSERRINTSTS);
15818c2ecf20Sopenharmony_ci	if (!reg)
15828c2ecf20Sopenharmony_ci		goto chk_iob_axi0;
15838c2ecf20Sopenharmony_ci	dev_err(edac_dev->dev, "IOB processing agent (PA) transaction error\n");
15848c2ecf20Sopenharmony_ci	if (reg & IOBPA_RDATA_CORRUPT_MASK)
15858c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "IOB PA read data RAM error\n");
15868c2ecf20Sopenharmony_ci	if (reg & IOBPA_M_RDATA_CORRUPT_MASK)
15878c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
15888c2ecf20Sopenharmony_ci			"Multiple IOB PA read data RAM error\n");
15898c2ecf20Sopenharmony_ci	if (reg & IOBPA_WDATA_CORRUPT_MASK)
15908c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "IOB PA write data RAM error\n");
15918c2ecf20Sopenharmony_ci	if (reg & IOBPA_M_WDATA_CORRUPT_MASK)
15928c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
15938c2ecf20Sopenharmony_ci			"Multiple IOB PA write data RAM error\n");
15948c2ecf20Sopenharmony_ci	if (reg & IOBPA_TRANS_CORRUPT_MASK)
15958c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "IOB PA transaction error\n");
15968c2ecf20Sopenharmony_ci	if (reg & IOBPA_M_TRANS_CORRUPT_MASK)
15978c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "Multiple IOB PA transaction error\n");
15988c2ecf20Sopenharmony_ci	if (reg & IOBPA_REQIDRAM_CORRUPT_MASK)
15998c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "IOB PA transaction ID RAM error\n");
16008c2ecf20Sopenharmony_ci	if (reg & IOBPA_M_REQIDRAM_CORRUPT_MASK)
16018c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev,
16028c2ecf20Sopenharmony_ci			"Multiple IOB PA transaction ID RAM error\n");
16038c2ecf20Sopenharmony_ci	writel(reg, ctx->dev_csr + IOBPATRANSERRINTSTS);
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_cichk_iob_axi0:
16068c2ecf20Sopenharmony_ci	/* IOB AXI0 Error */
16078c2ecf20Sopenharmony_ci	reg = readl(ctx->dev_csr + IOBAXIS0TRANSERRINTSTS);
16088c2ecf20Sopenharmony_ci	if (!reg)
16098c2ecf20Sopenharmony_ci		goto chk_iob_axi1;
16108c2ecf20Sopenharmony_ci	err_addr_lo = readl(ctx->dev_csr + IOBAXIS0TRANSERRREQINFOL);
16118c2ecf20Sopenharmony_ci	err_addr_hi = readl(ctx->dev_csr + IOBAXIS0TRANSERRREQINFOH);
16128c2ecf20Sopenharmony_ci	dev_err(edac_dev->dev,
16138c2ecf20Sopenharmony_ci		"%sAXI slave 0 illegal %s access @ 0x%02X.%08X (0x%08X)\n",
16148c2ecf20Sopenharmony_ci		reg & IOBAXIS0_M_ILLEGAL_ACCESS_MASK ? "Multiple " : "",
16158c2ecf20Sopenharmony_ci		REQTYPE_RD(err_addr_hi) ? "read" : "write",
16168c2ecf20Sopenharmony_ci		ERRADDRH_RD(err_addr_hi), err_addr_lo, err_addr_hi);
16178c2ecf20Sopenharmony_ci	writel(reg, ctx->dev_csr + IOBAXIS0TRANSERRINTSTS);
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_cichk_iob_axi1:
16208c2ecf20Sopenharmony_ci	/* IOB AXI1 Error */
16218c2ecf20Sopenharmony_ci	reg = readl(ctx->dev_csr + IOBAXIS1TRANSERRINTSTS);
16228c2ecf20Sopenharmony_ci	if (!reg)
16238c2ecf20Sopenharmony_ci		return;
16248c2ecf20Sopenharmony_ci	err_addr_lo = readl(ctx->dev_csr + IOBAXIS1TRANSERRREQINFOL);
16258c2ecf20Sopenharmony_ci	err_addr_hi = readl(ctx->dev_csr + IOBAXIS1TRANSERRREQINFOH);
16268c2ecf20Sopenharmony_ci	dev_err(edac_dev->dev,
16278c2ecf20Sopenharmony_ci		"%sAXI slave 1 illegal %s access @ 0x%02X.%08X (0x%08X)\n",
16288c2ecf20Sopenharmony_ci		reg & IOBAXIS0_M_ILLEGAL_ACCESS_MASK ? "Multiple " : "",
16298c2ecf20Sopenharmony_ci		REQTYPE_RD(err_addr_hi) ? "read" : "write",
16308c2ecf20Sopenharmony_ci		ERRADDRH_RD(err_addr_hi), err_addr_lo, err_addr_hi);
16318c2ecf20Sopenharmony_ci	writel(reg, ctx->dev_csr + IOBAXIS1TRANSERRINTSTS);
16328c2ecf20Sopenharmony_ci}
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_cistatic void xgene_edac_soc_check(struct edac_device_ctl_info *edac_dev)
16358c2ecf20Sopenharmony_ci{
16368c2ecf20Sopenharmony_ci	struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
16378c2ecf20Sopenharmony_ci	const char * const *soc_mem_err = NULL;
16388c2ecf20Sopenharmony_ci	u32 pcp_hp_stat;
16398c2ecf20Sopenharmony_ci	u32 pcp_lp_stat;
16408c2ecf20Sopenharmony_ci	u32 reg;
16418c2ecf20Sopenharmony_ci	int i;
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	xgene_edac_pcp_rd(ctx->edac, PCPHPERRINTSTS, &pcp_hp_stat);
16448c2ecf20Sopenharmony_ci	xgene_edac_pcp_rd(ctx->edac, PCPLPERRINTSTS, &pcp_lp_stat);
16458c2ecf20Sopenharmony_ci	xgene_edac_pcp_rd(ctx->edac, MEMERRINTSTS, &reg);
16468c2ecf20Sopenharmony_ci	if (!((pcp_hp_stat & (IOB_PA_ERR_MASK | IOB_BA_ERR_MASK |
16478c2ecf20Sopenharmony_ci			      IOB_XGIC_ERR_MASK | IOB_RB_ERR_MASK)) ||
16488c2ecf20Sopenharmony_ci	      (pcp_lp_stat & CSW_SWITCH_TRACE_ERR_MASK) || reg))
16498c2ecf20Sopenharmony_ci		return;
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ci	if (pcp_hp_stat & IOB_XGIC_ERR_MASK)
16528c2ecf20Sopenharmony_ci		xgene_edac_iob_gic_report(edac_dev);
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ci	if (pcp_hp_stat & (IOB_RB_ERR_MASK | IOB_BA_ERR_MASK))
16558c2ecf20Sopenharmony_ci		xgene_edac_rb_report(edac_dev);
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	if (pcp_hp_stat & IOB_PA_ERR_MASK)
16588c2ecf20Sopenharmony_ci		xgene_edac_pa_report(edac_dev);
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	if (pcp_lp_stat & CSW_SWITCH_TRACE_ERR_MASK) {
16618c2ecf20Sopenharmony_ci		dev_info(edac_dev->dev,
16628c2ecf20Sopenharmony_ci			 "CSW switch trace correctable memory parity error\n");
16638c2ecf20Sopenharmony_ci		edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
16648c2ecf20Sopenharmony_ci	}
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_ci	if (!reg)
16678c2ecf20Sopenharmony_ci		return;
16688c2ecf20Sopenharmony_ci	if (ctx->version == 1)
16698c2ecf20Sopenharmony_ci		soc_mem_err = soc_mem_err_v1;
16708c2ecf20Sopenharmony_ci	if (!soc_mem_err) {
16718c2ecf20Sopenharmony_ci		dev_err(edac_dev->dev, "SoC memory parity error 0x%08X\n",
16728c2ecf20Sopenharmony_ci			reg);
16738c2ecf20Sopenharmony_ci		edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
16748c2ecf20Sopenharmony_ci		return;
16758c2ecf20Sopenharmony_ci	}
16768c2ecf20Sopenharmony_ci	for (i = 0; i < 31; i++) {
16778c2ecf20Sopenharmony_ci		if (reg & (1 << i)) {
16788c2ecf20Sopenharmony_ci			dev_err(edac_dev->dev, "%s memory parity error\n",
16798c2ecf20Sopenharmony_ci				soc_mem_err[i]);
16808c2ecf20Sopenharmony_ci			edac_device_handle_ue(edac_dev, 0, 0,
16818c2ecf20Sopenharmony_ci					      edac_dev->ctl_name);
16828c2ecf20Sopenharmony_ci		}
16838c2ecf20Sopenharmony_ci	}
16848c2ecf20Sopenharmony_ci}
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_cistatic void xgene_edac_soc_hw_init(struct edac_device_ctl_info *edac_dev,
16878c2ecf20Sopenharmony_ci				   bool enable)
16888c2ecf20Sopenharmony_ci{
16898c2ecf20Sopenharmony_ci	struct xgene_edac_dev_ctx *ctx = edac_dev->pvt_info;
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_ci	/* Enable SoC IP error interrupt */
16928c2ecf20Sopenharmony_ci	if (edac_dev->op_state == OP_RUNNING_INTERRUPT) {
16938c2ecf20Sopenharmony_ci		if (enable) {
16948c2ecf20Sopenharmony_ci			xgene_edac_pcp_clrbits(ctx->edac, PCPHPERRINTMSK,
16958c2ecf20Sopenharmony_ci					       IOB_PA_ERR_MASK |
16968c2ecf20Sopenharmony_ci					       IOB_BA_ERR_MASK |
16978c2ecf20Sopenharmony_ci					       IOB_XGIC_ERR_MASK |
16988c2ecf20Sopenharmony_ci					       IOB_RB_ERR_MASK);
16998c2ecf20Sopenharmony_ci			xgene_edac_pcp_clrbits(ctx->edac, PCPLPERRINTMSK,
17008c2ecf20Sopenharmony_ci					       CSW_SWITCH_TRACE_ERR_MASK);
17018c2ecf20Sopenharmony_ci		} else {
17028c2ecf20Sopenharmony_ci			xgene_edac_pcp_setbits(ctx->edac, PCPHPERRINTMSK,
17038c2ecf20Sopenharmony_ci					       IOB_PA_ERR_MASK |
17048c2ecf20Sopenharmony_ci					       IOB_BA_ERR_MASK |
17058c2ecf20Sopenharmony_ci					       IOB_XGIC_ERR_MASK |
17068c2ecf20Sopenharmony_ci					       IOB_RB_ERR_MASK);
17078c2ecf20Sopenharmony_ci			xgene_edac_pcp_setbits(ctx->edac, PCPLPERRINTMSK,
17088c2ecf20Sopenharmony_ci					       CSW_SWITCH_TRACE_ERR_MASK);
17098c2ecf20Sopenharmony_ci		}
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_ci		writel(enable ? 0x0 : 0xFFFFFFFF,
17128c2ecf20Sopenharmony_ci		       ctx->dev_csr + IOBAXIS0TRANSERRINTMSK);
17138c2ecf20Sopenharmony_ci		writel(enable ? 0x0 : 0xFFFFFFFF,
17148c2ecf20Sopenharmony_ci		       ctx->dev_csr + IOBAXIS1TRANSERRINTMSK);
17158c2ecf20Sopenharmony_ci		writel(enable ? 0x0 : 0xFFFFFFFF,
17168c2ecf20Sopenharmony_ci		       ctx->dev_csr + XGICTRANSERRINTMSK);
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci		xgene_edac_pcp_setbits(ctx->edac, MEMERRINTMSK,
17198c2ecf20Sopenharmony_ci				       enable ? 0x0 : 0xFFFFFFFF);
17208c2ecf20Sopenharmony_ci	}
17218c2ecf20Sopenharmony_ci}
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_cistatic int xgene_edac_soc_add(struct xgene_edac *edac, struct device_node *np,
17248c2ecf20Sopenharmony_ci			      int version)
17258c2ecf20Sopenharmony_ci{
17268c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *edac_dev;
17278c2ecf20Sopenharmony_ci	struct xgene_edac_dev_ctx *ctx;
17288c2ecf20Sopenharmony_ci	void __iomem *dev_csr;
17298c2ecf20Sopenharmony_ci	struct resource res;
17308c2ecf20Sopenharmony_ci	int edac_idx;
17318c2ecf20Sopenharmony_ci	int rc;
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci	if (!devres_open_group(edac->dev, xgene_edac_soc_add, GFP_KERNEL))
17348c2ecf20Sopenharmony_ci		return -ENOMEM;
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci	rc = of_address_to_resource(np, 0, &res);
17378c2ecf20Sopenharmony_ci	if (rc < 0) {
17388c2ecf20Sopenharmony_ci		dev_err(edac->dev, "no SoC resource address\n");
17398c2ecf20Sopenharmony_ci		goto err_release_group;
17408c2ecf20Sopenharmony_ci	}
17418c2ecf20Sopenharmony_ci	dev_csr = devm_ioremap_resource(edac->dev, &res);
17428c2ecf20Sopenharmony_ci	if (IS_ERR(dev_csr)) {
17438c2ecf20Sopenharmony_ci		dev_err(edac->dev,
17448c2ecf20Sopenharmony_ci			"devm_ioremap_resource failed for soc resource address\n");
17458c2ecf20Sopenharmony_ci		rc = PTR_ERR(dev_csr);
17468c2ecf20Sopenharmony_ci		goto err_release_group;
17478c2ecf20Sopenharmony_ci	}
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci	edac_idx = edac_device_alloc_index();
17508c2ecf20Sopenharmony_ci	edac_dev = edac_device_alloc_ctl_info(sizeof(*ctx),
17518c2ecf20Sopenharmony_ci					      "SOC", 1, "SOC", 1, 2, NULL, 0,
17528c2ecf20Sopenharmony_ci					      edac_idx);
17538c2ecf20Sopenharmony_ci	if (!edac_dev) {
17548c2ecf20Sopenharmony_ci		rc = -ENOMEM;
17558c2ecf20Sopenharmony_ci		goto err_release_group;
17568c2ecf20Sopenharmony_ci	}
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci	ctx = edac_dev->pvt_info;
17598c2ecf20Sopenharmony_ci	ctx->dev_csr = dev_csr;
17608c2ecf20Sopenharmony_ci	ctx->name = "xgene_soc_err";
17618c2ecf20Sopenharmony_ci	ctx->edac_idx = edac_idx;
17628c2ecf20Sopenharmony_ci	ctx->edac = edac;
17638c2ecf20Sopenharmony_ci	ctx->edac_dev = edac_dev;
17648c2ecf20Sopenharmony_ci	ctx->ddev = *edac->dev;
17658c2ecf20Sopenharmony_ci	ctx->version = version;
17668c2ecf20Sopenharmony_ci	edac_dev->dev = &ctx->ddev;
17678c2ecf20Sopenharmony_ci	edac_dev->ctl_name = ctx->name;
17688c2ecf20Sopenharmony_ci	edac_dev->dev_name = ctx->name;
17698c2ecf20Sopenharmony_ci	edac_dev->mod_name = EDAC_MOD_STR;
17708c2ecf20Sopenharmony_ci
17718c2ecf20Sopenharmony_ci	if (edac_op_state == EDAC_OPSTATE_POLL)
17728c2ecf20Sopenharmony_ci		edac_dev->edac_check = xgene_edac_soc_check;
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci	rc = edac_device_add_device(edac_dev);
17758c2ecf20Sopenharmony_ci	if (rc > 0) {
17768c2ecf20Sopenharmony_ci		dev_err(edac->dev, "failed edac_device_add_device()\n");
17778c2ecf20Sopenharmony_ci		rc = -ENOMEM;
17788c2ecf20Sopenharmony_ci		goto err_ctl_free;
17798c2ecf20Sopenharmony_ci	}
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci	if (edac_op_state == EDAC_OPSTATE_INT)
17828c2ecf20Sopenharmony_ci		edac_dev->op_state = OP_RUNNING_INTERRUPT;
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci	list_add(&ctx->next, &edac->socs);
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci	xgene_edac_soc_hw_init(edac_dev, 1);
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci	devres_remove_group(edac->dev, xgene_edac_soc_add);
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci	dev_info(edac->dev, "X-Gene EDAC SoC registered\n");
17918c2ecf20Sopenharmony_ci
17928c2ecf20Sopenharmony_ci	return 0;
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_cierr_ctl_free:
17958c2ecf20Sopenharmony_ci	edac_device_free_ctl_info(edac_dev);
17968c2ecf20Sopenharmony_cierr_release_group:
17978c2ecf20Sopenharmony_ci	devres_release_group(edac->dev, xgene_edac_soc_add);
17988c2ecf20Sopenharmony_ci	return rc;
17998c2ecf20Sopenharmony_ci}
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_cistatic int xgene_edac_soc_remove(struct xgene_edac_dev_ctx *soc)
18028c2ecf20Sopenharmony_ci{
18038c2ecf20Sopenharmony_ci	struct edac_device_ctl_info *edac_dev = soc->edac_dev;
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_ci	xgene_edac_soc_hw_init(edac_dev, 0);
18068c2ecf20Sopenharmony_ci	edac_device_del_device(soc->edac->dev);
18078c2ecf20Sopenharmony_ci	edac_device_free_ctl_info(edac_dev);
18088c2ecf20Sopenharmony_ci	return 0;
18098c2ecf20Sopenharmony_ci}
18108c2ecf20Sopenharmony_ci
18118c2ecf20Sopenharmony_cistatic irqreturn_t xgene_edac_isr(int irq, void *dev_id)
18128c2ecf20Sopenharmony_ci{
18138c2ecf20Sopenharmony_ci	struct xgene_edac *ctx = dev_id;
18148c2ecf20Sopenharmony_ci	struct xgene_edac_pmd_ctx *pmd;
18158c2ecf20Sopenharmony_ci	struct xgene_edac_dev_ctx *node;
18168c2ecf20Sopenharmony_ci	unsigned int pcp_hp_stat;
18178c2ecf20Sopenharmony_ci	unsigned int pcp_lp_stat;
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci	xgene_edac_pcp_rd(ctx, PCPHPERRINTSTS, &pcp_hp_stat);
18208c2ecf20Sopenharmony_ci	xgene_edac_pcp_rd(ctx, PCPLPERRINTSTS, &pcp_lp_stat);
18218c2ecf20Sopenharmony_ci	if ((MCU_UNCORR_ERR_MASK & pcp_hp_stat) ||
18228c2ecf20Sopenharmony_ci	    (MCU_CTL_ERR_MASK & pcp_hp_stat) ||
18238c2ecf20Sopenharmony_ci	    (MCU_CORR_ERR_MASK & pcp_lp_stat)) {
18248c2ecf20Sopenharmony_ci		struct xgene_edac_mc_ctx *mcu;
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci		list_for_each_entry(mcu, &ctx->mcus, next)
18278c2ecf20Sopenharmony_ci			xgene_edac_mc_check(mcu->mci);
18288c2ecf20Sopenharmony_ci	}
18298c2ecf20Sopenharmony_ci
18308c2ecf20Sopenharmony_ci	list_for_each_entry(pmd, &ctx->pmds, next) {
18318c2ecf20Sopenharmony_ci		if ((PMD0_MERR_MASK << pmd->pmd) & pcp_hp_stat)
18328c2ecf20Sopenharmony_ci			xgene_edac_pmd_check(pmd->edac_dev);
18338c2ecf20Sopenharmony_ci	}
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci	list_for_each_entry(node, &ctx->l3s, next)
18368c2ecf20Sopenharmony_ci		xgene_edac_l3_check(node->edac_dev);
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci	list_for_each_entry(node, &ctx->socs, next)
18398c2ecf20Sopenharmony_ci		xgene_edac_soc_check(node->edac_dev);
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
18428c2ecf20Sopenharmony_ci}
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_cistatic int xgene_edac_probe(struct platform_device *pdev)
18458c2ecf20Sopenharmony_ci{
18468c2ecf20Sopenharmony_ci	struct xgene_edac *edac;
18478c2ecf20Sopenharmony_ci	struct device_node *child;
18488c2ecf20Sopenharmony_ci	struct resource *res;
18498c2ecf20Sopenharmony_ci	int rc;
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci	edac = devm_kzalloc(&pdev->dev, sizeof(*edac), GFP_KERNEL);
18528c2ecf20Sopenharmony_ci	if (!edac)
18538c2ecf20Sopenharmony_ci		return -ENOMEM;
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_ci	edac->dev = &pdev->dev;
18568c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, edac);
18578c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&edac->mcus);
18588c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&edac->pmds);
18598c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&edac->l3s);
18608c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&edac->socs);
18618c2ecf20Sopenharmony_ci	spin_lock_init(&edac->lock);
18628c2ecf20Sopenharmony_ci	mutex_init(&edac->mc_lock);
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci	edac->csw_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
18658c2ecf20Sopenharmony_ci							"regmap-csw");
18668c2ecf20Sopenharmony_ci	if (IS_ERR(edac->csw_map)) {
18678c2ecf20Sopenharmony_ci		dev_err(edac->dev, "unable to get syscon regmap csw\n");
18688c2ecf20Sopenharmony_ci		rc = PTR_ERR(edac->csw_map);
18698c2ecf20Sopenharmony_ci		goto out_err;
18708c2ecf20Sopenharmony_ci	}
18718c2ecf20Sopenharmony_ci
18728c2ecf20Sopenharmony_ci	edac->mcba_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
18738c2ecf20Sopenharmony_ci							 "regmap-mcba");
18748c2ecf20Sopenharmony_ci	if (IS_ERR(edac->mcba_map)) {
18758c2ecf20Sopenharmony_ci		dev_err(edac->dev, "unable to get syscon regmap mcba\n");
18768c2ecf20Sopenharmony_ci		rc = PTR_ERR(edac->mcba_map);
18778c2ecf20Sopenharmony_ci		goto out_err;
18788c2ecf20Sopenharmony_ci	}
18798c2ecf20Sopenharmony_ci
18808c2ecf20Sopenharmony_ci	edac->mcbb_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
18818c2ecf20Sopenharmony_ci							 "regmap-mcbb");
18828c2ecf20Sopenharmony_ci	if (IS_ERR(edac->mcbb_map)) {
18838c2ecf20Sopenharmony_ci		dev_err(edac->dev, "unable to get syscon regmap mcbb\n");
18848c2ecf20Sopenharmony_ci		rc = PTR_ERR(edac->mcbb_map);
18858c2ecf20Sopenharmony_ci		goto out_err;
18868c2ecf20Sopenharmony_ci	}
18878c2ecf20Sopenharmony_ci	edac->efuse_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
18888c2ecf20Sopenharmony_ci							  "regmap-efuse");
18898c2ecf20Sopenharmony_ci	if (IS_ERR(edac->efuse_map)) {
18908c2ecf20Sopenharmony_ci		dev_err(edac->dev, "unable to get syscon regmap efuse\n");
18918c2ecf20Sopenharmony_ci		rc = PTR_ERR(edac->efuse_map);
18928c2ecf20Sopenharmony_ci		goto out_err;
18938c2ecf20Sopenharmony_ci	}
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci	/*
18968c2ecf20Sopenharmony_ci	 * NOTE: The register bus resource is optional for compatibility
18978c2ecf20Sopenharmony_ci	 * reason.
18988c2ecf20Sopenharmony_ci	 */
18998c2ecf20Sopenharmony_ci	edac->rb_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
19008c2ecf20Sopenharmony_ci						       "regmap-rb");
19018c2ecf20Sopenharmony_ci	if (IS_ERR(edac->rb_map)) {
19028c2ecf20Sopenharmony_ci		dev_warn(edac->dev, "missing syscon regmap rb\n");
19038c2ecf20Sopenharmony_ci		edac->rb_map = NULL;
19048c2ecf20Sopenharmony_ci	}
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
19078c2ecf20Sopenharmony_ci	edac->pcp_csr = devm_ioremap_resource(&pdev->dev, res);
19088c2ecf20Sopenharmony_ci	if (IS_ERR(edac->pcp_csr)) {
19098c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no PCP resource address\n");
19108c2ecf20Sopenharmony_ci		rc = PTR_ERR(edac->pcp_csr);
19118c2ecf20Sopenharmony_ci		goto out_err;
19128c2ecf20Sopenharmony_ci	}
19138c2ecf20Sopenharmony_ci
19148c2ecf20Sopenharmony_ci	if (edac_op_state == EDAC_OPSTATE_INT) {
19158c2ecf20Sopenharmony_ci		int irq;
19168c2ecf20Sopenharmony_ci		int i;
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_ci		for (i = 0; i < 3; i++) {
19198c2ecf20Sopenharmony_ci			irq = platform_get_irq(pdev, i);
19208c2ecf20Sopenharmony_ci			if (irq < 0) {
19218c2ecf20Sopenharmony_ci				dev_err(&pdev->dev, "No IRQ resource\n");
19228c2ecf20Sopenharmony_ci				rc = irq;
19238c2ecf20Sopenharmony_ci				goto out_err;
19248c2ecf20Sopenharmony_ci			}
19258c2ecf20Sopenharmony_ci			rc = devm_request_irq(&pdev->dev, irq,
19268c2ecf20Sopenharmony_ci					      xgene_edac_isr, IRQF_SHARED,
19278c2ecf20Sopenharmony_ci					      dev_name(&pdev->dev), edac);
19288c2ecf20Sopenharmony_ci			if (rc) {
19298c2ecf20Sopenharmony_ci				dev_err(&pdev->dev,
19308c2ecf20Sopenharmony_ci					"Could not request IRQ %d\n", irq);
19318c2ecf20Sopenharmony_ci				goto out_err;
19328c2ecf20Sopenharmony_ci			}
19338c2ecf20Sopenharmony_ci		}
19348c2ecf20Sopenharmony_ci	}
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci	edac->dfs = edac_debugfs_create_dir(pdev->dev.kobj.name);
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci	for_each_child_of_node(pdev->dev.of_node, child) {
19398c2ecf20Sopenharmony_ci		if (!of_device_is_available(child))
19408c2ecf20Sopenharmony_ci			continue;
19418c2ecf20Sopenharmony_ci		if (of_device_is_compatible(child, "apm,xgene-edac-mc"))
19428c2ecf20Sopenharmony_ci			xgene_edac_mc_add(edac, child);
19438c2ecf20Sopenharmony_ci		if (of_device_is_compatible(child, "apm,xgene-edac-pmd"))
19448c2ecf20Sopenharmony_ci			xgene_edac_pmd_add(edac, child, 1);
19458c2ecf20Sopenharmony_ci		if (of_device_is_compatible(child, "apm,xgene-edac-pmd-v2"))
19468c2ecf20Sopenharmony_ci			xgene_edac_pmd_add(edac, child, 2);
19478c2ecf20Sopenharmony_ci		if (of_device_is_compatible(child, "apm,xgene-edac-l3"))
19488c2ecf20Sopenharmony_ci			xgene_edac_l3_add(edac, child, 1);
19498c2ecf20Sopenharmony_ci		if (of_device_is_compatible(child, "apm,xgene-edac-l3-v2"))
19508c2ecf20Sopenharmony_ci			xgene_edac_l3_add(edac, child, 2);
19518c2ecf20Sopenharmony_ci		if (of_device_is_compatible(child, "apm,xgene-edac-soc"))
19528c2ecf20Sopenharmony_ci			xgene_edac_soc_add(edac, child, 0);
19538c2ecf20Sopenharmony_ci		if (of_device_is_compatible(child, "apm,xgene-edac-soc-v1"))
19548c2ecf20Sopenharmony_ci			xgene_edac_soc_add(edac, child, 1);
19558c2ecf20Sopenharmony_ci	}
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci	return 0;
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ciout_err:
19608c2ecf20Sopenharmony_ci	return rc;
19618c2ecf20Sopenharmony_ci}
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_cistatic int xgene_edac_remove(struct platform_device *pdev)
19648c2ecf20Sopenharmony_ci{
19658c2ecf20Sopenharmony_ci	struct xgene_edac *edac = dev_get_drvdata(&pdev->dev);
19668c2ecf20Sopenharmony_ci	struct xgene_edac_mc_ctx *mcu;
19678c2ecf20Sopenharmony_ci	struct xgene_edac_mc_ctx *temp_mcu;
19688c2ecf20Sopenharmony_ci	struct xgene_edac_pmd_ctx *pmd;
19698c2ecf20Sopenharmony_ci	struct xgene_edac_pmd_ctx *temp_pmd;
19708c2ecf20Sopenharmony_ci	struct xgene_edac_dev_ctx *node;
19718c2ecf20Sopenharmony_ci	struct xgene_edac_dev_ctx *temp_node;
19728c2ecf20Sopenharmony_ci
19738c2ecf20Sopenharmony_ci	list_for_each_entry_safe(mcu, temp_mcu, &edac->mcus, next)
19748c2ecf20Sopenharmony_ci		xgene_edac_mc_remove(mcu);
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_ci	list_for_each_entry_safe(pmd, temp_pmd, &edac->pmds, next)
19778c2ecf20Sopenharmony_ci		xgene_edac_pmd_remove(pmd);
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci	list_for_each_entry_safe(node, temp_node, &edac->l3s, next)
19808c2ecf20Sopenharmony_ci		xgene_edac_l3_remove(node);
19818c2ecf20Sopenharmony_ci
19828c2ecf20Sopenharmony_ci	list_for_each_entry_safe(node, temp_node, &edac->socs, next)
19838c2ecf20Sopenharmony_ci		xgene_edac_soc_remove(node);
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_ci	return 0;
19868c2ecf20Sopenharmony_ci}
19878c2ecf20Sopenharmony_ci
19888c2ecf20Sopenharmony_cistatic const struct of_device_id xgene_edac_of_match[] = {
19898c2ecf20Sopenharmony_ci	{ .compatible = "apm,xgene-edac" },
19908c2ecf20Sopenharmony_ci	{},
19918c2ecf20Sopenharmony_ci};
19928c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, xgene_edac_of_match);
19938c2ecf20Sopenharmony_ci
19948c2ecf20Sopenharmony_cistatic struct platform_driver xgene_edac_driver = {
19958c2ecf20Sopenharmony_ci	.probe = xgene_edac_probe,
19968c2ecf20Sopenharmony_ci	.remove = xgene_edac_remove,
19978c2ecf20Sopenharmony_ci	.driver = {
19988c2ecf20Sopenharmony_ci		.name = "xgene-edac",
19998c2ecf20Sopenharmony_ci		.of_match_table = xgene_edac_of_match,
20008c2ecf20Sopenharmony_ci	},
20018c2ecf20Sopenharmony_ci};
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_cistatic int __init xgene_edac_init(void)
20048c2ecf20Sopenharmony_ci{
20058c2ecf20Sopenharmony_ci	int rc;
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_ci	/* Make sure error reporting method is sane */
20088c2ecf20Sopenharmony_ci	switch (edac_op_state) {
20098c2ecf20Sopenharmony_ci	case EDAC_OPSTATE_POLL:
20108c2ecf20Sopenharmony_ci	case EDAC_OPSTATE_INT:
20118c2ecf20Sopenharmony_ci		break;
20128c2ecf20Sopenharmony_ci	default:
20138c2ecf20Sopenharmony_ci		edac_op_state = EDAC_OPSTATE_INT;
20148c2ecf20Sopenharmony_ci		break;
20158c2ecf20Sopenharmony_ci	}
20168c2ecf20Sopenharmony_ci
20178c2ecf20Sopenharmony_ci	rc = platform_driver_register(&xgene_edac_driver);
20188c2ecf20Sopenharmony_ci	if (rc) {
20198c2ecf20Sopenharmony_ci		edac_printk(KERN_ERR, EDAC_MOD_STR,
20208c2ecf20Sopenharmony_ci			    "EDAC fails to register\n");
20218c2ecf20Sopenharmony_ci		goto reg_failed;
20228c2ecf20Sopenharmony_ci	}
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_ci	return 0;
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_cireg_failed:
20278c2ecf20Sopenharmony_ci	return rc;
20288c2ecf20Sopenharmony_ci}
20298c2ecf20Sopenharmony_cimodule_init(xgene_edac_init);
20308c2ecf20Sopenharmony_ci
20318c2ecf20Sopenharmony_cistatic void __exit xgene_edac_exit(void)
20328c2ecf20Sopenharmony_ci{
20338c2ecf20Sopenharmony_ci	platform_driver_unregister(&xgene_edac_driver);
20348c2ecf20Sopenharmony_ci}
20358c2ecf20Sopenharmony_cimodule_exit(xgene_edac_exit);
20368c2ecf20Sopenharmony_ci
20378c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
20388c2ecf20Sopenharmony_ciMODULE_AUTHOR("Feng Kan <fkan@apm.com>");
20398c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("APM X-Gene EDAC driver");
20408c2ecf20Sopenharmony_cimodule_param(edac_op_state, int, 0444);
20418c2ecf20Sopenharmony_ciMODULE_PARM_DESC(edac_op_state,
20428c2ecf20Sopenharmony_ci		 "EDAC error reporting state: 0=Poll, 2=Interrupt");
2043