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

Commit 56a45931 authored by Russell King's avatar Russell King Committed by Russell King
Browse files

Merge branch 'iommu' of...

parents fc05505b 5c651ffa
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -30,6 +30,14 @@ struct map_desc {

#ifdef CONFIG_MMU
extern void iotable_init(struct map_desc *, int);

struct mem_type;
extern const struct mem_type *get_mem_type(unsigned int type);
/*
 * external interface to remap single page with appropriate type
 */
extern int ioremap_page(unsigned long virt, unsigned long phys,
			const struct mem_type *mtype);
#else
#define iotable_init(map,num)	do { } while (0)
#endif
+5 −0
Original line number Diff line number Diff line
@@ -28,6 +28,11 @@ endif
obj-$(CONFIG_ARCH_OMAP2)		+= clock24xx.o
obj-$(CONFIG_ARCH_OMAP3)		+= clock34xx.o

iommu-y					+= iommu2.o
iommu-$(CONFIG_ARCH_OMAP3)		+= omap3-iommu.o

obj-$(CONFIG_OMAP_IOMMU)		+= $(iommu-y)

# Specific board support
obj-$(CONFIG_MACH_OMAP_GENERIC)		+= board-generic.o
obj-$(CONFIG_MACH_OMAP_H4)		+= board-h4.o
+323 −0
Original line number Diff line number Diff line
/*
 * omap iommu: omap2/3 architecture specific functions
 *
 * Copyright (C) 2008-2009 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/device.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/stringify.h>

#include <mach/iommu.h>

/*
 * omap2 architecture specific register bit definitions
 */
#define IOMMU_ARCH_VERSION	0x00000011

/* SYSCONF */
#define MMU_SYS_IDLE_SHIFT	3
#define MMU_SYS_IDLE_FORCE	(0 << MMU_SYS_IDLE_SHIFT)
#define MMU_SYS_IDLE_NONE	(1 << MMU_SYS_IDLE_SHIFT)
#define MMU_SYS_IDLE_SMART	(2 << MMU_SYS_IDLE_SHIFT)
#define MMU_SYS_IDLE_MASK	(3 << MMU_SYS_IDLE_SHIFT)

#define MMU_SYS_SOFTRESET	(1 << 1)
#define MMU_SYS_AUTOIDLE	1

/* SYSSTATUS */
#define MMU_SYS_RESETDONE	1

/* IRQSTATUS & IRQENABLE */
#define MMU_IRQ_MULTIHITFAULT	(1 << 4)
#define MMU_IRQ_TABLEWALKFAULT	(1 << 3)
#define MMU_IRQ_EMUMISS		(1 << 2)
#define MMU_IRQ_TRANSLATIONFAULT	(1 << 1)
#define MMU_IRQ_TLBMISS		(1 << 0)
#define MMU_IRQ_MASK	\
	(MMU_IRQ_MULTIHITFAULT | MMU_IRQ_TABLEWALKFAULT | MMU_IRQ_EMUMISS | \
	 MMU_IRQ_TRANSLATIONFAULT)

/* MMU_CNTL */
#define MMU_CNTL_SHIFT		1
#define MMU_CNTL_MASK		(7 << MMU_CNTL_SHIFT)
#define MMU_CNTL_EML_TLB	(1 << 3)
#define MMU_CNTL_TWL_EN		(1 << 2)
#define MMU_CNTL_MMU_EN		(1 << 1)

#define get_cam_va_mask(pgsz)				\
	(((pgsz) == MMU_CAM_PGSZ_16M) ? 0xff000000 :	\
	 ((pgsz) == MMU_CAM_PGSZ_1M)  ? 0xfff00000 :	\
	 ((pgsz) == MMU_CAM_PGSZ_64K) ? 0xffff0000 :	\
	 ((pgsz) == MMU_CAM_PGSZ_4K)  ? 0xfffff000 : 0)

static int omap2_iommu_enable(struct iommu *obj)
{
	u32 l, pa;
	unsigned long timeout;

	if (!obj->iopgd || !IS_ALIGNED((u32)obj->iopgd,  SZ_16K))
		return -EINVAL;

	pa = virt_to_phys(obj->iopgd);
	if (!IS_ALIGNED(pa, SZ_16K))
		return -EINVAL;

	iommu_write_reg(obj, MMU_SYS_SOFTRESET, MMU_SYSCONFIG);

	timeout = jiffies + msecs_to_jiffies(20);
	do {
		l = iommu_read_reg(obj, MMU_SYSSTATUS);
		if (l & MMU_SYS_RESETDONE)
			break;
	} while (time_after(jiffies, timeout));

	if (!(l & MMU_SYS_RESETDONE)) {
		dev_err(obj->dev, "can't take mmu out of reset\n");
		return -ENODEV;
	}

	l = iommu_read_reg(obj, MMU_REVISION);
	dev_info(obj->dev, "%s: version %d.%d\n", obj->name,
		 (l >> 4) & 0xf, l & 0xf);

	l = iommu_read_reg(obj, MMU_SYSCONFIG);
	l &= ~MMU_SYS_IDLE_MASK;
	l |= (MMU_SYS_IDLE_SMART | MMU_SYS_AUTOIDLE);
	iommu_write_reg(obj, l, MMU_SYSCONFIG);

	iommu_write_reg(obj, MMU_IRQ_MASK, MMU_IRQENABLE);
	iommu_write_reg(obj, pa, MMU_TTB);

	l = iommu_read_reg(obj, MMU_CNTL);
	l &= ~MMU_CNTL_MASK;
	l |= (MMU_CNTL_MMU_EN | MMU_CNTL_TWL_EN);
	iommu_write_reg(obj, l, MMU_CNTL);

	return 0;
}

static void omap2_iommu_disable(struct iommu *obj)
{
	u32 l = iommu_read_reg(obj, MMU_CNTL);

	l &= ~MMU_CNTL_MASK;
	iommu_write_reg(obj, l, MMU_CNTL);
	iommu_write_reg(obj, MMU_SYS_IDLE_FORCE, MMU_SYSCONFIG);

	dev_dbg(obj->dev, "%s is shutting down\n", obj->name);
}

static u32 omap2_iommu_fault_isr(struct iommu *obj, u32 *ra)
{
	int i;
	u32 stat, da;
	const char *err_msg[] =	{
		"tlb miss",
		"translation fault",
		"emulation miss",
		"table walk fault",
		"multi hit fault",
	};

	stat = iommu_read_reg(obj, MMU_IRQSTATUS);
	stat &= MMU_IRQ_MASK;
	if (!stat)
		return 0;

	da = iommu_read_reg(obj, MMU_FAULT_AD);
	*ra = da;

	dev_err(obj->dev, "%s:\tda:%08x ", __func__, da);

	for (i = 0; i < ARRAY_SIZE(err_msg); i++) {
		if (stat & (1 << i))
			printk("%s ", err_msg[i]);
	}
	printk("\n");

	iommu_write_reg(obj, stat, MMU_IRQSTATUS);
	return stat;
}

static void omap2_tlb_read_cr(struct iommu *obj, struct cr_regs *cr)
{
	cr->cam = iommu_read_reg(obj, MMU_READ_CAM);
	cr->ram = iommu_read_reg(obj, MMU_READ_RAM);
}

static void omap2_tlb_load_cr(struct iommu *obj, struct cr_regs *cr)
{
	iommu_write_reg(obj, cr->cam | MMU_CAM_V, MMU_CAM);
	iommu_write_reg(obj, cr->ram, MMU_RAM);
}

static u32 omap2_cr_to_virt(struct cr_regs *cr)
{
	u32 page_size = cr->cam & MMU_CAM_PGSZ_MASK;
	u32 mask = get_cam_va_mask(cr->cam & page_size);

	return cr->cam & mask;
}

static struct cr_regs *omap2_alloc_cr(struct iommu *obj, struct iotlb_entry *e)
{
	struct cr_regs *cr;

	if (e->da & ~(get_cam_va_mask(e->pgsz))) {
		dev_err(obj->dev, "%s:\twrong alignment: %08x\n", __func__,
			e->da);
		return ERR_PTR(-EINVAL);
	}

	cr = kmalloc(sizeof(*cr), GFP_KERNEL);
	if (!cr)
		return ERR_PTR(-ENOMEM);

	cr->cam = (e->da & MMU_CAM_VATAG_MASK) | e->prsvd | e->pgsz;
	cr->ram = e->pa | e->endian | e->elsz | e->mixed;

	return cr;
}

static inline int omap2_cr_valid(struct cr_regs *cr)
{
	return cr->cam & MMU_CAM_V;
}

static u32 omap2_get_pte_attr(struct iotlb_entry *e)
{
	u32 attr;

	attr = e->mixed << 5;
	attr |= e->endian;
	attr |= e->elsz >> 3;
	attr <<= ((e->pgsz & MMU_CAM_PGSZ_4K) ? 0 : 6);

	return attr;
}

static ssize_t omap2_dump_cr(struct iommu *obj, struct cr_regs *cr, char *buf)
{
	char *p = buf;

	/* FIXME: Need more detail analysis of cam/ram */
	p += sprintf(p, "%08x %08x\n", cr->cam, cr->ram);

	return p - buf;
}

#define pr_reg(name)							\
	p += sprintf(p, "%20s: %08x\n",					\
		     __stringify(name), iommu_read_reg(obj, MMU_##name));

static ssize_t omap2_iommu_dump_ctx(struct iommu *obj, char *buf)
{
	char *p = buf;

	pr_reg(REVISION);
	pr_reg(SYSCONFIG);
	pr_reg(SYSSTATUS);
	pr_reg(IRQSTATUS);
	pr_reg(IRQENABLE);
	pr_reg(WALKING_ST);
	pr_reg(CNTL);
	pr_reg(FAULT_AD);
	pr_reg(TTB);
	pr_reg(LOCK);
	pr_reg(LD_TLB);
	pr_reg(CAM);
	pr_reg(RAM);
	pr_reg(GFLUSH);
	pr_reg(FLUSH_ENTRY);
	pr_reg(READ_CAM);
	pr_reg(READ_RAM);
	pr_reg(EMU_FAULT_AD);

	return p - buf;
}

static void omap2_iommu_save_ctx(struct iommu *obj)
{
	int i;
	u32 *p = obj->ctx;

	for (i = 0; i < (MMU_REG_SIZE / sizeof(u32)); i++) {
		p[i] = iommu_read_reg(obj, i * sizeof(u32));
		dev_dbg(obj->dev, "%s\t[%02d] %08x\n", __func__, i, p[i]);
	}

	BUG_ON(p[0] != IOMMU_ARCH_VERSION);
}

static void omap2_iommu_restore_ctx(struct iommu *obj)
{
	int i;
	u32 *p = obj->ctx;

	for (i = 0; i < (MMU_REG_SIZE / sizeof(u32)); i++) {
		iommu_write_reg(obj, p[i], i * sizeof(u32));
		dev_dbg(obj->dev, "%s\t[%02d] %08x\n", __func__, i, p[i]);
	}

	BUG_ON(p[0] != IOMMU_ARCH_VERSION);
}

static void omap2_cr_to_e(struct cr_regs *cr, struct iotlb_entry *e)
{
	e->da		= cr->cam & MMU_CAM_VATAG_MASK;
	e->pa		= cr->ram & MMU_RAM_PADDR_MASK;
	e->valid	= cr->cam & MMU_CAM_V;
	e->pgsz		= cr->cam & MMU_CAM_PGSZ_MASK;
	e->endian	= cr->ram & MMU_RAM_ENDIAN_MASK;
	e->elsz		= cr->ram & MMU_RAM_ELSZ_MASK;
	e->mixed	= cr->ram & MMU_RAM_MIXED;
}

static const struct iommu_functions omap2_iommu_ops = {
	.version	= IOMMU_ARCH_VERSION,

	.enable		= omap2_iommu_enable,
	.disable	= omap2_iommu_disable,
	.fault_isr	= omap2_iommu_fault_isr,

	.tlb_read_cr	= omap2_tlb_read_cr,
	.tlb_load_cr	= omap2_tlb_load_cr,

	.cr_to_e	= omap2_cr_to_e,
	.cr_to_virt	= omap2_cr_to_virt,
	.alloc_cr	= omap2_alloc_cr,
	.cr_valid	= omap2_cr_valid,
	.dump_cr	= omap2_dump_cr,

	.get_pte_attr	= omap2_get_pte_attr,

	.save_ctx	= omap2_iommu_save_ctx,
	.restore_ctx	= omap2_iommu_restore_ctx,
	.dump_ctx	= omap2_iommu_dump_ctx,
};

static int __init omap2_iommu_init(void)
{
	return install_iommu_arch(&omap2_iommu_ops);
}
module_init(omap2_iommu_init);

static void __exit omap2_iommu_exit(void)
{
	uninstall_iommu_arch(&omap2_iommu_ops);
}
module_exit(omap2_iommu_exit);

MODULE_AUTHOR("Hiroshi DOYU, Paul Mundt and Toshihiro Kobayashi");
MODULE_DESCRIPTION("omap iommu: omap2/3 architecture specific functions");
MODULE_LICENSE("GPL v2");
+105 −0
Original line number Diff line number Diff line
/*
 * omap iommu: omap3 device registration
 *
 * Copyright (C) 2008-2009 Nokia Corporation
 *
 * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
 *
 * 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/platform_device.h>

#include <mach/iommu.h>

#define OMAP3_MMU1_BASE	0x480bd400
#define OMAP3_MMU2_BASE	0x5d000000
#define OMAP3_MMU1_IRQ	24
#define OMAP3_MMU2_IRQ	28


static unsigned long iommu_base[] __initdata = {
	OMAP3_MMU1_BASE,
	OMAP3_MMU2_BASE,
};

static int iommu_irq[] __initdata = {
	OMAP3_MMU1_IRQ,
	OMAP3_MMU2_IRQ,
};

static const struct iommu_platform_data omap3_iommu_pdata[] __initconst = {
	{
		.name = "isp",
		.nr_tlb_entries = 8,
		.clk_name = "cam_ick",
	},
#if defined(CONFIG_MPU_BRIDGE_IOMMU)
	{
		.name = "iva2",
		.nr_tlb_entries = 32,
		.clk_name = "iva2_ck",
	},
#endif
};
#define NR_IOMMU_DEVICES ARRAY_SIZE(omap3_iommu_pdata)

static struct platform_device *omap3_iommu_pdev[NR_IOMMU_DEVICES];

static int __init omap3_iommu_init(void)
{
	int i, err;

	for (i = 0; i < NR_IOMMU_DEVICES; i++) {
		struct platform_device *pdev;
		struct resource res[2];

		pdev = platform_device_alloc("omap-iommu", i);
		if (!pdev) {
			err = -ENOMEM;
			goto err_out;
		}

		memset(res, 0,  sizeof(res));
		res[0].start = iommu_base[i];
		res[0].end = iommu_base[i] + MMU_REG_SIZE - 1;
		res[0].flags = IORESOURCE_MEM;
		res[1].start = res[1].end = iommu_irq[i];
		res[1].flags = IORESOURCE_IRQ;

		err = platform_device_add_resources(pdev, res,
						    ARRAY_SIZE(res));
		if (err)
			goto err_out;
		err = platform_device_add_data(pdev, &omap3_iommu_pdata[i],
					       sizeof(omap3_iommu_pdata[0]));
		if (err)
			goto err_out;
		err = platform_device_add(pdev);
		if (err)
			goto err_out;
		omap3_iommu_pdev[i] = pdev;
	}
	return 0;

err_out:
	while (i--)
		platform_device_put(omap3_iommu_pdev[i]);
	return err;
}
module_init(omap3_iommu_init);

static void __exit omap3_iommu_exit(void)
{
	int i;

	for (i = 0; i < NR_IOMMU_DEVICES; i++)
		platform_device_unregister(omap3_iommu_pdev[i]);
}
module_exit(omap3_iommu_exit);

MODULE_AUTHOR("Hiroshi DOYU");
MODULE_DESCRIPTION("omap iommu: omap3 device registration");
MODULE_LICENSE("GPL v2");
+6 −0
Original line number Diff line number Diff line
@@ -110,6 +110,12 @@ static int remap_area_pages(unsigned long start, unsigned long pfn,
	return err;
}

int ioremap_page(unsigned long virt, unsigned long phys,
		 const struct mem_type *mtype)
{
	return remap_area_pages(virt, __phys_to_pfn(phys), PAGE_SIZE, mtype);
}
EXPORT_SYMBOL(ioremap_page);

void __check_kvm_seq(struct mm_struct *mm)
{
Loading