[PATCH v3 1/2] iommu/shmobile: Add iommu driver for Renesas IPMMU modules
Nobuhiro Iwamatsu
nobuhiro.iwamatsu.yj at renesas.com
Wed Sep 12 04:07:00 EDT 2012
Hi,
On Tue, Sep 11, 2012 at 3:10 PM, Hideki EIRAKU <hdk at igel.co.jp> wrote:
> 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 at igel.co.jp>
> Tested-by: Damian Hobson-Garcia <dhobsong at 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 | 36 +++
> drivers/iommu/Makefile | 1 +
> drivers/iommu/shmobile-iommu.c | 372 +++++++++++++++++++++++++++
> 7 files changed, 584 insertions(+), 0 deletions(-)
> 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
>
> diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig
> index 4cacc2d..86adcf0 100644
> --- a/arch/arm/mach-shmobile/Kconfig
> +++ b/arch/arm/mach-shmobile/Kconfig
> @@ -210,6 +210,12 @@ endmenu
> config SH_CLK_CPG
> bool
>
> +config SHMOBILE_IPMMU
> + bool
> +
> +config SHMOBILE_IPMMU_TLB
> + bool
> +
> source "drivers/sh/Kconfig"
>
> endif
> diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile
> index 0df5ae6..1e72199 100644
> --- a/arch/arm/mach-shmobile/Makefile
> +++ b/arch/arm/mach-shmobile/Makefile
> @@ -60,3 +60,6 @@ obj-$(CONFIG_MACH_KZM9G) += board-kzm9g.o
> # Framework support
> obj-$(CONFIG_SMP) += $(smp-y)
> obj-$(CONFIG_GENERIC_GPIO) += $(pfc-y)
> +
> +# IPMMU/IPMMUI
> +obj-$(CONFIG_SHMOBILE_IPMMU) += ipmmu.o
> diff --git a/arch/arm/mach-shmobile/include/mach/ipmmu.h b/arch/arm/mach-shmobile/include/mach/ipmmu.h
> new file mode 100644
> index 0000000..ede2f0b
> --- /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)
> +{
> +}
Please use 'do { } while (0)'.
> +
> +static int ipmmu_iommu_init(struct device *dev)
> +{
> + return -EINVAL;
> +}
> +#endif
> diff --git a/arch/arm/mach-shmobile/ipmmu.c b/arch/arm/mach-shmobile/ipmmu.c
> new file mode 100644
> index 0000000..72cacb9
> --- /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;
> + }
I thought that you could describe more briefly if ffs() is used.
> + 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);
> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> index 9f69b56..0ed44d0 100644
> --- a/drivers/iommu/Kconfig
> +++ b/drivers/iommu/Kconfig
> @@ -187,4 +187,40 @@ config EXYNOS_IOMMU_DEBUG
>
> Say N unless you need kernel log message for IOMMU debugging
>
> +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 1st level page table size"
> + default SHMOBILE_IOMMU_L1SIZE_8192
> + depends on SHMOBILE_IOMMU
> +
> + config SHMOBILE_IOMMU_L1SIZE_8192
> + bool "8192 bytes (2 GiB address space)"
> +
> + config SHMOBILE_IOMMU_L1SIZE_4096
> + bool "4096 bytes (1 GiB address space)"
> +
> + config SHMOBILE_IOMMU_L1SIZE_2048
> + bool "2048 bytes (512 MiB address space)"
> +
> + config SHMOBILE_IOMMU_L1SIZE_1024
> + bool "1024 bytes (256 MiB address space)"
> +
> + config SHMOBILE_IOMMU_L1SIZE_512
> + bool "512 bytes (128 MiB address space)"
> +
> + config SHMOBILE_IOMMU_L1SIZE_256
> + bool "256 bytes (64 MiB address space)"
> +
> + config SHMOBILE_IOMMU_L1SIZE_128
> + bool "128 bytes (32 MiB address space)"
> +
> +endchoice
> +
> endif # IOMMU_SUPPORT
> diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
> index 14a4d5f..62cf917 100644
> --- a/drivers/iommu/Makefile
> +++ b/drivers/iommu/Makefile
> @@ -12,3 +12,4 @@ 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_EXYNOS_IOMMU) += exynos-iommu.o
> +obj-$(CONFIG_SHMOBILE_IOMMU) += shmobile-iommu.o
> diff --git a/drivers/iommu/shmobile-iommu.c b/drivers/iommu/shmobile-iommu.c
> new file mode 100644
> index 0000000..3285b3b
> --- /dev/null
> +++ b/drivers/iommu/shmobile-iommu.c
> @@ -0,0 +1,372 @@
> +/*
> + * 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>
> +
> +#ifdef CONFIG_SHMOBILE_IOMMU_L1SIZE_8192
> +#define L1_SIZE 8192
> +#endif
> +#ifdef CONFIG_SHMOBILE_IOMMU_L1SIZE_4096
> +#define L1_SIZE 4096
> +#endif
> +#ifdef CONFIG_SHMOBILE_IOMMU_L1SIZE_2048
> +#define L1_SIZE 2048
> +#endif
> +#ifdef CONFIG_SHMOBILE_IOMMU_L1SIZE_1024
> +#define L1_SIZE 1024
> +#endif
> +#ifdef CONFIG_SHMOBILE_IOMMU_L1SIZE_512
> +#define L1_SIZE 512
> +#endif
> +#ifdef CONFIG_SHMOBILE_IOMMU_L1SIZE_256
> +#define L1_SIZE 256
> +#endif
> +#ifdef CONFIG_SHMOBILE_IOMMU_L1SIZE_128
> +#define L1_SIZE 128
> +#endif
I think that it was better to define by kconfig.
For example, following codes.
+config SHMOBILE_IOMMU_L1SIZE
+ hex
+ default "0x00002000" if SHMOBILE_IOMMU_L1SIZE_8192
+ default "0x00001000" if SHMOBILE_IOMMU_L1SIZE_4096
+ default "0x00000800" if SHMOBILE_IOMMU_L1SIZE_2048
+ default "0x00000400" if SHMOBILE_IOMMU_L1SIZE_1024
+ default "0x00000200" if SHMOBILE_IOMMU_L1SIZE_512
+ default "0x00000100" if SHMOBILE_IOMMU_L1SIZE_256
+ default "0x00000080" if SHMOBILE_IOMMU_L1SIZE_128
> +#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;
> +}
> --
> 1.7.0.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-sh" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
Best regards,
Nobuhiro
--
Nobuhiro Iwamatsu
More information about the linux-arm-kernel
mailing list