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

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
Loading
Loading
Loading
Loading
+42 −7
Original line number Diff line number Diff line
@@ -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;

	/*
	 * 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,6 +559,8 @@ 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);
	/* 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 */
	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 */
	}