110590a9dSQiuxu Zhuo // SPDX-License-Identifier: GPL-2.0 210590a9dSQiuxu Zhuo /* 310590a9dSQiuxu Zhuo * Driver for Intel client SoC with integrated memory controller using IBECC 410590a9dSQiuxu Zhuo * 510590a9dSQiuxu Zhuo * Copyright (C) 2020 Intel Corporation 610590a9dSQiuxu Zhuo * 710590a9dSQiuxu Zhuo * The In-Band ECC (IBECC) IP provides ECC protection to all or specific 810590a9dSQiuxu Zhuo * regions of the physical memory space. It's used for memory controllers 910590a9dSQiuxu Zhuo * that don't support the out-of-band ECC which often needs an additional 1010590a9dSQiuxu Zhuo * storage device to each channel for storing ECC data. 1110590a9dSQiuxu Zhuo */ 1210590a9dSQiuxu Zhuo 1310590a9dSQiuxu Zhuo #include <linux/module.h> 1410590a9dSQiuxu Zhuo #include <linux/init.h> 1510590a9dSQiuxu Zhuo #include <linux/pci.h> 1610590a9dSQiuxu Zhuo #include <linux/slab.h> 1710590a9dSQiuxu Zhuo #include <linux/irq_work.h> 1810590a9dSQiuxu Zhuo #include <linux/llist.h> 1910590a9dSQiuxu Zhuo #include <linux/genalloc.h> 2010590a9dSQiuxu Zhuo #include <linux/edac.h> 2110590a9dSQiuxu Zhuo #include <linux/bits.h> 2210590a9dSQiuxu Zhuo #include <linux/io.h> 2310590a9dSQiuxu Zhuo #include <asm/mach_traps.h> 2410590a9dSQiuxu Zhuo #include <asm/nmi.h> 250b7338b2SQiuxu Zhuo #include <asm/mce.h> 2610590a9dSQiuxu Zhuo 2710590a9dSQiuxu Zhuo #include "edac_mc.h" 2810590a9dSQiuxu Zhuo #include "edac_module.h" 2910590a9dSQiuxu Zhuo 30ad774bd5SQiuxu Zhuo #define IGEN6_REVISION "v2.5" 3110590a9dSQiuxu Zhuo 3210590a9dSQiuxu Zhuo #define EDAC_MOD_STR "igen6_edac" 3310590a9dSQiuxu Zhuo #define IGEN6_NMI_NAME "igen6_ibecc" 3410590a9dSQiuxu Zhuo 3510590a9dSQiuxu Zhuo /* Debug macros */ 3610590a9dSQiuxu Zhuo #define igen6_printk(level, fmt, arg...) \ 3710590a9dSQiuxu Zhuo edac_printk(level, "igen6", fmt, ##arg) 3810590a9dSQiuxu Zhuo 3910590a9dSQiuxu Zhuo #define igen6_mc_printk(mci, level, fmt, arg...) \ 4010590a9dSQiuxu Zhuo edac_mc_chipset_printk(mci, level, "igen6", fmt, ##arg) 4110590a9dSQiuxu Zhuo 4210590a9dSQiuxu Zhuo #define GET_BITFIELD(v, lo, hi) (((v) & GENMASK_ULL(hi, lo)) >> (lo)) 4310590a9dSQiuxu Zhuo 440b7338b2SQiuxu Zhuo #define NUM_IMC 2 /* Max memory controllers */ 4510590a9dSQiuxu Zhuo #define NUM_CHANNELS 2 /* Max channels */ 4610590a9dSQiuxu Zhuo #define NUM_DIMMS 2 /* Max DIMMs per channel */ 4710590a9dSQiuxu Zhuo 4810590a9dSQiuxu Zhuo #define _4GB BIT_ULL(32) 4910590a9dSQiuxu Zhuo 5010590a9dSQiuxu Zhuo /* Size of physical memory */ 5110590a9dSQiuxu Zhuo #define TOM_OFFSET 0xa0 5210590a9dSQiuxu Zhuo /* Top of low usable DRAM */ 5310590a9dSQiuxu Zhuo #define TOLUD_OFFSET 0xbc 5410590a9dSQiuxu Zhuo /* Capability register C */ 5510590a9dSQiuxu Zhuo #define CAPID_C_OFFSET 0xec 5610590a9dSQiuxu Zhuo #define CAPID_C_IBECC BIT(15) 5710590a9dSQiuxu Zhuo 580b7338b2SQiuxu Zhuo /* Capability register E */ 590b7338b2SQiuxu Zhuo #define CAPID_E_OFFSET 0xf0 600b7338b2SQiuxu Zhuo #define CAPID_E_IBECC BIT(12) 610b7338b2SQiuxu Zhuo 6210590a9dSQiuxu Zhuo /* Error Status */ 6310590a9dSQiuxu Zhuo #define ERRSTS_OFFSET 0xc8 6410590a9dSQiuxu Zhuo #define ERRSTS_CE BIT_ULL(6) 6510590a9dSQiuxu Zhuo #define ERRSTS_UE BIT_ULL(7) 6610590a9dSQiuxu Zhuo 6710590a9dSQiuxu Zhuo /* Error Command */ 6810590a9dSQiuxu Zhuo #define ERRCMD_OFFSET 0xca 6910590a9dSQiuxu Zhuo #define ERRCMD_CE BIT_ULL(6) 7010590a9dSQiuxu Zhuo #define ERRCMD_UE BIT_ULL(7) 7110590a9dSQiuxu Zhuo 7210590a9dSQiuxu Zhuo /* IBECC MMIO base address */ 7310590a9dSQiuxu Zhuo #define IBECC_BASE (res_cfg->ibecc_base) 7410590a9dSQiuxu Zhuo #define IBECC_ACTIVATE_OFFSET IBECC_BASE 7510590a9dSQiuxu Zhuo #define IBECC_ACTIVATE_EN BIT(0) 7610590a9dSQiuxu Zhuo 7710590a9dSQiuxu Zhuo /* IBECC error log */ 78ad774bd5SQiuxu Zhuo #define ECC_ERROR_LOG_OFFSET (IBECC_BASE + res_cfg->ibecc_error_log_offset) 7910590a9dSQiuxu Zhuo #define ECC_ERROR_LOG_CE BIT_ULL(62) 8010590a9dSQiuxu Zhuo #define ECC_ERROR_LOG_UE BIT_ULL(63) 8110590a9dSQiuxu Zhuo #define ECC_ERROR_LOG_ADDR_SHIFT 5 8210590a9dSQiuxu Zhuo #define ECC_ERROR_LOG_ADDR(v) GET_BITFIELD(v, 5, 38) 8310590a9dSQiuxu Zhuo #define ECC_ERROR_LOG_SYND(v) GET_BITFIELD(v, 46, 61) 8410590a9dSQiuxu Zhuo 8510590a9dSQiuxu Zhuo /* Host MMIO base address */ 8610590a9dSQiuxu Zhuo #define MCHBAR_OFFSET 0x48 8710590a9dSQiuxu Zhuo #define MCHBAR_EN BIT_ULL(0) 8810590a9dSQiuxu Zhuo #define MCHBAR_BASE(v) (GET_BITFIELD(v, 16, 38) << 16) 8910590a9dSQiuxu Zhuo #define MCHBAR_SIZE 0x10000 9010590a9dSQiuxu Zhuo 9110590a9dSQiuxu Zhuo /* Parameters for the channel decode stage */ 92ad774bd5SQiuxu Zhuo #define IMC_BASE (res_cfg->imc_base) 93ad774bd5SQiuxu Zhuo #define MAD_INTER_CHANNEL_OFFSET IMC_BASE 9410590a9dSQiuxu Zhuo #define MAD_INTER_CHANNEL_DDR_TYPE(v) GET_BITFIELD(v, 0, 2) 9510590a9dSQiuxu Zhuo #define MAD_INTER_CHANNEL_ECHM(v) GET_BITFIELD(v, 3, 3) 9610590a9dSQiuxu Zhuo #define MAD_INTER_CHANNEL_CH_L_MAP(v) GET_BITFIELD(v, 4, 4) 9710590a9dSQiuxu Zhuo #define MAD_INTER_CHANNEL_CH_S_SIZE(v) ((u64)GET_BITFIELD(v, 12, 19) << 29) 9810590a9dSQiuxu Zhuo 9910590a9dSQiuxu Zhuo /* Parameters for DRAM decode stage */ 100ad774bd5SQiuxu Zhuo #define MAD_INTRA_CH0_OFFSET (IMC_BASE + 4) 10110590a9dSQiuxu Zhuo #define MAD_INTRA_CH_DIMM_L_MAP(v) GET_BITFIELD(v, 0, 0) 10210590a9dSQiuxu Zhuo 10310590a9dSQiuxu Zhuo /* DIMM characteristics */ 104ad774bd5SQiuxu Zhuo #define MAD_DIMM_CH0_OFFSET (IMC_BASE + 0xc) 10510590a9dSQiuxu Zhuo #define MAD_DIMM_CH_DIMM_L_SIZE(v) ((u64)GET_BITFIELD(v, 0, 6) << 29) 10610590a9dSQiuxu Zhuo #define MAD_DIMM_CH_DLW(v) GET_BITFIELD(v, 7, 8) 10710590a9dSQiuxu Zhuo #define MAD_DIMM_CH_DIMM_S_SIZE(v) ((u64)GET_BITFIELD(v, 16, 22) << 29) 10810590a9dSQiuxu Zhuo #define MAD_DIMM_CH_DSW(v) GET_BITFIELD(v, 24, 25) 10910590a9dSQiuxu Zhuo 110ad774bd5SQiuxu Zhuo /* Hash for memory controller selection */ 111ad774bd5SQiuxu Zhuo #define MAD_MC_HASH_OFFSET (IMC_BASE + 0x1b8) 112ad774bd5SQiuxu Zhuo #define MAC_MC_HASH_LSB(v) GET_BITFIELD(v, 1, 3) 113ad774bd5SQiuxu Zhuo 11410590a9dSQiuxu Zhuo /* Hash for channel selection */ 115ad774bd5SQiuxu Zhuo #define CHANNEL_HASH_OFFSET (IMC_BASE + 0x24) 11610590a9dSQiuxu Zhuo /* Hash for enhanced channel selection */ 117ad774bd5SQiuxu Zhuo #define CHANNEL_EHASH_OFFSET (IMC_BASE + 0x28) 11810590a9dSQiuxu Zhuo #define CHANNEL_HASH_MASK(v) (GET_BITFIELD(v, 6, 19) << 6) 11910590a9dSQiuxu Zhuo #define CHANNEL_HASH_LSB_MASK_BIT(v) GET_BITFIELD(v, 24, 26) 12010590a9dSQiuxu Zhuo #define CHANNEL_HASH_MODE(v) GET_BITFIELD(v, 28, 28) 12110590a9dSQiuxu Zhuo 1220b7338b2SQiuxu Zhuo /* Parameters for memory slice decode stage */ 1230b7338b2SQiuxu Zhuo #define MEM_SLICE_HASH_MASK(v) (GET_BITFIELD(v, 6, 19) << 6) 1240b7338b2SQiuxu Zhuo #define MEM_SLICE_HASH_LSB_MASK_BIT(v) GET_BITFIELD(v, 24, 26) 1250b7338b2SQiuxu Zhuo 12610590a9dSQiuxu Zhuo static struct res_config { 1270b7338b2SQiuxu Zhuo bool machine_check; 12810590a9dSQiuxu Zhuo int num_imc; 129ad774bd5SQiuxu Zhuo u32 imc_base; 1300b7338b2SQiuxu Zhuo u32 cmf_base; 1310b7338b2SQiuxu Zhuo u32 cmf_size; 1320b7338b2SQiuxu Zhuo u32 ms_hash_offset; 13310590a9dSQiuxu Zhuo u32 ibecc_base; 134ad774bd5SQiuxu Zhuo u32 ibecc_error_log_offset; 13510590a9dSQiuxu Zhuo bool (*ibecc_available)(struct pci_dev *pdev); 13610590a9dSQiuxu Zhuo /* Convert error address logged in IBECC to system physical address */ 1370b7338b2SQiuxu Zhuo u64 (*err_addr_to_sys_addr)(u64 eaddr, int mc); 13810590a9dSQiuxu Zhuo /* Convert error address logged in IBECC to integrated memory controller address */ 139ad774bd5SQiuxu Zhuo u64 (*err_addr_to_imc_addr)(u64 eaddr, int mc); 14010590a9dSQiuxu Zhuo } *res_cfg; 14110590a9dSQiuxu Zhuo 14210590a9dSQiuxu Zhuo struct igen6_imc { 14310590a9dSQiuxu Zhuo int mc; 14410590a9dSQiuxu Zhuo struct mem_ctl_info *mci; 14510590a9dSQiuxu Zhuo struct pci_dev *pdev; 14610590a9dSQiuxu Zhuo struct device dev; 14710590a9dSQiuxu Zhuo void __iomem *window; 1480b7338b2SQiuxu Zhuo u64 size; 14910590a9dSQiuxu Zhuo u64 ch_s_size; 15010590a9dSQiuxu Zhuo int ch_l_map; 15110590a9dSQiuxu Zhuo u64 dimm_s_size[NUM_CHANNELS]; 15210590a9dSQiuxu Zhuo u64 dimm_l_size[NUM_CHANNELS]; 15310590a9dSQiuxu Zhuo int dimm_l_map[NUM_CHANNELS]; 15410590a9dSQiuxu Zhuo }; 15510590a9dSQiuxu Zhuo 15610590a9dSQiuxu Zhuo static struct igen6_pvt { 15710590a9dSQiuxu Zhuo struct igen6_imc imc[NUM_IMC]; 1580b7338b2SQiuxu Zhuo u64 ms_hash; 1590b7338b2SQiuxu Zhuo u64 ms_s_size; 1600b7338b2SQiuxu Zhuo int ms_l_map; 16110590a9dSQiuxu Zhuo } *igen6_pvt; 16210590a9dSQiuxu Zhuo 16310590a9dSQiuxu Zhuo /* The top of low usable DRAM */ 16410590a9dSQiuxu Zhuo static u32 igen6_tolud; 16510590a9dSQiuxu Zhuo /* The size of physical memory */ 16610590a9dSQiuxu Zhuo static u64 igen6_tom; 16710590a9dSQiuxu Zhuo 16810590a9dSQiuxu Zhuo struct decoded_addr { 16910590a9dSQiuxu Zhuo int mc; 17010590a9dSQiuxu Zhuo u64 imc_addr; 17110590a9dSQiuxu Zhuo u64 sys_addr; 17210590a9dSQiuxu Zhuo int channel_idx; 17310590a9dSQiuxu Zhuo u64 channel_addr; 17410590a9dSQiuxu Zhuo int sub_channel_idx; 17510590a9dSQiuxu Zhuo u64 sub_channel_addr; 17610590a9dSQiuxu Zhuo }; 17710590a9dSQiuxu Zhuo 17810590a9dSQiuxu Zhuo struct ecclog_node { 17910590a9dSQiuxu Zhuo struct llist_node llnode; 18010590a9dSQiuxu Zhuo int mc; 18110590a9dSQiuxu Zhuo u64 ecclog; 18210590a9dSQiuxu Zhuo }; 18310590a9dSQiuxu Zhuo 18410590a9dSQiuxu Zhuo /* 18510590a9dSQiuxu Zhuo * In the NMI handler, the driver uses the lock-less memory allocator 18610590a9dSQiuxu Zhuo * to allocate memory to store the IBECC error logs and links the logs 18710590a9dSQiuxu Zhuo * to the lock-less list. Delay printk() and the work of error reporting 18810590a9dSQiuxu Zhuo * to EDAC core in a worker. 18910590a9dSQiuxu Zhuo */ 19010590a9dSQiuxu Zhuo #define ECCLOG_POOL_SIZE PAGE_SIZE 19177429eebSkernel test robot static LLIST_HEAD(ecclog_llist); 19210590a9dSQiuxu Zhuo static struct gen_pool *ecclog_pool; 19310590a9dSQiuxu Zhuo static char ecclog_buf[ECCLOG_POOL_SIZE]; 19410590a9dSQiuxu Zhuo static struct irq_work ecclog_irq_work; 19510590a9dSQiuxu Zhuo static struct work_struct ecclog_work; 19610590a9dSQiuxu Zhuo 19710590a9dSQiuxu Zhuo /* Compute die IDs for Elkhart Lake with IBECC */ 19810590a9dSQiuxu Zhuo #define DID_EHL_SKU5 0x4514 19910590a9dSQiuxu Zhuo #define DID_EHL_SKU6 0x4528 20010590a9dSQiuxu Zhuo #define DID_EHL_SKU7 0x452a 20110590a9dSQiuxu Zhuo #define DID_EHL_SKU8 0x4516 20210590a9dSQiuxu Zhuo #define DID_EHL_SKU9 0x452c 20310590a9dSQiuxu Zhuo #define DID_EHL_SKU10 0x452e 20410590a9dSQiuxu Zhuo #define DID_EHL_SKU11 0x4532 20510590a9dSQiuxu Zhuo #define DID_EHL_SKU12 0x4518 20610590a9dSQiuxu Zhuo #define DID_EHL_SKU13 0x451a 20710590a9dSQiuxu Zhuo #define DID_EHL_SKU14 0x4534 20810590a9dSQiuxu Zhuo #define DID_EHL_SKU15 0x4536 20910590a9dSQiuxu Zhuo 2104e591c05SQiuxu Zhuo /* Compute die IDs for ICL-NNPI with IBECC */ 2114e591c05SQiuxu Zhuo #define DID_ICL_SKU8 0x4581 2124e591c05SQiuxu Zhuo #define DID_ICL_SKU10 0x4585 2134e591c05SQiuxu Zhuo #define DID_ICL_SKU11 0x4589 2144e591c05SQiuxu Zhuo #define DID_ICL_SKU12 0x458d 2154e591c05SQiuxu Zhuo 2160b7338b2SQiuxu Zhuo /* Compute die IDs for Tiger Lake with IBECC */ 2170b7338b2SQiuxu Zhuo #define DID_TGL_SKU 0x9a14 2180b7338b2SQiuxu Zhuo 219ad774bd5SQiuxu Zhuo /* Compute die IDs for Alder Lake with IBECC */ 220ad774bd5SQiuxu Zhuo #define DID_ADL_SKU1 0x4601 221ad774bd5SQiuxu Zhuo #define DID_ADL_SKU2 0x4602 222ad774bd5SQiuxu Zhuo #define DID_ADL_SKU3 0x4621 223ad774bd5SQiuxu Zhuo #define DID_ADL_SKU4 0x4641 224ad774bd5SQiuxu Zhuo 22510590a9dSQiuxu Zhuo static bool ehl_ibecc_available(struct pci_dev *pdev) 22610590a9dSQiuxu Zhuo { 22710590a9dSQiuxu Zhuo u32 v; 22810590a9dSQiuxu Zhuo 22910590a9dSQiuxu Zhuo if (pci_read_config_dword(pdev, CAPID_C_OFFSET, &v)) 23010590a9dSQiuxu Zhuo return false; 23110590a9dSQiuxu Zhuo 23210590a9dSQiuxu Zhuo return !!(CAPID_C_IBECC & v); 23310590a9dSQiuxu Zhuo } 23410590a9dSQiuxu Zhuo 2350b7338b2SQiuxu Zhuo static u64 ehl_err_addr_to_sys_addr(u64 eaddr, int mc) 23610590a9dSQiuxu Zhuo { 23710590a9dSQiuxu Zhuo return eaddr; 23810590a9dSQiuxu Zhuo } 23910590a9dSQiuxu Zhuo 240ad774bd5SQiuxu Zhuo static u64 ehl_err_addr_to_imc_addr(u64 eaddr, int mc) 24110590a9dSQiuxu Zhuo { 24210590a9dSQiuxu Zhuo if (eaddr < igen6_tolud) 24310590a9dSQiuxu Zhuo return eaddr; 24410590a9dSQiuxu Zhuo 24510590a9dSQiuxu Zhuo if (igen6_tom <= _4GB) 24610590a9dSQiuxu Zhuo return eaddr + igen6_tolud - _4GB; 24710590a9dSQiuxu Zhuo 24810590a9dSQiuxu Zhuo if (eaddr < _4GB) 24910590a9dSQiuxu Zhuo return eaddr + igen6_tolud - igen6_tom; 25010590a9dSQiuxu Zhuo 25110590a9dSQiuxu Zhuo return eaddr; 25210590a9dSQiuxu Zhuo } 25310590a9dSQiuxu Zhuo 2544e591c05SQiuxu Zhuo static bool icl_ibecc_available(struct pci_dev *pdev) 2554e591c05SQiuxu Zhuo { 2564e591c05SQiuxu Zhuo u32 v; 2574e591c05SQiuxu Zhuo 2584e591c05SQiuxu Zhuo if (pci_read_config_dword(pdev, CAPID_C_OFFSET, &v)) 2594e591c05SQiuxu Zhuo return false; 2604e591c05SQiuxu Zhuo 2614e591c05SQiuxu Zhuo return !(CAPID_C_IBECC & v) && 2624e591c05SQiuxu Zhuo (boot_cpu_data.x86_stepping >= 1); 2634e591c05SQiuxu Zhuo } 2644e591c05SQiuxu Zhuo 2650b7338b2SQiuxu Zhuo static bool tgl_ibecc_available(struct pci_dev *pdev) 2660b7338b2SQiuxu Zhuo { 2670b7338b2SQiuxu Zhuo u32 v; 2680b7338b2SQiuxu Zhuo 2690b7338b2SQiuxu Zhuo if (pci_read_config_dword(pdev, CAPID_E_OFFSET, &v)) 2700b7338b2SQiuxu Zhuo return false; 2710b7338b2SQiuxu Zhuo 2720b7338b2SQiuxu Zhuo return !(CAPID_E_IBECC & v); 2730b7338b2SQiuxu Zhuo } 2740b7338b2SQiuxu Zhuo 2750b7338b2SQiuxu Zhuo static u64 mem_addr_to_sys_addr(u64 maddr) 2760b7338b2SQiuxu Zhuo { 2770b7338b2SQiuxu Zhuo if (maddr < igen6_tolud) 2780b7338b2SQiuxu Zhuo return maddr; 2790b7338b2SQiuxu Zhuo 2800b7338b2SQiuxu Zhuo if (igen6_tom <= _4GB) 2810b7338b2SQiuxu Zhuo return maddr - igen6_tolud + _4GB; 2820b7338b2SQiuxu Zhuo 2830b7338b2SQiuxu Zhuo if (maddr < _4GB) 2840b7338b2SQiuxu Zhuo return maddr - igen6_tolud + igen6_tom; 2850b7338b2SQiuxu Zhuo 2860b7338b2SQiuxu Zhuo return maddr; 2870b7338b2SQiuxu Zhuo } 2880b7338b2SQiuxu Zhuo 2890b7338b2SQiuxu Zhuo static u64 mem_slice_hash(u64 addr, u64 mask, u64 hash_init, int intlv_bit) 2900b7338b2SQiuxu Zhuo { 2910b7338b2SQiuxu Zhuo u64 hash_addr = addr & mask, hash = hash_init; 2920b7338b2SQiuxu Zhuo u64 intlv = (addr >> intlv_bit) & 1; 2930b7338b2SQiuxu Zhuo int i; 2940b7338b2SQiuxu Zhuo 2950b7338b2SQiuxu Zhuo for (i = 6; i < 20; i++) 2960b7338b2SQiuxu Zhuo hash ^= (hash_addr >> i) & 1; 2970b7338b2SQiuxu Zhuo 2980b7338b2SQiuxu Zhuo return hash ^ intlv; 2990b7338b2SQiuxu Zhuo } 3000b7338b2SQiuxu Zhuo 3010b7338b2SQiuxu Zhuo static u64 tgl_err_addr_to_mem_addr(u64 eaddr, int mc) 3020b7338b2SQiuxu Zhuo { 3030b7338b2SQiuxu Zhuo u64 maddr, hash, mask, ms_s_size; 3040b7338b2SQiuxu Zhuo int intlv_bit; 3050b7338b2SQiuxu Zhuo u32 ms_hash; 3060b7338b2SQiuxu Zhuo 3070b7338b2SQiuxu Zhuo ms_s_size = igen6_pvt->ms_s_size; 3080b7338b2SQiuxu Zhuo if (eaddr >= ms_s_size) 3090b7338b2SQiuxu Zhuo return eaddr + ms_s_size; 3100b7338b2SQiuxu Zhuo 3110b7338b2SQiuxu Zhuo ms_hash = igen6_pvt->ms_hash; 3120b7338b2SQiuxu Zhuo 3130b7338b2SQiuxu Zhuo mask = MEM_SLICE_HASH_MASK(ms_hash); 3140b7338b2SQiuxu Zhuo intlv_bit = MEM_SLICE_HASH_LSB_MASK_BIT(ms_hash) + 6; 3150b7338b2SQiuxu Zhuo 3160b7338b2SQiuxu Zhuo maddr = GET_BITFIELD(eaddr, intlv_bit, 63) << (intlv_bit + 1) | 3170b7338b2SQiuxu Zhuo GET_BITFIELD(eaddr, 0, intlv_bit - 1); 3180b7338b2SQiuxu Zhuo 3190b7338b2SQiuxu Zhuo hash = mem_slice_hash(maddr, mask, mc, intlv_bit); 3200b7338b2SQiuxu Zhuo 3210b7338b2SQiuxu Zhuo return maddr | (hash << intlv_bit); 3220b7338b2SQiuxu Zhuo } 3230b7338b2SQiuxu Zhuo 3240b7338b2SQiuxu Zhuo static u64 tgl_err_addr_to_sys_addr(u64 eaddr, int mc) 3250b7338b2SQiuxu Zhuo { 3260b7338b2SQiuxu Zhuo u64 maddr = tgl_err_addr_to_mem_addr(eaddr, mc); 3270b7338b2SQiuxu Zhuo 3280b7338b2SQiuxu Zhuo return mem_addr_to_sys_addr(maddr); 3290b7338b2SQiuxu Zhuo } 3300b7338b2SQiuxu Zhuo 331ad774bd5SQiuxu Zhuo static u64 tgl_err_addr_to_imc_addr(u64 eaddr, int mc) 3320b7338b2SQiuxu Zhuo { 3330b7338b2SQiuxu Zhuo return eaddr; 3340b7338b2SQiuxu Zhuo } 3350b7338b2SQiuxu Zhuo 336ad774bd5SQiuxu Zhuo static u64 adl_err_addr_to_sys_addr(u64 eaddr, int mc) 337ad774bd5SQiuxu Zhuo { 338ad774bd5SQiuxu Zhuo return mem_addr_to_sys_addr(eaddr); 339ad774bd5SQiuxu Zhuo } 340ad774bd5SQiuxu Zhuo 341ad774bd5SQiuxu Zhuo static u64 adl_err_addr_to_imc_addr(u64 eaddr, int mc) 342ad774bd5SQiuxu Zhuo { 343ad774bd5SQiuxu Zhuo u64 imc_addr, ms_s_size = igen6_pvt->ms_s_size; 344ad774bd5SQiuxu Zhuo struct igen6_imc *imc = &igen6_pvt->imc[mc]; 345ad774bd5SQiuxu Zhuo int intlv_bit; 346ad774bd5SQiuxu Zhuo u32 mc_hash; 347ad774bd5SQiuxu Zhuo 348ad774bd5SQiuxu Zhuo if (eaddr >= 2 * ms_s_size) 349ad774bd5SQiuxu Zhuo return eaddr - ms_s_size; 350ad774bd5SQiuxu Zhuo 351ad774bd5SQiuxu Zhuo mc_hash = readl(imc->window + MAD_MC_HASH_OFFSET); 352ad774bd5SQiuxu Zhuo 353ad774bd5SQiuxu Zhuo intlv_bit = MAC_MC_HASH_LSB(mc_hash) + 6; 354ad774bd5SQiuxu Zhuo 355ad774bd5SQiuxu Zhuo imc_addr = GET_BITFIELD(eaddr, intlv_bit + 1, 63) << intlv_bit | 356ad774bd5SQiuxu Zhuo GET_BITFIELD(eaddr, 0, intlv_bit - 1); 357ad774bd5SQiuxu Zhuo 358ad774bd5SQiuxu Zhuo return imc_addr; 359ad774bd5SQiuxu Zhuo } 360ad774bd5SQiuxu Zhuo 36110590a9dSQiuxu Zhuo static struct res_config ehl_cfg = { 36210590a9dSQiuxu Zhuo .num_imc = 1, 363ad774bd5SQiuxu Zhuo .imc_base = 0x5000, 36410590a9dSQiuxu Zhuo .ibecc_base = 0xdc00, 36510590a9dSQiuxu Zhuo .ibecc_available = ehl_ibecc_available, 366ad774bd5SQiuxu Zhuo .ibecc_error_log_offset = 0x170, 36710590a9dSQiuxu Zhuo .err_addr_to_sys_addr = ehl_err_addr_to_sys_addr, 36810590a9dSQiuxu Zhuo .err_addr_to_imc_addr = ehl_err_addr_to_imc_addr, 36910590a9dSQiuxu Zhuo }; 37010590a9dSQiuxu Zhuo 3714e591c05SQiuxu Zhuo static struct res_config icl_cfg = { 3724e591c05SQiuxu Zhuo .num_imc = 1, 373ad774bd5SQiuxu Zhuo .imc_base = 0x5000, 3744e591c05SQiuxu Zhuo .ibecc_base = 0xd800, 375ad774bd5SQiuxu Zhuo .ibecc_error_log_offset = 0x170, 3764e591c05SQiuxu Zhuo .ibecc_available = icl_ibecc_available, 3774e591c05SQiuxu Zhuo .err_addr_to_sys_addr = ehl_err_addr_to_sys_addr, 3784e591c05SQiuxu Zhuo .err_addr_to_imc_addr = ehl_err_addr_to_imc_addr, 3794e591c05SQiuxu Zhuo }; 3804e591c05SQiuxu Zhuo 3810b7338b2SQiuxu Zhuo static struct res_config tgl_cfg = { 3820b7338b2SQiuxu Zhuo .machine_check = true, 3830b7338b2SQiuxu Zhuo .num_imc = 2, 384ad774bd5SQiuxu Zhuo .imc_base = 0x5000, 3850b7338b2SQiuxu Zhuo .cmf_base = 0x11000, 3860b7338b2SQiuxu Zhuo .cmf_size = 0x800, 3870b7338b2SQiuxu Zhuo .ms_hash_offset = 0xac, 3880b7338b2SQiuxu Zhuo .ibecc_base = 0xd400, 389ad774bd5SQiuxu Zhuo .ibecc_error_log_offset = 0x170, 3900b7338b2SQiuxu Zhuo .ibecc_available = tgl_ibecc_available, 3910b7338b2SQiuxu Zhuo .err_addr_to_sys_addr = tgl_err_addr_to_sys_addr, 3920b7338b2SQiuxu Zhuo .err_addr_to_imc_addr = tgl_err_addr_to_imc_addr, 3930b7338b2SQiuxu Zhuo }; 3940b7338b2SQiuxu Zhuo 395ad774bd5SQiuxu Zhuo static struct res_config adl_cfg = { 396ad774bd5SQiuxu Zhuo .machine_check = true, 397ad774bd5SQiuxu Zhuo .num_imc = 2, 398ad774bd5SQiuxu Zhuo .imc_base = 0xd800, 399ad774bd5SQiuxu Zhuo .ibecc_base = 0xd400, 400ad774bd5SQiuxu Zhuo .ibecc_error_log_offset = 0x68, 401ad774bd5SQiuxu Zhuo .ibecc_available = tgl_ibecc_available, 402ad774bd5SQiuxu Zhuo .err_addr_to_sys_addr = adl_err_addr_to_sys_addr, 403ad774bd5SQiuxu Zhuo .err_addr_to_imc_addr = adl_err_addr_to_imc_addr, 404ad774bd5SQiuxu Zhuo }; 405ad774bd5SQiuxu Zhuo 40610590a9dSQiuxu Zhuo static const struct pci_device_id igen6_pci_tbl[] = { 40710590a9dSQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_EHL_SKU5), (kernel_ulong_t)&ehl_cfg }, 40810590a9dSQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_EHL_SKU6), (kernel_ulong_t)&ehl_cfg }, 40910590a9dSQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_EHL_SKU7), (kernel_ulong_t)&ehl_cfg }, 41010590a9dSQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_EHL_SKU8), (kernel_ulong_t)&ehl_cfg }, 41110590a9dSQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_EHL_SKU9), (kernel_ulong_t)&ehl_cfg }, 41210590a9dSQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_EHL_SKU10), (kernel_ulong_t)&ehl_cfg }, 41310590a9dSQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_EHL_SKU11), (kernel_ulong_t)&ehl_cfg }, 41410590a9dSQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_EHL_SKU12), (kernel_ulong_t)&ehl_cfg }, 41510590a9dSQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_EHL_SKU13), (kernel_ulong_t)&ehl_cfg }, 41610590a9dSQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_EHL_SKU14), (kernel_ulong_t)&ehl_cfg }, 41710590a9dSQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_EHL_SKU15), (kernel_ulong_t)&ehl_cfg }, 4184e591c05SQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_ICL_SKU8), (kernel_ulong_t)&icl_cfg }, 4194e591c05SQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_ICL_SKU10), (kernel_ulong_t)&icl_cfg }, 4204e591c05SQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_ICL_SKU11), (kernel_ulong_t)&icl_cfg }, 4214e591c05SQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_ICL_SKU12), (kernel_ulong_t)&icl_cfg }, 4220b7338b2SQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_TGL_SKU), (kernel_ulong_t)&tgl_cfg }, 423ad774bd5SQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_ADL_SKU1), (kernel_ulong_t)&adl_cfg }, 424ad774bd5SQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_ADL_SKU2), (kernel_ulong_t)&adl_cfg }, 425ad774bd5SQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_ADL_SKU3), (kernel_ulong_t)&adl_cfg }, 426ad774bd5SQiuxu Zhuo { PCI_VDEVICE(INTEL, DID_ADL_SKU4), (kernel_ulong_t)&adl_cfg }, 42710590a9dSQiuxu Zhuo { }, 42810590a9dSQiuxu Zhuo }; 42910590a9dSQiuxu Zhuo MODULE_DEVICE_TABLE(pci, igen6_pci_tbl); 43010590a9dSQiuxu Zhuo 43110590a9dSQiuxu Zhuo static enum dev_type get_width(int dimm_l, u32 mad_dimm) 43210590a9dSQiuxu Zhuo { 43310590a9dSQiuxu Zhuo u32 w = dimm_l ? MAD_DIMM_CH_DLW(mad_dimm) : 43410590a9dSQiuxu Zhuo MAD_DIMM_CH_DSW(mad_dimm); 43510590a9dSQiuxu Zhuo 43610590a9dSQiuxu Zhuo switch (w) { 43710590a9dSQiuxu Zhuo case 0: 43810590a9dSQiuxu Zhuo return DEV_X8; 43910590a9dSQiuxu Zhuo case 1: 44010590a9dSQiuxu Zhuo return DEV_X16; 44110590a9dSQiuxu Zhuo case 2: 44210590a9dSQiuxu Zhuo return DEV_X32; 44310590a9dSQiuxu Zhuo default: 44410590a9dSQiuxu Zhuo return DEV_UNKNOWN; 44510590a9dSQiuxu Zhuo } 44610590a9dSQiuxu Zhuo } 44710590a9dSQiuxu Zhuo 44810590a9dSQiuxu Zhuo static enum mem_type get_memory_type(u32 mad_inter) 44910590a9dSQiuxu Zhuo { 45010590a9dSQiuxu Zhuo u32 t = MAD_INTER_CHANNEL_DDR_TYPE(mad_inter); 45110590a9dSQiuxu Zhuo 45210590a9dSQiuxu Zhuo switch (t) { 45310590a9dSQiuxu Zhuo case 0: 45410590a9dSQiuxu Zhuo return MEM_DDR4; 45510590a9dSQiuxu Zhuo case 1: 45610590a9dSQiuxu Zhuo return MEM_DDR3; 45710590a9dSQiuxu Zhuo case 2: 45810590a9dSQiuxu Zhuo return MEM_LPDDR3; 45910590a9dSQiuxu Zhuo case 3: 46010590a9dSQiuxu Zhuo return MEM_LPDDR4; 46110590a9dSQiuxu Zhuo case 4: 46210590a9dSQiuxu Zhuo return MEM_WIO2; 46310590a9dSQiuxu Zhuo default: 46410590a9dSQiuxu Zhuo return MEM_UNKNOWN; 46510590a9dSQiuxu Zhuo } 46610590a9dSQiuxu Zhuo } 46710590a9dSQiuxu Zhuo 46810590a9dSQiuxu Zhuo static int decode_chan_idx(u64 addr, u64 mask, int intlv_bit) 46910590a9dSQiuxu Zhuo { 47010590a9dSQiuxu Zhuo u64 hash_addr = addr & mask, hash = 0; 47110590a9dSQiuxu Zhuo u64 intlv = (addr >> intlv_bit) & 1; 47210590a9dSQiuxu Zhuo int i; 47310590a9dSQiuxu Zhuo 47410590a9dSQiuxu Zhuo for (i = 6; i < 20; i++) 47510590a9dSQiuxu Zhuo hash ^= (hash_addr >> i) & 1; 47610590a9dSQiuxu Zhuo 47710590a9dSQiuxu Zhuo return (int)hash ^ intlv; 47810590a9dSQiuxu Zhuo } 47910590a9dSQiuxu Zhuo 48010590a9dSQiuxu Zhuo static u64 decode_channel_addr(u64 addr, int intlv_bit) 48110590a9dSQiuxu Zhuo { 48210590a9dSQiuxu Zhuo u64 channel_addr; 48310590a9dSQiuxu Zhuo 48410590a9dSQiuxu Zhuo /* Remove the interleave bit and shift upper part down to fill gap */ 48510590a9dSQiuxu Zhuo channel_addr = GET_BITFIELD(addr, intlv_bit + 1, 63) << intlv_bit; 48610590a9dSQiuxu Zhuo channel_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1); 48710590a9dSQiuxu Zhuo 48810590a9dSQiuxu Zhuo return channel_addr; 48910590a9dSQiuxu Zhuo } 49010590a9dSQiuxu Zhuo 49110590a9dSQiuxu Zhuo static void decode_addr(u64 addr, u32 hash, u64 s_size, int l_map, 49210590a9dSQiuxu Zhuo int *idx, u64 *sub_addr) 49310590a9dSQiuxu Zhuo { 49410590a9dSQiuxu Zhuo int intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6; 49510590a9dSQiuxu Zhuo 49610590a9dSQiuxu Zhuo if (addr > 2 * s_size) { 49710590a9dSQiuxu Zhuo *sub_addr = addr - s_size; 49810590a9dSQiuxu Zhuo *idx = l_map; 49910590a9dSQiuxu Zhuo return; 50010590a9dSQiuxu Zhuo } 50110590a9dSQiuxu Zhuo 50210590a9dSQiuxu Zhuo if (CHANNEL_HASH_MODE(hash)) { 50310590a9dSQiuxu Zhuo *sub_addr = decode_channel_addr(addr, intlv_bit); 50410590a9dSQiuxu Zhuo *idx = decode_chan_idx(addr, CHANNEL_HASH_MASK(hash), intlv_bit); 50510590a9dSQiuxu Zhuo } else { 50610590a9dSQiuxu Zhuo *sub_addr = decode_channel_addr(addr, 6); 50710590a9dSQiuxu Zhuo *idx = GET_BITFIELD(addr, 6, 6); 50810590a9dSQiuxu Zhuo } 50910590a9dSQiuxu Zhuo } 51010590a9dSQiuxu Zhuo 51110590a9dSQiuxu Zhuo static int igen6_decode(struct decoded_addr *res) 51210590a9dSQiuxu Zhuo { 51310590a9dSQiuxu Zhuo struct igen6_imc *imc = &igen6_pvt->imc[res->mc]; 51410590a9dSQiuxu Zhuo u64 addr = res->imc_addr, sub_addr, s_size; 51510590a9dSQiuxu Zhuo int idx, l_map; 51610590a9dSQiuxu Zhuo u32 hash; 51710590a9dSQiuxu Zhuo 51810590a9dSQiuxu Zhuo if (addr >= igen6_tom) { 51910590a9dSQiuxu Zhuo edac_dbg(0, "Address 0x%llx out of range\n", addr); 52010590a9dSQiuxu Zhuo return -EINVAL; 52110590a9dSQiuxu Zhuo } 52210590a9dSQiuxu Zhuo 52310590a9dSQiuxu Zhuo /* Decode channel */ 52410590a9dSQiuxu Zhuo hash = readl(imc->window + CHANNEL_HASH_OFFSET); 52510590a9dSQiuxu Zhuo s_size = imc->ch_s_size; 52610590a9dSQiuxu Zhuo l_map = imc->ch_l_map; 52710590a9dSQiuxu Zhuo decode_addr(addr, hash, s_size, l_map, &idx, &sub_addr); 52810590a9dSQiuxu Zhuo res->channel_idx = idx; 52910590a9dSQiuxu Zhuo res->channel_addr = sub_addr; 53010590a9dSQiuxu Zhuo 53110590a9dSQiuxu Zhuo /* Decode sub-channel/DIMM */ 53210590a9dSQiuxu Zhuo hash = readl(imc->window + CHANNEL_EHASH_OFFSET); 53310590a9dSQiuxu Zhuo s_size = imc->dimm_s_size[idx]; 53410590a9dSQiuxu Zhuo l_map = imc->dimm_l_map[idx]; 53510590a9dSQiuxu Zhuo decode_addr(res->channel_addr, hash, s_size, l_map, &idx, &sub_addr); 53610590a9dSQiuxu Zhuo res->sub_channel_idx = idx; 53710590a9dSQiuxu Zhuo res->sub_channel_addr = sub_addr; 53810590a9dSQiuxu Zhuo 53910590a9dSQiuxu Zhuo return 0; 54010590a9dSQiuxu Zhuo } 54110590a9dSQiuxu Zhuo 54210590a9dSQiuxu Zhuo static void igen6_output_error(struct decoded_addr *res, 54310590a9dSQiuxu Zhuo struct mem_ctl_info *mci, u64 ecclog) 54410590a9dSQiuxu Zhuo { 54510590a9dSQiuxu Zhuo enum hw_event_mc_err_type type = ecclog & ECC_ERROR_LOG_UE ? 54610590a9dSQiuxu Zhuo HW_EVENT_ERR_UNCORRECTED : 54710590a9dSQiuxu Zhuo HW_EVENT_ERR_CORRECTED; 54810590a9dSQiuxu Zhuo 54910590a9dSQiuxu Zhuo edac_mc_handle_error(type, mci, 1, 55010590a9dSQiuxu Zhuo res->sys_addr >> PAGE_SHIFT, 55110590a9dSQiuxu Zhuo res->sys_addr & ~PAGE_MASK, 55210590a9dSQiuxu Zhuo ECC_ERROR_LOG_SYND(ecclog), 55310590a9dSQiuxu Zhuo res->channel_idx, res->sub_channel_idx, 55410590a9dSQiuxu Zhuo -1, "", ""); 55510590a9dSQiuxu Zhuo } 55610590a9dSQiuxu Zhuo 55710590a9dSQiuxu Zhuo static struct gen_pool *ecclog_gen_pool_create(void) 55810590a9dSQiuxu Zhuo { 55910590a9dSQiuxu Zhuo struct gen_pool *pool; 56010590a9dSQiuxu Zhuo 56110590a9dSQiuxu Zhuo pool = gen_pool_create(ilog2(sizeof(struct ecclog_node)), -1); 56210590a9dSQiuxu Zhuo if (!pool) 56310590a9dSQiuxu Zhuo return NULL; 56410590a9dSQiuxu Zhuo 56510590a9dSQiuxu Zhuo if (gen_pool_add(pool, (unsigned long)ecclog_buf, ECCLOG_POOL_SIZE, -1)) { 56610590a9dSQiuxu Zhuo gen_pool_destroy(pool); 56710590a9dSQiuxu Zhuo return NULL; 56810590a9dSQiuxu Zhuo } 56910590a9dSQiuxu Zhuo 57010590a9dSQiuxu Zhuo return pool; 57110590a9dSQiuxu Zhuo } 57210590a9dSQiuxu Zhuo 57310590a9dSQiuxu Zhuo static int ecclog_gen_pool_add(int mc, u64 ecclog) 57410590a9dSQiuxu Zhuo { 57510590a9dSQiuxu Zhuo struct ecclog_node *node; 57610590a9dSQiuxu Zhuo 57710590a9dSQiuxu Zhuo node = (void *)gen_pool_alloc(ecclog_pool, sizeof(*node)); 57810590a9dSQiuxu Zhuo if (!node) 57910590a9dSQiuxu Zhuo return -ENOMEM; 58010590a9dSQiuxu Zhuo 58110590a9dSQiuxu Zhuo node->mc = mc; 58210590a9dSQiuxu Zhuo node->ecclog = ecclog; 58310590a9dSQiuxu Zhuo llist_add(&node->llnode, &ecclog_llist); 58410590a9dSQiuxu Zhuo 58510590a9dSQiuxu Zhuo return 0; 58610590a9dSQiuxu Zhuo } 58710590a9dSQiuxu Zhuo 58810590a9dSQiuxu Zhuo /* 58910590a9dSQiuxu Zhuo * Either the memory-mapped I/O status register ECC_ERROR_LOG or the PCI 59010590a9dSQiuxu Zhuo * configuration space status register ERRSTS can indicate whether a 59110590a9dSQiuxu Zhuo * correctable error or an uncorrectable error occurred. We only use the 59210590a9dSQiuxu Zhuo * ECC_ERROR_LOG register to check error type, but need to clear both 59310590a9dSQiuxu Zhuo * registers to enable future error events. 59410590a9dSQiuxu Zhuo */ 59510590a9dSQiuxu Zhuo static u64 ecclog_read_and_clear(struct igen6_imc *imc) 59610590a9dSQiuxu Zhuo { 59710590a9dSQiuxu Zhuo u64 ecclog = readq(imc->window + ECC_ERROR_LOG_OFFSET); 59810590a9dSQiuxu Zhuo 59910590a9dSQiuxu Zhuo if (ecclog & (ECC_ERROR_LOG_CE | ECC_ERROR_LOG_UE)) { 60010590a9dSQiuxu Zhuo /* Clear CE/UE bits by writing 1s */ 60110590a9dSQiuxu Zhuo writeq(ecclog, imc->window + ECC_ERROR_LOG_OFFSET); 60210590a9dSQiuxu Zhuo return ecclog; 60310590a9dSQiuxu Zhuo } 60410590a9dSQiuxu Zhuo 60510590a9dSQiuxu Zhuo return 0; 60610590a9dSQiuxu Zhuo } 60710590a9dSQiuxu Zhuo 60810590a9dSQiuxu Zhuo static void errsts_clear(struct igen6_imc *imc) 60910590a9dSQiuxu Zhuo { 61010590a9dSQiuxu Zhuo u16 errsts; 61110590a9dSQiuxu Zhuo 61210590a9dSQiuxu Zhuo if (pci_read_config_word(imc->pdev, ERRSTS_OFFSET, &errsts)) { 61310590a9dSQiuxu Zhuo igen6_printk(KERN_ERR, "Failed to read ERRSTS\n"); 61410590a9dSQiuxu Zhuo return; 61510590a9dSQiuxu Zhuo } 61610590a9dSQiuxu Zhuo 61710590a9dSQiuxu Zhuo /* Clear CE/UE bits by writing 1s */ 61810590a9dSQiuxu Zhuo if (errsts & (ERRSTS_CE | ERRSTS_UE)) 61910590a9dSQiuxu Zhuo pci_write_config_word(imc->pdev, ERRSTS_OFFSET, errsts); 62010590a9dSQiuxu Zhuo } 62110590a9dSQiuxu Zhuo 62210590a9dSQiuxu Zhuo static int errcmd_enable_error_reporting(bool enable) 62310590a9dSQiuxu Zhuo { 62410590a9dSQiuxu Zhuo struct igen6_imc *imc = &igen6_pvt->imc[0]; 62510590a9dSQiuxu Zhuo u16 errcmd; 62610590a9dSQiuxu Zhuo int rc; 62710590a9dSQiuxu Zhuo 62810590a9dSQiuxu Zhuo rc = pci_read_config_word(imc->pdev, ERRCMD_OFFSET, &errcmd); 62910590a9dSQiuxu Zhuo if (rc) 63010590a9dSQiuxu Zhuo return rc; 63110590a9dSQiuxu Zhuo 63210590a9dSQiuxu Zhuo if (enable) 63310590a9dSQiuxu Zhuo errcmd |= ERRCMD_CE | ERRSTS_UE; 63410590a9dSQiuxu Zhuo else 63510590a9dSQiuxu Zhuo errcmd &= ~(ERRCMD_CE | ERRSTS_UE); 63610590a9dSQiuxu Zhuo 63710590a9dSQiuxu Zhuo rc = pci_write_config_word(imc->pdev, ERRCMD_OFFSET, errcmd); 63810590a9dSQiuxu Zhuo if (rc) 63910590a9dSQiuxu Zhuo return rc; 64010590a9dSQiuxu Zhuo 64110590a9dSQiuxu Zhuo return 0; 64210590a9dSQiuxu Zhuo } 64310590a9dSQiuxu Zhuo 64410590a9dSQiuxu Zhuo static int ecclog_handler(void) 64510590a9dSQiuxu Zhuo { 64610590a9dSQiuxu Zhuo struct igen6_imc *imc; 64710590a9dSQiuxu Zhuo int i, n = 0; 64810590a9dSQiuxu Zhuo u64 ecclog; 64910590a9dSQiuxu Zhuo 65010590a9dSQiuxu Zhuo for (i = 0; i < res_cfg->num_imc; i++) { 65110590a9dSQiuxu Zhuo imc = &igen6_pvt->imc[i]; 65210590a9dSQiuxu Zhuo 65310590a9dSQiuxu Zhuo /* errsts_clear() isn't NMI-safe. Delay it in the IRQ context */ 65410590a9dSQiuxu Zhuo 65510590a9dSQiuxu Zhuo ecclog = ecclog_read_and_clear(imc); 65610590a9dSQiuxu Zhuo if (!ecclog) 65710590a9dSQiuxu Zhuo continue; 65810590a9dSQiuxu Zhuo 65910590a9dSQiuxu Zhuo if (!ecclog_gen_pool_add(i, ecclog)) 66010590a9dSQiuxu Zhuo irq_work_queue(&ecclog_irq_work); 66110590a9dSQiuxu Zhuo 66210590a9dSQiuxu Zhuo n++; 66310590a9dSQiuxu Zhuo } 66410590a9dSQiuxu Zhuo 66510590a9dSQiuxu Zhuo return n; 66610590a9dSQiuxu Zhuo } 66710590a9dSQiuxu Zhuo 66810590a9dSQiuxu Zhuo static void ecclog_work_cb(struct work_struct *work) 66910590a9dSQiuxu Zhuo { 67010590a9dSQiuxu Zhuo struct ecclog_node *node, *tmp; 67110590a9dSQiuxu Zhuo struct mem_ctl_info *mci; 67210590a9dSQiuxu Zhuo struct llist_node *head; 67310590a9dSQiuxu Zhuo struct decoded_addr res; 67410590a9dSQiuxu Zhuo u64 eaddr; 67510590a9dSQiuxu Zhuo 67610590a9dSQiuxu Zhuo head = llist_del_all(&ecclog_llist); 67710590a9dSQiuxu Zhuo if (!head) 67810590a9dSQiuxu Zhuo return; 67910590a9dSQiuxu Zhuo 68010590a9dSQiuxu Zhuo llist_for_each_entry_safe(node, tmp, head, llnode) { 68110590a9dSQiuxu Zhuo memset(&res, 0, sizeof(res)); 68210590a9dSQiuxu Zhuo eaddr = ECC_ERROR_LOG_ADDR(node->ecclog) << 68310590a9dSQiuxu Zhuo ECC_ERROR_LOG_ADDR_SHIFT; 68410590a9dSQiuxu Zhuo res.mc = node->mc; 6850b7338b2SQiuxu Zhuo res.sys_addr = res_cfg->err_addr_to_sys_addr(eaddr, res.mc); 686ad774bd5SQiuxu Zhuo res.imc_addr = res_cfg->err_addr_to_imc_addr(eaddr, res.mc); 68710590a9dSQiuxu Zhuo 68810590a9dSQiuxu Zhuo mci = igen6_pvt->imc[res.mc].mci; 68910590a9dSQiuxu Zhuo 69010590a9dSQiuxu Zhuo edac_dbg(2, "MC %d, ecclog = 0x%llx\n", node->mc, node->ecclog); 69110590a9dSQiuxu Zhuo igen6_mc_printk(mci, KERN_DEBUG, "HANDLING IBECC MEMORY ERROR\n"); 69210590a9dSQiuxu Zhuo igen6_mc_printk(mci, KERN_DEBUG, "ADDR 0x%llx ", res.sys_addr); 69310590a9dSQiuxu Zhuo 69410590a9dSQiuxu Zhuo if (!igen6_decode(&res)) 69510590a9dSQiuxu Zhuo igen6_output_error(&res, mci, node->ecclog); 69610590a9dSQiuxu Zhuo 69710590a9dSQiuxu Zhuo gen_pool_free(ecclog_pool, (unsigned long)node, sizeof(*node)); 69810590a9dSQiuxu Zhuo } 69910590a9dSQiuxu Zhuo } 70010590a9dSQiuxu Zhuo 70110590a9dSQiuxu Zhuo static void ecclog_irq_work_cb(struct irq_work *irq_work) 70210590a9dSQiuxu Zhuo { 70310590a9dSQiuxu Zhuo int i; 70410590a9dSQiuxu Zhuo 70510590a9dSQiuxu Zhuo for (i = 0; i < res_cfg->num_imc; i++) 70610590a9dSQiuxu Zhuo errsts_clear(&igen6_pvt->imc[i]); 70710590a9dSQiuxu Zhuo 70810590a9dSQiuxu Zhuo if (!llist_empty(&ecclog_llist)) 70910590a9dSQiuxu Zhuo schedule_work(&ecclog_work); 71010590a9dSQiuxu Zhuo } 71110590a9dSQiuxu Zhuo 71210590a9dSQiuxu Zhuo static int ecclog_nmi_handler(unsigned int cmd, struct pt_regs *regs) 71310590a9dSQiuxu Zhuo { 71410590a9dSQiuxu Zhuo unsigned char reason; 71510590a9dSQiuxu Zhuo 71610590a9dSQiuxu Zhuo if (!ecclog_handler()) 71710590a9dSQiuxu Zhuo return NMI_DONE; 71810590a9dSQiuxu Zhuo 71910590a9dSQiuxu Zhuo /* 72010590a9dSQiuxu Zhuo * Both In-Band ECC correctable error and uncorrectable error are 72110590a9dSQiuxu Zhuo * reported by SERR# NMI. The NMI generic code (see pci_serr_error()) 72210590a9dSQiuxu Zhuo * doesn't clear the bit NMI_REASON_CLEAR_SERR (in port 0x61) to 72310590a9dSQiuxu Zhuo * re-enable the SERR# NMI after NMI handling. So clear this bit here 72410590a9dSQiuxu Zhuo * to re-enable SERR# NMI for receiving future In-Band ECC errors. 72510590a9dSQiuxu Zhuo */ 72610590a9dSQiuxu Zhuo reason = x86_platform.get_nmi_reason() & NMI_REASON_CLEAR_MASK; 72710590a9dSQiuxu Zhuo reason |= NMI_REASON_CLEAR_SERR; 72810590a9dSQiuxu Zhuo outb(reason, NMI_REASON_PORT); 72910590a9dSQiuxu Zhuo reason &= ~NMI_REASON_CLEAR_SERR; 73010590a9dSQiuxu Zhuo outb(reason, NMI_REASON_PORT); 73110590a9dSQiuxu Zhuo 73210590a9dSQiuxu Zhuo return NMI_HANDLED; 73310590a9dSQiuxu Zhuo } 73410590a9dSQiuxu Zhuo 7350b7338b2SQiuxu Zhuo static int ecclog_mce_handler(struct notifier_block *nb, unsigned long val, 7360b7338b2SQiuxu Zhuo void *data) 7370b7338b2SQiuxu Zhuo { 7380b7338b2SQiuxu Zhuo struct mce *mce = (struct mce *)data; 7390b7338b2SQiuxu Zhuo char *type; 7400b7338b2SQiuxu Zhuo 7410b7338b2SQiuxu Zhuo if (mce->kflags & MCE_HANDLED_CEC) 7420b7338b2SQiuxu Zhuo return NOTIFY_DONE; 7430b7338b2SQiuxu Zhuo 7440b7338b2SQiuxu Zhuo /* 7450b7338b2SQiuxu Zhuo * Ignore unless this is a memory related error. 7460b7338b2SQiuxu Zhuo * We don't check the bit MCI_STATUS_ADDRV of MCi_STATUS here, 7470b7338b2SQiuxu Zhuo * since this bit isn't set on some CPU (e.g., Tiger Lake UP3). 7480b7338b2SQiuxu Zhuo */ 7490b7338b2SQiuxu Zhuo if ((mce->status & 0xefff) >> 7 != 1) 7500b7338b2SQiuxu Zhuo return NOTIFY_DONE; 7510b7338b2SQiuxu Zhuo 7520b7338b2SQiuxu Zhuo if (mce->mcgstatus & MCG_STATUS_MCIP) 7530b7338b2SQiuxu Zhuo type = "Exception"; 7540b7338b2SQiuxu Zhuo else 7550b7338b2SQiuxu Zhuo type = "Event"; 7560b7338b2SQiuxu Zhuo 7570b7338b2SQiuxu Zhuo edac_dbg(0, "CPU %d: Machine Check %s: 0x%llx Bank %d: 0x%llx\n", 7580b7338b2SQiuxu Zhuo mce->extcpu, type, mce->mcgstatus, 7590b7338b2SQiuxu Zhuo mce->bank, mce->status); 7600b7338b2SQiuxu Zhuo edac_dbg(0, "TSC 0x%llx\n", mce->tsc); 7610b7338b2SQiuxu Zhuo edac_dbg(0, "ADDR 0x%llx\n", mce->addr); 7620b7338b2SQiuxu Zhuo edac_dbg(0, "MISC 0x%llx\n", mce->misc); 7630b7338b2SQiuxu Zhuo edac_dbg(0, "PROCESSOR %u:0x%x TIME %llu SOCKET %u APIC 0x%x\n", 7640b7338b2SQiuxu Zhuo mce->cpuvendor, mce->cpuid, mce->time, 7650b7338b2SQiuxu Zhuo mce->socketid, mce->apicid); 7660b7338b2SQiuxu Zhuo /* 7670b7338b2SQiuxu Zhuo * We just use the Machine Check for the memory error notification. 7680b7338b2SQiuxu Zhuo * Each memory controller is associated with an IBECC instance. 7690b7338b2SQiuxu Zhuo * Directly read and clear the error information(error address and 7700b7338b2SQiuxu Zhuo * error type) on all the IBECC instances so that we know on which 7710b7338b2SQiuxu Zhuo * memory controller the memory error(s) occurred. 7720b7338b2SQiuxu Zhuo */ 7730b7338b2SQiuxu Zhuo if (!ecclog_handler()) 7740b7338b2SQiuxu Zhuo return NOTIFY_DONE; 7750b7338b2SQiuxu Zhuo 7760b7338b2SQiuxu Zhuo mce->kflags |= MCE_HANDLED_EDAC; 7770b7338b2SQiuxu Zhuo 7780b7338b2SQiuxu Zhuo return NOTIFY_DONE; 7790b7338b2SQiuxu Zhuo } 7800b7338b2SQiuxu Zhuo 7810b7338b2SQiuxu Zhuo static struct notifier_block ecclog_mce_dec = { 7820b7338b2SQiuxu Zhuo .notifier_call = ecclog_mce_handler, 7830b7338b2SQiuxu Zhuo .priority = MCE_PRIO_EDAC, 7840b7338b2SQiuxu Zhuo }; 7850b7338b2SQiuxu Zhuo 78610590a9dSQiuxu Zhuo static bool igen6_check_ecc(struct igen6_imc *imc) 78710590a9dSQiuxu Zhuo { 78810590a9dSQiuxu Zhuo u32 activate = readl(imc->window + IBECC_ACTIVATE_OFFSET); 78910590a9dSQiuxu Zhuo 79010590a9dSQiuxu Zhuo return !!(activate & IBECC_ACTIVATE_EN); 79110590a9dSQiuxu Zhuo } 79210590a9dSQiuxu Zhuo 79310590a9dSQiuxu Zhuo static int igen6_get_dimm_config(struct mem_ctl_info *mci) 79410590a9dSQiuxu Zhuo { 79510590a9dSQiuxu Zhuo struct igen6_imc *imc = mci->pvt_info; 79610590a9dSQiuxu Zhuo u32 mad_inter, mad_intra, mad_dimm; 79710590a9dSQiuxu Zhuo int i, j, ndimms, mc = imc->mc; 79810590a9dSQiuxu Zhuo struct dimm_info *dimm; 79910590a9dSQiuxu Zhuo enum mem_type mtype; 80010590a9dSQiuxu Zhuo enum dev_type dtype; 80110590a9dSQiuxu Zhuo u64 dsize; 80210590a9dSQiuxu Zhuo bool ecc; 80310590a9dSQiuxu Zhuo 80410590a9dSQiuxu Zhuo edac_dbg(2, "\n"); 80510590a9dSQiuxu Zhuo 80610590a9dSQiuxu Zhuo mad_inter = readl(imc->window + MAD_INTER_CHANNEL_OFFSET); 80710590a9dSQiuxu Zhuo mtype = get_memory_type(mad_inter); 80810590a9dSQiuxu Zhuo ecc = igen6_check_ecc(imc); 80910590a9dSQiuxu Zhuo imc->ch_s_size = MAD_INTER_CHANNEL_CH_S_SIZE(mad_inter); 81010590a9dSQiuxu Zhuo imc->ch_l_map = MAD_INTER_CHANNEL_CH_L_MAP(mad_inter); 81110590a9dSQiuxu Zhuo 81210590a9dSQiuxu Zhuo for (i = 0; i < NUM_CHANNELS; i++) { 81310590a9dSQiuxu Zhuo mad_intra = readl(imc->window + MAD_INTRA_CH0_OFFSET + i * 4); 81410590a9dSQiuxu Zhuo mad_dimm = readl(imc->window + MAD_DIMM_CH0_OFFSET + i * 4); 81510590a9dSQiuxu Zhuo 81610590a9dSQiuxu Zhuo imc->dimm_l_size[i] = MAD_DIMM_CH_DIMM_L_SIZE(mad_dimm); 81710590a9dSQiuxu Zhuo imc->dimm_s_size[i] = MAD_DIMM_CH_DIMM_S_SIZE(mad_dimm); 81810590a9dSQiuxu Zhuo imc->dimm_l_map[i] = MAD_INTRA_CH_DIMM_L_MAP(mad_intra); 8190b7338b2SQiuxu Zhuo imc->size += imc->dimm_s_size[i]; 8200b7338b2SQiuxu Zhuo imc->size += imc->dimm_l_size[i]; 82110590a9dSQiuxu Zhuo ndimms = 0; 82210590a9dSQiuxu Zhuo 82310590a9dSQiuxu Zhuo for (j = 0; j < NUM_DIMMS; j++) { 82410590a9dSQiuxu Zhuo dimm = edac_get_dimm(mci, i, j, 0); 82510590a9dSQiuxu Zhuo 82610590a9dSQiuxu Zhuo if (j ^ imc->dimm_l_map[i]) { 82710590a9dSQiuxu Zhuo dtype = get_width(0, mad_dimm); 82810590a9dSQiuxu Zhuo dsize = imc->dimm_s_size[i]; 82910590a9dSQiuxu Zhuo } else { 83010590a9dSQiuxu Zhuo dtype = get_width(1, mad_dimm); 83110590a9dSQiuxu Zhuo dsize = imc->dimm_l_size[i]; 83210590a9dSQiuxu Zhuo } 83310590a9dSQiuxu Zhuo 83410590a9dSQiuxu Zhuo if (!dsize) 83510590a9dSQiuxu Zhuo continue; 83610590a9dSQiuxu Zhuo 83710590a9dSQiuxu Zhuo dimm->grain = 64; 83810590a9dSQiuxu Zhuo dimm->mtype = mtype; 83910590a9dSQiuxu Zhuo dimm->dtype = dtype; 84010590a9dSQiuxu Zhuo dimm->nr_pages = MiB_TO_PAGES(dsize >> 20); 84110590a9dSQiuxu Zhuo dimm->edac_mode = EDAC_SECDED; 84210590a9dSQiuxu Zhuo snprintf(dimm->label, sizeof(dimm->label), 84310590a9dSQiuxu Zhuo "MC#%d_Chan#%d_DIMM#%d", mc, i, j); 84410590a9dSQiuxu Zhuo edac_dbg(0, "MC %d, Channel %d, DIMM %d, Size %llu MiB (%u pages)\n", 84510590a9dSQiuxu Zhuo mc, i, j, dsize >> 20, dimm->nr_pages); 84610590a9dSQiuxu Zhuo 84710590a9dSQiuxu Zhuo ndimms++; 84810590a9dSQiuxu Zhuo } 84910590a9dSQiuxu Zhuo 85010590a9dSQiuxu Zhuo if (ndimms && !ecc) { 85110590a9dSQiuxu Zhuo igen6_printk(KERN_ERR, "MC%d In-Band ECC is disabled\n", mc); 85210590a9dSQiuxu Zhuo return -ENODEV; 85310590a9dSQiuxu Zhuo } 85410590a9dSQiuxu Zhuo } 85510590a9dSQiuxu Zhuo 8560b7338b2SQiuxu Zhuo edac_dbg(0, "MC %d, total size %llu MiB\n", mc, imc->size >> 20); 8570b7338b2SQiuxu Zhuo 85810590a9dSQiuxu Zhuo return 0; 85910590a9dSQiuxu Zhuo } 86010590a9dSQiuxu Zhuo 86110590a9dSQiuxu Zhuo #ifdef CONFIG_EDAC_DEBUG 8622223d8c7SQiuxu Zhuo /* Top of upper usable DRAM */ 8632223d8c7SQiuxu Zhuo static u64 igen6_touud; 8642223d8c7SQiuxu Zhuo #define TOUUD_OFFSET 0xa8 8652223d8c7SQiuxu Zhuo 86610590a9dSQiuxu Zhuo static void igen6_reg_dump(struct igen6_imc *imc) 86710590a9dSQiuxu Zhuo { 86810590a9dSQiuxu Zhuo int i; 86910590a9dSQiuxu Zhuo 87010590a9dSQiuxu Zhuo edac_dbg(2, "CHANNEL_HASH : 0x%x\n", 87110590a9dSQiuxu Zhuo readl(imc->window + CHANNEL_HASH_OFFSET)); 87210590a9dSQiuxu Zhuo edac_dbg(2, "CHANNEL_EHASH : 0x%x\n", 87310590a9dSQiuxu Zhuo readl(imc->window + CHANNEL_EHASH_OFFSET)); 87410590a9dSQiuxu Zhuo edac_dbg(2, "MAD_INTER_CHANNEL: 0x%x\n", 87510590a9dSQiuxu Zhuo readl(imc->window + MAD_INTER_CHANNEL_OFFSET)); 87610590a9dSQiuxu Zhuo edac_dbg(2, "ECC_ERROR_LOG : 0x%llx\n", 87710590a9dSQiuxu Zhuo readq(imc->window + ECC_ERROR_LOG_OFFSET)); 87810590a9dSQiuxu Zhuo 87910590a9dSQiuxu Zhuo for (i = 0; i < NUM_CHANNELS; i++) { 88010590a9dSQiuxu Zhuo edac_dbg(2, "MAD_INTRA_CH%d : 0x%x\n", i, 88110590a9dSQiuxu Zhuo readl(imc->window + MAD_INTRA_CH0_OFFSET + i * 4)); 88210590a9dSQiuxu Zhuo edac_dbg(2, "MAD_DIMM_CH%d : 0x%x\n", i, 88310590a9dSQiuxu Zhuo readl(imc->window + MAD_DIMM_CH0_OFFSET + i * 4)); 88410590a9dSQiuxu Zhuo } 88510590a9dSQiuxu Zhuo edac_dbg(2, "TOLUD : 0x%x", igen6_tolud); 8862223d8c7SQiuxu Zhuo edac_dbg(2, "TOUUD : 0x%llx", igen6_touud); 88710590a9dSQiuxu Zhuo edac_dbg(2, "TOM : 0x%llx", igen6_tom); 88810590a9dSQiuxu Zhuo } 8892223d8c7SQiuxu Zhuo 8902223d8c7SQiuxu Zhuo static struct dentry *igen6_test; 8912223d8c7SQiuxu Zhuo 8922223d8c7SQiuxu Zhuo static int debugfs_u64_set(void *data, u64 val) 8932223d8c7SQiuxu Zhuo { 8942223d8c7SQiuxu Zhuo u64 ecclog; 8952223d8c7SQiuxu Zhuo 8962223d8c7SQiuxu Zhuo if ((val >= igen6_tolud && val < _4GB) || val >= igen6_touud) { 8972223d8c7SQiuxu Zhuo edac_dbg(0, "Address 0x%llx out of range\n", val); 8982223d8c7SQiuxu Zhuo return 0; 8992223d8c7SQiuxu Zhuo } 9002223d8c7SQiuxu Zhuo 9012223d8c7SQiuxu Zhuo pr_warn_once("Fake error to 0x%llx injected via debugfs\n", val); 9022223d8c7SQiuxu Zhuo 9032223d8c7SQiuxu Zhuo val >>= ECC_ERROR_LOG_ADDR_SHIFT; 9042223d8c7SQiuxu Zhuo ecclog = (val << ECC_ERROR_LOG_ADDR_SHIFT) | ECC_ERROR_LOG_CE; 9052223d8c7SQiuxu Zhuo 9062223d8c7SQiuxu Zhuo if (!ecclog_gen_pool_add(0, ecclog)) 9072223d8c7SQiuxu Zhuo irq_work_queue(&ecclog_irq_work); 9082223d8c7SQiuxu Zhuo 9092223d8c7SQiuxu Zhuo return 0; 9102223d8c7SQiuxu Zhuo } 9112223d8c7SQiuxu Zhuo DEFINE_SIMPLE_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n"); 9122223d8c7SQiuxu Zhuo 9132223d8c7SQiuxu Zhuo static void igen6_debug_setup(void) 9142223d8c7SQiuxu Zhuo { 9152223d8c7SQiuxu Zhuo igen6_test = edac_debugfs_create_dir("igen6_test"); 9162223d8c7SQiuxu Zhuo if (!igen6_test) 9172223d8c7SQiuxu Zhuo return; 9182223d8c7SQiuxu Zhuo 9192223d8c7SQiuxu Zhuo if (!edac_debugfs_create_file("addr", 0200, igen6_test, 9202223d8c7SQiuxu Zhuo NULL, &fops_u64_wo)) { 9212223d8c7SQiuxu Zhuo debugfs_remove(igen6_test); 9222223d8c7SQiuxu Zhuo igen6_test = NULL; 9232223d8c7SQiuxu Zhuo } 9242223d8c7SQiuxu Zhuo } 9252223d8c7SQiuxu Zhuo 9262223d8c7SQiuxu Zhuo static void igen6_debug_teardown(void) 9272223d8c7SQiuxu Zhuo { 9282223d8c7SQiuxu Zhuo debugfs_remove_recursive(igen6_test); 9292223d8c7SQiuxu Zhuo } 93010590a9dSQiuxu Zhuo #else 93110590a9dSQiuxu Zhuo static void igen6_reg_dump(struct igen6_imc *imc) {} 9322223d8c7SQiuxu Zhuo static void igen6_debug_setup(void) {} 9332223d8c7SQiuxu Zhuo static void igen6_debug_teardown(void) {} 93410590a9dSQiuxu Zhuo #endif 93510590a9dSQiuxu Zhuo 93610590a9dSQiuxu Zhuo static int igen6_pci_setup(struct pci_dev *pdev, u64 *mchbar) 93710590a9dSQiuxu Zhuo { 93810590a9dSQiuxu Zhuo union { 93910590a9dSQiuxu Zhuo u64 v; 94010590a9dSQiuxu Zhuo struct { 94110590a9dSQiuxu Zhuo u32 v_lo; 94210590a9dSQiuxu Zhuo u32 v_hi; 94310590a9dSQiuxu Zhuo }; 94410590a9dSQiuxu Zhuo } u; 94510590a9dSQiuxu Zhuo 94610590a9dSQiuxu Zhuo edac_dbg(2, "\n"); 94710590a9dSQiuxu Zhuo 94810590a9dSQiuxu Zhuo if (!res_cfg->ibecc_available(pdev)) { 94910590a9dSQiuxu Zhuo edac_dbg(2, "No In-Band ECC IP\n"); 95010590a9dSQiuxu Zhuo goto fail; 95110590a9dSQiuxu Zhuo } 95210590a9dSQiuxu Zhuo 95310590a9dSQiuxu Zhuo if (pci_read_config_dword(pdev, TOLUD_OFFSET, &igen6_tolud)) { 95410590a9dSQiuxu Zhuo igen6_printk(KERN_ERR, "Failed to read TOLUD\n"); 95510590a9dSQiuxu Zhuo goto fail; 95610590a9dSQiuxu Zhuo } 95710590a9dSQiuxu Zhuo 95810590a9dSQiuxu Zhuo igen6_tolud &= GENMASK(31, 20); 95910590a9dSQiuxu Zhuo 96010590a9dSQiuxu Zhuo if (pci_read_config_dword(pdev, TOM_OFFSET, &u.v_lo)) { 96110590a9dSQiuxu Zhuo igen6_printk(KERN_ERR, "Failed to read lower TOM\n"); 96210590a9dSQiuxu Zhuo goto fail; 96310590a9dSQiuxu Zhuo } 96410590a9dSQiuxu Zhuo 96510590a9dSQiuxu Zhuo if (pci_read_config_dword(pdev, TOM_OFFSET + 4, &u.v_hi)) { 96610590a9dSQiuxu Zhuo igen6_printk(KERN_ERR, "Failed to read upper TOM\n"); 96710590a9dSQiuxu Zhuo goto fail; 96810590a9dSQiuxu Zhuo } 96910590a9dSQiuxu Zhuo 97010590a9dSQiuxu Zhuo igen6_tom = u.v & GENMASK_ULL(38, 20); 97110590a9dSQiuxu Zhuo 97210590a9dSQiuxu Zhuo if (pci_read_config_dword(pdev, MCHBAR_OFFSET, &u.v_lo)) { 97310590a9dSQiuxu Zhuo igen6_printk(KERN_ERR, "Failed to read lower MCHBAR\n"); 97410590a9dSQiuxu Zhuo goto fail; 97510590a9dSQiuxu Zhuo } 97610590a9dSQiuxu Zhuo 97710590a9dSQiuxu Zhuo if (pci_read_config_dword(pdev, MCHBAR_OFFSET + 4, &u.v_hi)) { 97810590a9dSQiuxu Zhuo igen6_printk(KERN_ERR, "Failed to read upper MCHBAR\n"); 97910590a9dSQiuxu Zhuo goto fail; 98010590a9dSQiuxu Zhuo } 98110590a9dSQiuxu Zhuo 98210590a9dSQiuxu Zhuo if (!(u.v & MCHBAR_EN)) { 98310590a9dSQiuxu Zhuo igen6_printk(KERN_ERR, "MCHBAR is disabled\n"); 98410590a9dSQiuxu Zhuo goto fail; 98510590a9dSQiuxu Zhuo } 98610590a9dSQiuxu Zhuo 98710590a9dSQiuxu Zhuo *mchbar = MCHBAR_BASE(u.v); 98810590a9dSQiuxu Zhuo 9892223d8c7SQiuxu Zhuo #ifdef CONFIG_EDAC_DEBUG 9902223d8c7SQiuxu Zhuo if (pci_read_config_dword(pdev, TOUUD_OFFSET, &u.v_lo)) 9912223d8c7SQiuxu Zhuo edac_dbg(2, "Failed to read lower TOUUD\n"); 9922223d8c7SQiuxu Zhuo else if (pci_read_config_dword(pdev, TOUUD_OFFSET + 4, &u.v_hi)) 9932223d8c7SQiuxu Zhuo edac_dbg(2, "Failed to read upper TOUUD\n"); 9942223d8c7SQiuxu Zhuo else 9952223d8c7SQiuxu Zhuo igen6_touud = u.v & GENMASK_ULL(38, 20); 9962223d8c7SQiuxu Zhuo #endif 9972223d8c7SQiuxu Zhuo 99810590a9dSQiuxu Zhuo return 0; 99910590a9dSQiuxu Zhuo fail: 100010590a9dSQiuxu Zhuo return -ENODEV; 100110590a9dSQiuxu Zhuo } 100210590a9dSQiuxu Zhuo 100310590a9dSQiuxu Zhuo static int igen6_register_mci(int mc, u64 mchbar, struct pci_dev *pdev) 100410590a9dSQiuxu Zhuo { 100510590a9dSQiuxu Zhuo struct edac_mc_layer layers[2]; 100610590a9dSQiuxu Zhuo struct mem_ctl_info *mci; 100710590a9dSQiuxu Zhuo struct igen6_imc *imc; 100810590a9dSQiuxu Zhuo void __iomem *window; 100910590a9dSQiuxu Zhuo int rc; 101010590a9dSQiuxu Zhuo 101110590a9dSQiuxu Zhuo edac_dbg(2, "\n"); 101210590a9dSQiuxu Zhuo 101310590a9dSQiuxu Zhuo mchbar += mc * MCHBAR_SIZE; 101410590a9dSQiuxu Zhuo window = ioremap(mchbar, MCHBAR_SIZE); 101510590a9dSQiuxu Zhuo if (!window) { 101610590a9dSQiuxu Zhuo igen6_printk(KERN_ERR, "Failed to ioremap 0x%llx\n", mchbar); 101710590a9dSQiuxu Zhuo return -ENODEV; 101810590a9dSQiuxu Zhuo } 101910590a9dSQiuxu Zhuo 102010590a9dSQiuxu Zhuo layers[0].type = EDAC_MC_LAYER_CHANNEL; 102110590a9dSQiuxu Zhuo layers[0].size = NUM_CHANNELS; 102210590a9dSQiuxu Zhuo layers[0].is_virt_csrow = false; 102310590a9dSQiuxu Zhuo layers[1].type = EDAC_MC_LAYER_SLOT; 102410590a9dSQiuxu Zhuo layers[1].size = NUM_DIMMS; 102510590a9dSQiuxu Zhuo layers[1].is_virt_csrow = true; 102610590a9dSQiuxu Zhuo 102710590a9dSQiuxu Zhuo mci = edac_mc_alloc(mc, ARRAY_SIZE(layers), layers, 0); 102810590a9dSQiuxu Zhuo if (!mci) { 102910590a9dSQiuxu Zhuo rc = -ENOMEM; 103010590a9dSQiuxu Zhuo goto fail; 103110590a9dSQiuxu Zhuo } 103210590a9dSQiuxu Zhuo 103310590a9dSQiuxu Zhuo mci->ctl_name = kasprintf(GFP_KERNEL, "Intel_client_SoC MC#%d", mc); 103410590a9dSQiuxu Zhuo if (!mci->ctl_name) { 103510590a9dSQiuxu Zhuo rc = -ENOMEM; 103610590a9dSQiuxu Zhuo goto fail2; 103710590a9dSQiuxu Zhuo } 103810590a9dSQiuxu Zhuo 103910590a9dSQiuxu Zhuo mci->mtype_cap = MEM_FLAG_LPDDR4 | MEM_FLAG_DDR4; 104010590a9dSQiuxu Zhuo mci->edac_ctl_cap = EDAC_FLAG_SECDED; 104110590a9dSQiuxu Zhuo mci->edac_cap = EDAC_FLAG_SECDED; 104210590a9dSQiuxu Zhuo mci->mod_name = EDAC_MOD_STR; 104310590a9dSQiuxu Zhuo mci->dev_name = pci_name(pdev); 104410590a9dSQiuxu Zhuo mci->pvt_info = &igen6_pvt->imc[mc]; 104510590a9dSQiuxu Zhuo 104610590a9dSQiuxu Zhuo imc = mci->pvt_info; 104710590a9dSQiuxu Zhuo device_initialize(&imc->dev); 104810590a9dSQiuxu Zhuo /* 104910590a9dSQiuxu Zhuo * EDAC core uses mci->pdev(pointer of structure device) as 105010590a9dSQiuxu Zhuo * memory controller ID. The client SoCs attach one or more 105110590a9dSQiuxu Zhuo * memory controllers to single pci_dev (single pci_dev->dev 105210590a9dSQiuxu Zhuo * can be for multiple memory controllers). 105310590a9dSQiuxu Zhuo * 105410590a9dSQiuxu Zhuo * To make mci->pdev unique, assign pci_dev->dev to mci->pdev 105510590a9dSQiuxu Zhuo * for the first memory controller and assign a unique imc->dev 105610590a9dSQiuxu Zhuo * to mci->pdev for each non-first memory controller. 105710590a9dSQiuxu Zhuo */ 105810590a9dSQiuxu Zhuo mci->pdev = mc ? &imc->dev : &pdev->dev; 105910590a9dSQiuxu Zhuo imc->mc = mc; 106010590a9dSQiuxu Zhuo imc->pdev = pdev; 106110590a9dSQiuxu Zhuo imc->window = window; 106210590a9dSQiuxu Zhuo 106310590a9dSQiuxu Zhuo igen6_reg_dump(imc); 106410590a9dSQiuxu Zhuo 106510590a9dSQiuxu Zhuo rc = igen6_get_dimm_config(mci); 106610590a9dSQiuxu Zhuo if (rc) 106710590a9dSQiuxu Zhuo goto fail3; 106810590a9dSQiuxu Zhuo 106910590a9dSQiuxu Zhuo rc = edac_mc_add_mc(mci); 107010590a9dSQiuxu Zhuo if (rc) { 107110590a9dSQiuxu Zhuo igen6_printk(KERN_ERR, "Failed to register mci#%d\n", mc); 107210590a9dSQiuxu Zhuo goto fail3; 107310590a9dSQiuxu Zhuo } 107410590a9dSQiuxu Zhuo 107510590a9dSQiuxu Zhuo imc->mci = mci; 107610590a9dSQiuxu Zhuo return 0; 107710590a9dSQiuxu Zhuo fail3: 107810590a9dSQiuxu Zhuo kfree(mci->ctl_name); 107910590a9dSQiuxu Zhuo fail2: 108010590a9dSQiuxu Zhuo edac_mc_free(mci); 108110590a9dSQiuxu Zhuo fail: 108210590a9dSQiuxu Zhuo iounmap(window); 108310590a9dSQiuxu Zhuo return rc; 108410590a9dSQiuxu Zhuo } 108510590a9dSQiuxu Zhuo 108610590a9dSQiuxu Zhuo static void igen6_unregister_mcis(void) 108710590a9dSQiuxu Zhuo { 108810590a9dSQiuxu Zhuo struct mem_ctl_info *mci; 108910590a9dSQiuxu Zhuo struct igen6_imc *imc; 109010590a9dSQiuxu Zhuo int i; 109110590a9dSQiuxu Zhuo 109210590a9dSQiuxu Zhuo edac_dbg(2, "\n"); 109310590a9dSQiuxu Zhuo 109410590a9dSQiuxu Zhuo for (i = 0; i < res_cfg->num_imc; i++) { 109510590a9dSQiuxu Zhuo imc = &igen6_pvt->imc[i]; 109610590a9dSQiuxu Zhuo mci = imc->mci; 109710590a9dSQiuxu Zhuo if (!mci) 109810590a9dSQiuxu Zhuo continue; 109910590a9dSQiuxu Zhuo 110010590a9dSQiuxu Zhuo edac_mc_del_mc(mci->pdev); 110110590a9dSQiuxu Zhuo kfree(mci->ctl_name); 110210590a9dSQiuxu Zhuo edac_mc_free(mci); 110310590a9dSQiuxu Zhuo iounmap(imc->window); 110410590a9dSQiuxu Zhuo } 110510590a9dSQiuxu Zhuo } 110610590a9dSQiuxu Zhuo 11070b7338b2SQiuxu Zhuo static int igen6_mem_slice_setup(u64 mchbar) 11080b7338b2SQiuxu Zhuo { 11090b7338b2SQiuxu Zhuo struct igen6_imc *imc = &igen6_pvt->imc[0]; 11100b7338b2SQiuxu Zhuo u64 base = mchbar + res_cfg->cmf_base; 11110b7338b2SQiuxu Zhuo u32 offset = res_cfg->ms_hash_offset; 11120b7338b2SQiuxu Zhuo u32 size = res_cfg->cmf_size; 11130b7338b2SQiuxu Zhuo u64 ms_s_size, ms_hash; 11140b7338b2SQiuxu Zhuo void __iomem *cmf; 11150b7338b2SQiuxu Zhuo int ms_l_map; 11160b7338b2SQiuxu Zhuo 11170b7338b2SQiuxu Zhuo edac_dbg(2, "\n"); 11180b7338b2SQiuxu Zhuo 11190b7338b2SQiuxu Zhuo if (imc[0].size < imc[1].size) { 11200b7338b2SQiuxu Zhuo ms_s_size = imc[0].size; 11210b7338b2SQiuxu Zhuo ms_l_map = 1; 11220b7338b2SQiuxu Zhuo } else { 11230b7338b2SQiuxu Zhuo ms_s_size = imc[1].size; 11240b7338b2SQiuxu Zhuo ms_l_map = 0; 11250b7338b2SQiuxu Zhuo } 11260b7338b2SQiuxu Zhuo 11270b7338b2SQiuxu Zhuo igen6_pvt->ms_s_size = ms_s_size; 11280b7338b2SQiuxu Zhuo igen6_pvt->ms_l_map = ms_l_map; 11290b7338b2SQiuxu Zhuo 11300b7338b2SQiuxu Zhuo edac_dbg(0, "ms_s_size: %llu MiB, ms_l_map %d\n", 11310b7338b2SQiuxu Zhuo ms_s_size >> 20, ms_l_map); 11320b7338b2SQiuxu Zhuo 1133ad774bd5SQiuxu Zhuo if (!size) 1134ad774bd5SQiuxu Zhuo return 0; 1135ad774bd5SQiuxu Zhuo 11360b7338b2SQiuxu Zhuo cmf = ioremap(base, size); 11370b7338b2SQiuxu Zhuo if (!cmf) { 11380b7338b2SQiuxu Zhuo igen6_printk(KERN_ERR, "Failed to ioremap cmf 0x%llx\n", base); 11390b7338b2SQiuxu Zhuo return -ENODEV; 11400b7338b2SQiuxu Zhuo } 11410b7338b2SQiuxu Zhuo 11420b7338b2SQiuxu Zhuo ms_hash = readq(cmf + offset); 11430b7338b2SQiuxu Zhuo igen6_pvt->ms_hash = ms_hash; 11440b7338b2SQiuxu Zhuo 11450b7338b2SQiuxu Zhuo edac_dbg(0, "MEM_SLICE_HASH: 0x%llx\n", ms_hash); 11460b7338b2SQiuxu Zhuo 11470b7338b2SQiuxu Zhuo iounmap(cmf); 11480b7338b2SQiuxu Zhuo 11490b7338b2SQiuxu Zhuo return 0; 11500b7338b2SQiuxu Zhuo } 11510b7338b2SQiuxu Zhuo 11520b7338b2SQiuxu Zhuo static int register_err_handler(void) 11530b7338b2SQiuxu Zhuo { 11540b7338b2SQiuxu Zhuo int rc; 11550b7338b2SQiuxu Zhuo 11560b7338b2SQiuxu Zhuo if (res_cfg->machine_check) { 11570b7338b2SQiuxu Zhuo mce_register_decode_chain(&ecclog_mce_dec); 11580b7338b2SQiuxu Zhuo return 0; 11590b7338b2SQiuxu Zhuo } 11600b7338b2SQiuxu Zhuo 11610b7338b2SQiuxu Zhuo rc = register_nmi_handler(NMI_SERR, ecclog_nmi_handler, 11620b7338b2SQiuxu Zhuo 0, IGEN6_NMI_NAME); 11630b7338b2SQiuxu Zhuo if (rc) { 11640b7338b2SQiuxu Zhuo igen6_printk(KERN_ERR, "Failed to register NMI handler\n"); 11650b7338b2SQiuxu Zhuo return rc; 11660b7338b2SQiuxu Zhuo } 11670b7338b2SQiuxu Zhuo 11680b7338b2SQiuxu Zhuo return 0; 11690b7338b2SQiuxu Zhuo } 11700b7338b2SQiuxu Zhuo 11710b7338b2SQiuxu Zhuo static void unregister_err_handler(void) 11720b7338b2SQiuxu Zhuo { 11730b7338b2SQiuxu Zhuo if (res_cfg->machine_check) { 11740b7338b2SQiuxu Zhuo mce_unregister_decode_chain(&ecclog_mce_dec); 11750b7338b2SQiuxu Zhuo return; 11760b7338b2SQiuxu Zhuo } 11770b7338b2SQiuxu Zhuo 11780b7338b2SQiuxu Zhuo unregister_nmi_handler(NMI_SERR, IGEN6_NMI_NAME); 11790b7338b2SQiuxu Zhuo } 11800b7338b2SQiuxu Zhuo 118110590a9dSQiuxu Zhuo static int igen6_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 118210590a9dSQiuxu Zhuo { 118310590a9dSQiuxu Zhuo u64 mchbar; 118410590a9dSQiuxu Zhuo int i, rc; 118510590a9dSQiuxu Zhuo 118610590a9dSQiuxu Zhuo edac_dbg(2, "\n"); 118710590a9dSQiuxu Zhuo 118810590a9dSQiuxu Zhuo igen6_pvt = kzalloc(sizeof(*igen6_pvt), GFP_KERNEL); 118910590a9dSQiuxu Zhuo if (!igen6_pvt) 119010590a9dSQiuxu Zhuo return -ENOMEM; 119110590a9dSQiuxu Zhuo 119210590a9dSQiuxu Zhuo res_cfg = (struct res_config *)ent->driver_data; 119310590a9dSQiuxu Zhuo 119410590a9dSQiuxu Zhuo rc = igen6_pci_setup(pdev, &mchbar); 119510590a9dSQiuxu Zhuo if (rc) 119610590a9dSQiuxu Zhuo goto fail; 119710590a9dSQiuxu Zhuo 119810590a9dSQiuxu Zhuo for (i = 0; i < res_cfg->num_imc; i++) { 119910590a9dSQiuxu Zhuo rc = igen6_register_mci(i, mchbar, pdev); 120010590a9dSQiuxu Zhuo if (rc) 120110590a9dSQiuxu Zhuo goto fail2; 120210590a9dSQiuxu Zhuo } 120310590a9dSQiuxu Zhuo 12040b7338b2SQiuxu Zhuo if (res_cfg->num_imc > 1) { 12050b7338b2SQiuxu Zhuo rc = igen6_mem_slice_setup(mchbar); 12060b7338b2SQiuxu Zhuo if (rc) 12070b7338b2SQiuxu Zhuo goto fail2; 12080b7338b2SQiuxu Zhuo } 12090b7338b2SQiuxu Zhuo 121010590a9dSQiuxu Zhuo ecclog_pool = ecclog_gen_pool_create(); 121110590a9dSQiuxu Zhuo if (!ecclog_pool) { 121210590a9dSQiuxu Zhuo rc = -ENOMEM; 121310590a9dSQiuxu Zhuo goto fail2; 121410590a9dSQiuxu Zhuo } 121510590a9dSQiuxu Zhuo 121610590a9dSQiuxu Zhuo INIT_WORK(&ecclog_work, ecclog_work_cb); 121710590a9dSQiuxu Zhuo init_irq_work(&ecclog_irq_work, ecclog_irq_work_cb); 121810590a9dSQiuxu Zhuo 121910590a9dSQiuxu Zhuo /* Check if any pending errors before registering the NMI handler */ 122010590a9dSQiuxu Zhuo ecclog_handler(); 122110590a9dSQiuxu Zhuo 12220b7338b2SQiuxu Zhuo rc = register_err_handler(); 12230b7338b2SQiuxu Zhuo if (rc) 122410590a9dSQiuxu Zhuo goto fail3; 122510590a9dSQiuxu Zhuo 122610590a9dSQiuxu Zhuo /* Enable error reporting */ 122710590a9dSQiuxu Zhuo rc = errcmd_enable_error_reporting(true); 122810590a9dSQiuxu Zhuo if (rc) { 122910590a9dSQiuxu Zhuo igen6_printk(KERN_ERR, "Failed to enable error reporting\n"); 123010590a9dSQiuxu Zhuo goto fail4; 123110590a9dSQiuxu Zhuo } 123210590a9dSQiuxu Zhuo 12332223d8c7SQiuxu Zhuo igen6_debug_setup(); 123410590a9dSQiuxu Zhuo return 0; 123510590a9dSQiuxu Zhuo fail4: 123610590a9dSQiuxu Zhuo unregister_nmi_handler(NMI_SERR, IGEN6_NMI_NAME); 123710590a9dSQiuxu Zhuo fail3: 123810590a9dSQiuxu Zhuo gen_pool_destroy(ecclog_pool); 123910590a9dSQiuxu Zhuo fail2: 124010590a9dSQiuxu Zhuo igen6_unregister_mcis(); 124110590a9dSQiuxu Zhuo fail: 124210590a9dSQiuxu Zhuo kfree(igen6_pvt); 124310590a9dSQiuxu Zhuo return rc; 124410590a9dSQiuxu Zhuo } 124510590a9dSQiuxu Zhuo 124610590a9dSQiuxu Zhuo static void igen6_remove(struct pci_dev *pdev) 124710590a9dSQiuxu Zhuo { 124810590a9dSQiuxu Zhuo edac_dbg(2, "\n"); 124910590a9dSQiuxu Zhuo 12502223d8c7SQiuxu Zhuo igen6_debug_teardown(); 125110590a9dSQiuxu Zhuo errcmd_enable_error_reporting(false); 12520b7338b2SQiuxu Zhuo unregister_err_handler(); 125310590a9dSQiuxu Zhuo irq_work_sync(&ecclog_irq_work); 125410590a9dSQiuxu Zhuo flush_work(&ecclog_work); 125510590a9dSQiuxu Zhuo gen_pool_destroy(ecclog_pool); 125610590a9dSQiuxu Zhuo igen6_unregister_mcis(); 125710590a9dSQiuxu Zhuo kfree(igen6_pvt); 125810590a9dSQiuxu Zhuo } 125910590a9dSQiuxu Zhuo 126010590a9dSQiuxu Zhuo static struct pci_driver igen6_driver = { 126110590a9dSQiuxu Zhuo .name = EDAC_MOD_STR, 126210590a9dSQiuxu Zhuo .probe = igen6_probe, 126310590a9dSQiuxu Zhuo .remove = igen6_remove, 126410590a9dSQiuxu Zhuo .id_table = igen6_pci_tbl, 126510590a9dSQiuxu Zhuo }; 126610590a9dSQiuxu Zhuo 126710590a9dSQiuxu Zhuo static int __init igen6_init(void) 126810590a9dSQiuxu Zhuo { 126910590a9dSQiuxu Zhuo const char *owner; 127010590a9dSQiuxu Zhuo int rc; 127110590a9dSQiuxu Zhuo 127210590a9dSQiuxu Zhuo edac_dbg(2, "\n"); 127310590a9dSQiuxu Zhuo 1274*315bada6SJia He if (ghes_get_devices()) 1275*315bada6SJia He return -EBUSY; 1276*315bada6SJia He 127710590a9dSQiuxu Zhuo owner = edac_get_owner(); 127810590a9dSQiuxu Zhuo if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR))) 127910590a9dSQiuxu Zhuo return -ENODEV; 128010590a9dSQiuxu Zhuo 128110590a9dSQiuxu Zhuo edac_op_state = EDAC_OPSTATE_NMI; 128210590a9dSQiuxu Zhuo 128310590a9dSQiuxu Zhuo rc = pci_register_driver(&igen6_driver); 128410590a9dSQiuxu Zhuo if (rc) 128510590a9dSQiuxu Zhuo return rc; 128610590a9dSQiuxu Zhuo 128710590a9dSQiuxu Zhuo igen6_printk(KERN_INFO, "%s\n", IGEN6_REVISION); 128810590a9dSQiuxu Zhuo 128910590a9dSQiuxu Zhuo return 0; 129010590a9dSQiuxu Zhuo } 129110590a9dSQiuxu Zhuo 129210590a9dSQiuxu Zhuo static void __exit igen6_exit(void) 129310590a9dSQiuxu Zhuo { 129410590a9dSQiuxu Zhuo edac_dbg(2, "\n"); 129510590a9dSQiuxu Zhuo 129610590a9dSQiuxu Zhuo pci_unregister_driver(&igen6_driver); 129710590a9dSQiuxu Zhuo } 129810590a9dSQiuxu Zhuo 129910590a9dSQiuxu Zhuo module_init(igen6_init); 130010590a9dSQiuxu Zhuo module_exit(igen6_exit); 130110590a9dSQiuxu Zhuo 130210590a9dSQiuxu Zhuo MODULE_LICENSE("GPL v2"); 130310590a9dSQiuxu Zhuo MODULE_AUTHOR("Qiuxu Zhuo"); 130410590a9dSQiuxu Zhuo MODULE_DESCRIPTION("MC Driver for Intel client SoC using In-Band ECC"); 1305