diff mbox series

[PATCHv2,1/2] EDAC/synopsys: add support for version 3 of the Synopsys EDAC DDR

Message ID 20210916231231.1430499-1-dinguyen@kernel.org (mailing list archive)
State New, archived
Headers show
Series [PATCHv2,1/2] EDAC/synopsys: add support for version 3 of the Synopsys EDAC DDR | expand

Commit Message

Dinh Nguyen Sept. 16, 2021, 11:12 p.m. UTC
Adds support for version 3.80a of the Synopsys DDR controller with EDAC. 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>
---
v2: remove "This patch" from commit message
---
 drivers/edac/synopsys_edac.c | 53 ++++++++++++++++++++++++++++++------
 1 file changed, 44 insertions(+), 9 deletions(-)

Comments

Michal Simek Sept. 17, 2021, 12:18 p.m. UTC | #1
On 9/17/21 1:12 AM, Dinh Nguyen wrote:
> Adds support for version 3.80a of the Synopsys DDR controller with EDAC. 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>
> ---
> v2: remove "This patch" from commit message
> ---
>  drivers/edac/synopsys_edac.c | 53 ++++++++++++++++++++++++++++++------
>  1 file changed, 44 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c
> index 7e7146b22c16..297845e65b65 100644
> --- a/drivers/edac/synopsys_edac.c
> +++ b/drivers/edac/synopsys_edac.c
> @@ -101,6 +101,7 @@
>  /* 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 */
> @@ -171,6 +172,10 @@
>  #define DDR_QOS_IRQ_EN_OFST		0x20208
>  #define DDR_QOS_IRQ_DB_OFST		0x2020C
>  
> +/* DDR QOS Interrupt register definitions */
> +#define DDR_UE_MASK			0x200
> +#define DDR_CE_MASK			0x100

Use BIT macro or genmask for these.

> +
>  /* 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
> +	 * cleared, so this condition does not apply.

cleared is here twice

> +	 */
> +	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 */
>  	}
> @@ -1352,8 +1387,8 @@ static int mc_probe(struct platform_device *pdev)
>  		}
>  	}
>  
> -	if (of_device_is_compatible(pdev->dev.of_node,
> -				    "xlnx,zynqmp-ddrc-2.40a"))
> +	if (!of_device_is_compatible(pdev->dev.of_node,
> +				    "xlnx,zynq-ddrc-a05"))
>  		setup_address_map(priv);

would be better to create quirk for it. You have the whole
infrastructure in place that's why it should be easy to do. (In separate
patch please).

Thanks,
Michal
diff mbox series

Patch

diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c
index 7e7146b22c16..297845e65b65 100644
--- a/drivers/edac/synopsys_edac.c
+++ b/drivers/edac/synopsys_edac.c
@@ -101,6 +101,7 @@ 
 /* 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 */
@@ -171,6 +172,10 @@ 
 #define DDR_QOS_IRQ_EN_OFST		0x20208
 #define DDR_QOS_IRQ_DB_OFST		0x2020C
 
+/* DDR QOS Interrupt register definitions */
+#define DDR_UE_MASK			0x200
+#define DDR_CE_MASK			0x100
+
 /* 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
+	 * cleared, 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 */
 	}
@@ -1352,8 +1387,8 @@  static int mc_probe(struct platform_device *pdev)
 		}
 	}
 
-	if (of_device_is_compatible(pdev->dev.of_node,
-				    "xlnx,zynqmp-ddrc-2.40a"))
+	if (!of_device_is_compatible(pdev->dev.of_node,
+				    "xlnx,zynq-ddrc-a05"))
 		setup_address_map(priv);
 #endif