18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Freescale Memory Controller kernel module
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Support Power-based SoCs including MPC85xx, MPC86xx, MPC83xx and
58c2ecf20Sopenharmony_ci * ARM-based Layerscape SoCs including LS2xxx and LS1021A. Originally
68c2ecf20Sopenharmony_ci * split out from mpc85xx_edac EDAC driver.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Parts Copyrighted (c) 2013 by Freescale Semiconductor, Inc.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Author: Dave Jiang <djiang@mvista.com>
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * 2006-2007 (c) MontaVista Software, Inc. This file is licensed under
138c2ecf20Sopenharmony_ci * the terms of the GNU General Public License version 2. This program
148c2ecf20Sopenharmony_ci * is licensed "as is" without any warranty of any kind, whether express
158c2ecf20Sopenharmony_ci * or implied.
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/init.h>
198c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
208c2ecf20Sopenharmony_ci#include <linux/ctype.h>
218c2ecf20Sopenharmony_ci#include <linux/io.h>
228c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h>
238c2ecf20Sopenharmony_ci#include <linux/edac.h>
248c2ecf20Sopenharmony_ci#include <linux/smp.h>
258c2ecf20Sopenharmony_ci#include <linux/gfp.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
288c2ecf20Sopenharmony_ci#include <linux/of_device.h>
298c2ecf20Sopenharmony_ci#include <linux/of_address.h>
308c2ecf20Sopenharmony_ci#include "edac_module.h"
318c2ecf20Sopenharmony_ci#include "fsl_ddr_edac.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define EDAC_MOD_STR	"fsl_ddr_edac"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int edac_mc_idx;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic u32 orig_ddr_err_disable;
388c2ecf20Sopenharmony_cistatic u32 orig_ddr_err_sbe;
398c2ecf20Sopenharmony_cistatic bool little_endian;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic inline u32 ddr_in32(void __iomem *addr)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	return little_endian ? ioread32(addr) : ioread32be(addr);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic inline void ddr_out32(void __iomem *addr, u32 value)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	if (little_endian)
498c2ecf20Sopenharmony_ci		iowrite32(value, addr);
508c2ecf20Sopenharmony_ci	else
518c2ecf20Sopenharmony_ci		iowrite32be(value, addr);
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG
558c2ecf20Sopenharmony_ci/************************ MC SYSFS parts ***********************************/
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define to_mci(k) container_of(k, struct mem_ctl_info, dev)
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic ssize_t fsl_mc_inject_data_hi_show(struct device *dev,
608c2ecf20Sopenharmony_ci					  struct device_attribute *mattr,
618c2ecf20Sopenharmony_ci					  char *data)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci = to_mci(dev);
648c2ecf20Sopenharmony_ci	struct fsl_mc_pdata *pdata = mci->pvt_info;
658c2ecf20Sopenharmony_ci	return sprintf(data, "0x%08x",
668c2ecf20Sopenharmony_ci		       ddr_in32(pdata->mc_vbase + FSL_MC_DATA_ERR_INJECT_HI));
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic ssize_t fsl_mc_inject_data_lo_show(struct device *dev,
708c2ecf20Sopenharmony_ci					  struct device_attribute *mattr,
718c2ecf20Sopenharmony_ci					      char *data)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci = to_mci(dev);
748c2ecf20Sopenharmony_ci	struct fsl_mc_pdata *pdata = mci->pvt_info;
758c2ecf20Sopenharmony_ci	return sprintf(data, "0x%08x",
768c2ecf20Sopenharmony_ci		       ddr_in32(pdata->mc_vbase + FSL_MC_DATA_ERR_INJECT_LO));
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic ssize_t fsl_mc_inject_ctrl_show(struct device *dev,
808c2ecf20Sopenharmony_ci				       struct device_attribute *mattr,
818c2ecf20Sopenharmony_ci					   char *data)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci = to_mci(dev);
848c2ecf20Sopenharmony_ci	struct fsl_mc_pdata *pdata = mci->pvt_info;
858c2ecf20Sopenharmony_ci	return sprintf(data, "0x%08x",
868c2ecf20Sopenharmony_ci		       ddr_in32(pdata->mc_vbase + FSL_MC_ECC_ERR_INJECT));
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic ssize_t fsl_mc_inject_data_hi_store(struct device *dev,
908c2ecf20Sopenharmony_ci					   struct device_attribute *mattr,
918c2ecf20Sopenharmony_ci					       const char *data, size_t count)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci = to_mci(dev);
948c2ecf20Sopenharmony_ci	struct fsl_mc_pdata *pdata = mci->pvt_info;
958c2ecf20Sopenharmony_ci	unsigned long val;
968c2ecf20Sopenharmony_ci	int rc;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (isdigit(*data)) {
998c2ecf20Sopenharmony_ci		rc = kstrtoul(data, 0, &val);
1008c2ecf20Sopenharmony_ci		if (rc)
1018c2ecf20Sopenharmony_ci			return rc;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci		ddr_out32(pdata->mc_vbase + FSL_MC_DATA_ERR_INJECT_HI, val);
1048c2ecf20Sopenharmony_ci		return count;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci	return 0;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic ssize_t fsl_mc_inject_data_lo_store(struct device *dev,
1108c2ecf20Sopenharmony_ci					   struct device_attribute *mattr,
1118c2ecf20Sopenharmony_ci					       const char *data, size_t count)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci = to_mci(dev);
1148c2ecf20Sopenharmony_ci	struct fsl_mc_pdata *pdata = mci->pvt_info;
1158c2ecf20Sopenharmony_ci	unsigned long val;
1168c2ecf20Sopenharmony_ci	int rc;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (isdigit(*data)) {
1198c2ecf20Sopenharmony_ci		rc = kstrtoul(data, 0, &val);
1208c2ecf20Sopenharmony_ci		if (rc)
1218c2ecf20Sopenharmony_ci			return rc;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		ddr_out32(pdata->mc_vbase + FSL_MC_DATA_ERR_INJECT_LO, val);
1248c2ecf20Sopenharmony_ci		return count;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci	return 0;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic ssize_t fsl_mc_inject_ctrl_store(struct device *dev,
1308c2ecf20Sopenharmony_ci					struct device_attribute *mattr,
1318c2ecf20Sopenharmony_ci					       const char *data, size_t count)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci = to_mci(dev);
1348c2ecf20Sopenharmony_ci	struct fsl_mc_pdata *pdata = mci->pvt_info;
1358c2ecf20Sopenharmony_ci	unsigned long val;
1368c2ecf20Sopenharmony_ci	int rc;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (isdigit(*data)) {
1398c2ecf20Sopenharmony_ci		rc = kstrtoul(data, 0, &val);
1408c2ecf20Sopenharmony_ci		if (rc)
1418c2ecf20Sopenharmony_ci			return rc;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		ddr_out32(pdata->mc_vbase + FSL_MC_ECC_ERR_INJECT, val);
1448c2ecf20Sopenharmony_ci		return count;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci	return 0;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic DEVICE_ATTR(inject_data_hi, S_IRUGO | S_IWUSR,
1508c2ecf20Sopenharmony_ci		   fsl_mc_inject_data_hi_show, fsl_mc_inject_data_hi_store);
1518c2ecf20Sopenharmony_cistatic DEVICE_ATTR(inject_data_lo, S_IRUGO | S_IWUSR,
1528c2ecf20Sopenharmony_ci		   fsl_mc_inject_data_lo_show, fsl_mc_inject_data_lo_store);
1538c2ecf20Sopenharmony_cistatic DEVICE_ATTR(inject_ctrl, S_IRUGO | S_IWUSR,
1548c2ecf20Sopenharmony_ci		   fsl_mc_inject_ctrl_show, fsl_mc_inject_ctrl_store);
1558c2ecf20Sopenharmony_ci#endif /* CONFIG_EDAC_DEBUG */
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic struct attribute *fsl_ddr_dev_attrs[] = {
1588c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG
1598c2ecf20Sopenharmony_ci	&dev_attr_inject_data_hi.attr,
1608c2ecf20Sopenharmony_ci	&dev_attr_inject_data_lo.attr,
1618c2ecf20Sopenharmony_ci	&dev_attr_inject_ctrl.attr,
1628c2ecf20Sopenharmony_ci#endif
1638c2ecf20Sopenharmony_ci	NULL
1648c2ecf20Sopenharmony_ci};
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(fsl_ddr_dev);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci/**************************** MC Err device ***************************/
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/*
1718c2ecf20Sopenharmony_ci * Taken from table 8-55 in the MPC8641 User's Manual and/or 9-61 in the
1728c2ecf20Sopenharmony_ci * MPC8572 User's Manual.  Each line represents a syndrome bit column as a
1738c2ecf20Sopenharmony_ci * 64-bit value, but split into an upper and lower 32-bit chunk.  The labels
1748c2ecf20Sopenharmony_ci * below correspond to Freescale's manuals.
1758c2ecf20Sopenharmony_ci */
1768c2ecf20Sopenharmony_cistatic unsigned int ecc_table[16] = {
1778c2ecf20Sopenharmony_ci	/* MSB           LSB */
1788c2ecf20Sopenharmony_ci	/* [0:31]    [32:63] */
1798c2ecf20Sopenharmony_ci	0xf00fe11e, 0xc33c0ff7,	/* Syndrome bit 7 */
1808c2ecf20Sopenharmony_ci	0x00ff00ff, 0x00fff0ff,
1818c2ecf20Sopenharmony_ci	0x0f0f0f0f, 0x0f0fff00,
1828c2ecf20Sopenharmony_ci	0x11113333, 0x7777000f,
1838c2ecf20Sopenharmony_ci	0x22224444, 0x8888222f,
1848c2ecf20Sopenharmony_ci	0x44448888, 0xffff4441,
1858c2ecf20Sopenharmony_ci	0x8888ffff, 0x11118882,
1868c2ecf20Sopenharmony_ci	0xffff1111, 0x22221114,	/* Syndrome bit 0 */
1878c2ecf20Sopenharmony_ci};
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci/*
1908c2ecf20Sopenharmony_ci * Calculate the correct ECC value for a 64-bit value specified by high:low
1918c2ecf20Sopenharmony_ci */
1928c2ecf20Sopenharmony_cistatic u8 calculate_ecc(u32 high, u32 low)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	u32 mask_low;
1958c2ecf20Sopenharmony_ci	u32 mask_high;
1968c2ecf20Sopenharmony_ci	int bit_cnt;
1978c2ecf20Sopenharmony_ci	u8 ecc = 0;
1988c2ecf20Sopenharmony_ci	int i;
1998c2ecf20Sopenharmony_ci	int j;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++) {
2028c2ecf20Sopenharmony_ci		mask_high = ecc_table[i * 2];
2038c2ecf20Sopenharmony_ci		mask_low = ecc_table[i * 2 + 1];
2048c2ecf20Sopenharmony_ci		bit_cnt = 0;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci		for (j = 0; j < 32; j++) {
2078c2ecf20Sopenharmony_ci			if ((mask_high >> j) & 1)
2088c2ecf20Sopenharmony_ci				bit_cnt ^= (high >> j) & 1;
2098c2ecf20Sopenharmony_ci			if ((mask_low >> j) & 1)
2108c2ecf20Sopenharmony_ci				bit_cnt ^= (low >> j) & 1;
2118c2ecf20Sopenharmony_ci		}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci		ecc |= bit_cnt << i;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return ecc;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci/*
2208c2ecf20Sopenharmony_ci * Create the syndrome code which is generated if the data line specified by
2218c2ecf20Sopenharmony_ci * 'bit' failed.  Eg generate an 8-bit codes seen in Table 8-55 in the MPC8641
2228c2ecf20Sopenharmony_ci * User's Manual and 9-61 in the MPC8572 User's Manual.
2238c2ecf20Sopenharmony_ci */
2248c2ecf20Sopenharmony_cistatic u8 syndrome_from_bit(unsigned int bit) {
2258c2ecf20Sopenharmony_ci	int i;
2268c2ecf20Sopenharmony_ci	u8 syndrome = 0;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/*
2298c2ecf20Sopenharmony_ci	 * Cycle through the upper or lower 32-bit portion of each value in
2308c2ecf20Sopenharmony_ci	 * ecc_table depending on if 'bit' is in the upper or lower half of
2318c2ecf20Sopenharmony_ci	 * 64-bit data.
2328c2ecf20Sopenharmony_ci	 */
2338c2ecf20Sopenharmony_ci	for (i = bit < 32; i < 16; i += 2)
2348c2ecf20Sopenharmony_ci		syndrome |= ((ecc_table[i] >> (bit % 32)) & 1) << (i / 2);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	return syndrome;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci/*
2408c2ecf20Sopenharmony_ci * Decode data and ecc syndrome to determine what went wrong
2418c2ecf20Sopenharmony_ci * Note: This can only decode single-bit errors
2428c2ecf20Sopenharmony_ci */
2438c2ecf20Sopenharmony_cistatic void sbe_ecc_decode(u32 cap_high, u32 cap_low, u32 cap_ecc,
2448c2ecf20Sopenharmony_ci		       int *bad_data_bit, int *bad_ecc_bit)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	int i;
2478c2ecf20Sopenharmony_ci	u8 syndrome;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	*bad_data_bit = -1;
2508c2ecf20Sopenharmony_ci	*bad_ecc_bit = -1;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	/*
2538c2ecf20Sopenharmony_ci	 * Calculate the ECC of the captured data and XOR it with the captured
2548c2ecf20Sopenharmony_ci	 * ECC to find an ECC syndrome value we can search for
2558c2ecf20Sopenharmony_ci	 */
2568c2ecf20Sopenharmony_ci	syndrome = calculate_ecc(cap_high, cap_low) ^ cap_ecc;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	/* Check if a data line is stuck... */
2598c2ecf20Sopenharmony_ci	for (i = 0; i < 64; i++) {
2608c2ecf20Sopenharmony_ci		if (syndrome == syndrome_from_bit(i)) {
2618c2ecf20Sopenharmony_ci			*bad_data_bit = i;
2628c2ecf20Sopenharmony_ci			return;
2638c2ecf20Sopenharmony_ci		}
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	/* If data is correct, check ECC bits for errors... */
2678c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++) {
2688c2ecf20Sopenharmony_ci		if ((syndrome >> i) & 0x1) {
2698c2ecf20Sopenharmony_ci			*bad_ecc_bit = i;
2708c2ecf20Sopenharmony_ci			return;
2718c2ecf20Sopenharmony_ci		}
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci#define make64(high, low) (((u64)(high) << 32) | (low))
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic void fsl_mc_check(struct mem_ctl_info *mci)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	struct fsl_mc_pdata *pdata = mci->pvt_info;
2808c2ecf20Sopenharmony_ci	struct csrow_info *csrow;
2818c2ecf20Sopenharmony_ci	u32 bus_width;
2828c2ecf20Sopenharmony_ci	u32 err_detect;
2838c2ecf20Sopenharmony_ci	u32 syndrome;
2848c2ecf20Sopenharmony_ci	u64 err_addr;
2858c2ecf20Sopenharmony_ci	u32 pfn;
2868c2ecf20Sopenharmony_ci	int row_index;
2878c2ecf20Sopenharmony_ci	u32 cap_high;
2888c2ecf20Sopenharmony_ci	u32 cap_low;
2898c2ecf20Sopenharmony_ci	int bad_data_bit;
2908c2ecf20Sopenharmony_ci	int bad_ecc_bit;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	err_detect = ddr_in32(pdata->mc_vbase + FSL_MC_ERR_DETECT);
2938c2ecf20Sopenharmony_ci	if (!err_detect)
2948c2ecf20Sopenharmony_ci		return;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	fsl_mc_printk(mci, KERN_ERR, "Err Detect Register: %#8.8x\n",
2978c2ecf20Sopenharmony_ci		      err_detect);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	/* no more processing if not ECC bit errors */
3008c2ecf20Sopenharmony_ci	if (!(err_detect & (DDR_EDE_SBE | DDR_EDE_MBE))) {
3018c2ecf20Sopenharmony_ci		ddr_out32(pdata->mc_vbase + FSL_MC_ERR_DETECT, err_detect);
3028c2ecf20Sopenharmony_ci		return;
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	syndrome = ddr_in32(pdata->mc_vbase + FSL_MC_CAPTURE_ECC);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	/* Mask off appropriate bits of syndrome based on bus width */
3088c2ecf20Sopenharmony_ci	bus_width = (ddr_in32(pdata->mc_vbase + FSL_MC_DDR_SDRAM_CFG) &
3098c2ecf20Sopenharmony_ci		     DSC_DBW_MASK) ? 32 : 64;
3108c2ecf20Sopenharmony_ci	if (bus_width == 64)
3118c2ecf20Sopenharmony_ci		syndrome &= 0xff;
3128c2ecf20Sopenharmony_ci	else
3138c2ecf20Sopenharmony_ci		syndrome &= 0xffff;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	err_addr = make64(
3168c2ecf20Sopenharmony_ci		ddr_in32(pdata->mc_vbase + FSL_MC_CAPTURE_EXT_ADDRESS),
3178c2ecf20Sopenharmony_ci		ddr_in32(pdata->mc_vbase + FSL_MC_CAPTURE_ADDRESS));
3188c2ecf20Sopenharmony_ci	pfn = err_addr >> PAGE_SHIFT;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	for (row_index = 0; row_index < mci->nr_csrows; row_index++) {
3218c2ecf20Sopenharmony_ci		csrow = mci->csrows[row_index];
3228c2ecf20Sopenharmony_ci		if ((pfn >= csrow->first_page) && (pfn <= csrow->last_page))
3238c2ecf20Sopenharmony_ci			break;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	cap_high = ddr_in32(pdata->mc_vbase + FSL_MC_CAPTURE_DATA_HI);
3278c2ecf20Sopenharmony_ci	cap_low = ddr_in32(pdata->mc_vbase + FSL_MC_CAPTURE_DATA_LO);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	/*
3308c2ecf20Sopenharmony_ci	 * Analyze single-bit errors on 64-bit wide buses
3318c2ecf20Sopenharmony_ci	 * TODO: Add support for 32-bit wide buses
3328c2ecf20Sopenharmony_ci	 */
3338c2ecf20Sopenharmony_ci	if ((err_detect & DDR_EDE_SBE) && (bus_width == 64)) {
3348c2ecf20Sopenharmony_ci		sbe_ecc_decode(cap_high, cap_low, syndrome,
3358c2ecf20Sopenharmony_ci				&bad_data_bit, &bad_ecc_bit);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci		if (bad_data_bit != -1)
3388c2ecf20Sopenharmony_ci			fsl_mc_printk(mci, KERN_ERR,
3398c2ecf20Sopenharmony_ci				"Faulty Data bit: %d\n", bad_data_bit);
3408c2ecf20Sopenharmony_ci		if (bad_ecc_bit != -1)
3418c2ecf20Sopenharmony_ci			fsl_mc_printk(mci, KERN_ERR,
3428c2ecf20Sopenharmony_ci				"Faulty ECC bit: %d\n", bad_ecc_bit);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci		fsl_mc_printk(mci, KERN_ERR,
3458c2ecf20Sopenharmony_ci			"Expected Data / ECC:\t%#8.8x_%08x / %#2.2x\n",
3468c2ecf20Sopenharmony_ci			cap_high ^ (1 << (bad_data_bit - 32)),
3478c2ecf20Sopenharmony_ci			cap_low ^ (1 << bad_data_bit),
3488c2ecf20Sopenharmony_ci			syndrome ^ (1 << bad_ecc_bit));
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	fsl_mc_printk(mci, KERN_ERR,
3528c2ecf20Sopenharmony_ci			"Captured Data / ECC:\t%#8.8x_%08x / %#2.2x\n",
3538c2ecf20Sopenharmony_ci			cap_high, cap_low, syndrome);
3548c2ecf20Sopenharmony_ci	fsl_mc_printk(mci, KERN_ERR, "Err addr: %#8.8llx\n", err_addr);
3558c2ecf20Sopenharmony_ci	fsl_mc_printk(mci, KERN_ERR, "PFN: %#8.8x\n", pfn);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	/* we are out of range */
3588c2ecf20Sopenharmony_ci	if (row_index == mci->nr_csrows)
3598c2ecf20Sopenharmony_ci		fsl_mc_printk(mci, KERN_ERR, "PFN out of range!\n");
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (err_detect & DDR_EDE_SBE)
3628c2ecf20Sopenharmony_ci		edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
3638c2ecf20Sopenharmony_ci				     pfn, err_addr & ~PAGE_MASK, syndrome,
3648c2ecf20Sopenharmony_ci				     row_index, 0, -1,
3658c2ecf20Sopenharmony_ci				     mci->ctl_name, "");
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	if (err_detect & DDR_EDE_MBE)
3688c2ecf20Sopenharmony_ci		edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
3698c2ecf20Sopenharmony_ci				     pfn, err_addr & ~PAGE_MASK, syndrome,
3708c2ecf20Sopenharmony_ci				     row_index, 0, -1,
3718c2ecf20Sopenharmony_ci				     mci->ctl_name, "");
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	ddr_out32(pdata->mc_vbase + FSL_MC_ERR_DETECT, err_detect);
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic irqreturn_t fsl_mc_isr(int irq, void *dev_id)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci = dev_id;
3798c2ecf20Sopenharmony_ci	struct fsl_mc_pdata *pdata = mci->pvt_info;
3808c2ecf20Sopenharmony_ci	u32 err_detect;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	err_detect = ddr_in32(pdata->mc_vbase + FSL_MC_ERR_DETECT);
3838c2ecf20Sopenharmony_ci	if (!err_detect)
3848c2ecf20Sopenharmony_ci		return IRQ_NONE;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	fsl_mc_check(mci);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic void fsl_ddr_init_csrows(struct mem_ctl_info *mci)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	struct fsl_mc_pdata *pdata = mci->pvt_info;
3948c2ecf20Sopenharmony_ci	struct csrow_info *csrow;
3958c2ecf20Sopenharmony_ci	struct dimm_info *dimm;
3968c2ecf20Sopenharmony_ci	u32 sdram_ctl;
3978c2ecf20Sopenharmony_ci	u32 sdtype;
3988c2ecf20Sopenharmony_ci	enum mem_type mtype;
3998c2ecf20Sopenharmony_ci	u32 cs_bnds;
4008c2ecf20Sopenharmony_ci	int index;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	sdram_ctl = ddr_in32(pdata->mc_vbase + FSL_MC_DDR_SDRAM_CFG);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	sdtype = sdram_ctl & DSC_SDTYPE_MASK;
4058c2ecf20Sopenharmony_ci	if (sdram_ctl & DSC_RD_EN) {
4068c2ecf20Sopenharmony_ci		switch (sdtype) {
4078c2ecf20Sopenharmony_ci		case 0x02000000:
4088c2ecf20Sopenharmony_ci			mtype = MEM_RDDR;
4098c2ecf20Sopenharmony_ci			break;
4108c2ecf20Sopenharmony_ci		case 0x03000000:
4118c2ecf20Sopenharmony_ci			mtype = MEM_RDDR2;
4128c2ecf20Sopenharmony_ci			break;
4138c2ecf20Sopenharmony_ci		case 0x07000000:
4148c2ecf20Sopenharmony_ci			mtype = MEM_RDDR3;
4158c2ecf20Sopenharmony_ci			break;
4168c2ecf20Sopenharmony_ci		case 0x05000000:
4178c2ecf20Sopenharmony_ci			mtype = MEM_RDDR4;
4188c2ecf20Sopenharmony_ci			break;
4198c2ecf20Sopenharmony_ci		default:
4208c2ecf20Sopenharmony_ci			mtype = MEM_UNKNOWN;
4218c2ecf20Sopenharmony_ci			break;
4228c2ecf20Sopenharmony_ci		}
4238c2ecf20Sopenharmony_ci	} else {
4248c2ecf20Sopenharmony_ci		switch (sdtype) {
4258c2ecf20Sopenharmony_ci		case 0x02000000:
4268c2ecf20Sopenharmony_ci			mtype = MEM_DDR;
4278c2ecf20Sopenharmony_ci			break;
4288c2ecf20Sopenharmony_ci		case 0x03000000:
4298c2ecf20Sopenharmony_ci			mtype = MEM_DDR2;
4308c2ecf20Sopenharmony_ci			break;
4318c2ecf20Sopenharmony_ci		case 0x07000000:
4328c2ecf20Sopenharmony_ci			mtype = MEM_DDR3;
4338c2ecf20Sopenharmony_ci			break;
4348c2ecf20Sopenharmony_ci		case 0x05000000:
4358c2ecf20Sopenharmony_ci			mtype = MEM_DDR4;
4368c2ecf20Sopenharmony_ci			break;
4378c2ecf20Sopenharmony_ci		default:
4388c2ecf20Sopenharmony_ci			mtype = MEM_UNKNOWN;
4398c2ecf20Sopenharmony_ci			break;
4408c2ecf20Sopenharmony_ci		}
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	for (index = 0; index < mci->nr_csrows; index++) {
4448c2ecf20Sopenharmony_ci		u32 start;
4458c2ecf20Sopenharmony_ci		u32 end;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci		csrow = mci->csrows[index];
4488c2ecf20Sopenharmony_ci		dimm = csrow->channels[0]->dimm;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci		cs_bnds = ddr_in32(pdata->mc_vbase + FSL_MC_CS_BNDS_0 +
4518c2ecf20Sopenharmony_ci				   (index * FSL_MC_CS_BNDS_OFS));
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci		start = (cs_bnds & 0xffff0000) >> 16;
4548c2ecf20Sopenharmony_ci		end   = (cs_bnds & 0x0000ffff);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci		if (start == end)
4578c2ecf20Sopenharmony_ci			continue;	/* not populated */
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci		start <<= (24 - PAGE_SHIFT);
4608c2ecf20Sopenharmony_ci		end   <<= (24 - PAGE_SHIFT);
4618c2ecf20Sopenharmony_ci		end    |= (1 << (24 - PAGE_SHIFT)) - 1;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci		csrow->first_page = start;
4648c2ecf20Sopenharmony_ci		csrow->last_page = end;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci		dimm->nr_pages = end + 1 - start;
4678c2ecf20Sopenharmony_ci		dimm->grain = 8;
4688c2ecf20Sopenharmony_ci		dimm->mtype = mtype;
4698c2ecf20Sopenharmony_ci		dimm->dtype = DEV_UNKNOWN;
4708c2ecf20Sopenharmony_ci		if (sdram_ctl & DSC_X32_EN)
4718c2ecf20Sopenharmony_ci			dimm->dtype = DEV_X32;
4728c2ecf20Sopenharmony_ci		dimm->edac_mode = EDAC_SECDED;
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ciint fsl_mc_err_probe(struct platform_device *op)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci;
4798c2ecf20Sopenharmony_ci	struct edac_mc_layer layers[2];
4808c2ecf20Sopenharmony_ci	struct fsl_mc_pdata *pdata;
4818c2ecf20Sopenharmony_ci	struct resource r;
4828c2ecf20Sopenharmony_ci	u32 sdram_ctl;
4838c2ecf20Sopenharmony_ci	int res;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	if (!devres_open_group(&op->dev, fsl_mc_err_probe, GFP_KERNEL))
4868c2ecf20Sopenharmony_ci		return -ENOMEM;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
4898c2ecf20Sopenharmony_ci	layers[0].size = 4;
4908c2ecf20Sopenharmony_ci	layers[0].is_virt_csrow = true;
4918c2ecf20Sopenharmony_ci	layers[1].type = EDAC_MC_LAYER_CHANNEL;
4928c2ecf20Sopenharmony_ci	layers[1].size = 1;
4938c2ecf20Sopenharmony_ci	layers[1].is_virt_csrow = false;
4948c2ecf20Sopenharmony_ci	mci = edac_mc_alloc(edac_mc_idx, ARRAY_SIZE(layers), layers,
4958c2ecf20Sopenharmony_ci			    sizeof(*pdata));
4968c2ecf20Sopenharmony_ci	if (!mci) {
4978c2ecf20Sopenharmony_ci		devres_release_group(&op->dev, fsl_mc_err_probe);
4988c2ecf20Sopenharmony_ci		return -ENOMEM;
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	pdata = mci->pvt_info;
5028c2ecf20Sopenharmony_ci	pdata->name = "fsl_mc_err";
5038c2ecf20Sopenharmony_ci	mci->pdev = &op->dev;
5048c2ecf20Sopenharmony_ci	pdata->edac_idx = edac_mc_idx++;
5058c2ecf20Sopenharmony_ci	dev_set_drvdata(mci->pdev, mci);
5068c2ecf20Sopenharmony_ci	mci->ctl_name = pdata->name;
5078c2ecf20Sopenharmony_ci	mci->dev_name = pdata->name;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	/*
5108c2ecf20Sopenharmony_ci	 * Get the endianness of DDR controller registers.
5118c2ecf20Sopenharmony_ci	 * Default is big endian.
5128c2ecf20Sopenharmony_ci	 */
5138c2ecf20Sopenharmony_ci	little_endian = of_property_read_bool(op->dev.of_node, "little-endian");
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	res = of_address_to_resource(op->dev.of_node, 0, &r);
5168c2ecf20Sopenharmony_ci	if (res) {
5178c2ecf20Sopenharmony_ci		pr_err("%s: Unable to get resource for MC err regs\n",
5188c2ecf20Sopenharmony_ci		       __func__);
5198c2ecf20Sopenharmony_ci		goto err;
5208c2ecf20Sopenharmony_ci	}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	if (!devm_request_mem_region(&op->dev, r.start, resource_size(&r),
5238c2ecf20Sopenharmony_ci				     pdata->name)) {
5248c2ecf20Sopenharmony_ci		pr_err("%s: Error while requesting mem region\n",
5258c2ecf20Sopenharmony_ci		       __func__);
5268c2ecf20Sopenharmony_ci		res = -EBUSY;
5278c2ecf20Sopenharmony_ci		goto err;
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	pdata->mc_vbase = devm_ioremap(&op->dev, r.start, resource_size(&r));
5318c2ecf20Sopenharmony_ci	if (!pdata->mc_vbase) {
5328c2ecf20Sopenharmony_ci		pr_err("%s: Unable to setup MC err regs\n", __func__);
5338c2ecf20Sopenharmony_ci		res = -ENOMEM;
5348c2ecf20Sopenharmony_ci		goto err;
5358c2ecf20Sopenharmony_ci	}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	sdram_ctl = ddr_in32(pdata->mc_vbase + FSL_MC_DDR_SDRAM_CFG);
5388c2ecf20Sopenharmony_ci	if (!(sdram_ctl & DSC_ECC_EN)) {
5398c2ecf20Sopenharmony_ci		/* no ECC */
5408c2ecf20Sopenharmony_ci		pr_warn("%s: No ECC DIMMs discovered\n", __func__);
5418c2ecf20Sopenharmony_ci		res = -ENODEV;
5428c2ecf20Sopenharmony_ci		goto err;
5438c2ecf20Sopenharmony_ci	}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	edac_dbg(3, "init mci\n");
5468c2ecf20Sopenharmony_ci	mci->mtype_cap = MEM_FLAG_DDR | MEM_FLAG_RDDR |
5478c2ecf20Sopenharmony_ci			 MEM_FLAG_DDR2 | MEM_FLAG_RDDR2 |
5488c2ecf20Sopenharmony_ci			 MEM_FLAG_DDR3 | MEM_FLAG_RDDR3 |
5498c2ecf20Sopenharmony_ci			 MEM_FLAG_DDR4 | MEM_FLAG_RDDR4;
5508c2ecf20Sopenharmony_ci	mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
5518c2ecf20Sopenharmony_ci	mci->edac_cap = EDAC_FLAG_SECDED;
5528c2ecf20Sopenharmony_ci	mci->mod_name = EDAC_MOD_STR;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	if (edac_op_state == EDAC_OPSTATE_POLL)
5558c2ecf20Sopenharmony_ci		mci->edac_check = fsl_mc_check;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	mci->ctl_page_to_phys = NULL;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	mci->scrub_mode = SCRUB_SW_SRC;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	fsl_ddr_init_csrows(mci);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	/* store the original error disable bits */
5648c2ecf20Sopenharmony_ci	orig_ddr_err_disable = ddr_in32(pdata->mc_vbase + FSL_MC_ERR_DISABLE);
5658c2ecf20Sopenharmony_ci	ddr_out32(pdata->mc_vbase + FSL_MC_ERR_DISABLE, 0);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	/* clear all error bits */
5688c2ecf20Sopenharmony_ci	ddr_out32(pdata->mc_vbase + FSL_MC_ERR_DETECT, ~0);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	res = edac_mc_add_mc_with_groups(mci, fsl_ddr_dev_groups);
5718c2ecf20Sopenharmony_ci	if (res) {
5728c2ecf20Sopenharmony_ci		edac_dbg(3, "failed edac_mc_add_mc()\n");
5738c2ecf20Sopenharmony_ci		goto err;
5748c2ecf20Sopenharmony_ci	}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	if (edac_op_state == EDAC_OPSTATE_INT) {
5778c2ecf20Sopenharmony_ci		ddr_out32(pdata->mc_vbase + FSL_MC_ERR_INT_EN,
5788c2ecf20Sopenharmony_ci			  DDR_EIE_MBEE | DDR_EIE_SBEE);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci		/* store the original error management threshold */
5818c2ecf20Sopenharmony_ci		orig_ddr_err_sbe = ddr_in32(pdata->mc_vbase +
5828c2ecf20Sopenharmony_ci					    FSL_MC_ERR_SBE) & 0xff0000;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci		/* set threshold to 1 error per interrupt */
5858c2ecf20Sopenharmony_ci		ddr_out32(pdata->mc_vbase + FSL_MC_ERR_SBE, 0x10000);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci		/* register interrupts */
5888c2ecf20Sopenharmony_ci		pdata->irq = platform_get_irq(op, 0);
5898c2ecf20Sopenharmony_ci		res = devm_request_irq(&op->dev, pdata->irq,
5908c2ecf20Sopenharmony_ci				       fsl_mc_isr,
5918c2ecf20Sopenharmony_ci				       IRQF_SHARED,
5928c2ecf20Sopenharmony_ci				       "[EDAC] MC err", mci);
5938c2ecf20Sopenharmony_ci		if (res < 0) {
5948c2ecf20Sopenharmony_ci			pr_err("%s: Unable to request irq %d for FSL DDR DRAM ERR\n",
5958c2ecf20Sopenharmony_ci			       __func__, pdata->irq);
5968c2ecf20Sopenharmony_ci			res = -ENODEV;
5978c2ecf20Sopenharmony_ci			goto err2;
5988c2ecf20Sopenharmony_ci		}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci		pr_info(EDAC_MOD_STR " acquired irq %d for MC\n",
6018c2ecf20Sopenharmony_ci		       pdata->irq);
6028c2ecf20Sopenharmony_ci	}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	devres_remove_group(&op->dev, fsl_mc_err_probe);
6058c2ecf20Sopenharmony_ci	edac_dbg(3, "success\n");
6068c2ecf20Sopenharmony_ci	pr_info(EDAC_MOD_STR " MC err registered\n");
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	return 0;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cierr2:
6118c2ecf20Sopenharmony_ci	edac_mc_del_mc(&op->dev);
6128c2ecf20Sopenharmony_cierr:
6138c2ecf20Sopenharmony_ci	devres_release_group(&op->dev, fsl_mc_err_probe);
6148c2ecf20Sopenharmony_ci	edac_mc_free(mci);
6158c2ecf20Sopenharmony_ci	return res;
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ciint fsl_mc_err_remove(struct platform_device *op)
6198c2ecf20Sopenharmony_ci{
6208c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci = dev_get_drvdata(&op->dev);
6218c2ecf20Sopenharmony_ci	struct fsl_mc_pdata *pdata = mci->pvt_info;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	edac_dbg(0, "\n");
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	if (edac_op_state == EDAC_OPSTATE_INT) {
6268c2ecf20Sopenharmony_ci		ddr_out32(pdata->mc_vbase + FSL_MC_ERR_INT_EN, 0);
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	ddr_out32(pdata->mc_vbase + FSL_MC_ERR_DISABLE,
6308c2ecf20Sopenharmony_ci		  orig_ddr_err_disable);
6318c2ecf20Sopenharmony_ci	ddr_out32(pdata->mc_vbase + FSL_MC_ERR_SBE, orig_ddr_err_sbe);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	edac_mc_del_mc(&op->dev);
6348c2ecf20Sopenharmony_ci	edac_mc_free(mci);
6358c2ecf20Sopenharmony_ci	return 0;
6368c2ecf20Sopenharmony_ci}
637