diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-25 13:28:00 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-25 13:28:00 -0800 |
commit | b271b2127e6654a72dc1685f0825fe1cc2f36939 (patch) | |
tree | f1fbcfa89ab651c831c84abc8efad748ff706169 /drivers | |
parent | 8e61e7b5c4de2bea534438bd7a008accd85492b0 (diff) | |
parent | 75dfa87035f9a2659dd0c48af6b4d9899e79cdc9 (diff) | |
download | linux-b271b2127e6654a72dc1685f0825fe1cc2f36939.tar.bz2 |
Merge tag 'edac_for_4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp
Pull EDAC updates from Borislav Petkov:
- Support for ZynqMP DDR controller support to synopsys_edac along with
a driver cleanup and generalization for the addition of support for
the new IP. (Manish Narani)
- Removal of the /sys/bus/edac devices hierarchy. This enabled us to
get rid of the silly memory controllers maximum number notion. (Tony
Luck and Borislav Petkov)
- skx_edac improvements and fixes. (Qiuxu Zhuo and Tony Luck)
- The usual garden variety of small cleanups and fixes.
* tag 'edac_for_4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp: (25 commits)
EDAC, fsl_ddr: Add LS1021A to the list of supported hardware
EDAC, i5000: Remove set but not used local variables
MAINTAINERS, EDAC: Drop bouncing email
EDAC, i82975x: Fix spelling mistake "reserverd" -> "reserved"
EDAC, fsl: Move error injection under CONFIG_EDAC_DEBUG
EDAC, skx: Let EDAC core show the decoded result for debugfs
EDAC, skx: Move debugfs node under EDAC's hierarchy
EDAC, skx: Prepend hex formatting with '0x'
EDAC, skx: Fix function calling order in skx_exit()
EDAC: Drop per-memory controller buses
EDAC: Don't add devices under /sys/bus/edac
EDAC: Fix indentation issues in several EDAC drivers
EDAC, skx: Fix randconfig builds in a better way
EDAC, i82975x: Remove set but not used variable dtype
EDAC, qcom_edac: Remove irq_handled local variable
EDAC, synopsys: Add Error Injection support for ZynqMP DDR controller
EDAC, synopsys: Add ECC support for ZynqMP DDR controller
EDAC, synopsys: Add macro defines for ZynqMP DDRC
dt: bindings: Document ZynqMP DDRC in Synopsys documentation
EDAC, synopsys: Add error handling for the of_device_get_match_data() result
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/edac/Kconfig | 6 | ||||
-rw-r--r-- | drivers/edac/e752x_edac.c | 4 | ||||
-rw-r--r-- | drivers/edac/edac_mc.c | 9 | ||||
-rw-r--r-- | drivers/edac/edac_mc_sysfs.c | 33 | ||||
-rw-r--r-- | drivers/edac/fsl_ddr_edac.c | 8 | ||||
-rw-r--r-- | drivers/edac/fsl_ddr_edac.h | 4 | ||||
-rw-r--r-- | drivers/edac/i3000_edac.c | 4 | ||||
-rw-r--r-- | drivers/edac/i5000_edac.c | 13 | ||||
-rw-r--r-- | drivers/edac/i7core_edac.c | 2 | ||||
-rw-r--r-- | drivers/edac/i82975x_edac.c | 21 | ||||
-rw-r--r-- | drivers/edac/qcom_edac.c | 8 | ||||
-rw-r--r-- | drivers/edac/skx_edac.c | 151 | ||||
-rw-r--r-- | drivers/edac/synopsys_edac.c | 1204 |
13 files changed, 1152 insertions, 315 deletions
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 41c9ccdd20d6..e286b5b99003 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -231,10 +231,10 @@ config EDAC_SBRIDGE config EDAC_SKX tristate "Intel Skylake server Integrated MC" - depends on PCI && X86_64 && X86_MCE_INTEL && PCI_MMCONFIG + depends on PCI && X86_64 && X86_MCE_INTEL && PCI_MMCONFIG && ACPI depends on ACPI_NFIT || !ACPI_NFIT # if ACPI_NFIT=m, EDAC_SKX can't be y select DMI - select ACPI_ADXL if ACPI + select ACPI_ADXL help Support for error detection and correction the Intel Skylake server Integrated Memory Controllers. If your @@ -442,7 +442,7 @@ config EDAC_ALTERA_SDMMC config EDAC_SYNOPSYS tristate "Synopsys DDR Memory Controller" - depends on ARCH_ZYNQ + depends on ARCH_ZYNQ || ARCH_ZYNQMP help Support for error detection and correction on the Synopsys DDR memory controller. diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c index b5de9a13ea3f..de732dc2ef33 100644 --- a/drivers/edac/e752x_edac.c +++ b/drivers/edac/e752x_edac.c @@ -1446,8 +1446,8 @@ static int __init e752x_init(void) edac_dbg(3, "\n"); - /* Ensure that the OPSTATE is set correctly for POLL or NMI */ - opstate_init(); + /* Ensure that the OPSTATE is set correctly for POLL or NMI */ + opstate_init(); pci_rc = pci_register_driver(&e752x_driver); return (pci_rc < 0) ? pci_rc : 0; diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 7d3edd713932..13594ffadcb3 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -55,8 +55,6 @@ static LIST_HEAD(mc_devices); */ static const char *edac_mc_owner; -static struct bus_type mc_bus[EDAC_MAX_MCS]; - int edac_get_report_status(void) { return edac_report; @@ -716,11 +714,6 @@ int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci, int ret = -EINVAL; edac_dbg(0, "\n"); - if (mci->mc_idx >= EDAC_MAX_MCS) { - pr_warn_once("Too many memory controllers: %d\n", mci->mc_idx); - return -ENODEV; - } - #ifdef CONFIG_EDAC_DEBUG if (edac_debug_level >= 3) edac_mc_dump_mci(mci); @@ -760,7 +753,7 @@ int edac_mc_add_mc_with_groups(struct mem_ctl_info *mci, /* set load time so that error rate can be tracked */ mci->start_time = jiffies; - mci->bus = &mc_bus[mci->mc_idx]; + mci->bus = edac_get_sysfs_subsys(); if (edac_create_sysfs_mci_device(mci, groups)) { edac_mc_printk(mci, KERN_WARNING, diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 20374b8248f0..464174685589 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -405,7 +405,6 @@ static int edac_create_csrow_object(struct mem_ctl_info *mci, struct csrow_info *csrow, int index) { csrow->dev.type = &csrow_attr_type; - csrow->dev.bus = mci->bus; csrow->dev.groups = csrow_dev_groups; device_initialize(&csrow->dev); csrow->dev.parent = &mci->dev; @@ -636,7 +635,6 @@ static int edac_create_dimm_object(struct mem_ctl_info *mci, dimm->mci = mci; dimm->dev.type = &dimm_attr_type; - dimm->dev.bus = mci->bus; device_initialize(&dimm->dev); dimm->dev.parent = &mci->dev; @@ -914,33 +912,13 @@ static const struct device_type mci_attr_type = { int edac_create_sysfs_mci_device(struct mem_ctl_info *mci, const struct attribute_group **groups) { - char *name; int i, err; - /* - * The memory controller needs its own bus, in order to avoid - * namespace conflicts at /sys/bus/edac. - */ - name = kasprintf(GFP_KERNEL, "mc%d", mci->mc_idx); - if (!name) - return -ENOMEM; - - mci->bus->name = name; - - edac_dbg(0, "creating bus %s\n", mci->bus->name); - - err = bus_register(mci->bus); - if (err < 0) { - kfree(name); - return err; - } - /* get the /sys/devices/system/edac subsys reference */ mci->dev.type = &mci_attr_type; device_initialize(&mci->dev); mci->dev.parent = mci_pdev; - mci->dev.bus = mci->bus; mci->dev.groups = groups; dev_set_name(&mci->dev, "mc%d", mci->mc_idx); dev_set_drvdata(&mci->dev, mci); @@ -950,7 +928,7 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci, err = device_add(&mci->dev); if (err < 0) { edac_dbg(1, "failure: create device %s\n", dev_name(&mci->dev)); - goto fail_unregister_bus; + goto out; } /* @@ -998,10 +976,8 @@ fail_unregister_dimm: device_unregister(&dimm->dev); } device_unregister(&mci->dev); -fail_unregister_bus: - bus_unregister(mci->bus); - kfree(name); +out: return err; } @@ -1032,13 +1008,8 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) void edac_unregister_sysfs(struct mem_ctl_info *mci) { - struct bus_type *bus = mci->bus; - const char *name = mci->bus->name; - edac_dbg(1, "Unregistering device %s\n", dev_name(&mci->dev)); device_unregister(&mci->dev); - bus_unregister(bus); - kfree(name); } static void mc_attr_release(struct device *dev) diff --git a/drivers/edac/fsl_ddr_edac.c b/drivers/edac/fsl_ddr_edac.c index efc8276d1d9c..6d8ea226010d 100644 --- a/drivers/edac/fsl_ddr_edac.c +++ b/drivers/edac/fsl_ddr_edac.c @@ -2,8 +2,8 @@ * Freescale Memory Controller kernel module * * Support Power-based SoCs including MPC85xx, MPC86xx, MPC83xx and - * ARM-based Layerscape SoCs including LS2xxx. Originally split - * out from mpc85xx_edac EDAC driver. + * ARM-based Layerscape SoCs including LS2xxx and LS1021A. Originally + * split out from mpc85xx_edac EDAC driver. * * Parts Copyrighted (c) 2013 by Freescale Semiconductor, Inc. * @@ -51,6 +51,7 @@ static inline void ddr_out32(void __iomem *addr, u32 value) iowrite32be(value, addr); } +#ifdef CONFIG_EDAC_DEBUG /************************ MC SYSFS parts ***********************************/ #define to_mci(k) container_of(k, struct mem_ctl_info, dev) @@ -151,11 +152,14 @@ static DEVICE_ATTR(inject_data_lo, S_IRUGO | S_IWUSR, fsl_mc_inject_data_lo_show, fsl_mc_inject_data_lo_store); static DEVICE_ATTR(inject_ctrl, S_IRUGO | S_IWUSR, fsl_mc_inject_ctrl_show, fsl_mc_inject_ctrl_store); +#endif /* CONFIG_EDAC_DEBUG */ static struct attribute *fsl_ddr_dev_attrs[] = { +#ifdef CONFIG_EDAC_DEBUG &dev_attr_inject_data_hi.attr, &dev_attr_inject_data_lo.attr, &dev_attr_inject_ctrl.attr, +#endif NULL }; diff --git a/drivers/edac/fsl_ddr_edac.h b/drivers/edac/fsl_ddr_edac.h index 4ccee292eff1..589b9b4a5e8a 100644 --- a/drivers/edac/fsl_ddr_edac.h +++ b/drivers/edac/fsl_ddr_edac.h @@ -2,8 +2,8 @@ * Freescale Memory Controller kernel module * * Support Power-based SoCs including MPC85xx, MPC86xx, MPC83xx and - * ARM-based Layerscape SoCs including LS2xxx. Originally split - * out from mpc85xx_edac EDAC driver. + * ARM-based Layerscape SoCs including LS2xxx and LS1021A. Originally + * split out from mpc85xx_edac EDAC driver. * * Author: Dave Jiang <djiang@mvista.com> * diff --git a/drivers/edac/i3000_edac.c b/drivers/edac/i3000_edac.c index 8085a32ec3bd..f564a4a8a4ae 100644 --- a/drivers/edac/i3000_edac.c +++ b/drivers/edac/i3000_edac.c @@ -508,8 +508,8 @@ static int __init i3000_init(void) edac_dbg(3, "MC:\n"); - /* Ensure that the OPSTATE is set correctly for POLL or NMI */ - opstate_init(); + /* Ensure that the OPSTATE is set correctly for POLL or NMI */ + opstate_init(); pci_rc = pci_register_driver(&i3000_driver); if (pci_rc < 0) diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c index 53f24b18cd61..078a7351bf05 100644 --- a/drivers/edac/i5000_edac.c +++ b/drivers/edac/i5000_edac.c @@ -1134,8 +1134,6 @@ static void i5000_get_mc_regs(struct mem_ctl_info *mci) u32 actual_tolm; u16 limit; int slot_row; - int maxch; - int maxdimmperch; int way0, way1; pvt = mci->pvt_info; @@ -1145,9 +1143,6 @@ static void i5000_get_mc_regs(struct mem_ctl_info *mci) pci_read_config_dword(pvt->system_address, AMBASE + sizeof(u32), &pvt->u.ambase_top); - maxdimmperch = pvt->maxdimmperch; - maxch = pvt->maxch; - edac_dbg(2, "AMBASE= 0x%lx MAXCH= %d MAX-DIMM-Per-CH= %d\n", (long unsigned int)pvt->ambase, pvt->maxch, pvt->maxdimmperch); @@ -1253,7 +1248,7 @@ static int i5000_init_csrows(struct mem_ctl_info *mci) { struct i5000_pvt *pvt; struct dimm_info *dimm; - int empty, channel_count; + int empty; int max_csrows; int mtr; int csrow_megs; @@ -1261,8 +1256,6 @@ static int i5000_init_csrows(struct mem_ctl_info *mci) int slot; pvt = mci->pvt_info; - - channel_count = pvt->maxch; max_csrows = pvt->maxdimmperch * 2; empty = 1; /* Assume NO memory */ @@ -1559,8 +1552,8 @@ static int __init i5000_init(void) edac_dbg(2, "MC:\n"); - /* Ensure that the OPSTATE is set correctly for POLL or NMI */ - opstate_init(); + /* Ensure that the OPSTATE is set correctly for POLL or NMI */ + opstate_init(); pci_rc = pci_register_driver(&i5000_driver); diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 9ef448fef12f..40297550313a 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -724,7 +724,7 @@ static ssize_t i7core_inject_type_store(struct device *dev, const char *data, size_t count) { struct mem_ctl_info *mci = to_mci(dev); -struct i7core_pvt *pvt = mci->pvt_info; + struct i7core_pvt *pvt = mci->pvt_info; unsigned long value; int rc; diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c index 892815eaa97b..7c6a2d4d2360 100644 --- a/drivers/edac/i82975x_edac.c +++ b/drivers/edac/i82975x_edac.c @@ -104,7 +104,7 @@ NOTE: Only ONE of the three must be enabled * * 31:14 Base Addr of 16K memory-mapped * configuration space - * 13:1 reserverd + * 13:1 reserved * 0 mem-mapped config space enable */ @@ -358,14 +358,6 @@ static int dual_channel_active(void __iomem *mch_window) return dualch; } -static enum dev_type i82975x_dram_type(void __iomem *mch_window, int rank) -{ - /* - * ECC is possible on i92975x ONLY with DEV_X8 - */ - return DEV_X8; -} - static void i82975x_init_csrows(struct mem_ctl_info *mci, struct pci_dev *pdev, void __iomem *mch_window) { @@ -375,7 +367,6 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci, u32 cumul_size, nr_pages; int index, chan; struct dimm_info *dimm; - enum dev_type dtype; last_cumul_size = 0; @@ -413,7 +404,6 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci, * [0-7] for single-channel; i.e. csrow->nr_channels = 1 * [0-3] for dual-channel; i.e. csrow->nr_channels = 2 */ - dtype = i82975x_dram_type(mch_window, index); for (chan = 0; chan < csrow->nr_channels; chan++) { dimm = mci->csrows[index]->channels[chan]->dimm; @@ -423,7 +413,10 @@ static void i82975x_init_csrows(struct mem_ctl_info *mci, (chan == 0) ? 'A' : 'B', index); dimm->grain = 1 << 7; /* 128Byte cache-line resolution */ - dimm->dtype = i82975x_dram_type(mch_window, index); + + /* ECC is possible on i92975x ONLY with DEV_X8. */ + dimm->dtype = DEV_X8; + dimm->mtype = MEM_DDR2; /* I82975x supports only DDR2 */ dimm->edac_mode = EDAC_SECDED; /* only supported */ } @@ -655,8 +648,8 @@ static int __init i82975x_init(void) edac_dbg(3, "\n"); - /* Ensure that the OPSTATE is set correctly for POLL or NMI */ - opstate_init(); + /* Ensure that the OPSTATE is set correctly for POLL or NMI */ + opstate_init(); pci_rc = pci_register_driver(&i82975x_driver); if (pci_rc < 0) diff --git a/drivers/edac/qcom_edac.c b/drivers/edac/qcom_edac.c index 82bd775124f2..97a27e42dd61 100644 --- a/drivers/edac/qcom_edac.c +++ b/drivers/edac/qcom_edac.c @@ -292,7 +292,6 @@ llcc_ecc_irq_handler(int irq, void *edev_ctl) struct llcc_drv_data *drv = edac_dev_ctl->pvt_info; irqreturn_t irq_rc = IRQ_NONE; u32 drp_error, trp_error, i; - bool irq_handled; int ret; /* Iterate over the banks and look for Tag RAM or Data RAM errors */ @@ -311,7 +310,7 @@ llcc_ecc_irq_handler(int irq, void *edev_ctl) ret = dump_syn_reg(edev_ctl, LLCC_DRAM_UE, i); } if (!ret) - irq_handled = true; + irq_rc = IRQ_HANDLED; ret = regmap_read(drv->regmap, drv->offsets[i] + TRP_INTERRUPT_0_STATUS, @@ -327,12 +326,9 @@ llcc_ecc_irq_handler(int irq, void *edev_ctl) ret = dump_syn_reg(edev_ctl, LLCC_TRAM_UE, i); } if (!ret) - irq_handled = true; + irq_rc = IRQ_HANDLED; } - if (irq_handled) - irq_rc = IRQ_HANDLED; - return irq_rc; } diff --git a/drivers/edac/skx_edac.c b/drivers/edac/skx_edac.c index a99ea61dad32..93ef161bb5e1 100644 --- a/drivers/edac/skx_edac.c +++ b/drivers/edac/skx_edac.c @@ -209,7 +209,7 @@ static int get_all_bus_mappings(void) d->bus[1] = GET_BITFIELD(reg, 8, 15); d->bus[2] = GET_BITFIELD(reg, 16, 23); d->bus[3] = GET_BITFIELD(reg, 24, 31); - edac_dbg(2, "busses: %x, %x, %x, %x\n", + edac_dbg(2, "busses: 0x%x, 0x%x, 0x%x, 0x%x\n", d->bus[0], d->bus[1], d->bus[2], d->bus[3]); list_add_tail(&d->list, &skx_edac_list); skx_num_sockets++; @@ -245,8 +245,8 @@ static int get_all_munits(const struct munit *m) /* Be sure that the device is enabled */ if (unlikely(pci_enable_device(pdev) < 0)) { - skx_printk(KERN_ERR, - "Couldn't enable %04x:%04x\n", PCI_VENDOR_ID_INTEL, m->did); + skx_printk(KERN_ERR, "Couldn't enable device %04x:%04x\n", + PCI_VENDOR_ID_INTEL, m->did); goto fail; } @@ -323,7 +323,7 @@ static int get_dimm_attr(u32 reg, int lobit, int hibit, int add, int minval, u32 val = GET_BITFIELD(reg, lobit, hibit); if (val < minval || val > maxval) { - edac_dbg(2, "bad %s = %d (raw=%x)\n", name, val, reg); + edac_dbg(2, "bad %s = %d (raw=0x%x)\n", name, val, reg); return -EINVAL; } return val + add; @@ -368,7 +368,7 @@ static int skx_get_hi_lo(void) skx_tohm |= (u64)reg << 32; pci_dev_put(pdev); - edac_dbg(2, "tolm=%llx tohm=%llx\n", skx_tolm, skx_tohm); + edac_dbg(2, "tolm=0x%llx tohm=0x%llx\n", skx_tolm, skx_tohm); return 0; } @@ -389,7 +389,7 @@ static int get_dimm_info(u32 mtr, u32 amap, struct dimm_info *dimm, size = ((1ull << (rows + cols + ranks)) * banks) >> (20 - 3); npages = MiB_TO_PAGES(size); - edac_dbg(0, "mc#%d: channel %d, dimm %d, %lld MiB (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n", + edac_dbg(0, "mc#%d: channel %d, dimm %d, %lld MiB (%d pages) bank: %d, rank: %d, row: 0x%#x, col: 0x%#x\n", imc->mc, chan, dimmno, size, npages, banks, 1 << ranks, rows, cols); @@ -430,18 +430,18 @@ static int get_nvdimm_info(struct dimm_info *dimm, struct skx_imc *imc, } if (smbios_handle < 0) { - skx_printk(KERN_ERR, "Can't find handle for NVDIMM ADR=%x\n", dev_handle); + skx_printk(KERN_ERR, "Can't find handle for NVDIMM ADR=0x%x\n", dev_handle); goto unknown_size; } if (flags & ACPI_NFIT_MEM_MAP_FAILED) { - skx_printk(KERN_ERR, "NVDIMM ADR=%x is not mapped\n", dev_handle); + skx_printk(KERN_ERR, "NVDIMM ADR=0x%x is not mapped\n", dev_handle); goto unknown_size; } size = dmi_memdev_size(smbios_handle); if (size == ~0ull) - skx_printk(KERN_ERR, "Can't find size for NVDIMM ADR=%x/SMBIOS=%x\n", + skx_printk(KERN_ERR, "Can't find size for NVDIMM ADR=0x%x/SMBIOS=0x%x\n", dev_handle, smbios_handle); unknown_size: @@ -616,7 +616,7 @@ static bool skx_sad_decode(struct decoded_addr *res) /* Simple sanity check for I/O space or out of range */ if (addr >= skx_tohm || (addr >= skx_tolm && addr < BIT_ULL(32))) { - edac_dbg(0, "Address %llx out of range\n", addr); + edac_dbg(0, "Address 0x%llx out of range\n", addr); return false; } @@ -631,7 +631,7 @@ restart: } prev_limit = limit + 1; } - edac_dbg(0, "No SAD entry for %llx\n", addr); + edac_dbg(0, "No SAD entry for 0x%llx\n", addr); return false; sad_found: @@ -709,7 +709,7 @@ sad_found: res->imc = GET_BITFIELD(d->mcroute, lchan * 3, lchan * 3 + 2); res->channel = GET_BITFIELD(d->mcroute, lchan * 2 + 18, lchan * 2 + 19); - edac_dbg(2, "%llx: socket=%d imc=%d channel=%d\n", + edac_dbg(2, "0x%llx: socket=%d imc=%d channel=%d\n", res->addr, res->socket, res->imc, res->channel); return true; } @@ -756,7 +756,7 @@ static bool skx_tad_decode(struct decoded_addr *res) if (SKX_TAD_BASE(base) <= res->addr && res->addr <= SKX_TAD_LIMIT(wayness)) goto tad_found; } - edac_dbg(0, "No TAD entry for %llx\n", res->addr); + edac_dbg(0, "No TAD entry for 0x%llx\n", res->addr); return false; tad_found: @@ -784,7 +784,7 @@ tad_found: res->chan_addr = channel_addr; - edac_dbg(2, "%llx: chan_addr=%llx sktways=%d chanways=%d\n", + edac_dbg(2, "0x%llx: chan_addr=0x%llx sktways=%d chanways=%d\n", res->addr, res->chan_addr, res->sktways, res->chanways); return true; } @@ -826,7 +826,7 @@ static bool skx_rir_decode(struct decoded_addr *res) } prev_limit = limit; } - edac_dbg(0, "No RIR entry for %llx\n", res->addr); + edac_dbg(0, "No RIR entry for 0x%llx\n", res->addr); return false; rir_found: @@ -845,7 +845,7 @@ rir_found: res->dimm = chan_rank / 4; res->rank = chan_rank % 4; - edac_dbg(2, "%llx: dimm=%d rank=%d chan_rank=%d rank_addr=%llx\n", + edac_dbg(2, "0x%llx: dimm=%d rank=%d chan_rank=%d rank_addr=0x%llx\n", res->addr, res->dimm, res->rank, res->channel_rank, res->rank_address); return true; @@ -908,7 +908,7 @@ static bool skx_mad_decode(struct decoded_addr *r) } r->row &= (1u << dimm->rowbits) - 1; - edac_dbg(2, "%llx: row=%x col=%x bank_addr=%d bank_group=%d\n", + edac_dbg(2, "0x%llx: row=0x%x col=0x%x bank_addr=%d bank_group=%d\n", r->addr, r->row, r->column, r->bank_address, r->bank_group); return true; @@ -921,53 +921,6 @@ static bool skx_decode(struct decoded_addr *res) skx_rir_decode(res) && skx_mad_decode(res); } -#ifdef CONFIG_EDAC_DEBUG -/* - * Debug feature. Make /sys/kernel/debug/skx_edac_test/addr. - * Write an address to this file to exercise the address decode - * logic in this driver. - */ -static struct dentry *skx_test; -static u64 skx_fake_addr; - -static int debugfs_u64_set(void *data, u64 val) -{ - struct decoded_addr res; - - res.addr = val; - skx_decode(&res); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n"); - -static struct dentry *mydebugfs_create(const char *name, umode_t mode, - struct dentry *parent, u64 *value) -{ - return debugfs_create_file(name, mode, parent, value, &fops_u64_wo); -} - -static void setup_skx_debug(void) -{ - skx_test = debugfs_create_dir("skx_edac_test", NULL); - mydebugfs_create("addr", S_IWUSR, skx_test, &skx_fake_addr); -} - -static void teardown_skx_debug(void) -{ - debugfs_remove_recursive(skx_test); -} -#else -static void setup_skx_debug(void) -{ -} - -static void teardown_skx_debug(void) -{ -} -#endif /*CONFIG_EDAC_DEBUG*/ - static bool skx_adxl_decode(struct decoded_addr *res) { @@ -1069,13 +1022,13 @@ static void skx_mce_output_error(struct mem_ctl_info *mci, } } if (adxl_component_count) { - snprintf(skx_msg, MSG_SIZE, "%s%s err_code:%04x:%04x %s", + snprintf(skx_msg, MSG_SIZE, "%s%s err_code:0x%04x:0x%04x %s", overflow ? " OVERFLOW" : "", (uncorrected_error && recoverable) ? " recoverable" : "", mscod, errcode, adxl_msg); } else { snprintf(skx_msg, MSG_SIZE, - "%s%s err_code:%04x:%04x socket:%d imc:%d rank:%d bg:%d ba:%d row:%x col:%x", + "%s%s err_code:0x%04x:0x%04x socket:%d imc:%d rank:%d bg:%d ba:%d row:0x%x col:0x%x", overflow ? " OVERFLOW" : "", (uncorrected_error && recoverable) ? " recoverable" : "", mscod, errcode, @@ -1151,15 +1104,15 @@ static int skx_mce_check_error(struct notifier_block *nb, unsigned long val, skx_mc_printk(mci, KERN_DEBUG, "HANDLING MCE MEMORY ERROR\n"); - skx_mc_printk(mci, KERN_DEBUG, "CPU %d: Machine Check %s: %Lx " + skx_mc_printk(mci, KERN_DEBUG, "CPU %d: Machine Check %s: 0x%llx " "Bank %d: %016Lx\n", mce->extcpu, type, mce->mcgstatus, mce->bank, mce->status); - skx_mc_printk(mci, KERN_DEBUG, "TSC %llx ", mce->tsc); - skx_mc_printk(mci, KERN_DEBUG, "ADDR %llx ", mce->addr); - skx_mc_printk(mci, KERN_DEBUG, "MISC %llx ", mce->misc); + skx_mc_printk(mci, KERN_DEBUG, "TSC 0x%llx ", mce->tsc); + skx_mc_printk(mci, KERN_DEBUG, "ADDR 0x%llx ", mce->addr); + skx_mc_printk(mci, KERN_DEBUG, "MISC 0x%llx ", mce->misc); - skx_mc_printk(mci, KERN_DEBUG, "PROCESSOR %u:%x TIME %llu SOCKET " - "%u APIC %x\n", mce->cpuvendor, mce->cpuid, + skx_mc_printk(mci, KERN_DEBUG, "PROCESSOR %u:0x%x TIME %llu SOCKET " + "%u APIC 0x%x\n", mce->cpuvendor, mce->cpuid, mce->time, mce->socketid, mce->apicid); skx_mce_output_error(mci, mce, &res); @@ -1172,6 +1125,54 @@ static struct notifier_block skx_mce_dec = { .priority = MCE_PRIO_EDAC, }; +#ifdef CONFIG_EDAC_DEBUG +/* + * Debug feature. + * Exercise the address decode logic by writing an address to + * /sys/kernel/debug/edac/skx_test/addr. + */ +static struct dentry *skx_test; + +static int debugfs_u64_set(void *data, u64 val) +{ + struct mce m; + + pr_warn_once("Fake error to 0x%llx injected via debugfs\n", val); + + memset(&m, 0, sizeof(m)); + /* ADDRV + MemRd + Unknown channel */ + m.status = MCI_STATUS_ADDRV + 0x90; + /* One corrected error */ + m.status |= BIT_ULL(MCI_STATUS_CEC_SHIFT); + m.addr = val; + skx_mce_check_error(NULL, 0, &m); + + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n"); + +static void setup_skx_debug(void) +{ + skx_test = edac_debugfs_create_dir("skx_test"); + if (!skx_test) + return; + + if (!edac_debugfs_create_file("addr", 0200, skx_test, + NULL, &fops_u64_wo)) { + debugfs_remove(skx_test); + skx_test = NULL; + } +} + +static void teardown_skx_debug(void) +{ + debugfs_remove_recursive(skx_test); +} +#else +static void setup_skx_debug(void) {} +static void teardown_skx_debug(void) {} +#endif /*CONFIG_EDAC_DEBUG*/ + static void skx_remove(void) { int i, j; @@ -1291,7 +1292,7 @@ static int __init skx_init(void) if (rc < 0) goto fail; if (rc != m->per_socket * skx_num_sockets) { - edac_dbg(2, "Expected %d, got %d of %x\n", + edac_dbg(2, "Expected %d, got %d of 0x%x\n", m->per_socket * skx_num_sockets, rc, m->did); rc = -ENODEV; goto fail; @@ -1339,11 +1340,11 @@ static void __exit skx_exit(void) { edac_dbg(2, "\n"); mce_unregister_decode_chain(&skx_mce_dec); - skx_remove(); + teardown_skx_debug(); if (nvdimm_count) skx_adxl_put(); kfree(skx_msg); - teardown_skx_debug(); + skx_remove(); } module_init(skx_init); diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c index 0c9c59e2b5a3..2d263382d797 100644 --- a/drivers/edac/synopsys_edac.c +++ b/drivers/edac/synopsys_edac.c @@ -22,86 +22,259 @@ #include <linux/edac.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/of.h> +#include <linux/of_device.h> #include "edac_module.h" /* Number of cs_rows needed per memory controller */ -#define SYNPS_EDAC_NR_CSROWS 1 +#define SYNPS_EDAC_NR_CSROWS 1 /* Number of channels per memory controller */ -#define SYNPS_EDAC_NR_CHANS 1 +#define SYNPS_EDAC_NR_CHANS 1 /* Granularity of reported error in bytes */ -#define SYNPS_EDAC_ERR_GRAIN 1 +#define SYNPS_EDAC_ERR_GRAIN 1 -#define SYNPS_EDAC_MSG_SIZE 256 +#define SYNPS_EDAC_MSG_SIZE 256 -#define SYNPS_EDAC_MOD_STRING "synps_edac" -#define SYNPS_EDAC_MOD_VER "1" +#define SYNPS_EDAC_MOD_STRING "synps_edac" +#define SYNPS_EDAC_MOD_VER "1" /* Synopsys DDR memory controller registers that are relevant to ECC */ -#define CTRL_OFST 0x0 -#define T_ZQ_OFST 0xA4 +#define CTRL_OFST 0x0 +#define T_ZQ_OFST 0xA4 /* ECC control register */ -#define ECC_CTRL_OFST 0xC4 +#define ECC_CTRL_OFST 0xC4 /* ECC log register */ -#define CE_LOG_OFST 0xC8 +#define CE_LOG_OFST 0xC8 /* ECC address register */ -#define CE_ADDR_OFST 0xCC +#define CE_ADDR_OFST 0xCC /* ECC data[31:0] register */ -#define CE_DATA_31_0_OFST 0xD0 +#define CE_DATA_31_0_OFST 0xD0 /* Uncorrectable error info registers */ -#define UE_LOG_OFST 0xDC -#define UE_ADDR_OFST 0xE0 -#define UE_DATA_31_0_OFST 0xE4 +#define UE_LOG_OFST 0xDC +#define UE_ADDR_OFST 0xE0 +#define UE_DATA_31_0_OFST 0xE4 -#define STAT_OFST 0xF0 -#define SCRUB_OFST 0xF4 +#define STAT_OFST 0xF0 +#define SCRUB_OFST 0xF4 /* Control register bit field definitions */ -#define CTRL_BW_MASK 0xC -#define CTRL_BW_SHIFT 2 +#define CTRL_BW_MASK 0xC +#define CTRL_BW_SHIFT 2 -#define DDRCTL_WDTH_16 1 -#define DDRCTL_WDTH_32 0 +#define DDRCTL_WDTH_16 1 +#define DDRCTL_WDTH_32 0 /* ZQ register bit field definitions */ -#define T_ZQ_DDRMODE_MASK 0x2 +#define T_ZQ_DDRMODE_MASK 0x2 /* ECC control register bit field definitions */ -#define ECC_CTRL_CLR_CE_ERR 0x2 -#define ECC_CTRL_CLR_UE_ERR 0x1 +#define ECC_CTRL_CLR_CE_ERR 0x2 +#define ECC_CTRL_CLR_UE_ERR 0x1 /* ECC correctable/uncorrectable error log register definitions */ -#define LOG_VALID 0x1 -#define CE_LOG_BITPOS_MASK 0xFE -#define CE_LOG_BITPOS_SHIFT 1 +#define LOG_VALID 0x1 +#define CE_LOG_BITPOS_MASK 0xFE +#define CE_LOG_BITPOS_SHIFT 1 /* ECC correctable/uncorrectable error address register definitions */ -#define ADDR_COL_MASK 0xFFF -#define ADDR_ROW_MASK 0xFFFF000 -#define ADDR_ROW_SHIFT 12 -#define ADDR_BANK_MASK 0x70000000 -#define ADDR_BANK_SHIFT 28 +#define ADDR_COL_MASK 0xFFF +#define ADDR_ROW_MASK 0xFFFF000 +#define ADDR_ROW_SHIFT 12 +#define ADDR_BANK_MASK 0x70000000 +#define ADDR_BANK_SHIFT 28 /* ECC statistic register definitions */ -#define STAT_UECNT_MASK 0xFF -#define STAT_CECNT_MASK 0xFF00 -#define STAT_CECNT_SHIFT 8 +#define STAT_UECNT_MASK 0xFF +#define STAT_CECNT_MASK 0xFF00 +#define STAT_CECNT_SHIFT 8 /* ECC scrub register definitions */ -#define SCRUB_MODE_MASK 0x7 -#define SCRUB_MODE_SECDED 0x4 +#define SCRUB_MODE_MASK 0x7 +#define SCRUB_MODE_SECDED 0x4 + +/* DDR ECC Quirks */ +#define DDR_ECC_INTR_SUPPORT BIT(0) +#define DDR_ECC_DATA_POISON_SUPPORT BIT(1) + +/* ZynqMP Enhanced DDR memory controller registers that are relevant to ECC */ +/* ECC Configuration Registers */ +#define ECC_CFG0_OFST 0x70 +#define ECC_CFG1_OFST 0x74 + +/* ECC Status Register */ +#define ECC_STAT_OFST 0x78 + +/* ECC Clear Register */ +#define ECC_CLR_OFST 0x7C + +/* ECC Error count Register */ +#define ECC_ERRCNT_OFST 0x80 + +/* ECC Corrected Error Address Register */ +#define ECC_CEADDR0_OFST 0x84 +#define ECC_CEADDR1_OFST 0x88 + +/* ECC Syndrome Registers */ +#define ECC_CSYND0_OFST 0x8C +#define ECC_CSYND1_OFST 0x90 +#define ECC_CSYND2_OFST 0x94 + +/* ECC Bit Mask0 Address Register */ +#define ECC_BITMASK0_OFST 0x98 +#define ECC_BITMASK1_OFST 0x9C +#define ECC_BITMASK2_OFST 0xA0 + +/* ECC UnCorrected Error Address Register */ +#define ECC_UEADDR0_OFST 0xA4 +#define ECC_UEADDR1_OFST 0xA8 + +/* ECC Syndrome Registers */ +#define ECC_UESYND0_OFST 0xAC +#define ECC_UESYND1_OFST 0xB0 +#define ECC_UESYND2_OFST 0xB4 + +/* ECC Poison Address Reg */ +#define ECC_POISON0_OFST 0xB8 +#define ECC_POISON1_OFST 0xBC + +#define ECC_ADDRMAP0_OFFSET 0x200 + +/* Control register bitfield definitions */ +#define ECC_CTRL_BUSWIDTH_MASK 0x3000 +#define ECC_CTRL_BUSWIDTH_SHIFT 12 +#define ECC_CTRL_CLR_CE_ERRCNT BIT(2) +#define ECC_CTRL_CLR_UE_ERRCNT BIT(3) + +/* DDR Control Register width definitions */ +#define DDRCTL_EWDTH_16 2 +#define DDRCTL_EWDTH_32 1 +#define DDRCTL_EWDTH_64 0 + +/* ECC status register definitions */ +#define ECC_STAT_UECNT_MASK 0xF0000 +#define ECC_STAT_UECNT_SHIFT 16 +#define ECC_STAT_CECNT_MASK 0xF00 +#define ECC_STAT_CECNT_SHIFT 8 +#define ECC_STAT_BITNUM_MASK 0x7F + +/* DDR QOS Interrupt register definitions */ +#define DDR_QOS_IRQ_STAT_OFST 0x20200 +#define DDR_QOSUE_MASK 0x4 +#define DDR_QOSCE_MASK 0x2 +#define ECC_CE_UE_INTR_MASK 0x6 +#define DDR_QOS_IRQ_EN_OFST 0x20208 +#define DDR_QOS_IRQ_DB_OFST 0x2020C + +/* ECC Corrected Error Register Mask and Shifts*/ +#define ECC_CEADDR0_RW_MASK 0x3FFFF +#define ECC_CEADDR0_RNK_MASK BIT(24) +#define ECC_CEADDR1_BNKGRP_MASK 0x3000000 +#define ECC_CEADDR1_BNKNR_MASK 0x70000 +#define ECC_CEADDR1_BLKNR_MASK 0xFFF +#define ECC_CEADDR1_BNKGRP_SHIFT 24 +#define ECC_CEADDR1_BNKNR_SHIFT 16 + +/* ECC Poison register shifts */ +#define ECC_POISON0_RANK_SHIFT 24 +#define ECC_POISON0_RANK_MASK BIT(24) +#define ECC_POISON0_COLUMN_SHIFT 0 +#define ECC_POISON0_COLUMN_MASK 0xFFF +#define ECC_POISON1_BG_SHIFT 28 +#define ECC_POISON1_BG_MASK 0x30000000 +#define ECC_POISON1_BANKNR_SHIFT 24 +#define ECC_POISON1_BANKNR_MASK 0x7000000 +#define ECC_POISON1_ROW_SHIFT 0 +#define ECC_POISON1_ROW_MASK 0x3FFFF + +/* DDR Memory type defines */ +#define MEM_TYPE_DDR3 0x1 +#define MEM_TYPE_LPDDR3 0x8 +#define MEM_TYPE_DDR2 0x4 +#define MEM_TYPE_DDR4 0x10 +#define MEM_TYPE_LPDDR4 0x20 + +/* DDRC Software control register */ +#define DDRC_SWCTL 0x320 + +/* DDRC ECC CE & UE poison mask */ +#define ECC_CEPOISON_MASK 0x3 +#define ECC_UEPOISON_MASK 0x1 + +/* DDRC Device config masks */ +#define DDRC_MSTR_CFG_MASK 0xC0000000 +#define DDRC_MSTR_CFG_SHIFT 30 +#define DDRC_MSTR_CFG_X4_MASK 0x0 +#define DDRC_MSTR_CFG_X8_MASK 0x1 +#define DDRC_MSTR_CFG_X16_MASK 0x2 +#define DDRC_MSTR_CFG_X32_MASK 0x3 + +#define DDR_MAX_ROW_SHIFT 18 +#define DDR_MAX_COL_SHIFT 14 +#define DDR_MAX_BANK_SHIFT 3 +#define DDR_MAX_BANKGRP_SHIFT 2 + +#define ROW_MAX_VAL_MASK 0xF +#define COL_MAX_VAL_MASK 0xF +#define BANK_MAX_VAL_MASK 0x1F +#define BANKGRP_MAX_VAL_MASK 0x1F +#define RANK_MAX_VAL_MASK 0x1F + +#define ROW_B0_BASE 6 +#define ROW_B1_BASE 7 +#define ROW_B2_BASE 8 +#define ROW_B3_BASE 9 +#define ROW_B4_BASE 10 +#define ROW_B5_BASE 11 +#define ROW_B6_BASE 12 +#define ROW_B7_BASE 13 +#define ROW_B8_BASE 14 +#define ROW_B9_BASE 15 +#define ROW_B10_BASE 16 +#define ROW_B11_BASE 17 +#define ROW_B12_BASE 18 +#define ROW_B13_BASE 19 +#define ROW_B14_BASE 20 +#define ROW_B15_BASE 21 +#define ROW_B16_BASE 22 +#define ROW_B17_BASE 23 + +#define COL_B2_BASE 2 +#define COL_B3_BASE 3 +#define COL_B4_BASE 4 +#define COL_B5_BASE 5 +#define COL_B6_BASE 6 +#define COL_B7_BASE 7 +#define COL_B8_BASE 8 +#define COL_B9_BASE 9 +#define COL_B10_BASE 10 +#define COL_B11_BASE 11 +#define COL_B12_BASE 12 +#define COL_B13_BASE 13 + +#define BANK_B0_BASE 2 +#define BANK_B1_BASE 3 +#define BANK_B2_BASE 4 + +#define BANKGRP_B0_BASE 2 +#define BANKGRP_B1_BASE 3 + +#define RANK_B0_BASE 6 /** - * struct ecc_error_info - ECC error log information - * @row: Row number - * @col: Column number - * @bank: Bank number - * @bitpos: Bit position - * @data: Data causing the error + * struct ecc_error_info - ECC error log information. + * @row: Row number. + * @col: Column number. + * @bank: Bank number. + * @bitpos: Bit position. + * @data: Data causing the error. + * @bankgrpnr: Bank group number. + * @blknr: Block number. */ struct ecc_error_info { u32 row; @@ -109,14 +282,16 @@ struct ecc_error_info { u32 bank; u32 bitpos; u32 data; + u32 bankgrpnr; + u32 blknr; }; /** - * struct synps_ecc_status - ECC status information to report - * @ce_cnt: Correctable error count - * @ue_cnt: Uncorrectable error count - * @ceinfo: Correctable error log information - * @ueinfo: Uncorrectable error log information + * struct synps_ecc_status - ECC status information to report. + * @ce_cnt: Correctable error count. + * @ue_cnt: Uncorrectable error count. + * @ceinfo: Correctable error log information. + * @ueinfo: Uncorrectable error log information. */ struct synps_ecc_status { u32 ce_cnt; @@ -126,34 +301,67 @@ struct synps_ecc_status { }; /** - * struct synps_edac_priv - DDR memory controller private instance data - * @baseaddr: Base address of the DDR controller - * @message: Buffer for framing the event specific info - * @stat: ECC status information - * @ce_cnt: Correctable Error count - * @ue_cnt: Uncorrectable Error count + * struct synps_edac_priv - DDR memory controller private instance data. + * @baseaddr: Base address of the DDR controller. + * @message: Buffer for framing the event specific info. + * @stat: ECC status information. + * @p_data: Platform data. + * @ce_cnt: Correctable Error count. + * @ue_cnt: Uncorrectable Error count. + * @poison_addr: Data poison address. + * @row_shift: Bit shifts for row bit. + * @col_shift: Bit shifts for column bit. + * @bank_shift: Bit shifts for bank bit. + * @bankgrp_shift: Bit shifts for bank group bit. + * @rank_shift: Bit shifts for rank bit. */ struct synps_edac_priv { void __iomem *baseaddr; char message[SYNPS_EDAC_MSG_SIZE]; struct synps_ecc_status stat; + const struct synps_platform_data *p_data; u32 ce_cnt; u32 ue_cnt; +#ifdef CONFIG_EDAC_DEBUG + ulong poison_addr; + u32 row_shift[18]; + u32 col_shift[14]; + u32 bank_shift[3]; + u32 bankgrp_shift[2]; + u32 rank_shift[1]; +#endif }; /** - * synps_edac_geterror_info - Get the current ecc error info - * @base: Pointer to the base address of the ddr memory controller - * @p: Pointer to the synopsys ecc status structure - * - * Determines there is any ecc error or not + * struct synps_platform_data - synps platform data structure. + * @get_error_info: Get EDAC error info. + * @get_mtype: Get mtype. + * @get_dtype: Get dtype. + * @get_ecc_state: Get ECC state. + * @quirks: To differentiate IPs. + */ +struct synps_platform_data { + int (*get_error_info)(struct synps_edac_priv *priv); + enum mem_type (*get_mtype)(const void __iomem *base); + enum dev_type (*get_dtype)(const void __iomem *base); + bool (*get_ecc_state)(void __iomem *base); + int quirks; +}; + +/** + * zynq_get_error_info - Get the current ECC error info. + * @priv: DDR memory controller private instance data. * - * Return: one if there is no error otherwise returns zero + * Return: one if there is no error, otherwise zero. */ -static int synps_edac_geterror_info(void __iomem *base, - struct synps_ecc_status *p) +static int zynq_get_error_info(struct synps_edac_priv *priv) { + struct synps_ecc_status *p; u32 regval, clearval = 0; + void __iomem *base; + + base = priv->baseaddr; + p = &priv->stat; regval = readl(base + STAT_OFST); if (!regval) @@ -172,7 +380,7 @@ static int synps_edac_geterror_info(void __iomem *base, p->ceinfo.col = regval & ADDR_COL_MASK; p->ceinfo.bank = (regval & ADDR_BANK_MASK) >> ADDR_BANK_SHIFT; p->ceinfo.data = readl(base + CE_DATA_31_0_OFST); - edac_dbg(3, "ce bit position: %d data: %d\n", p->ceinfo.bitpos, + edac_dbg(3, "CE bit position: %d data: %d\n", p->ceinfo.bitpos, p->ceinfo.data); clearval = ECC_CTRL_CLR_CE_ERR; @@ -196,23 +404,98 @@ out: } /** - * synps_edac_handle_error - Handle controller error types CE and UE - * @mci: Pointer to the edac memory controller instance - * @p: Pointer to the synopsys ecc status structure + * zynqmp_get_error_info - Get the current ECC error info. + * @priv: DDR memory controller private instance data. * - * Handles the controller ECC correctable and un correctable error. + * Return: one if there is no error otherwise returns zero. */ -static void synps_edac_handle_error(struct mem_ctl_info *mci, - struct synps_ecc_status *p) +static int zynqmp_get_error_info(struct synps_edac_priv *priv) +{ + struct synps_ecc_status *p; + u32 regval, clearval = 0; + void __iomem *base; + + base = priv->baseaddr; + p = &priv->stat; + + regval = readl(base + ECC_STAT_OFST); + if (!regval) + return 1; + + p->ce_cnt = (regval & ECC_STAT_CECNT_MASK) >> ECC_STAT_CECNT_SHIFT; + p->ue_cnt = (regval & ECC_STAT_UECNT_MASK) >> ECC_STAT_UECNT_SHIFT; + if (!p->ce_cnt) + goto ue_err; + + p->ceinfo.bitpos = (regval & ECC_STAT_BITNUM_MASK); + + regval = readl(base + ECC_CEADDR0_OFST); + p->ceinfo.row = (regval & ECC_CEADDR0_RW_MASK); + regval = readl(base + ECC_CEADDR1_OFST); + p->ceinfo.bank = (regval & ECC_CEADDR1_BNKNR_MASK) >> + ECC_CEADDR1_BNKNR_SHIFT; + p->ceinfo.bankgrpnr = (regval & ECC_CEADDR1_BNKGRP_MASK) >> + ECC_CEADDR1_BNKGRP_SHIFT; + p->ceinfo.blknr = (regval & ECC_CEADDR1_BLKNR_MASK); + p->ceinfo.data = readl(base + ECC_CSYND0_OFST); + edac_dbg(2, "ECCCSYN0: 0x%08X ECCCSYN1: 0x%08X ECCCSYN2: 0x%08X\n", + readl(base + ECC_CSYND0_OFST), readl(base + ECC_CSYND1_OFST), + readl(base + ECC_CSYND2_OFST)); +ue_err: + if (!p->ue_cnt) + goto out; + + regval = readl(base + ECC_UEADDR0_OFST); + p->ueinfo.row = (regval & ECC_CEADDR0_RW_MASK); + regval = readl(base + ECC_UEADDR1_OFST); + p->ueinfo.bankgrpnr = (regval & ECC_CEADDR1_BNKGRP_MASK) >> + ECC_CEADDR1_BNKGRP_SHIFT; + p->ueinfo.bank = (regval & ECC_CEADDR1_BNKNR_MASK) >> + ECC_CEADDR1_BNKNR_SHIFT; + p->ueinfo.blknr = (regval & ECC_CEADDR1_BLKNR_MASK); + p->ueinfo.data = readl(base + ECC_UESYND0_OFST); +out: + clearval = ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_CE_ERRCNT; + clearval |= ECC_CTRL_CLR_UE_ERR | ECC_CTRL_CLR_UE_ERRCNT; + writel(clearval, base + ECC_CLR_OFST); + writel(0x0, base + ECC_CLR_OFST); + + return 0; +} + +/** + * handle_error - Handle Correctable and Uncorrectable errors. + * @mci: EDAC memory controller instance. + * @p: Synopsys ECC status structure. + * + * Handles ECC correctable and uncorrectable errors. + */ +static void handle_error(struct mem_ctl_info *mci, struct synps_ecc_status *p) { struct synps_edac_priv *priv = mci->pvt_info; struct ecc_error_info *pinf; if (p->ce_cnt) { pinf = &p->ceinfo; - snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, - "DDR ECC error type :%s Row %d Bank %d Col %d ", - "CE", pinf->row, pinf->bank, pinf->col); + if (!priv->p_data->quirks) { + snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, + "DDR ECC error type:%s Row %d Bank %d Col %d ", + "CE", pinf->row, pinf->bank, pinf->col); + snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, + "Bit Position: %d Data: 0x%08x\n", + pinf->bitpos, pinf->data); + } else { + snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, + "DDR ECC error type:%s Row %d Bank %d Col %d ", + "CE", pinf->row, pinf->bank, pinf->col); + snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, + "BankGroup Number %d Block Number %d ", + pinf->bankgrpnr, pinf->blknr); + snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, + "Bit Position: %d Data: 0x%08x\n", + pinf->bitpos, pinf->data); + } + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, p->ce_cnt, 0, 0, 0, 0, 0, -1, priv->message, ""); @@ -220,9 +503,19 @@ static void synps_edac_handle_error(struct mem_ctl_info *mci, if (p->ue_cnt) { pinf = &p->ueinfo; - snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, - "DDR ECC error type :%s Row %d Bank %d Col %d ", - "UE", pinf->row, pinf->bank, pinf->col); + if (!priv->p_data->quirks) { + snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, + "DDR ECC error type :%s Row %d Bank %d Col %d ", + "UE", pinf->row, pinf->bank, pinf->col); + } else { + snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, + "DDR ECC error type :%s Row %d Bank %d Col %d ", + "UE", pinf->row, pinf->bank, pinf->col); + snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, + "BankGroup Number %d Block Number %d", + pinf->bankgrpnr, pinf->blknr); + } + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, p->ue_cnt, 0, 0, 0, 0, 0, -1, priv->message, ""); @@ -232,38 +525,78 @@ static void synps_edac_handle_error(struct mem_ctl_info *mci, } /** - * synps_edac_check - Check controller for ECC errors - * @mci: Pointer to the edac memory controller instance + * intr_handler - Interrupt Handler for ECC interrupts. + * @irq: IRQ number. + * @dev_id: Device ID. * - * Used to check and post ECC errors. Called by the polling thread + * Return: IRQ_NONE, if interrupt not set or IRQ_HANDLED otherwise. */ -static void synps_edac_check(struct mem_ctl_info *mci) +static irqreturn_t intr_handler(int irq, void *dev_id) { - struct synps_edac_priv *priv = mci->pvt_info; + const struct synps_platform_data *p_data; + struct mem_ctl_info *mci = dev_id; + struct synps_edac_priv *priv; + int status, regval; + + priv = mci->pvt_info; + p_data = priv->p_data; + + regval = readl(priv->baseaddr + DDR_QOS_IRQ_STAT_OFST); + regval &= (DDR_QOSCE_MASK | DDR_QOSUE_MASK); + if (!(regval & ECC_CE_UE_INTR_MASK)) + return IRQ_NONE; + + status = p_data->get_error_info(priv); + if (status) + return IRQ_NONE; + + priv->ce_cnt += priv->stat.ce_cnt; + priv->ue_cnt += priv->stat.ue_cnt; + handle_error(mci, &priv->stat); + + edac_dbg(3, "Total error count CE %d UE %d\n", + priv->ce_cnt, priv->ue_cnt); + writel(regval, priv->baseaddr + DDR_QOS_IRQ_STAT_OFST); + return IRQ_HANDLED; +} + +/** + * check_errors - Check controller for ECC errors. + * @mci: EDAC memory controller instance. + * + * Check and post ECC errors. Called by the polling thread. + */ +static void check_errors(struct mem_ctl_info *mci) +{ + const struct synps_platform_data *p_data; + struct synps_edac_priv *priv; int status; - status = synps_edac_geterror_info(priv->baseaddr, &priv->stat); + priv = mci->pvt_info; + p_data = priv->p_data; + + status = p_data->get_error_info(priv); if (status) return; priv->ce_cnt += priv->stat.ce_cnt; priv->ue_cnt += priv->stat.ue_cnt; - synps_edac_handle_error(mci, &priv->stat); + handle_error(mci, &priv->stat); - edac_dbg(3, "Total error count ce %d ue %d\n", + edac_dbg(3, "Total error count CE %d UE %d\n", priv->ce_cnt, priv->ue_cnt); } /** - * synps_edac_get_dtype - Return the controller memory width - * @base: Pointer to the ddr memory controller base address + * zynq_get_dtype - Return the controller memory width. + * @base: DDR memory controller base address. * * Get the EDAC device type width appropriate for the current controller * configuration. * * Return: a device type width enumeration. */ -static enum dev_type synps_edac_get_dtype(const void __iomem *base) +static enum dev_type zynq_get_dtype(const void __iomem *base) { enum dev_type dt; u32 width; @@ -286,36 +619,93 @@ static enum dev_type synps_edac_get_dtype(const void __iomem *base) } /** - * synps_edac_get_eccstate - Return the controller ecc enable/disable status - * @base: Pointer to the ddr memory controller base address + * zynqmp_get_dtype - Return the controller memory width. + * @base: DDR memory controller base address. + * + * Get the EDAC device type width appropriate for the current controller + * configuration. + * + * Return: a device type width enumeration. + */ +static enum dev_type zynqmp_get_dtype(const void __iomem *base) +{ + enum dev_type dt; + u32 width; + + width = readl(base + CTRL_OFST); + width = (width & ECC_CTRL_BUSWIDTH_MASK) >> ECC_CTRL_BUSWIDTH_SHIFT; + switch (width) { + case DDRCTL_EWDTH_16: + dt = DEV_X2; + break; + case DDRCTL_EWDTH_32: + dt = DEV_X4; + break; + case DDRCTL_EWDTH_64: + dt = DEV_X8; + break; + default: + dt = DEV_UNKNOWN; + } + + return dt; +} + +/** + * zynq_get_ecc_state - Return the controller ECC enable/disable status. + * @base: DDR memory controller base address. * - * Get the ECC enable/disable status for the controller + * Get the ECC enable/disable status of the controller. * - * Return: a ecc status boolean i.e true/false - enabled/disabled. + * Return: true if enabled, otherwise false. */ -static bool synps_edac_get_eccstate(void __iomem *base) +static bool zynq_get_ecc_state(void __iomem *base) { enum dev_type dt; u32 ecctype; - bool state = false; - dt = synps_edac_get_dtype(base); + dt = zynq_get_dtype(base); if (dt == DEV_UNKNOWN) - return state; + return false; ecctype = readl(base + SCRUB_OFST) & SCRUB_MODE_MASK; if ((ecctype == SCRUB_MODE_SECDED) && (dt == DEV_X2)) - state = true; + return true; - return state; + return false; } /** - * synps_edac_get_memsize - reads the size of the attached memory device + * zynqmp_get_ecc_state - Return the controller ECC enable/disable status. + * @base: DDR memory controller base address. * - * Return: the memory size in bytes + * Get the ECC enable/disable status for the controller. + * + * Return: a ECC status boolean i.e true/false - enabled/disabled. */ -static u32 synps_edac_get_memsize(void) +static bool zynqmp_get_ecc_state(void __iomem *base) +{ + enum dev_type dt; + u32 ecctype; + + dt = zynqmp_get_dtype(base); + if (dt == DEV_UNKNOWN) + return false; + + ecctype = readl(base + ECC_CFG0_OFST) & SCRUB_MODE_MASK; + if ((ecctype == SCRUB_MODE_SECDED) && + ((dt == DEV_X2) || (dt == DEV_X4) || (dt == DEV_X8))) + return true; + + return false; +} + +/** + * get_memsize - Read the size of the attached memory device. + * + * Return: the memory size in bytes. + */ +static u32 get_memsize(void) { struct sysinfo inf; @@ -325,15 +715,15 @@ static u32 synps_edac_get_memsize(void) } /** - * synps_edac_get_mtype - Returns controller memory type - * @base: pointer to the synopsys ecc status structure + * zynq_get_mtype - Return the controller memory type. + * @base: Synopsys ECC status structure. * * Get the EDAC memory type appropriate for the current controller * configuration. * * Return: a memory type enumeration. */ -static enum mem_type synps_edac_get_mtype(const void __iomem *base) +static enum mem_type zynq_get_mtype(const void __iomem *base) { enum mem_type mt; u32 memtype; @@ -349,54 +739,77 @@ static enum mem_type synps_edac_get_mtype(const void __iomem *base) } /** - * synps_edac_init_csrows - Initialize the cs row data - * @mci: Pointer to the edac memory controller instance + * zynqmp_get_mtype - Returns controller memory type. + * @base: Synopsys ECC status structure. * - * Initializes the chip select rows associated with the EDAC memory - * controller instance + * Get the EDAC memory type appropriate for the current controller + * configuration. * - * Return: Unconditionally 0. + * Return: a memory type enumeration. */ -static int synps_edac_init_csrows(struct mem_ctl_info *mci) +static enum mem_type zynqmp_get_mtype(const void __iomem *base) { + enum mem_type mt; + u32 memtype; + + memtype = readl(base + CTRL_OFST); + + if ((memtype & MEM_TYPE_DDR3) || (memtype & MEM_TYPE_LPDDR3)) + mt = MEM_DDR3; + else if (memtype & MEM_TYPE_DDR2) + mt = MEM_RDDR2; + else if ((memtype & MEM_TYPE_LPDDR4) || (memtype & MEM_TYPE_DDR4)) + mt = MEM_DDR4; + else + mt = MEM_EMPTY; + + return mt; +} + +/** + * init_csrows - Initialize the csrow data. + * @mci: EDAC memory controller instance. + * + * Initialize the chip select rows associated with the EDAC memory + * controller instance. + */ +static void init_csrows(struct mem_ctl_info *mci) +{ + struct synps_edac_priv *priv = mci->pvt_info; + const struct synps_platform_data *p_data; struct csrow_info *csi; struct dimm_info *dimm; - struct synps_edac_priv *priv = mci->pvt_info; - u32 size; - int row, j; + u32 size, row; + int j; + + p_data = priv->p_data; for (row = 0; row < mci->nr_csrows; row++) { csi = mci->csrows[row]; - size = synps_edac_get_memsize(); + size = get_memsize(); for (j = 0; j < csi->nr_channels; j++) { - dimm = csi->channels[j]->dimm; - dimm->edac_mode = EDAC_FLAG_SECDED; - dimm->mtype = synps_edac_get_mtype(priv->baseaddr); - dimm->nr_pages = (size >> PAGE_SHIFT) / csi->nr_channels; - dimm->grain = SYNPS_EDAC_ERR_GRAIN; - dimm->dtype = synps_edac_get_dtype(priv->baseaddr); + dimm = csi->channels[j]->dimm; + dimm->edac_mode = EDAC_FLAG_SECDED; + dimm->mtype = p_data->get_mtype(priv->baseaddr); + dimm->nr_pages = (size >> PAGE_SHIFT) / csi->nr_channels; + dimm->grain = SYNPS_EDAC_ERR_GRAIN; + dimm->dtype = p_data->get_dtype(priv->baseaddr); } } - - return 0; } /** - * synps_edac_mc_init - Initialize driver instance - * @mci: Pointer to the edac memory controller instance - * @pdev: Pointer to the platform_device struct + * mc_init - Initialize one driver instance. + * @mci: EDAC memory controller instance. + * @pdev: platform device. * - * Performs initialization of the EDAC memory controller instance and + * Perform initialization of the EDAC memory controller instance and * related driver-private data associated with the memory controller the * instance is bound to. - * - * Return: Always zero. */ -static int synps_edac_mc_init(struct mem_ctl_info *mci, - struct platform_device *pdev) +static void mc_init(struct mem_ctl_info *mci, struct platform_device *pdev) { - int status; struct synps_edac_priv *priv; mci->pdev = &pdev->dev; @@ -414,39 +827,491 @@ static int synps_edac_mc_init(struct mem_ctl_info *mci, mci->dev_name = SYNPS_EDAC_MOD_STRING; mci->mod_name = SYNPS_EDAC_MOD_VER; - edac_op_state = EDAC_OPSTATE_POLL; - mci->edac_check = synps_edac_check; + if (priv->p_data->quirks & DDR_ECC_INTR_SUPPORT) { + edac_op_state = EDAC_OPSTATE_INT; + } else { + edac_op_state = EDAC_OPSTATE_POLL; + mci->edac_check = check_errors; + } + mci->ctl_page_to_phys = NULL; - status = synps_edac_init_csrows(mci); + init_csrows(mci); +} + +static void enable_intr(struct synps_edac_priv *priv) +{ + /* Enable UE/CE Interrupts */ + writel(DDR_QOSUE_MASK | DDR_QOSCE_MASK, + priv->baseaddr + DDR_QOS_IRQ_EN_OFST); +} + +static void disable_intr(struct synps_edac_priv *priv) +{ + /* Disable UE/CE Interrupts */ + writel(DDR_QOSUE_MASK | DDR_QOSCE_MASK, + priv->baseaddr + DDR_QOS_IRQ_DB_OFST); +} + +static int setup_irq(struct mem_ctl_info *mci, + struct platform_device *pdev) +{ + struct synps_edac_priv *priv = mci->pvt_info; + int ret, irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + edac_printk(KERN_ERR, EDAC_MC, + "No IRQ %d in DT\n", irq); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, intr_handler, + 0, dev_name(&pdev->dev), mci); + if (ret < 0) { + edac_printk(KERN_ERR, EDAC_MC, "Failed to request IRQ\n"); + return ret; + } + + enable_intr(priv); + + return 0; +} + +static const struct synps_platform_data zynq_edac_def = { + .get_error_info = zynq_get_error_info, + .get_mtype = zynq_get_mtype, + .get_dtype = zynq_get_dtype, + .get_ecc_state = zynq_get_ecc_state, + .quirks = 0, +}; + +static const struct synps_platform_data zynqmp_edac_def = { + .get_error_info = zynqmp_get_error_info, + .get_mtype = zynqmp_get_mtype, + .get_dtype = zynqmp_get_dtype, + .get_ecc_state = zynqmp_get_ecc_state, + .quirks = (DDR_ECC_INTR_SUPPORT +#ifdef CONFIG_EDAC_DEBUG + | DDR_ECC_DATA_POISON_SUPPORT +#endif + ), +}; + +static const struct of_device_id synps_edac_match[] = { + { + .compatible = "xlnx,zynq-ddrc-a05", + .data = (void *)&zynq_edac_def + }, + { + .compatible = "xlnx,zynqmp-ddrc-2.40a", + .data = (void *)&zynqmp_edac_def + }, + { + /* end of table */ + } +}; + +MODULE_DEVICE_TABLE(of, synps_edac_match); + +#ifdef CONFIG_EDAC_DEBUG +#define to_mci(k) container_of(k, struct mem_ctl_info, dev) + +/** + * ddr_poison_setup - Update poison registers. + * @priv: DDR memory controller private instance data. + * + * Update poison registers as per DDR mapping. + * Return: none. + */ +static void ddr_poison_setup(struct synps_edac_priv *priv) +{ + int col = 0, row = 0, bank = 0, bankgrp = 0, rank = 0, regval; + int index; + ulong hif_addr = 0; + + hif_addr = priv->poison_addr >> 3; + + for (index = 0; index < DDR_MAX_ROW_SHIFT; index++) { + if (priv->row_shift[index]) + row |= (((hif_addr >> priv->row_shift[index]) & + BIT(0)) << index); + else + break; + } + + for (index = 0; index < DDR_MAX_COL_SHIFT; index++) { + if (priv->col_shift[index] || index < 3) + col |= (((hif_addr >> priv->col_shift[index]) & + BIT(0)) << index); + else + break; + } + + for (index = 0; index < DDR_MAX_BANK_SHIFT; index++) { + if (priv->bank_shift[index]) + bank |= (((hif_addr >> priv->bank_shift[index]) & + BIT(0)) << index); + else + break; + } + + for (index = 0; index < DDR_MAX_BANKGRP_SHIFT; index++) { + if (priv->bankgrp_shift[index]) + bankgrp |= (((hif_addr >> priv->bankgrp_shift[index]) + & BIT(0)) << index); + else + break; + } + + if (priv->rank_shift[0]) + rank = (hif_addr >> priv->rank_shift[0]) & BIT(0); + + regval = (rank << ECC_POISON0_RANK_SHIFT) & ECC_POISON0_RANK_MASK; + regval |= (col << ECC_POISON0_COLUMN_SHIFT) & ECC_POISON0_COLUMN_MASK; + writel(regval, priv->baseaddr + ECC_POISON0_OFST); + + regval = (bankgrp << ECC_POISON1_BG_SHIFT) & ECC_POISON1_BG_MASK; + regval |= (bank << ECC_POISON1_BANKNR_SHIFT) & ECC_POISON1_BANKNR_MASK; + regval |= (row << ECC_POISON1_ROW_SHIFT) & ECC_POISON1_ROW_MASK; + writel(regval, priv->baseaddr + ECC_POISON1_OFST); +} + +static ssize_t inject_data_error_show(struct device *dev, + struct device_attribute *mattr, + char *data) +{ + struct mem_ctl_info *mci = to_mci(dev); + struct synps_edac_priv *priv = mci->pvt_info; + + return sprintf(data, "Poison0 Addr: 0x%08x\n\rPoison1 Addr: 0x%08x\n\r" + "Error injection Address: 0x%lx\n\r", + readl(priv->baseaddr + ECC_POISON0_OFST), + readl(priv->baseaddr + ECC_POISON1_OFST), + priv->poison_addr); +} + +static ssize_t inject_data_error_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) +{ + struct mem_ctl_info *mci = to_mci(dev); + struct synps_edac_priv *priv = mci->pvt_info; + + if (kstrtoul(data, 0, &priv->poison_addr)) + return -EINVAL; + + ddr_poison_setup(priv); + + return count; +} + +static ssize_t inject_data_poison_show(struct device *dev, + struct device_attribute *mattr, + char *data) +{ + struct mem_ctl_info *mci = to_mci(dev); + struct synps_edac_priv *priv = mci->pvt_info; + + return sprintf(data, "Data Poisoning: %s\n\r", + (((readl(priv->baseaddr + ECC_CFG1_OFST)) & 0x3) == 0x3) + ? ("Correctable Error") : ("UnCorrectable Error")); +} + +static ssize_t inject_data_poison_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) +{ + struct mem_ctl_info *mci = to_mci(dev); + struct synps_edac_priv *priv = mci->pvt_info; + + writel(0, priv->baseaddr + DDRC_SWCTL); + if (strncmp(data, "CE", 2) == 0) + writel(ECC_CEPOISON_MASK, priv->baseaddr + ECC_CFG1_OFST); + else + writel(ECC_UEPOISON_MASK, priv->baseaddr + ECC_CFG1_OFST); + writel(1, priv->baseaddr + DDRC_SWCTL); + + return count; +} + +static DEVICE_ATTR_RW(inject_data_error); +static DEVICE_ATTR_RW(inject_data_poison); + +static int edac_create_sysfs_attributes(struct mem_ctl_info *mci) +{ + int rc; + + rc = device_create_file(&mci->dev, &dev_attr_inject_data_error); + if (rc < 0) + return rc; + rc = device_create_file(&mci->dev, &dev_attr_inject_data_poison); + if (rc < 0) + return rc; + return 0; +} + +static void edac_remove_sysfs_attributes(struct mem_ctl_info *mci) +{ + device_remove_file(&mci->dev, &dev_attr_inject_data_error); + device_remove_file(&mci->dev, &dev_attr_inject_data_poison); +} + +static void setup_row_address_map(struct synps_edac_priv *priv, u32 *addrmap) +{ + u32 addrmap_row_b2_10; + int index; + + priv->row_shift[0] = (addrmap[5] & ROW_MAX_VAL_MASK) + ROW_B0_BASE; + priv->row_shift[1] = ((addrmap[5] >> 8) & + ROW_MAX_VAL_MASK) + ROW_B1_BASE; + + addrmap_row_b2_10 = (addrmap[5] >> 16) & ROW_MAX_VAL_MASK; + if (addrmap_row_b2_10 != ROW_MAX_VAL_MASK) { + for (index = 2; index < 11; index++) + priv->row_shift[index] = addrmap_row_b2_10 + + index + ROW_B0_BASE; + + } else { + priv->row_shift[2] = (addrmap[9] & + ROW_MAX_VAL_MASK) + ROW_B2_BASE; + priv->row_shift[3] = ((addrmap[9] >> 8) & + ROW_MAX_VAL_MASK) + ROW_B3_BASE; + priv->row_shift[4] = ((addrmap[9] >> 16) & + ROW_MAX_VAL_MASK) + ROW_B4_BASE; + priv->row_shift[5] = ((addrmap[9] >> 24) & + ROW_MAX_VAL_MASK) + ROW_B5_BASE; + priv->row_shift[6] = (addrmap[10] & + ROW_MAX_VAL_MASK) + ROW_B6_BASE; + priv->row_shift[7] = ((addrmap[10] >> 8) & + ROW_MAX_VAL_MASK) + ROW_B7_BASE; + priv->row_shift[8] = ((addrmap[10] >> 16) & + ROW_MAX_VAL_MASK) + ROW_B8_BASE; + priv->row_shift[9] = ((addrmap[10] >> 24) & + ROW_MAX_VAL_MASK) + ROW_B9_BASE; + priv->row_shift[10] = (addrmap[11] & + ROW_MAX_VAL_MASK) + ROW_B10_BASE; + } - return status; + priv->row_shift[11] = (((addrmap[5] >> 24) & ROW_MAX_VAL_MASK) == + ROW_MAX_VAL_MASK) ? 0 : (((addrmap[5] >> 24) & + ROW_MAX_VAL_MASK) + ROW_B11_BASE); + priv->row_shift[12] = ((addrmap[6] & ROW_MAX_VAL_MASK) == + ROW_MAX_VAL_MASK) ? 0 : ((addrmap[6] & + ROW_MAX_VAL_MASK) + ROW_B12_BASE); + priv->row_shift[13] = (((addrmap[6] >> 8) & ROW_MAX_VAL_MASK) == + ROW_MAX_VAL_MASK) ? 0 : (((addrmap[6] >> 8) & + ROW_MAX_VAL_MASK) + ROW_B13_BASE); + priv->row_shift[14] = (((addrmap[6] >> 16) & ROW_MAX_VAL_MASK) == + ROW_MAX_VAL_MASK) ? 0 : (((addrmap[6] >> 16) & + ROW_MAX_VAL_MASK) + ROW_B14_BASE); + priv->row_shift[15] = (((addrmap[6] >> 24) & ROW_MAX_VAL_MASK) == + ROW_MAX_VAL_MASK) ? 0 : (((addrmap[6] >> 24) & + ROW_MAX_VAL_MASK) + ROW_B15_BASE); + priv->row_shift[16] = ((addrmap[7] & ROW_MAX_VAL_MASK) == + ROW_MAX_VAL_MASK) ? 0 : ((addrmap[7] & + ROW_MAX_VAL_MASK) + ROW_B16_BASE); + priv->row_shift[17] = (((addrmap[7] >> 8) & ROW_MAX_VAL_MASK) == + ROW_MAX_VAL_MASK) ? 0 : (((addrmap[7] >> 8) & + ROW_MAX_VAL_MASK) + ROW_B17_BASE); +} + +static void setup_column_address_map(struct synps_edac_priv *priv, u32 *addrmap) +{ + u32 width, memtype; + int index; + + memtype = readl(priv->baseaddr + CTRL_OFST); + width = (memtype & ECC_CTRL_BUSWIDTH_MASK) >> ECC_CTRL_BUSWIDTH_SHIFT; + + priv->col_shift[0] = 0; + priv->col_shift[1] = 1; + priv->col_shift[2] = (addrmap[2] & COL_MAX_VAL_MASK) + COL_B2_BASE; + priv->col_shift[3] = ((addrmap[2] >> 8) & + COL_MAX_VAL_MASK) + COL_B3_BASE; + priv->col_shift[4] = (((addrmap[2] >> 16) & COL_MAX_VAL_MASK) == + COL_MAX_VAL_MASK) ? 0 : (((addrmap[2] >> 16) & + COL_MAX_VAL_MASK) + COL_B4_BASE); + priv->col_shift[5] = (((addrmap[2] >> 24) & COL_MAX_VAL_MASK) == + COL_MAX_VAL_MASK) ? 0 : (((addrmap[2] >> 24) & + COL_MAX_VAL_MASK) + COL_B5_BASE); + priv->col_shift[6] = ((addrmap[3] & COL_MAX_VAL_MASK) == + COL_MAX_VAL_MASK) ? 0 : ((addrmap[3] & + COL_MAX_VAL_MASK) + COL_B6_BASE); + priv->col_shift[7] = (((addrmap[3] >> 8) & COL_MAX_VAL_MASK) == + COL_MAX_VAL_MASK) ? 0 : (((addrmap[3] >> 8) & + COL_MAX_VAL_MASK) + COL_B7_BASE); + priv->col_shift[8] = (((addrmap[3] >> 16) & COL_MAX_VAL_MASK) == + COL_MAX_VAL_MASK) ? 0 : (((addrmap[3] >> 16) & + COL_MAX_VAL_MASK) + COL_B8_BASE); + priv->col_shift[9] = (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) == + COL_MAX_VAL_MASK) ? 0 : (((addrmap[3] >> 24) & + COL_MAX_VAL_MASK) + COL_B9_BASE); + if (width == DDRCTL_EWDTH_64) { + if (memtype & MEM_TYPE_LPDDR3) { + priv->col_shift[10] = ((addrmap[4] & + COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : + ((addrmap[4] & COL_MAX_VAL_MASK) + + COL_B10_BASE); + priv->col_shift[11] = (((addrmap[4] >> 8) & + COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : + (((addrmap[4] >> 8) & COL_MAX_VAL_MASK) + + COL_B11_BASE); + } else { + priv->col_shift[11] = ((addrmap[4] & + COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : + ((addrmap[4] & COL_MAX_VAL_MASK) + + COL_B10_BASE); + priv->col_shift[13] = (((addrmap[4] >> 8) & + COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : + (((addrmap[4] >> 8) & COL_MAX_VAL_MASK) + + COL_B11_BASE); + } + } else if (width == DDRCTL_EWDTH_32) { + if (memtype & MEM_TYPE_LPDDR3) { + priv->col_shift[10] = (((addrmap[3] >> 24) & + COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : + (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) + + COL_B9_BASE); + priv->col_shift[11] = ((addrmap[4] & + COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : + ((addrmap[4] & COL_MAX_VAL_MASK) + + COL_B10_BASE); + } else { + priv->col_shift[11] = (((addrmap[3] >> 24) & + COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : + (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) + + COL_B9_BASE); + priv->col_shift[13] = ((addrmap[4] & + COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : + ((addrmap[4] & COL_MAX_VAL_MASK) + + COL_B10_BASE); + } + } else { + if (memtype & MEM_TYPE_LPDDR3) { + priv->col_shift[10] = (((addrmap[3] >> 16) & + COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : + (((addrmap[3] >> 16) & COL_MAX_VAL_MASK) + + COL_B8_BASE); + priv->col_shift[11] = (((addrmap[3] >> 24) & + COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : + (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) + + COL_B9_BASE); + priv->col_shift[13] = ((addrmap[4] & + COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : + ((addrmap[4] & COL_MAX_VAL_MASK) + + COL_B10_BASE); + } else { + priv->col_shift[11] = (((addrmap[3] >> 16) & + COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : + (((addrmap[3] >> 16) & COL_MAX_VAL_MASK) + + COL_B8_BASE); + priv->col_shift[13] = (((addrmap[3] >> 24) & + COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : + (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) + + COL_B9_BASE); + } + } + + if (width) { + for (index = 9; index > width; index--) { + priv->col_shift[index] = priv->col_shift[index - width]; + priv->col_shift[index - width] = 0; + } + } + +} + +static void setup_bank_address_map(struct synps_edac_priv *priv, u32 *addrmap) +{ + priv->bank_shift[0] = (addrmap[1] & BANK_MAX_VAL_MASK) + BANK_B0_BASE; + priv->bank_shift[1] = ((addrmap[1] >> 8) & + BANK_MAX_VAL_MASK) + BANK_B1_BASE; + priv->bank_shift[2] = (((addrmap[1] >> 16) & + BANK_MAX_VAL_MASK) == BANK_MAX_VAL_MASK) ? 0 : + (((addrmap[1] >> 16) & BANK_MAX_VAL_MASK) + + BANK_B2_BASE); + +} + +static void setup_bg_address_map(struct synps_edac_priv *priv, u32 *addrmap) +{ + priv->bankgrp_shift[0] = (addrmap[8] & + BANKGRP_MAX_VAL_MASK) + BANKGRP_B0_BASE; + priv->bankgrp_shift[1] = (((addrmap[8] >> 8) & BANKGRP_MAX_VAL_MASK) == + BANKGRP_MAX_VAL_MASK) ? 0 : (((addrmap[8] >> 8) + & BANKGRP_MAX_VAL_MASK) + BANKGRP_B1_BASE); + +} + +static void setup_rank_address_map(struct synps_edac_priv *priv, u32 *addrmap) +{ + priv->rank_shift[0] = ((addrmap[0] & RANK_MAX_VAL_MASK) == + RANK_MAX_VAL_MASK) ? 0 : ((addrmap[0] & + RANK_MAX_VAL_MASK) + RANK_B0_BASE); } /** - * synps_edac_mc_probe - Check controller and bind driver - * @pdev: Pointer to the platform_device struct + * setup_address_map - Set Address Map by querying ADDRMAP registers. + * @priv: DDR memory controller private instance data. * - * Probes a specific controller instance for binding with the driver. + * Set Address Map by querying ADDRMAP registers. + * + * Return: none. + */ +static void setup_address_map(struct synps_edac_priv *priv) +{ + u32 addrmap[12]; + int index; + + for (index = 0; index < 12; index++) { + u32 addrmap_offset; + + addrmap_offset = ECC_ADDRMAP0_OFFSET + (index * 4); + addrmap[index] = readl(priv->baseaddr + addrmap_offset); + } + + setup_row_address_map(priv, addrmap); + + setup_column_address_map(priv, addrmap); + + setup_bank_address_map(priv, addrmap); + + setup_bg_address_map(priv, addrmap); + + setup_rank_address_map(priv, addrmap); +} +#endif /* CONFIG_EDAC_DEBUG */ + +/** + * mc_probe - Check controller and bind driver. + * @pdev: platform device. + * + * Probe a specific controller instance for binding with the driver. * * Return: 0 if the controller instance was successfully bound to the * driver; otherwise, < 0 on error. */ -static int synps_edac_mc_probe(struct platform_device *pdev) +static int mc_probe(struct platform_device *pdev) { - struct mem_ctl_info *mci; + const struct synps_platform_data *p_data; struct edac_mc_layer layers[2]; struct synps_edac_priv *priv; - int rc; - struct resource *res; + struct mem_ctl_info *mci; void __iomem *baseaddr; + struct resource *res; + int rc; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); baseaddr = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(baseaddr)) return PTR_ERR(baseaddr); - if (!synps_edac_get_eccstate(baseaddr)) { + p_data = of_device_get_match_data(&pdev->dev); + if (!p_data) + return -ENODEV; + + if (!p_data->get_ecc_state(baseaddr)) { edac_printk(KERN_INFO, EDAC_MC, "ECC not enabled\n"); return -ENXIO; } @@ -468,11 +1333,14 @@ static int synps_edac_mc_probe(struct platform_device *pdev) priv = mci->pvt_info; priv->baseaddr = baseaddr; - rc = synps_edac_mc_init(mci, pdev); - if (rc) { - edac_printk(KERN_ERR, EDAC_MC, - "Failed to initialize instance\n"); - goto free_edac_mc; + priv->p_data = p_data; + + mc_init(mci, pdev); + + if (priv->p_data->quirks & DDR_ECC_INTR_SUPPORT) { + rc = setup_irq(mci, pdev); + if (rc) + goto free_edac_mc; } rc = edac_mc_add_mc(mci); @@ -482,11 +1350,27 @@ static int synps_edac_mc_probe(struct platform_device *pdev) goto free_edac_mc; } +#ifdef CONFIG_EDAC_DEBUG + if (priv->p_data->quirks & DDR_ECC_DATA_POISON_SUPPORT) { + if (edac_create_sysfs_attributes(mci)) { + edac_printk(KERN_ERR, EDAC_MC, + "Failed to create sysfs entries\n"); + goto free_edac_mc; + } + } + + if (of_device_is_compatible(pdev->dev.of_node, + "xlnx,zynqmp-ddrc-2.40a")) + setup_address_map(priv); +#endif + /* * Start capturing the correctable and uncorrectable errors. A write of * 0 starts the counters. */ - writel(0x0, baseaddr + ECC_CTRL_OFST); + if (!(priv->p_data->quirks & DDR_ECC_INTR_SUPPORT)) + writel(0x0, baseaddr + ECC_CTRL_OFST); + return rc; free_edac_mc: @@ -496,14 +1380,23 @@ free_edac_mc: } /** - * synps_edac_mc_remove - Unbind driver from controller - * @pdev: Pointer to the platform_device struct + * mc_remove - Unbind driver from controller. + * @pdev: Platform device. * * Return: Unconditionally 0 */ -static int synps_edac_mc_remove(struct platform_device *pdev) +static int mc_remove(struct platform_device *pdev) { struct mem_ctl_info *mci = platform_get_drvdata(pdev); + struct synps_edac_priv *priv = mci->pvt_info; + + if (priv->p_data->quirks & DDR_ECC_INTR_SUPPORT) + disable_intr(priv); + +#ifdef CONFIG_EDAC_DEBUG + if (priv->p_data->quirks & DDR_ECC_DATA_POISON_SUPPORT) + edac_remove_sysfs_attributes(mci); +#endif edac_mc_del_mc(&pdev->dev); edac_mc_free(mci); @@ -511,20 +1404,13 @@ static int synps_edac_mc_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id synps_edac_match[] = { - { .compatible = "xlnx,zynq-ddrc-a05", }, - { /* end of table */ } -}; - -MODULE_DEVICE_TABLE(of, synps_edac_match); - static struct platform_driver synps_edac_mc_driver = { .driver = { .name = "synopsys-edac", .of_match_table = synps_edac_match, }, - .probe = synps_edac_mc_probe, - .remove = synps_edac_mc_remove, + .probe = mc_probe, + .remove = mc_remove, }; module_platform_driver(synps_edac_mc_driver); |