diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-26 11:09:17 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-26 11:09:17 -0800 |
commit | f6c0ffa8f0b0781f4954cb06f0a81d6c10c1b434 (patch) | |
tree | f99190661706d18f129497a4ef2d37822c9c34db /drivers/iommu/shmobile-ipmmu.c | |
parent | 42a0a1b0fd343888c59afc8b243a77bcec2cc11c (diff) | |
parent | 604542b824f72fa5d7fd977af277538c1e15b5f0 (diff) |
Merge tag 'iommu-updates-v3.9' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu
Pull IOMMU Updates from Joerg Roedel:
"Besides some fixes and cleanups in the code there are three more
important changes to point out this time:
* New IOMMU driver for the ARM SHMOBILE platform
* An IOMMU-API extension for non-paging IOMMUs (required for
upcoming PAMU driver)
* Rework of the way the Tegra IOMMU driver accesses its
registetrs - register windows are easier to extend now.
There are also a few changes to non-iommu code, but that is acked by
the respective maintainers."
* tag 'iommu-updates-v3.9' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu: (23 commits)
iommu/tegra: assume CONFIG_OF in SMMU driver
iommu/tegra: assume CONFIG_OF in gart driver
iommu/amd: Remove redundant NULL check before dma_ops_domain_free().
iommu/amd: Initialize device table after dma_ops
iommu/vt-d: Zero out allocated memory in dmar_enable_qi
iommu/tegra: smmu: Fix incorrect mask for regbase
iommu/exynos: Make exynos_sysmmu_disable static
ARM: mach-shmobile: r8a7740: Add IPMMU device
ARM: mach-shmobile: sh73a0: Add IPMMU device
ARM: mach-shmobile: sh7372: Add IPMMU device
iommu/shmobile: Add iommu driver for Renesas IPMMU modules
iommu: Add DOMAIN_ATTR_WINDOWS domain attribute
iommu: Add domain window handling functions
iommu: Implement DOMAIN_ATTR_PAGING attribute
iommu: Check for valid pgsize_bitmap in iommu_map/unmap
iommu: Make sure DOMAIN_ATTR_MAX is really the maximum
iommu/tegra: smmu: Change SMMU's dependency on ARCH_TEGRA
iommu/tegra: smmu: Use helper function to check for valid register offset
iommu/tegra: smmu: Support variable MMIO ranges/blocks
iommu/tegra: Add missing spinlock initialization
...
Diffstat (limited to 'drivers/iommu/shmobile-ipmmu.c')
-rw-r--r-- | drivers/iommu/shmobile-ipmmu.c | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/drivers/iommu/shmobile-ipmmu.c b/drivers/iommu/shmobile-ipmmu.c new file mode 100644 index 00000000000..8321f89596c --- /dev/null +++ b/drivers/iommu/shmobile-ipmmu.c @@ -0,0 +1,136 @@ +/* + * 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. + */ + +#include <linux/err.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/platform_data/sh_ipmmu.h> +#include "shmobile-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) + +static void ipmmu_reg_write(struct shmobile_ipmmu *ipmmu, unsigned long reg_off, + unsigned long data) +{ + iowrite32(data, ipmmu->ipmmu_base + reg_off); +} + +void ipmmu_tlb_flush(struct shmobile_ipmmu *ipmmu) +{ + if (!ipmmu) + return; + + mutex_lock(&ipmmu->flush_lock); + if (ipmmu->tlb_enabled) + ipmmu_reg_write(ipmmu, IMCTR1, IMCTR1_FLUSH | IMCTR1_TLBEN); + else + ipmmu_reg_write(ipmmu, IMCTR1, IMCTR1_FLUSH); + mutex_unlock(&ipmmu->flush_lock); +} + +void ipmmu_tlb_set(struct shmobile_ipmmu *ipmmu, unsigned long phys, int size, + int asid) +{ + if (!ipmmu) + return; + + mutex_lock(&ipmmu->flush_lock); + switch (size) { + default: + ipmmu->tlb_enabled = 0; + break; + case 0x2000: + ipmmu_reg_write(ipmmu, IMTTBCR, 1); + ipmmu->tlb_enabled = 1; + break; + case 0x1000: + ipmmu_reg_write(ipmmu, IMTTBCR, 2); + ipmmu->tlb_enabled = 1; + break; + case 0x800: + ipmmu_reg_write(ipmmu, IMTTBCR, 3); + ipmmu->tlb_enabled = 1; + break; + case 0x400: + ipmmu_reg_write(ipmmu, IMTTBCR, 4); + ipmmu->tlb_enabled = 1; + break; + case 0x200: + ipmmu_reg_write(ipmmu, IMTTBCR, 5); + ipmmu->tlb_enabled = 1; + break; + case 0x100: + ipmmu_reg_write(ipmmu, IMTTBCR, 6); + ipmmu->tlb_enabled = 1; + break; + case 0x80: + ipmmu_reg_write(ipmmu, IMTTBCR, 7); + ipmmu->tlb_enabled = 1; + break; + } + ipmmu_reg_write(ipmmu, IMTTBR, phys); + ipmmu_reg_write(ipmmu, IMASID, asid); + mutex_unlock(&ipmmu->flush_lock); +} + +static int ipmmu_probe(struct platform_device *pdev) +{ + struct shmobile_ipmmu *ipmmu; + struct resource *res; + struct shmobile_ipmmu_platform_data *pdata = pdev->dev.platform_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "cannot get platform resources\n"); + return -ENOENT; + } + ipmmu = devm_kzalloc(&pdev->dev, sizeof(*ipmmu), GFP_KERNEL); + if (!ipmmu) { + dev_err(&pdev->dev, "cannot allocate device data\n"); + return -ENOMEM; + } + mutex_init(&ipmmu->flush_lock); + ipmmu->dev = &pdev->dev; + ipmmu->ipmmu_base = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); + if (!ipmmu->ipmmu_base) { + dev_err(&pdev->dev, "ioremap_nocache failed\n"); + return -ENOMEM; + } + ipmmu->dev_names = pdata->dev_names; + ipmmu->num_dev_names = pdata->num_dev_names; + platform_set_drvdata(pdev, ipmmu); + ipmmu_reg_write(ipmmu, IMCTR1, 0x0); /* disable TLB */ + ipmmu_reg_write(ipmmu, IMCTR2, 0x0); /* disable PMB */ + ipmmu_iommu_init(ipmmu); + 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); |