EDAC, altera: Add Arria10 OCRAM ECC support
authorThor Thayer <tthayer@opensource.altera.com>
Thu, 7 Apr 2016 01:22:54 +0000 (20:22 -0500)
committerBorislav Petkov <bp@suse.de>
Thu, 7 Apr 2016 10:42:56 +0000 (12:42 +0200)
Add Arria10 On-Chip RAM ECC handling.

Signed-off-by: Thor Thayer <tthayer@opensource.altera.com>
Cc: devicetree@vger.kernel.org
Cc: dinguyen@opensource.altera.com
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux@arm.linux.org.uk
Cc: linux-edac <linux-edac@vger.kernel.org>
Link: http://lkml.kernel.org/r/1459992174-8015-1-git-send-email-tthayer@opensource.altera.com
Signed-off-by: Borislav Petkov <bp@suse.de>
drivers/edac/altera_edac.c
drivers/edac/altera_edac.h

index f7ffc77998a4521d57e97396bf79f68956c09d4d..11775dc0b139e26905a7c3b20271f5466ad0ff5b 100644 (file)
@@ -550,6 +550,7 @@ module_platform_driver(altr_edac_driver);
 
 const struct edac_device_prv_data ocramecc_data;
 const struct edac_device_prv_data l2ecc_data;
+const struct edac_device_prv_data a10_ocramecc_data;
 const struct edac_device_prv_data a10_l2ecc_data;
 
 static irqreturn_t altr_edac_device_handler(int irq, void *dev_id)
@@ -674,6 +675,16 @@ static const struct file_operations altr_edac_device_inject_fops = {
        .llseek = generic_file_llseek,
 };
 
