Add support to register an isr for IOMMU fault situations and adapt it
to allow such (*isr)() to be used as fault callback. Drivers using IOMMU
module might want to be informed when errors happen in order to debug it
or react.

Signed-off-by: David Cohen <[email protected]>
---
 arch/arm/mach-omap2/iommu2.c            |   17 +++++++++-
 arch/arm/plat-omap/include/plat/iommu.h |   14 ++++++++-
 arch/arm/plat-omap/iommu.c              |   52 ++++++++++++++++++++++---------
 3 files changed, 65 insertions(+), 18 deletions(-)

diff --git a/arch/arm/mach-omap2/iommu2.c b/arch/arm/mach-omap2/iommu2.c
index 49a1e5e..adb083e 100644
--- a/arch/arm/mach-omap2/iommu2.c
+++ b/arch/arm/mach-omap2/iommu2.c
@@ -146,18 +146,31 @@ static void omap2_iommu_set_twl(struct iommu *obj, bool 
on)
 static u32 omap2_iommu_fault_isr(struct iommu *obj, u32 *ra)
 {
        u32 stat, da;
+       u32 errs = 0;
 
        stat = iommu_read_reg(obj, MMU_IRQSTATUS);
        stat &= MMU_IRQ_MASK;
-       if (!stat)
+       if (!stat) {
+               *ra = 0;
                return 0;
+       }
 
        da = iommu_read_reg(obj, MMU_FAULT_AD);
        *ra = da;
 
+       if (stat & MMU_IRQ_TLBMISS)
+               errs |= OMAP_IOMMU_ERR_TLB_MISS;
+       if (stat & MMU_IRQ_TRANSLATIONFAULT)
+               errs |= OMAP_IOMMU_ERR_TRANS_FAULT;
+       if (stat & MMU_IRQ_EMUMISS)
+               errs |= OMAP_IOMMU_ERR_EMU_MISS;
+       if (stat & MMU_IRQ_TABLEWALKFAULT)
+               errs |= OMAP_IOMMU_ERR_TBLWALK_FAULT;
+       if (stat & MMU_IRQ_MULTIHITFAULT)
+               errs |= OMAP_IOMMU_ERR_MULTIHIT_FAULT;
        iommu_write_reg(obj, stat, MMU_IRQSTATUS);
 
-       return stat;
+       return errs;
 }
 
 static void omap2_tlb_read_cr(struct iommu *obj, struct cr_regs *cr)
