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:
parent
bd1d6da17c
commit
f7824ded41
1 changed files with 42 additions and 7 deletions
|
@ -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 @@
|
||||||
#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)
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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 = {
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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[] = {
|
||||||
.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 */
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue