Merge tag 'edac_for_4.20' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp
[linux-2.6-block.git] / drivers / edac / altera_edac.c
index ab7c5a937ab00b19c6557b063d7c3c106e4a05cb..c89d82aa277601d67ae673097eae2c9a8d6f997a 100644 (file)
@@ -69,25 +69,6 @@ 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.       */
@@ -239,7 +220,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},
+       { .compatible = "altr,sdram-edac-s10", .data = &a10_data},
        {},
 };
 MODULE_DEVICE_TABLE(of, altr_sdram_ctrl_of_match);
@@ -293,6 +274,7 @@ release:
        return ret;
 }
 
+static int socfpga_is_a10(void);
 static int altr_sdram_probe(struct platform_device *pdev)
 {
        const struct of_device_id *id;
@@ -416,7 +398,7 @@ static int altr_sdram_probe(struct platform_device *pdev)
                goto err;
 
        /* Only the Arria10 has separate IRQs */
-       if (irq2 > 0) {
+       if (socfpga_is_a10()) {
                /* Arria10 specific initialization */
                res = a10_init(mc_vbase);
                if (res < 0)
@@ -502,8 +484,9 @@ static int s10_protected_reg_write(void *context, unsigned int reg,
                                   unsigned int val)
 {
        struct arm_smccc_res result;
+       unsigned long offset = (unsigned long)context;
 
-       arm_smccc_smc(INTEL_SIP_SMC_REG_WRITE, reg, val, 0, 0,
+       arm_smccc_smc(INTEL_SIP_SMC_REG_WRITE, offset + reg, val, 0, 0,
                      0, 0, 0, &result);
 
        return (int)result.a0;
@@ -523,8 +506,9 @@ static int s10_protected_reg_read(void *context, unsigned int reg,
                                  unsigned int *val)
 {
        struct arm_smccc_res result;
+       unsigned long offset = (unsigned long)context;
 
-       arm_smccc_smc(INTEL_SIP_SMC_REG_READ, reg, 0, 0, 0,
+       arm_smccc_smc(INTEL_SIP_SMC_REG_READ, offset + reg, 0, 0, 0,
                      0, 0, 0, &result);
 
        *val = (unsigned int)result.a1;
@@ -532,246 +516,18 @@ static int s10_protected_reg_read(void *context, unsigned int reg,
        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,
+       .max_register = 0xffd12228,
        .reg_read = s10_protected_reg_read,
        .reg_write = s10_protected_reg_write,
        .use_single_read = true,
        .use_single_write = 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");
-               ret = -ENODEV;
-               goto err2;
-       }
-
-       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> ***********/
 
 /*
@@ -805,20 +561,6 @@ 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[];
@@ -972,6 +714,16 @@ static const struct file_operations altr_edac_a10_device_inject_fops = {
        .llseek = generic_file_llseek,
 };
 
+static ssize_t altr_edac_a10_device_trig2(struct file *file,
+                                         const char __user *user_buf,
+                                         size_t count, loff_t *ppos);
+
+static const struct file_operations altr_edac_a10_device_inject2_fops = {
+       .open = simple_open,
+       .write = altr_edac_a10_device_trig2,
+       .llseek = generic_file_llseek,
+};
+
 static void altr_create_edacdev_dbgfs(struct edac_device_ctl_info *edac_dci,
                                      const struct edac_device_prv_data *priv)
 {
@@ -1253,6 +1005,16 @@ static int __maybe_unused altr_init_memory_port(void __iomem *ioaddr, int port)
        return ret;
 }
 
+static int socfpga_is_a10(void)
+{
+       return of_machine_is_compatible("altr,socfpga-arria10");
+}
+
+static int socfpga_is_s10(void)
+{
+       return of_machine_is_compatible("altr,socfpga-stratix10");
+}
+
 static __init int __maybe_unused
 altr_init_a10_ecc_block(struct device_node *np, u32 irq_mask,
                        u32 ecc_ctrl_en_mask, bool dual_port)
@@ -1267,8 +1029,32 @@ altr_init_a10_ecc_block(struct device_node *np, u32 irq_mask,
 
        /* Get the ECC Manager - parent of the device EDACs */
        np_eccmgr = of_get_parent(np);
-       ecc_mgr_map = syscon_regmap_lookup_by_phandle(np_eccmgr,
-                                                     "altr,sysmgr-syscon");
+
+       if (socfpga_is_a10()) {
+               ecc_mgr_map = syscon_regmap_lookup_by_phandle(np_eccmgr,
+                                                             "altr,sysmgr-syscon");
+       } else {
+               struct device_node *sysmgr_np;
+               struct resource res;
+               uintptr_t base;
+
+               sysmgr_np = of_parse_phandle(np_eccmgr,
+                                            "altr,sysmgr-syscon", 0);
+               if (!sysmgr_np) {
+                       edac_printk(KERN_ERR, EDAC_DEVICE,
+                                   "Unable to find altr,sysmgr-syscon\n");
+                       return -ENODEV;
+               }
+
+               if (of_address_to_resource(sysmgr_np, 0, &res))
+                       return -ENOMEM;
+
+               /* Need physical address for SMCC call */
+               base = res.start;
+
+               ecc_mgr_map = regmap_init(NULL, NULL, (void *)base,
+                                         &s10_sdram_regmap_cfg);
+       }
        of_node_put(np_eccmgr);
        if (IS_ERR(ecc_mgr_map)) {
                edac_printk(KERN_ERR, EDAC_DEVICE,
@@ -1326,11 +1112,6 @@ out:
        return ret;
 }
 
-static int socfpga_is_a10(void)
-{
-       return of_machine_is_compatible("altr,socfpga-arria10");
-}
-
 static int validate_parent_available(struct device_node *np);
 static const struct of_device_id altr_edac_a10_device_of_match[];
 static int __init __maybe_unused altr_init_a10_ecc_device_type(char *compat)
@@ -1338,7 +1119,7 @@ static int __init __maybe_unused altr_init_a10_ecc_device_type(char *compat)
        int irq;
        struct device_node *child, *np;
 
-       if (!socfpga_is_a10())
+       if (!socfpga_is_a10() && !socfpga_is_s10())
                return -ENODEV;
 
        np = of_find_compatible_node(NULL, NULL,
@@ -1584,7 +1365,7 @@ static const struct edac_device_prv_data a10_enetecc_data = {
        .ue_set_mask = ALTR_A10_ECC_TDERRA,
        .set_err_ofst = ALTR_A10_ECC_INTTEST_OFST,
        .ecc_irq_handler = altr_edac_a10_ecc_irq,
-       .inject_fops = &altr_edac_a10_device_inject_fops,
+       .inject_fops = &altr_edac_a10_device_inject2_fops,
 };
 
 static int __init socfpga_init_ethernet_ecc(void)
@@ -1662,7 +1443,7 @@ static const struct edac_device_prv_data a10_usbecc_data = {
        .ue_set_mask = ALTR_A10_ECC_TDERRA,
        .set_err_ofst = ALTR_A10_ECC_INTTEST_OFST,
        .ecc_irq_handler = altr_edac_a10_ecc_irq,
-       .inject_fops = &altr_edac_a10_device_inject_fops,
+       .inject_fops = &altr_edac_a10_device_inject2_fops,
 };
 
 static int __init socfpga_init_usb_ecc(void)
@@ -1860,7 +1641,7 @@ static int __init socfpga_init_sdmmc_ecc(void)
        int rc = -ENODEV;
        struct device_node *child;
 
-       if (!socfpga_is_a10())
+       if (!socfpga_is_a10() && !socfpga_is_s10())
                return -ENODEV;
 
        child = of_find_compatible_node(NULL, NULL, "altr,socfpga-sdmmc-ecc");
@@ -1944,6 +1725,74 @@ static ssize_t altr_edac_a10_device_trig(struct file *file,
                writel(priv->ue_set_mask, set_addr);
        else
                writel(priv->ce_set_mask, set_addr);
+
+       /* Ensure the interrupt test bits are set */
+       wmb();
+       local_irq_restore(flags);
+
+       return count;
+}
+
+/*
+ * The Stratix10 EDAC Error Injection Functions differ from Arria10
+ * slightly. A few Arria10 peripherals can use this injection function.
+ * Inject the error into the memory and then readback to trigger the IRQ.
+ */
+static ssize_t altr_edac_a10_device_trig2(struct file *file,
+                                         const char __user *user_buf,
+                                         size_t count, loff_t *ppos)
+{
+       struct edac_device_ctl_info *edac_dci = file->private_data;
+       struct altr_edac_device_dev *drvdata = edac_dci->pvt_info;
+       const struct edac_device_prv_data *priv = drvdata->data;
+       void __iomem *set_addr = (drvdata->base + priv->set_err_ofst);
+       unsigned long flags;
+       u8 trig_type;
+
+       if (!user_buf || get_user(trig_type, user_buf))
+               return -EFAULT;
+
+       local_irq_save(flags);
+       if (trig_type == ALTR_UE_TRIGGER_CHAR) {
+               writel(priv->ue_set_mask, set_addr);
+       } else {
+               /* Setup write of 0 to first 4 bytes */
+               writel(0x0, drvdata->base + ECC_BLK_WDATA0_OFST);
+               writel(0x0, drvdata->base + ECC_BLK_WDATA1_OFST);
+               writel(0x0, drvdata->base + ECC_BLK_WDATA2_OFST);
+               writel(0x0, drvdata->base + ECC_BLK_WDATA3_OFST);
+               /* Setup write of 4 bytes */
+               writel(ECC_WORD_WRITE, drvdata->base + ECC_BLK_DBYTECTRL_OFST);
+               /* Setup Address to 0 */
+               writel(0x0, drvdata->base + ECC_BLK_ADDRESS_OFST);
+               /* Setup accctrl to write & data override */
+               writel(ECC_WRITE_DOVR, drvdata->base + ECC_BLK_ACCCTRL_OFST);
+               /* Kick it. */
+               writel(ECC_XACT_KICK, drvdata->base + ECC_BLK_STARTACC_OFST);
+               /* Setup accctrl to read & ecc override */
+               writel(ECC_READ_EOVR, drvdata->base + ECC_BLK_ACCCTRL_OFST);
+               /* Kick it. */
+               writel(ECC_XACT_KICK, drvdata->base + ECC_BLK_STARTACC_OFST);
+               /* Setup write for single bit change */
+               writel(0x1, drvdata->base + ECC_BLK_WDATA0_OFST);
+               writel(0x0, drvdata->base + ECC_BLK_WDATA1_OFST);
+               writel(0x0, drvdata->base + ECC_BLK_WDATA2_OFST);
+               writel(0x0, drvdata->base + ECC_BLK_WDATA3_OFST);
+               /* Copy Read ECC to Write ECC */
+               writel(readl(drvdata->base + ECC_BLK_RECC0_OFST),
+                      drvdata->base + ECC_BLK_WECC0_OFST);
+               writel(readl(drvdata->base + ECC_BLK_RECC1_OFST),
+                      drvdata->base + ECC_BLK_WECC1_OFST);
+               /* Setup accctrl to write & ecc override & data override */
+               writel(ECC_WRITE_EDOVR, drvdata->base + ECC_BLK_ACCCTRL_OFST);
+               /* Kick it. */
+               writel(ECC_XACT_KICK, drvdata->base + ECC_BLK_STARTACC_OFST);
+               /* Setup accctrl to read & ecc overwrite & data overwrite */
+               writel(ECC_READ_EDOVR, drvdata->base + ECC_BLK_ACCCTRL_OFST);
+               /* Kick it. */
+               writel(ECC_XACT_KICK, drvdata->base + ECC_BLK_STARTACC_OFST);
+       }
+
        /* Ensure the interrupt test bits are set */
        wmb();
        local_irq_restore(flags);
@@ -2147,6 +1996,35 @@ static const struct irq_domain_ops a10_eccmgr_ic_ops = {
        .xlate = irq_domain_xlate_twocell,
 };
 
+/************** Stratix 10 EDAC Double Bit Error Handler ************/
+#define to_a10edac(p, m) container_of(p, struct altr_arria10_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_arria10_edac *edac = to_a10edac(this, panic_notifier);
+       int err_addr, dberror;
+
+       regmap_read(edac->ecc_mgr_map, S10_SYSMGR_ECC_INTSTAT_DERR_OFST,
+                   &dberror);
+       regmap_write(edac->ecc_mgr_map, S10_SYSMGR_UE_VAL_OFST, dberror);
+       if (dberror & S10_DDR0_IRQ_MASK) {
+               regmap_read(edac->ecc_mgr_map, A10_DERRADDR_OFST, &err_addr);
+               regmap_write(edac->ecc_mgr_map, 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;
+}
+
+/****************** Arria 10 EDAC Probe Function *********************/
 static int altr_edac_a10_probe(struct platform_device *pdev)
 {
        struct altr_arria10_edac *edac;
@@ -2160,8 +2038,34 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, edac);
        INIT_LIST_HEAD(&edac->a10_ecc_devices);
 
-       edac->ecc_mgr_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+       if (socfpga_is_a10()) {
+               edac->ecc_mgr_map =
+                       syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
                                                        "altr,sysmgr-syscon");
+       } else {
+               struct device_node *sysmgr_np;
+               struct resource res;
+               uintptr_t base;
+
+               sysmgr_np = of_parse_phandle(pdev->dev.of_node,
+                                            "altr,sysmgr-syscon", 0);
+               if (!sysmgr_np) {
+                       edac_printk(KERN_ERR, EDAC_DEVICE,
+                                   "Unable to find altr,sysmgr-syscon\n");
+                       return -ENODEV;
+               }
+
+               if (of_address_to_resource(sysmgr_np, 0, &res))
+                       return -ENOMEM;
+
+               /* Need physical address for SMCC call */
+               base = res.start;
+
+               edac->ecc_mgr_map = devm_regmap_init(&pdev->dev, NULL,
+                                                    (void *)base,
+                                                    &s10_sdram_regmap_cfg);
+       }
+
        if (IS_ERR(edac->ecc_mgr_map)) {
                edac_printk(KERN_ERR, EDAC_DEVICE,
                            "Unable to get syscon altr,sysmgr-syscon\n");
@@ -2188,14 +2092,38 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
                                         altr_edac_a10_irq_handler,
                                         edac);
 
-       edac->db_irq = platform_get_irq(pdev, 1);
-       if (edac->db_irq < 0) {
-               dev_err(&pdev->dev, "No DBERR IRQ resource\n");
-               return edac->db_irq;
+       if (socfpga_is_a10()) {
+               edac->db_irq = platform_get_irq(pdev, 1);
+               if (edac->db_irq < 0) {
+                       dev_err(&pdev->dev, "No DBERR IRQ resource\n");
+                       return edac->db_irq;
+               }
+               irq_set_chained_handler_and_data(edac->db_irq,
+                                                altr_edac_a10_irq_handler,
+                                                edac);
+       } else {
+               int dberror, err_addr;
+
+               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. */
+               regmap_read(edac->ecc_mgr_map, S10_SYSMGR_UE_VAL_OFST,
+                           &dberror);
+               if (dberror) {
+                       regmap_read(edac->ecc_mgr_map, 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 */
+                       regmap_write(edac->ecc_mgr_map,
+                                    S10_SYSMGR_UE_VAL_OFST, 0);
+                       regmap_write(edac->ecc_mgr_map,
+                                    S10_SYSMGR_UE_ADDR_OFST, 0);
+               }
        }
-       irq_set_chained_handler_and_data(edac->db_irq,
-                                        altr_edac_a10_irq_handler,
-                                        edac);
 
        for_each_child_of_node(pdev->dev.of_node, child) {
                if (!of_device_is_available(child))
@@ -2212,7 +2140,8 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
 
                        altr_edac_a10_device_add(edac, child);
 
-               else if (of_device_is_compatible(child, "altr,sdram-edac-a10"))
+               else if ((of_device_is_compatible(child, "altr,sdram-edac-a10")) ||
+                        (of_device_is_compatible(child, "altr,sdram-edac-s10")))
                        of_platform_populate(pdev->dev.of_node,
                                             altr_sdram_ctrl_of_match,
                                             NULL, &pdev->dev);
@@ -2223,6 +2152,7 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
 
 static const struct of_device_id altr_edac_a10_of_match[] = {
        { .compatible = "altr,socfpga-a10-ecc-manager" },
+       { .compatible = "altr,socfpga-s10-ecc-manager" },
        {},
 };
 MODULE_DEVICE_TABLE(of, altr_edac_a10_of_match);
@@ -2236,171 +2166,6 @@ 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");