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