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: David Cohen <dacohen@gmail.com> Acked-by: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
This commit is contained in:
parent
92e753d798
commit
d594f1f31a
3 changed files with 65 additions and 18 deletions
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -783,25 +783,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);
|
||||
|
@ -809,15 +803,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;
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue