Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+15 −2
Original line number Original line Diff line number Diff line
@@ -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)
+13 −1
Original line number Original line Diff line number Diff line
@@ -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 {
	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 {
	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);
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);
+37 −15
Original line number Original line Diff line number Diff line
@@ -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)
	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)
}
}
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
 */
 */