Merge tag 'edac_urgent_for_v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / drivers / edac / synopsys_edac.c
index 5527055b09641c6f36ec4889b235435b7a7fcac0..ea7a9a342dd30b56ea39ebb5505c303c6be3176b 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/edac.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/spinlock.h>
 #include <linux/interrupt.h>
 #include <linux/of.h>
 
@@ -299,6 +300,7 @@ struct synps_ecc_status {
 /**
  * struct synps_edac_priv - DDR memory controller private instance data.
  * @baseaddr:          Base address of the DDR controller.
+ * @reglock:           Concurrent CSRs access lock.
  * @message:           Buffer for framing the event specific info.
  * @stat:              ECC status information.
  * @p_data:            Platform data.
@@ -313,6 +315,7 @@ struct synps_ecc_status {
  */
 struct synps_edac_priv {
        void __iomem *baseaddr;
+       spinlock_t reglock;
        char message[SYNPS_EDAC_MSG_SIZE];
        struct synps_ecc_status stat;
        const struct synps_platform_data *p_data;
@@ -408,7 +411,8 @@ out:
 static int zynqmp_get_error_info(struct synps_edac_priv *priv)
 {
        struct synps_ecc_status *p;
-       u32 regval, clearval = 0;
+       u32 regval, clearval;
+       unsigned long flags;
        void __iomem *base;
 
        base = priv->baseaddr;
@@ -452,10 +456,14 @@ ue_err:
        p->ueinfo.blknr = (regval & ECC_CEADDR1_BLKNR_MASK);
        p->ueinfo.data = readl(base + ECC_UESYND0_OFST);
 out:
-       clearval = ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_CE_ERRCNT;
-       clearval |= ECC_CTRL_CLR_UE_ERR | ECC_CTRL_CLR_UE_ERRCNT;
+       spin_lock_irqsave(&priv->reglock, flags);
+
+       clearval = readl(base + ECC_CLR_OFST) |
+                  ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_CE_ERRCNT |
+                  ECC_CTRL_CLR_UE_ERR | ECC_CTRL_CLR_UE_ERRCNT;
        writel(clearval, base + ECC_CLR_OFST);
-       writel(0x0, base + ECC_CLR_OFST);
+
+       spin_unlock_irqrestore(&priv->reglock, flags);
 
        return 0;
 }
@@ -515,24 +523,41 @@ static void handle_error(struct mem_ctl_info *mci, struct synps_ecc_status *p)
 
 static void enable_intr(struct synps_edac_priv *priv)
 {
+       unsigned long flags;
+
        /* 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
+       if (!(priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR)) {
                writel(DDR_QOSUE_MASK | DDR_QOSCE_MASK,
                       priv->baseaddr + DDR_QOS_IRQ_EN_OFST);
 
+               return;
+       }
+
+       spin_lock_irqsave(&priv->reglock, flags);
+
+       writel(DDR_UE_MASK | DDR_CE_MASK,
+              priv->baseaddr + ECC_CLR_OFST);
+
+       spin_unlock_irqrestore(&priv->reglock, flags);
 }
 
 static void disable_intr(struct synps_edac_priv *priv)
 {
+       unsigned long flags;
+
        /* Disable UE/CE Interrupts */
-       if (priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR)
-               writel(0x0, priv->baseaddr + ECC_CLR_OFST);
-       else
+       if (!(priv->p_data->quirks & DDR_ECC_INTR_SELF_CLEAR)) {
                writel(DDR_QOSUE_MASK | DDR_QOSCE_MASK,
                       priv->baseaddr + DDR_QOS_IRQ_DB_OFST);
+
+               return;
+       }
+
+       spin_lock_irqsave(&priv->reglock, flags);
+
+       writel(0, priv->baseaddr + ECC_CLR_OFST);
+
+       spin_unlock_irqrestore(&priv->reglock, flags);
 }
 
 /**
@@ -576,8 +601,6 @@ static irqreturn_t intr_handler(int irq, void *dev_id)
        /* 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);
-       else
-               enable_intr(priv);
 
        return IRQ_HANDLED;
 }
@@ -1357,6 +1380,7 @@ static int mc_probe(struct platform_device *pdev)
        priv = mci->pvt_info;
        priv->baseaddr = baseaddr;
        priv->p_data = p_data;
+       spin_lock_init(&priv->reglock);
 
        mc_init(mci, pdev);