diff --git a/arch/arm/plat-omap/include/plat/iommu.h 
b/arch/arm/plat-omap/include/plat/iommu.h
index 19cbb5e..174f1b9 100644
--- a/arch/arm/plat-omap/include/plat/iommu.h
+++ b/arch/arm/plat-omap/include/plat/iommu.h
@@ -31,6 +31,7 @@ struct iommu {
        struct clk      *clk;
        void __iomem    *regbase;
        struct device   *dev;
+       void            *isr_priv;
 
        unsigned int    refcount;
        struct mutex    iommu_lock;     /* global for this whole object */
@@ -47,7 +48,7 @@ struct iommu {
        struct list_head        mmap;
        struct mutex            mmap_lock; /* protect mmap */
 
-       int (*isr)(struct iommu *obj);
+       int (*isr)(struct iommu *obj, u32 da, u32 iommu_errs, void *priv);
 
        void *ctx; /* iommu context: registres saved area */
        u32 da_start;
@@ -109,6 +110,13 @@ struct iommu_platform_data {
        u32 da_end;
 };
 
+/* IOMMU errors */
+#define OMAP_IOMMU_ERR_TLB_MISS                (1 << 0)
+#define OMAP_IOMMU_ERR_TRANS_FAULT     (1 << 1)
+#define OMAP_IOMMU_ERR_EMU_MISS                (1 << 2)
+#define OMAP_IOMMU_ERR_TBLWALK_FAULT   (1 << 3)
+#define OMAP_IOMMU_ERR_MULTIHIT_FAULT  (1 << 4)
+
 #if defined(CONFIG_ARCH_OMAP1)
 #error "iommu for this processor not implemented yet"
 #else
@@ -161,6 +169,10 @@ extern size_t iopgtable_clear_entry(struct iommu *obj, u32 
iova);
 extern int iommu_set_da_range(struct iommu *obj, u32 start, u32 end);
 extern struct iommu *iommu_get(const char *name);
 extern void iommu_put(struct iommu *obj);
+extern int iommu_set_isr(const char *name,
+                        int (*isr)(struct iommu *obj, u32 da, u32 iommu_errs,
+                                   void *priv),
+                        void *isr_priv);
 
 extern void iommu_save_ctx(struct iommu *obj);
 extern void iommu_restore_ctx(struct iommu *obj);
diff --git a/arch/arm/plat-omap/iommu.c b/arch/arm/plat-omap/iommu.c
index f55f458..b0e0efc 100644
--- a/arch/arm/plat-omap/iommu.c
+++ b/arch/arm/plat-omap/iommu.c
@@ -780,25 +780,19 @@ static void iopgtable_clear_entry_all(struct iommu *obj)
  */
 static irqreturn_t iommu_fault_handler(int irq, void *data)
 {
-       u32 stat, da;
+       u32 da, errs;
        u32 *iopgd, *iopte;
-       int err = -EIO;
        struct iommu *obj = data;
 
        if (!obj->refcount)
                return IRQ_NONE;
 
-       /* Dynamic loading TLB or PTE */
-       if (obj->isr)
-               err = obj->isr(obj);
-
-       if (!err)
-               return IRQ_HANDLED;
-
        clk_enable(obj->clk);
-       stat = iommu_report_fault(obj, &da);
+       errs = iommu_report_fault(obj, &da);
        clk_disable(obj->clk);
-       if (!stat)
+
+       /* Fault callback or TLB/PTE Dynamic loading */
+       if (obj->isr && !obj->isr(obj, da, errs, obj->isr_priv))
                return IRQ_HANDLED;
 
        iommu_disable(obj);
@@ -806,15 +800,16 @@ static irqreturn_t iommu_fault_handler(int irq, void 
*data)
        iopgd = iopgd_offset(obj, da);
 
        if (!iopgd_is_table(*iopgd)) {
-               dev_err(obj->dev, "%s: da:%08x pgd:%p *pgd:%08x\n", obj->name,
-                       da, iopgd, *iopgd);
+               dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p "
+                       "*pgd:px%08x\n", obj->name, errs, da, iopgd, *iopgd);
                return IRQ_NONE;
        }
 
        iopte = iopte_offset(iopgd, da);
 
-       dev_err(obj->dev, "%s: da:%08x pgd:%p *pgd:%08x pte:%p *pte:%08x\n",
-               obj->name, da, iopgd, *iopgd, iopte, *iopte);
+       dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p *pgd:0x%08x "
+               "pte:0x%p *pte:0x%08x\n", obj->name, errs, da, iopgd, *iopgd,
+               iopte, *iopte);
 
        return IRQ_NONE;
 }
@@ -917,6 +912,33 @@ void iommu_put(struct iommu *obj)
 }
 EXPORT_SYMBOL_GPL(iommu_put);
 
+int iommu_set_isr(const char *name,
+                 int (*isr)(struct iommu *obj, u32 da, u32 iommu_errs,
+                            void *priv),
+                 void *isr_priv)
+{
+       struct device *dev;
+       struct iommu *obj;
+
+       dev = driver_find_device(&omap_iommu_driver.driver, NULL, (void *)name,
+                                device_match_by_alias);
+       if (!dev)
+               return -ENODEV;
+
+       obj = to_iommu(dev);
+       mutex_lock(&obj->iommu_lock);
+       if (obj->refcount != 0) {
+               mutex_unlock(&obj->iommu_lock);
+               return -EBUSY;
+       }
+       obj->isr = isr;
+       obj->isr_priv = isr_priv;
+       mutex_unlock(&obj->iommu_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_set_isr);
+
 /*
  *     OMAP Device MMU(IOMMU) detection
  */
-- 
1.7.2.3

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to