Skip to content

Commit ba16c1c

Browse files
committed
Merge tag 'edac_urgent_for_v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras
Pull EDAC fix from Borislav Petkov: - Fix a race condition when clearing error count bits and toggling the error interrupt throug the same register, in synopsys_edac * tag 'edac_urgent_for_v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras: EDAC/synopsys: Fix ECC status and IRQ control race condition
2 parents 775a0ec + 591c946 commit ba16c1c

File tree

1 file changed

+37
-13
lines changed

1 file changed

+37
-13
lines changed

drivers/edac/synopsys_edac.c

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/edac.h>
1010
#include <linux/module.h>
1111
#include <linux/platform_device.h>
12+
#include <linux/spinlock.h>
1213
#include <linux/interrupt.h>
1314
#include <linux/of.h>
1415

@@ -299,6 +300,7 @@ struct synps_ecc_status {
299300
/**
300301
* struct synps_edac_priv - DDR memory controller private instance data.
301302
* @baseaddr: Base address of the DDR controller.
303+
* @reglock: Concurrent CSRs access lock.
302304
* @message: Buffer for framing the event specific info.
303305
* @stat: ECC status information.
304306
* @p_data: Platform data.
@@ -313,6 +315,7 @@ struct synps_ecc_status {
313315
*/
314316
struct synps_edac_priv {
315317
void __iomem *baseaddr;
318+
spinlock_t reglock;
316319
char message[SYNPS_EDAC_MSG_SIZE];
317320
struct synps_ecc_status stat;
318321
const struct synps_platform_data *p_data;
@@ -408,7 +411,8 @@ static int zynq_get_error_info(struct synps_edac_priv *priv)
408411
static int zynqmp_get_error_info(struct synps_edac_priv *priv)
409412
{
410413
struct synps_ecc_status *p;
411-
u32 regval, clearval = 0;
414+
u32 regval, clearval;
415+
unsigned long flags;
412416
void __iomem *base;
413417

414418
base = priv->baseaddr;
@@ -452,10 +456,14 @@ static int zynqmp_get_error_info(struct synps_edac_priv *priv)
452456
p->ueinfo.blknr = (regval & ECC_CEADDR1_BLKNR_MASK);
453457
p->ueinfo.data = readl(base + ECC_UESYND0_OFST);
454458
out:
455-
clearval = ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_CE_ERRCNT;
456-
clearval |= ECC_CTRL_CLR_UE_ERR | ECC_CTRL_CLR_UE_ERRCNT;
459+
spin_lock_irqsave(&priv->reglock, flags);
460+
461+
clearval = readl(base + ECC_CLR_OFST) |
462+
ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_CE_ERRCNT |
463+
ECC_CTRL_CLR_UE_ERR | ECC_CTRL_CLR_UE_ERRCNT;
457464
writel(clearval, base + ECC_CLR_OFST);
458-
writel(0x0, base + ECC_CLR_OFST);
465+
466+
spin_unlock_irqrestore(&priv->reglock, flags);
459467

460468
return 0;
461469
}
@@ -515,24 +523,41 @@ static void handle_error(struct mem_ctl_info *mci, struct synps_ecc_status *p)
515523

516524
static void enable_intr(struct synps_edac_priv *priv)
517525
{
526+
unsigned long flags;
527+
518528
/* Enable UE/CE Interrupts */
519-
if (priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR)
520-
writel(DDR_UE_MASK | DDR_CE_MASK,
521-
priv->baseaddr + ECC_CLR_OFST);
522-
else
529+
if (!(priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR)) {
523530
writel(DDR_QOSUE_MASK | DDR_QOSCE_MASK,
524531
priv->baseaddr + DDR_QOS_IRQ_EN_OFST);
525532

533+
return;
534+
}
535+
536+
spin_lock_irqsave(&priv->reglock, flags);
537+
538+
writel(DDR_UE_MASK | DDR_CE_MASK,
539+
priv->baseaddr + ECC_CLR_OFST);
540+
541+
spin_unlock_irqrestore(&priv->reglock, flags);
526542
}
527543

528544
static void disable_intr(struct synps_edac_priv *priv)
529545
{
546+
unsigned long flags;
547+
530548
/* Disable UE/CE Interrupts */
531-
if (priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR)
532-
writel(0x0, priv->baseaddr + ECC_CLR_OFST);
533-
else
549+
if (!(priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR)) {
534550
writel(DDR_QOSUE_MASK | DDR_QOSCE_MASK,
535551
priv->baseaddr + DDR_QOS_IRQ_DB_OFST);
552+
553+
return;
554+
}
555+
556+
spin_lock_irqsave(&priv->reglock, flags);
557+
558+
writel(0, priv->baseaddr + ECC_CLR_OFST);
559+
560+
spin_unlock_irqrestore(&priv->reglock, flags);
536561
}
537562

538563
/**
@@ -576,8 +601,6 @@ static irqreturn_t intr_handler(int irq, void *dev_id)
576601
/* v3.0 of the controller does not have this register */
577602
if (!(priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR))
578603
writel(regval, priv->baseaddr + DDR_QOS_IRQ_STAT_OFST);
579-
else
580-
enable_intr(priv);
581604

582605
return IRQ_HANDLED;
583606
}
@@ -1357,6 +1380,7 @@ static int mc_probe(struct platform_device *pdev)
13571380
priv = mci->pvt_info;
13581381
priv->baseaddr = baseaddr;
13591382
priv->p_data = p_data;
1383+
spin_lock_init(&priv->reglock);
13601384

13611385
mc_init(mci, pdev);
13621386

0 commit comments

Comments
 (0)