diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-12-05 14:44:20 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-12-05 14:44:20 -0800 |
commit | 75612d3929a4c52a5c39126b427865d6e583dff9 (patch) | |
tree | f66352e01255d2bb775542fb65e53676e9168470 | |
parent | c8c7af548d05efd37a5a7d14e2019317bca9bdf8 (diff) | |
download | ltsi-kernel-75612d3929a4c52a5c39126b427865d6e583dff9.tar.gz |
add more armadillo800 patches
7 files changed, 1796 insertions, 0 deletions
diff --git a/patches.armadillo800/iommu-shmobile-add-iommu-driver-for-renesas-ipmmu-modules.patch b/patches.armadillo800/iommu-shmobile-add-iommu-driver-for-renesas-ipmmu-modules.patch new file mode 100644 index 0000000000000..06ca44126dade --- /dev/null +++ b/patches.armadillo800/iommu-shmobile-add-iommu-driver-for-renesas-ipmmu-modules.patch @@ -0,0 +1,660 @@ +From dhobsong@igel.co.jp Wed Nov 21 23:30:36 2012 +From: Damian Hobson-Garcia <dhobsong@igel.co.jp> +Date: Thu, 22 Nov 2012 16:28:26 +0900 +Subject: [PATCH 3/7] iommu/shmobile: Add iommu driver for Renesas IPMMU modules +To: gregkh@linuxfoundation.org +Cc: ltsi-dev@lists.linuxfoundation.org, Hideki EIRAKU <hdk@igel.co.jp> +Message-ID: <1353569310-16564-4-git-send-email-dhobsong@igel.co.jp> + + +From: Hideki EIRAKU <hdk@igel.co.jp> + +This is the Renesas IPMMU driver and IOMMU API implementation. + +The IPMMU module supports the MMU function and the PMB function. The +MMU function provides address translation by pagetable compatible with +ARMv6. The PMB function provides address translation including +tile-linear translation. This patch implements the MMU function. + +The iommu driver does not register a platform driver directly because: +- the register space of the MMU function and the PMB function + have a common register (used for settings flush), so they should ideally + have a way to appropriately share this register. +- the MMU function uses the IOMMU API while the PMB function does not. +- the two functions may be used independently. + +Signed-off-by: Hideki EIRAKU <hdk@igel.co.jp> +--- + arch/arm/mach-shmobile/Kconfig | 6 + arch/arm/mach-shmobile/Makefile | 3 + arch/arm/mach-shmobile/include/mach/ipmmu.h | 16 + + arch/arm/mach-shmobile/ipmmu.c | 150 +++++++++++ + drivers/iommu/Kconfig | 56 ++++ + drivers/iommu/Makefile | 1 + drivers/iommu/shmobile-iommu.c | 352 ++++++++++++++++++++++++++++ + 7 files changed, 584 insertions(+) + create mode 100644 arch/arm/mach-shmobile/include/mach/ipmmu.h + create mode 100644 arch/arm/mach-shmobile/ipmmu.c + create mode 100644 drivers/iommu/shmobile-iommu.c + +--- a/arch/arm/mach-shmobile/Kconfig ++++ b/arch/arm/mach-shmobile/Kconfig +@@ -175,6 +175,12 @@ endmenu + config SH_CLK_CPG + bool + ++config SHMOBILE_IPMMU ++ bool ++ ++config SHMOBILE_IPMMU_TLB ++ bool ++ + source "drivers/sh/Kconfig" + + endif +--- a/arch/arm/mach-shmobile/Makefile ++++ b/arch/arm/mach-shmobile/Makefile +@@ -56,3 +56,6 @@ obj-$(CONFIG_MACH_ARMADILLO800EVA) += bo + # Framework support + obj-$(CONFIG_SMP) += $(smp-y) + obj-$(CONFIG_GENERIC_GPIO) += $(pfc-y) ++ ++# IPMMU/IPMMUI ++obj-$(CONFIG_SHMOBILE_IPMMU) += ipmmu.o +--- /dev/null ++++ b/arch/arm/mach-shmobile/include/mach/ipmmu.h +@@ -0,0 +1,16 @@ ++#ifdef CONFIG_SHMOBILE_IPMMU_TLB ++void ipmmu_tlb_flush(struct device *ipmmu_dev); ++void ipmmu_tlb_set(struct device *ipmmu_dev, unsigned long phys, int size, ++ int asid); ++void ipmmu_add_device(struct device *dev); ++int ipmmu_iommu_init(struct device *dev); ++#else ++static inline void ipmmu_add_device(struct device *dev) ++{ ++} ++ ++static int ipmmu_iommu_init(struct device *dev) ++{ ++ return -EINVAL; ++} ++#endif +--- /dev/null ++++ b/arch/arm/mach-shmobile/ipmmu.c +@@ -0,0 +1,150 @@ ++/* ++ * IPMMU/IPMMUI ++ * Copyright (C) 2012 Hideki EIRAKU ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/io.h> ++#include <linux/err.h> ++#include <linux/export.h> ++#include <linux/slab.h> ++#include <mach/ipmmu.h> ++ ++#define IMCTR1 0x000 ++#define IMCTR2 0x004 ++#define IMASID 0x010 ++#define IMTTBR 0x014 ++#define IMTTBCR 0x018 ++ ++#define IMCTR1_TLBEN (1 << 0) ++#define IMCTR1_FLUSH (1 << 1) ++ ++struct ipmmu_priv { ++ void __iomem *ipmmu_base; ++ int tlb_enabled; ++ struct mutex flush_lock; ++}; ++ ++static void ipmmu_reg_write(struct ipmmu_priv *priv, unsigned long reg_off, ++ unsigned long data) ++{ ++ iowrite32(data, priv->ipmmu_base + reg_off); ++} ++ ++void ipmmu_tlb_flush(struct device *dev) ++{ ++ struct ipmmu_priv *priv; ++ ++ if (!dev) ++ return; ++ priv = dev_get_drvdata(dev); ++ mutex_lock(&priv->flush_lock); ++ if (priv->tlb_enabled) ++ ipmmu_reg_write(priv, IMCTR1, IMCTR1_FLUSH | IMCTR1_TLBEN); ++ else ++ ipmmu_reg_write(priv, IMCTR1, IMCTR1_FLUSH); ++ mutex_unlock(&priv->flush_lock); ++} ++ ++void ipmmu_tlb_set(struct device *dev, unsigned long phys, int size, int asid) ++{ ++ struct ipmmu_priv *priv; ++ ++ if (!dev) ++ return; ++ priv = dev_get_drvdata(dev); ++ mutex_lock(&priv->flush_lock); ++ switch (size) { ++ default: ++ priv->tlb_enabled = 0; ++ break; ++ case 0x2000: ++ ipmmu_reg_write(priv, IMTTBCR, 1); ++ priv->tlb_enabled = 1; ++ break; ++ case 0x1000: ++ ipmmu_reg_write(priv, IMTTBCR, 2); ++ priv->tlb_enabled = 1; ++ break; ++ case 0x800: ++ ipmmu_reg_write(priv, IMTTBCR, 3); ++ priv->tlb_enabled = 1; ++ break; ++ case 0x400: ++ ipmmu_reg_write(priv, IMTTBCR, 4); ++ priv->tlb_enabled = 1; ++ break; ++ case 0x200: ++ ipmmu_reg_write(priv, IMTTBCR, 5); ++ priv->tlb_enabled = 1; ++ break; ++ case 0x100: ++ ipmmu_reg_write(priv, IMTTBCR, 6); ++ priv->tlb_enabled = 1; ++ break; ++ case 0x80: ++ ipmmu_reg_write(priv, IMTTBCR, 7); ++ priv->tlb_enabled = 1; ++ break; ++ } ++ ipmmu_reg_write(priv, IMTTBR, phys); ++ ipmmu_reg_write(priv, IMASID, asid); ++ mutex_unlock(&priv->flush_lock); ++} ++ ++static int __devinit ipmmu_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ struct ipmmu_priv *priv; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "cannot get platform resources\n"); ++ return -ENOENT; ++ } ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) { ++ dev_err(&pdev->dev, "cannot allocate device data\n"); ++ return -ENOMEM; ++ } ++ mutex_init(&priv->flush_lock); ++ priv->ipmmu_base = ioremap_nocache(res->start, resource_size(res)); ++ if (!priv->ipmmu_base) { ++ dev_err(&pdev->dev, "ioremap_nocache failed\n"); ++ kfree(priv); ++ return -ENOMEM; ++ } ++ platform_set_drvdata(pdev, priv); ++ ipmmu_reg_write(priv, IMCTR1, 0x0); /* disable TLB */ ++ ipmmu_reg_write(priv, IMCTR2, 0x0); /* disable PMB */ ++ ipmmu_iommu_init(&pdev->dev); ++ return 0; ++} ++ ++static struct platform_driver ipmmu_driver = { ++ .probe = ipmmu_probe, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "ipmmu", ++ }, ++}; ++ ++static int __init ipmmu_init(void) ++{ ++ return platform_driver_register(&ipmmu_driver); ++} ++subsys_initcall(ipmmu_init); +--- a/drivers/iommu/Kconfig ++++ b/drivers/iommu/Kconfig +@@ -162,4 +162,60 @@ config TEGRA_IOMMU_SMMU + space through the SMMU (System Memory Management Unit) + hardware included on Tegra SoCs. + ++config SHMOBILE_IOMMU ++ bool "IOMMU for Renesas IPMMU/IPMMUI" ++ default n ++ select IOMMU_API ++ select ARM_DMA_USE_IOMMU ++ select SHMOBILE_IPMMU ++ select SHMOBILE_IPMMU_TLB ++ ++choice ++ prompt "IPMMU/IPMMUI address space size" ++ default SHMOBILE_IOMMU_ADDRSIZE_2048MB ++ depends on SHMOBILE_IOMMU ++ help ++ This option sets IPMMU/IPMMUI address space size by ++ adjusting the 1st level page table size. The page table size ++ is calculated as follows: ++ ++ page table size = number of page table entries * 4 bytes ++ number of page table entries = address space size / 1 MiB ++ ++ For example, when the address space size is 2048 MiB, the ++ 1st level page table size is 8192 bytes. ++ ++ config SHMOBILE_IOMMU_ADDRSIZE_2048MB ++ bool "2 GiB" ++ ++ config SHMOBILE_IOMMU_ADDRSIZE_1024MB ++ bool "1 GiB" ++ ++ config SHMOBILE_IOMMU_ADDRSIZE_512MB ++ bool "512 MiB" ++ ++ config SHMOBILE_IOMMU_ADDRSIZE_256MB ++ bool "256 MiB" ++ ++ config SHMOBILE_IOMMU_ADDRSIZE_128MB ++ bool "128 MiB" ++ ++ config SHMOBILE_IOMMU_ADDRSIZE_64MB ++ bool "64 MiB" ++ ++ config SHMOBILE_IOMMU_ADDRSIZE_32MB ++ bool "32 MiB" ++ ++endchoice ++ ++config SHMOBILE_IOMMU_L1SIZE ++ int ++ default 8192 if SHMOBILE_IOMMU_ADDRSIZE_2048MB ++ default 4096 if SHMOBILE_IOMMU_ADDRSIZE_1024MB ++ default 2048 if SHMOBILE_IOMMU_ADDRSIZE_512MB ++ default 1024 if SHMOBILE_IOMMU_ADDRSIZE_256MB ++ default 512 if SHMOBILE_IOMMU_ADDRSIZE_128MB ++ default 256 if SHMOBILE_IOMMU_ADDRSIZE_64MB ++ default 128 if SHMOBILE_IOMMU_ADDRSIZE_32MB ++ + endif # IOMMU_SUPPORT +--- a/drivers/iommu/Makefile ++++ b/drivers/iommu/Makefile +@@ -10,3 +10,4 @@ obj-$(CONFIG_OMAP_IOVMM) += omap-iovmm.o + obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o + obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o + obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o ++obj-$(CONFIG_SHMOBILE_IOMMU) += shmobile-iommu.o +--- /dev/null ++++ b/drivers/iommu/shmobile-iommu.c +@@ -0,0 +1,352 @@ ++/* ++ * IOMMU for IPMMU/IPMMUI ++ * Copyright (C) 2012 Hideki EIRAKU ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ */ ++ ++#include <linux/io.h> ++#include <linux/dmapool.h> ++#include <linux/slab.h> ++#include <linux/platform_device.h> ++#include <linux/iommu.h> ++#include <linux/dma-mapping.h> ++#include <mach/ipmmu.h> ++#include <asm/dma-iommu.h> ++ ++#define L1_SIZE CONFIG_SHMOBILE_IOMMU_L1SIZE ++#define L1_LEN (L1_SIZE / 4) ++#define L1_ALIGN L1_SIZE ++#define L2_SIZE 0x400 ++#define L2_LEN (L2_SIZE / 4) ++#define L2_ALIGN L2_SIZE ++ ++struct shmobile_iommu_priv_pgtable { ++ uint32_t *pgtable; ++ dma_addr_t handle; ++}; ++ ++struct shmobile_iommu_priv { ++ struct shmobile_iommu_priv_pgtable l1, l2[L1_LEN]; ++ spinlock_t map_lock; ++ atomic_t active; ++}; ++ ++static struct dma_iommu_mapping *iommu_mapping; ++static struct device *ipmmu_devices; ++static struct dma_pool *l1pool, *l2pool; ++static spinlock_t lock; ++static DEFINE_SPINLOCK(lock_add); ++static struct shmobile_iommu_priv *attached; ++static int num_attached_devices; ++static struct device *ipmmu_access_device; ++ ++static int shmobile_iommu_domain_init(struct iommu_domain *domain) ++{ ++ struct shmobile_iommu_priv *priv; ++ int i; ++ ++ priv = kmalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ priv->l1.pgtable = dma_pool_alloc(l1pool, GFP_KERNEL, ++ &priv->l1.handle); ++ if (!priv->l1.pgtable) { ++ kfree(priv); ++ return -ENOMEM; ++ } ++ for (i = 0; i < L1_LEN; i++) ++ priv->l2[i].pgtable = NULL; ++ memset(priv->l1.pgtable, 0, L1_SIZE); ++ spin_lock_init(&priv->map_lock); ++ atomic_set(&priv->active, 0); ++ domain->priv = priv; ++ return 0; ++} ++ ++static void shmobile_iommu_domain_destroy(struct iommu_domain *domain) ++{ ++ struct shmobile_iommu_priv *priv = domain->priv; ++ int i; ++ ++ for (i = 0; i < L1_LEN; i++) { ++ if (priv->l2[i].pgtable) ++ dma_pool_free(l2pool, priv->l2[i].pgtable, ++ priv->l2[i].handle); ++ } ++ dma_pool_free(l1pool, priv->l1.pgtable, priv->l1.handle); ++ kfree(priv); ++ domain->priv = NULL; ++} ++ ++static int shmobile_iommu_attach_device(struct iommu_domain *domain, ++ struct device *dev) ++{ ++ struct shmobile_iommu_priv *priv = domain->priv; ++ int ret = -EBUSY; ++ ++ spin_lock(&lock); ++ if (attached != priv) { ++ if (attached) ++ goto err; ++ atomic_set(&priv->active, 1); ++ ipmmu_tlb_set(ipmmu_access_device, priv->l1.handle, L1_SIZE, ++ 0); ++ wmb(); ++ ipmmu_tlb_flush(ipmmu_access_device); ++ attached = priv; ++ num_attached_devices = 0; ++ } ++ num_attached_devices++; ++ ret = 0; ++err: ++ spin_unlock(&lock); ++ return ret; ++} ++ ++static void shmobile_iommu_detach_device(struct iommu_domain *domain, ++ struct device *dev) ++{ ++ struct shmobile_iommu_priv *priv = domain->priv; ++ ++ spin_lock(&lock); ++ atomic_set(&priv->active, 0); ++ num_attached_devices--; ++ if (!num_attached_devices) { ++ ipmmu_tlb_set(ipmmu_access_device, 0, 0, 0); ++ ipmmu_tlb_flush(ipmmu_access_device); ++ attached = NULL; ++ } ++ spin_unlock(&lock); ++} ++ ++static int ++l2alloc(struct shmobile_iommu_priv *priv, unsigned int l1index) ++{ ++ if (!priv->l2[l1index].pgtable) { ++ priv->l2[l1index].pgtable = dma_pool_alloc(l2pool, GFP_KERNEL, ++ &priv->l2[l1index].handle); ++ if (!priv->l2[l1index].pgtable) ++ return -ENOMEM; ++ memset(priv->l2[l1index].pgtable, 0, L2_SIZE); ++ } ++ priv->l1.pgtable[l1index] = priv->l2[l1index].handle | 0x1; ++ return 0; ++} ++ ++static void ++l2realfree(struct shmobile_iommu_priv_pgtable *l2) ++{ ++ if (l2->pgtable) ++ dma_pool_free(l2pool, l2->pgtable, l2->handle); ++} ++ ++static int ++l2free(struct shmobile_iommu_priv *priv, unsigned int l1index, ++ struct shmobile_iommu_priv_pgtable *l2) ++{ ++ priv->l1.pgtable[l1index] = 0; ++ if (priv->l2[l1index].pgtable) { ++ *l2 = priv->l2[l1index]; ++ priv->l2[l1index].pgtable = NULL; ++ } ++ return 0; ++} ++ ++static int shmobile_iommu_map(struct iommu_domain *domain, unsigned long iova, ++ phys_addr_t paddr, size_t size, int prot) ++{ ++ struct shmobile_iommu_priv_pgtable l2 = { .pgtable = NULL }; ++ struct shmobile_iommu_priv *priv = domain->priv; ++ unsigned int l1index, l2index, i; ++ int ret; ++ ++ l1index = iova >> 20; ++ switch (size) { ++ case 0x1000: ++ l2index = (iova >> 12) & 0xff; ++ spin_lock(&priv->map_lock); ++ ret = l2alloc(priv, l1index); ++ if (!ret) ++ priv->l2[l1index].pgtable[l2index] = paddr | 0xff2; ++ spin_unlock(&priv->map_lock); ++ break; ++ case 0x10000: ++ l2index = (iova >> 12) & 0xf0; ++ spin_lock(&priv->map_lock); ++ ret = l2alloc(priv, l1index); ++ if (!ret) { ++ for (i = 0; i < 0x10; i++) ++ priv->l2[l1index].pgtable[l2index + i] = ++ paddr | 0xff1; ++ } ++ spin_unlock(&priv->map_lock); ++ break; ++ case 0x100000: ++ spin_lock(&priv->map_lock); ++ l2free(priv, l1index, &l2); ++ priv->l1.pgtable[l1index] = paddr | 0xc02; ++ spin_unlock(&priv->map_lock); ++ ret = 0; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ if (!ret && atomic_read(&priv->active)) { ++ wmb(); ++ ipmmu_tlb_flush(ipmmu_access_device); ++ l2realfree(&l2); ++ } ++ return ret; ++} ++ ++static size_t shmobile_iommu_unmap(struct iommu_domain *domain, ++ unsigned long iova, size_t size) ++{ ++ struct shmobile_iommu_priv_pgtable l2 = { .pgtable = NULL }; ++ struct shmobile_iommu_priv *priv = domain->priv; ++ unsigned int l1index, l2index, i; ++ uint32_t l2entry = 0; ++ size_t ret = 0; ++ ++ l1index = iova >> 20; ++ if (!(iova & 0xFFFFF) && size >= 0x100000) { ++ spin_lock(&priv->map_lock); ++ l2free(priv, l1index, &l2); ++ spin_unlock(&priv->map_lock); ++ ret = 0x100000; ++ goto done; ++ } ++ l2index = (iova >> 12) & 0xff; ++ spin_lock(&priv->map_lock); ++ if (priv->l2[l1index].pgtable) ++ l2entry = priv->l2[l1index].pgtable[l2index]; ++ switch (l2entry & 3) { ++ case 1: ++ if (l2index & 0xf) ++ break; ++ for (i = 0; i < 0x10; i++) ++ priv->l2[l1index].pgtable[l2index + i] = 0; ++ ret = 0x10000; ++ break; ++ case 2: ++ priv->l2[l1index].pgtable[l2index] = 0; ++ ret = 0x1000; ++ break; ++ } ++ spin_unlock(&priv->map_lock); ++done: ++ if (ret && atomic_read(&priv->active)) { ++ wmb(); ++ ipmmu_tlb_flush(ipmmu_access_device); ++ l2realfree(&l2); ++ } ++ return ret; ++} ++ ++static phys_addr_t shmobile_iommu_iova_to_phys(struct iommu_domain *domain, ++ unsigned long iova) ++{ ++ struct shmobile_iommu_priv *priv = domain->priv; ++ uint32_t l1entry = 0, l2entry = 0; ++ unsigned int l1index, l2index; ++ ++ l1index = iova >> 20; ++ l2index = (iova >> 12) & 0xff; ++ spin_lock(&priv->map_lock); ++ if (priv->l2[l1index].pgtable) ++ l2entry = priv->l2[l1index].pgtable[l2index]; ++ else ++ l1entry = priv->l1.pgtable[l1index]; ++ spin_unlock(&priv->map_lock); ++ switch (l2entry & 3) { ++ case 1: ++ return (l2entry & ~0xffff) | (iova & 0xffff); ++ case 2: ++ return (l2entry & ~0xfff) | (iova & 0xfff); ++ default: ++ if ((l1entry & 3) == 2) ++ return (l1entry & ~0xfffff) | (iova & 0xfffff); ++ return 0; ++ } ++} ++ ++static struct iommu_ops shmobile_iommu_ops = { ++ .domain_init = shmobile_iommu_domain_init, ++ .domain_destroy = shmobile_iommu_domain_destroy, ++ .attach_dev = shmobile_iommu_attach_device, ++ .detach_dev = shmobile_iommu_detach_device, ++ .map = shmobile_iommu_map, ++ .unmap = shmobile_iommu_unmap, ++ .iova_to_phys = shmobile_iommu_iova_to_phys, ++ .pgsize_bitmap = 0x111000, ++}; ++ ++static int shmobile_iommu_attach_all_devices(void) ++{ ++ struct device *dev; ++ int ret = 0; ++ ++ spin_lock(&lock_add); ++ iommu_mapping = arm_iommu_create_mapping(&platform_bus_type, 0x0, ++ L1_LEN << 20, 0); ++ if (IS_ERR_OR_NULL(iommu_mapping)) { ++ ret = PTR_ERR(iommu_mapping); ++ goto err; ++ } ++ for (dev = ipmmu_devices; dev; dev = dev->archdata.iommu) { ++ if (arm_iommu_attach_device(dev, iommu_mapping)) ++ pr_err("arm_iommu_attach_device failed\n"); ++ } ++err: ++ spin_unlock(&lock_add); ++ return 0; ++} ++ ++void ipmmu_add_device(struct device *dev) ++{ ++ spin_lock(&lock_add); ++ dev->archdata.iommu = ipmmu_devices; ++ ipmmu_devices = dev; ++ if (!IS_ERR_OR_NULL(iommu_mapping)) { ++ if (arm_iommu_attach_device(dev, iommu_mapping)) ++ pr_err("arm_iommu_attach_device failed\n"); ++ } ++ spin_unlock(&lock_add); ++} ++ ++int ipmmu_iommu_init(struct device *dev) ++{ ++ dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); ++ l1pool = dma_pool_create("shmobile-iommu-pgtable1", dev, ++ L1_SIZE, L1_ALIGN, 0); ++ if (!l1pool) ++ goto nomem_pool1; ++ l2pool = dma_pool_create("shmobile-iommu-pgtable2", dev, ++ L2_SIZE, L2_ALIGN, 0); ++ if (!l2pool) ++ goto nomem_pool2; ++ spin_lock_init(&lock); ++ attached = NULL; ++ ipmmu_access_device = dev; ++ bus_set_iommu(&platform_bus_type, &shmobile_iommu_ops); ++ if (shmobile_iommu_attach_all_devices()) ++ pr_err("shmobile_iommu_attach_all_devices failed\n"); ++ return 0; ++nomem_pool2: ++ dma_pool_destroy(l1pool); ++nomem_pool1: ++ return -ENOMEM; ++} diff --git a/patches.armadillo800/shmobile-armadillo800eva-enable-iommu-support.patch b/patches.armadillo800/shmobile-armadillo800eva-enable-iommu-support.patch new file mode 100644 index 0000000000000..95a54e889c618 --- /dev/null +++ b/patches.armadillo800/shmobile-armadillo800eva-enable-iommu-support.patch @@ -0,0 +1,90 @@ +From dhobsong@igel.co.jp Wed Nov 21 23:30:38 2012 +From: Damian Hobson-Garcia <dhobsong@igel.co.jp> +Date: Thu, 22 Nov 2012 16:28:27 +0900 +Subject: [PATCH 4/7] shmobile: armadillo800eva enable IOMMU support +To: gregkh@linuxfoundation.org +Cc: ltsi-dev@lists.linuxfoundation.org, Damian Hobson-Garcia <dhobsong@igel.co.jp> +Message-ID: <1353569310-16564-5-git-send-email-dhobsong@igel.co.jp> + + +Add the IPMMU device and register the LCDC, VPU, and VIO devices to allocate +DMA memory via the IPMMU. + +Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp> +--- + arch/arm/mach-shmobile/board-armadillo800eva.c | 3 +++ + arch/arm/mach-shmobile/setup-r8a7740.c | 23 +++++++++++++++++++++++ + 2 files changed, 26 insertions(+) + +--- a/arch/arm/mach-shmobile/board-armadillo800eva.c ++++ b/arch/arm/mach-shmobile/board-armadillo800eva.c +@@ -40,6 +40,7 @@ + #include <mach/common.h> + #include <mach/irqs.h> + #include <mach/r8a7740.h> ++#include <mach/ipmmu.h> + #include <media/mt9t112.h> + #include <media/sh_mobile_ceu.h> + #include <media/soc_camera.h> +@@ -1177,6 +1178,8 @@ static void __init eva_init(void) + + r8a7740_add_standard_devices(); + ++ ipmmu_add_device(&lcdc0_device.dev); ++ + platform_add_devices(eva_devices, + ARRAY_SIZE(eva_devices)); + +--- a/arch/arm/mach-shmobile/setup-r8a7740.c ++++ b/arch/arm/mach-shmobile/setup-r8a7740.c +@@ -35,6 +35,7 @@ + #include <mach/r8a7740.h> + #include <mach/pm-rmobile.h> + #include <mach/common.h> ++#include <mach/ipmmu.h> + #include <mach/irqs.h> + #include <asm/mach-types.h> + #include <asm/mach/map.h> +@@ -359,6 +360,23 @@ static struct platform_device cmt10_devi + .num_resources = ARRAY_SIZE(cmt10_resources), + }; + ++/* IPMMUI (an IPMMU module for ICB/LMB) */ ++static struct resource ipmmu_resources[] = { ++ [0] = { ++ .name = "IPMMUI", ++ .start = 0xfe951000, ++ .end = 0xfe9510ff, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static struct platform_device ipmmu_device = { ++ .name = "ipmmu", ++ .id = -1, ++ .resource = ipmmu_resources, ++ .num_resources = ARRAY_SIZE(ipmmu_resources), ++}; ++ + static struct platform_device *r8a7740_early_devices[] __initdata = { + &scif0_device, + &scif1_device, +@@ -373,6 +391,7 @@ static struct platform_device *r8a7740_e + &vpu_device, + &vpc_device, + &vio_device, ++ &ipmmu_device, + + }; + +@@ -778,6 +797,10 @@ void __init r8a7740_add_standard_devices + /* add devices */ + platform_add_devices(r8a7740_early_devices, + ARRAY_SIZE(r8a7740_early_devices)); ++ ++ ipmmu_add_device(&vpu_device.dev); ++ ipmmu_add_device(&vio_device.dev); ++ + platform_add_devices(r8a7740_late_devices, + ARRAY_SIZE(r8a7740_late_devices)); + diff --git a/patches.armadillo800/shmobile-armadillo800eva-set-lcd-color-depth-to-32-bpp.patch b/patches.armadillo800/shmobile-armadillo800eva-set-lcd-color-depth-to-32-bpp.patch new file mode 100644 index 0000000000000..b4a8fd02954b6 --- /dev/null +++ b/patches.armadillo800/shmobile-armadillo800eva-set-lcd-color-depth-to-32-bpp.patch @@ -0,0 +1,25 @@ +From dhobsong@igel.co.jp Wed Nov 21 23:30:33 2012 +From: Damian Hobson-Garcia <dhobsong@igel.co.jp> +Date: Thu, 22 Nov 2012 16:28:25 +0900 +Subject: [PATCH 2/7] shmobile: armadillo800eva: Set LCD color depth to 32 bpp +To: gregkh@linuxfoundation.org +Cc: ltsi-dev@lists.linuxfoundation.org, Damian Hobson-Garcia <dhobsong@igel.co.jp> +Message-ID: <1353569310-16564-3-git-send-email-dhobsong@igel.co.jp> + + +Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp> +--- + arch/arm/mach-shmobile/board-armadillo800eva.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm/mach-shmobile/board-armadillo800eva.c ++++ b/arch/arm/mach-shmobile/board-armadillo800eva.c +@@ -385,7 +385,7 @@ static struct sh_mobile_lcdc_info lcdc0_ + .clock_source = LCDC_CLK_BUS, + .ch[0] = { + .chan = LCDC_CHAN_MAINLCD, +- .fourcc = V4L2_PIX_FMT_RGB565, ++ .fourcc = V4L2_PIX_FMT_BGR32, + .interface_type = RGB24, + .clock_divider = 5, + .flags = 0, diff --git a/patches.armadillo800/shmobile-ipmmu-add-basic-pmb-support.patch b/patches.armadillo800/shmobile-ipmmu-add-basic-pmb-support.patch new file mode 100644 index 0000000000000..41a7ee086626e --- /dev/null +++ b/patches.armadillo800/shmobile-ipmmu-add-basic-pmb-support.patch @@ -0,0 +1,676 @@ +From dhobsong@igel.co.jp Wed Nov 21 23:30:40 2012 +From: Damian Hobson-Garcia <dhobsong@igel.co.jp> +Date: Thu, 22 Nov 2012 16:28:28 +0900 +Subject: [PATCH 5/7] shmobile: ipmmu: Add basic PMB support +To: gregkh@linuxfoundation.org +Cc: ltsi-dev@lists.linuxfoundation.org, Damian Hobson-Garcia <dhobsong@igel.co.jp> +Message-ID: <1353569310-16564-6-git-send-email-dhobsong@igel.co.jp> + + +Currently only the userspace API via character device is supported. +All register access and hardware dependent functionality is +provided by the IPMMU driver, which is shared with the IOMMU/TLB API. + +Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp> +--- + arch/arm/mach-shmobile/Kconfig | 7 + arch/arm/mach-shmobile/Makefile | 1 + arch/arm/mach-shmobile/include/mach/ipmmu.h | 34 +++ + arch/arm/mach-shmobile/ipmmu-pmb.c | 269 ++++++++++++++++++++++++++++ + arch/arm/mach-shmobile/ipmmu.c | 236 +++++++++++++++++++++++- + include/linux/ipmmu.h | 29 +++ + 6 files changed, 565 insertions(+), 11 deletions(-) + create mode 100644 arch/arm/mach-shmobile/ipmmu-pmb.c + create mode 100644 include/linux/ipmmu.h + +--- a/arch/arm/mach-shmobile/Kconfig ++++ b/arch/arm/mach-shmobile/Kconfig +@@ -181,6 +181,13 @@ config SHMOBILE_IPMMU + config SHMOBILE_IPMMU_TLB + bool + ++config SHMOBILE_PMB ++ bool "IPMMU PMB driver" ++ default n ++ select SHMOBILE_IPMMU ++ help ++ This enables build of the IPMMU PMB driver. ++ + source "drivers/sh/Kconfig" + + endif +--- a/arch/arm/mach-shmobile/Makefile ++++ b/arch/arm/mach-shmobile/Makefile +@@ -59,3 +59,4 @@ obj-$(CONFIG_GENERIC_GPIO) += $(pfc-y) + + # IPMMU/IPMMUI + obj-$(CONFIG_SHMOBILE_IPMMU) += ipmmu.o ++obj-$(CONFIG_SHMOBILE_PMB) += ipmmu-pmb.o +--- a/arch/arm/mach-shmobile/include/mach/ipmmu.h ++++ b/arch/arm/mach-shmobile/include/mach/ipmmu.h +@@ -1,5 +1,12 @@ +-#ifdef CONFIG_SHMOBILE_IPMMU_TLB ++#include <linux/ipmmu.h> ++#ifndef __SHMOBILE_IPMMU_H__ ++#define __SHMOBILE_IPMMU_H__ ++ ++#ifdef CONFIG_SHMOBILE_IPMMU + void ipmmu_tlb_flush(struct device *ipmmu_dev); ++#endif ++ ++#ifdef CONFIG_SHMOBILE_IPMMU_TLB + void ipmmu_tlb_set(struct device *ipmmu_dev, unsigned long phys, int size, + int asid); + void ipmmu_add_device(struct device *dev); +@@ -9,8 +16,31 @@ static inline void ipmmu_add_device(stru + { + } + +-static int ipmmu_iommu_init(struct device *dev) ++static inline int ipmmu_iommu_init(struct device *dev) + { + return -EINVAL; + } + #endif ++#ifdef CONFIG_SHMOBILE_PMB ++/* Access functions used by PMB device */ ++void handle_free(struct device *dev, unsigned long handle, int size_mb); ++unsigned long handle_alloc(struct device *dev, int size_mb); ++int ipmmu_pmb_set_addr(struct device *dev, int index, unsigned long addr, ++ int enabled); ++int ipmmu_pmb_set_data(struct device *dev, int index, ++ struct ipmmu_pmb_info *info, ++ struct pmb_tile_info *tile); ++int ipmmu_pmb_enable(struct device *dev, int index); ++/* PMB initialization */ ++void *ipmmu_pmb_init(struct device *dev); ++void ipmmu_pmb_deinit(void *pmb_priv); ++#else ++static inline void *ipmmu_pmb_init(struct device *dev) ++{ ++ return NULL; ++} ++static inline void ipmmu_pmb_deinit(void *pmb_priv) ++{ ++} ++#endif ++#endif /* __SHMOBILE_IPMMU_H__ */ +--- /dev/null ++++ b/arch/arm/mach-shmobile/ipmmu-pmb.c +@@ -0,0 +1,269 @@ ++/* ++ * IPMMU-PMB driver ++ * Copyright (C) 2012 Damian Hobson-Garcia ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/io.h> ++#include <linux/err.h> ++#include <linux/export.h> ++#include <linux/cdev.h> ++#include <linux/fs.h> ++#include <linux/slab.h> ++#include <linux/ipmmu.h> ++#include <mach/ipmmu.h> ++#include <asm/uaccess.h> ++ ++#define PMB_DEVICE_NAME "pmb" ++ ++#define PMB_NR 16 ++struct ipmmu_pmb_data { ++ struct ipmmu_pmb_priv *priv; ++ struct ipmmu_pmb_info info; ++ struct pmb_tile_info tile; ++ unsigned long handle; ++ int index; ++}; ++ ++struct ipmmu_pmb_priv { ++ struct cdev cdev; ++ struct class *pmb_class; ++ dev_t pmb_dev; ++ unsigned long busy_pmbs; ++ struct mutex pmb_lock; ++ struct ipmmu_pmb_data pmbs[PMB_NR]; ++ struct device *ipmmu_dev; ++ int pmb_enabled; ++}; ++ ++struct ipmmu_pmb_priv *static_priv; ++ ++static int set_pmb(struct ipmmu_pmb_data *data, ++ struct ipmmu_pmb_info *info) ++{ ++ struct ipmmu_pmb_priv *priv = data->priv; ++ unsigned long data_mask; ++ int err; ++ ++ if (!info->enabled) { ++ if (data->handle) { ++ handle_free(priv->ipmmu_dev, data->handle, ++ data->info.size_mb); ++ data->handle = 0; ++ } ++ data->info = *info; ++ ipmmu_pmb_set_data(priv->ipmmu_dev, data->index, NULL, NULL); ++ ipmmu_pmb_set_addr(priv->ipmmu_dev, data->index, 0, 0); ++ ipmmu_tlb_flush(priv->ipmmu_dev); ++ return 0; ++ } ++ ++ if (data->info.enabled) { ++ err = -EBUSY; ++ goto err_out; ++ } ++ ++ data_mask = ~((info->size_mb) - 1); ++ ++ if (info->paddr & ~(data_mask)) { ++ err = -EINVAL; ++ goto err_out; ++ } ++ ++ data->handle = handle_alloc(priv->ipmmu_dev, info->size_mb); ++ ++ if (!data->handle) { ++ err = -ENOMEM; ++ goto err_out; ++ } ++ ++ data->info = *info; ++ ++ ipmmu_pmb_set_addr(priv->ipmmu_dev, data->index, data->handle, 1); ++ ipmmu_pmb_set_data(priv->ipmmu_dev, data->index, &data->info, ++ &data->tile); ++ ++ if (!data->priv->pmb_enabled) { ++ ipmmu_pmb_enable(priv->ipmmu_dev, 1); ++ data->priv->pmb_enabled = 1; ++ } ++ ++ ipmmu_tlb_flush(priv->ipmmu_dev); ++ ++ return 0; ++ ++err_out: ++ info->enabled = 0; ++ return err; ++} ++ ++static int set_tile(struct ipmmu_pmb_data *data, ++ struct pmb_tile_info *tile) ++{ ++ struct ipmmu_pmb_priv *priv = data->priv; ++ data->tile = *tile; ++ return ipmmu_pmb_set_data(priv->ipmmu_dev, data->index, &data->info, ++ &data->tile); ++} ++ ++static int ipmmu_pmb_open(struct inode *inode, struct file *filp) ++{ ++ struct ipmmu_pmb_priv *priv; ++ int idx; ++ priv = container_of(inode->i_cdev, struct ipmmu_pmb_priv, ++ cdev); ++ ++ mutex_lock(&priv->pmb_lock); ++ idx = find_first_zero_bit(&priv->busy_pmbs, PMB_NR); ++ if (idx == PMB_NR) ++ return -EBUSY; ++ ++ __set_bit(idx, &priv->busy_pmbs); ++ mutex_unlock(&priv->pmb_lock); ++ priv->pmbs[idx].index = idx; ++ priv->pmbs[idx].priv = priv; ++ filp->private_data = &priv->pmbs[idx]; ++ return 0; ++} ++ ++static int ipmmu_pmb_release(struct inode *inode, struct file *filp) ++{ ++ struct ipmmu_pmb_data *pmb; ++ pmb = filp->private_data; ++ if (pmb->info.enabled) { ++ pmb->info.enabled = 0; ++ set_pmb(pmb, &pmb->info); ++ } ++ ++ mutex_lock(&pmb->priv->pmb_lock); ++ __clear_bit(pmb->index, &pmb->priv->busy_pmbs); ++ mutex_unlock(&pmb->priv->pmb_lock); ++ return 0; ++} ++ ++static long ipmmu_pmb_ioctl(struct file *filp, unsigned int cmd_in, ++ unsigned long arg) ++{ ++ struct ipmmu_pmb_data *pmb; ++ struct ipmmu_pmb_info user_set; ++ struct pmb_tile_info user_tile; ++ long ret = -EINVAL; ++ pmb = filp->private_data; ++ switch (cmd_in) { ++ case IPMMU_GET_PMB: ++ ret = copy_to_user((char *)arg, &pmb->info, sizeof(pmb->info)); ++ break; ++ case IPMMU_SET_PMB: ++ ret = copy_from_user(&user_set, (char *)arg, sizeof(user_set)); ++ if (ret) ++ break; ++ ret = set_pmb(pmb, &user_set); ++ if (!ret) ++ pmb->info = user_set; ++ break; ++ case IPMMU_GET_PMB_HANDLE: ++ ret = copy_to_user((char *)arg, &pmb->handle, ++ sizeof(pmb->handle)); ++ break; ++ case IPMMU_GET_PMB_TL: ++ ret = copy_to_user((char *)arg, &pmb->tile, sizeof(pmb->tile)); ++ break; ++ case IPMMU_SET_PMB_TL: ++ ret = copy_from_user(&user_tile, (char *)arg, ++ sizeof(user_tile)); ++ if (ret) ++ break; ++ ret = set_tile(pmb, &user_tile); ++ if (!ret) ++ pmb->tile = user_tile; ++ break; ++ } ++ return ret; ++} ++ ++static const struct file_operations ipmmu_pmb_fops = { ++ .owner = THIS_MODULE, ++ .open = ipmmu_pmb_open, ++ .release = ipmmu_pmb_release, ++ .unlocked_ioctl = ipmmu_pmb_ioctl, ++}; ++ ++void ipmmu_pmb_deinit(void *arg) ++{ ++ struct ipmmu_pmb_priv *priv = arg; ++ ++ if (!priv || IS_ERR(priv)) ++ return; ++ ++ cdev_del(&priv->cdev); ++ device_destroy(priv->pmb_class, priv->pmb_dev); ++ unregister_chrdev_region(priv->pmb_dev, 1); ++ class_destroy(priv->pmb_class); ++ kfree(priv); ++} ++ ++void *ipmmu_pmb_init(struct device *ipmmu_dev) ++{ ++ int err = -ENOENT; ++ struct ipmmu_pmb_priv *priv; ++ ++ priv = kzalloc(sizeof(struct ipmmu_pmb_priv), GFP_KERNEL); ++ if (!priv) { ++ dev_err(ipmmu_dev, "cannot allocate device data\n"); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ priv->ipmmu_dev = ipmmu_dev; ++ static_priv = priv; ++ ++ mutex_init(&priv->pmb_lock); ++ ++ priv->pmb_class = class_create(THIS_MODULE, "ipmmu-pmb"); ++ if (!priv->pmb_class) ++ goto free_priv; ++ ++ err = alloc_chrdev_region(&priv->pmb_dev, 0, 1, PMB_DEVICE_NAME); ++ if (err) { ++ dev_err(ipmmu_dev, "cannot allocate device num\n"); ++ goto destroy_class; ++ } ++ ++ if (!device_create(priv->pmb_class, ipmmu_dev, priv->pmb_dev, priv, ++ "pmb")) ++ goto unregister_region; ++ ++ cdev_init(&priv->cdev, &ipmmu_pmb_fops); ++ priv->cdev.owner = THIS_MODULE; ++ priv->cdev.ops = &ipmmu_pmb_fops; ++ err = cdev_add(&priv->cdev, priv->pmb_dev, 1); ++ if (err) { ++ dev_err(ipmmu_dev, "cannot add ipmmu_pmb device\n"); ++ goto destroy_device; ++ } ++ ++ return priv; ++ ++destroy_device: ++ device_destroy(priv->pmb_class, priv->pmb_dev); ++unregister_region: ++ unregister_chrdev_region(priv->pmb_dev, 1); ++destroy_class: ++ class_destroy(priv->pmb_class); ++free_priv: ++ kfree(priv); ++ return ERR_PTR(err); ++} +--- a/arch/arm/mach-shmobile/ipmmu.c ++++ b/arch/arm/mach-shmobile/ipmmu.c +@@ -23,28 +23,242 @@ + #include <linux/export.h> + #include <linux/slab.h> + #include <mach/ipmmu.h> ++#include <linux/ipmmu.h> + +-#define IMCTR1 0x000 +-#define IMCTR2 0x004 +-#define IMASID 0x010 +-#define IMTTBR 0x014 +-#define IMTTBCR 0x018 +- ++#define IMCTR1 0x00 + #define IMCTR1_TLBEN (1 << 0) + #define IMCTR1_FLUSH (1 << 1) ++#define IMCTR2 0x04 ++#define IMCTR2_PMBEN 0x01 ++#define IMASID 0x010 ++#define IMTTBR 0x014 ++#define IMTTBCR 0x018 ++#define IMPMBA_BASE 0x80 ++#define IMPBMA_V (1 << 8) ++#define IMPMBD_BASE 0xC0 ++#define IMPBMD_V (1 << 8) ++#define IMPBMD_SZ_16M 0x00 ++#define IMPBMD_SZ_64M 0x10 ++#define IMPBMD_SZ_128M 0x80 ++#define IMPBMD_SZ_512M 0x90 ++#define IMPBMD_BV (1 << 9) ++#define IMPBMD_TBM_MASK (7 << 12) ++#define IMPBMD_TBM_POS 12 ++#define IMPBMD_HBM_MASK (7 << 16) ++#define IMPBMD_HBM_POS 16 ++#define IMPBMD_VBM_MASK (7 << 20) ++#define IMPBMD_VBM_POS 20 ++ ++#define IMPBMA(x) (IMPMBA_BASE + 0x4*x) ++#define IMPBMD(x) (IMPMBD_BASE + 0x4*x) ++ ++/* the smallest size that can be reserverd in the pmb */ ++#define PMB_GRANULARITY (16 << 20) ++#define PMB_START_ADDR 0x80000000 ++#define PMB_SIZE 0x40000000 ++#define NUM_BITS(x) ((x) / PMB_GRANULARITY) ++#define NR_BITMAPS ((NUM_BITS(PMB_SIZE) + BITS_PER_LONG - 1) \ ++ >> ilog2(BITS_PER_LONG)) + + struct ipmmu_priv { +- void __iomem *ipmmu_base; +- int tlb_enabled; +- struct mutex flush_lock; ++ struct device *dev; ++ void __iomem *ipmmu_base; ++ int tlb_enabled; ++ struct mutex flush_lock; ++ struct mutex alloc_lock; ++ unsigned long alloc_bitmaps[NR_BITMAPS]; ++ void *pmb_priv; + }; + ++static int valid_size(int size_mb) ++{ ++ switch (size_mb) { ++ case 16: ++ case 64: ++ case 128: ++ case 512: ++ return 1; ++ } ++ return 0; ++ ++} ++unsigned long handle_alloc(struct device *dev, ++ int size_mb) ++{ ++ int i; ++ int idx; ++ unsigned long tmp_bitmap; ++ unsigned long alloc_mask; ++ unsigned long align_mask; ++ int alloc_bits; ++ struct ipmmu_priv *priv; ++ ++ if (!valid_size(size_mb)) ++ return -1; ++ ++ priv = dev_get_drvdata(dev); ++ ++ alloc_bits = NUM_BITS(size_mb << 20); ++ alloc_mask = alloc_bits < BITS_PER_LONG ? ++ (1 << alloc_bits) - 1 : -1; ++ ++ ++ align_mask = alloc_mask - 1; ++ for (i = BITS_PER_LONG >> 1; i >= alloc_bits; i = i >> 1) ++ align_mask = align_mask | (align_mask << i); ++ ++ mutex_lock(&priv->alloc_lock); ++ for (i = 0; i < NR_BITMAPS; i++) { ++ tmp_bitmap = priv->alloc_bitmaps[i]; ++ tmp_bitmap |= align_mask; ++ idx = 0; ++ while (idx < BITS_PER_LONG) { ++ idx = find_next_zero_bit(&tmp_bitmap, BITS_PER_LONG, ++ idx); ++ if (!((alloc_mask << idx) & priv->alloc_bitmaps[i]) || ++ (idx == BITS_PER_LONG)) ++ break; ++ idx++; ++ } ++ if (idx < BITS_PER_LONG) ++ break; ++ } ++ if (i == NR_BITMAPS) { ++ mutex_unlock(&priv->alloc_lock); ++ return 0; ++ } ++ ++ priv->alloc_bitmaps[i] |= (alloc_mask << idx); ++ mutex_unlock(&priv->alloc_lock); ++ ++ return PMB_START_ADDR + (i * BITS_PER_LONG + idx) * PMB_GRANULARITY; ++} ++ ++void handle_free(struct device *dev, ++ unsigned long handle, ++ int size_mb) ++{ ++ int idx; ++ unsigned long offset; ++ unsigned long alloc_bits; ++ unsigned long alloc_mask; ++ struct ipmmu_priv *priv; ++ ++ priv = dev_get_drvdata(dev); ++ ++ alloc_bits = NUM_BITS(size_mb << 20); ++ alloc_mask = alloc_bits < BITS_PER_LONG ? ++ (1 << alloc_bits) - 1 : -1; ++ offset = handle - PMB_START_ADDR; ++ offset /= PMB_GRANULARITY; ++ idx = offset & (BITS_PER_LONG - 1); ++ offset = offset / BITS_PER_LONG; ++ mutex_lock(&priv->alloc_lock); ++ priv->alloc_bitmaps[offset] &= ~(alloc_mask << idx); ++ mutex_unlock(&priv->alloc_lock); ++} ++ + static void ipmmu_reg_write(struct ipmmu_priv *priv, unsigned long reg_off, + unsigned long data) + { + iowrite32(data, priv->ipmmu_base + reg_off); + } + ++int ipmmu_pmb_enable(struct device *dev, ++ int enable) ++{ ++ ++ struct ipmmu_priv *priv; ++ ++ priv = dev_get_drvdata(dev); ++ ipmmu_reg_write(priv, IMCTR2, enable ? IMCTR2_PMBEN : 0); ++ return 0; ++ ++} ++int ipmmu_pmb_set_addr(struct device *dev, ++ int index, ++ unsigned long addr, ++ int enabled) ++{ ++ ++ struct ipmmu_priv *priv; ++ ++ priv = dev_get_drvdata(dev); ++ if (!enabled) { ++ ipmmu_reg_write(priv, IMPBMA(index), 0); ++ return 0; ++ } ++ ++ ipmmu_reg_write(priv, IMPBMA(index), addr | ++ IMPBMD_V); ++ return 0; ++ ++} ++ ++int ipmmu_pmb_set_data(struct device *dev, ++ int index, ++ struct ipmmu_pmb_info *info, ++ struct pmb_tile_info *tile) ++{ ++ int vbm, hbm, tbm; ++ int w, h; ++ unsigned long temp; ++ struct ipmmu_priv *priv; ++ ++ priv = dev_get_drvdata(dev); ++ ++ if (!info || !info->enabled) { ++ ipmmu_reg_write(priv, IMPBMD(index), 0); ++ return 0; ++ } ++ ++ temp = info->paddr; ++ ++ switch (info->size_mb) { ++ case 16: ++ temp |= IMPBMD_SZ_16M; ++ break; ++ case 64: ++ temp |= IMPBMD_SZ_64M; ++ break; ++ case 128: ++ temp |= IMPBMD_SZ_128M; ++ break; ++ case 512: ++ temp |= IMPBMD_SZ_512M; ++ break; ++ default: ++ break; ++ } ++ ++ temp |= IMPBMD_V; ++ ++ if (!tile || !tile->enabled) { ++ ipmmu_reg_write(priv, IMPBMD(index), temp); ++ return 0; ++ } ++ ++ w = tile->tile_width; ++ h = tile->tile_height; ++ ++ if (w & (w - 1) || h & (h - 1)) ++ return -EINVAL; ++ ++ tbm = ilog2(tile->tile_width); ++ vbm = ilog2(tile->tile_height) - 1; ++ hbm = ilog2(tile->buffer_pitch) - tbm - 1; ++ tbm -= 4; ++ ++ temp |= (tbm << IMPBMD_TBM_POS) & IMPBMD_TBM_MASK; ++ temp |= (vbm << IMPBMD_VBM_POS) & IMPBMD_VBM_MASK; ++ temp |= (hbm << IMPBMD_HBM_POS) & IMPBMD_HBM_MASK; ++ temp |= IMPBMD_BV; ++ ipmmu_reg_write(priv, IMPBMD(index), ++ temp); ++ ipmmu_tlb_flush(priv->dev); ++ return 0; ++} ++ + void ipmmu_tlb_flush(struct device *dev) + { + struct ipmmu_priv *priv; +@@ -122,6 +336,8 @@ static int __devinit ipmmu_probe(struct + return -ENOMEM; + } + mutex_init(&priv->flush_lock); ++ mutex_init(&priv->alloc_lock); ++ + priv->ipmmu_base = ioremap_nocache(res->start, resource_size(res)); + if (!priv->ipmmu_base) { + dev_err(&pdev->dev, "ioremap_nocache failed\n"); +@@ -129,9 +345,11 @@ static int __devinit ipmmu_probe(struct + return -ENOMEM; + } + platform_set_drvdata(pdev, priv); ++ priv->dev = &pdev->dev; + ipmmu_reg_write(priv, IMCTR1, 0x0); /* disable TLB */ + ipmmu_reg_write(priv, IMCTR2, 0x0); /* disable PMB */ + ipmmu_iommu_init(&pdev->dev); ++ priv->pmb_priv = ipmmu_pmb_init(&pdev->dev); + return 0; + } + +--- /dev/null ++++ b/include/linux/ipmmu.h +@@ -0,0 +1,29 @@ ++#ifndef __LINUX_IPMMU_PMB_H__ ++#define __LINUX_IPMMU_PMB_H__ ++ ++struct ipmmu_pmb_info { ++ int enabled; ++ unsigned long paddr; ++ int size_mb; ++}; ++ ++struct pmb_tile_info { ++ int tile_width; ++ int tile_height; ++ int buffer_pitch; ++ int enabled; ++}; ++ ++/* IOCTL commands. */ ++ ++#define IPMMU_SET_PMB _IOW('S', 37, struct ipmmu_pmb_phys *) ++#define IPMMU_GET_PMB _IOR('S', 38, struct ipmmu_pmb_phys *) ++#define IPMMU_GET_PMB_HANDLE _IOR('S', 39, unsigned long *) ++#define IPMMU_SET_PMB_TL _IOW('S', 41, struct ipmmu_pmb_tile_info *) ++#define IPMMU_GET_PMB_TL _IOR('S', 42, struct ipmmu_pmb_tile_info *) ++ ++#ifdef __kernel ++ ++#endif /* __kernel */ ++ ++#endif /* __LINUX_IPMMU_PMB_H__ */ diff --git a/patches.armadillo800/shmobile-r8a7740-add-meram-uio-device.patch b/patches.armadillo800/shmobile-r8a7740-add-meram-uio-device.patch new file mode 100644 index 0000000000000..401ef797d66e3 --- /dev/null +++ b/patches.armadillo800/shmobile-r8a7740-add-meram-uio-device.patch @@ -0,0 +1,64 @@ +From dhobsong@igel.co.jp Wed Nov 21 23:30:45 2012 +From: Damian Hobson-Garcia <dhobsong@igel.co.jp> +Date: Thu, 22 Nov 2012 16:28:30 +0900 +Subject: [PATCH 7/7] shmobile: r8a7740: Add MERAM UIO device +To: gregkh@linuxfoundation.org +Cc: ltsi-dev@lists.linuxfoundation.org, Damian Hobson-Garcia <dhobsong@igel.co.jp> +Message-ID: <1353569310-16564-8-git-send-email-dhobsong@igel.co.jp> + + +Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp> +--- + arch/arm/mach-shmobile/setup-r8a7740.c | 34 +++++++++++++++++++++++++++++++++ + 1 file changed, 34 insertions(+) + +--- a/arch/arm/mach-shmobile/setup-r8a7740.c ++++ b/arch/arm/mach-shmobile/setup-r8a7740.c +@@ -105,6 +105,39 @@ static struct platform_device vpc_device + .num_resources = ARRAY_SIZE(vpc_resources), + }; + ++/* MERAM */ ++static struct uio_info meram_uio_platform_data = { ++ .name = "MERAM", ++ .version = "0", ++ .irq = -1, ++}; ++ ++static struct resource meram_uio_resources[] = { ++ [0] = { ++ .name = "MERAM", ++ .start = 0xe8000000, ++ .end = 0xe807ffff, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .name = "MERAM_MEM", ++ .start = 0xe8080000, ++ .end = 0xe81fffff, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static struct platform_device meram_uio_device = { ++ .name = "uio_pdrv_genirq", ++ .id = 0, ++ .dev = { ++ .platform_data = &meram_uio_platform_data, ++ .coherent_dma_mask = ~0, ++ }, ++ .resource = meram_uio_resources, ++ .num_resources = ARRAY_SIZE(meram_uio_resources), ++}; ++ + /* VCP1 */ + static unsigned int regions[] = { + (80 << 20), +@@ -424,6 +457,7 @@ static struct platform_device *r8a7740_e + &vio_device, + &ipmmu_device, + &meram_device, ++ &meram_uio_device, + + }; + diff --git a/patches.armadillo800/shmobile-r8a7740-add-support-for-vcp-1-vpu-vpc-vio-multimedia-ip.patch b/patches.armadillo800/shmobile-r8a7740-add-support-for-vcp-1-vpu-vpc-vio-multimedia-ip.patch new file mode 100644 index 0000000000000..cf034f39aa4f6 --- /dev/null +++ b/patches.armadillo800/shmobile-r8a7740-add-support-for-vcp-1-vpu-vpc-vio-multimedia-ip.patch @@ -0,0 +1,205 @@ +From dhobsong@igel.co.jp Wed Nov 21 23:30:31 2012 +From: Damian Hobson-Garcia <dhobsong@igel.co.jp> +Date: Thu, 22 Nov 2012 16:28:24 +0900 +Subject: [PATCH 1/7] shmobile: r8a7740: Add support for VCP-1 (VPU), VPC, VIO multimedia IP +To: gregkh@linuxfoundation.org +Cc: ltsi-dev@lists.linuxfoundation.org, Damian Hobson-Garcia <dhobsong@igel.co.jp> +Message-ID: <1353569310-16564-2-git-send-email-dhobsong@igel.co.jp> + + +Att the UIO device and clock settings for these multimedia IP cores + +Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp> +--- + arch/arm/mach-shmobile/board-armadillo800eva.c | 1 + arch/arm/mach-shmobile/clock-r8a7740.c | 9 ++ + arch/arm/mach-shmobile/include/mach/common.h | 1 + arch/arm/mach-shmobile/setup-r8a7740.c | 100 +++++++++++++++++++++++++ + 4 files changed, 110 insertions(+), 1 deletion(-) + +--- a/arch/arm/mach-shmobile/board-armadillo800eva.c ++++ b/arch/arm/mach-shmobile/board-armadillo800eva.c +@@ -1212,6 +1212,7 @@ DT_MACHINE_START(ARMADILLO800EVA_DT, "ar + .init_early = eva_add_early_devices, + .init_irq = r8a7740_init_irq, + .handle_irq = shmobile_handle_irq_intc, ++ .reserve = r8a7740_reserve_memory, + .init_machine = eva_init, + .init_late = shmobile_init_late, + .timer = &shmobile_timer, +--- a/arch/arm/mach-shmobile/clock-r8a7740.c ++++ b/arch/arm/mach-shmobile/clock-r8a7740.c +@@ -459,7 +459,7 @@ static struct clk div6_clks[DIV6_NR] = { + + enum { + MSTP128, MSTP127, MSTP125, +- MSTP116, MSTP111, MSTP100, MSTP117, ++ MSTP116, MSTP111, MSTP100, MSTP117, MSTP101, MSTP107, MSTP103, + + MSTP230, + MSTP222, +@@ -483,6 +483,9 @@ static struct clk mstp_clks[MSTP_NR] = { + [MSTP116] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR1, 16, 0), /* IIC0 */ + [MSTP111] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR1, 11, 0), /* TMU1 */ + [MSTP100] = SH_CLK_MSTP32(&div4_clks[DIV4_B], SMSTPCR1, 0, 0), /* LCDC0 */ ++ [MSTP101] = SH_CLK_MSTP32(&div4_clks[DIV4_B], SMSTPCR1, 1, 0), /* VPU5HA2 */ ++ [MSTP103] = SH_CLK_MSTP32(&div4_clks[DIV4_B], SMSTPCR1, 3, 0), /* VIO6C */ ++ [MSTP107] = SH_CLK_MSTP32(&div4_clks[DIV4_B], SMSTPCR1, 7, 0), /* VPC */ + + [MSTP230] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 30, 0), /* SCIFA6 */ + [MSTP222] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 22, 0), /* SCIFA7 */ +@@ -554,6 +557,10 @@ static struct clk_lookup lookups[] = { + + /* MSTP32 clocks */ + CLKDEV_DEV_ID("sh_mobile_lcdc_fb.0", &mstp_clks[MSTP100]), ++ CLKDEV_DEV_ID("uio_dmem_genirq.0", &mstp_clks[MSTP101]), ++ CLKDEV_DEV_ID("uio_pdrv_genirq.1", &mstp_clks[MSTP103]), ++ CLKDEV_DEV_ID("uio_pdrv_genirq.2", &mstp_clks[MSTP107]), ++ CLKDEV_DEV_ID("sh_mobile_lcdc_fb.0", &mstp_clks[MSTP100]), + CLKDEV_DEV_ID("sh_tmu.1", &mstp_clks[MSTP111]), + CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[MSTP116]), + CLKDEV_DEV_ID("sh_mobile_lcdc_fb.1", &mstp_clks[MSTP117]), +--- a/arch/arm/mach-shmobile/include/mach/common.h ++++ b/arch/arm/mach-shmobile/include/mach/common.h +@@ -68,6 +68,7 @@ extern void r8a7740_map_io(void); + extern void r8a7740_add_early_devices(void); + extern void r8a7740_add_standard_devices(void); + extern void r8a7740_clock_init(u8 md_ck); ++extern void r8a7740_reserve_memory(void); + extern void r8a7740_pinmux_init(void); + + extern void r8a7779_init_irq(void); +--- a/arch/arm/mach-shmobile/setup-r8a7740.c ++++ b/arch/arm/mach-shmobile/setup-r8a7740.c +@@ -23,10 +23,13 @@ + #include <linux/init.h> + #include <linux/io.h> + #include <linux/platform_device.h> ++#include <linux/platform_data/uio_dmem_genirq.h> + #include <linux/of_platform.h> + #include <linux/serial_sci.h> + #include <linux/sh_dma.h> + #include <linux/sh_timer.h> ++#include <linux/uio_driver.h> ++#include <linux/dma-contiguous.h> + #include <linux/dma-mapping.h> + #include <mach/dma-register.h> + #include <mach/r8a7740.h> +@@ -74,6 +77,94 @@ void __init r8a7740_map_io(void) + init_consistent_dma_size(12 << 20); + } + ++/* VPC */ ++static struct uio_info vpc_platform_data = { ++ .name = "VPC", ++ .version = "0", ++ .irq = -1, ++}; ++ ++static struct resource vpc_resources[] = { ++ [0] = { ++ .name = "VPC", ++ .start = 0xfe9d0000, ++ .end = 0xfe9d0020, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static struct platform_device vpc_device = { ++ .name = "uio_pdrv_genirq", ++ .id = 2, ++ .dev = { ++ .platform_data = &vpc_platform_data, ++ }, ++ .resource = vpc_resources, ++ .num_resources = ARRAY_SIZE(vpc_resources), ++}; ++ ++/* VCP1 */ ++static unsigned int regions[] = { ++ (80 << 20), ++}; ++ ++static struct uio_dmem_genirq_pdata vpu_platform_data = { ++ .uioinfo = { ++ .name = "VPU5", ++ .version = "0", ++ .irq = intcs_evt2irq(0x880), ++ }, ++ .dynamic_region_sizes = regions, ++ .num_dynamic_regions = ARRAY_SIZE(regions), ++}; ++ ++static struct resource vpu_resources[] = { ++ [0] = { ++ .name = "VPU", ++ .start = 0xfe900000, ++ .end = 0xfe900157, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static struct platform_device vpu_device = { ++ .name = "uio_dmem_genirq", ++ .id = 0, ++ .dev = { ++ .platform_data = &vpu_platform_data, ++ .coherent_dma_mask = ~0, ++ }, ++ .resource = vpu_resources, ++ .num_resources = ARRAY_SIZE(vpu_resources), ++}; ++ ++/* VI00 */ ++static struct uio_info vio_platform_data = { ++ .name = "VIO6C", ++ .version = "0", ++ .irq = intcs_evt2irq(0x4E0), ++}; ++ ++static struct resource vio_resources[] = { ++ [0] = { ++ .name = "VIO6", ++ .start = 0xfe920000, ++ .end = 0xfe928000, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static struct platform_device vio_device = { ++ .name = "uio_pdrv_genirq", ++ .id = 1, ++ .dev = { ++ .platform_data = &vio_platform_data, ++ .coherent_dma_mask = ~0, ++ }, ++ .resource = vio_resources, ++ .num_resources = ARRAY_SIZE(vio_resources), ++}; ++ + /* SCIFA0 */ + static struct plat_sci_port scif0_platform_data = { + .mapbase = 0xe6c40000, +@@ -279,6 +370,10 @@ static struct platform_device *r8a7740_e + &scif7_device, + &scifb_device, + &cmt10_device, ++ &vpu_device, ++ &vpc_device, ++ &vio_device, ++ + }; + + /* DMA */ +@@ -718,6 +813,11 @@ void __init r8a7740_add_early_devices(vo + shmobile_timer.init = r8a7740_earlytimer_init; + } + ++void __init r8a7740_reserve_memory(void) ++{ ++ dma_declare_contiguous(&vpu_device.dev, 80 << 20, 0, 0); ++} ++ + #ifdef CONFIG_USE_OF + + void __init r8a7740_add_early_devices_dt(void) diff --git a/patches.armadillo800/shmobile-r8a7740-set-up-meram-address-range.patch b/patches.armadillo800/shmobile-r8a7740-set-up-meram-address-range.patch new file mode 100644 index 0000000000000..123a2b6278099 --- /dev/null +++ b/patches.armadillo800/shmobile-r8a7740-set-up-meram-address-range.patch @@ -0,0 +1,76 @@ +From dhobsong@igel.co.jp Wed Nov 21 23:30:42 2012 +From: Damian Hobson-Garcia <dhobsong@igel.co.jp> +Date: Thu, 22 Nov 2012 16:28:29 +0900 +Subject: [PATCH 6/7] shmobile: r8a7740: Set up MERAM address range +To: gregkh@linuxfoundation.org +Cc: ltsi-dev@lists.linuxfoundation.org, Damian Hobson-Garcia <dhobsong@igel.co.jp> +Message-ID: <1353569310-16564-7-git-send-email-dhobsong@igel.co.jp> + + +The default address range settings have both the MERAM and the IPMMU-PMB +set to use the same range, starting from 0x80000000. The MERAM has +priority over this address range, which means that regardless of the PMB +setting, any H/W accesses to the 0x80000000 memory region will be intercepted +by the MERAM. This patch uses the MERAM driver to change the MERAM memory +range to start at 0xC0000000. + +Signed-off-by: Damian Hobson-Garcia <dhobsong@igel.co.jp> +--- + arch/arm/mach-shmobile/setup-r8a7740.c | 32 ++++++++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +--- a/arch/arm/mach-shmobile/setup-r8a7740.c ++++ b/arch/arm/mach-shmobile/setup-r8a7740.c +@@ -30,6 +30,7 @@ + #include <linux/sh_timer.h> + #include <linux/uio_driver.h> + #include <linux/dma-contiguous.h> ++#include <video/sh_mobile_meram.h> + #include <linux/dma-mapping.h> + #include <mach/dma-register.h> + #include <mach/r8a7740.h> +@@ -377,6 +378,36 @@ static struct platform_device ipmmu_devi + .num_resources = ARRAY_SIZE(ipmmu_resources), + }; + ++/* MERAM */ ++static struct sh_mobile_meram_info meram_info = { ++ .addr_mode = SH_MOBILE_MERAM_MODE1, ++}; ++ ++static struct resource meram_resources[] = { ++ [0] = { ++ .name = "MERAM", ++ .start = 0xe8000000, ++ .end = 0xe807ffff, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .name = "MERAM", ++ .start = 0xe8080000, ++ .end = 0xe81fffff, ++ .flags = IORESOURCE_MEM, ++ } ++}; ++ ++static struct platform_device meram_device = { ++ .name = "sh_mobile_meram", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(meram_resources), ++ .resource = meram_resources, ++ .dev = { ++ .platform_data = &meram_info, ++ }, ++}; ++ + static struct platform_device *r8a7740_early_devices[] __initdata = { + &scif0_device, + &scif1_device, +@@ -392,6 +423,7 @@ static struct platform_device *r8a7740_e + &vpc_device, + &vio_device, + &ipmmu_device, ++ &meram_device, + + }; + |