summaryrefslogtreecommitdiffstats
path: root/drivers/edac
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/edac')
-rw-r--r--drivers/edac/Kconfig3
-rw-r--r--drivers/edac/altera_edac.c504
-rw-r--r--drivers/edac/altera_edac.h132
-rw-r--r--drivers/edac/ghes_edac.c55
-rw-r--r--drivers/edac/i7core_edac.c2
5 files changed, 645 insertions, 51 deletions
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index da2da53bca6d0..57304b2e989f2 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -232,6 +232,7 @@ config EDAC_SBRIDGE
config EDAC_SKX
tristate "Intel Skylake server Integrated MC"
depends on PCI && X86_64 && X86_MCE_INTEL && PCI_MMCONFIG
+ depends on ACPI_NFIT || !ACPI_NFIT # if ACPI_NFIT=m, EDAC_SKX can't be y
select DMI
help
Support for error detection and correction the Intel
@@ -374,7 +375,7 @@ config EDAC_THUNDERX
config EDAC_ALTERA
bool "Altera SOCFPGA ECC"
- depends on EDAC=y && ARCH_SOCFPGA
+ depends on EDAC=y && (ARCH_SOCFPGA || ARCH_STRATIX10)
help
Support for error detection and correction on the
Altera SOCs. This must be selected for SDRAM ECC.
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
index 11d6419788c2d..d0d5c4dbe0973 100644
--- a/drivers/edac/altera_edac.c
+++ b/drivers/edac/altera_edac.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
+ * Copyright (C) 2017-2018, Intel Corporation. All rights reserved
* Copyright Altera Corporation (C) 2014-2016. All rights reserved.
* Copyright 2011-2012 Calxeda, Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Adapted from the highbank_mc_edac driver.
*/
#include <asm/cacheflush.h>
@@ -26,6 +14,7 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
+#include <linux/notifier.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
@@ -80,6 +69,25 @@ static const struct altr_sdram_prv_data a10_data = {
.ue_set_mask = A10_DIAGINT_TDERRA_MASK,
};
+static const struct altr_sdram_prv_data s10_data = {
+ .ecc_ctrl_offset = S10_ECCCTRL1_OFST,
+ .ecc_ctl_en_mask = A10_ECCCTRL1_ECC_EN,
+ .ecc_stat_offset = S10_INTSTAT_OFST,
+ .ecc_stat_ce_mask = A10_INTSTAT_SBEERR,
+ .ecc_stat_ue_mask = A10_INTSTAT_DBEERR,
+ .ecc_saddr_offset = S10_SERRADDR_OFST,
+ .ecc_daddr_offset = S10_DERRADDR_OFST,
+ .ecc_irq_en_offset = S10_ERRINTEN_OFST,
+ .ecc_irq_en_mask = A10_ECC_IRQ_EN_MASK,
+ .ecc_irq_clr_offset = S10_INTSTAT_OFST,
+ .ecc_irq_clr_mask = (A10_INTSTAT_SBEERR | A10_INTSTAT_DBEERR),
+ .ecc_cnt_rst_offset = S10_ECCCTRL1_OFST,
+ .ecc_cnt_rst_mask = A10_ECC_CNT_RESET_MASK,
+ .ce_ue_trgr_offset = S10_DIAGINTTEST_OFST,
+ .ce_set_mask = A10_DIAGINT_TSERRA_MASK,
+ .ue_set_mask = A10_DIAGINT_TDERRA_MASK,
+};
+
/*********************** EDAC Memory Controller Functions ****************/
/* The SDRAM controller uses the EDAC Memory Controller framework. */
@@ -231,6 +239,7 @@ static unsigned long get_total_mem(void)
static const struct of_device_id altr_sdram_ctrl_of_match[] = {
{ .compatible = "altr,sdram-edac", .data = &c5_data},
{ .compatible = "altr,sdram-edac-a10", .data = &a10_data},
+ { .compatible = "altr,sdram-edac-s10", .data = &s10_data},
{},
};
MODULE_DEVICE_TABLE(of, altr_sdram_ctrl_of_match);
@@ -477,6 +486,292 @@ static int altr_sdram_remove(struct platform_device *pdev)
return 0;
}
+/**************** Stratix 10 EDAC Memory Controller Functions ************/
+
+/**
+ * s10_protected_reg_write
+ * Write to a protected SMC register.
+ * @context: Not used.
+ * @reg: Address of register
+ * @value: Value to write
+ * Return: INTEL_SIP_SMC_STATUS_OK (0) on success
+ * INTEL_SIP_SMC_REG_ERROR on error
+ * INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION if not supported
+ */
+static int s10_protected_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct arm_smccc_res result;
+
+ arm_smccc_smc(INTEL_SIP_SMC_REG_WRITE, reg, val, 0, 0,
+ 0, 0, 0, &result);
+
+ return (int)result.a0;
+}
+
+/**
+ * s10_protected_reg_read
+ * Read the status of a protected SMC register
+ * @context: Not used.
+ * @reg: Address of register
+ * @value: Value read.
+ * Return: INTEL_SIP_SMC_STATUS_OK (0) on success
+ * INTEL_SIP_SMC_REG_ERROR on error
+ * INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION if not supported
+ */
+static int s10_protected_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct arm_smccc_res result;
+
+ arm_smccc_smc(INTEL_SIP_SMC_REG_READ, reg, 0, 0, 0,
+ 0, 0, 0, &result);
+
+ *val = (unsigned int)result.a1;
+
+ return (int)result.a0;
+}
+
+static bool s10_sdram_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case S10_ECCCTRL1_OFST:
+ case S10_ERRINTEN_OFST:
+ case S10_INTMODE_OFST:
+ case S10_INTSTAT_OFST:
+ case S10_DIAGINTTEST_OFST:
+ case S10_SYSMGR_ECC_INTMASK_VAL_OFST:
+ case S10_SYSMGR_ECC_INTMASK_SET_OFST:
+ case S10_SYSMGR_ECC_INTMASK_CLR_OFST:
+ return true;
+ }
+ return false;
+}
+
+static bool s10_sdram_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case S10_ECCCTRL1_OFST:
+ case S10_ERRINTEN_OFST:
+ case S10_INTMODE_OFST:
+ case S10_INTSTAT_OFST:
+ case S10_DERRADDR_OFST:
+ case S10_SERRADDR_OFST:
+ case S10_DIAGINTTEST_OFST:
+ case S10_SYSMGR_ECC_INTMASK_VAL_OFST:
+ case S10_SYSMGR_ECC_INTMASK_SET_OFST:
+ case S10_SYSMGR_ECC_INTMASK_CLR_OFST:
+ case S10_SYSMGR_ECC_INTSTAT_SERR_OFST:
+ case S10_SYSMGR_ECC_INTSTAT_DERR_OFST:
+ return true;
+ }
+ return false;
+}
+
+static bool s10_sdram_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case S10_ECCCTRL1_OFST:
+ case S10_ERRINTEN_OFST:
+ case S10_INTMODE_OFST:
+ case S10_INTSTAT_OFST:
+ case S10_DERRADDR_OFST:
+ case S10_SERRADDR_OFST:
+ case S10_DIAGINTTEST_OFST:
+ case S10_SYSMGR_ECC_INTMASK_VAL_OFST:
+ case S10_SYSMGR_ECC_INTMASK_SET_OFST:
+ case S10_SYSMGR_ECC_INTMASK_CLR_OFST:
+ case S10_SYSMGR_ECC_INTSTAT_SERR_OFST:
+ case S10_SYSMGR_ECC_INTSTAT_DERR_OFST:
+ return true;
+ }
+ return false;
+}
+
+static const struct regmap_config s10_sdram_regmap_cfg = {
+ .name = "s10_ddr",
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0xffffffff,
+ .writeable_reg = s10_sdram_writeable_reg,
+ .readable_reg = s10_sdram_readable_reg,
+ .volatile_reg = s10_sdram_volatile_reg,
+ .reg_read = s10_protected_reg_read,
+ .reg_write = s10_protected_reg_write,
+ .use_single_rw = true,
+};
+
+static int altr_s10_sdram_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *id;
+ struct edac_mc_layer layers[2];
+ struct mem_ctl_info *mci;
+ struct altr_sdram_mc_data *drvdata;
+ const struct altr_sdram_prv_data *priv;
+ struct regmap *regmap;
+ struct dimm_info *dimm;
+ u32 read_reg;
+ int irq, ret = 0;
+ unsigned long mem_size;
+
+ id = of_match_device(altr_sdram_ctrl_of_match, &pdev->dev);
+ if (!id)
+ return -ENODEV;
+
+ /* Grab specific offsets and masks for Stratix10 */
+ priv = of_match_node(altr_sdram_ctrl_of_match,
+ pdev->dev.of_node)->data;
+
+ regmap = devm_regmap_init(&pdev->dev, NULL, (void *)priv,
+ &s10_sdram_regmap_cfg);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /* Validate the SDRAM controller has ECC enabled */
+ if (regmap_read(regmap, priv->ecc_ctrl_offset, &read_reg) ||
+ ((read_reg & priv->ecc_ctl_en_mask) != priv->ecc_ctl_en_mask)) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "No ECC/ECC disabled [0x%08X]\n", read_reg);
+ return -ENODEV;
+ }
+
+ /* Grab memory size from device tree. */
+ mem_size = get_total_mem();
+ if (!mem_size) {
+ edac_printk(KERN_ERR, EDAC_MC, "Unable to calculate memory size\n");
+ return -ENODEV;
+ }
+
+ /* Ensure the SDRAM Interrupt is disabled */
+ if (regmap_update_bits(regmap, priv->ecc_irq_en_offset,
+ priv->ecc_irq_en_mask, 0)) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Error disabling SDRAM ECC IRQ\n");
+ return -ENODEV;
+ }
+
+ /* Toggle to clear the SDRAM Error count */
+ if (regmap_update_bits(regmap, priv->ecc_cnt_rst_offset,
+ priv->ecc_cnt_rst_mask,
+ priv->ecc_cnt_rst_mask)) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Error clearing SDRAM ECC count\n");
+ return -ENODEV;
+ }
+
+ if (regmap_update_bits(regmap, priv->ecc_cnt_rst_offset,
+ priv->ecc_cnt_rst_mask, 0)) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Error clearing SDRAM ECC count\n");
+ return -ENODEV;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "No irq %d in DT\n", irq);
+ return -ENODEV;
+ }
+
+ layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+ layers[0].size = 1;
+ layers[0].is_virt_csrow = true;
+ layers[1].type = EDAC_MC_LAYER_CHANNEL;
+ layers[1].size = 1;
+ layers[1].is_virt_csrow = false;
+ mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
+ sizeof(struct altr_sdram_mc_data));
+ if (!mci)
+ return -ENOMEM;
+
+ mci->pdev = &pdev->dev;
+ drvdata = mci->pvt_info;
+ drvdata->mc_vbase = regmap;
+ drvdata->data = priv;
+ platform_set_drvdata(pdev, mci);
+
+ if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Unable to get managed device resource\n");
+ ret = -ENOMEM;
+ goto free;
+ }
+
+ mci->mtype_cap = MEM_FLAG_DDR3;
+ mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
+ mci->edac_cap = EDAC_FLAG_SECDED;
+ mci->mod_name = EDAC_MOD_STR;
+ mci->ctl_name = dev_name(&pdev->dev);
+ mci->scrub_mode = SCRUB_SW_SRC;
+ mci->dev_name = dev_name(&pdev->dev);
+
+ dimm = *mci->dimms;
+ dimm->nr_pages = ((mem_size - 1) >> PAGE_SHIFT) + 1;
+ dimm->grain = 8;
+ dimm->dtype = DEV_X8;
+ dimm->mtype = MEM_DDR3;
+ dimm->edac_mode = EDAC_SECDED;
+
+ ret = edac_mc_add_mc(mci);
+ if (ret < 0)
+ goto err;
+
+ ret = devm_request_irq(&pdev->dev, irq, altr_sdram_mc_err_handler,
+ IRQF_SHARED, dev_name(&pdev->dev), mci);
+ if (ret < 0) {
+ edac_mc_printk(mci, KERN_ERR,
+ "Unable to request irq %d\n", irq);
+ ret = -ENODEV;
+ goto err2;
+ }
+
+ if (regmap_write(regmap, S10_SYSMGR_ECC_INTMASK_CLR_OFST,
+ S10_DDR0_IRQ_MASK)) {
+ edac_printk(KERN_ERR, EDAC_MC,
+ "Error clearing SDRAM ECC count\n");
+ return -ENODEV;
+ }
+
+ if (regmap_update_bits(drvdata->mc_vbase, priv->ecc_irq_en_offset,
+ priv->ecc_irq_en_mask, priv->ecc_irq_en_mask)) {
+ edac_mc_printk(mci, KERN_ERR,
+ "Error enabling SDRAM ECC IRQ\n");
+ ret = -ENODEV;
+ goto err2;
+ }
+
+ altr_sdr_mc_create_debugfs_nodes(mci);
+
+ devres_close_group(&pdev->dev, NULL);
+
+ return 0;
+
+err2:
+ edac_mc_del_mc(&pdev->dev);
+err:
+ devres_release_group(&pdev->dev, NULL);
+free:
+ edac_mc_free(mci);
+ edac_printk(KERN_ERR, EDAC_MC,
+ "EDAC Probe Failed; Error %d\n", ret);
+
+ return ret;
+}
+
+static int altr_s10_sdram_remove(struct platform_device *pdev)
+{
+ struct mem_ctl_info *mci = platform_get_drvdata(pdev);
+
+ edac_mc_del_mc(&pdev->dev);
+ edac_mc_free(mci);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+/************** </Stratix10 EDAC Memory Controller Functions> ***********/
+
/*
* If you want to suspend, need to disable EDAC by removing it
* from the device tree or defconfig.
@@ -508,6 +803,20 @@ static struct platform_driver altr_sdram_edac_driver = {
module_platform_driver(altr_sdram_edac_driver);
+static struct platform_driver altr_s10_sdram_edac_driver = {
+ .probe = altr_s10_sdram_probe,
+ .remove = altr_s10_sdram_remove,
+ .driver = {
+ .name = "altr_s10_sdram_edac",
+#ifdef CONFIG_PM
+ .pm = &altr_sdram_pm_ops,
+#endif
+ .of_match_table = altr_sdram_ctrl_of_match,
+ },
+};
+
+module_platform_driver(altr_s10_sdram_edac_driver);
+
/************************* EDAC Parent Probe *************************/
static const struct of_device_id altr_edac_device_of_match[];
@@ -1106,7 +1415,7 @@ static void *ocram_alloc_mem(size_t size, void **other)
static void ocram_free_mem(void *p, size_t size, void *other)
{
- gen_pool_free((struct gen_pool *)other, (u32)p, size);
+ gen_pool_free((struct gen_pool *)other, (unsigned long)p, size);
}
static const struct edac_device_prv_data ocramecc_data = {
@@ -1925,6 +2234,171 @@ static struct platform_driver altr_edac_a10_driver = {
};
module_platform_driver(altr_edac_a10_driver);
+/************** Stratix 10 EDAC Device Controller Functions> ************/
+
+#define to_s10edac(p, m) container_of(p, struct altr_stratix10_edac, m)
+
+/*
+ * The double bit error is handled through SError which is fatal. This is
+ * called as a panic notifier to printout ECC error info as part of the panic.
+ */
+static int s10_edac_dberr_handler(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct altr_stratix10_edac *edac = to_s10edac(this, panic_notifier);
+ int err_addr, dberror;
+
+ s10_protected_reg_read(edac, S10_SYSMGR_ECC_INTSTAT_DERR_OFST,
+ &dberror);
+ /* Remember the UE Errors for a reboot */
+ s10_protected_reg_write(edac, S10_SYSMGR_UE_VAL_OFST, dberror);
+ if (dberror & S10_DDR0_IRQ_MASK) {
+ s10_protected_reg_read(edac, S10_DERRADDR_OFST, &err_addr);
+ /* Remember the UE Error address */
+ s10_protected_reg_write(edac, S10_SYSMGR_UE_ADDR_OFST,
+ err_addr);
+ edac_printk(KERN_ERR, EDAC_MC,
+ "EDAC: [Uncorrectable errors @ 0x%08X]\n\n",
+ err_addr);
+ }
+
+ return NOTIFY_DONE;
+}
+
+static void altr_edac_s10_irq_handler(struct irq_desc *desc)
+{
+ struct altr_stratix10_edac *edac = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ int irq = irq_desc_get_irq(desc);
+ int bit, sm_offset, irq_status;
+
+ sm_offset = S10_SYSMGR_ECC_INTSTAT_SERR_OFST;
+
+ chained_irq_enter(chip, desc);
+
+ s10_protected_reg_read(NULL, sm_offset, &irq_status);
+
+ for_each_set_bit(bit, (unsigned long *)&irq_status, 32) {
+ irq = irq_linear_revmap(edac->domain, bit);
+ if (irq)
+ generic_handle_irq(irq);
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static void s10_eccmgr_irq_mask(struct irq_data *d)
+{
+ struct altr_stratix10_edac *edac = irq_data_get_irq_chip_data(d);
+
+ s10_protected_reg_write(edac, S10_SYSMGR_ECC_INTMASK_SET_OFST,
+ BIT(d->hwirq));
+}
+
+static void s10_eccmgr_irq_unmask(struct irq_data *d)
+{
+ struct altr_stratix10_edac *edac = irq_data_get_irq_chip_data(d);
+
+ s10_protected_reg_write(edac, S10_SYSMGR_ECC_INTMASK_CLR_OFST,
+ BIT(d->hwirq));
+}
+
+static int s10_eccmgr_irqdomain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct altr_stratix10_edac *edac = d->host_data;
+
+ irq_set_chip_and_handler(irq, &edac->irq_chip, handle_simple_irq);
+ irq_set_chip_data(irq, edac);
+ irq_set_noprobe(irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops s10_eccmgr_ic_ops = {
+ .map = s10_eccmgr_irqdomain_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int altr_edac_s10_probe(struct platform_device *pdev)
+{
+ struct altr_stratix10_edac *edac;
+ struct device_node *child;
+ int dberror, err_addr;
+
+ edac = devm_kzalloc(&pdev->dev, sizeof(*edac), GFP_KERNEL);
+ if (!edac)
+ return -ENOMEM;
+
+ edac->dev = &pdev->dev;
+ platform_set_drvdata(pdev, edac);
+ INIT_LIST_HEAD(&edac->s10_ecc_devices);
+
+ edac->irq_chip.name = pdev->dev.of_node->name;
+ edac->irq_chip.irq_mask = s10_eccmgr_irq_mask;
+ edac->irq_chip.irq_unmask = s10_eccmgr_irq_unmask;
+ edac->domain = irq_domain_add_linear(pdev->dev.of_node, 64,
+ &s10_eccmgr_ic_ops, edac);
+ if (!edac->domain) {
+ dev_err(&pdev->dev, "Error adding IRQ domain\n");
+ return -ENOMEM;
+ }
+
+ edac->sb_irq = platform_get_irq(pdev, 0);
+ if (edac->sb_irq < 0) {
+ dev_err(&pdev->dev, "No SBERR IRQ resource\n");
+ return edac->sb_irq;
+ }
+
+ irq_set_chained_handler_and_data(edac->sb_irq,
+ altr_edac_s10_irq_handler,
+ edac);
+
+ edac->panic_notifier.notifier_call = s10_edac_dberr_handler;
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &edac->panic_notifier);
+
+ /* Printout a message if uncorrectable error previously. */
+ s10_protected_reg_read(edac, S10_SYSMGR_UE_VAL_OFST, &dberror);
+ if (dberror) {
+ s10_protected_reg_read(edac, S10_SYSMGR_UE_ADDR_OFST,
+ &err_addr);
+ edac_printk(KERN_ERR, EDAC_DEVICE,
+ "Previous Boot UE detected[0x%X] @ 0x%X\n",
+ dberror, err_addr);
+ /* Reset the sticky registers */
+ s10_protected_reg_write(edac, S10_SYSMGR_UE_VAL_OFST, 0);
+ s10_protected_reg_write(edac, S10_SYSMGR_UE_ADDR_OFST, 0);
+ }
+
+ for_each_child_of_node(pdev->dev.of_node, child) {
+ if (!of_device_is_available(child))
+ continue;
+
+ if (of_device_is_compatible(child, "altr,sdram-edac-s10"))
+ of_platform_populate(pdev->dev.of_node,
+ altr_sdram_ctrl_of_match,
+ NULL, &pdev->dev);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id altr_edac_s10_of_match[] = {
+ { .compatible = "altr,socfpga-s10-ecc-manager" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, altr_edac_s10_of_match);
+
+static struct platform_driver altr_edac_s10_driver = {
+ .probe = altr_edac_s10_probe,
+ .driver = {
+ .name = "socfpga_s10_ecc_manager",
+ .of_match_table = altr_edac_s10_of_match,
+ },
+};
+module_platform_driver(altr_edac_s10_driver);
+
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Thor Thayer");
MODULE_DESCRIPTION("EDAC Driver for Altera Memories");
diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h
index cbc96290f7439..81f0554e09de8 100644
--- a/drivers/edac/altera_edac.h
+++ b/drivers/edac/altera_edac.h
@@ -1,23 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
- *
+ * Copyright (C) 2017-2018, Intel Corporation
* Copyright (C) 2015 Altera Corporation
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _ALTERA_EDAC_H
#define _ALTERA_EDAC_H
+#include <linux/arm-smccc.h>
#include <linux/edac.h>
#include <linux/types.h>
@@ -94,6 +84,7 @@
/* SDRAM Controller Address Width Register */
#define CV_DRAMADDRW 0xFFC2502C
#define A10_DRAMADDRW 0xFFCFA0A8
+#define S10_DRAMADDRW 0xF80110E0
/* SDRAM Controller Address Widths Field Register */
#define DRAMADDRW_COLBIT_MASK 0x001F
@@ -115,6 +106,7 @@
/* SDRAM Controller Interface Data Width Register */
#define CV_DRAMIFWIDTH 0xFFC25030
#define A10_DRAMIFWIDTH 0xFFCFB008
+#define S10_DRAMIFWIDTH 0xF8011008
/* SDRAM Controller Interface Data Width Defines */
#define CV_DRAMIFWIDTH_16B_ECC 24
@@ -164,6 +156,34 @@
#define A10_INTMASK_CLR_OFST 0x10
#define A10_DDR0_IRQ_MASK BIT(17)
+/************* Stratix10 Defines **************/
+
+/* SDRAM Controller EccCtrl Register */
+#define S10_ECCCTRL1_OFST 0xF8011100
+
+/* SDRAM Controller DRAM IRQ Register */
+#define S10_ERRINTEN_OFST 0xF8011110
+
+/* SDRAM Interrupt Mode Register */
+#define S10_INTMODE_OFST 0xF801111C
+
+/* SDRAM Controller Error Status Register */
+#define S10_INTSTAT_OFST 0xF8011120
+
+/* SDRAM Controller ECC Error Address Register */
+#define S10_DERRADDR_OFST 0xF801112C
+#define S10_SERRADDR_OFST 0xF8011130
+
+/* SDRAM Controller ECC Diagnostic Register */
+#define S10_DIAGINTTEST_OFST 0xF8011124
+
+/* SDRAM Single Bit Error Count Compare Set Register */
+#define S10_SERRCNTREG_OFST 0xF801113C
+
+/* Sticky registers for Uncorrected Errors */
+#define S10_SYSMGR_UE_VAL_OFST 0xFFD12220
+#define S10_SYSMGR_UE_ADDR_OFST 0xFFD12224
+
struct altr_sdram_prv_data {
int ecc_ctrl_offset;
int ecc_ctl_en_mask;
@@ -296,6 +316,18 @@ struct altr_sdram_mc_data {
/* A10 ECC Controller memory initialization timeout */
#define ALTR_A10_ECC_INIT_WATCHDOG_10US 10000
+/************* Stratix10 Defines **************/
+
+/* Stratix10 ECC Manager Defines */
+#define S10_SYSMGR_ECC_INTMASK_VAL_OFST 0xFFD12090
+#define S10_SYSMGR_ECC_INTMASK_SET_OFST 0xFFD12094
+#define S10_SYSMGR_ECC_INTMASK_CLR_OFST 0xFFD12098
+
+#define S10_SYSMGR_ECC_INTSTAT_SERR_OFST 0xFFD1209C
+#define S10_SYSMGR_ECC_INTSTAT_DERR_OFST 0xFFD120A0
+
+#define S10_DDR0_IRQ_MASK BIT(16)
+
struct altr_edac_device_dev;
struct edac_device_prv_data {
@@ -340,4 +372,78 @@ struct altr_arria10_edac {
struct list_head a10_ecc_devices;
};
+/*
+ * Functions specified by ARM SMC Calling convention:
+ *
+ * FAST call executes atomic operations, returns when the requested operation
+ * has completed.
+ * STD call starts a operation which can be preempted by a non-secure
+ * interrupt. The call can return before the requested operation has
+ * completed.
+ *
+ * a0..a7 is used as register names in the descriptions below, on arm32
+ * that translates to r0..r7 and on arm64 to w0..w7.
+ */
+
+#define INTEL_SIP_SMC_STD_CALL_VAL(func_num) \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, ARM_SMCCC_SMC_64, \
+ ARM_SMCCC_OWNER_SIP, (func_num))
+
+#define INTEL_SIP_SMC_FAST_CALL_VAL(func_num) \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, \
+ ARM_SMCCC_OWNER_SIP, (func_num))
+
+#define INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION 0xFFFFFFFF
+#define INTEL_SIP_SMC_STATUS_OK 0x0
+#define INTEL_SIP_SMC_REG_ERROR 0x5
+
+/*
+ * Request INTEL_SIP_SMC_REG_READ
+ *
+ * Read a protected register using SMCCC
+ *
+ * Call register usage:
+ * a0: INTEL_SIP_SMC_REG_READ.
+ * a1: register address.
+ * a2-7: not used.
+ *
+ * Return status:
+ * a0: INTEL_SIP_SMC_STATUS_OK, INTEL_SIP_SMC_REG_ERROR, or
+ * INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION
+ * a1: Value in the register
+ * a2-3: not used.
+ */
+#define INTEL_SIP_SMC_FUNCID_REG_READ 7
+#define INTEL_SIP_SMC_REG_READ \
+ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_REG_READ)
+
+/*
+ * Request INTEL_SIP_SMC_REG_WRITE
+ *
+ * Write a protected register using SMCCC
+ *
+ * Call register usage:
+ * a0: INTEL_SIP_SMC_REG_WRITE.
+ * a1: register address
+ * a2: value to program into register.
+ * a3-7: not used.
+ *
+ * Return status:
+ * a0: INTEL_SIP_SMC_STATUS_OK, INTEL_SIP_SMC_REG_ERROR, or
+ * INTEL_SIP_SMC_RETURN_UNKNOWN_FUNCTION
+ * a1-3: not used.
+ */
+#define INTEL_SIP_SMC_FUNCID_REG_WRITE 8
+#define INTEL_SIP_SMC_REG_WRITE \
+ INTEL_SIP_SMC_FAST_CALL_VAL(INTEL_SIP_SMC_FUNCID_REG_WRITE)
+
+struct altr_stratix10_edac {
+ struct device *dev;
+ int sb_irq;
+ struct irq_domain *domain;
+ struct irq_chip irq_chip;
+ struct list_head s10_ecc_devices;
+ struct notifier_block panic_notifier;
+};
+
#endif /* #ifndef _ALTERA_EDAC_H */
diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c
index 68b6ee18bea64..473aeec4b1da4 100644
--- a/drivers/edac/ghes_edac.c
+++ b/drivers/edac/ghes_edac.c
@@ -91,6 +91,7 @@ static void ghes_edac_dmidecode(const struct dmi_header *dh, void *arg)
struct dimm_info *dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms,
mci->n_layers,
dimm_fill->count, 0, 0);
+ u16 rdr_mask = BIT(7) | BIT(13);
if (entry->size == 0xffff) {
pr_info("Can't get DIMM%i size\n",
@@ -99,22 +100,21 @@ static void ghes_edac_dmidecode(const struct dmi_header *dh, void *arg)
} else if (entry->size == 0x7fff) {
dimm->nr_pages = MiB_TO_PAGES(entry->extended_size);
} else {
- if (entry->size & 1 << 15)
- dimm->nr_pages = MiB_TO_PAGES((entry->size &
- 0x7fff) << 10);
+ if (entry->size & BIT(15))
+ dimm->nr_pages = MiB_TO_PAGES((entry->size & 0x7fff) << 10);
else
dimm->nr_pages = MiB_TO_PAGES(entry->size);
}
switch (entry->memory_type) {
case 0x12:
- if (entry->type_detail & 1 << 13)
+ if (entry->type_detail & BIT(13))
dimm->mtype = MEM_RDDR;
else
dimm->mtype = MEM_DDR;
break;
case 0x13:
- if (entry->type_detail & 1 << 13)
+ if (entry->type_detail & BIT(13))
dimm->mtype = MEM_RDDR2;
else
dimm->mtype = MEM_DDR2;
@@ -123,20 +123,29 @@ static void ghes_edac_dmidecode(const struct dmi_header *dh, void *arg)
dimm->mtype = MEM_FB_DDR2;
break;
case 0x18:
- if (entry->type_detail & 1 << 13)
+ if (entry->type_detail & BIT(12))
+ dimm->mtype = MEM_NVDIMM;
+ else if (entry->type_detail & BIT(13))
dimm->mtype = MEM_RDDR3;
else
dimm->mtype = MEM_DDR3;
break;
+ case 0x1a:
+ if (entry->type_detail & BIT(12))
+ dimm->mtype = MEM_NVDIMM;
+ else if (entry->type_detail & BIT(13))
+ dimm->mtype = MEM_RDDR4;
+ else
+ dimm->mtype = MEM_DDR4;
+ break;
default:
- if (entry->type_detail & 1 << 6)
+ if (entry->type_detail & BIT(6))
dimm->mtype = MEM_RMBS;
- else if ((entry->type_detail & ((1 << 7) | (1 << 13)))
- == ((1 << 7) | (1 << 13)))
+ else if ((entry->type_detail & rdr_mask) == rdr_mask)
dimm->mtype = MEM_RDR;
- else if (entry->type_detail & 1 << 7)
+ else if (entry->type_detail & BIT(7))
dimm->mtype = MEM_SDR;
- else if (entry->type_detail & 1 << 9)
+ else if (entry->type_detail & BIT(9))
dimm->mtype = MEM_EDO;
else
dimm->mtype = MEM_UNKNOWN;
@@ -172,8 +181,7 @@ static void ghes_edac_dmidecode(const struct dmi_header *dh, void *arg)
}
}
-void ghes_edac_report_mem_error(struct ghes *ghes, int sev,
- struct cper_sec_mem_err *mem_err)
+void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err)
{
enum hw_event_mc_err_type type;
struct edac_raw_error_desc *e;
@@ -183,10 +191,8 @@ void ghes_edac_report_mem_error(struct ghes *ghes, int sev,
char *p;
u8 grain_bits;
- if (!pvt) {
- pr_err("Internal error: Can't find EDAC structure\n");
+ if (!pvt)
return;
- }
/*
* We can do the locking below because GHES defers error processing
@@ -434,12 +440,16 @@ int ghes_edac_register(struct ghes *ghes, struct device *dev)
struct mem_ctl_info *mci;
struct edac_mc_layer layers[1];
struct ghes_edac_dimm_fill dimm_fill;
- int idx;
+ int idx = -1;
- /* Check if safe to enable on this system */
- idx = acpi_match_platform_list(plat_list);
- if (!force_load && idx < 0)
- return 0;
+ if (IS_ENABLED(CONFIG_X86)) {
+ /* Check if safe to enable on this system */
+ idx = acpi_match_platform_list(plat_list);
+ if (!force_load && idx < 0)
+ return -ENODEV;
+ } else {
+ idx = 0;
+ }
/*
* We have only one logical memory controller to which all DIMMs belong.
@@ -519,6 +529,9 @@ void ghes_edac_unregister(struct ghes *ghes)
{
struct mem_ctl_info *mci;
+ if (!ghes_pvt)
+ return;
+
mci = ghes_pvt->mci;
edac_mc_del_mc(mci->pdev);
edac_mc_free(mci);
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index 8c5540160a23b..4d0ea3563d47d 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -1743,7 +1743,7 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci,
err = "write parity error";
break;
case 19:
- err = "redundacy loss";
+ err = "redundancy loss";
break;
case 20:
err = "reserved";