diff options
Diffstat (limited to 'drivers/edac')
54 files changed, 1351 insertions, 704 deletions
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c index 58d3e2b39b5b..c5a5b91f37f0 100644 --- a/drivers/edac/altera_edac.c +++ b/drivers/edac/altera_edac.c @@ -35,7 +35,6 @@ #include <linux/uaccess.h> #include "altera_edac.h" -#include "edac_core.h" #include "edac_module.h" #define EDAC_MOD_STR "altera_edac" @@ -153,13 +152,17 @@ static ssize_t altr_sdr_mc_err_inject_write(struct file *file, if (count == 3) { edac_printk(KERN_ALERT, EDAC_MC, "Inject Double bit error\n"); + local_irq_disable(); regmap_write(drvdata->mc_vbase, priv->ce_ue_trgr_offset, (read_reg | priv->ue_set_mask)); + local_irq_enable(); } else { edac_printk(KERN_ALERT, EDAC_MC, "Inject Single bit error\n"); + local_irq_disable(); regmap_write(drvdata->mc_vbase, priv->ce_ue_trgr_offset, (read_reg | priv->ce_set_mask)); + local_irq_enable(); } ptemp[0] = 0x5A5A5A5A; diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index ee181c53626f..260251177830 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -164,8 +164,23 @@ static inline int amd64_read_dct_pci_cfg(struct amd64_pvt *pvt, u8 dct, * other archs, we might not have access to the caches directly. */ +static inline void __f17h_set_scrubval(struct amd64_pvt *pvt, u32 scrubval) +{ + /* + * Fam17h supports scrub values between 0x5 and 0x14. Also, the values + * are shifted down by 0x5, so scrubval 0x5 is written to the register + * as 0x0, scrubval 0x6 as 0x1, etc. + */ + if (scrubval >= 0x5 && scrubval <= 0x14) { + scrubval -= 0x5; + pci_write_bits32(pvt->F6, F17H_SCR_LIMIT_ADDR, scrubval, 0xF); + pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 1, 0x1); + } else { + pci_write_bits32(pvt->F6, F17H_SCR_BASE_ADDR, 0, 0x1); + } +} /* - * scan the scrub rate mapping table for a close or matching bandwidth value to + * Scan the scrub rate mapping table for a close or matching bandwidth value to * issue. If requested is too big, then use last maximum value found. */ static int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate) @@ -196,7 +211,9 @@ static int __set_scrub_rate(struct amd64_pvt *pvt, u32 new_bw, u32 min_rate) scrubval = scrubrates[i].scrubval; - if (pvt->fam == 0x15 && pvt->model == 0x60) { + if (pvt->fam == 0x17) { + __f17h_set_scrubval(pvt, scrubval); + } else if (pvt->fam == 0x15 && pvt->model == 0x60) { f15h_select_dct(pvt, 0); pci_write_bits32(pvt->F2, F15H_M60H_SCRCTRL, scrubval, 0x001F); f15h_select_dct(pvt, 1); @@ -233,18 +250,34 @@ static int set_scrub_rate(struct mem_ctl_info *mci, u32 bw) static int get_scrub_rate(struct mem_ctl_info *mci) { struct amd64_pvt *pvt = mci->pvt_info; - u32 scrubval = 0; int i, retval = -EINVAL; + u32 scrubval = 0; - if (pvt->fam == 0x15) { + switch (pvt->fam) { + case 0x15: /* Erratum #505 */ if (pvt->model < 0x10) f15h_select_dct(pvt, 0); if (pvt->model == 0x60) amd64_read_pci_cfg(pvt->F2, F15H_M60H_SCRCTRL, &scrubval); - } else + break; + + case 0x17: + amd64_read_pci_cfg(pvt->F6, F17H_SCR_BASE_ADDR, &scrubval); + if (scrubval & BIT(0)) { + amd64_read_pci_cfg(pvt->F6, F17H_SCR_LIMIT_ADDR, &scrubval); + scrubval &= 0xF; + scrubval += 0x5; + } else { + scrubval = 0; + } + break; + + default: amd64_read_pci_cfg(pvt->F3, SCRCTRL, &scrubval); + break; + } scrubval = scrubval & 0x001F; @@ -682,15 +715,33 @@ static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16); */ static unsigned long determine_edac_cap(struct amd64_pvt *pvt) { - u8 bit; unsigned long edac_cap = EDAC_FLAG_NONE; + u8 bit; - bit = (pvt->fam > 0xf || pvt->ext_model >= K8_REV_F) - ? 19 - : 17; + if (pvt->umc) { + u8 i, umc_en_mask = 0, dimm_ecc_en_mask = 0; - if (pvt->dclr0 & BIT(bit)) - edac_cap = EDAC_FLAG_SECDED; + for (i = 0; i < NUM_UMCS; i++) { + if (!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT)) + continue; + + umc_en_mask |= BIT(i); + + /* UMC Configuration bit 12 (DimmEccEn) */ + if (pvt->umc[i].umc_cfg & BIT(12)) + dimm_ecc_en_mask |= BIT(i); + } + + if (umc_en_mask == dimm_ecc_en_mask) + edac_cap = EDAC_FLAG_SECDED; + } else { + bit = (pvt->fam > 0xf || pvt->ext_model >= K8_REV_F) + ? 19 + : 17; + + if (pvt->dclr0 & BIT(bit)) + edac_cap = EDAC_FLAG_SECDED; + } return edac_cap; } @@ -729,8 +780,75 @@ static void debug_dump_dramcfg_low(struct amd64_pvt *pvt, u32 dclr, int chan) (dclr & BIT(15)) ? "yes" : "no"); } +static void debug_display_dimm_sizes_df(struct amd64_pvt *pvt, u8 ctrl) +{ + u32 *dcsb = ctrl ? pvt->csels[1].csbases : pvt->csels[0].csbases; + int dimm, size0, size1; + + edac_printk(KERN_DEBUG, EDAC_MC, "UMC%d chip selects:\n", ctrl); + + for (dimm = 0; dimm < 4; dimm++) { + size0 = 0; + + if (dcsb[dimm*2] & DCSB_CS_ENABLE) + size0 = pvt->ops->dbam_to_cs(pvt, ctrl, 0, dimm); + + size1 = 0; + if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE) + size1 = pvt->ops->dbam_to_cs(pvt, ctrl, 0, dimm); + + amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n", + dimm * 2, size0, + dimm * 2 + 1, size1); + } +} + +static void __dump_misc_regs_df(struct amd64_pvt *pvt) +{ + struct amd64_umc *umc; + u32 i, tmp, umc_base; + + for (i = 0; i < NUM_UMCS; i++) { + umc_base = get_umc_base(i); + umc = &pvt->umc[i]; + + edac_dbg(1, "UMC%d DIMM cfg: 0x%x\n", i, umc->dimm_cfg); + edac_dbg(1, "UMC%d UMC cfg: 0x%x\n", i, umc->umc_cfg); + edac_dbg(1, "UMC%d SDP ctrl: 0x%x\n", i, umc->sdp_ctrl); + edac_dbg(1, "UMC%d ECC ctrl: 0x%x\n", i, umc->ecc_ctrl); + + amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ECC_BAD_SYMBOL, &tmp); + edac_dbg(1, "UMC%d ECC bad symbol: 0x%x\n", i, tmp); + + amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_UMC_CAP, &tmp); + edac_dbg(1, "UMC%d UMC cap: 0x%x\n", i, tmp); + edac_dbg(1, "UMC%d UMC cap high: 0x%x\n", i, umc->umc_cap_hi); + + edac_dbg(1, "UMC%d ECC capable: %s, ChipKill ECC capable: %s\n", + i, (umc->umc_cap_hi & BIT(30)) ? "yes" : "no", + (umc->umc_cap_hi & BIT(31)) ? "yes" : "no"); + edac_dbg(1, "UMC%d All DIMMs support ECC: %s\n", + i, (umc->umc_cfg & BIT(12)) ? "yes" : "no"); + edac_dbg(1, "UMC%d x4 DIMMs present: %s\n", + i, (umc->dimm_cfg & BIT(6)) ? "yes" : "no"); + edac_dbg(1, "UMC%d x16 DIMMs present: %s\n", + i, (umc->dimm_cfg & BIT(7)) ? "yes" : "no"); + + if (pvt->dram_type == MEM_LRDDR4) { + amd_smn_read(pvt->mc_node_id, umc_base + UMCCH_ADDR_CFG, &tmp); + edac_dbg(1, "UMC%d LRDIMM %dx rank multiply\n", + i, 1 << ((tmp >> 4) & 0x3)); + } + + debug_display_dimm_sizes_df(pvt, i); + } + + edac_dbg(1, "F0x104 (DRAM Hole Address): 0x%08x, base: 0x%08x\n", + pvt->dhar, dhar_base(pvt)); +} + /* Display and decode various NB registers for debug purposes. */ -static void dump_misc_regs(struct amd64_pvt *pvt) +static void __dump_misc_regs(struct amd64_pvt *pvt) { edac_dbg(1, "F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap); @@ -750,8 +868,6 @@ static void dump_misc_regs(struct amd64_pvt *pvt) (pvt->fam == 0xf) ? k8_dhar_offset(pvt) : f10_dhar_offset(pvt)); - edac_dbg(1, " DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no"); - debug_display_dimm_sizes(pvt, 0); /* everything below this point is Fam10h and above */ @@ -760,13 +876,25 @@ static void dump_misc_regs(struct amd64_pvt *pvt) debug_display_dimm_sizes(pvt, 1); - amd64_info("using %s syndromes.\n", ((pvt->ecc_sym_sz == 8) ? "x8" : "x4")); - /* Only if NOT ganged does dclr1 have valid info */ if (!dct_ganging_enabled(pvt)) debug_dump_dramcfg_low(pvt, pvt->dclr1, 1); } +/* Display and decode various NB registers for debug purposes. */ +static void dump_misc_regs(struct amd64_pvt *pvt) +{ + if (pvt->umc) + __dump_misc_regs_df(pvt); + else + __dump_misc_regs(pvt); + + edac_dbg(1, " DramHoleValid: %s\n", dhar_valid(pvt) ? "yes" : "no"); + + amd64_info("using %s syndromes.\n", + ((pvt->ecc_sym_sz == 8) ? "x8" : "x4")); +} + /* * See BKDG, F2x[1,0][5C:40], F2[1,0][6C:60] */ @@ -789,46 +917,78 @@ static void prep_chip_selects(struct amd64_pvt *pvt) */ static void read_dct_base_mask(struct amd64_pvt *pvt) { - int cs; + int base_reg0, base_reg1, mask_reg0, mask_reg1, cs; prep_chip_selects(pvt); + if (pvt->umc) { + base_reg0 = get_umc_base(0) + UMCCH_BASE_ADDR; + base_reg1 = get_umc_base(1) + UMCCH_BASE_ADDR; + mask_reg0 = get_umc_base(0) + UMCCH_ADDR_MASK; + mask_reg1 = get_umc_base(1) + UMCCH_ADDR_MASK; + } else { + base_reg0 = DCSB0; + base_reg1 = DCSB1; + mask_reg0 = DCSM0; + mask_reg1 = DCSM1; + } + for_each_chip_select(cs, 0, pvt) { - int reg0 = DCSB0 + (cs * 4); - int reg1 = DCSB1 + (cs * 4); + int reg0 = base_reg0 + (cs * 4); + int reg1 = base_reg1 + (cs * 4); u32 *base0 = &pvt->csels[0].csbases[cs]; u32 *base1 = &pvt->csels[1].csbases[cs]; - if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0)) - edac_dbg(0, " DCSB0[%d]=0x%08x reg: F2x%x\n", - cs, *base0, reg0); + if (pvt->umc) { + if (!amd_smn_read(pvt->mc_node_id, reg0, base0)) + edac_dbg(0, " DCSB0[%d]=0x%08x reg: 0x%x\n", + cs, *base0, reg0); - if (pvt->fam == 0xf) - continue; + if (!amd_smn_read(pvt->mc_node_id, reg1, base1)) + edac_dbg(0, " DCSB1[%d]=0x%08x reg: 0x%x\n", + cs, *base1, reg1); + } else { + if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, base0)) + edac_dbg(0, " DCSB0[%d]=0x%08x reg: F2x%x\n", + cs, *base0, reg0); + + if (pvt->fam == 0xf) + continue; - if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1)) - edac_dbg(0, " DCSB1[%d]=0x%08x reg: F2x%x\n", - cs, *base1, (pvt->fam == 0x10) ? reg1 + if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, base1)) + edac_dbg(0, " DCSB1[%d]=0x%08x reg: F2x%x\n", + cs, *base1, (pvt->fam == 0x10) ? reg1 : reg0); + } } for_each_chip_select_mask(cs, 0, pvt) { - int reg0 = DCSM0 + (cs * 4); - int reg1 = DCSM1 + (cs * 4); + int reg0 = mask_reg0 + (cs * 4); + int reg1 = mask_reg1 + (cs * 4); u32 *mask0 = &pvt->csels[0].csmasks[cs]; u32 *mask1 = &pvt->csels[1].csmasks[cs]; - if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0)) - edac_dbg(0, " DCSM0[%d]=0x%08x reg: F2x%x\n", - cs, *mask0, reg0); + if (pvt->umc) { + if (!amd_smn_read(pvt->mc_node_id, reg0, mask0)) + edac_dbg(0, " DCSM0[%d]=0x%08x reg: 0x%x\n", + cs, *mask0, reg0); - if (pvt->fam == 0xf) - continue; + if (!amd_smn_read(pvt->mc_node_id, reg1, mask1)) + edac_dbg(0, " DCSM1[%d]=0x%08x reg: 0x%x\n", + cs, *mask1, reg1); + } else { + if (!amd64_read_dct_pci_cfg(pvt, 0, reg0, mask0)) + edac_dbg(0, " DCSM0[%d]=0x%08x reg: F2x%x\n", + cs, *mask0, reg0); + + if (pvt->fam == 0xf) + continue; - if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1)) - edac_dbg(0, " DCSM1[%d]=0x%08x reg: F2x%x\n", - cs, *mask1, (pvt->fam == 0x10) ? reg1 + if (!amd64_read_dct_pci_cfg(pvt, 1, reg0, mask1)) + edac_dbg(0, " DCSM1[%d]=0x%08x reg: F2x%x\n", + cs, *mask1, (pvt->fam == 0x10) ? reg1 : reg0); + } } } @@ -881,6 +1041,15 @@ static void determine_memory_type(struct amd64_pvt *pvt) case 0x16: goto ddr3; + case 0x17: + if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(5)) + pvt->dram_type = MEM_LRDDR4; + else if ((pvt->umc[0].dimm_cfg | pvt->umc[1].dimm_cfg) & BIT(4)) + pvt->dram_type = MEM_RDDR4; + else + pvt->dram_type = MEM_DDR4; + return; + default: WARN(1, KERN_ERR "%s: Family??? 0x%x\n", __func__, pvt->fam); pvt->dram_type = MEM_EMPTY; @@ -1210,6 +1379,19 @@ static int f1x_early_channel_count(struct amd64_pvt *pvt) return channels; } +static int f17_early_channel_count(struct amd64_pvt *pvt) +{ + int i, channels = 0; + + /* SDP Control bit 31 (SdpInit) is clear for unused UMC channels */ + for (i = 0; i < NUM_UMCS; i++) + channels += !!(pvt->umc[i].sdp_ctrl & UMC_SDP_INIT); + + amd64_info("MCT channel count: %d\n", channels); + + return channels; +} + static int ddr3_cs_size(unsigned i, bool dct_width) { unsigned shift = 0; @@ -1337,6 +1519,23 @@ static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct, return ddr3_cs_size(cs_mode, false); } +static int f17_base_addr_to_cs_size(struct amd64_pvt *pvt, u8 umc, + unsigned int cs_mode, int csrow_nr) +{ + u32 base_addr = pvt->csels[umc].csbases[csrow_nr]; + + /* Each mask is used for every two base addresses. */ + u32 addr_mask = pvt->csels[umc].csmasks[csrow_nr >> 1]; + + /* Register [31:1] = Address [39:9]. Size is in kBs here. */ + u32 size = ((addr_mask >> 1) - (base_addr >> 1) + 1) >> 1; + + edac_dbg(1, "BaseAddr: 0x%x, AddrMask: 0x%x\n", base_addr, addr_mask); + + /* Return size in MBs. */ + return size >> 10; +} + static void read_dram_ctl_register(struct amd64_pvt *pvt) { @@ -1897,8 +2096,9 @@ static void debug_display_dimm_sizes(struct amd64_pvt *pvt, u8 ctrl) size0 = 0; if (dcsb[dimm*2] & DCSB_CS_ENABLE) - /* For f15m60h, need multiplier for LRDIMM cs_size - * calculation. We pass 'dimm' value to the dbam_to_cs + /* + * For F15m60h, we need multiplier for LRDIMM cs_size + * calculation. We pass dimm value to the dbam_to_cs * mapper so we can find the multiplier from the * corresponding DCSM. */ @@ -1989,6 +2189,15 @@ static struct amd64_family_type family_types[] = { .dbam_to_cs = f16_dbam_to_chip_select, } }, + [F17_CPUS] = { + .ctl_name = "F17h", + .f0_id = PCI_DEVICE_ID_AMD_17H_DF_F0, + .f6_id = PCI_DEVICE_ID_AMD_17H_DF_F6, + .ops = { + .early_channel_count = f17_early_channel_count, + .dbam_to_cs = f17_base_addr_to_cs_size, + } + }, }; /* @@ -2155,7 +2364,7 @@ static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome) return map_err_sym_to_channel(err_sym, pvt->ecc_sym_sz); } -static void __log_bus_error(struct mem_ctl_info *mci, struct err_info *err, +static void __log_ecc_error(struct mem_ctl_info *mci, struct err_info *err, u8 ecc_type) { enum hw_event_mc_err_type err_type; @@ -2165,6 +2374,8 @@ static void __log_bus_error(struct mem_ctl_info *mci, struct err_info *err, err_type = HW_EVENT_ERR_CORRECTED; else if (ecc_type == 1) err_type = HW_EVENT_ERR_UNCORRECTED; + else if (ecc_type == 3) + err_type = HW_EVENT_ERR_DEFERRED; else { WARN(1, "Something is rotten in the state of Denmark.\n"); return; @@ -2181,7 +2392,13 @@ static void __log_bus_error(struct mem_ctl_info *mci, struct err_info *err, string = "Failed to map error addr to a csrow"; break; case ERR_CHANNEL: - string = "unknown syndrome - possible error reporting race"; + string = "Unknown syndrome - possible error reporting race"; + break; + case ERR_SYND: + string = "MCA_SYND not valid - unknown syndrome and csrow"; + break; + case ERR_NORM_ADDR: + string = "Cannot decode normalized address"; break; default: string = "WTF error"; @@ -2227,36 +2444,127 @@ static inline void decode_bus_error(int node_id, struct mce *m) pvt->ops->map_sysaddr_to_csrow(mci, sys_addr, &err); - __log_bus_error(mci, &err, ecc_type); + __log_ecc_error(mci, &err, ecc_type); +} + +/* + * To find the UMC channel represented by this bank we need to match on its + * instance_id. The instance_id of a bank is held in the lower 32 bits of its + * IPID. + */ +static int find_umc_channel(struct amd64_pvt *pvt, struct mce *m) +{ + u32 umc_instance_id[] = {0x50f00, 0x150f00}; + u32 instance_id = m->ipid & GENMASK(31, 0); + int i, channel = -1; + + for (i = 0; i < ARRAY_SIZE(umc_instance_id); i++) + if (umc_instance_id[i] == instance_id) + channel = i; + + return channel; +} + +static void decode_umc_error(int node_id, struct mce *m) +{ + u8 ecc_type = (m->status >> 45) & 0x3; + struct mem_ctl_info *mci; + struct amd64_pvt *pvt; + struct err_info err; + u64 sys_addr; + + mci = edac_mc_find(node_id); + if (!mci) + return; + + pvt = mci->pvt_info; + + memset(&err, 0, sizeof(err)); + + if (m->status & MCI_STATUS_DEFERRED) + ecc_type = 3; + + err.channel = find_umc_channel(pvt, m); + if (err.channel < 0) { + err.err_code = ERR_CHANNEL; + goto log_error; + } + + if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, err.channel, &sys_addr)) { + err.err_code = ERR_NORM_ADDR; + goto log_error; + } + + error_address_to_page_and_offset(sys_addr, &err); + + if (!(m->status & MCI_STATUS_SYNDV)) { + err.err_code = ERR_SYND; + goto log_error; + } + + if (ecc_type == 2) { + u8 length = (m->synd >> 18) & 0x3f; + + if (length) + err.syndrome = (m->synd >> 32) & GENMASK(length - 1, 0); + else + err.err_code = ERR_CHANNEL; + } + + err.csrow = m->synd & 0x7; + +log_error: + __log_ecc_error(mci, &err, ecc_type); } /* * Use pvt->F3 which contains the F3 CPU PCI device to get the related * F1 (AddrMap) and F2 (Dct) devices. Return negative value on error. + * Reserve F0 and F6 on systems with a UMC. */ -static int reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 f1_id, u16 f2_id) -{ +static int +reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 pci_id1, u16 pci_id2) +{ + if (pvt->umc) { + pvt->F0 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3); + if (!pvt->F0) { + amd64_err("F0 not found, device 0x%x (broken BIOS?)\n", pci_id1); + return -ENODEV; + } + + pvt->F6 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3); + if (!pvt->F6) { + pci_dev_put(pvt->F0); + pvt->F0 = NULL; + + amd64_err("F6 not found: device 0x%x (broken BIOS?)\n", pci_id2); + return -ENODEV; + } + + edac_dbg(1, "F0: %s\n", pci_name(pvt->F0)); + edac_dbg(1, "F3: %s\n", pci_name(pvt->F3)); + edac_dbg(1, "F6: %s\n", pci_name(pvt->F6)); + + return 0; + } + /* Reserve the ADDRESS MAP Device */ - pvt->F1 = pci_get_related_function(pvt->F3->vendor, f1_id, pvt->F3); + pvt->F1 = pci_get_related_function(pvt->F3->vendor, pci_id1, pvt->F3); if (!pvt->F1) { - amd64_err("error address map device not found: " - "vendor %x device 0x%x (broken BIOS?)\n", - PCI_VENDOR_ID_AMD, f1_id); + amd64_err("F1 not found: device 0x%x (broken BIOS?)\n", pci_id1); return -ENODEV; } /* Reserve the DCT Device */ - pvt->F2 = pci_get_related_function(pvt->F3->vendor, f2_id, pvt->F3); + pvt->F2 = pci_get_related_function(pvt->F3->vendor, pci_id2, pvt->F3); if (!pvt->F2) { pci_dev_put(pvt->F1); pvt->F1 = NULL; - amd64_err("error F2 device not found: " - "vendor %x device 0x%x (broken BIOS?)\n", - PCI_VENDOR_ID_AMD, f2_id); - + amd64_err("F2 not found: device 0x%x (broken BIOS?)\n", pci_id2); return -ENODEV; } + edac_dbg(1, "F1: %s\n", pci_name(pvt->F1)); edac_dbg(1, "F2: %s\n", pci_name(pvt->F2)); edac_dbg(1, "F3: %s\n", pci_name(pvt->F3)); @@ -2266,8 +2574,69 @@ static int reserve_mc_sibling_devs(struct amd64_pvt *pvt, u16 f1_id, u16 f2_id) static void free_mc_sibling_devs(struct amd64_pvt *pvt) { - pci_dev_put(pvt->F1); - pci_dev_put(pvt->F2); + if (pvt->umc) { + pci_dev_put(pvt->F0); + pci_dev_put(pvt->F6); + } else { + pci_dev_put(pvt->F1); + pci_dev_put(pvt->F2); + } +} + +static void determine_ecc_sym_sz(struct amd64_pvt *pvt) +{ + pvt->ecc_sym_sz = 4; + + if (pvt->umc) { + u8 i; + + for (i = 0; i < NUM_UMCS; i++) { + /* Check enabled channels only: */ + if ((pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) && + (pvt->umc[i].ecc_ctrl & BIT(7))) { + pvt->ecc_sym_sz = 8; + break; + } + } + + return; + } + + if (pvt->fam >= 0x10) { + u32 tmp; + + amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp); + /* F16h has only DCT0, so no need to read dbam1. */ + if (pvt->fam != 0x16) + amd64_read_dct_pci_cfg(pvt, 1, DBAM0, &pvt->dbam1); + + /* F10h, revD and later can do x8 ECC too. */ + if ((pvt->fam > 0x10 || pvt->model > 7) && tmp & BIT(25)) + pvt->ecc_sym_sz = 8; + } +} + +/* + * Retrieve the hardware registers of the memory controller. + */ +static void __read_mc_regs_df(struct amd64_pvt *pvt) +{ + u8 nid = pvt->mc_node_id; + struct amd64_umc *umc; + u32 i, umc_base; + + /* Read registers from each UMC */ + for (i = 0; i < NUM_UMCS; i++) { + + umc_base = get_umc_base(i); + umc = &pvt->umc[i]; + + amd_smn_read(nid, umc_base + UMCCH_DIMM_CFG, &umc->dimm_cfg); + amd_smn_read(nid, umc_base + UMCCH_UMC_CFG, &umc->umc_cfg); + amd_smn_read(nid, umc_base + UMCCH_SDP_CTRL, &umc->sdp_ctrl); + amd_smn_read(nid, umc_base + UMCCH_ECC_CTRL, &umc->ecc_ctrl); + amd_smn_read(nid, umc_base + UMCCH_UMC_CAP_HI, &umc->umc_cap_hi); + } } /* @@ -2276,24 +2645,31 @@ static void free_mc_sibling_devs(struct amd64_pvt *pvt) */ static void read_mc_regs(struct amd64_pvt *pvt) { - unsigned range; + unsigned int range; u64 msr_val; - u32 tmp; /* * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since - * those are Read-As-Zero + * those are Read-As-Zero. */ rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem); edac_dbg(0, " TOP_MEM: 0x%016llx\n", pvt->top_mem); - /* check first whether TOP_MEM2 is enabled */ + /* Check first whether TOP_MEM2 is enabled: */ rdmsrl(MSR_K8_SYSCFG, msr_val); - if (msr_val & (1U << 21)) { + if (msr_val & BIT(21)) { rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2); edac_dbg(0, " TOP_MEM2: 0x%016llx\n", pvt->top_mem2); - } else + } else { edac_dbg(0, " TOP_MEM2 disabled\n"); + } + + if (pvt->umc) { + __read_mc_regs_df(pvt); + amd64_read_pci_cfg(pvt->F0, DF_DHAR, &pvt->dhar); + + goto skip; + } amd64_read_pci_cfg(pvt->F3, NBCAP, &pvt->nbcap); @@ -2322,8 +2698,6 @@ static void read_mc_regs(struct amd64_pvt *pvt) dram_dst_node(pvt, range)); } - read_dct_base_mask(pvt); - amd64_read_pci_cfg(pvt->F1, DHAR, &pvt->dhar); amd64_read_dct_pci_cfg(pvt, 0, DBAM0, &pvt->dbam0); @@ -2337,20 +2711,14 @@ static void read_mc_regs(struct amd64_pvt *pvt) amd64_read_dct_pci_cfg(pvt, 1, DCHR0, &pvt->dchr1); } - pvt->ecc_sym_sz = 4; +skip: + read_dct_base_mask(pvt); + determine_memory_type(pvt); edac_dbg(1, " DIMM type: %s\n", edac_mem_types[pvt->dram_type]); - if (pvt->fam >= 0x10) { - amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp); - /* F16h has only DCT0, so no need to read dbam1 */ - if (pvt->fam != 0x16) - amd64_read_dct_pci_cfg(pvt, 1, DBAM0, &pvt->dbam1); + determine_ecc_sym_sz(pvt); - /* F10h, revD and later can do x8 ECC too */ - if ((pvt->fam > 0x10 || pvt->model > 7) && tmp & BIT(25)) - pvt->ecc_sym_sz = 8; - } dump_misc_regs(pvt); } @@ -2420,20 +2788,22 @@ static u32 get_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr) static int init_csrows(struct mem_ctl_info *mci) { struct amd64_pvt *pvt = mci->pvt_info; + enum edac_type edac_mode = EDAC_NONE; struct csrow_info *csrow; struct dimm_info *dimm; - enum edac_type edac_mode; int i, j, empty = 1; int nr_pages = 0; u32 val; - amd64_read_pci_cfg(pvt->F3, NBCFG, &val); + if (!pvt->umc) { + amd64_read_pci_cfg(pvt->F3, NBCFG, &val); - pvt->nbcfg = val; + pvt->nbcfg = val; - edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n", - pvt->mc_node_id, val, - !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE)); + edac_dbg(0, "node %d, NBCFG=0x%08x[ChipKillEccCap: %d|DramEccEn: %d]\n", + pvt->mc_node_id, val, + !!(val & NBCFG_CHIPKILL), !!(val & NBCFG_ECC_ENABLE)); + } /* * We iterate over DCT0 here but we look at DCT1 in parallel, if needed. @@ -2469,14 +2839,18 @@ static int init_csrows(struct mem_ctl_info *mci) edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages); - /* - * determine whether CHIPKILL or JUST ECC or NO ECC is operating - */ - if (pvt->nbcfg & NBCFG_ECC_ENABLE) - edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) ? - EDAC_S4ECD4ED : EDAC_SECDED; - else - edac_mode = EDAC_NONE; + /* Determine DIMM ECC mode: */ + if (pvt->umc) { + if (mci->edac_ctl_cap & EDAC_FLAG_S4ECD4ED) + edac_mode = EDAC_S4ECD4ED; + else if (mci->edac_ctl_cap & EDAC_FLAG_SECDED) + edac_mode = EDAC_SECDED; + + } else if (pvt->nbcfg & NBCFG_ECC_ENABLE) { + edac_mode = (pvt->nbcfg & NBCFG_CHIPKILL) + ? EDAC_S4ECD4ED + : EDAC_SECDED; + } for (j = 0; j < pvt->channel_count; j++) { dimm = csrow->channels[j]->dimm; @@ -2539,7 +2913,7 @@ static int toggle_ecc_err_reporting(struct ecc_settings *s, u16 nid, bool on) if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) { amd64_warn("%s: error allocating mask\n", __func__); - return false; + return -ENOMEM; } get_cpus_on_this_dct_cpumask(cmask, nid); @@ -2627,7 +3001,6 @@ static void restore_ecc_error_reporting(struct ecc_settings *s, u16 nid, { u32 value, mask = 0x3; /* UECC/CECC enable */ - if (!s->nbctl_valid) return; @@ -2663,20 +3036,50 @@ static const char *ecc_msg = static bool ecc_enabled(struct pci_dev *F3, u16 nid) { - u32 value; - u8 ecc_en = 0; bool nb_mce_en = false; + u8 ecc_en = 0, i; + u32 value; - amd64_read_pci_cfg(F3, NBCFG, &value); + if (boot_cpu_data.x86 >= 0x17) { + u8 umc_en_mask = 0, ecc_en_mask = 0; - ecc_en = !!(value & NBCFG_ECC_ENABLE); - amd64_info("DRAM ECC %s.\n", (ecc_en ? "enabled" : "disabled")); + for (i = 0; i < NUM_UMCS; i++) { + u32 base = get_umc_base(i); + + /* Only check enabled UMCs. */ + if (amd_smn_read(nid, base + UMCCH_SDP_CTRL, &value)) + continue; + + if (!(value & UMC_SDP_INIT)) + continue; + + umc_en_mask |= BIT(i); - nb_mce_en = nb_mce_bank_enabled_on_node(nid); - if (!nb_mce_en) - amd64_notice("NB MCE bank disabled, set MSR " - "0x%08x[4] on node %d to enable.\n", - MSR_IA32_MCG_CTL, nid); + if (amd_smn_read(nid, base + UMCCH_UMC_CAP_HI, &value)) + continue; + + if (value & UMC_ECC_ENABLED) + ecc_en_mask |= BIT(i); + } + + /* Check whether at least one UMC is enabled: */ + if (umc_en_mask) + ecc_en = umc_en_mask == ecc_en_mask; + + /* Assume UMC MCA banks are enabled. */ + nb_mce_en = true; + } else { + amd64_read_pci_cfg(F3, NBCFG, &value); + + ecc_en = !!(value & NBCFG_ECC_ENABLE); + + nb_mce_en = nb_mce_bank_enabled_on_node(nid); + if (!nb_mce_en) + amd64_notice("NB MCE bank disabled, set MSR 0x%08x[4] on node %d to enable.\n", + MSR_IA32_MCG_CTL, nid); + } + + amd64_info("DRAM ECC %s.\n", (ecc_en ? "enabled" : "disabled")); if (!ecc_en || !nb_mce_en) { amd64_notice("%s", ecc_msg); @@ -2685,6 +3088,27 @@ static bool ecc_enabled(struct pci_dev *F3, u16 nid) return true; } +static inline void +f17h_determine_edac_ctl_cap(struct mem_ctl_info *mci, struct amd64_pvt *pvt) +{ + u8 i, ecc_en = 1, cpk_en = 1; + + for (i = 0; i < NUM_UMCS; i++) { + if (pvt->umc[i].sdp_ctrl & UMC_SDP_INIT) { + ecc_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_ENABLED); + cpk_en &= !!(pvt->umc[i].umc_cap_hi & UMC_ECC_CHIPKILL_CAP); + } + } + + /* Set chipkill only if ECC is enabled: */ + if (ecc_en) { + mci->edac_ctl_cap |= EDAC_FLAG_SECDED; + + if (cpk_en) + mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED; + } +} + static void setup_mci_misc_attrs(struct mem_ctl_info *mci, struct amd64_family_type *fam) { @@ -2693,17 +3117,21 @@ static void setup_mci_misc_attrs(struct mem_ctl_info *mci, mci->mtype_cap = MEM_FLAG_DDR2 | MEM_FLAG_RDDR2; mci->edac_ctl_cap = EDAC_FLAG_NONE; - if (pvt->nbcap & NBCAP_SECDED) - mci->edac_ctl_cap |= EDAC_FLAG_SECDED; + if (pvt->umc) { + f17h_determine_edac_ctl_cap(mci, pvt); + } else { + if (pvt->nbcap & NBCAP_SECDED) + mci->edac_ctl_cap |= EDAC_FLAG_SECDED; - if (pvt->nbcap & NBCAP_CHIPKILL) - mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED; + if (pvt->nbcap & NBCAP_CHIPKILL) + mci->edac_ctl_cap |= EDAC_FLAG_S4ECD4ED; + } mci->edac_cap = determine_edac_cap(pvt); mci->mod_name = EDAC_MOD_STR; mci->mod_ver = EDAC_AMD64_VERSION; mci->ctl_name = fam->ctl_name; - mci->dev_name = pci_name(pvt->F2); + mci->dev_name = pci_name(pvt->F3); mci->ctl_page_to_phys = NULL; /* memory scrubber interface */ @@ -2759,6 +3187,11 @@ static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt) pvt->ops = &family_types[F16_CPUS].ops; break; + case 0x17: + fam_type = &family_types[F17_CPUS]; + pvt->ops = &family_types[F17_CPUS].ops; + break; + default: amd64_err("Unsupported family!\n"); return NULL; @@ -2789,6 +3222,7 @@ static int init_one_instance(unsigned int nid) struct mem_ctl_info *mci = NULL; struct edac_mc_layer layers[2]; struct amd64_pvt *pvt = NULL; + u16 pci_id1, pci_id2; int err = 0, ret; ret = -ENOMEM; @@ -2804,10 +3238,23 @@ static int init_one_instance(unsigned int nid) if (!fam_type) goto err_free; - ret = -ENODEV; - err = reserve_mc_sibling_devs(pvt, fam_type->f1_id, fam_type->f2_id); + if (pvt->fam >= 0x17) { + pvt->umc = kcalloc(NUM_UMCS, sizeof(struct amd64_umc), GFP_KERNEL); + if (!pvt->umc) { + ret = -ENOMEM; + goto err_free; + } + + pci_id1 = fam_type->f0_id; + pci_id2 = fam_type->f6_id; + } else { + pci_id1 = fam_type->f1_id; + pci_id2 = fam_type->f2_id; + } + + err = reserve_mc_sibling_devs(pvt, pci_id1, pci_id2); if (err) - goto err_free; + goto err_post_init; read_mc_regs(pvt); @@ -2857,7 +3304,10 @@ static int init_one_instance(unsigned int nid) if (report_gart_errors) amd_report_gart_errors(true); - amd_register_ecc_decoder(decode_bus_error); + if (pvt->umc) + amd_register_ecc_decoder(decode_umc_error); + else + amd_register_ecc_decoder(decode_bus_error); return 0; @@ -2867,6 +3317,10 @@ err_add_mc: err_siblings: free_mc_sibling_devs(pvt); +err_post_init: + if (pvt->fam >= 0x17) + kfree(pvt->umc); + err_free: kfree(pvt); @@ -2893,7 +3347,11 @@ static int probe_one_instance(unsigned int nid) if (!ecc_enable_override) goto err_enable; - amd64_warn("Forcing ECC on!\n"); + if (boot_cpu_data.x86 >= 0x17) { + amd64_warn("Forcing ECC on is not recommended on newer systems. Please enable ECC in BIOS."); + goto err_enable; + } else + amd64_warn("Forcing ECC on!\n"); if (!enable_ecc_error_reporting(s, nid, F3)) goto err_enable; @@ -2902,7 +3360,9 @@ static int probe_one_instance(unsigned int nid) ret = init_one_instance(nid); if (ret < 0) { amd64_err("Error probing instance: %d\n", nid); - restore_ecc_error_reporting(s, nid, F3); + + if (boot_cpu_data.x86 < 0x17) + restore_ecc_error_reporting(s, nid, F3); } return ret; @@ -2938,7 +3398,11 @@ static void remove_one_instance(unsigned int nid) /* unregister from EDAC MCE */ amd_report_gart_errors(false); - amd_unregister_ecc_decoder(decode_bus_error); + + if (pvt->umc) + amd_unregister_ecc_decoder(decode_umc_error); + else + amd_unregister_ecc_decoder(decode_bus_error); kfree(ecc_stngs[nid]); ecc_stngs[nid] = NULL; @@ -2963,7 +3427,10 @@ static void setup_pci_device(void) return; pvt = mci->pvt_info; - pci_ctl = edac_pci_create_generic_ctl(&pvt->F2->dev, EDAC_MOD_STR); + if (pvt->umc) + pci_ctl = edac_pci_create_generic_ctl(&pvt->F0->dev, EDAC_MOD_STR); + else + pci_ctl = edac_pci_create_generic_ctl(&pvt->F2->dev, EDAC_MOD_STR); if (!pci_ctl) { pr_warn("%s(): Unable to create PCI control\n", __func__); pr_warn("%s(): PCI error report via EDAC not set\n", __func__); @@ -2975,6 +3442,7 @@ static const struct x86_cpu_id amd64_cpuids[] = { { X86_VENDOR_AMD, 0x10, X86_MODEL_ANY, X86_FEATURE_ANY, 0 }, { X86_VENDOR_AMD, 0x15, X86_MODEL_ANY, X86_FEATURE_ANY, 0 }, { X86_VENDOR_AMD, 0x16, X86_MODEL_ANY, X86_FEATURE_ANY, 0 }, + { X86_VENDOR_AMD, 0x17, X86_MODEL_ANY, X86_FEATURE_ANY, 0 }, { } }; MODULE_DEVICE_TABLE(x86cpu, amd64_cpuids); diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h index c08870479054..496603d8f3d2 100644 --- a/drivers/edac/amd64_edac.h +++ b/drivers/edac/amd64_edac.h @@ -17,7 +17,7 @@ #include <linux/mmzone.h> #include <linux/edac.h> #include <asm/msr.h> -#include "edac_core.h" +#include "edac_module.h" #include "mce_amd.h" #define amd64_debug(fmt, arg...) \ @@ -30,10 +30,10 @@ edac_printk(KERN_NOTICE, "amd64", fmt, ##arg) #define amd64_warn(fmt, arg...) \ - edac_printk(KERN_WARNING, "amd64", fmt, ##arg) + edac_printk(KERN_WARNING, "amd64", "Warning: " fmt, ##arg) #define amd64_err(fmt, arg...) \ - edac_printk(KERN_ERR, "amd64", fmt, ##arg) + edac_printk(KERN_ERR, "amd64", "Error: " fmt, ##arg) #define amd64_mc_warn(mci, fmt, arg...) \ edac_mc_chipset_printk(mci, KERN_WARNING, "amd64", fmt, ##arg) @@ -118,6 +118,8 @@ #define PCI_DEVICE_ID_AMD_16H_NB_F2 0x1532 #define PCI_DEVICE_ID_AMD_16H_M30H_NB_F1 0x1581 #define PCI_DEVICE_ID_AMD_16H_M30H_NB_F2 0x1582 +#define PCI_DEVICE_ID_AMD_17H_DF_F0 0x1460 +#define PCI_DEVICE_ID_AMD_17H_DF_F6 0x1466 /* * Function 1 - Address Map @@ -202,6 +204,8 @@ #define DCT_SEL_HI 0x114 #define F15H_M60H_SCRCTRL 0x1C8 +#define F17H_SCR_BASE_ADDR 0x48 +#define F17H_SCR_LIMIT_ADDR 0x4C /* * Function 3 - Misc Control @@ -248,6 +252,31 @@ /* MSRs */ #define MSR_MCGCTL_NBE BIT(4) +/* F17h */ + +/* F0: */ +#define DF_DHAR 0x104 + +/* UMC CH register offsets */ +#define UMCCH_BASE_ADDR 0x0 +#define UMCCH_ADDR_MASK 0x20 +#define UMCCH_ADDR_CFG 0x30 +#define UMCCH_DIMM_CFG 0x80 +#define UMCCH_UMC_CFG 0x100 +#define UMCCH_SDP_CTRL 0x104 +#define UMCCH_ECC_CTRL 0x14C +#define UMCCH_ECC_BAD_SYMBOL 0xD90 +#define UMCCH_UMC_CAP 0xDF0 +#define UMCCH_UMC_CAP_HI 0xDF4 + +/* UMC CH bitfields */ +#define UMC_ECC_CHIPKILL_CAP BIT(31) +#define UMC_ECC_ENABLED BIT(30) + +#define UMC_SDP_INIT BIT(31) + +#define NUM_UMCS 2 + enum amd_families { K8_CPUS = 0, F10_CPUS, @@ -256,6 +285,7 @@ enum amd_families { F15_M60H_CPUS, F16_CPUS, F16_M30H_CPUS, + F17_CPUS, NUM_FAMILIES, }; @@ -288,11 +318,19 @@ struct chip_select { u8 m_cnt; }; +struct amd64_umc { + u32 dimm_cfg; /* DIMM Configuration reg */ + u32 umc_cfg; /* Configuration reg */ + u32 sdp_ctrl; /* SDP Control reg */ + u32 ecc_ctrl; /* DRAM ECC Control reg */ + u32 umc_cap_hi; /* Capabilities High reg */ +}; + struct amd64_pvt { struct low_ops *ops; /* pci_device handles which we utilize */ - struct pci_dev *F1, *F2, *F3; + struct pci_dev *F0, *F1, *F2, *F3, *F6; u16 mc_node_id; /* MC index of this MC node */ u8 fam; /* CPU family */ @@ -335,6 +373,8 @@ struct amd64_pvt { /* cache the dram_type */ enum mem_type dram_type; + + struct amd64_umc *umc; /* UMC registers */ }; enum err_codes { @@ -342,6 +382,8 @@ enum err_codes { ERR_NODE = -1, ERR_CSROW = -2, ERR_CHANNEL = -3, + ERR_SYND = -4, + ERR_NORM_ADDR = -5, }; struct err_info { @@ -354,6 +396,12 @@ struct err_info { u32 offset; }; +static inline u32 get_umc_base(u8 channel) +{ + /* ch0: 0x50000, ch1: 0x150000 */ + return 0x50000 + (!!channel << 20); +} + static inline u64 get_dram_base(struct amd64_pvt *pvt, u8 i) { u64 addr = ((u64)pvt->ranges[i].base.lo & 0xffff0000) << 8; @@ -422,7 +470,7 @@ struct low_ops { struct amd64_family_type { const char *ctl_name; - u16 f1_id, f2_id; + u16 f0_id, f1_id, f2_id, f6_id; struct low_ops ops; }; diff --git a/drivers/edac/amd76x_edac.c b/drivers/edac/amd76x_edac.c index 3a501b530e11..a7450275ad28 100644 --- a/drivers/edac/amd76x_edac.c +++ b/drivers/edac/amd76x_edac.c @@ -17,7 +17,7 @@ #include <linux/pci.h> #include <linux/pci_ids.h> #include <linux/edac.h> -#include "edac_core.h" +#include "edac_module.h" #define AMD76X_REVISION " Ver: 2.0.2" #define EDAC_MOD_STR "amd76x_edac" diff --git a/drivers/edac/amd8111_edac.c b/drivers/edac/amd8111_edac.c index 2b63f7c2d6d2..b5786cfded3a 100644 --- a/drivers/edac/amd8111_edac.c +++ b/drivers/edac/amd8111_edac.c @@ -29,7 +29,6 @@ #include <linux/pci_ids.h> #include <asm/io.h> -#include "edac_core.h" #include "edac_module.h" #include "amd8111_edac.h" diff --git a/drivers/edac/amd8131_edac.c b/drivers/edac/amd8131_edac.c index a5c680561c73..8851c33d7d24 100644 --- a/drivers/edac/amd8131_edac.c +++ b/drivers/edac/amd8131_edac.c @@ -29,7 +29,6 @@ #include <linux/edac.h> #include <linux/pci_ids.h> -#include "edac_core.h" #include "edac_module.h" #include "amd8131_edac.h" diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c index a9259b069dcd..bc1f3416400e 100644 --- a/drivers/edac/cell_edac.c +++ b/drivers/edac/cell_edac.c @@ -19,7 +19,7 @@ #include <asm/machdep.h> #include <asm/cell-regs.h> -#include "edac_core.h" +#include "edac_module.h" struct cell_edac_priv { diff --git a/drivers/edac/cpc925_edac.c b/drivers/edac/cpc925_edac.c index 682288ced4ac..837b62c4993d 100644 --- a/drivers/edac/cpc925_edac.c +++ b/drivers/edac/cpc925_edac.c @@ -27,7 +27,6 @@ #include <linux/platform_device.h> #include <linux/gfp.h> -#include "edac_core.h" #include "edac_module.h" #define CPC925_EDAC_REVISION " Ver: 1.0.0" diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c index b2d71388172b..1a352cae1f52 100644 --- a/drivers/edac/e752x_edac.c +++ b/drivers/edac/e752x_edac.c @@ -24,7 +24,7 @@ #include <linux/pci.h> #include <linux/pci_ids.h> #include <linux/edac.h> -#include "edac_core.h" +#include "edac_module.h" #define E752X_REVISION " Ver: 2.0.2" #define EDAC_MOD_STR "e752x_edac" diff --git a/drivers/edac/e7xxx_edac.c b/drivers/edac/e7xxx_edac.c index ece3aef16bb1..67ef07aed923 100644 --- a/drivers/edac/e7xxx_edac.c +++ b/drivers/edac/e7xxx_edac.c @@ -30,7 +30,7 @@ #include <linux/pci.h> #include <linux/pci_ids.h> #include <linux/edac.h> -#include "edac_core.h" +#include "edac_module.h" #define E7XXX_REVISION " Ver: 2.0.2" #define EDAC_MOD_STR "e7xxx_edac" diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c index a97900333e2d..65cf2b9355c4 100644 --- a/drivers/edac/edac_device.c +++ b/drivers/edac/edac_device.c @@ -12,23 +12,20 @@ * 19 Jan 2007 */ +#include <asm/page.h> +#include <linux/uaccess.h> +#include <linux/ctype.h> +#include <linux/highmem.h> +#include <linux/init.h> +#include <linux/jiffies.h> #include <linux/module.h> -#include <linux/types.h> +#include <linux/slab.h> #include <linux/smp.h> -#include <linux/init.h> +#include <linux/spinlock.h> #include <linux/sysctl.h> -#include <linux/highmem.h> #include <linux/timer.h> -#include <linux/slab.h> -#include <linux/jiffies.h> -#include <linux/spinlock.h> -#include <linux/list.h> -#include <linux/ctype.h> -#include <linux/workqueue.h> -#include <asm/uaccess.h> -#include <asm/page.h> -#include "edac_core.h" +#include "edac_device.h" #include "edac_module.h" /* lock for the list: 'edac_device_list', manipulation of this list @@ -50,21 +47,6 @@ static void edac_device_dump_device(struct edac_device_ctl_info *edac_dev) } #endif /* CONFIG_EDAC_DEBUG */ - -/* - * edac_device_alloc_ctl_info() - * Allocate a new edac device control info structure - * - * The control structure is allocated in complete chunk - * from the OS. It is in turn sub allocated to the - * various objects that compose the structure - * - * The structure has a 'nr_instance' array within itself. - * Each instance represents a major component - * Example: L1 cache and L2 cache are 2 instance components - * - * Within each instance is an array of 'nr_blocks' blockoffsets - */ struct edac_device_ctl_info *edac_device_alloc_ctl_info( unsigned sz_private, char *edac_device_name, unsigned nr_instances, @@ -244,11 +226,6 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info( } EXPORT_SYMBOL_GPL(edac_device_alloc_ctl_info); -/* - * edac_device_free_ctl_info() - * frees the memory allocated by the edac_device_alloc_ctl_info() - * function - */ void edac_device_free_ctl_info(struct edac_device_ctl_info *ctl_info) { edac_device_unregister_sysfs_main_kobj(ctl_info); @@ -460,12 +437,6 @@ void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, edac_mod_work(&edac_dev->work, jiffs); } -/* - * edac_device_alloc_index: Allocate a unique device index number - * - * Return: - * allocated index number - */ int edac_device_alloc_index(void) { static atomic_t device_indexes = ATOMIC_INIT(0); @@ -474,17 +445,6 @@ int edac_device_alloc_index(void) } EXPORT_SYMBOL_GPL(edac_device_alloc_index); -/** - * edac_device_add_device: Insert the 'edac_dev' structure into the - * edac_device global list and create sysfs entries associated with - * edac_device structure. - * @edac_device: pointer to the edac_device structure to be added to the list - * 'edac_device' structure. - * - * Return: - * 0 Success - * !0 Failure - */ int edac_device_add_device(struct edac_device_ctl_info *edac_dev) { edac_dbg(0, "\n"); @@ -541,19 +501,6 @@ fail0: } EXPORT_SYMBOL_GPL(edac_device_add_device); -/** - * edac_device_del_device: - * Remove sysfs entries for specified edac_device structure and - * then remove edac_device structure from global list - * - * @dev: - * Pointer to 'struct device' representing edac_device - * structure to remove. - * - * Return: - * Pointer to removed edac_device structure, - * OR NULL if device not found. - */ struct edac_device_ctl_info *edac_device_del_device(struct device *dev) { struct edac_device_ctl_info *edac_dev; @@ -608,10 +555,6 @@ static inline int edac_device_get_panic_on_ue(struct edac_device_ctl_info return edac_dev->panic_on_ue; } -/* - * edac_device_handle_ce - * perform a common output and handling of an 'edac_dev' CE event - */ void edac_device_handle_ce(struct edac_device_ctl_info *edac_dev, int inst_nr, int block_nr, const char *msg) { @@ -654,10 +597,6 @@ void edac_device_handle_ce(struct edac_device_ctl_info *edac_dev, } EXPORT_SYMBOL_GPL(edac_device_handle_ce); -/* - * edac_device_handle_ue - * perform a common output and handling of an 'edac_dev' UE event - */ void edac_device_handle_ue(struct edac_device_ctl_info *edac_dev, int inst_nr, int block_nr, const char *msg) { diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_device.h index 4861542163d7..1aaba74ae411 100644 --- a/drivers/edac/edac_core.h +++ b/drivers/edac/edac_device.h @@ -1,5 +1,5 @@ /* - * Defines, structures, APIs for edac_core module + * Defines, structures, APIs for edac_device * * (C) 2007 Linux Networx (http://lnxi.com) * This file may be distributed under the terms of the @@ -15,86 +15,22 @@ * Refactored for multi-source files: * Doug Thompson <norsk5@xmission.com> * + * Please look at Documentation/driver-api/edac.rst for more info about + * EDAC core structs and functions. */ -#ifndef _EDAC_CORE_H_ -#define _EDAC_CORE_H_ +#ifndef _EDAC_DEVICE_H_ +#define _EDAC_DEVICE_H_ -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/module.h> -#include <linux/spinlock.h> -#include <linux/smp.h> -#include <linux/pci.h> -#include <linux/time.h> -#include <linux/nmi.h> -#include <linux/rcupdate.h> #include <linux/completion.h> +#include <linux/device.h> +#include <linux/edac.h> #include <linux/kobject.h> -#include <linux/platform_device.h> +#include <linux/list.h> +#include <linux/types.h> +#include <linux/sysfs.h> #include <linux/workqueue.h> -#include <linux/edac.h> - -#define EDAC_DEVICE_NAME_LEN 31 -#define EDAC_ATTRIB_VALUE_LEN 15 - -#if PAGE_SHIFT < 20 -#define PAGES_TO_MiB(pages) ((pages) >> (20 - PAGE_SHIFT)) -#define MiB_TO_PAGES(mb) ((mb) << (20 - PAGE_SHIFT)) -#else /* PAGE_SHIFT > 20 */ -#define PAGES_TO_MiB(pages) ((pages) << (PAGE_SHIFT - 20)) -#define MiB_TO_PAGES(mb) ((mb) >> (PAGE_SHIFT - 20)) -#endif - -#define edac_printk(level, prefix, fmt, arg...) \ - printk(level "EDAC " prefix ": " fmt, ##arg) - -#define edac_mc_printk(mci, level, fmt, arg...) \ - printk(level "EDAC MC%d: " fmt, mci->mc_idx, ##arg) - -#define edac_mc_chipset_printk(mci, level, prefix, fmt, arg...) \ - printk(level "EDAC " prefix " MC%d: " fmt, mci->mc_idx, ##arg) - -#define edac_device_printk(ctl, level, fmt, arg...) \ - printk(level "EDAC DEVICE%d: " fmt, ctl->dev_idx, ##arg) - -#define edac_pci_printk(ctl, level, fmt, arg...) \ - printk(level "EDAC PCI%d: " fmt, ctl->pci_idx, ##arg) -/* prefixes for edac_printk() and edac_mc_printk() */ -#define EDAC_MC "MC" -#define EDAC_PCI "PCI" -#define EDAC_DEBUG "DEBUG" - -extern const char * const edac_mem_types[]; - -#ifdef CONFIG_EDAC_DEBUG -extern int edac_debug_level; - -#define edac_dbg(level, fmt, ...) \ -do { \ - if (level <= edac_debug_level) \ - edac_printk(KERN_DEBUG, EDAC_DEBUG, \ - "%s: " fmt, __func__, ##__VA_ARGS__); \ -} while (0) - -#else /* !CONFIG_EDAC_DEBUG */ - -#define edac_dbg(level, fmt, ...) \ -do { \ - if (0) \ - edac_printk(KERN_DEBUG, EDAC_DEBUG, \ - "%s: " fmt, __func__, ##__VA_ARGS__); \ -} while (0) - -#endif /* !CONFIG_EDAC_DEBUG */ - -#define PCI_VEND_DEV(vend, dev) PCI_VENDOR_ID_ ## vend, \ - PCI_DEVICE_ID_ ## vend ## _ ## dev - -#define edac_dev_name(dev) (dev)->dev_name - -#define to_mci(k) container_of(k, struct mem_ctl_info, dev) /* * The following are the structures to provide for a generic @@ -321,197 +257,64 @@ extern struct edac_device_ctl_info *edac_device_alloc_ctl_info( extern void edac_device_free_ctl_info(struct edac_device_ctl_info *ctl_info); -#ifdef CONFIG_PCI - -struct edac_pci_counter { - atomic_t pe_count; - atomic_t npe_count; -}; - -/* - * Abstract edac_pci control info structure +/** + * edac_device_add_device: Insert the 'edac_dev' structure into the + * edac_device global list and create sysfs entries associated with + * edac_device structure. + * + * @edac_dev: pointer to edac_device structure to be added to the list + * 'edac_device' structure. * + * Returns: + * 0 on Success, or an error code on failure */ -struct edac_pci_ctl_info { - /* for global list of edac_pci_ctl_info structs */ - struct list_head link; - - int pci_idx; - - struct bus_type *edac_subsys; /* pointer to subsystem */ - - /* the internal state of this controller instance */ - int op_state; - /* work struct for this instance */ - struct delayed_work work; - - /* pointer to edac polling checking routine: - * If NOT NULL: points to polling check routine - * If NULL: Then assumes INTERRUPT operation, where - * MC driver will receive events - */ - void (*edac_check) (struct edac_pci_ctl_info * edac_dev); - - struct device *dev; /* pointer to device structure */ - - const char *mod_name; /* module name */ - const char *ctl_name; /* edac controller name */ - const char *dev_name; /* pci/platform/etc... name */ - - void *pvt_info; /* pointer to 'private driver' info */ - - unsigned long start_time; /* edac_pci load start time (jiffies) */ - - struct completion complete; - - /* sysfs top name under 'edac' directory - * and instance name: - * cpu/cpu0/... - * cpu/cpu1/... - * cpu/cpu2/... - * ... - */ - char name[EDAC_DEVICE_NAME_LEN + 1]; - - /* Event counters for the this whole EDAC Device */ - struct edac_pci_counter counters; - - /* edac sysfs device control for the 'name' - * device this structure controls - */ - struct kobject kobj; - struct completion kobj_complete; -}; - -#define to_edac_pci_ctl_work(w) \ - container_of(w, struct edac_pci_ctl_info,work) - -/* write all or some bits in a byte-register*/ -static inline void pci_write_bits8(struct pci_dev *pdev, int offset, u8 value, - u8 mask) -{ - if (mask != 0xff) { - u8 buf; - - pci_read_config_byte(pdev, offset, &buf); - value &= mask; - buf &= ~mask; - value |= buf; - } - - pci_write_config_byte(pdev, offset, value); -} - -/* write all or some bits in a word-register*/ -static inline void pci_write_bits16(struct pci_dev *pdev, int offset, - u16 value, u16 mask) -{ - if (mask != 0xffff) { - u16 buf; - - pci_read_config_word(pdev, offset, &buf); - value &= mask; - buf &= ~mask; - value |= buf; - } - - pci_write_config_word(pdev, offset, value); -} +extern int edac_device_add_device(struct edac_device_ctl_info *edac_dev); -/* - * pci_write_bits32 +/** + * edac_device_del_device: + * Remove sysfs entries for specified edac_device structure and + * then remove edac_device structure from global list * - * edac local routine to do pci_write_config_dword, but adds - * a mask parameter. If mask is all ones, ignore the mask. - * Otherwise utilize the mask to isolate specified bits + * @dev: + * Pointer to struct &device representing the edac device + * structure to remove. * - * write all or some bits in a dword-register + * Returns: + * Pointer to removed edac_device structure, + * or %NULL if device not found. */ -static inline void pci_write_bits32(struct pci_dev *pdev, int offset, - u32 value, u32 mask) -{ - if (mask != 0xffffffff) { - u32 buf; - - pci_read_config_dword(pdev, offset, &buf); - value &= mask; - buf &= ~mask; - value |= buf; - } - - pci_write_config_dword(pdev, offset, value); -} - -#endif /* CONFIG_PCI */ - -struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, - unsigned n_layers, - struct edac_mc_layer *layers, - unsigned sz_pvt); -extern int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci, - const struct attribute_group **groups); -#define edac_mc_add_mc(mci) edac_mc_add_mc_with_groups(mci, NULL) -extern void edac_mc_free(struct mem_ctl_info *mci); -extern struct mem_ctl_info *edac_mc_find(int idx); -extern struct mem_ctl_info *find_mci_by_dev(struct device *dev); -extern struct mem_ctl_info *edac_mc_del_mc(struct device *dev); -extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, - unsigned long page); - -void edac_raw_mc_handle_error(const enum hw_event_mc_err_type type, - struct mem_ctl_info *mci, - struct edac_raw_error_desc *e); - -void edac_mc_handle_error(const enum hw_event_mc_err_type type, - struct mem_ctl_info *mci, - const u16 error_count, - const unsigned long page_frame_number, - const unsigned long offset_in_page, - const unsigned long syndrome, - const int top_layer, - const int mid_layer, - const int low_layer, - const char *msg, - const char *other_detail); +extern struct edac_device_ctl_info *edac_device_del_device(struct device *dev); -/* - * edac_device APIs +/** + * edac_device_handle_ue(): + * perform a common output and handling of an 'edac_dev' UE event + * + * @edac_dev: pointer to struct &edac_device_ctl_info + * @inst_nr: number of the instance where the UE error happened + * @block_nr: number of the block where the UE error happened + * @msg: message to be printed */ -extern int edac_device_add_device(struct edac_device_ctl_info *edac_dev); -extern struct edac_device_ctl_info *edac_device_del_device(struct device *dev); extern void edac_device_handle_ue(struct edac_device_ctl_info *edac_dev, int inst_nr, int block_nr, const char *msg); +/** + * edac_device_handle_ce(): + * perform a common output and handling of an 'edac_dev' CE event + * + * @edac_dev: pointer to struct &edac_device_ctl_info + * @inst_nr: number of the instance where the CE error happened + * @block_nr: number of the block where the CE error happened + * @msg: message to be printed + */ extern void edac_device_handle_ce(struct edac_device_ctl_info *edac_dev, int inst_nr, int block_nr, const char *msg); -extern int edac_device_alloc_index(void); -extern const char *edac_layer_name[]; -/* - * edac_pci APIs - */ -extern struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt, - const char *edac_pci_name); - -extern void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci); - -extern void edac_pci_reset_delay_period(struct edac_pci_ctl_info *pci, - unsigned long value); - -extern int edac_pci_alloc_index(void); -extern int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx); -extern struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev); - -extern struct edac_pci_ctl_info *edac_pci_create_generic_ctl( - struct device *dev, - const char *mod_name); - -extern void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci); -extern int edac_pci_create_sysfs(struct edac_pci_ctl_info *pci); -extern void edac_pci_remove_sysfs(struct edac_pci_ctl_info *pci); - -/* - * edac misc APIs +/** + * edac_device_alloc_index: Allocate a unique device index number + * + * Returns: + * allocated index number */ -extern char *edac_op_state_to_string(int op_state); +extern int edac_device_alloc_index(void); +extern const char *edac_layer_name[]; -#endif /* _EDAC_CORE_H_ */ +#endif diff --git a/drivers/edac/edac_device_sysfs.c b/drivers/edac/edac_device_sysfs.c index 93da1a45c716..0e7ea3591b78 100644 --- a/drivers/edac/edac_device_sysfs.c +++ b/drivers/edac/edac_device_sysfs.c @@ -1,7 +1,7 @@ /* * file for managing the edac_device subsystem of devices for EDAC * - * (C) 2007 SoftwareBitMaker + * (C) 2007 SoftwareBitMaker * * This file may be distributed under the terms of the * GNU General Public License. @@ -15,7 +15,7 @@ #include <linux/slab.h> #include <linux/edac.h> -#include "edac_core.h" +#include "edac_device.h" #include "edac_module.h" #define EDAC_DEVICE_SYMLINK "device" diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index c3ee3ad98a63..750891ea07de 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -28,9 +28,9 @@ #include <linux/ctype.h> #include <linux/edac.h> #include <linux/bitops.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <asm/page.h> -#include "edac_core.h" +#include "edac_mc.h" #include "edac_module.h" #include <ras/ras_event.h> @@ -239,30 +239,6 @@ static void _edac_mc_free(struct mem_ctl_info *mci) kfree(mci); } -/** - * edac_mc_alloc: Allocate and partially fill a struct mem_ctl_info structure - * @mc_num: Memory controller number - * @n_layers: Number of MC hierarchy layers - * layers: Describes each layer as seen by the Memory Controller - * @size_pvt: size of private storage needed - * - * - * Everything is kmalloc'ed as one big chunk - more efficient. - * Only can be used if all structures have the same lifetime - otherwise - * you have to allocate and initialize your own structures. - * - * Use edac_mc_free() to free mc structures allocated by this function. - * - * NOTE: drivers handle multi-rank memories in different ways: in some - * drivers, one multi-rank memory stick is mapped as one entry, while, in - * others, a single multi-rank memory stick would be mapped into several - * entries. Currently, this function will allocate multiple struct dimm_info - * on such scenarios, as grouping the multiple ranks require drivers change. - * - * Returns: - * On failure: NULL - * On success: struct mem_ctl_info pointer - */ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, unsigned n_layers, struct edac_mc_layer *layers, @@ -460,11 +436,6 @@ error: } EXPORT_SYMBOL_GPL(edac_mc_alloc); -/** - * edac_mc_free - * 'Free' a previously allocated 'mci' structure - * @mci: pointer to a struct mem_ctl_info structure - */ void edac_mc_free(struct mem_ctl_info *mci) { edac_dbg(1, "\n"); @@ -482,15 +453,8 @@ void edac_mc_free(struct mem_ctl_info *mci) } EXPORT_SYMBOL_GPL(edac_mc_free); - -/** - * find_mci_by_dev - * - * scan list of controllers looking for the one that manages - * the 'dev' device - * @dev: pointer to a struct device related with the MCI - */ -struct mem_ctl_info *find_mci_by_dev(struct device *dev) +/* Caller must hold mem_ctls_mutex */ +static struct mem_ctl_info *__find_mci_by_dev(struct device *dev) { struct mem_ctl_info *mci; struct list_head *item; @@ -506,6 +470,24 @@ struct mem_ctl_info *find_mci_by_dev(struct device *dev) return NULL; } + +/** + * find_mci_by_dev + * + * scan list of controllers looking for the one that manages + * the 'dev' device + * @dev: pointer to a struct device related with the MCI + */ +struct mem_ctl_info *find_mci_by_dev(struct device *dev) +{ + struct mem_ctl_info *ret; + + mutex_lock(&mem_ctls_mutex); + ret = __find_mci_by_dev(dev); + mutex_unlock(&mem_ctls_mutex); + + return ret; +} EXPORT_SYMBOL_GPL(find_mci_by_dev); /* @@ -588,7 +570,7 @@ static int add_mc_to_global_list(struct mem_ctl_info *mci) insert_before = &mc_devices; - p = find_mci_by_dev(mci->pdev); + p = __find_mci_by_dev(mci->pdev); if (unlikely(p != NULL)) goto fail0; @@ -635,44 +617,30 @@ static int del_mc_from_global_list(struct mem_ctl_info *mci) return handlers; } -/** - * edac_mc_find: Search for a mem_ctl_info structure whose index is 'idx'. - * - * If found, return a pointer to the structure. - * Else return NULL. - * - * Caller must hold mem_ctls_mutex. - */ struct mem_ctl_info *edac_mc_find(int idx) { + struct mem_ctl_info *mci = NULL; struct list_head *item; - struct mem_ctl_info *mci; + + mutex_lock(&mem_ctls_mutex); list_for_each(item, &mc_devices) { mci = list_entry(item, struct mem_ctl_info, link); if (mci->mc_idx >= idx) { - if (mci->mc_idx == idx) - return mci; - + if (mci->mc_idx == idx) { + goto unlock; + } break; } } - return NULL; +unlock: + mutex_unlock(&mem_ctls_mutex); + return mci; } EXPORT_SYMBOL(edac_mc_find); -/** - * edac_mc_add_mc_with_groups: Insert the 'mci' structure into the mci - * global list and create sysfs entries associated with mci structure - * @mci: pointer to the mci structure to be added to the list - * @groups: optional attribute groups for the driver-specific sysfs entries - * - * Return: - * 0 Success - * !0 Failure - */ /* FIXME - should a warning be printed if no error detection? correction? */ int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci, @@ -763,13 +731,6 @@ fail0: } EXPORT_SYMBOL_GPL(edac_mc_add_mc_with_groups); -/** - * edac_mc_del_mc: Remove sysfs entries for specified mci structure and - * remove mci structure from global list - * @pdev: Pointer to 'struct device' representing mci structure to remove. - * - * Return pointer to removed mci structure, or NULL if device not found. - */ struct mem_ctl_info *edac_mc_del_mc(struct device *dev) { struct mem_ctl_info *mci; @@ -779,7 +740,7 @@ struct mem_ctl_info *edac_mc_del_mc(struct device *dev) mutex_lock(&mem_ctls_mutex); /* find the requested mci struct in the global list */ - mci = find_mci_by_dev(dev); + mci = __find_mci_by_dev(dev); if (mci == NULL) { mutex_unlock(&mem_ctls_mutex); return NULL; @@ -1033,18 +994,6 @@ static void edac_ue_error(struct mem_ctl_info *mci, edac_inc_ue_error(mci, enable_per_layer_report, pos, error_count); } -/** - * edac_raw_mc_handle_error - reports a memory event to userspace without doing - * anything to discover the error location - * - * @type: severity of the error (CE/UE/Fatal) - * @mci: a struct mem_ctl_info pointer - * @e: error description - * - * This raw function is used internally by edac_mc_handle_error(). It should - * only be called directly when the hardware error come directly from BIOS, - * like in the case of APEI GHES driver. - */ void edac_raw_mc_handle_error(const enum hw_event_mc_err_type type, struct mem_ctl_info *mci, struct edac_raw_error_desc *e) @@ -1074,24 +1023,6 @@ void edac_raw_mc_handle_error(const enum hw_event_mc_err_type type, } EXPORT_SYMBOL_GPL(edac_raw_mc_handle_error); -/** - * edac_mc_handle_error - reports a memory event to userspace - * - * @type: severity of the error (CE/UE/Fatal) - * @mci: a struct mem_ctl_info pointer - * @error_count: Number of errors of the same type - * @page_frame_number: mem page where the error occurred - * @offset_in_page: offset of the error inside the page - * @syndrome: ECC syndrome - * @top_layer: Memory layer[0] position - * @mid_layer: Memory layer[1] position - * @low_layer: Memory layer[2] position - * @msg: Message meaningful to the end users that - * explains the event - * @other_detail: Technical details about the event that - * may help hardware manufacturers and - * EDAC developers to analyse the event - */ void edac_mc_handle_error(const enum hw_event_mc_err_type type, struct mem_ctl_info *mci, const u16 error_count, diff --git a/drivers/edac/edac_mc.h b/drivers/edac/edac_mc.h new file mode 100644 index 000000000000..50fc1dc9c0d8 --- /dev/null +++ b/drivers/edac/edac_mc.h @@ -0,0 +1,245 @@ +/* + * Defines, structures, APIs for edac_mc module + * + * (C) 2007 Linux Networx (http://lnxi.com) + * This file may be distributed under the terms of the + * GNU General Public License. + * + * Written by Thayne Harbaugh + * Based on work by Dan Hollis <goemon at anime dot net> and others. + * http://www.anime.net/~goemon/linux-ecc/ + * + * NMI handling support added by + * Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com> + * + * Refactored for multi-source files: + * Doug Thompson <norsk5@xmission.com> + * + * Please look at Documentation/driver-api/edac.rst for more info about + * EDAC core structs and functions. + */ + +#ifndef _EDAC_MC_H_ +#define _EDAC_MC_H_ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/smp.h> +#include <linux/pci.h> +#include <linux/time.h> +#include <linux/nmi.h> +#include <linux/rcupdate.h> +#include <linux/completion.h> +#include <linux/kobject.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> +#include <linux/edac.h> + +#if PAGE_SHIFT < 20 +#define PAGES_TO_MiB(pages) ((pages) >> (20 - PAGE_SHIFT)) +#define MiB_TO_PAGES(mb) ((mb) << (20 - PAGE_SHIFT)) +#else /* PAGE_SHIFT > 20 */ +#define PAGES_TO_MiB(pages) ((pages) << (PAGE_SHIFT - 20)) +#define MiB_TO_PAGES(mb) ((mb) >> (PAGE_SHIFT - 20)) +#endif + +#define edac_printk(level, prefix, fmt, arg...) \ + printk(level "EDAC " prefix ": " fmt, ##arg) + +#define edac_mc_printk(mci, level, fmt, arg...) \ + printk(level "EDAC MC%d: " fmt, mci->mc_idx, ##arg) + +#define edac_mc_chipset_printk(mci, level, prefix, fmt, arg...) \ + printk(level "EDAC " prefix " MC%d: " fmt, mci->mc_idx, ##arg) + +#define edac_device_printk(ctl, level, fmt, arg...) \ + printk(level "EDAC DEVICE%d: " fmt, ctl->dev_idx, ##arg) + +#define edac_pci_printk(ctl, level, fmt, arg...) \ + printk(level "EDAC PCI%d: " fmt, ctl->pci_idx, ##arg) + +/* prefixes for edac_printk() and edac_mc_printk() */ +#define EDAC_MC "MC" +#define EDAC_PCI "PCI" +#define EDAC_DEBUG "DEBUG" + +extern const char * const edac_mem_types[]; + +#ifdef CONFIG_EDAC_DEBUG +extern int edac_debug_level; + +#define edac_dbg(level, fmt, ...) \ +do { \ + if (level <= edac_debug_level) \ + edac_printk(KERN_DEBUG, EDAC_DEBUG, \ + "%s: " fmt, __func__, ##__VA_ARGS__); \ +} while (0) + +#else /* !CONFIG_EDAC_DEBUG */ + +#define edac_dbg(level, fmt, ...) \ +do { \ + if (0) \ + edac_printk(KERN_DEBUG, EDAC_DEBUG, \ + "%s: " fmt, __func__, ##__VA_ARGS__); \ +} while (0) + +#endif /* !CONFIG_EDAC_DEBUG */ + +#define PCI_VEND_DEV(vend, dev) PCI_VENDOR_ID_ ## vend, \ + PCI_DEVICE_ID_ ## vend ## _ ## dev + +#define edac_dev_name(dev) (dev)->dev_name + +#define to_mci(k) container_of(k, struct mem_ctl_info, dev) + +/** + * edac_mc_alloc() - Allocate and partially fill a struct &mem_ctl_info. + * + * @mc_num: Memory controller number + * @n_layers: Number of MC hierarchy layers + * @layers: Describes each layer as seen by the Memory Controller + * @sz_pvt: size of private storage needed + * + * + * Everything is kmalloc'ed as one big chunk - more efficient. + * Only can be used if all structures have the same lifetime - otherwise + * you have to allocate and initialize your own structures. + * + * Use edac_mc_free() to free mc structures allocated by this function. + * + * .. note:: + * + * drivers handle multi-rank memories in different ways: in some + * drivers, one multi-rank memory stick is mapped as one entry, while, in + * others, a single multi-rank memory stick would be mapped into several + * entries. Currently, this function will allocate multiple struct dimm_info + * on such scenarios, as grouping the multiple ranks require drivers change. + * + * Returns: + * On success, return a pointer to struct mem_ctl_info pointer; + * %NULL otherwise + */ +struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, + unsigned n_layers, + struct edac_mc_layer *layers, + unsigned sz_pvt); + +/** + * edac_mc_add_mc_with_groups() - Insert the @mci structure into the mci + * global list and create sysfs entries associated with @mci structure. + * + * @mci: pointer to the mci structure to be added to the list + * @groups: optional attribute groups for the driver-specific sysfs entries + * + * Returns: + * 0 on Success, or an error code on failure + */ +extern int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci, + const struct attribute_group **groups); +#define edac_mc_add_mc(mci) edac_mc_add_mc_with_groups(mci, NULL) + +/** + * edac_mc_free() - Frees a previously allocated @mci structure + * + * @mci: pointer to a struct mem_ctl_info structure + */ +extern void edac_mc_free(struct mem_ctl_info *mci); + +/** + * edac_mc_find() - Search for a mem_ctl_info structure whose index is @idx. + * + * @idx: index to be seek + * + * If found, return a pointer to the structure. + * Else return NULL. + */ +extern struct mem_ctl_info *edac_mc_find(int idx); + +/** + * find_mci_by_dev() - Scan list of controllers looking for the one that + * manages the @dev device. + * + * @dev: pointer to a struct device related with the MCI + * + * Returns: on success, returns a pointer to struct &mem_ctl_info; + * %NULL otherwise. + */ +extern struct mem_ctl_info *find_mci_by_dev(struct device *dev); + +/** + * edac_mc_del_mc() - Remove sysfs entries for mci structure associated with + * @dev and remove mci structure from global list. + * + * @dev: Pointer to struct &device representing mci structure to remove. + * + * Returns: pointer to removed mci structure, or %NULL if device not found. + */ +extern struct mem_ctl_info *edac_mc_del_mc(struct device *dev); + +/** + * edac_mc_find_csrow_by_page() - Ancillary routine to identify what csrow + * contains a memory page. + * + * @mci: pointer to a struct mem_ctl_info structure + * @page: memory page to find + * + * Returns: on success, returns the csrow. -1 if not found. + */ +extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, + unsigned long page); + +/** + * edac_raw_mc_handle_error() - Reports a memory event to userspace without + * doing anything to discover the error location. + * + * @type: severity of the error (CE/UE/Fatal) + * @mci: a struct mem_ctl_info pointer + * @e: error description + * + * This raw function is used internally by edac_mc_handle_error(). It should + * only be called directly when the hardware error come directly from BIOS, + * like in the case of APEI GHES driver. + */ +void edac_raw_mc_handle_error(const enum hw_event_mc_err_type type, + struct mem_ctl_info *mci, + struct edac_raw_error_desc *e); + +/** + * edac_mc_handle_error() - Reports a memory event to userspace. + * + * @type: severity of the error (CE/UE/Fatal) + * @mci: a struct mem_ctl_info pointer + * @error_count: Number of errors of the same type + * @page_frame_number: mem page where the error occurred + * @offset_in_page: offset of the error inside the page + * @syndrome: ECC syndrome + * @top_layer: Memory layer[0] position + * @mid_layer: Memory layer[1] position + * @low_layer: Memory layer[2] position + * @msg: Message meaningful to the end users that + * explains the event + * @other_detail: Technical details about the event that + * may help hardware manufacturers and + * EDAC developers to analyse the event + */ +void edac_mc_handle_error(const enum hw_event_mc_err_type type, + struct mem_ctl_info *mci, + const u16 error_count, + const unsigned long page_frame_number, + const unsigned long offset_in_page, + const unsigned long syndrome, + const int top_layer, + const int mid_layer, + const int low_layer, + const char *msg, + const char *other_detail); + +/* + * edac misc APIs + */ +extern char *edac_op_state_to_string(int op_state); + +#endif /* _EDAC_MC_H_ */ diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 4e0f8e720ad9..39dbab7d62f1 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -19,7 +19,7 @@ #include <linux/pm_runtime.h> #include <linux/uaccess.h> -#include "edac_core.h" +#include "edac_mc.h" #include "edac_module.h" /* MC EDAC Controls, setable by module parameter, and sysfs */ diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c index 5f8543be995a..172598a27d7d 100644 --- a/drivers/edac/edac_module.c +++ b/drivers/edac/edac_module.c @@ -12,7 +12,7 @@ */ #include <linux/edac.h> -#include "edac_core.h" +#include "edac_mc.h" #include "edac_module.h" #define EDAC_VERSION "Ver: 3.0.0" diff --git a/drivers/edac/edac_module.h b/drivers/edac/edac_module.h index cfaacb99c973..014871e169cc 100644 --- a/drivers/edac/edac_module.h +++ b/drivers/edac/edac_module.h @@ -10,7 +10,9 @@ #ifndef __EDAC_MODULE_H__ #define __EDAC_MODULE_H__ -#include "edac_core.h" +#include "edac_mc.h" +#include "edac_pci.h" +#include "edac_device.h" /* * INTERNAL EDAC MODULE: diff --git a/drivers/edac/edac_pci.c b/drivers/edac/edac_pci.c index 8f2f2899a7a2..48c844a72a27 100644 --- a/drivers/edac/edac_pci.c +++ b/drivers/edac/edac_pci.c @@ -9,35 +9,25 @@ * or implied. * */ +#include <asm/page.h> +#include <linux/uaccess.h> +#include <linux/ctype.h> +#include <linux/highmem.h> +#include <linux/init.h> #include <linux/module.h> -#include <linux/types.h> +#include <linux/slab.h> #include <linux/smp.h> -#include <linux/init.h> +#include <linux/spinlock.h> #include <linux/sysctl.h> -#include <linux/highmem.h> #include <linux/timer.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/list.h> -#include <linux/ctype.h> -#include <linux/workqueue.h> -#include <asm/uaccess.h> -#include <asm/page.h> -#include "edac_core.h" +#include "edac_pci.h" #include "edac_module.h" static DEFINE_MUTEX(edac_pci_ctls_mutex); static LIST_HEAD(edac_pci_list); static atomic_t pci_indexes = ATOMIC_INIT(0); -/* - * edac_pci_alloc_ctl_info - * - * The alloc() function for the 'edac_pci' control info - * structure. The chip driver will allocate one of these for each - * edac_pci it is going to control/register with the EDAC CORE. - */ struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt, const char *edac_pci_name) { @@ -68,16 +58,6 @@ struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt, } EXPORT_SYMBOL_GPL(edac_pci_alloc_ctl_info); -/* - * edac_pci_free_ctl_info() - * - * Last action on the pci control structure. - * - * call the remove sysfs information, which will unregister - * this control struct's kobj. When that kobj's ref count - * goes to zero, its release function will be call and then - * kfree() the memory. - */ void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci) { edac_dbg(1, "\n"); @@ -215,31 +195,12 @@ static void edac_pci_workq_function(struct work_struct *work_req) mutex_unlock(&edac_pci_ctls_mutex); } -/* - * edac_pci_alloc_index: Allocate a unique PCI index number - * - * Return: - * allocated index number - * - */ int edac_pci_alloc_index(void) { return atomic_inc_return(&pci_indexes) - 1; } EXPORT_SYMBOL_GPL(edac_pci_alloc_index); -/* - * edac_pci_add_device: Insert the 'edac_dev' structure into the - * edac_pci global list and create sysfs entries associated with - * edac_pci structure. - * @pci: pointer to the edac_device structure to be added to the list - * @edac_idx: A unique numeric identifier to be assigned to the - * 'edac_pci' structure. - * - * Return: - * 0 Success - * !0 Failure - */ int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx) { edac_dbg(0, "\n"); @@ -285,19 +246,6 @@ fail0: } EXPORT_SYMBOL_GPL(edac_pci_add_device); -/* - * edac_pci_del_device() - * Remove sysfs entries for specified edac_pci structure and - * then remove edac_pci structure from global list - * - * @dev: - * Pointer to 'struct device' representing edac_pci structure - * to remove - * - * Return: - * Pointer to removed edac_pci structure, - * or NULL if device not found - */ struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev) { struct edac_pci_ctl_info *pci; @@ -351,17 +299,6 @@ struct edac_pci_gen_data { int edac_idx; }; -/* - * edac_pci_create_generic_ctl - * - * A generic constructor for a PCI parity polling device - * Some systems have more than one domain of PCI busses. - * For systems with one domain, then this API will - * provide for a generic poller. - * - * This routine calls the edac_pci_alloc_ctl_info() for - * the generic device, with default values - */ struct edac_pci_ctl_info *edac_pci_create_generic_ctl(struct device *dev, const char *mod_name) { @@ -394,11 +331,6 @@ struct edac_pci_ctl_info *edac_pci_create_generic_ctl(struct device *dev, } EXPORT_SYMBOL_GPL(edac_pci_create_generic_ctl); -/* - * edac_pci_release_generic_ctl - * - * The release function of a generic EDAC PCI polling device - */ void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci) { edac_dbg(0, "pci mod=%s\n", pci->mod_name); diff --git a/drivers/edac/edac_pci.h b/drivers/edac/edac_pci.h new file mode 100644 index 000000000000..5175f5724cfa --- /dev/null +++ b/drivers/edac/edac_pci.h @@ -0,0 +1,271 @@ +/* + * Defines, structures, APIs for edac_pci and edac_pci_sysfs + * + * (C) 2007 Linux Networx (http://lnxi.com) + * This file may be distributed under the terms of the + * GNU General Public License. + * + * Written by Thayne Harbaugh + * Based on work by Dan Hollis <goemon at anime dot net> and others. + * http://www.anime.net/~goemon/linux-ecc/ + * + * NMI handling support added by + * Dave Peterson <dsp@llnl.gov> <dave_peterson@pobox.com> + * + * Refactored for multi-source files: + * Doug Thompson <norsk5@xmission.com> + * + * Please look at Documentation/driver-api/edac.rst for more info about + * EDAC core structs and functions. + */ + +#ifndef _EDAC_PCI_H_ +#define _EDAC_PCI_H_ + +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/edac.h> +#include <linux/kobject.h> +#include <linux/list.h> +#include <linux/pci.h> +#include <linux/types.h> +#include <linux/workqueue.h> + +#ifdef CONFIG_PCI + +struct edac_pci_counter { + atomic_t pe_count; + atomic_t npe_count; +}; + +/* + * Abstract edac_pci control info structure + * + */ +struct edac_pci_ctl_info { + /* for global list of edac_pci_ctl_info structs */ + struct list_head link; + + int pci_idx; + + struct bus_type *edac_subsys; /* pointer to subsystem */ + + /* the internal state of this controller instance */ + int op_state; + /* work struct for this instance */ + struct delayed_work work; + + /* pointer to edac polling checking routine: + * If NOT NULL: points to polling check routine + * If NULL: Then assumes INTERRUPT operation, where + * MC driver will receive events + */ + void (*edac_check) (struct edac_pci_ctl_info * edac_dev); + + struct device *dev; /* pointer to device structure */ + + const char *mod_name; /* module name */ + const char *ctl_name; /* edac controller name */ + const char *dev_name; /* pci/platform/etc... name */ + + void *pvt_info; /* pointer to 'private driver' info */ + + unsigned long start_time; /* edac_pci load start time (jiffies) */ + + struct completion complete; + + /* sysfs top name under 'edac' directory + * and instance name: + * cpu/cpu0/... + * cpu/cpu1/... + * cpu/cpu2/... + * ... + */ + char name[EDAC_DEVICE_NAME_LEN + 1]; + + /* Event counters for the this whole EDAC Device */ + struct edac_pci_counter counters; + + /* edac sysfs device control for the 'name' + * device this structure controls + */ + struct kobject kobj; +}; + +#define to_edac_pci_ctl_work(w) \ + container_of(w, struct edac_pci_ctl_info,work) + +/* write all or some bits in a byte-register*/ +static inline void pci_write_bits8(struct pci_dev *pdev, int offset, u8 value, + u8 mask) +{ + if (mask != 0xff) { + u8 buf; + + pci_read_config_byte(pdev, offset, &buf); + value &= mask; + buf &= ~mask; + value |= buf; + } + + pci_write_config_byte(pdev, offset, value); +} + +/* write all or some bits in a word-register*/ +static inline void pci_write_bits16(struct pci_dev *pdev, int offset, + u16 value, u16 mask) +{ + if (mask != 0xffff) { + u16 buf; + + pci_read_config_word(pdev, offset, &buf); + value &= mask; + buf &= ~mask; + value |= buf; + } + + pci_write_config_word(pdev, offset, value); +} + +/* + * pci_write_bits32 + * + * edac local routine to do pci_write_config_dword, but adds + * a mask parameter. If mask is all ones, ignore the mask. + * Otherwise utilize the mask to isolate specified bits + * + * write all or some bits in a dword-register + */ +static inline void pci_write_bits32(struct pci_dev *pdev, int offset, + u32 value, u32 mask) +{ + if (mask != 0xffffffff) { + u32 buf; + + pci_read_config_dword(pdev, offset, &buf); + value &= mask; + buf &= ~mask; + value |= buf; + } + + pci_write_config_dword(pdev, offset, value); +} + +#endif /* CONFIG_PCI */ + +/* + * edac_pci APIs + */ + +/** + * edac_pci_alloc_ctl_info: + * The alloc() function for the 'edac_pci' control info + * structure. + * + * @sz_pvt: size of the private info at struct &edac_pci_ctl_info + * @edac_pci_name: name of the PCI device + * + * The chip driver will allocate one of these for each + * edac_pci it is going to control/register with the EDAC CORE. + * + * Returns: a pointer to struct &edac_pci_ctl_info on success; %NULL otherwise. + */ +extern struct edac_pci_ctl_info *edac_pci_alloc_ctl_info(unsigned int sz_pvt, + const char *edac_pci_name); + +/** + * edac_pci_free_ctl_info(): + * Last action on the pci control structure. + * + * @pci: pointer to struct &edac_pci_ctl_info + * + * Calls the remove sysfs information, which will unregister + * this control struct's kobj. When that kobj's ref count + * goes to zero, its release function will be call and then + * kfree() the memory. + */ +extern void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci); + +/** + * edac_pci_alloc_index: Allocate a unique PCI index number + * + * Returns: + * allocated index number + * + */ +extern int edac_pci_alloc_index(void); + +/** + * edac_pci_add_device(): Insert the 'edac_dev' structure into the + * edac_pci global list and create sysfs entries associated with + * edac_pci structure. + * + * @pci: pointer to the edac_device structure to be added to the list + * @edac_idx: A unique numeric identifier to be assigned to the + * 'edac_pci' structure. + * + * Returns: + * 0 on Success, or an error code on failure + */ +extern int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx); + +/** + * edac_pci_del_device() + * Remove sysfs entries for specified edac_pci structure and + * then remove edac_pci structure from global list + * + * @dev: + * Pointer to 'struct device' representing edac_pci structure + * to remove + * + * Returns: + * Pointer to removed edac_pci structure, + * or %NULL if device not found + */ +extern struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev); + +/** + * edac_pci_create_generic_ctl() + * A generic constructor for a PCI parity polling device + * Some systems have more than one domain of PCI busses. + * For systems with one domain, then this API will + * provide for a generic poller. + * + * @dev: pointer to struct &device; + * @mod_name: name of the PCI device + * + * This routine calls the edac_pci_alloc_ctl_info() for + * the generic device, with default values + * + * Returns: Pointer to struct &edac_pci_ctl_info on success, %NULL on + * failure. + */ +extern struct edac_pci_ctl_info *edac_pci_create_generic_ctl( + struct device *dev, + const char *mod_name); + +/** + * edac_pci_release_generic_ctl + * The release function of a generic EDAC PCI polling device + * + * @pci: pointer to struct &edac_pci_ctl_info + */ +extern void edac_pci_release_generic_ctl(struct edac_pci_ctl_info *pci); + +/** + * edac_pci_create_sysfs + * Create the controls/attributes for the specified EDAC PCI device + * + * @pci: pointer to struct &edac_pci_ctl_info + */ +extern int edac_pci_create_sysfs(struct edac_pci_ctl_info *pci); + +/** + * edac_pci_remove_sysfs() + * remove the controls and attributes for this EDAC PCI device + * + * @pci: pointer to struct &edac_pci_ctl_info + */ +extern void edac_pci_remove_sysfs(struct edac_pci_ctl_info *pci); + +#endif diff --git a/drivers/edac/edac_pci_sysfs.c b/drivers/edac/edac_pci_sysfs.c index 6e3428ba400f..72c9eb9fdffb 100644 --- a/drivers/edac/edac_pci_sysfs.c +++ b/drivers/edac/edac_pci_sysfs.c @@ -11,7 +11,7 @@ #include <linux/slab.h> #include <linux/ctype.h> -#include "edac_core.h" +#include "edac_pci.h" #include "edac_module.h" #define EDAC_PCI_SYMLINK "device" @@ -418,12 +418,6 @@ static void edac_pci_main_kobj_teardown(void) } } -/* - * - * edac_pci_create_sysfs - * - * Create the controls/attributes for the specified EDAC PCI device - */ int edac_pci_create_sysfs(struct edac_pci_ctl_info *pci) { int err; @@ -459,11 +453,6 @@ unregister_cleanup: return err; } -/* - * edac_pci_remove_sysfs - * - * remove the controls and attributes for this EDAC PCI device - */ void edac_pci_remove_sysfs(struct edac_pci_ctl_info *pci) { edac_dbg(0, "index=%d\n", pci->pci_idx); diff --git a/drivers/edac/fsl_ddr_edac.c b/drivers/edac/fsl_ddr_edac.c index 9774f52f0c3e..4e9608a958e7 100644 --- a/drivers/edac/fsl_ddr_edac.c +++ b/drivers/edac/fsl_ddr_edac.c @@ -28,7 +28,6 @@ #include <linux/of_device.h> #include <linux/of_address.h> #include "edac_module.h" -#include "edac_core.h" #include "fsl_ddr_edac.h" #define EDAC_MOD_STR "fsl_ddr_edac" diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c index e3fa4390f846..4e61a6229dd2 100644 --- a/drivers/edac/ghes_edac.c +++ b/drivers/edac/ghes_edac.c @@ -14,7 +14,7 @@ #include <acpi/ghes.h> #include <linux/edac.h> #include <linux/dmi.h> -#include "edac_core.h" +#include "edac_module.h" #include <ras/ras_event.h> #define GHES_EDAC_REVISION " Ver: 1.0.0" diff --git a/drivers/edac/highbank_l2_edac.c b/drivers/edac/highbank_l2_edac.c index 2f193668ebc7..cd9a2bb7c548 100644 --- a/drivers/edac/highbank_l2_edac.c +++ b/drivers/edac/highbank_l2_edac.c @@ -21,7 +21,6 @@ #include <linux/platform_device.h> #include <linux/of_platform.h> -#include "edac_core.h" #include "edac_module.h" #define SR_CLR_SB_ECC_INTR 0x0 diff --git a/drivers/edac/highbank_mc_edac.c b/drivers/edac/highbank_mc_edac.c index 11260cc3360e..0e7e0a404d89 100644 --- a/drivers/edac/highbank_mc_edac.c +++ b/drivers/edac/highbank_mc_edac.c @@ -22,7 +22,6 @@ #include <linux/of_platform.h> #include <linux/uaccess.h> -#include "edac_core.h" #include "edac_module.h" /* DDR Ctrlr Error Registers */ diff --git a/drivers/edac/i3000_edac.c b/drivers/edac/i3000_edac.c index 5cb36a6022cc..5306240570d7 100644 --- a/drivers/edac/i3000_edac.c +++ b/drivers/edac/i3000_edac.c @@ -14,7 +14,7 @@ #include <linux/pci.h> #include <linux/pci_ids.h> #include <linux/edac.h> -#include "edac_core.h" +#include "edac_module.h" #define I3000_REVISION "1.1" diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c index 1f453382258a..77c58d201a30 100644 --- a/drivers/edac/i3200_edac.c +++ b/drivers/edac/i3200_edac.c @@ -13,7 +13,7 @@ #include <linux/pci_ids.h> #include <linux/edac.h> #include <linux/io.h> -#include "edac_core.h" +#include "edac_module.h" #include <linux/io-64-nonatomic-lo-hi.h> diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c index 72e07e3cf718..1670d27bcac8 100644 --- a/drivers/edac/i5000_edac.c +++ b/drivers/edac/i5000_edac.c @@ -22,7 +22,7 @@ #include <linux/edac.h> #include <asm/mmzone.h> -#include "edac_core.h" +#include "edac_module.h" /* * Alter this version for the I5000 module when modifications are made diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c index c655162caf08..a8334c4acea7 100644 --- a/drivers/edac/i5100_edac.c +++ b/drivers/edac/i5100_edac.c @@ -29,7 +29,6 @@ #include <linux/mmzone.h> #include <linux/debugfs.h> -#include "edac_core.h" #include "edac_module.h" /* register addresses */ diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c index 6ef6ad1ba16e..abf6ef22e220 100644 --- a/drivers/edac/i5400_edac.c +++ b/drivers/edac/i5400_edac.c @@ -32,7 +32,7 @@ #include <linux/edac.h> #include <linux/mmzone.h> -#include "edac_core.h" +#include "edac_module.h" /* * Alter this version for the I5400 module when modifications are made diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c index dcac982fdc7a..0a912bf6de00 100644 --- a/drivers/edac/i7300_edac.c +++ b/drivers/edac/i7300_edac.c @@ -26,7 +26,7 @@ #include <linux/edac.h> #include <linux/mmzone.h> -#include "edac_core.h" +#include "edac_module.h" /* * Alter this version for the I7300 module when modifications are made diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 8a68a5e943ea..69b5adead0ad 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -39,7 +39,7 @@ #include <asm/processor.h> #include <asm/div64.h> -#include "edac_core.h" +#include "edac_module.h" /* Static vars */ static LIST_HEAD(i7core_edac_list); diff --git a/drivers/edac/i82443bxgx_edac.c b/drivers/edac/i82443bxgx_edac.c index 4d4110364f02..cb61a5b7d080 100644 --- a/drivers/edac/i82443bxgx_edac.c +++ b/drivers/edac/i82443bxgx_edac.c @@ -29,7 +29,7 @@ #include <linux/edac.h> -#include "edac_core.h" +#include "edac_module.h" #define I82443_REVISION "0.1" diff --git a/drivers/edac/i82860_edac.c b/drivers/edac/i82860_edac.c index ee1078cd3b96..236c813227fc 100644 --- a/drivers/edac/i82860_edac.c +++ b/drivers/edac/i82860_edac.c @@ -14,7 +14,7 @@ #include <linux/pci.h> #include <linux/pci_ids.h> #include <linux/edac.h> -#include "edac_core.h" +#include "edac_module.h" #define I82860_REVISION " Ver: 2.0.2" #define EDAC_MOD_STR "i82860_edac" diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c index c26a513f8869..e286b7e74c7a 100644 --- a/drivers/edac/i82875p_edac.c +++ b/drivers/edac/i82875p_edac.c @@ -18,7 +18,7 @@ #include <linux/pci.h> #include <linux/pci_ids.h> #include <linux/edac.h> -#include "edac_core.h" +#include "edac_module.h" #define I82875P_REVISION " Ver: 2.0.2" #define EDAC_MOD_STR "i82875p_edac" diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c index 35ab66c623a3..7baa8ace267b 100644 --- a/drivers/edac/i82975x_edac.c +++ b/drivers/edac/i82975x_edac.c @@ -14,7 +14,7 @@ #include <linux/pci.h> #include <linux/pci_ids.h> #include <linux/edac.h> -#include "edac_core.h" +#include "edac_module.h" #define I82975X_REVISION " Ver: 1.0.0" #define EDAC_MOD_STR "i82975x_edac" diff --git a/drivers/edac/ie31200_edac.c b/drivers/edac/ie31200_edac.c index 1c88d9707495..2733fb5938a4 100644 --- a/drivers/edac/ie31200_edac.c +++ b/drivers/edac/ie31200_edac.c @@ -41,7 +41,7 @@ #include <linux/edac.h> #include <linux/io-64-nonatomic-lo-hi.h> -#include "edac_core.h" +#include "edac_module.h" #define IE31200_REVISION "1.0" #define EDAC_MOD_STR "ie31200_edac" diff --git a/drivers/edac/layerscape_edac.c b/drivers/edac/layerscape_edac.c index 6c59d897ad12..94cac7686a56 100644 --- a/drivers/edac/layerscape_edac.c +++ b/drivers/edac/layerscape_edac.c @@ -16,7 +16,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include "edac_core.h" +#include "edac_module.h" #include "fsl_ddr_edac.h" static const struct of_device_id fsl_ddr_mc_err_of_match[] = { diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index daaac2c79ca7..34208f38c5b1 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -8,7 +8,7 @@ static struct amd_decoder_ops *fam_ops; static u8 xec_mask = 0xf; static bool report_gart_errors; -static void (*nb_bus_decoder)(int node_id, struct mce *m); +static void (*decode_dram_ecc)(int node_id, struct mce *m); void amd_report_gart_errors(bool v) { @@ -18,16 +18,16 @@ EXPORT_SYMBOL_GPL(amd_report_gart_errors); void amd_register_ecc_decoder(void (*f)(int, struct mce *)) { - nb_bus_decoder = f; + decode_dram_ecc = f; } EXPORT_SYMBOL_GPL(amd_register_ecc_decoder); void amd_unregister_ecc_decoder(void (*f)(int, struct mce *)) { - if (nb_bus_decoder) { - WARN_ON(nb_bus_decoder != f); + if (decode_dram_ecc) { + WARN_ON(decode_dram_ecc != f); - nb_bus_decoder = NULL; + decode_dram_ecc = NULL; } } EXPORT_SYMBOL_GPL(amd_unregister_ecc_decoder); @@ -763,8 +763,8 @@ static void decode_mc4_mce(struct mce *m) pr_cont("%s.\n", mc4_mce_desc[xec]); - if (nb_bus_decoder) - nb_bus_decoder(node_id, m); + if (decode_dram_ecc) + decode_dram_ecc(node_id, m); return; } break; @@ -851,7 +851,7 @@ static void decode_mc6_mce(struct mce *m) /* Decode errors according to Scalable MCA specification */ static void decode_smca_errors(struct mce *m) { - struct smca_hwid_mcatype *type; + struct smca_hwid *hwid; unsigned int bank_type; const char *ip_name; u8 xec = XEC(m->status, xec_mask); @@ -862,21 +862,28 @@ static void decode_smca_errors(struct mce *m) if (boot_cpu_data.x86 >= 0x17 && m->bank == 4) pr_emerg(HW_ERR "Bank 4 is reserved on Fam17h.\n"); - type = smca_banks[m->bank].type; - if (!type) + hwid = smca_banks[m->bank].hwid; + if (!hwid) return; - bank_type = type->bank_type; - ip_name = smca_bank_names[bank_type].long_name; + bank_type = hwid->bank_type; + ip_name = smca_get_long_name(bank_type); pr_emerg(HW_ERR "%s Extended Error Code: %d\n", ip_name, xec); /* Only print the decode of valid error codes */ if (xec < smca_mce_descs[bank_type].num_descs && - (type->xec_bitmap & BIT_ULL(xec))) { + (hwid->xec_bitmap & BIT_ULL(xec))) { pr_emerg(HW_ERR "%s Error: ", ip_name); pr_cont("%s.\n", smca_mce_descs[bank_type].descs[xec]); } + + /* + * amd_get_nb_id() returns the last level cache id. + * The last level cache on Fam17h is 1 level below the node. + */ + if (bank_type == SMCA_UMC && xec == 0 && decode_dram_ecc) + decode_dram_ecc(amd_get_nb_id(m->extcpu) >> 1, m); } static inline void amd_decode_err_code(u16 ec) @@ -957,10 +964,13 @@ int amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data) ((m->status & MCI_STATUS_PCC) ? "PCC" : "-"), ((m->status & MCI_STATUS_ADDRV) ? "AddrV" : "-")); - if (c->x86 >= 0x15) - pr_cont("|%s|%s", - ((m->status & MCI_STATUS_DEFERRED) ? "Deferred" : "-"), - ((m->status & MCI_STATUS_POISON) ? "Poison" : "-")); + if (c->x86 >= 0x15) { + pr_cont("|%s", (m->status & MCI_STATUS_DEFERRED ? "Deferred" : "-")); + + /* F15h, bank4, bit 43 is part of McaStatSubCache. */ + if (c->x86 != 0x15 || m->bank != 4) + pr_cont("|%s", (m->status & MCI_STATUS_POISON ? "Poison" : "-")); + } if (boot_cpu_has(X86_FEATURE_SMCA)) { u32 low, high; diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c index ff0567526ee3..8f66cbed70b7 100644 --- a/drivers/edac/mpc85xx_edac.c +++ b/drivers/edac/mpc85xx_edac.c @@ -25,7 +25,6 @@ #include <linux/of_platform.h> #include <linux/of_device.h> #include "edac_module.h" -#include "edac_core.h" #include "mpc85xx_edac.h" #include "fsl_ddr_edac.h" @@ -300,6 +299,22 @@ err: return res; } +static int mpc85xx_pci_err_remove(struct platform_device *op) +{ + struct edac_pci_ctl_info *pci = dev_get_drvdata(&op->dev); + struct mpc85xx_pci_pdata *pdata = pci->pvt_info; + + edac_dbg(0, "\n"); + + out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ADDR, orig_pci_err_cap_dr); + out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, orig_pci_err_en); + + edac_pci_del_device(&op->dev); + edac_pci_free_ctl_info(pci); + + return 0; +} + static const struct platform_device_id mpc85xx_pci_err_match[] = { { .name = "mpc85xx-pci-edac" @@ -309,6 +324,7 @@ static const struct platform_device_id mpc85xx_pci_err_match[] = { static struct platform_driver mpc85xx_pci_err_driver = { .probe = mpc85xx_pci_err_probe, + .remove = mpc85xx_pci_err_remove, .id_table = mpc85xx_pci_err_match, .driver = { .name = "mpc85xx_pci_err", diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c index cb9b8577acbc..14b7e7b71eaa 100644 --- a/drivers/edac/mv64x60_edac.c +++ b/drivers/edac/mv64x60_edac.c @@ -17,7 +17,6 @@ #include <linux/edac.h> #include <linux/gfp.h> -#include "edac_core.h" #include "edac_module.h" #include "mv64x60_edac.h" diff --git a/drivers/edac/octeon_edac-l2c.c b/drivers/edac/octeon_edac-l2c.c index afea7fc625cc..c33059e9b0be 100644 --- a/drivers/edac/octeon_edac-l2c.c +++ b/drivers/edac/octeon_edac-l2c.c @@ -16,7 +16,6 @@ #include <asm/octeon/cvmx.h> -#include "edac_core.h" #include "edac_module.h" #define EDAC_MOD_STR "octeon-l2c" diff --git a/drivers/edac/octeon_edac-lmc.c b/drivers/edac/octeon_edac-lmc.c index cda6dab5067a..9c1ffe3e912b 100644 --- a/drivers/edac/octeon_edac-lmc.c +++ b/drivers/edac/octeon_edac-lmc.c @@ -19,7 +19,6 @@ #include <asm/octeon/octeon.h> #include <asm/octeon/cvmx-lmcx-defs.h> -#include "edac_core.h" #include "edac_module.h" #define OCTEON_MAX_MC 4 diff --git a/drivers/edac/octeon_edac-pc.c b/drivers/edac/octeon_edac-pc.c index 2ab6cf24c959..754eced59c32 100644 --- a/drivers/edac/octeon_edac-pc.c +++ b/drivers/edac/octeon_edac-pc.c @@ -15,7 +15,6 @@ #include <linux/io.h> #include <linux/edac.h> -#include "edac_core.h" #include "edac_module.h" #include <asm/octeon/cvmx.h> diff --git a/drivers/edac/octeon_edac-pci.c b/drivers/edac/octeon_edac-pci.c index 9ca73cec74e7..28b238eecefc 100644 --- a/drivers/edac/octeon_edac-pci.c +++ b/drivers/edac/octeon_edac-pci.c @@ -18,7 +18,6 @@ #include <asm/octeon/cvmx-pci-defs.h> #include <asm/octeon/octeon.h> -#include "edac_core.h" #include "edac_module.h" static void octeon_pci_poll(struct edac_pci_ctl_info *pci) diff --git a/drivers/edac/pasemi_edac.c b/drivers/edac/pasemi_edac.c index 9c971b575530..199f2c80480d 100644 --- a/drivers/edac/pasemi_edac.c +++ b/drivers/edac/pasemi_edac.c @@ -26,7 +26,7 @@ #include <linux/pci.h> #include <linux/pci_ids.h> #include <linux/edac.h> -#include "edac_core.h" +#include "edac_module.h" #define MODULE_NAME "pasemi_edac" diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c index 691ce25e9010..e55e92590106 100644 --- a/drivers/edac/ppc4xx_edac.c +++ b/drivers/edac/ppc4xx_edac.c @@ -21,7 +21,7 @@ #include <asm/dcr.h> -#include "edac_core.h" +#include "edac_module.h" #include "ppc4xx_edac.h" /* diff --git a/drivers/edac/r82600_edac.c b/drivers/edac/r82600_edac.c index 8f936bc7a010..978916625ced 100644 --- a/drivers/edac/r82600_edac.c +++ b/drivers/edac/r82600_edac.c @@ -20,7 +20,7 @@ #include <linux/pci.h> #include <linux/pci_ids.h> #include <linux/edac.h> -#include "edac_core.h" +#include "edac_module.h" #define R82600_REVISION " Ver: 2.0.2" #define EDAC_MOD_STR "r82600_edac" diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c index 54775221a01f..54ae6dc45ab2 100644 --- a/drivers/edac/sb_edac.c +++ b/drivers/edac/sb_edac.c @@ -23,10 +23,11 @@ #include <linux/math64.h> #include <linux/mod_devicetable.h> #include <asm/cpu_device_id.h> +#include <asm/intel-family.h> #include <asm/processor.h> #include <asm/mce.h> -#include "edac_core.h" +#include "edac_module.h" /* Static vars */ static LIST_HEAD(sbridge_edac_list); @@ -3365,12 +3366,13 @@ fail0: { X86_VENDOR_INTEL, 6, model, 0, (unsigned long)&table } static const struct x86_cpu_id sbridge_cpuids[] = { - ICPU(0x2d, pci_dev_descr_sbridge_table), /* SANDY_BRIDGE */ - ICPU(0x3e, pci_dev_descr_ibridge_table), /* IVY_BRIDGE */ - ICPU(0x3f, pci_dev_descr_haswell_table), /* HASWELL */ - ICPU(0x4f, pci_dev_descr_broadwell_table), /* BROADWELL */ - ICPU(0x56, pci_dev_descr_broadwell_table), /* BROADWELL-DE */ - ICPU(0x57, pci_dev_descr_knl_table), /* KNIGHTS_LANDING */ + ICPU(INTEL_FAM6_SANDYBRIDGE_X, pci_dev_descr_sbridge_table), + ICPU(INTEL_FAM6_IVYBRIDGE_X, pci_dev_descr_ibridge_table), + ICPU(INTEL_FAM6_HASWELL_X, pci_dev_descr_haswell_table), + ICPU(INTEL_FAM6_BROADWELL_X, pci_dev_descr_broadwell_table), + ICPU(INTEL_FAM6_BROADWELL_XEON_D, pci_dev_descr_broadwell_table), + ICPU(INTEL_FAM6_XEON_PHI_KNL, pci_dev_descr_knl_table), + ICPU(INTEL_FAM6_XEON_PHI_KNM, pci_dev_descr_knl_table), { } }; MODULE_DEVICE_TABLE(x86cpu, sbridge_cpuids); diff --git a/drivers/edac/skx_edac.c b/drivers/edac/skx_edac.c index 0ff4878c2aa1..79ef675e4d6f 100644 --- a/drivers/edac/skx_edac.c +++ b/drivers/edac/skx_edac.c @@ -25,10 +25,11 @@ #include <linux/math64.h> #include <linux/mod_devicetable.h> #include <asm/cpu_device_id.h> +#include <asm/intel-family.h> #include <asm/processor.h> #include <asm/mce.h> -#include "edac_core.h" +#include "edac_module.h" #define SKX_REVISION " Ver: 1.0 " @@ -262,8 +263,8 @@ fail: return -ENODEV; } -const struct x86_cpu_id skx_cpuids[] = { - { X86_VENDOR_INTEL, 6, 0x55, 0, 0 }, /* Skylake */ +static const struct x86_cpu_id skx_cpuids[] = { + { X86_VENDOR_INTEL, 6, INTEL_FAM6_SKYLAKE_X, 0, 0 }, { } }; MODULE_DEVICE_TABLE(x86cpu, skx_cpuids); @@ -1036,7 +1037,7 @@ static void skx_remove(void) * search for all the devices we need * check which DIMMs are present. */ -int __init skx_init(void) +static int __init skx_init(void) { const struct x86_cpu_id *id; const struct munit *m; diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c index fc153aea2f6c..1c01dec78ec3 100644 --- a/drivers/edac/synopsys_edac.c +++ b/drivers/edac/synopsys_edac.c @@ -23,7 +23,7 @@ #include <linux/module.h> #include <linux/platform_device.h> -#include "edac_core.h" +#include "edac_module.h" /* Number of cs_rows needed per memory controller */ #define SYNPS_EDAC_NR_CSROWS 1 diff --git a/drivers/edac/tile_edac.c b/drivers/edac/tile_edac.c index 71381642ce2a..8a33a87e67f1 100644 --- a/drivers/edac/tile_edac.c +++ b/drivers/edac/tile_edac.c @@ -30,7 +30,7 @@ #include <hv/hypervisor.h> #include <hv/drv_mshim_intf.h> -#include "edac_core.h" +#include "edac_module.h" #define DRV_NAME "tile-edac" diff --git a/drivers/edac/x38_edac.c b/drivers/edac/x38_edac.c index 314cf5cf268c..03c97a4bf590 100644 --- a/drivers/edac/x38_edac.c +++ b/drivers/edac/x38_edac.c @@ -16,7 +16,7 @@ #include <linux/edac.h> #include <linux/io-64-nonatomic-lo-hi.h> -#include "edac_core.h" +#include "edac_module.h" #define X38_REVISION "1.1" diff --git a/drivers/edac/xgene_edac.c b/drivers/edac/xgene_edac.c index bf19b6e3bd12..6c270d9d304a 100644 --- a/drivers/edac/xgene_edac.c +++ b/drivers/edac/xgene_edac.c @@ -28,7 +28,6 @@ #include <linux/of_address.h> #include <linux/regmap.h> -#include "edac_core.h" #include "edac_module.h" #define EDAC_MOD_STR "xgene_edac" @@ -1602,16 +1601,16 @@ static void xgene_edac_pa_report(struct edac_device_ctl_info *edac_dev) dev_err(edac_dev->dev, "IOB PA read data RAM error\n"); if (reg & IOBPA_M_RDATA_CORRUPT_MASK) dev_err(edac_dev->dev, - "Mutilple IOB PA read data RAM error\n"); + "Multiple IOB PA read data RAM error\n"); if (reg & IOBPA_WDATA_CORRUPT_MASK) dev_err(edac_dev->dev, "IOB PA write data RAM error\n"); if (reg & IOBPA_M_WDATA_CORRUPT_MASK) dev_err(edac_dev->dev, - "Mutilple IOB PA write data RAM error\n"); + "Multiple IOB PA write data RAM error\n"); if (reg & IOBPA_TRANS_CORRUPT_MASK) dev_err(edac_dev->dev, "IOB PA transaction error\n"); if (reg & IOBPA_M_TRANS_CORRUPT_MASK) - dev_err(edac_dev->dev, "Mutilple IOB PA transaction error\n"); + dev_err(edac_dev->dev, "Multiple IOB PA transaction error\n"); if (reg & IOBPA_REQIDRAM_CORRUPT_MASK) dev_err(edac_dev->dev, "IOB PA transaction ID RAM error\n"); if (reg & IOBPA_M_REQIDRAM_CORRUPT_MASK) |