EDAC, xgene: Add missing SoC register bus error handling
authorLoc Ho <lho@apm.com>
Fri, 22 Jan 2016 20:47:04 +0000 (13:47 -0700)
committerBorislav Petkov <bp@suse.de>
Mon, 25 Jan 2016 10:17:22 +0000 (11:17 +0100)
Add missing register bus error handling for APM X-Gene EDAC SoC and fix
a checking condition for CE error promoted to UE.

Signed-off-by: Loc Ho <lho@apm.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: devicetree@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-edac <linux-edac@vger.kernel.org>
Cc: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
Cc: patches@apm.com
Link: http://lkml.kernel.org/r/1453495625-28006-3-git-send-email-lho@apm.com
Signed-off-by: Borislav Petkov <bp@suse.de>
drivers/edac/xgene_edac.c

index 41f8764..bf19b6e 100644 (file)
@@ -61,6 +61,7 @@ struct xgene_edac {
        struct regmap           *mcba_map;
        struct regmap           *mcbb_map;
        struct regmap           *efuse_map;
+       struct regmap           *rb_map;
        void __iomem            *pcp_csr;
        spinlock_t              lock;
        struct dentry           *dfs;
@@ -1057,7 +1058,7 @@ static bool xgene_edac_l3_promote_to_uc_err(u32 l3cesr, u32 l3celr)
                case 0x041:
                        return true;
                }
-       } else if (L3C_ELR_ERRSYN(l3celr) == 9)
+       } else if (L3C_ELR_ERRWAY(l3celr) == 9)
                return true;
 
        return false;
@@ -1353,6 +1354,17 @@ static int xgene_edac_l3_remove(struct xgene_edac_dev_ctx *l3)
 #define GLBL_MDED_ERRH                 0x0848
 #define GLBL_MDED_ERRHMASK             0x084c
 
+/* IO Bus Registers */
+#define RBCSR                          0x0000
+#define STICKYERR_MASK                 BIT(0)
+#define RBEIR                          0x0008
+#define AGENT_OFFLINE_ERR_MASK         BIT(30)
+#define UNIMPL_RBPAGE_ERR_MASK         BIT(29)
+#define WORD_ALIGNED_ERR_MASK          BIT(28)
+#define PAGE_ACCESS_ERR_MASK           BIT(27)
+#define WRITE_ACCESS_MASK              BIT(26)
+#define RBERRADDR_RD(src)              ((src) & 0x03FFFFFF)
+
 static const char * const soc_mem_err_v1[] = {
        "10GbE0",
        "10GbE1",
@@ -1470,6 +1482,51 @@ static void xgene_edac_rb_report(struct edac_device_ctl_info *edac_dev)
        u32 err_addr_hi;
        u32 reg;
 
+       /* If the register bus resource isn't available, just skip it */
+       if (!ctx->edac->rb_map)
+               goto rb_skip;
+
+       /*
+        * Check RB access errors
+        * 1. Out of range
+        * 2. Un-implemented page
+        * 3. Un-aligned access
+        * 4. Offline slave IP
+        */
+       if (regmap_read(ctx->edac->rb_map, RBCSR, &reg))
+               return;
+       if (reg & STICKYERR_MASK) {
+               bool write;
+               u32 address;
+
+               dev_err(edac_dev->dev, "IOB bus access error(s)\n");
+               if (regmap_read(ctx->edac->rb_map, RBEIR, &reg))
+                       return;
+               write = reg & WRITE_ACCESS_MASK ? 1 : 0;
+               address = RBERRADDR_RD(reg);
+               if (reg & AGENT_OFFLINE_ERR_MASK)
+                       dev_err(edac_dev->dev,
+                               "IOB bus %s access to offline agent error\n",
+                               write ? "write" : "read");
+               if (reg & UNIMPL_RBPAGE_ERR_MASK)
+                       dev_err(edac_dev->dev,
+                               "IOB bus %s access to unimplemented page error\n",
+                               write ? "write" : "read");
+               if (reg & WORD_ALIGNED_ERR_MASK)
+                       dev_err(edac_dev->dev,
+                               "IOB bus %s word aligned access error\n",
+                               write ? "write" : "read");
+               if (reg & PAGE_ACCESS_ERR_MASK)
+                       dev_err(edac_dev->dev,
+                               "IOB bus %s to page out of range access error\n",
+                               write ? "write" : "read");
+               if (regmap_write(ctx->edac->rb_map, RBEIR, 0))
+                       return;
+               if (regmap_write(ctx->edac->rb_map, RBCSR, 0))
+                       return;
+       }
+rb_skip:
+
        /* IOB Bridge agent transaction error interrupt */
        reg = readl(ctx->dev_csr + IOBBATRANSERRINTSTS);
        if (!reg)
@@ -1852,6 +1909,17 @@ static int xgene_edac_probe(struct platform_device *pdev)
                goto out_err;
        }
 
+       /*
+        * NOTE: The register bus resource is optional for compatibility
+        * reason.
+        */
+       edac->rb_map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+                                                      "regmap-rb");
+       if (IS_ERR(edac->rb_map)) {
+               dev_warn(edac->dev, "missing syscon regmap rb\n");
+               edac->rb_map = NULL;
+       }
+
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        edac->pcp_csr = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(edac->pcp_csr)) {