+static ssize_t altr_edac_a10_device_trig(struct file *file,
+                                        const char __user *user_buf,
+                                        size_t count, loff_t *ppos);
+
+static const struct file_operations altr_edac_a10_device_inject_fops = {
+       .open = simple_open,
+       .write = altr_edac_a10_device_trig,
+       .llseek = generic_file_llseek,
+};
+
 static void altr_create_edacdev_dbgfs(struct edac_device_ctl_info *edac_dci,
                                      const struct edac_device_prv_data *priv)
 {
@@ -701,6 +712,8 @@ static const struct of_device_id altr_edac_device_of_match[] = {
 #ifdef CONFIG_EDAC_ALTERA_OCRAM
        { .compatible = "altr,socfpga-ocram-ecc",
          .data = (void *)&ocramecc_data },
+       { .compatible = "altr,socfpga-a10-ocram-ecc",
+         .data = (void *)&a10_ocramecc_data },
 #endif
        {},
 };
@@ -889,6 +902,24 @@ const struct edac_device_prv_data ocramecc_data = {
        .inject_fops = &altr_edac_device_inject_fops,
 };
 
+static irqreturn_t altr_edac_a10_ecc_irq(struct altr_edac_device_dev *dci,
+                                        bool sberr);
+
+const struct edac_device_prv_data a10_ocramecc_data = {
+       .setup = altr_check_ecc_deps,
+       .ce_clear_mask = ALTR_A10_ECC_SERRPENA,
+       .ue_clear_mask = ALTR_A10_ECC_DERRPENA,
+       .irq_status_mask = A10_SYSMGR_ECC_INTSTAT_OCRAM,
+       .dbgfs_name = "altr_ocram_trigger",
+       .ecc_enable_mask = ALTR_A10_OCRAM_ECC_EN_CTL,
+       .ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST,
+       .ce_set_mask = ALTR_A10_ECC_TSERRA,
+       .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,
+};
+
 #endif /* CONFIG_EDAC_ALTERA_OCRAM */
 
 /********************* L2 Cache EDAC Device Functions ********************/
@@ -1007,6 +1038,50 @@ const struct edac_device_prv_data a10_l2ecc_data = {
  * Based on xgene_edac.c peripheral code.
  */
 
+static ssize_t altr_edac_a10_device_trig(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
+               writel(priv->ce_set_mask, set_addr);
+       /* Ensure the interrupt test bits are set */
+       wmb();
+       local_irq_restore(flags);
+
+       return count;
+}
+
+static irqreturn_t altr_edac_a10_ecc_irq(struct altr_edac_device_dev *dci,
+                                        bool sberr)
+{
+       void __iomem  *base = dci->base;
+
+       if (sberr) {
+               writel(ALTR_A10_ECC_SERRPENA,
+                      base + ALTR_A10_ECC_INTSTAT_OFST);
+               edac_device_handle_ce(dci->edac_dev, 0, 0, dci->edac_dev_name);
+       } else {
+               writel(ALTR_A10_ECC_DERRPENA,
+                      base + ALTR_A10_ECC_INTSTAT_OFST);
+               edac_device_handle_ue(dci->edac_dev, 0, 0, dci->edac_dev_name);
+               panic("\nEDAC:ECC_DEVICE[Uncorrectable errors]\n");
+       }
+       return IRQ_HANDLED;
+}
+
 static irqreturn_t altr_edac_a10_irq_handler(int irq, void *dev_id)
 {
        irqreturn_t rc = IRQ_NONE;
@@ -1171,6 +1246,9 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
                        continue;
                if (of_device_is_compatible(child, "altr,socfpga-a10-l2-ecc"))
                        altr_edac_a10_device_add(edac, child);
+               else if (of_device_is_compatible(child,
+                                                "altr,socfpga-a10-ocram-ecc"))
+                       altr_edac_a10_device_add(edac, child);
        }
 
        return 0;
index cb6b2b9079e8b496624939ae2cc7007f1786dacb..42090f36ba6eabddb8e2b8a94c3758136764d38a 100644 (file)
@@ -220,9 +220,41 @@ struct altr_sdram_mc_data {
 #define ALTR_L2_ECC_INJD                BIT(2)
 
 /* Arria10 General ECC Block Module Defines */
+#define ALTR_A10_ECC_CTRL_OFST          0x08
+#define ALTR_A10_ECC_EN                 BIT(0)
+#define ALTR_A10_ECC_INITA              BIT(16)
+#define ALTR_A10_ECC_INITB              BIT(24)
+
+#define ALTR_A10_ECC_INITSTAT_OFST      0x0C
+#define ALTR_A10_ECC_INITCOMPLETEA      BIT(0)
+#define ALTR_A10_ECC_INITCOMPLETEB      BIT(8)
+
+#define ALTR_A10_ECC_ERRINTEN_OFST      0x10
+#define ALTR_A10_ECC_SERRINTEN          BIT(0)
+
+#define ALTR_A10_ECC_INTSTAT_OFST       0x20
+#define ALTR_A10_ECC_SERRPENA           BIT(0)
+#define ALTR_A10_ECC_DERRPENA           BIT(8)
+#define ALTR_A10_ECC_ERRPENA_MASK       (ALTR_A10_ECC_SERRPENA | \
+                                        ALTR_A10_ECC_DERRPENA)
+#define ALTR_A10_ECC_SERRPENB           BIT(16)
+#define ALTR_A10_ECC_DERRPENB           BIT(24)
+#define ALTR_A10_ECC_ERRPENB_MASK       (ALTR_A10_ECC_SERRPENB | \
+                                        ALTR_A10_ECC_DERRPENB)
+
+#define ALTR_A10_ECC_INTTEST_OFST       0x24
+#define ALTR_A10_ECC_TSERRA             BIT(0)
+#define ALTR_A10_ECC_TDERRA             BIT(8)
+
+/* ECC Manager Defines */
+#define A10_SYSMGR_ECC_INTMASK_SET_OFST   0x94
+#define A10_SYSMGR_ECC_INTMASK_CLR_OFST   0x98
+#define A10_SYSMGR_ECC_INTMASK_OCRAM      BIT(1)
+
 #define A10_SYSMGR_ECC_INTSTAT_SERR_OFST  0x9C
 #define A10_SYSMGR_ECC_INTSTAT_DERR_OFST  0xA0
 #define A10_SYSMGR_ECC_INTSTAT_L2         BIT(0)
+#define A10_SYSMGR_ECC_INTSTAT_OCRAM      BIT(1)
 
 #define A10_SYSGMR_MPU_CLEAR_L2_ECC_OFST  0xA8
 #define A10_SYSGMR_MPU_CLEAR_L2_ECC_SB    BIT(15)
@@ -245,6 +277,9 @@ struct altr_sdram_mc_data {
 #define ALTR_A10_L2_ECC_CE_INJ_MASK     0x00000101
 #define ALTR_A10_L2_ECC_UE_INJ_MASK     0x00010101
 
+/* Arria 10 OCRAM ECC Management Group Defines */
+#define ALTR_A10_OCRAM_ECC_EN_CTL       (BIT(1) | BIT(0))
+
 struct altr_edac_device_dev;
 
 struct edac_device_prv_data {