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: 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
This commit is contained in:
Dinh Nguyen 2021-10-12 14:07:07 -05:00 committed by Borislav Petkov
parent bd1d6da17c
commit f7824ded41

View file

@ -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 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 */
}