/*
* omap iommu: tlb and pagetable primitives
*
* Copyright (C) 2008-2010 Nokia Corporation
*
* Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>,
* Paul Mundt and Toshihiro Kobayashi
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <asm/cacheflush.h>
#include <plat/iommu.h>
#include "iopgtable.h"
#define for_each_iotlb_cr(obj, n, __i, cr) \
for (__i = 0; \
(__i < (n)) && (cr = __iotlb_read_cr((obj), __i), true); \
__i++)
/* accommodate the difference between omap1 and omap2/3 */
static const struct iommu_functions *arch_iommu;
static struct platform_driver omap_iommu_driver;
static struct kmem_cache *iopte_cachep;
/**
* install_iommu_arch - Install archtecure specific iommu functions
* @ops: a pointer to architecture specific iommu functions
*
* There are several kind of iommu algorithm(tlb, pagetable) among
* omap series. This interface installs such an iommu algorighm.
**/
int install_iommu_arch(const struct iommu_functions *ops)
{
if (arch_iommu)
return -EBUSY;
arch_iommu = ops;
return 0;
}
EXPORT_SYMBOL_GPL(install_iommu_arch);
/**
* uninstall_iommu_arch - Uninstall archtecure specific iommu functions
* @ops: a pointer to architecture specific iommu functions
*
* This interface uninstalls the iommu algorighm installed previously.
**/
void uninstall_iommu_arch(const struct iommu_functions *ops)
{
if (arch_iommu != ops)
pr_err("%s: not your arch\n", __func__);
arch_iommu = NULL;
}
EXPORT_SYMBOL_GPL(uninstall_iommu_arch);
/**
* iommu_save_ctx - Save registers for pm off-mode support
* @obj: target iommu
**/
void iommu_save_ctx(struct iommu *obj)
{
arch_iommu->save_ctx(obj);
}
EXPORT_SYMBOL_GPL(iommu_save_ctx);
/**
* iommu_restore_ctx - Restore registers for pm off-mode support
* @obj: target iommu
**/
void iommu_restore_ctx(struct iommu *obj)
{
arch_iommu->restore_ctx(obj);
}
EXPORT_SYMBOL_GPL(iommu_restore_ctx);
/**
* iommu_arch_version - Return running iommu arch version
**/
u32 iommu_arch_version(void)
{
return arch_iommu->version;
}
EXPORT_SYMBOL_GPL(iommu_arch_version);
static int iommu_enable(struct iommu *obj)
{
int err;
if (!obj)
return -EINVAL;
clk_enable(obj->clk);
err = arch_iommu->enable(obj);
clk_disable(obj->clk);
return err;
}
static void iommu_disable(struct iommu *obj)
{
if (!obj)
return;
clk_enable(obj->clk);
arch_iommu->disable(obj);
clk_disable(obj->clk);
}
/*
* TLB operations
*/
void iotlb_cr_to_e(struct cr_regs *cr, struct iotlb_entry *e)
{
BUG_ON(!cr || !e);
arch_iommu->cr_to_e(cr, e);
}
EXPORT_SYMBOL_GPL(iotlb_cr_to_e);
static inline int iotlb_cr_valid(struct cr_regs *cr)
{
if (!cr)
return -EINVAL;
return arch_iommu->cr_valid(cr);
}
static inline struct cr_regs *iotlb_alloc_cr(struct iommu *obj,
struct iotlb_entry *e)
{
if (!e)
return NULL;
return arch_iommu->alloc_cr(obj, e);
}
u32 iotlb_cr_to_virt(struct cr_regs *cr)
{
return arch_iommu->cr_to_virt(cr);
}
EXPORT_SYMBOL_GPL(iotlb_cr_to_virt);
static u32 get_iopte_attr(struct iotlb_entry *e)
{
return arch_iommu->get_pte_attr(e);
}
static u32 iommu_report_fault(struct iommu *obj, u32 *da)
{
return arch_iommu->fault_isr(obj, da);
}
static void iotlb_lock_get(struct iommu *obj, struct iotlb_lock *l)
{
u32 val;
val = iommu_read_reg(obj, MMU_LOCK);
l->base = MMU_LOCK_BASE(val);
l->vict = MMU_LOCK_VICT(val);
}
static void iotlb_lock_set(struct iommu *obj, struct iotlb_lock *l)
{
u32 val;
val = (l->base << MMU_LOCK_BASE_SHIFT);
val |= (l->vict << MMU_LOCK_VICT_SHIFT);
iommu_write_reg(obj, val, MMU_LOCK);
}
static void iotlb_read_cr(struct iommu *obj, struct cr_regs *cr)