diff options
| author | Hideki EIRAKU <hdk@igel.co.jp> | 2013-01-21 05:54:26 -0500 |
|---|---|---|
| committer | Joerg Roedel <joro@8bytes.org> | 2013-02-06 04:57:25 -0500 |
| commit | c2c460f7c148aa1a59630f61dac2481f1efb4f4e (patch) | |
| tree | 97710b216a60c2ee1e9538a0ac55078508dcf622 | |
| parent | 88b62b915b0b7e25870eb0604ed9a92ba4bfc9f7 (diff) | |
iommu/shmobile: Add iommu driver for Renesas IPMMU modules
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>
Signed-off-by: Joerg Roedel <joro@8bytes.org>
| -rw-r--r-- | drivers/iommu/Kconfig | 74 | ||||
| -rw-r--r-- | drivers/iommu/Makefile | 2 | ||||
| -rw-r--r-- | drivers/iommu/shmobile-iommu.c | 395 | ||||
| -rw-r--r-- | drivers/iommu/shmobile-ipmmu.c | 136 | ||||
| -rw-r--r-- | drivers/iommu/shmobile-ipmmu.h | 34 | ||||
| -rw-r--r-- | include/linux/platform_data/sh_ipmmu.h | 18 |
6 files changed, 659 insertions, 0 deletions
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index e39f9dbf297b..d364494c9e7c 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig | |||
| @@ -187,4 +187,78 @@ config EXYNOS_IOMMU_DEBUG | |||
| 187 | 187 | ||
| 188 | Say N unless you need kernel log message for IOMMU debugging | 188 | Say N unless you need kernel log message for IOMMU debugging |
| 189 | 189 | ||
| 190 | config SHMOBILE_IPMMU | ||
| 191 | bool | ||
| 192 | |||
| 193 | config SHMOBILE_IPMMU_TLB | ||
| 194 | bool | ||
| 195 | |||
| 196 | config SHMOBILE_IOMMU | ||
| 197 | bool "IOMMU for Renesas IPMMU/IPMMUI" | ||
| 198 | default n | ||
| 199 | depends on (ARM && ARCH_SHMOBILE) | ||
| 200 | select IOMMU_API | ||
| 201 | select ARM_DMA_USE_IOMMU | ||
| 202 | select SHMOBILE_IPMMU | ||
| 203 | select SHMOBILE_IPMMU_TLB | ||
| 204 | help | ||
| 205 | Support for Renesas IPMMU/IPMMUI. This option enables | ||
| 206 | remapping of DMA memory accesses from all of the IP blocks | ||
| 207 | on the ICB. | ||
| 208 | |||
| 209 | Warning: Drivers (including userspace drivers of UIO | ||
| 210 | devices) of the IP blocks on the ICB *must* use addresses | ||
| 211 | allocated from the IPMMU (iova) for DMA with this option | ||
| 212 | enabled. | ||
| 213 | |||
| 214 | If unsure, say N. | ||
| 215 | |||
| 216 | choice | ||
| 217 | prompt "IPMMU/IPMMUI address space size" | ||
| 218 | default SHMOBILE_IOMMU_ADDRSIZE_2048MB | ||
| 219 | depends on SHMOBILE_IOMMU | ||
| 220 | help | ||
| 221 | This option sets IPMMU/IPMMUI address space size by | ||
| 222 | adjusting the 1st level page table size. The page table size | ||
| 223 | is calculated as follows: | ||
| 224 | |||
| 225 | page table size = number of page table entries * 4 bytes | ||
| 226 | number of page table entries = address space size / 1 MiB | ||
| 227 | |||
| 228 | For example, when the address space size is 2048 MiB, the | ||
| 229 | 1st level page table size is 8192 bytes. | ||
| 230 | |||
| 231 | config SHMOBILE_IOMMU_ADDRSIZE_2048MB | ||
| 232 | bool "2 GiB" | ||
| 233 | |||
| 234 | config SHMOBILE_IOMMU_ADDRSIZE_1024MB | ||
| 235 | bool "1 GiB" | ||
| 236 | |||
| 237 | config SHMOBILE_IOMMU_ADDRSIZE_512MB | ||
| 238 | bool "512 MiB" | ||
| 239 | |||
| 240 | config SHMOBILE_IOMMU_ADDRSIZE_256MB | ||
| 241 | bool "256 MiB" | ||
| 242 | |||
| 243 | config SHMOBILE_IOMMU_ADDRSIZE_128MB | ||
| 244 | bool "128 MiB" | ||
| 245 | |||
| 246 | config SHMOBILE_IOMMU_ADDRSIZE_64MB | ||
| 247 | bool "64 MiB" | ||
| 248 | |||
| 249 | config SHMOBILE_IOMMU_ADDRSIZE_32MB | ||
| 250 | bool "32 MiB" | ||
| 251 | |||
| 252 | endchoice | ||
| 253 | |||
| 254 | config SHMOBILE_IOMMU_L1SIZE | ||
| 255 | int | ||
| 256 | default 8192 if SHMOBILE_IOMMU_ADDRSIZE_2048MB | ||
| 257 | default 4096 if SHMOBILE_IOMMU_ADDRSIZE_1024MB | ||
| 258 | default 2048 if SHMOBILE_IOMMU_ADDRSIZE_512MB | ||
| 259 | default 1024 if SHMOBILE_IOMMU_ADDRSIZE_256MB | ||
| 260 | default 512 if SHMOBILE_IOMMU_ADDRSIZE_128MB | ||
| 261 | default 256 if SHMOBILE_IOMMU_ADDRSIZE_64MB | ||
| 262 | default 128 if SHMOBILE_IOMMU_ADDRSIZE_32MB | ||
| 263 | |||
| 190 | endif # IOMMU_SUPPORT | 264 | endif # IOMMU_SUPPORT |
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index f66b816d455c..ef0e5207ad69 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile | |||
| @@ -13,3 +13,5 @@ obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o | |||
| 13 | obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o | 13 | obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o |
| 14 | obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o | 14 | obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o |
| 15 | obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o | 15 | obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o |
| 16 | obj-$(CONFIG_SHMOBILE_IOMMU) += shmobile-iommu.o | ||
| 17 | obj-$(CONFIG_SHMOBILE_IPMMU) += shmobile-ipmmu.o | ||
diff --git a/drivers/iommu/shmobile-iommu.c b/drivers/iommu/shmobile-iommu.c new file mode 100644 index 000000000000..b6e8b57cf0a8 --- /dev/null +++ b/drivers/iommu/shmobile-iommu.c | |||
| @@ -0,0 +1,395 @@ | |||
| 1 | /* | ||
| 2 | * IOMMU for IPMMU/IPMMUI | ||
| 3 | * Copyright (C) 2012 Hideki EIRAKU | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation; version 2 of the License. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <linux/dma-mapping.h> | ||
| 11 | #include <linux/io.h> | ||
| 12 | #include <linux/iommu.h> | ||
| 13 | #include <linux/platform_device.h> | ||
| 14 | #include <linux/sizes.h> | ||
| 15 | #include <linux/slab.h> | ||
| 16 | #include <asm/dma-iommu.h> | ||
| 17 | #include "shmobile-ipmmu.h" | ||
| 18 | |||
| 19 | #define L1_SIZE CONFIG_SHMOBILE_IOMMU_L1SIZE | ||
| 20 | #define L1_LEN (L1_SIZE / 4) | ||
| 21 | #define L1_ALIGN L1_SIZE | ||
| 22 | #define L2_SIZE SZ_1K | ||
| 23 | #define L2_LEN (L2_SIZE / 4) | ||
| 24 | #define L2_ALIGN L2_SIZE | ||
| 25 | |||
| 26 | struct shmobile_iommu_domain_pgtable { | ||
| 27 | uint32_t *pgtable; | ||
| 28 | dma_addr_t handle; | ||
| 29 | }; | ||
| 30 | |||
| 31 | struct shmobile_iommu_archdata { | ||
| 32 | struct list_head attached_list; | ||
| 33 | struct dma_iommu_mapping *iommu_mapping; | ||
| 34 | spinlock_t attach_lock; | ||
| 35 | struct shmobile_iommu_domain *attached; | ||
| 36 | int num_attached_devices; | ||
| 37 | struct shmobile_ipmmu *ipmmu; | ||
| 38 | }; | ||
| 39 | |||
| 40 | struct shmobile_iommu_domain { | ||
| 41 | struct shmobile_iommu_domain_pgtable l1, l2[L1_LEN]; | ||
| 42 | spinlock_t map_lock; | ||
| 43 | spinlock_t attached_list_lock; | ||
| 44 | struct list_head attached_list; | ||
| 45 | }; | ||
| 46 | |||
| 47 | static struct shmobile_iommu_archdata *ipmmu_archdata; | ||
| 48 | static struct kmem_cache *l1cache, *l2cache; | ||
| 49 | |||
| 50 | static int pgtable_alloc(struct shmobile_iommu_domain_pgtable *pgtable, | ||
| 51 | struct kmem_cache *cache, size_t size) | ||
| 52 | { | ||
| 53 | pgtable->pgtable = kmem_cache_zalloc(cache, GFP_ATOMIC); | ||
| 54 | if (!pgtable->pgtable) | ||
| 55 | return -ENOMEM; | ||
| 56 | pgtable->handle = dma_map_single(NULL, pgtable->pgtable, size, | ||
| 57 | DMA_TO_DEVICE); | ||
| 58 | return 0; | ||
| 59 | } | ||
| 60 | |||
| 61 | static void pgtable_free(struct shmobile_iommu_domain_pgtable *pgtable, | ||
| 62 | struct kmem_cache *cache, size_t size) | ||
| 63 | { | ||
| 64 | dma_unmap_single(NULL, pgtable->handle, size, DMA_TO_DEVICE); | ||
| 65 | kmem_cache_free(cache, pgtable->pgtable); | ||
| 66 | } | ||
| 67 | |||
| 68 | static uint32_t pgtable_read(struct shmobile_iommu_domain_pgtable *pgtable, | ||
| 69 | unsigned int index) | ||
| 70 | { | ||
| 71 | return pgtable->pgtable[index]; | ||
| 72 | } | ||
| 73 | |||
| 74 | static void pgtable_write(struct shmobile_iommu_domain_pgtable *pgtable, | ||
| 75 | unsigned int index, unsigned int count, uint32_t val) | ||
| 76 | { | ||
| 77 | unsigned int i; | ||
| 78 | |||
| 79 | for (i = 0; i < count; i++) | ||
| 80 | pgtable->pgtable[index + i] = val; | ||
| 81 | dma_sync_single_for_device(NULL, pgtable->handle + index * sizeof(val), | ||
| 82 | sizeof(val) * count, DMA_TO_DEVICE); | ||
| 83 | } | ||
| 84 | |||
| 85 | static int shmobile_iommu_domain_init(struct iommu_domain *domain) | ||
| 86 | { | ||
| 87 | struct shmobile_iommu_domain *sh_domain; | ||
| 88 | int i, ret; | ||
| 89 | |||
| 90 | sh_domain = kmalloc(sizeof(*sh_domain), GFP_KERNEL); | ||
| 91 | if (!sh_domain) | ||
| 92 | return -ENOMEM; | ||
| 93 | ret = pgtable_alloc(&sh_domain->l1, l1cache, L1_SIZE); | ||
| 94 | if (ret < 0) { | ||
| 95 | kfree(sh_domain); | ||
| 96 | return ret; | ||
| 97 | } | ||
| 98 | <|||
