add more armadillo800 patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 5 Dec 2012 22:44:20 +0000 (14:44 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 5 Dec 2012 22:44:20 +0000 (14:44 -0800)
patches.armadillo800/iommu-shmobile-add-iommu-driver-for-renesas-ipmmu-modules.patch [new file with mode: 0644]
patches.armadillo800/shmobile-armadillo800eva-enable-iommu-support.patch [new file with mode: 0644]
patches.armadillo800/shmobile-armadillo800eva-set-lcd-color-depth-to-32-bpp.patch [new file with mode: 0644]
patches.armadillo800/shmobile-ipmmu-add-basic-pmb-support.patch [new file with mode: 0644]
patches.armadillo800/shmobile-r8a7740-add-meram-uio-device.patch [new file with mode: 0644]
patches.armadillo800/shmobile-r8a7740-add-support-for-vcp-1-vpu-vpc-vio-multimedia-ip.patch [new file with mode: 0644]
patches.armadillo800/shmobile-r8a7740-set-up-meram-address-range.patch [new file with mode: 0644]

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 (file)
index 0000000..06ca441
--- /dev/null
@@ -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 (file)
index 0000000..95a54e8
--- /dev/null
@@ -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 (file)
index 0000000..b4a8fd0
--- /dev/null
@@ -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 (file)
index 0000000..41a7ee0
--- /dev/null
@@ -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 (file)
index 0000000..401ef79
--- /dev/null
@@ -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 (file)
index 0000000..cf034f3
--- /dev/null
@@ -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 (file)
index 0000000..123a2b6
--- /dev/null
@@ -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,
+ };