Commit d594f1f3 authored by David Cohen's avatar David Cohen Committed by Tony Lindgren

omap: IOMMU: add support to callback during fault handling

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: default avatarDavid Cohen <dacohen@gmail.com>
Acked-by: default avatarHiroshi DOYU <Hiroshi.DOYU@nokia.com>
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
parent 92e753d7
...@@ -146,18 +146,31 @@ static void omap2_iommu_set_twl(struct iommu *obj, bool on) ...@@ -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) static u32 omap2_iommu_fault_isr(struct iommu *obj, u32 *ra)
{ {
u32 stat, da; u32 stat, da;
u32 errs = 0;
stat = iommu_read_reg(obj, MMU_IRQSTATUS); stat = iommu_read_reg(obj, MMU_IRQSTATUS);
stat &= MMU_IRQ_MASK; stat &= MMU_IRQ_MASK;
if (!stat) if (!stat) {
*ra = 0;
return 0; return 0;
}
da = iommu_read_reg(obj, MMU_FAULT_AD); da = iommu_read_reg(obj, MMU_FAULT_AD);
*ra = da; *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); iommu_write_reg(obj, stat, MMU_IRQSTATUS);
return stat; return errs;
} }
static void omap2_tlb_read_cr(struct iommu *obj, struct cr_regs *cr) static void omap2_tlb_read_cr(struct iommu *obj, struct cr_regs *cr)
......
...@@ -31,6 +31,7 @@ struct iommu { ...@@ -31,6 +31,7 @@ struct iommu {
struct clk *clk; struct clk *clk;
void __iomem *regbase; void __iomem *regbase;
struct device *dev; struct device *dev;
void *isr_priv;
unsigned int refcount; unsigned int refcount;
struct mutex iommu_lock; /* global for this whole object */ struct mutex iommu_lock; /* global for this whole object */
...@@ -47,7 +48,7 @@ struct iommu { ...@@ -47,7 +48,7 @@ struct iommu {
struct list_head mmap; struct list_head mmap;
struct mutex mmap_lock; /* protect 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 */ void *ctx; /* iommu context: registres saved area */
u32 da_start; u32 da_start;
...@@ -109,6 +110,13 @@ struct iommu_platform_data { ...@@ -109,6 +110,13 @@ struct iommu_platform_data {
u32 da_end; 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) #if defined(CONFIG_ARCH_OMAP1)
#error "iommu for this processor not implemented yet" #error "iommu for this processor not implemented yet"
#else #else
...@@ -161,6 +169,10 @@ extern size_t iopgtable_clear_entry(struct iommu *obj, u32 iova); ...@@ -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 int iommu_set_da_range(struct iommu *obj, u32 start, u32 end);
extern struct iommu *iommu_get(const char *name); extern struct iommu *iommu_get(const char *name);
extern void iommu_put(struct iommu *obj); 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_save_ctx(struct iommu *obj);
extern void iommu_restore_ctx(struct iommu *obj); extern void iommu_restore_ctx(struct iommu *obj);
......
...@@ -783,25 +783,19 @@ static void iopgtable_clear_entry_all(struct iommu *obj) ...@@ -783,25 +783,19 @@ static void iopgtable_clear_entry_all(struct iommu *obj)
*/ */
static irqreturn_t iommu_fault_handler(int irq, void *data) static irqreturn_t iommu_fault_handler(int irq, void *data)
{ {
u32 stat, da; u32 da, errs;
u32 *iopgd, *iopte; u32 *iopgd, *iopte;
int err = -EIO;
struct iommu *obj = data; struct iommu *obj = data;
if (!obj->refcount) if (!obj->refcount)
return IRQ_NONE; return IRQ_NONE;
/* Dynamic loading TLB or PTE */
if (obj->isr)
err = obj->isr(obj);
if (!err)
return IRQ_HANDLED;
clk_enable(obj->clk); clk_enable(obj->clk);
stat = iommu_report_fault(obj, &da); errs = iommu_report_fault(obj, &da);
clk_disable(obj->clk); 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; return IRQ_HANDLED;
iommu_disable(obj); iommu_disable(obj);
...@@ -809,15 +803,16 @@ static irqreturn_t iommu_fault_handler(int irq, void *data) ...@@ -809,15 +803,16 @@ static irqreturn_t iommu_fault_handler(int irq, void *data)
iopgd = iopgd_offset(obj, da); iopgd = iopgd_offset(obj, da);
if (!iopgd_is_table(*iopgd)) { if (!iopgd_is_table(*iopgd)) {
dev_err(obj->dev, "%s: da:%08x pgd:%p *pgd:%08x\n", obj->name, dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p "
da, iopgd, *iopgd); "*pgd:px%08x\n", obj->name, errs, da, iopgd, *iopgd);
return IRQ_NONE; return IRQ_NONE;
} }
iopte = iopte_offset(iopgd, da); iopte = iopte_offset(iopgd, da);
dev_err(obj->dev, "%s: da:%08x pgd:%p *pgd:%08x pte:%p *pte:%08x\n", dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p *pgd:0x%08x "
obj->name, da, iopgd, *iopgd, iopte, *iopte); "pte:0x%p *pte:0x%08x\n", obj->name, errs, da, iopgd, *iopgd,
iopte, *iopte);
return IRQ_NONE; return IRQ_NONE;
} }
...@@ -920,6 +915,33 @@ void iommu_put(struct iommu *obj) ...@@ -920,6 +915,33 @@ void iommu_put(struct iommu *obj)
} }
EXPORT_SYMBOL_GPL(iommu_put); 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 * OMAP Device MMU(IOMMU) detection
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment