Skip to content

Commit

Permalink
omap: IOMMU: add support to callback during fault handling
Browse files Browse the repository at this point in the history
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 <dacohen@gmail.com>
Acked-by: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
  • Loading branch information
David Cohen authored and Tony Lindgren committed Feb 24, 2011
1 parent 92e753d commit d594f1f
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 18 deletions.
17 changes: 15 additions & 2 deletions arch/arm/mach-omap2/iommu2.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
14 changes: 13 additions & 1 deletion arch/arm/plat-omap/include/plat/iommu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
52 changes: 37 additions & 15 deletions arch/arm/plat-omap/iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -783,41 +783,36 @@ 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);

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;
}
Expand Down Expand Up @@ -920,6 +915,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
*/
Expand Down

0 comments on commit d594f1f

Please sign in to comment.