ARM: tegra: mc: Make error handling make more sense
Alex Waterman [Tue, 23 Jul 2013 19:12:30 +0000 (12:12 -0700)]
On T114 and later the errors being reported by the MC are starting
to grow in complexity. This takes that complexity and boils it down
into something more manageable for developers debugging SMMU and
other faults generated by the MC.

The aim of this patch is to make the errors more understandable and
correctly reflect the errors actually generated. The previous
approach to error handling was mis-representing certain errors.

Change-Id: Ie5b96d1a0ead344a3d1fa120e30b3152bfe23b70
Signed-off-by: Alex Waterman <alexw@nvidia.com>
Reviewed-on: http://git-master/r/252617
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Krishna Reddy <vdumpa@nvidia.com>

arch/arm/mach-tegra/mcerr-t11.c
arch/arm/mach-tegra/mcerr-t14.c
arch/arm/mach-tegra/mcerr.c
arch/arm/mach-tegra/mcerr.h

index fd6bf27..e5eca1c 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Tegra 11x SoC-specific mcerr code.
  *
- * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved.
+ * Copyright (c) 2010-2013, NVIDIA Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -24,8 +24,6 @@
 
 #define dummy_client   client("dummy")
 
-static char unknown_buf[30];
-
 struct mc_client mc_clients[] = {
        client("ptc"),
        client("display0_wina"),        client("display1_wina"),
@@ -84,37 +82,20 @@ struct mc_client mc_clients[] = {
 
 int mc_client_last = ARRAY_SIZE(mc_clients) - 1;
 
-static const char *mcerr_t11x_info(u32 stat)
-{
-       if (stat & MC_INT_DECERR_EMEM)
-               return "MC_DECERR";
-       else if (stat & MC_INT_SECURITY_VIOLATION)
-               return "MC_SECURITY_ERR";
-       else if (stat & MC_INT_INVALID_SMMU_PAGE)
-               return "MC_SMMU_ERR";
-       else if (stat & MC_INT_DECERR_VPR)
-               return "MC_DECERR_VPR";
-       else if (stat & MC_INT_SECERR_SEC)
-               return "MC_DECERR_SEC";
-
-       /* Otherwise we have an unknown type... */
-       snprintf(unknown_buf, 30, "unknown - 0x%04x", stat);
-       return unknown_buf;
-}
-
 static void mcerr_t11x_info_update(struct mc_client *c, u32 stat)
 {
        if (stat & MC_INT_DECERR_EMEM)
                c->intr_counts[0]++;
-       else if (stat & MC_INT_SECURITY_VIOLATION)
+       if (stat & MC_INT_SECURITY_VIOLATION)
                c->intr_counts[1]++;
-       else if (stat & MC_INT_INVALID_SMMU_PAGE)
+       if (stat & MC_INT_INVALID_SMMU_PAGE)
                c->intr_counts[2]++;
-       else if (stat & MC_INT_DECERR_VPR)
+       if (stat & MC_INT_DECERR_VPR)
                c->intr_counts[3]++;
-       else if (stat & MC_INT_SECERR_SEC)
+       if (stat & MC_INT_SECERR_SEC)
                c->intr_counts[4]++;
-       else
+
+       if (stat & ~MC_INT_EN_MASK)
                c->intr_counts[5]++;
 }
 
@@ -122,17 +103,17 @@ static void mcerr_t11x_info_update(struct mc_client *c, u32 stat)
  * T11x reports addresses in a 32 byte range thus we can only give an
  * approximate location for the invalid memory request, not the exact address.
  */
-static void mcerr_t11x_print(const char *mc_type, u32 err, u32 addr,
-                            const struct mc_client *client, int is_secure,
-                            int is_write, const char *mc_err_info)
+static void mcerr_t11x_print(const struct mc_error *err,
+                            const struct mc_client *client,
+                            u32 status, phys_addr_t addr,
+                            int secure, int rw, const char *smmu_info)
 {
-       pr_err("%s (0x%08lx) [0x%p -> 0x%p] %s (%s %s %s)\n", mc_type,
-              (unsigned long) err,
-              (void *)(addr & ~0x1f), (void *)(addr | 0x1f),
-              (client) ? client->name : "unknown",
-              (is_secure) ? "secure" : "non-secure",
-              (is_write) ? "write" : "read",
-              mc_err_info);
+       pr_err("[mcerr] %s: %s\n", client->name, err->msg);
+       pr_err("[mcerr]   status = 0x%08x; addr = [0x%08lx -> 0x%08lx]",
+              status, (ulong)(addr & ~0x1f), (ulong)(addr | 0x1f));
+       pr_err("[mcerr]   secure: %s, access-type: %s, SMMU fault: %s\n",
+              secure ? "yes" : "no", rw ? "write" : "read",
+              smmu_info ? smmu_info : "none");
 }
 
 static int mcerr_t11x_debugfs_show(struct seq_file *s, void *v)
@@ -173,7 +154,6 @@ static int mcerr_t11x_debugfs_show(struct seq_file *s, void *v)
  */
 void mcerr_chip_specific_setup(struct mcerr_chip_specific *spec)
 {
-       spec->mcerr_info = mcerr_t11x_info;
        spec->mcerr_print = mcerr_t11x_print;
        spec->mcerr_info_update = mcerr_t11x_info_update;
        spec->mcerr_debugfs_show = mcerr_t11x_debugfs_show;
index 179c0b8..d201f92 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Tegra 14x SoC-specific mcerr code.
  *
- * Copyright (c) 2012, NVIDIA Corporation. All rights reserved.
+ * Copyright (c) 2012-2013, NVIDIA Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -24,8 +24,6 @@
 
 #define dummy_client   client("dummy")
 
-static char unknown_buf[30];
-
 struct mc_client mc_clients[] = {
        client("ptc"),
        client("display0_wina"),        client("display1_wina"),
@@ -44,7 +42,7 @@ struct mc_client mc_clients[] = {
        client("gr3d0_idx"),            dummy_client,
        dummy_client,                   dummy_client,
        client("msenc"),
-       client("ahb_dma"),              client("ahb_slave"), /* 30 */
+       client("ahb_dma"),              client("ahb_slave_r"), /* 30 */
        dummy_client,                   client("gr3d0_tex"),
        dummy_client,
        client("vde_bsev"),             client("vde_mbe"),
@@ -65,7 +63,7 @@ struct mc_client mc_clients[] = {
        client("isp"),
        client("cpu_lp"),               client("cpu"),
        dummy_client,
-       client("ahb_dma"),              client("ahb_slave"), /* 60 */
+       client("ahb_dma"),              client("ahb_slave_w"), /* 60 */
        dummy_client,
        client("vde_bsev"),             client("vde_dbg"),
        client("vde_mbe"),              client("vde_tpm"),
@@ -87,45 +85,24 @@ struct mc_client mc_clients[] = {
 
 int mc_client_last = ARRAY_SIZE(mc_clients) - 1;
 
-static const char *mcerr_t14x_info(u32 stat)
-{
-       if (stat & MC_INT_DECERR_EMEM)
-               return "MC_DECERR";
-       else if (stat & MC_INT_SECURITY_VIOLATION)
-               return "MC_SECURITY_ERR";
-       else if (stat & MC_INT_INVALID_SMMU_PAGE)
-               return "MC_SMMU_ERR";
-       else if (stat & MC_INT_DECERR_VPR)
-               return "MC_DECERR_VPR";
-       else if (stat & MC_INT_SECERR_SEC)
-               return "MC_SECERR_SEC";
-       else if (stat & MC_INT_BBC_PRIVATE_MEM_VIOLATION)
-               return "MC_BBC_PRIVATE_MEM_VIOLATION";
-       else if (stat & MC_INT_DECERR_BBC)
-               return "MC_DECERR_BBC";
-
-       /* Otherwise we have an unknown type... */
-       snprintf(unknown_buf, 30, "unknown - 0x%04x", stat);
-       return unknown_buf;
-}
-
 static void mcerr_t14x_info_update(struct mc_client *c, u32 stat)
 {
        if (stat & MC_INT_DECERR_EMEM)
                c->intr_counts[0]++;
-       else if (stat & MC_INT_SECURITY_VIOLATION)
+       if (stat & MC_INT_SECURITY_VIOLATION)
                c->intr_counts[1]++;
-       else if (stat & MC_INT_INVALID_SMMU_PAGE)
+       if (stat & MC_INT_INVALID_SMMU_PAGE)
                c->intr_counts[2]++;
-       else if (stat & MC_INT_DECERR_VPR)
+       if (stat & MC_INT_DECERR_VPR)
                c->intr_counts[3]++;
-       else if (stat & MC_INT_SECERR_SEC)
+       if (stat & MC_INT_SECERR_SEC)
                c->intr_counts[4]++;
-       else if (stat & MC_INT_BBC_PRIVATE_MEM_VIOLATION)
+       if (stat & MC_INT_BBC_PRIVATE_MEM_VIOLATION)
                c->intr_counts[5]++;
-       else if (stat & MC_INT_DECERR_BBC)
+       if (stat & MC_INT_DECERR_BBC)
                c->intr_counts[6]++;
-       else
+
+       if (stat & ~MC_INT_EN_MASK)
                c->intr_counts[7]++;
 }
 
@@ -171,7 +148,6 @@ static int mcerr_t14x_debugfs_show(struct seq_file *s, void *v)
  */
 void mcerr_chip_specific_setup(struct mcerr_chip_specific *spec)
 {
-       spec->mcerr_info = mcerr_t14x_info;
        spec->mcerr_info_update = mcerr_t14x_info_update;
        spec->mcerr_debugfs_show = mcerr_t14x_debugfs_show;
        spec->nr_clients = ARRAY_SIZE(mc_clients);
index a144776..622eb01 100644 (file)
@@ -71,14 +71,61 @@ module_param(spurious_mc1_intr, int, S_IRUGO | S_IWUSR);
 #endif
 
 static const char *const smmu_page_attrib[] = {
-       "SMMU: nr-nw-s",
-       "SMMU: nr-nw-ns",
-       "SMMU: nr-wr-s",
-       "SMMU: nr-wr-ns",
-       "SMMU: rd-nw-s",
-       "SMMU: rd-nw-ns",
-       "SMMU: rd-wr-s",
-       "SMMU: rd-wr-ns"
+       "nr-nw-s",
+       "nr-nw-ns",
+       "nr-wr-s",
+       "nr-wr-ns",
+       "rd-nw-s",
+       "rd-nw-ns",
+       "rd-wr-s",
+       "rd-wr-ns"
+};
+
+/*
+ * Table of known errors and their interrupt signatures.
+ */
+static const struct mc_error mc_errors[] = {
+       MC_ERR(MC_INT_DECERR_EMEM,
+              "EMEM address decode error",
+              0, MC_ERR_STATUS, MC_ERR_ADR),
+       MC_ERR(MC_INT_DECERR_VPR,
+              "MC request violates VPR requirments",
+              0, MC_ERR_VPR_STATUS, MC_ERR_VPR_ADR),
+       MC_ERR(MC_INT_SECURITY_VIOLATION,
+              "non secure access to secure region",
+              0, MC_ERR_STATUS, MC_ERR_ADR),
+       MC_ERR(MC_INT_SECERR_SEC,
+              "MC request violated SEC carveout requirements",
+              0, MC_ERR_SEC_STATUS, MC_ERR_SEC_ADR),
+
+       /*
+        * SMMU related faults.
+        */
+       MC_ERR(MC_INT_INVALID_SMMU_PAGE,
+              "SMMU address translation fault",
+              E_SMMU, MC_ERR_STATUS, MC_ERR_ADR),
+       MC_ERR(MC_INT_INVALID_SMMU_PAGE | MC_INT_DECERR_EMEM,
+              "EMEM decode error on PDE or PTE entry",
+              E_SMMU, MC_ERR_STATUS, MC_ERR_ADR),
+       MC_ERR(MC_INT_INVALID_SMMU_PAGE | MC_INT_SECERR_SEC,
+              "secure SMMU address translation fault",
+              E_SMMU, MC_ERR_SEC_STATUS, MC_ERR_SEC_ADR),
+       MC_ERR(MC_INT_INVALID_SMMU_PAGE | MC_INT_DECERR_VPR,
+              "VPR SMMU address translation fault",
+              E_SMMU, MC_ERR_VPR_STATUS, MC_ERR_VPR_ADR),
+
+       /*
+        * Baseband controller related faults.
+        */
+       MC_ERR(MC_INT_BBC_PRIVATE_MEM_VIOLATION,
+              "client accessed BBC aperture",
+              0, MC_ERR_BBC_STATUS, MC_ERR_BBC_ADR),
+       MC_ERR(MC_INT_DECERR_BBC,
+              "BBC accessed memory outside of its aperture",
+              E_NO_STATUS, 0, 0),
+
+       /* NULL terminate. */
+       MC_ERR(0, NULL, 0, 0, 0),
 };
 
 static DEFINE_SPINLOCK(mc_lock);
@@ -142,14 +189,15 @@ static irqreturn_t tegra_mc_error_isr(int irq, void *data)
 {
        void __iomem *err_mc = mc;
        struct mc_client *client = NULL;
-       const char *mc_type;
-       const char *mc_info;
+       const struct mc_error *fault;
+       const char *smmu_info;
        unsigned long count;
-       u32 addr, err, stat;
-       u32 is_write, is_secure;
+       phys_addr_t addr;
+       u32 status, intr;
+       u32 write, secure;
        u32 client_id;
 
-       stat = readl(mc + MC_INT_STATUS) & MC_INT_EN_MASK;
+       intr = readl(mc + MC_INT_STATUS) & MC_INT_EN_MASK;
 
        cancel_delayed_work(&unthrottle_prints_work);
 
@@ -158,16 +206,16 @@ static irqreturn_t tegra_mc_error_isr(int irq, void *data)
         * Interrupts can come from either MC; handle the case in which the
         * interrupt is generated by the second MC.
         */
-       if (stat & MC_INT_EXT_INTR_IN) {
+       if (intr & MC_INT_EXT_INTR_IN) {
                err_mc = mc1;
-               stat = readl(err_mc + MC_INT_STATUS);
+               intr = readl(err_mc + MC_INT_STATUS);
 
 #ifdef CONFIG_ARCH_TEGRA_11x_SOC
                /*
                 * It appears like the secondary MC occasionally generates a
                 * spurious interrupt. If so, just ignore this interrupt.
                 */
-               if (!stat) {
+               if (!intr) {
                        spurious_mc1_intr++;
                        goto out;
                }
@@ -175,37 +223,49 @@ static irqreturn_t tegra_mc_error_isr(int irq, void *data)
 #endif /* CONFIG_ARCH_TEGRA_11x_SOC */
 #endif
 
-       if (stat & MC_INT_ARBITRATION_EMEM) {
+       if (intr & MC_INT_ARBITRATION_EMEM) {
                arb_intr();
-               if (stat == MC_INT_ARBITRATION_EMEM)
+               if (intr == MC_INT_ARBITRATION_EMEM)
                        goto out;
+               intr &= ~MC_INT_ARBITRATION_EMEM;
        }
 
        spin_lock(&mc_lock);
        count = ++error_count;
        spin_unlock(&mc_lock);
 
-       if (stat & MC_INT_DECERR_VPR) {
-               err = readl(err_mc + MC_ERR_VPR_STATUS);
-               addr = readl(err_mc + MC_ERR_VPR_ADR);
-               mc_type = "VPR DECERR";
-       } else if (stat & MC_INT_SECERR_SEC) {
-               err = readl(err_mc + MC_ERR_SEC_STATUS);
-               addr = readl(err_mc + MC_ERR_SEC_ADR);
-               mc_type = "SEC SECERR";
-       } else {
-               err = readl(err_mc + MC_ERROR_STATUS);
-               addr = readl(err_mc + MC_ERROR_ADDRESS);
-               mc_type = chip_specific.mcerr_type(err);
+       fault = chip_specific.mcerr_info(intr & MC_INT_EN_MASK);
+       if (WARN(!fault, "[mcerr] Unknown error! intr sig: 0x%08x\n",
+                intr & MC_INT_EN_MASK))
+               goto out;
+
+       if (fault->flags & E_NO_STATUS) {
+               pr_err("[mcerr] MC fault - no status: %s\n", fault->msg);
+               goto out;
        }
-       is_write = err & (1 << 16);
-       is_secure = err & (1 << 17);
-       client_id = err & 0x7f;
+
+       status = readl(err_mc + fault->stat_reg);
+       addr = readl(err_mc + fault->addr_reg);
+       secure = !!(status & MC_ERR_STATUS_SECURE);
+       write = !!(status & MC_ERR_STATUS_WRITE);
+       client_id = status & 0x7f;
        client = &mc_clients[client_id <= mc_client_last
                             ? client_id : mc_client_last];
 
-       mc_info = chip_specific.mcerr_info(stat);
-       chip_specific.mcerr_info_update(client, stat);
+#ifdef MC_ERR_34BIT_PHYS_ADDR
+       /*
+        * LPAE: make sure we get the extra 2 physical address bits available
+        * and pass them down to the printing function.
+        */
+       addr |= (((phys_addr_t)(status & MC_ERR_STATUS_ADR_HI)) << 12);
+#endif
+
+       if (fault->flags & E_SMMU)
+               smmu_info = smmu_page_attrib[MC_ERR_SMMU_BITS(status)];
+       else
+               smmu_info = NULL;
+
+       chip_specific.mcerr_info_update(client, intr & MC_INT_EN_MASK);
 
        if (mcerr_throttle_enabled && count >= MAX_PRINTS) {
                schedule_delayed_work(&unthrottle_prints_work, HZ/2);
@@ -214,10 +274,10 @@ static irqreturn_t tegra_mc_error_isr(int irq, void *data)
                goto out;
        }
 
-       chip_specific.mcerr_print(mc_type, err, addr, client,
-                                 is_secure, is_write, mc_info);
+       chip_specific.mcerr_print(fault, client, status, addr, secure, write,
+                                 smmu_info);
 out:
-       writel(stat, err_mc + MC_INT_STATUS);
+       writel(intr, err_mc + MC_INT_STATUS);
        if (err_mc != mc) {
                readl(err_mc + MC_INT_STATUS);
                writel(MC_INT_EXT_INTR_IN, mc + MC_INT_STATUS);
@@ -225,62 +285,49 @@ out:
        return IRQ_HANDLED;
 }
 
-/*
- * Read the type of error that occured.
- */
-static const char *mcerr_default_type(u32 err)
+static const struct mc_error *mcerr_default_info(u32 intr)
 {
-       u32 type = (err >> 28) & 0x7;
-       u32 attrib;
-
-       switch (type) {
-       case MC_ERR_DECERR_EMEM:
-               return "DECERR_EMEM";
-       case MC_ERR_SECURITY_TRUSTZONE:
-               return "SECURITY_TRUSTZONE";
-       case MC_ERR_SECURITY_CARVEOUT:
-               return "SECURITY_CARVEOUT";
-       case MC_ERR_INVALID_SMMU_PAGE:
-               attrib = (err >> 25) & 7;
-               return smmu_page_attrib[attrib];
-       default:
-               return "BAD";
+       const struct mc_error *err;
+
+       for (err = mc_errors; err->sig && err->msg; err++) {
+               if (intr != err->sig)
+                       continue;
+               return err;
        }
-}
 
-static const char *mcerr_default_info(u32 stat)
-{
-       if (stat & MC_INT_DECERR_EMEM)
-               return "MC_DECERR";
-       else if (stat & MC_INT_SECURITY_VIOLATION)
-               return "MC_SECURITY_ERR";
-       else if (stat & MC_INT_INVALID_SMMU_PAGE)
-               return "MC_SMMU_ERR";
-       else
-               return "unknown";
+       return NULL;
 }
 
 static void mcerr_default_info_update(struct mc_client *c, u32 stat)
 {
        if (stat & MC_INT_DECERR_EMEM)
                c->intr_counts[0]++;
-       else if (stat & MC_INT_SECURITY_VIOLATION)
+       if (stat & MC_INT_SECURITY_VIOLATION)
                c->intr_counts[1]++;
-       else if (stat & MC_INT_INVALID_SMMU_PAGE)
+       if (stat & MC_INT_INVALID_SMMU_PAGE)
                c->intr_counts[2]++;
-       else
+
+       /* Unknown interrupts. */
+       if (stat & ~MC_INT_EN_MASK)
                c->intr_counts[3]++;
 }
 
-static void mcerr_default_print(const char *mc_err, u32 err, u32 addr,
-                               const struct mc_client *client, int is_secure,
-                               int is_write, const char *mc_err_info)
+/*
+ * This will print at least 8 hex digits for address. If the address is bigger
+ * then more digits will be printed but the full 16 hex digits for a 64 bit
+ * address will not get printed by the current code.
+ */
+static void mcerr_default_print(const struct mc_error *err,
+                               const struct mc_client *client,
+                               u32 status, phys_addr_t addr,
+                               int secure, int rw, const char *smmu_info)
 {
-       pr_err("%s (0x%08X): %p %s (%s %s %s)\n", mc_err, err, (void *)addr,
-              (client) ? client->name : "unknown",
-              (is_secure) ? "secure" : "non-secure",
-              (is_write) ? "write" : "read",
-              mc_err_info);
+       pr_err("[mcerr] %s: %s\n", client->name, err->msg);
+       pr_err("[mcerr]   status = 0x%08x; addr = 0x%08llx", status,
+              (long long unsigned int)addr);
+       pr_err("[mcerr]   secure: %s, access-type: %s, SMMU fault: %s\n",
+              secure ? "yes" : "no", rw ? "write" : "read",
+              smmu_info ? smmu_info : "none");
 }
 
 /*
@@ -367,7 +414,6 @@ static int __init tegra_mcerr_init(void)
        writel(reg, mc + MC_EMEM_ARB_OVERRIDE);
 #endif
 
-       chip_specific.mcerr_type         = mcerr_default_type;
        chip_specific.mcerr_info         = mcerr_default_info;
        chip_specific.mcerr_info_update  = mcerr_default_info_update;
        chip_specific.mcerr_print        = mcerr_default_print;
index 7195504..c3f83a7 100644 (file)
 
 #define MC_INT_STATUS                  0x0
 #define MC_INT_MASK                    0x4
-#define MC_ERROR_STATUS                        0x8
-#define MC_ERROR_ADDRESS               0xC
+#define MC_ERR_STATUS                  0x8
+#define MC_ERR_ADR                     0xC
+#define MC_ERR_BBC_STATUS              0x84
+#define MC_ERR_BBC_ADR                 0x88
 #define MC_ERR_VPR_STATUS              0x654
 #define MC_ERR_VPR_ADR                 0x658
 #define MC_ERR_SEC_STATUS              0x67c
 #define MC_ERR_SEC_ADR                 0x680
 
+#define MC_ERR_SMMU_MASK               (0x7 << 25)
+#define MC_ERR_SMMU_BITS(err)          (((err) & MC_ERR_SMMU_MASK) >> 25)
+#define MC_ERR_STATUS_WRITE            (1 << 16)
+#define MC_ERR_STATUS_SECURE           (1 << 17)
+#define MC_ERR_STATUS_ADR_HI           (3 << 20)
+
 #define MC_INT_EXT_INTR_IN                     (1<<1)
 #define MC_INT_DECERR_EMEM                     (1<<6)
 #define MC_INT_SECURITY_VIOLATION              (1<<8)
@@ -64,8 +72,7 @@
 #define MC_INT_SECERR_SEC                      (1<<13)
 #define MC_INT_BBC_PRIVATE_MEM_VIOLATION       (1<<14)
 #define MC_INT_DECERR_BBC                      (1<<15)
-#define MC_INT_DECERR_MTS              (1<<16)
-
+#define MC_INT_DECERR_MTS                      (1<<16)
 
 /*
  * Number of unique interrupts we have for this chip.
@@ -75,7 +82,7 @@
 #elif defined(CONFIG_ARCH_TEGRA_14x_SOC)
 #define INTR_COUNT     8
 #elif defined(CONFIG_ARCH_TEGRA_12x_SOC)
-#define INTR_COUNT  8
+#define INTR_COUNT     8
 #else
 #define INTR_COUNT     4
 #endif
@@ -129,17 +136,65 @@ struct mc_client {
        unsigned int intr_counts[INTR_COUNT];
 };
 
+/*
+ * This describes errors that can be generated by the MC. One is defined for
+ * each possibility.
+ *
+ * @sig Interrupt signiture for the error.
+ * @msg Error description.
+ * @flags Relevant flags for the error.
+ * @stat_reg Register offset that holds the status of the error.
+ * @addr_reg Register offset that holds the faulting address.
+ */
+struct mc_error {
+       const char *msg;
+       u32         sig;
+       int         flags;
+       u32         stat_reg;
+       u32         addr_reg;
+};
+
+#define E_SMMU       (1<<0)
+#define E_NO_STATUS  (1<<1)
+
 extern int mc_client_last;
 
 struct mcerr_chip_specific {
 
-       const char      *(*mcerr_info)(u32 status);
-       void             (*mcerr_info_update)(struct mc_client *c, u32 status);
-       const char      *(*mcerr_type)(u32 err);
-       void             (*mcerr_print)(const char *mc_err, u32 err, u32 addr,
-                                       const struct mc_client *client,
-                                       int is_secure, int is_write,
-                                       const char *mc_err_info);
+       /*
+        * Return a pointer to the relevant mc_error struct for the passed
+        * interrupt signature.
+        *
+        * Called in interrupt context - no sleeping, etc.
+        */
+       const struct mc_error *(*mcerr_info)(u32 intr);
+
+       /*
+        * Update the interrupt info for the passed client. This is called once
+        * the client who generated the error is determined.
+        *
+        * Called in interrupt context - no sleeping, etc.
+        */
+       void (*mcerr_info_update)(struct mc_client *c, u32 status);
+
+       /*
+        * Provide actual user feed back to the kernel log. The passed data is
+        * everything that could be determined about the fault.
+        *
+        * Note: @smmu_info may be NULL if the error occured without involving
+        * the SMMU. This is something @mcerr_print must handle gracefully.
+        *
+        * Called in interrupt context - no sleeping, etc.
+        */
+       void (*mcerr_print)(const struct mc_error *err,
+                           const struct mc_client *client,
+                           u32 status, phys_addr_t addr,
+                           int secure, int rw, const char *smmu_info);
+
+       /*
+        * Show the statistics for each client. This is called from a debugfs
+        * context - that means you can sleep and do general kernel stuff here.
+        */
        int              (*mcerr_debugfs_show)(struct seq_file *s, void *v);
 
        /* Numeric fields that must be set by the different architectures. */
@@ -147,6 +202,9 @@ struct mcerr_chip_specific {
 };
 
 #define client(_name) { .name = _name }
+#define MC_ERR(_sig, _msg, _flags, _stat_reg, _addr_reg)               \
+       { .sig = _sig, .msg = _msg, .flags = _flags,                    \
+                       .stat_reg = _stat_reg, .addr_reg = _addr_reg }
 
 /*
  * Error MMA tracking.