18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2017 Pengutronix, Jan Luebbe <kernel@pengutronix.de> 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/kernel.h> 78c2ecf20Sopenharmony_ci#include <linux/edac.h> 88c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <asm/hardware/cache-l2x0.h> 118c2ecf20Sopenharmony_ci#include <asm/hardware/cache-aurora-l2.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "edac_mc.h" 148c2ecf20Sopenharmony_ci#include "edac_device.h" 158c2ecf20Sopenharmony_ci#include "edac_module.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/************************ EDAC MC (DDR RAM) ********************************/ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define SDRAM_NUM_CS 4 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define SDRAM_CONFIG_REG 0x0 228c2ecf20Sopenharmony_ci#define SDRAM_CONFIG_ECC_MASK BIT(18) 238c2ecf20Sopenharmony_ci#define SDRAM_CONFIG_REGISTERED_MASK BIT(17) 248c2ecf20Sopenharmony_ci#define SDRAM_CONFIG_BUS_WIDTH_MASK BIT(15) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define SDRAM_ADDR_CTRL_REG 0x10 278c2ecf20Sopenharmony_ci#define SDRAM_ADDR_CTRL_SIZE_HIGH_OFFSET(cs) (20+cs) 288c2ecf20Sopenharmony_ci#define SDRAM_ADDR_CTRL_SIZE_HIGH_MASK(cs) (0x1 << SDRAM_ADDR_CTRL_SIZE_HIGH_OFFSET(cs)) 298c2ecf20Sopenharmony_ci#define SDRAM_ADDR_CTRL_ADDR_SEL_MASK(cs) BIT(16+cs) 308c2ecf20Sopenharmony_ci#define SDRAM_ADDR_CTRL_SIZE_LOW_OFFSET(cs) (cs*4+2) 318c2ecf20Sopenharmony_ci#define SDRAM_ADDR_CTRL_SIZE_LOW_MASK(cs) (0x3 << SDRAM_ADDR_CTRL_SIZE_LOW_OFFSET(cs)) 328c2ecf20Sopenharmony_ci#define SDRAM_ADDR_CTRL_STRUCT_OFFSET(cs) (cs*4) 338c2ecf20Sopenharmony_ci#define SDRAM_ADDR_CTRL_STRUCT_MASK(cs) (0x3 << SDRAM_ADDR_CTRL_STRUCT_OFFSET(cs)) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define SDRAM_ERR_DATA_H_REG 0x40 368c2ecf20Sopenharmony_ci#define SDRAM_ERR_DATA_L_REG 0x44 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define SDRAM_ERR_RECV_ECC_REG 0x48 398c2ecf20Sopenharmony_ci#define SDRAM_ERR_RECV_ECC_VALUE_MASK 0xff 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define SDRAM_ERR_CALC_ECC_REG 0x4c 428c2ecf20Sopenharmony_ci#define SDRAM_ERR_CALC_ECC_ROW_OFFSET 8 438c2ecf20Sopenharmony_ci#define SDRAM_ERR_CALC_ECC_ROW_MASK (0xffff << SDRAM_ERR_CALC_ECC_ROW_OFFSET) 448c2ecf20Sopenharmony_ci#define SDRAM_ERR_CALC_ECC_VALUE_MASK 0xff 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define SDRAM_ERR_ADDR_REG 0x50 478c2ecf20Sopenharmony_ci#define SDRAM_ERR_ADDR_BANK_OFFSET 23 488c2ecf20Sopenharmony_ci#define SDRAM_ERR_ADDR_BANK_MASK (0x7 << SDRAM_ERR_ADDR_BANK_OFFSET) 498c2ecf20Sopenharmony_ci#define SDRAM_ERR_ADDR_COL_OFFSET 8 508c2ecf20Sopenharmony_ci#define SDRAM_ERR_ADDR_COL_MASK (0x7fff << SDRAM_ERR_ADDR_COL_OFFSET) 518c2ecf20Sopenharmony_ci#define SDRAM_ERR_ADDR_CS_OFFSET 1 528c2ecf20Sopenharmony_ci#define SDRAM_ERR_ADDR_CS_MASK (0x3 << SDRAM_ERR_ADDR_CS_OFFSET) 538c2ecf20Sopenharmony_ci#define SDRAM_ERR_ADDR_TYPE_MASK BIT(0) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define SDRAM_ERR_CTRL_REG 0x54 568c2ecf20Sopenharmony_ci#define SDRAM_ERR_CTRL_THR_OFFSET 16 578c2ecf20Sopenharmony_ci#define SDRAM_ERR_CTRL_THR_MASK (0xff << SDRAM_ERR_CTRL_THR_OFFSET) 588c2ecf20Sopenharmony_ci#define SDRAM_ERR_CTRL_PROP_MASK BIT(9) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define SDRAM_ERR_SBE_COUNT_REG 0x58 618c2ecf20Sopenharmony_ci#define SDRAM_ERR_DBE_COUNT_REG 0x5c 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define SDRAM_ERR_CAUSE_ERR_REG 0xd0 648c2ecf20Sopenharmony_ci#define SDRAM_ERR_CAUSE_MSG_REG 0xd8 658c2ecf20Sopenharmony_ci#define SDRAM_ERR_CAUSE_DBE_MASK BIT(1) 668c2ecf20Sopenharmony_ci#define SDRAM_ERR_CAUSE_SBE_MASK BIT(0) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define SDRAM_RANK_CTRL_REG 0x1e0 698c2ecf20Sopenharmony_ci#define SDRAM_RANK_CTRL_EXIST_MASK(cs) BIT(cs) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistruct axp_mc_drvdata { 728c2ecf20Sopenharmony_ci void __iomem *base; 738c2ecf20Sopenharmony_ci /* width in bytes */ 748c2ecf20Sopenharmony_ci unsigned int width; 758c2ecf20Sopenharmony_ci /* bank interleaving */ 768c2ecf20Sopenharmony_ci bool cs_addr_sel[SDRAM_NUM_CS]; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci char msg[128]; 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* derived from "DRAM Address Multiplexing" in the ARMADA XP Functional Spec */ 828c2ecf20Sopenharmony_cistatic uint32_t axp_mc_calc_address(struct axp_mc_drvdata *drvdata, 838c2ecf20Sopenharmony_ci uint8_t cs, uint8_t bank, uint16_t row, 848c2ecf20Sopenharmony_ci uint16_t col) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci if (drvdata->width == 8) { 878c2ecf20Sopenharmony_ci /* 64 bit */ 888c2ecf20Sopenharmony_ci if (drvdata->cs_addr_sel[cs]) 898c2ecf20Sopenharmony_ci /* bank interleaved */ 908c2ecf20Sopenharmony_ci return (((row & 0xfff8) << 16) | 918c2ecf20Sopenharmony_ci ((bank & 0x7) << 16) | 928c2ecf20Sopenharmony_ci ((row & 0x7) << 13) | 938c2ecf20Sopenharmony_ci ((col & 0x3ff) << 3)); 948c2ecf20Sopenharmony_ci else 958c2ecf20Sopenharmony_ci return (((row & 0xffff << 16) | 968c2ecf20Sopenharmony_ci ((bank & 0x7) << 13) | 978c2ecf20Sopenharmony_ci ((col & 0x3ff)) << 3)); 988c2ecf20Sopenharmony_ci } else if (drvdata->width == 4) { 998c2ecf20Sopenharmony_ci /* 32 bit */ 1008c2ecf20Sopenharmony_ci if (drvdata->cs_addr_sel[cs]) 1018c2ecf20Sopenharmony_ci /* bank interleaved */ 1028c2ecf20Sopenharmony_ci return (((row & 0xfff0) << 15) | 1038c2ecf20Sopenharmony_ci ((bank & 0x7) << 16) | 1048c2ecf20Sopenharmony_ci ((row & 0xf) << 12) | 1058c2ecf20Sopenharmony_ci ((col & 0x3ff) << 2)); 1068c2ecf20Sopenharmony_ci else 1078c2ecf20Sopenharmony_ci return (((row & 0xffff << 15) | 1088c2ecf20Sopenharmony_ci ((bank & 0x7) << 12) | 1098c2ecf20Sopenharmony_ci ((col & 0x3ff)) << 2)); 1108c2ecf20Sopenharmony_ci } else { 1118c2ecf20Sopenharmony_ci /* 16 bit */ 1128c2ecf20Sopenharmony_ci if (drvdata->cs_addr_sel[cs]) 1138c2ecf20Sopenharmony_ci /* bank interleaved */ 1148c2ecf20Sopenharmony_ci return (((row & 0xffe0) << 14) | 1158c2ecf20Sopenharmony_ci ((bank & 0x7) << 16) | 1168c2ecf20Sopenharmony_ci ((row & 0x1f) << 11) | 1178c2ecf20Sopenharmony_ci ((col & 0x3ff) << 1)); 1188c2ecf20Sopenharmony_ci else 1198c2ecf20Sopenharmony_ci return (((row & 0xffff << 14) | 1208c2ecf20Sopenharmony_ci ((bank & 0x7) << 11) | 1218c2ecf20Sopenharmony_ci ((col & 0x3ff)) << 1)); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void axp_mc_check(struct mem_ctl_info *mci) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct axp_mc_drvdata *drvdata = mci->pvt_info; 1288c2ecf20Sopenharmony_ci uint32_t data_h, data_l, recv_ecc, calc_ecc, addr; 1298c2ecf20Sopenharmony_ci uint32_t cnt_sbe, cnt_dbe, cause_err, cause_msg; 1308c2ecf20Sopenharmony_ci uint32_t row_val, col_val, bank_val, addr_val; 1318c2ecf20Sopenharmony_ci uint8_t syndrome_val, cs_val; 1328c2ecf20Sopenharmony_ci char *msg = drvdata->msg; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci data_h = readl(drvdata->base + SDRAM_ERR_DATA_H_REG); 1358c2ecf20Sopenharmony_ci data_l = readl(drvdata->base + SDRAM_ERR_DATA_L_REG); 1368c2ecf20Sopenharmony_ci recv_ecc = readl(drvdata->base + SDRAM_ERR_RECV_ECC_REG); 1378c2ecf20Sopenharmony_ci calc_ecc = readl(drvdata->base + SDRAM_ERR_CALC_ECC_REG); 1388c2ecf20Sopenharmony_ci addr = readl(drvdata->base + SDRAM_ERR_ADDR_REG); 1398c2ecf20Sopenharmony_ci cnt_sbe = readl(drvdata->base + SDRAM_ERR_SBE_COUNT_REG); 1408c2ecf20Sopenharmony_ci cnt_dbe = readl(drvdata->base + SDRAM_ERR_DBE_COUNT_REG); 1418c2ecf20Sopenharmony_ci cause_err = readl(drvdata->base + SDRAM_ERR_CAUSE_ERR_REG); 1428c2ecf20Sopenharmony_ci cause_msg = readl(drvdata->base + SDRAM_ERR_CAUSE_MSG_REG); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* clear cause registers */ 1458c2ecf20Sopenharmony_ci writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK), 1468c2ecf20Sopenharmony_ci drvdata->base + SDRAM_ERR_CAUSE_ERR_REG); 1478c2ecf20Sopenharmony_ci writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK), 1488c2ecf20Sopenharmony_ci drvdata->base + SDRAM_ERR_CAUSE_MSG_REG); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* clear error counter registers */ 1518c2ecf20Sopenharmony_ci if (cnt_sbe) 1528c2ecf20Sopenharmony_ci writel(0, drvdata->base + SDRAM_ERR_SBE_COUNT_REG); 1538c2ecf20Sopenharmony_ci if (cnt_dbe) 1548c2ecf20Sopenharmony_ci writel(0, drvdata->base + SDRAM_ERR_DBE_COUNT_REG); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (!cnt_sbe && !cnt_dbe) 1578c2ecf20Sopenharmony_ci return; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (!(addr & SDRAM_ERR_ADDR_TYPE_MASK)) { 1608c2ecf20Sopenharmony_ci if (cnt_sbe) 1618c2ecf20Sopenharmony_ci cnt_sbe--; 1628c2ecf20Sopenharmony_ci else 1638c2ecf20Sopenharmony_ci dev_warn(mci->pdev, "inconsistent SBE count detected\n"); 1648c2ecf20Sopenharmony_ci } else { 1658c2ecf20Sopenharmony_ci if (cnt_dbe) 1668c2ecf20Sopenharmony_ci cnt_dbe--; 1678c2ecf20Sopenharmony_ci else 1688c2ecf20Sopenharmony_ci dev_warn(mci->pdev, "inconsistent DBE count detected\n"); 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* report earlier errors */ 1728c2ecf20Sopenharmony_ci if (cnt_sbe) 1738c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1748c2ecf20Sopenharmony_ci cnt_sbe, /* error count */ 1758c2ecf20Sopenharmony_ci 0, 0, 0, /* pfn, offset, syndrome */ 1768c2ecf20Sopenharmony_ci -1, -1, -1, /* top, mid, low layer */ 1778c2ecf20Sopenharmony_ci mci->ctl_name, 1788c2ecf20Sopenharmony_ci "details unavailable (multiple errors)"); 1798c2ecf20Sopenharmony_ci if (cnt_dbe) 1808c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1818c2ecf20Sopenharmony_ci cnt_dbe, /* error count */ 1828c2ecf20Sopenharmony_ci 0, 0, 0, /* pfn, offset, syndrome */ 1838c2ecf20Sopenharmony_ci -1, -1, -1, /* top, mid, low layer */ 1848c2ecf20Sopenharmony_ci mci->ctl_name, 1858c2ecf20Sopenharmony_ci "details unavailable (multiple errors)"); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* report details for most recent error */ 1888c2ecf20Sopenharmony_ci cs_val = (addr & SDRAM_ERR_ADDR_CS_MASK) >> SDRAM_ERR_ADDR_CS_OFFSET; 1898c2ecf20Sopenharmony_ci bank_val = (addr & SDRAM_ERR_ADDR_BANK_MASK) >> SDRAM_ERR_ADDR_BANK_OFFSET; 1908c2ecf20Sopenharmony_ci row_val = (calc_ecc & SDRAM_ERR_CALC_ECC_ROW_MASK) >> SDRAM_ERR_CALC_ECC_ROW_OFFSET; 1918c2ecf20Sopenharmony_ci col_val = (addr & SDRAM_ERR_ADDR_COL_MASK) >> SDRAM_ERR_ADDR_COL_OFFSET; 1928c2ecf20Sopenharmony_ci syndrome_val = (recv_ecc ^ calc_ecc) & 0xff; 1938c2ecf20Sopenharmony_ci addr_val = axp_mc_calc_address(drvdata, cs_val, bank_val, row_val, 1948c2ecf20Sopenharmony_ci col_val); 1958c2ecf20Sopenharmony_ci msg += sprintf(msg, "row=0x%04x ", row_val); /* 11 chars */ 1968c2ecf20Sopenharmony_ci msg += sprintf(msg, "bank=0x%x ", bank_val); /* 9 chars */ 1978c2ecf20Sopenharmony_ci msg += sprintf(msg, "col=0x%04x ", col_val); /* 11 chars */ 1988c2ecf20Sopenharmony_ci msg += sprintf(msg, "cs=%d", cs_val); /* 4 chars */ 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (!(addr & SDRAM_ERR_ADDR_TYPE_MASK)) { 2018c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 2028c2ecf20Sopenharmony_ci 1, /* error count */ 2038c2ecf20Sopenharmony_ci addr_val >> PAGE_SHIFT, 2048c2ecf20Sopenharmony_ci addr_val & ~PAGE_MASK, 2058c2ecf20Sopenharmony_ci syndrome_val, 2068c2ecf20Sopenharmony_ci cs_val, -1, -1, /* top, mid, low layer */ 2078c2ecf20Sopenharmony_ci mci->ctl_name, drvdata->msg); 2088c2ecf20Sopenharmony_ci } else { 2098c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 2108c2ecf20Sopenharmony_ci 1, /* error count */ 2118c2ecf20Sopenharmony_ci addr_val >> PAGE_SHIFT, 2128c2ecf20Sopenharmony_ci addr_val & ~PAGE_MASK, 2138c2ecf20Sopenharmony_ci syndrome_val, 2148c2ecf20Sopenharmony_ci cs_val, -1, -1, /* top, mid, low layer */ 2158c2ecf20Sopenharmony_ci mci->ctl_name, drvdata->msg); 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic void axp_mc_read_config(struct mem_ctl_info *mci) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct axp_mc_drvdata *drvdata = mci->pvt_info; 2228c2ecf20Sopenharmony_ci uint32_t config, addr_ctrl, rank_ctrl; 2238c2ecf20Sopenharmony_ci unsigned int i, cs_struct, cs_size; 2248c2ecf20Sopenharmony_ci struct dimm_info *dimm; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci config = readl(drvdata->base + SDRAM_CONFIG_REG); 2278c2ecf20Sopenharmony_ci if (config & SDRAM_CONFIG_BUS_WIDTH_MASK) 2288c2ecf20Sopenharmony_ci /* 64 bit */ 2298c2ecf20Sopenharmony_ci drvdata->width = 8; 2308c2ecf20Sopenharmony_ci else 2318c2ecf20Sopenharmony_ci /* 32 bit */ 2328c2ecf20Sopenharmony_ci drvdata->width = 4; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci addr_ctrl = readl(drvdata->base + SDRAM_ADDR_CTRL_REG); 2358c2ecf20Sopenharmony_ci rank_ctrl = readl(drvdata->base + SDRAM_RANK_CTRL_REG); 2368c2ecf20Sopenharmony_ci for (i = 0; i < SDRAM_NUM_CS; i++) { 2378c2ecf20Sopenharmony_ci dimm = mci->dimms[i]; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (!(rank_ctrl & SDRAM_RANK_CTRL_EXIST_MASK(i))) 2408c2ecf20Sopenharmony_ci continue; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci drvdata->cs_addr_sel[i] = 2438c2ecf20Sopenharmony_ci !!(addr_ctrl & SDRAM_ADDR_CTRL_ADDR_SEL_MASK(i)); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci cs_struct = (addr_ctrl & SDRAM_ADDR_CTRL_STRUCT_MASK(i)) >> SDRAM_ADDR_CTRL_STRUCT_OFFSET(i); 2468c2ecf20Sopenharmony_ci cs_size = ((addr_ctrl & SDRAM_ADDR_CTRL_SIZE_HIGH_MASK(i)) >> (SDRAM_ADDR_CTRL_SIZE_HIGH_OFFSET(i) - 2) | 2478c2ecf20Sopenharmony_ci ((addr_ctrl & SDRAM_ADDR_CTRL_SIZE_LOW_MASK(i)) >> SDRAM_ADDR_CTRL_SIZE_LOW_OFFSET(i))); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci switch (cs_size) { 2508c2ecf20Sopenharmony_ci case 0: /* 2GBit */ 2518c2ecf20Sopenharmony_ci dimm->nr_pages = 524288; 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci case 1: /* 256MBit */ 2548c2ecf20Sopenharmony_ci dimm->nr_pages = 65536; 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci case 2: /* 512MBit */ 2578c2ecf20Sopenharmony_ci dimm->nr_pages = 131072; 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci case 3: /* 1GBit */ 2608c2ecf20Sopenharmony_ci dimm->nr_pages = 262144; 2618c2ecf20Sopenharmony_ci break; 2628c2ecf20Sopenharmony_ci case 4: /* 4GBit */ 2638c2ecf20Sopenharmony_ci dimm->nr_pages = 1048576; 2648c2ecf20Sopenharmony_ci break; 2658c2ecf20Sopenharmony_ci case 5: /* 8GBit */ 2668c2ecf20Sopenharmony_ci dimm->nr_pages = 2097152; 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci dimm->grain = 8; 2708c2ecf20Sopenharmony_ci dimm->dtype = cs_struct ? DEV_X16 : DEV_X8; 2718c2ecf20Sopenharmony_ci dimm->mtype = (config & SDRAM_CONFIG_REGISTERED_MASK) ? 2728c2ecf20Sopenharmony_ci MEM_RDDR3 : MEM_DDR3; 2738c2ecf20Sopenharmony_ci dimm->edac_mode = EDAC_SECDED; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic const struct of_device_id axp_mc_of_match[] = { 2788c2ecf20Sopenharmony_ci {.compatible = "marvell,armada-xp-sdram-controller",}, 2798c2ecf20Sopenharmony_ci {}, 2808c2ecf20Sopenharmony_ci}; 2818c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, axp_mc_of_match); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int axp_mc_probe(struct platform_device *pdev) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct axp_mc_drvdata *drvdata; 2868c2ecf20Sopenharmony_ci struct edac_mc_layer layers[1]; 2878c2ecf20Sopenharmony_ci const struct of_device_id *id; 2888c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 2898c2ecf20Sopenharmony_ci struct resource *r; 2908c2ecf20Sopenharmony_ci void __iomem *base; 2918c2ecf20Sopenharmony_ci uint32_t config; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2948c2ecf20Sopenharmony_ci if (!r) { 2958c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to get mem resource\n"); 2968c2ecf20Sopenharmony_ci return -ENODEV; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci base = devm_ioremap_resource(&pdev->dev, r); 3008c2ecf20Sopenharmony_ci if (IS_ERR(base)) { 3018c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to map regs\n"); 3028c2ecf20Sopenharmony_ci return PTR_ERR(base); 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci config = readl(base + SDRAM_CONFIG_REG); 3068c2ecf20Sopenharmony_ci if (!(config & SDRAM_CONFIG_ECC_MASK)) { 3078c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "SDRAM ECC is not enabled\n"); 3088c2ecf20Sopenharmony_ci return -EINVAL; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 3128c2ecf20Sopenharmony_ci layers[0].size = SDRAM_NUM_CS; 3138c2ecf20Sopenharmony_ci layers[0].is_virt_csrow = true; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*drvdata)); 3168c2ecf20Sopenharmony_ci if (!mci) 3178c2ecf20Sopenharmony_ci return -ENOMEM; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci drvdata = mci->pvt_info; 3208c2ecf20Sopenharmony_ci drvdata->base = base; 3218c2ecf20Sopenharmony_ci mci->pdev = &pdev->dev; 3228c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, mci); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci id = of_match_device(axp_mc_of_match, &pdev->dev); 3258c2ecf20Sopenharmony_ci mci->edac_check = axp_mc_check; 3268c2ecf20Sopenharmony_ci mci->mtype_cap = MEM_FLAG_DDR3; 3278c2ecf20Sopenharmony_ci mci->edac_cap = EDAC_FLAG_SECDED; 3288c2ecf20Sopenharmony_ci mci->mod_name = pdev->dev.driver->name; 3298c2ecf20Sopenharmony_ci mci->ctl_name = id ? id->compatible : "unknown"; 3308c2ecf20Sopenharmony_ci mci->dev_name = dev_name(&pdev->dev); 3318c2ecf20Sopenharmony_ci mci->scrub_mode = SCRUB_NONE; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci axp_mc_read_config(mci); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* These SoCs have a reduced width bus */ 3368c2ecf20Sopenharmony_ci if (of_machine_is_compatible("marvell,armada380") || 3378c2ecf20Sopenharmony_ci of_machine_is_compatible("marvell,armadaxp-98dx3236")) 3388c2ecf20Sopenharmony_ci drvdata->width /= 2; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* configure SBE threshold */ 3418c2ecf20Sopenharmony_ci /* it seems that SBEs are not captured otherwise */ 3428c2ecf20Sopenharmony_ci writel(1 << SDRAM_ERR_CTRL_THR_OFFSET, drvdata->base + SDRAM_ERR_CTRL_REG); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci /* clear cause registers */ 3458c2ecf20Sopenharmony_ci writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK), drvdata->base + SDRAM_ERR_CAUSE_ERR_REG); 3468c2ecf20Sopenharmony_ci writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK), drvdata->base + SDRAM_ERR_CAUSE_MSG_REG); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* clear counter registers */ 3498c2ecf20Sopenharmony_ci writel(0, drvdata->base + SDRAM_ERR_SBE_COUNT_REG); 3508c2ecf20Sopenharmony_ci writel(0, drvdata->base + SDRAM_ERR_DBE_COUNT_REG); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (edac_mc_add_mc(mci)) { 3538c2ecf20Sopenharmony_ci edac_mc_free(mci); 3548c2ecf20Sopenharmony_ci return -EINVAL; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci edac_op_state = EDAC_OPSTATE_POLL; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int axp_mc_remove(struct platform_device *pdev) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = platform_get_drvdata(pdev); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci edac_mc_del_mc(&pdev->dev); 3668c2ecf20Sopenharmony_ci edac_mc_free(mci); 3678c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, NULL); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic struct platform_driver axp_mc_driver = { 3738c2ecf20Sopenharmony_ci .probe = axp_mc_probe, 3748c2ecf20Sopenharmony_ci .remove = axp_mc_remove, 3758c2ecf20Sopenharmony_ci .driver = { 3768c2ecf20Sopenharmony_ci .name = "armada_xp_mc_edac", 3778c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(axp_mc_of_match), 3788c2ecf20Sopenharmony_ci }, 3798c2ecf20Sopenharmony_ci}; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci/************************ EDAC Device (L2 Cache) ***************************/ 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistruct aurora_l2_drvdata { 3848c2ecf20Sopenharmony_ci void __iomem *base; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci char msg[128]; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /* error injection via debugfs */ 3898c2ecf20Sopenharmony_ci uint32_t inject_addr; 3908c2ecf20Sopenharmony_ci uint32_t inject_mask; 3918c2ecf20Sopenharmony_ci uint8_t inject_ctl; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci struct dentry *debugfs; 3948c2ecf20Sopenharmony_ci}; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG 3978c2ecf20Sopenharmony_cistatic void aurora_l2_inject(struct aurora_l2_drvdata *drvdata) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci drvdata->inject_addr &= AURORA_ERR_INJECT_CTL_ADDR_MASK; 4008c2ecf20Sopenharmony_ci drvdata->inject_ctl &= AURORA_ERR_INJECT_CTL_EN_MASK; 4018c2ecf20Sopenharmony_ci writel(0, drvdata->base + AURORA_ERR_INJECT_CTL_REG); 4028c2ecf20Sopenharmony_ci writel(drvdata->inject_mask, drvdata->base + AURORA_ERR_INJECT_MASK_REG); 4038c2ecf20Sopenharmony_ci writel(drvdata->inject_addr | drvdata->inject_ctl, drvdata->base + AURORA_ERR_INJECT_CTL_REG); 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci#endif 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic void aurora_l2_check(struct edac_device_ctl_info *dci) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct aurora_l2_drvdata *drvdata = dci->pvt_info; 4108c2ecf20Sopenharmony_ci uint32_t cnt, src, txn, err, attr_cap, addr_cap, way_cap; 4118c2ecf20Sopenharmony_ci unsigned int cnt_ce, cnt_ue; 4128c2ecf20Sopenharmony_ci char *msg = drvdata->msg; 4138c2ecf20Sopenharmony_ci size_t size = sizeof(drvdata->msg); 4148c2ecf20Sopenharmony_ci size_t len = 0; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci cnt = readl(drvdata->base + AURORA_ERR_CNT_REG); 4178c2ecf20Sopenharmony_ci attr_cap = readl(drvdata->base + AURORA_ERR_ATTR_CAP_REG); 4188c2ecf20Sopenharmony_ci addr_cap = readl(drvdata->base + AURORA_ERR_ADDR_CAP_REG); 4198c2ecf20Sopenharmony_ci way_cap = readl(drvdata->base + AURORA_ERR_WAY_CAP_REG); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci cnt_ce = (cnt & AURORA_ERR_CNT_CE_MASK) >> AURORA_ERR_CNT_CE_OFFSET; 4228c2ecf20Sopenharmony_ci cnt_ue = (cnt & AURORA_ERR_CNT_UE_MASK) >> AURORA_ERR_CNT_UE_OFFSET; 4238c2ecf20Sopenharmony_ci /* clear error counter registers */ 4248c2ecf20Sopenharmony_ci if (cnt_ce || cnt_ue) 4258c2ecf20Sopenharmony_ci writel(AURORA_ERR_CNT_CLR, drvdata->base + AURORA_ERR_CNT_REG); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (!(attr_cap & AURORA_ERR_ATTR_CAP_VALID)) 4288c2ecf20Sopenharmony_ci goto clear_remaining; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci src = (attr_cap & AURORA_ERR_ATTR_SRC_MSK) >> AURORA_ERR_ATTR_SRC_OFF; 4318c2ecf20Sopenharmony_ci if (src <= 3) 4328c2ecf20Sopenharmony_ci len += scnprintf(msg+len, size-len, "src=CPU%d ", src); 4338c2ecf20Sopenharmony_ci else 4348c2ecf20Sopenharmony_ci len += scnprintf(msg+len, size-len, "src=IO "); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci txn = (attr_cap & AURORA_ERR_ATTR_TXN_MSK) >> AURORA_ERR_ATTR_TXN_OFF; 4378c2ecf20Sopenharmony_ci switch (txn) { 4388c2ecf20Sopenharmony_ci case 0: 4398c2ecf20Sopenharmony_ci len += scnprintf(msg+len, size-len, "txn=Data-Read "); 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci case 1: 4428c2ecf20Sopenharmony_ci len += scnprintf(msg+len, size-len, "txn=Isn-Read "); 4438c2ecf20Sopenharmony_ci break; 4448c2ecf20Sopenharmony_ci case 2: 4458c2ecf20Sopenharmony_ci len += scnprintf(msg+len, size-len, "txn=Clean-Flush "); 4468c2ecf20Sopenharmony_ci break; 4478c2ecf20Sopenharmony_ci case 3: 4488c2ecf20Sopenharmony_ci len += scnprintf(msg+len, size-len, "txn=Eviction "); 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci case 4: 4518c2ecf20Sopenharmony_ci len += scnprintf(msg+len, size-len, 4528c2ecf20Sopenharmony_ci "txn=Read-Modify-Write "); 4538c2ecf20Sopenharmony_ci break; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci err = (attr_cap & AURORA_ERR_ATTR_ERR_MSK) >> AURORA_ERR_ATTR_ERR_OFF; 4578c2ecf20Sopenharmony_ci switch (err) { 4588c2ecf20Sopenharmony_ci case 0: 4598c2ecf20Sopenharmony_ci len += scnprintf(msg+len, size-len, "err=CorrECC "); 4608c2ecf20Sopenharmony_ci break; 4618c2ecf20Sopenharmony_ci case 1: 4628c2ecf20Sopenharmony_ci len += scnprintf(msg+len, size-len, "err=UnCorrECC "); 4638c2ecf20Sopenharmony_ci break; 4648c2ecf20Sopenharmony_ci case 2: 4658c2ecf20Sopenharmony_ci len += scnprintf(msg+len, size-len, "err=TagParity "); 4668c2ecf20Sopenharmony_ci break; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci len += scnprintf(msg+len, size-len, "addr=0x%x ", addr_cap & AURORA_ERR_ADDR_CAP_ADDR_MASK); 4708c2ecf20Sopenharmony_ci len += scnprintf(msg+len, size-len, "index=0x%x ", (way_cap & AURORA_ERR_WAY_IDX_MSK) >> AURORA_ERR_WAY_IDX_OFF); 4718c2ecf20Sopenharmony_ci len += scnprintf(msg+len, size-len, "way=0x%x", (way_cap & AURORA_ERR_WAY_CAP_WAY_MASK) >> AURORA_ERR_WAY_CAP_WAY_OFFSET); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* clear error capture registers */ 4748c2ecf20Sopenharmony_ci writel(AURORA_ERR_ATTR_CAP_VALID, drvdata->base + AURORA_ERR_ATTR_CAP_REG); 4758c2ecf20Sopenharmony_ci if (err) { 4768c2ecf20Sopenharmony_ci /* UnCorrECC or TagParity */ 4778c2ecf20Sopenharmony_ci if (cnt_ue) 4788c2ecf20Sopenharmony_ci cnt_ue--; 4798c2ecf20Sopenharmony_ci edac_device_handle_ue(dci, 0, 0, drvdata->msg); 4808c2ecf20Sopenharmony_ci } else { 4818c2ecf20Sopenharmony_ci if (cnt_ce) 4828c2ecf20Sopenharmony_ci cnt_ce--; 4838c2ecf20Sopenharmony_ci edac_device_handle_ce(dci, 0, 0, drvdata->msg); 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ciclear_remaining: 4878c2ecf20Sopenharmony_ci /* report remaining errors */ 4888c2ecf20Sopenharmony_ci while (cnt_ue--) 4898c2ecf20Sopenharmony_ci edac_device_handle_ue(dci, 0, 0, "details unavailable (multiple errors)"); 4908c2ecf20Sopenharmony_ci while (cnt_ce--) 4918c2ecf20Sopenharmony_ci edac_device_handle_ue(dci, 0, 0, "details unavailable (multiple errors)"); 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic void aurora_l2_poll(struct edac_device_ctl_info *dci) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG 4978c2ecf20Sopenharmony_ci struct aurora_l2_drvdata *drvdata = dci->pvt_info; 4988c2ecf20Sopenharmony_ci#endif 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci aurora_l2_check(dci); 5018c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG 5028c2ecf20Sopenharmony_ci aurora_l2_inject(drvdata); 5038c2ecf20Sopenharmony_ci#endif 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic const struct of_device_id aurora_l2_of_match[] = { 5078c2ecf20Sopenharmony_ci {.compatible = "marvell,aurora-system-cache",}, 5088c2ecf20Sopenharmony_ci {}, 5098c2ecf20Sopenharmony_ci}; 5108c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, aurora_l2_of_match); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic int aurora_l2_probe(struct platform_device *pdev) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct aurora_l2_drvdata *drvdata; 5158c2ecf20Sopenharmony_ci struct edac_device_ctl_info *dci; 5168c2ecf20Sopenharmony_ci const struct of_device_id *id; 5178c2ecf20Sopenharmony_ci uint32_t l2x0_aux_ctrl; 5188c2ecf20Sopenharmony_ci void __iomem *base; 5198c2ecf20Sopenharmony_ci struct resource *r; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 5228c2ecf20Sopenharmony_ci if (!r) { 5238c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to get mem resource\n"); 5248c2ecf20Sopenharmony_ci return -ENODEV; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci base = devm_ioremap_resource(&pdev->dev, r); 5288c2ecf20Sopenharmony_ci if (IS_ERR(base)) { 5298c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to map regs\n"); 5308c2ecf20Sopenharmony_ci return PTR_ERR(base); 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci l2x0_aux_ctrl = readl(base + L2X0_AUX_CTRL); 5348c2ecf20Sopenharmony_ci if (!(l2x0_aux_ctrl & AURORA_ACR_PARITY_EN)) 5358c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "tag parity is not enabled\n"); 5368c2ecf20Sopenharmony_ci if (!(l2x0_aux_ctrl & AURORA_ACR_ECC_EN)) 5378c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "data ECC is not enabled\n"); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci dci = edac_device_alloc_ctl_info(sizeof(*drvdata), 5408c2ecf20Sopenharmony_ci "cpu", 1, "L", 1, 2, NULL, 0, 0); 5418c2ecf20Sopenharmony_ci if (!dci) 5428c2ecf20Sopenharmony_ci return -ENOMEM; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci drvdata = dci->pvt_info; 5458c2ecf20Sopenharmony_ci drvdata->base = base; 5468c2ecf20Sopenharmony_ci dci->dev = &pdev->dev; 5478c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, dci); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci id = of_match_device(aurora_l2_of_match, &pdev->dev); 5508c2ecf20Sopenharmony_ci dci->edac_check = aurora_l2_poll; 5518c2ecf20Sopenharmony_ci dci->mod_name = pdev->dev.driver->name; 5528c2ecf20Sopenharmony_ci dci->ctl_name = id ? id->compatible : "unknown"; 5538c2ecf20Sopenharmony_ci dci->dev_name = dev_name(&pdev->dev); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* clear registers */ 5568c2ecf20Sopenharmony_ci writel(AURORA_ERR_CNT_CLR, drvdata->base + AURORA_ERR_CNT_REG); 5578c2ecf20Sopenharmony_ci writel(AURORA_ERR_ATTR_CAP_VALID, drvdata->base + AURORA_ERR_ATTR_CAP_REG); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (edac_device_add_device(dci)) { 5608c2ecf20Sopenharmony_ci edac_device_free_ctl_info(dci); 5618c2ecf20Sopenharmony_ci return -EINVAL; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG 5658c2ecf20Sopenharmony_ci drvdata->debugfs = edac_debugfs_create_dir(dev_name(&pdev->dev)); 5668c2ecf20Sopenharmony_ci if (drvdata->debugfs) { 5678c2ecf20Sopenharmony_ci edac_debugfs_create_x32("inject_addr", 0644, 5688c2ecf20Sopenharmony_ci drvdata->debugfs, 5698c2ecf20Sopenharmony_ci &drvdata->inject_addr); 5708c2ecf20Sopenharmony_ci edac_debugfs_create_x32("inject_mask", 0644, 5718c2ecf20Sopenharmony_ci drvdata->debugfs, 5728c2ecf20Sopenharmony_ci &drvdata->inject_mask); 5738c2ecf20Sopenharmony_ci edac_debugfs_create_x8("inject_ctl", 0644, 5748c2ecf20Sopenharmony_ci drvdata->debugfs, &drvdata->inject_ctl); 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci#endif 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci return 0; 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic int aurora_l2_remove(struct platform_device *pdev) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci struct edac_device_ctl_info *dci = platform_get_drvdata(pdev); 5848c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG 5858c2ecf20Sopenharmony_ci struct aurora_l2_drvdata *drvdata = dci->pvt_info; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci edac_debugfs_remove_recursive(drvdata->debugfs); 5888c2ecf20Sopenharmony_ci#endif 5898c2ecf20Sopenharmony_ci edac_device_del_device(&pdev->dev); 5908c2ecf20Sopenharmony_ci edac_device_free_ctl_info(dci); 5918c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, NULL); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci return 0; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic struct platform_driver aurora_l2_driver = { 5978c2ecf20Sopenharmony_ci .probe = aurora_l2_probe, 5988c2ecf20Sopenharmony_ci .remove = aurora_l2_remove, 5998c2ecf20Sopenharmony_ci .driver = { 6008c2ecf20Sopenharmony_ci .name = "aurora_l2_edac", 6018c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(aurora_l2_of_match), 6028c2ecf20Sopenharmony_ci }, 6038c2ecf20Sopenharmony_ci}; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci/************************ Driver registration ******************************/ 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic struct platform_driver * const drivers[] = { 6088c2ecf20Sopenharmony_ci &axp_mc_driver, 6098c2ecf20Sopenharmony_ci &aurora_l2_driver, 6108c2ecf20Sopenharmony_ci}; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic int __init armada_xp_edac_init(void) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci int res; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci /* only polling is supported */ 6178c2ecf20Sopenharmony_ci edac_op_state = EDAC_OPSTATE_POLL; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci res = platform_register_drivers(drivers, ARRAY_SIZE(drivers)); 6208c2ecf20Sopenharmony_ci if (res) 6218c2ecf20Sopenharmony_ci pr_warn("Armada XP EDAC drivers fail to register\n"); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci return 0; 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_cimodule_init(armada_xp_edac_init); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_cistatic void __exit armada_xp_edac_exit(void) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_cimodule_exit(armada_xp_edac_exit); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 6348c2ecf20Sopenharmony_ciMODULE_AUTHOR("Pengutronix"); 6358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("EDAC Drivers for Marvell Armada XP SDRAM and L2 Cache Controller"); 636