EDAC/synopsys: Add support for version 3 of the Synopsys EDAC DDR
authorDinh Nguyen <dinguyen@kernel.org>
Tue, 12 Oct 2021 19:07:07 +0000 (14:07 -0500)
committerBorislav Petkov <bp@suse.de>
Sat, 20 Nov 2021 18:23:49 +0000 (19:23 +0100)
Add support for version 3.80a of the Synopsys DDR controller. This
version of the controller has the following differences:

- UE/CE are auto cleared
- Interrupts are supported by default

Signed-off-by: Dinh Nguyen <dinguyen@kernel.org>
Signed-off-by: Borislav Petkov <bp@suse.de>
Reviewed-by: Michal Simek <michal.simek@xilinx.com>
Link: https://lkml.kernel.org/r/20211012190709.1504152-2-dinguyen@kernel.org
drivers/edac/synopsys_edac.c

index a5486d8..f05ff02 100644 (file)
 /* DDR ECC Quirks */
 #define DDR_ECC_INTR_SUPPORT           BIT(0)
 #define DDR_ECC_DATA_POISON_SUPPORT    BIT(1)
+#define DDR_ECC_INTR_SELF_CLEAR                BIT(2)
 
 /* ZynqMP Enhanced DDR memory controller registers that are relevant to ECC */
 /* ECC Configuration Registers */
 #define DDR_QOS_IRQ_EN_OFST            0x20208
 #define DDR_QOS_IRQ_DB_OFST            0x2020C
 
+/* DDR QOS Interrupt register definitions */
+#define DDR_UE_MASK                    BIT(9)
+#define DDR_CE_MASK                    BIT(8)
+
 /* ECC Corrected Error Register Mask and Shifts*/
 #define ECC_CEADDR0_RW_MASK            0x3FFFF
 #define ECC_CEADDR0_RNK_MASK           BIT(24)
@@ -533,10 +538,16 @@ static irqreturn_t intr_handler(int irq, void *dev_id)
        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;
+       /*
+        * v3.0 of the controller has the ce/ue bits cleared automatically,
+        * so this condition does not apply.
+        */
+       if (!(priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR)) {
+               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)
@@ -548,7 +559,9 @@ static irqreturn_t intr_handler(int irq, void *dev_id)
 
        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);
+       /* v3.0 of the controller does not have this register */
+       if (!(priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR))
+               writel(regval, priv->baseaddr + DDR_QOS_IRQ_STAT_OFST);
        return IRQ_HANDLED;
 }
 
@@ -834,8 +847,13 @@ static void mc_init(struct mem_ctl_info *mci, struct platform_device *pdev)
 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);
+       if (priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR)
+               writel(DDR_UE_MASK | DDR_CE_MASK,
+                      priv->baseaddr + ECC_CLR_OFST);
+       else
+               writel(DDR_QOSUE_MASK | DDR_QOSCE_MASK,
+                      priv->baseaddr + DDR_QOS_IRQ_EN_OFST);
+
 }
 
 static void disable_intr(struct synps_edac_priv *priv)
@@ -890,6 +908,19 @@ static const struct synps_platform_data zynqmp_edac_def = {
                          ),
 };
 
+static const struct synps_platform_data synopsys_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 | DDR_ECC_INTR_SELF_CLEAR
+#ifdef CONFIG_EDAC_DEBUG
+                         | DDR_ECC_DATA_POISON_SUPPORT
+#endif
+                         ),
+};
+
+
 static const struct of_device_id synps_edac_match[] = {
        {
                .compatible = "xlnx,zynq-ddrc-a05",
@@ -899,6 +930,10 @@ static const struct of_device_id synps_edac_match[] = {
                .compatible = "xlnx,zynqmp-ddrc-2.40a",
                .data = (void *)&zynqmp_edac_def
        },
+       {
+               .compatible = "snps,ddrc-3.80a",
+               .data = (void *)&synopsys_edac_def
+       },
        {
                /* end of table */
        }