Commit f7824ded authored by Dinh Nguyen's avatar Dinh Nguyen Committed by Borislav Petkov

EDAC/synopsys: Add support for version 3 of the Synopsys EDAC DDR

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: default avatarDinh Nguyen <dinguyen@kernel.org>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
Reviewed-by: default avatarMichal Simek <michal.simek@xilinx.com>
Link: https://lkml.kernel.org/r/20211012190709.1504152-2-dinguyen@kernel.org
parent bd1d6da1
...@@ -101,6 +101,7 @@ ...@@ -101,6 +101,7 @@
/* DDR ECC Quirks */ /* DDR ECC Quirks */
#define DDR_ECC_INTR_SUPPORT BIT(0) #define DDR_ECC_INTR_SUPPORT BIT(0)
#define DDR_ECC_DATA_POISON_SUPPORT BIT(1) #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 */ /* ZynqMP Enhanced DDR memory controller registers that are relevant to ECC */
/* ECC Configuration Registers */ /* ECC Configuration Registers */
...@@ -171,6 +172,10 @@ ...@@ -171,6 +172,10 @@
#define DDR_QOS_IRQ_EN_OFST 0x20208 #define DDR_QOS_IRQ_EN_OFST 0x20208
#define DDR_QOS_IRQ_DB_OFST 0x2020C #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*/ /* ECC Corrected Error Register Mask and Shifts*/
#define ECC_CEADDR0_RW_MASK 0x3FFFF #define ECC_CEADDR0_RW_MASK 0x3FFFF
#define ECC_CEADDR0_RNK_MASK BIT(24) #define ECC_CEADDR0_RNK_MASK BIT(24)
...@@ -533,10 +538,16 @@ static irqreturn_t intr_handler(int irq, void *dev_id) ...@@ -533,10 +538,16 @@ static irqreturn_t intr_handler(int irq, void *dev_id)
priv = mci->pvt_info; priv = mci->pvt_info;
p_data = priv->p_data; p_data = priv->p_data;
regval = readl(priv->baseaddr + DDR_QOS_IRQ_STAT_OFST); /*
regval &= (DDR_QOSCE_MASK | DDR_QOSUE_MASK); * v3.0 of the controller has the ce/ue bits cleared automatically,
if (!(regval & ECC_CE_UE_INTR_MASK)) * so this condition does not apply.
return IRQ_NONE; */
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); status = p_data->get_error_info(priv);
if (status) if (status)
...@@ -548,7 +559,9 @@ static irqreturn_t intr_handler(int irq, void *dev_id) ...@@ -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", edac_dbg(3, "Total error count CE %d UE %d\n",
priv->ce_cnt, priv->ue_cnt); 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; return IRQ_HANDLED;
} }
...@@ -834,8 +847,13 @@ static void mc_init(struct mem_ctl_info *mci, struct platform_device *pdev) ...@@ -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) static void enable_intr(struct synps_edac_priv *priv)
{ {
/* Enable UE/CE Interrupts */ /* Enable UE/CE Interrupts */
writel(DDR_QOSUE_MASK | DDR_QOSCE_MASK, if (priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR)
priv->baseaddr + DDR_QOS_IRQ_EN_OFST); 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) static void disable_intr(struct synps_edac_priv *priv)
...@@ -890,6 +908,19 @@ static const struct synps_platform_data zynqmp_edac_def = { ...@@ -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[] = { static const struct of_device_id synps_edac_match[] = {
{ {
.compatible = "xlnx,zynq-ddrc-a05", .compatible = "xlnx,zynq-ddrc-a05",
...@@ -899,6 +930,10 @@ static const struct of_device_id synps_edac_match[] = { ...@@ -899,6 +930,10 @@ static const struct of_device_id synps_edac_match[] = {
.compatible = "xlnx,zynqmp-ddrc-2.40a", .compatible = "xlnx,zynqmp-ddrc-2.40a",
.data = (void *)&zynqmp_edac_def .data = (void *)&zynqmp_edac_def
}, },
{
.compatible = "snps,ddrc-3.80a",
.data = (void *)&synopsys_edac_def
},
{ {
/* end of table */ /* end of table */
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment