Blame view
drivers/iommu/ipmmu-vmsa.c
30.4 KB
57d3f11c8 iommu/ipmmu-vmsa:... |
1 |
// SPDX-License-Identifier: GPL-2.0 |
d25a2a16f iommu: Add driver... |
2 |
/* |
8128ac3b0 iommu/ipmmu-vmsa:... |
3 4 |
* IOMMU API for Renesas VMSA-compatible IPMMU * Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com> |
d25a2a16f iommu: Add driver... |
5 |
* |
17fe16181 iommu/renesas: Ad... |
6 |
* Copyright (C) 2014-2020 Renesas Electronics Corporation |
d25a2a16f iommu: Add driver... |
7 |
*/ |
dbb706922 iommu/ipmmu-vmsa:... |
8 |
#include <linux/bitmap.h> |
d25a2a16f iommu: Add driver... |
9 |
#include <linux/delay.h> |
3ae472920 iommu/ipmmu-vmsa:... |
10 |
#include <linux/dma-iommu.h> |
d25a2a16f iommu: Add driver... |
11 12 13 |
#include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/export.h> |
8128ac3b0 iommu/ipmmu-vmsa:... |
14 |
#include <linux/init.h> |
d25a2a16f iommu: Add driver... |
15 16 |
#include <linux/interrupt.h> #include <linux/io.h> |
b77cf11f0 iommu: Allow io-p... |
17 |
#include <linux/io-pgtable.h> |
d25a2a16f iommu: Add driver... |
18 |
#include <linux/iommu.h> |
275f5053c iommu/ipmmu-vmsa:... |
19 |
#include <linux/of.h> |
33f3ac9b5 iommu/ipmmu-vmsa:... |
20 |
#include <linux/of_device.h> |
cda52fcd9 iommu/ipmmu-vmsa:... |
21 |
#include <linux/of_iommu.h> |
7b2d59611 iommu/ipmmu-vmsa:... |
22 |
#include <linux/of_platform.h> |
d25a2a16f iommu: Add driver... |
23 24 25 |
#include <linux/platform_device.h> #include <linux/sizes.h> #include <linux/slab.h> |
58b8e8bf4 iommu/ipmmu-vmsa:... |
26 |
#include <linux/sys_soc.h> |
d25a2a16f iommu: Add driver... |
27 |
|
3ae472920 iommu/ipmmu-vmsa:... |
28 |
#if defined(CONFIG_ARM) && !defined(CONFIG_IOMMU_DMA) |
d25a2a16f iommu: Add driver... |
29 |
#include <asm/dma-iommu.h> |
49c875f03 iommu/ipmmu-vmsa:... |
30 31 32 33 34 |
#else #define arm_iommu_create_mapping(...) NULL #define arm_iommu_attach_device(...) -ENODEV #define arm_iommu_release_mapping(...) do {} while (0) #define arm_iommu_detach_device(...) do {} while (0) |
3ae472920 iommu/ipmmu-vmsa:... |
35 |
#endif |
d25a2a16f iommu: Add driver... |
36 |
|
da38e9ec9 iommu/ipmmu-vmsa:... |
37 38 39 40 |
#define IPMMU_CTX_MAX 8U #define IPMMU_CTX_INVALID -1 #define IPMMU_UTLB_MAX 48U |
dbb706922 iommu/ipmmu-vmsa:... |
41 |
|
33f3ac9b5 iommu/ipmmu-vmsa:... |
42 43 |
struct ipmmu_features { bool use_ns_alias_offset; |
fd5140e29 iommu/ipmmu-vmsa:... |
44 |
bool has_cache_leaf_nodes; |
5fd163416 iommu/ipmmu-vmsa:... |
45 |
unsigned int number_of_contexts; |
b7f3f047a iommu/ipmmu-vmsa:... |
46 |
unsigned int num_utlbs; |
f5c858912 iommu/ipmmu-vmsa:... |
47 |
bool setup_imbuscr; |
c295f504f iommu/ipmmu-vmsa:... |
48 |
bool twobit_imttbcr_sl0; |
2ae869557 iommu/ipmmu-vmsa:... |
49 |
bool reserved_context; |
3623002f0 iommu/ipmmu-vmsa:... |
50 |
bool cache_snoop; |
3dc28d9f5 iommu/ipmmu-vmsa:... |
51 52 |
unsigned int ctx_offset_base; unsigned int ctx_offset_stride; |
1289f7f15 iommu/ipmmu-vmsa:... |
53 |
unsigned int utlb_offset_base; |
33f3ac9b5 iommu/ipmmu-vmsa:... |
54 |
}; |
d25a2a16f iommu: Add driver... |
55 56 57 |
struct ipmmu_vmsa_device { struct device *dev; void __iomem *base; |
01da21e56 iommu/ipmmu-vmsa:... |
58 |
struct iommu_device iommu; |
fd5140e29 iommu/ipmmu-vmsa:... |
59 |
struct ipmmu_vmsa_device *root; |
33f3ac9b5 iommu/ipmmu-vmsa:... |
60 |
const struct ipmmu_features *features; |
5fd163416 iommu/ipmmu-vmsa:... |
61 |
unsigned int num_ctx; |
dbb706922 iommu/ipmmu-vmsa:... |
62 63 64 |
spinlock_t lock; /* Protects ctx and domains[] */ DECLARE_BITMAP(ctx, IPMMU_CTX_MAX); struct ipmmu_vmsa_domain *domains[IPMMU_CTX_MAX]; |
da38e9ec9 iommu/ipmmu-vmsa:... |
65 |
s8 utlb_ctx[IPMMU_UTLB_MAX]; |
d25a2a16f iommu: Add driver... |
66 |
|
b354c73ed iommu/ipmmu-vmsa:... |
67 |
struct iommu_group *group; |
d25a2a16f iommu: Add driver... |
68 69 70 71 72 |
struct dma_iommu_mapping *mapping; }; struct ipmmu_vmsa_domain { struct ipmmu_vmsa_device *mmu; |
5914c5fdd iommu/ipmmu-vmsa:... |
73 |
struct iommu_domain io_domain; |
d25a2a16f iommu: Add driver... |
74 |
|
f20ed39f5 iommu/ipmmu-vmsa:... |
75 76 |
struct io_pgtable_cfg cfg; struct io_pgtable_ops *iop; |
d25a2a16f iommu: Add driver... |
77 |
unsigned int context_id; |
46583e8c4 iommu/ipmmu-vmsa:... |
78 |
struct mutex mutex; /* Protects mappings */ |
d25a2a16f iommu: Add driver... |
79 |
}; |
5914c5fdd iommu/ipmmu-vmsa:... |
80 81 82 83 |
static struct ipmmu_vmsa_domain *to_vmsa_domain(struct iommu_domain *dom) { return container_of(dom, struct ipmmu_vmsa_domain, io_domain); } |
e4efe4a9a iommu/ipmmu-vmsa:... |
84 |
static struct ipmmu_vmsa_device *to_ipmmu(struct device *dev) |
0fbc8b04c iommu/ipmmu-vmsa:... |
85 |
{ |
be568d6d5 iommu/renesas: Us... |
86 |
return dev_iommu_priv_get(dev); |
0fbc8b04c iommu/ipmmu-vmsa:... |
87 |
} |
d25a2a16f iommu: Add driver... |
88 89 90 91 92 |
#define TLB_LOOP_TIMEOUT 100 /* 100us */ /* ----------------------------------------------------------------------------- * Registers Definition */ |
275f5053c iommu/ipmmu-vmsa:... |
93 |
#define IM_NS_ALIAS_OFFSET 0x800 |
df9828aaa iommu/ipmmu-vmsa:... |
94 95 96 97 98 99 100 101 |
/* MMU "context" registers */ #define IMCTR 0x0000 /* R-Car Gen2/3 */ #define IMCTR_INTEN (1 << 2) /* R-Car Gen2/3 */ #define IMCTR_FLUSH (1 << 1) /* R-Car Gen2/3 */ #define IMCTR_MMUEN (1 << 0) /* R-Car Gen2/3 */ #define IMTTBCR 0x0008 /* R-Car Gen2/3 */ #define IMTTBCR_EAE (1 << 31) /* R-Car Gen2/3 */ |
3623002f0 iommu/ipmmu-vmsa:... |
102 |
#define IMTTBCR_SH0_INNER_SHAREABLE (3 << 12) /* R-Car Gen2 only */ |
3623002f0 iommu/ipmmu-vmsa:... |
103 |
#define IMTTBCR_ORGN0_WB_WA (1 << 10) /* R-Car Gen2 only */ |
3623002f0 iommu/ipmmu-vmsa:... |
104 |
#define IMTTBCR_IRGN0_WB_WA (1 << 8) /* R-Car Gen2 only */ |
5ca54fdc9 iommu/ipmmu-vmsa:... |
105 |
#define IMTTBCR_SL0_TWOBIT_LVL_1 (2 << 6) /* R-Car Gen3 only */ |
df9828aaa iommu/ipmmu-vmsa:... |
106 |
#define IMTTBCR_SL0_LVL_1 (1 << 4) /* R-Car Gen2 only */ |
d25a2a16f iommu: Add driver... |
107 |
|
df9828aaa iommu/ipmmu-vmsa:... |
108 109 110 |
#define IMBUSCR 0x000c /* R-Car Gen2 only */ #define IMBUSCR_DVM (1 << 2) /* R-Car Gen2 only */ #define IMBUSCR_BUSSEL_MASK (3 << 0) /* R-Car Gen2 only */ |
d25a2a16f iommu: Add driver... |
111 |
|
df9828aaa iommu/ipmmu-vmsa:... |
112 113 |
#define IMTTLBR0 0x0010 /* R-Car Gen2/3 */ #define IMTTUBR0 0x0014 /* R-Car Gen2/3 */ |
d25a2a16f iommu: Add driver... |
114 |
|
df9828aaa iommu/ipmmu-vmsa:... |
115 116 117 118 119 |
#define IMSTR 0x0020 /* R-Car Gen2/3 */ #define IMSTR_MHIT (1 << 4) /* R-Car Gen2/3 */ #define IMSTR_ABORT (1 << 2) /* R-Car Gen2/3 */ #define IMSTR_PF (1 << 1) /* R-Car Gen2/3 */ #define IMSTR_TF (1 << 0) /* R-Car Gen2/3 */ |
d25a2a16f iommu: Add driver... |
120 |
|
df9828aaa iommu/ipmmu-vmsa:... |
121 |
#define IMMAIR0 0x0028 /* R-Car Gen2/3 */ |
d25a2a16f iommu: Add driver... |
122 |
|
df9828aaa iommu/ipmmu-vmsa:... |
123 124 |
#define IMELAR 0x0030 /* R-Car Gen2/3, IMEAR on R-Car Gen2 */ #define IMEUAR 0x0034 /* R-Car Gen3 only */ |
d25a2a16f iommu: Add driver... |
125 |
|
df9828aaa iommu/ipmmu-vmsa:... |
126 |
/* uTLB registers */ |
ddbbddd76 iommu/ipmmu-vmsa:... |
127 |
#define IMUCTR(n) ((n) < 32 ? IMUCTR0(n) : IMUCTR32(n)) |
df9828aaa iommu/ipmmu-vmsa:... |
128 129 130 131 132 |
#define IMUCTR0(n) (0x0300 + ((n) * 16)) /* R-Car Gen2/3 */ #define IMUCTR32(n) (0x0600 + (((n) - 32) * 16)) /* R-Car Gen3 only */ #define IMUCTR_TTSEL_MMU(n) ((n) << 4) /* R-Car Gen2/3 */ #define IMUCTR_FLUSH (1 << 1) /* R-Car Gen2/3 */ #define IMUCTR_MMUEN (1 << 0) /* R-Car Gen2/3 */ |
d25a2a16f iommu: Add driver... |
133 |
|
ddbbddd76 iommu/ipmmu-vmsa:... |
134 |
#define IMUASID(n) ((n) < 32 ? IMUASID0(n) : IMUASID32(n)) |
df9828aaa iommu/ipmmu-vmsa:... |
135 136 |
#define IMUASID0(n) (0x0308 + ((n) * 16)) /* R-Car Gen2/3 */ #define IMUASID32(n) (0x0608 + (((n) - 32) * 16)) /* R-Car Gen3 only */ |
d25a2a16f iommu: Add driver... |
137 138 |
/* ----------------------------------------------------------------------------- |
fd5140e29 iommu/ipmmu-vmsa:... |
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
* Root device handling */ static struct platform_driver ipmmu_driver; static bool ipmmu_is_root(struct ipmmu_vmsa_device *mmu) { return mmu->root == mmu; } static int __ipmmu_check_device(struct device *dev, void *data) { struct ipmmu_vmsa_device *mmu = dev_get_drvdata(dev); struct ipmmu_vmsa_device **rootp = data; if (ipmmu_is_root(mmu)) *rootp = mmu; return 0; } static struct ipmmu_vmsa_device *ipmmu_find_root(void) { struct ipmmu_vmsa_device *root = NULL; return driver_for_each_device(&ipmmu_driver.driver, NULL, &root, __ipmmu_check_device) == 0 ? root : NULL; } /* ----------------------------------------------------------------------------- |
d25a2a16f iommu: Add driver... |
169 170 171 172 173 174 175 176 177 178 179 180 181 |
* Read/Write Access */ static u32 ipmmu_read(struct ipmmu_vmsa_device *mmu, unsigned int offset) { return ioread32(mmu->base + offset); } static void ipmmu_write(struct ipmmu_vmsa_device *mmu, unsigned int offset, u32 data) { iowrite32(data, mmu->base + offset); } |
16d9454f5 iommu/ipmmu-vmsa:... |
182 183 184 |
static unsigned int ipmmu_ctx_reg(struct ipmmu_vmsa_device *mmu, unsigned int context_id, unsigned int reg) { |
3dc28d9f5 iommu/ipmmu-vmsa:... |
185 186 |
return mmu->features->ctx_offset_base + context_id * mmu->features->ctx_offset_stride + reg; |
16d9454f5 iommu/ipmmu-vmsa:... |
187 188 189 190 191 192 193 194 195 196 197 198 199 |
} static u32 ipmmu_ctx_read(struct ipmmu_vmsa_device *mmu, unsigned int context_id, unsigned int reg) { return ipmmu_read(mmu, ipmmu_ctx_reg(mmu, context_id, reg)); } static void ipmmu_ctx_write(struct ipmmu_vmsa_device *mmu, unsigned int context_id, unsigned int reg, u32 data) { ipmmu_write(mmu, ipmmu_ctx_reg(mmu, context_id, reg), data); } |
d574893ae iommu/ipmmu-vmsa:... |
200 201 |
static u32 ipmmu_ctx_read_root(struct ipmmu_vmsa_domain *domain, unsigned int reg) |
d25a2a16f iommu: Add driver... |
202 |
{ |
16d9454f5 iommu/ipmmu-vmsa:... |
203 |
return ipmmu_ctx_read(domain->mmu->root, domain->context_id, reg); |
d25a2a16f iommu: Add driver... |
204 |
} |
d574893ae iommu/ipmmu-vmsa:... |
205 206 |
static void ipmmu_ctx_write_root(struct ipmmu_vmsa_domain *domain, unsigned int reg, u32 data) |
d25a2a16f iommu: Add driver... |
207 |
{ |
16d9454f5 iommu/ipmmu-vmsa:... |
208 |
ipmmu_ctx_write(domain->mmu->root, domain->context_id, reg, data); |
d25a2a16f iommu: Add driver... |
209 |
} |
d574893ae iommu/ipmmu-vmsa:... |
210 211 212 213 |
static void ipmmu_ctx_write_all(struct ipmmu_vmsa_domain *domain, unsigned int reg, u32 data) { if (domain->mmu != domain->mmu->root) |
16d9454f5 iommu/ipmmu-vmsa:... |
214 |
ipmmu_ctx_write(domain->mmu, domain->context_id, reg, data); |
d574893ae iommu/ipmmu-vmsa:... |
215 |
|
16d9454f5 iommu/ipmmu-vmsa:... |
216 |
ipmmu_ctx_write(domain->mmu->root, domain->context_id, reg, data); |
d574893ae iommu/ipmmu-vmsa:... |
217 |
} |
3667c9978 iommu/ipmmu-vmsa:... |
218 219 |
static u32 ipmmu_utlb_reg(struct ipmmu_vmsa_device *mmu, unsigned int reg) { |
1289f7f15 iommu/ipmmu-vmsa:... |
220 |
return mmu->features->utlb_offset_base + reg; |
3667c9978 iommu/ipmmu-vmsa:... |
221 222 223 224 225 226 227 |
} static void ipmmu_imuasid_write(struct ipmmu_vmsa_device *mmu, unsigned int utlb, u32 data) { ipmmu_write(mmu, ipmmu_utlb_reg(mmu, IMUASID(utlb)), data); } |
d574893ae iommu/ipmmu-vmsa:... |
228 |
|
3667c9978 iommu/ipmmu-vmsa:... |
229 230 231 232 |
static void ipmmu_imuctr_write(struct ipmmu_vmsa_device *mmu, unsigned int utlb, u32 data) { ipmmu_write(mmu, ipmmu_utlb_reg(mmu, IMUCTR(utlb)), data); |
d574893ae iommu/ipmmu-vmsa:... |
233 |
} |
d25a2a16f iommu: Add driver... |
234 235 236 237 238 239 240 241 |
/* ----------------------------------------------------------------------------- * TLB and microTLB Management */ /* Wait for any pending TLB invalidations to complete */ static void ipmmu_tlb_sync(struct ipmmu_vmsa_domain *domain) { unsigned int count = 0; |
d574893ae iommu/ipmmu-vmsa:... |
242 |
while (ipmmu_ctx_read_root(domain, IMCTR) & IMCTR_FLUSH) { |
d25a2a16f iommu: Add driver... |
243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
cpu_relax(); if (++count == TLB_LOOP_TIMEOUT) { dev_err_ratelimited(domain->mmu->dev, "TLB sync timed out -- MMU may be deadlocked "); return; } udelay(1); } } static void ipmmu_tlb_invalidate(struct ipmmu_vmsa_domain *domain) { u32 reg; |
d574893ae iommu/ipmmu-vmsa:... |
257 |
reg = ipmmu_ctx_read_root(domain, IMCTR); |
d25a2a16f iommu: Add driver... |
258 |
reg |= IMCTR_FLUSH; |
d574893ae iommu/ipmmu-vmsa:... |
259 |
ipmmu_ctx_write_all(domain, IMCTR, reg); |
d25a2a16f iommu: Add driver... |
260 261 262 263 264 265 266 267 |
ipmmu_tlb_sync(domain); } /* * Enable MMU translation for the microTLB. */ static void ipmmu_utlb_enable(struct ipmmu_vmsa_domain *domain, |
192d20457 iommu/ipmmu-vmsa:... |
268 |
unsigned int utlb) |
d25a2a16f iommu: Add driver... |
269 270 |
{ struct ipmmu_vmsa_device *mmu = domain->mmu; |
192d20457 iommu/ipmmu-vmsa:... |
271 272 273 274 |
/* * TODO: Reference-count the microTLB as several bus masters can be * connected to the same microTLB. */ |
d25a2a16f iommu: Add driver... |
275 |
/* TODO: What should we set the ASID to ? */ |
3667c9978 iommu/ipmmu-vmsa:... |
276 |
ipmmu_imuasid_write(mmu, utlb, 0); |
d25a2a16f iommu: Add driver... |
277 |
/* TODO: Do we need to flush the microTLB ? */ |
3667c9978 iommu/ipmmu-vmsa:... |
278 279 |
ipmmu_imuctr_write(mmu, utlb, IMUCTR_TTSEL_MMU(domain->context_id) | IMUCTR_FLUSH | IMUCTR_MMUEN); |
da38e9ec9 iommu/ipmmu-vmsa:... |
280 |
mmu->utlb_ctx[utlb] = domain->context_id; |
d25a2a16f iommu: Add driver... |
281 282 283 284 285 286 |
} /* * Disable MMU translation for the microTLB. */ static void ipmmu_utlb_disable(struct ipmmu_vmsa_domain *domain, |
192d20457 iommu/ipmmu-vmsa:... |
287 |
unsigned int utlb) |
d25a2a16f iommu: Add driver... |
288 289 |
{ struct ipmmu_vmsa_device *mmu = domain->mmu; |
3667c9978 iommu/ipmmu-vmsa:... |
290 |
ipmmu_imuctr_write(mmu, utlb, 0); |
da38e9ec9 iommu/ipmmu-vmsa:... |
291 |
mmu->utlb_ctx[utlb] = IPMMU_CTX_INVALID; |
d25a2a16f iommu: Add driver... |
292 |
} |
f20ed39f5 iommu/ipmmu-vmsa:... |
293 |
static void ipmmu_tlb_flush_all(void *cookie) |
d25a2a16f iommu: Add driver... |
294 |
{ |
f20ed39f5 iommu/ipmmu-vmsa:... |
295 296 297 298 |
struct ipmmu_vmsa_domain *domain = cookie; ipmmu_tlb_invalidate(domain); } |
05aed9412 iommu/io-pgtable:... |
299 300 |
static void ipmmu_tlb_flush(unsigned long iova, size_t size, size_t granule, void *cookie) |
f20ed39f5 iommu/ipmmu-vmsa:... |
301 |
{ |
05aed9412 iommu/io-pgtable:... |
302 |
ipmmu_tlb_flush_all(cookie); |
f20ed39f5 iommu/ipmmu-vmsa:... |
303 |
} |
298f78895 iommu/io-pgtable:... |
304 |
static const struct iommu_flush_ops ipmmu_flush_ops = { |
f20ed39f5 iommu/ipmmu-vmsa:... |
305 |
.tlb_flush_all = ipmmu_tlb_flush_all, |
05aed9412 iommu/io-pgtable:... |
306 307 |
.tlb_flush_walk = ipmmu_tlb_flush, .tlb_flush_leaf = ipmmu_tlb_flush, |
f20ed39f5 iommu/ipmmu-vmsa:... |
308 |
}; |
d25a2a16f iommu: Add driver... |
309 310 311 |
/* ----------------------------------------------------------------------------- * Domain/Context Management */ |
dbb706922 iommu/ipmmu-vmsa:... |
312 313 314 315 316 317 318 |
static int ipmmu_domain_allocate_context(struct ipmmu_vmsa_device *mmu, struct ipmmu_vmsa_domain *domain) { unsigned long flags; int ret; spin_lock_irqsave(&mmu->lock, flags); |
5fd163416 iommu/ipmmu-vmsa:... |
319 320 |
ret = find_first_zero_bit(mmu->ctx, mmu->num_ctx); if (ret != mmu->num_ctx) { |
dbb706922 iommu/ipmmu-vmsa:... |
321 322 |
mmu->domains[ret] = domain; set_bit(ret, mmu->ctx); |
5fd163416 iommu/ipmmu-vmsa:... |
323 324 |
} else ret = -EBUSY; |
dbb706922 iommu/ipmmu-vmsa:... |
325 326 327 328 329 |
spin_unlock_irqrestore(&mmu->lock, flags); return ret; } |
a175a67d3 iommu/ipmmu-vmsa:... |
330 331 332 333 334 335 336 337 338 339 340 341 |
static void ipmmu_domain_free_context(struct ipmmu_vmsa_device *mmu, unsigned int context_id) { unsigned long flags; spin_lock_irqsave(&mmu->lock, flags); clear_bit(context_id, mmu->ctx); mmu->domains[context_id] = NULL; spin_unlock_irqrestore(&mmu->lock, flags); } |
892db541c iommu/ipmmu-vmsa:... |
342 |
static void ipmmu_domain_setup_context(struct ipmmu_vmsa_domain *domain) |
d25a2a16f iommu: Add driver... |
343 |
{ |
f64232eee iommu/ipmmu-vmsa:... |
344 |
u64 ttbr; |
c295f504f iommu/ipmmu-vmsa:... |
345 |
u32 tmp; |
a175a67d3 iommu/ipmmu-vmsa:... |
346 |
|
d25a2a16f iommu: Add driver... |
347 |
/* TTBR0 */ |
d1e5f26f1 iommu/io-pgtable-... |
348 |
ttbr = domain->cfg.arm_lpae_s1_cfg.ttbr; |
d574893ae iommu/ipmmu-vmsa:... |
349 350 |
ipmmu_ctx_write_root(domain, IMTTLBR0, ttbr); ipmmu_ctx_write_root(domain, IMTTUBR0, ttbr >> 32); |
d25a2a16f iommu: Add driver... |
351 352 353 |
/* * TTBCR |
3623002f0 iommu/ipmmu-vmsa:... |
354 355 |
* We use long descriptors and allocate the whole 32-bit VA space to * TTBR0. |
d25a2a16f iommu: Add driver... |
356 |
*/ |
c295f504f iommu/ipmmu-vmsa:... |
357 358 359 360 |
if (domain->mmu->features->twobit_imttbcr_sl0) tmp = IMTTBCR_SL0_TWOBIT_LVL_1; else tmp = IMTTBCR_SL0_LVL_1; |
3623002f0 iommu/ipmmu-vmsa:... |
361 362 363 364 365 |
if (domain->mmu->features->cache_snoop) tmp |= IMTTBCR_SH0_INNER_SHAREABLE | IMTTBCR_ORGN0_WB_WA | IMTTBCR_IRGN0_WB_WA; ipmmu_ctx_write_root(domain, IMTTBCR, IMTTBCR_EAE | tmp); |
d25a2a16f iommu: Add driver... |
366 |
|
f20ed39f5 iommu/ipmmu-vmsa:... |
367 |
/* MAIR0 */ |
d574893ae iommu/ipmmu-vmsa:... |
368 |
ipmmu_ctx_write_root(domain, IMMAIR0, |
205577ab6 iommu/io-pgtable-... |
369 |
domain->cfg.arm_lpae_s1_cfg.mair); |
d25a2a16f iommu: Add driver... |
370 371 |
/* IMBUSCR */ |
f5c858912 iommu/ipmmu-vmsa:... |
372 373 374 375 |
if (domain->mmu->features->setup_imbuscr) ipmmu_ctx_write_root(domain, IMBUSCR, ipmmu_ctx_read_root(domain, IMBUSCR) & ~(IMBUSCR_DVM | IMBUSCR_BUSSEL_MASK)); |
d25a2a16f iommu: Add driver... |
376 377 378 379 380 |
/* * IMSTR * Clear all interrupt flags. */ |
d574893ae iommu/ipmmu-vmsa:... |
381 |
ipmmu_ctx_write_root(domain, IMSTR, ipmmu_ctx_read_root(domain, IMSTR)); |
d25a2a16f iommu: Add driver... |
382 383 384 385 386 387 388 389 |
/* * IMCTR * Enable the MMU and interrupt generation. The long-descriptor * translation table format doesn't use TEX remapping. Don't enable AF * software management as we have no use for it. Flush the TLB as * required when modifying the context registers. */ |
d574893ae iommu/ipmmu-vmsa:... |
390 391 |
ipmmu_ctx_write_all(domain, IMCTR, IMCTR_INTEN | IMCTR_FLUSH | IMCTR_MMUEN); |
892db541c iommu/ipmmu-vmsa:... |
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 |
} static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain) { int ret; /* * Allocate the page table operations. * * VMSA states in section B3.6.3 "Control of Secure or Non-secure memory * access, Long-descriptor format" that the NStable bit being set in a * table descriptor will result in the NStable and NS bits of all child * entries being ignored and considered as being set. The IPMMU seems * not to comply with this, as it generates a secure access page fault * if any of the NStable and NS bits isn't set when running in * non-secure mode. */ domain->cfg.quirks = IO_PGTABLE_QUIRK_ARM_NS; domain->cfg.pgsize_bitmap = SZ_1G | SZ_2M | SZ_4K; domain->cfg.ias = 32; domain->cfg.oas = 40; |
298f78895 iommu/io-pgtable:... |
413 |
domain->cfg.tlb = &ipmmu_flush_ops; |
892db541c iommu/ipmmu-vmsa:... |
414 415 416 417 418 419 |
domain->io_domain.geometry.aperture_end = DMA_BIT_MASK(32); domain->io_domain.geometry.force_aperture = true; /* * TODO: Add support for coherent walk through CCI with DVM and remove * cache handling. For now, delegate it to the io-pgtable code. */ |
3430abd6f Merge branch 'arm... |
420 |
domain->cfg.coherent_walk = false; |
892db541c iommu/ipmmu-vmsa:... |
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 |
domain->cfg.iommu_dev = domain->mmu->root->dev; /* * Find an unused context. */ ret = ipmmu_domain_allocate_context(domain->mmu->root, domain); if (ret < 0) return ret; domain->context_id = ret; domain->iop = alloc_io_pgtable_ops(ARM_32_LPAE_S1, &domain->cfg, domain); if (!domain->iop) { ipmmu_domain_free_context(domain->mmu->root, domain->context_id); return -EINVAL; } |
d25a2a16f iommu: Add driver... |
439 |
|
892db541c iommu/ipmmu-vmsa:... |
440 |
ipmmu_domain_setup_context(domain); |
d25a2a16f iommu: Add driver... |
441 442 443 444 445 |
return 0; } static void ipmmu_domain_destroy_context(struct ipmmu_vmsa_domain *domain) { |
e5b78f2e3 iommu/ipmmu-vmsa:... |
446 447 |
if (!domain->mmu) return; |
d25a2a16f iommu: Add driver... |
448 449 450 451 452 453 |
/* * Disable the context. Flush the TLB as required when modifying the * context registers. * * TODO: Is TLB flush really needed ? */ |
d574893ae iommu/ipmmu-vmsa:... |
454 |
ipmmu_ctx_write_all(domain, IMCTR, IMCTR_FLUSH); |
d25a2a16f iommu: Add driver... |
455 |
ipmmu_tlb_sync(domain); |
fd5140e29 iommu/ipmmu-vmsa:... |
456 |
ipmmu_domain_free_context(domain->mmu->root, domain->context_id); |
d25a2a16f iommu: Add driver... |
457 458 459 460 461 462 463 464 465 466 |
} /* ----------------------------------------------------------------------------- * Fault Handling */ static irqreturn_t ipmmu_domain_irq(struct ipmmu_vmsa_domain *domain) { const u32 err_mask = IMSTR_MHIT | IMSTR_ABORT | IMSTR_PF | IMSTR_TF; struct ipmmu_vmsa_device *mmu = domain->mmu; |
82576aa8a iommu/ipmmu-vmsa:... |
467 |
unsigned long iova; |
d25a2a16f iommu: Add driver... |
468 |
u32 status; |
d25a2a16f iommu: Add driver... |
469 |
|
d574893ae iommu/ipmmu-vmsa:... |
470 |
status = ipmmu_ctx_read_root(domain, IMSTR); |
d25a2a16f iommu: Add driver... |
471 472 |
if (!(status & err_mask)) return IRQ_NONE; |
82576aa8a iommu/ipmmu-vmsa:... |
473 474 475 |
iova = ipmmu_ctx_read_root(domain, IMELAR); if (IS_ENABLED(CONFIG_64BIT)) iova |= (u64)ipmmu_ctx_read_root(domain, IMEUAR) << 32; |
d25a2a16f iommu: Add driver... |
476 477 478 479 480 481 482 |
/* * Clear the error status flags. Unlike traditional interrupt flag * registers that must be cleared by writing 1, this status register * seems to require 0. The error address register must be read before, * otherwise its value will be 0. */ |
d574893ae iommu/ipmmu-vmsa:... |
483 |
ipmmu_ctx_write_root(domain, IMSTR, 0); |
d25a2a16f iommu: Add driver... |
484 485 486 |
/* Log fatal errors. */ if (status & IMSTR_MHIT) |
82576aa8a iommu/ipmmu-vmsa:... |
487 488 |
dev_err_ratelimited(mmu->dev, "Multiple TLB hits @0x%lx ", |
d25a2a16f iommu: Add driver... |
489 490 |
iova); if (status & IMSTR_ABORT) |
82576aa8a iommu/ipmmu-vmsa:... |
491 492 |
dev_err_ratelimited(mmu->dev, "Page Table Walk Abort @0x%lx ", |
d25a2a16f iommu: Add driver... |
493 494 495 496 497 498 499 500 501 502 503 |
iova); if (!(status & (IMSTR_PF | IMSTR_TF))) return IRQ_NONE; /* * Try to handle page faults and translation faults. * * TODO: We need to look up the faulty device based on the I/O VA. Use * the IOMMU device for now. */ |
5914c5fdd iommu/ipmmu-vmsa:... |
504 |
if (!report_iommu_fault(&domain->io_domain, mmu->dev, iova, 0)) |
d25a2a16f iommu: Add driver... |
505 506 507 |
return IRQ_HANDLED; dev_err_ratelimited(mmu->dev, |
82576aa8a iommu/ipmmu-vmsa:... |
508 509 |
"Unhandled fault: status 0x%08x iova 0x%lx ", |
d25a2a16f iommu: Add driver... |
510 511 512 513 514 515 516 517 |
status, iova); return IRQ_HANDLED; } static irqreturn_t ipmmu_irq(int irq, void *dev) { struct ipmmu_vmsa_device *mmu = dev; |
dbb706922 iommu/ipmmu-vmsa:... |
518 519 520 |
irqreturn_t status = IRQ_NONE; unsigned int i; unsigned long flags; |
d25a2a16f iommu: Add driver... |
521 |
|
dbb706922 iommu/ipmmu-vmsa:... |
522 523 524 525 526 |
spin_lock_irqsave(&mmu->lock, flags); /* * Check interrupts for all active contexts. */ |
5fd163416 iommu/ipmmu-vmsa:... |
527 |
for (i = 0; i < mmu->num_ctx; i++) { |
dbb706922 iommu/ipmmu-vmsa:... |
528 529 530 531 532 |
if (!mmu->domains[i]) continue; if (ipmmu_domain_irq(mmu->domains[i]) == IRQ_HANDLED) status = IRQ_HANDLED; } |
d25a2a16f iommu: Add driver... |
533 |
|
dbb706922 iommu/ipmmu-vmsa:... |
534 |
spin_unlock_irqrestore(&mmu->lock, flags); |
d25a2a16f iommu: Add driver... |
535 |
|
dbb706922 iommu/ipmmu-vmsa:... |
536 |
return status; |
d25a2a16f iommu: Add driver... |
537 538 539 |
} /* ----------------------------------------------------------------------------- |
d25a2a16f iommu: Add driver... |
540 541 |
* IOMMU Operations */ |
8e73bf659 iommu/ipmmu-vmsa:... |
542 |
static struct iommu_domain *__ipmmu_domain_alloc(unsigned type) |
d25a2a16f iommu: Add driver... |
543 544 545 546 547 |
{ struct ipmmu_vmsa_domain *domain; domain = kzalloc(sizeof(*domain), GFP_KERNEL); if (!domain) |
5914c5fdd iommu/ipmmu-vmsa:... |
548 |
return NULL; |
d25a2a16f iommu: Add driver... |
549 |
|
46583e8c4 iommu/ipmmu-vmsa:... |
550 |
mutex_init(&domain->mutex); |
d25a2a16f iommu: Add driver... |
551 |
|
5914c5fdd iommu/ipmmu-vmsa:... |
552 |
return &domain->io_domain; |
d25a2a16f iommu: Add driver... |
553 |
} |
1c7e7c027 iommu/ipmmu-vmsa:... |
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 |
static struct iommu_domain *ipmmu_domain_alloc(unsigned type) { struct iommu_domain *io_domain = NULL; switch (type) { case IOMMU_DOMAIN_UNMANAGED: io_domain = __ipmmu_domain_alloc(type); break; case IOMMU_DOMAIN_DMA: io_domain = __ipmmu_domain_alloc(type); if (io_domain && iommu_get_dma_cookie(io_domain)) { kfree(io_domain); io_domain = NULL; } break; } return io_domain; } |
5914c5fdd iommu/ipmmu-vmsa:... |
574 |
static void ipmmu_domain_free(struct iommu_domain *io_domain) |
d25a2a16f iommu: Add driver... |
575 |
{ |
5914c5fdd iommu/ipmmu-vmsa:... |
576 |
struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); |
d25a2a16f iommu: Add driver... |
577 578 579 580 581 |
/* * Free the domain resources. We assume that all devices have already * been detached. */ |
1c7e7c027 iommu/ipmmu-vmsa:... |
582 |
iommu_put_dma_cookie(io_domain); |
d25a2a16f iommu: Add driver... |
583 |
ipmmu_domain_destroy_context(domain); |
f20ed39f5 iommu/ipmmu-vmsa:... |
584 |
free_io_pgtable_ops(domain->iop); |
d25a2a16f iommu: Add driver... |
585 586 587 588 589 590 |
kfree(domain); } static int ipmmu_attach_device(struct iommu_domain *io_domain, struct device *dev) { |
df9036558 iommu/ipmmu-vmsa:... |
591 |
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); |
e4efe4a9a iommu/ipmmu-vmsa:... |
592 |
struct ipmmu_vmsa_device *mmu = to_ipmmu(dev); |
5914c5fdd iommu/ipmmu-vmsa:... |
593 |
struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); |
a166d31ee iommu/ipmmu-vmsa:... |
594 |
unsigned int i; |
d25a2a16f iommu: Add driver... |
595 |
int ret = 0; |
e4efe4a9a iommu/ipmmu-vmsa:... |
596 |
if (!mmu) { |
d25a2a16f iommu: Add driver... |
597 598 599 600 |
dev_err(dev, "Cannot attach to IPMMU "); return -ENXIO; } |
46583e8c4 iommu/ipmmu-vmsa:... |
601 |
mutex_lock(&domain->mutex); |
d25a2a16f iommu: Add driver... |
602 603 604 605 606 |
if (!domain->mmu) { /* The domain hasn't been used yet, initialize it. */ domain->mmu = mmu; ret = ipmmu_domain_init_context(domain); |
5fd163416 iommu/ipmmu-vmsa:... |
607 608 609 610 611 612 613 614 615 |
if (ret < 0) { dev_err(dev, "Unable to initialize IPMMU context "); domain->mmu = NULL; } else { dev_info(dev, "Using IPMMU context %u ", domain->context_id); } |
d25a2a16f iommu: Add driver... |
616 617 618 619 620 621 622 623 624 |
} else if (domain->mmu != mmu) { /* * Something is wrong, we can't attach two devices using * different IOMMUs to the same domain. */ dev_err(dev, "Can't attach IPMMU %s to domain on IPMMU %s ", dev_name(mmu->dev), dev_name(domain->mmu->dev)); ret = -EINVAL; |
3ae472920 iommu/ipmmu-vmsa:... |
625 626 627 |
} else dev_info(dev, "Reusing IPMMU context %u ", domain->context_id); |
d25a2a16f iommu: Add driver... |
628 |
|
46583e8c4 iommu/ipmmu-vmsa:... |
629 |
mutex_unlock(&domain->mutex); |
d25a2a16f iommu: Add driver... |
630 631 632 |
if (ret < 0) return ret; |
7b2d59611 iommu/ipmmu-vmsa:... |
633 634 |
for (i = 0; i < fwspec->num_ids; ++i) ipmmu_utlb_enable(domain, fwspec->ids[i]); |
d25a2a16f iommu: Add driver... |
635 636 637 638 639 640 641 |
return 0; } static void ipmmu_detach_device(struct iommu_domain *io_domain, struct device *dev) { |
df9036558 iommu/ipmmu-vmsa:... |
642 |
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); |
5914c5fdd iommu/ipmmu-vmsa:... |
643 |
struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); |
a166d31ee iommu/ipmmu-vmsa:... |
644 |
unsigned int i; |
d25a2a16f iommu: Add driver... |
645 |
|
7b2d59611 iommu/ipmmu-vmsa:... |
646 647 |
for (i = 0; i < fwspec->num_ids; ++i) ipmmu_utlb_disable(domain, fwspec->ids[i]); |
d25a2a16f iommu: Add driver... |
648 649 650 651 652 653 654 |
/* * TODO: Optimize by disabling the context when no device is attached. */ } static int ipmmu_map(struct iommu_domain *io_domain, unsigned long iova, |
781ca2de8 iommu: Add gfp pa... |
655 |
phys_addr_t paddr, size_t size, int prot, gfp_t gfp) |
d25a2a16f iommu: Add driver... |
656 |
{ |
5914c5fdd iommu/ipmmu-vmsa:... |
657 |
struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); |
d25a2a16f iommu: Add driver... |
658 659 660 |
if (!domain) return -ENODEV; |
f34ce7a70 iommu: Add gfp pa... |
661 |
return domain->iop->map(domain->iop, iova, paddr, size, prot, gfp); |
d25a2a16f iommu: Add driver... |
662 663 664 |
} static size_t ipmmu_unmap(struct iommu_domain *io_domain, unsigned long iova, |
56f8af5e9 iommu: Pass struc... |
665 |
size_t size, struct iommu_iotlb_gather *gather) |
d25a2a16f iommu: Add driver... |
666 |
{ |
5914c5fdd iommu/ipmmu-vmsa:... |
667 |
struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); |
d25a2a16f iommu: Add driver... |
668 |
|
a2d3a382d iommu/io-pgtable:... |
669 |
return domain->iop->unmap(domain->iop, iova, size, gather); |
d25a2a16f iommu: Add driver... |
670 |
} |
56f8af5e9 iommu: Pass struc... |
671 |
static void ipmmu_flush_iotlb_all(struct iommu_domain *io_domain) |
32b124492 iommu/io-pgtable-... |
672 673 674 675 676 677 |
{ struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); if (domain->mmu) ipmmu_tlb_flush_all(domain); } |
56f8af5e9 iommu: Pass struc... |
678 679 680 681 682 |
static void ipmmu_iotlb_sync(struct iommu_domain *io_domain, struct iommu_iotlb_gather *gather) { ipmmu_flush_iotlb_all(io_domain); } |
d25a2a16f iommu: Add driver... |
683 684 685 |
static phys_addr_t ipmmu_iova_to_phys(struct iommu_domain *io_domain, dma_addr_t iova) { |
5914c5fdd iommu/ipmmu-vmsa:... |
686 |
struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); |
d25a2a16f iommu: Add driver... |
687 688 |
/* TODO: Is locking needed ? */ |
f20ed39f5 iommu/ipmmu-vmsa:... |
689 |
return domain->iop->iova_to_phys(domain->iop, iova); |
d25a2a16f iommu: Add driver... |
690 |
} |
7b2d59611 iommu/ipmmu-vmsa:... |
691 692 |
static int ipmmu_init_platform_device(struct device *dev, struct of_phandle_args *args) |
d25a2a16f iommu: Add driver... |
693 |
{ |
7b2d59611 iommu/ipmmu-vmsa:... |
694 |
struct platform_device *ipmmu_pdev; |
bb590c901 iommu/ipmmu-vmsa:... |
695 |
|
7b2d59611 iommu/ipmmu-vmsa:... |
696 697 |
ipmmu_pdev = of_find_device_by_node(args->np); if (!ipmmu_pdev) |
bb590c901 iommu/ipmmu-vmsa:... |
698 |
return -ENODEV; |
be568d6d5 iommu/renesas: Us... |
699 |
dev_iommu_priv_set(dev, platform_get_drvdata(ipmmu_pdev)); |
383fef5f4 iommu/ipmmu-vmsa:... |
700 |
|
383fef5f4 iommu/ipmmu-vmsa:... |
701 |
return 0; |
58b8e8bf4 iommu/ipmmu-vmsa:... |
702 |
} |
0b8ac1409 iommu/ipmmu-vmsa:... |
703 |
static const struct soc_device_attribute soc_rcar_gen3[] = { |
60fb0083c iommu/ipmmu-vmsa:... |
704 |
{ .soc_id = "r8a774a1", }, |
757f26a3a iommu/ipmmu-vmsa:... |
705 |
{ .soc_id = "r8a774b1", }, |
b6d39cd82 iommu/ipmmu-vmsa:... |
706 |
{ .soc_id = "r8a774c0", }, |
4b2aa7a6f iommu/ipmmu-vmsa:... |
707 |
{ .soc_id = "r8a774e1", }, |
58b8e8bf4 iommu/ipmmu-vmsa:... |
708 |
{ .soc_id = "r8a7795", }, |
f3e048b78 iommu/ipmmu-vmsa:... |
709 |
{ .soc_id = "r8a77961", }, |
0b8ac1409 iommu/ipmmu-vmsa:... |
710 |
{ .soc_id = "r8a7796", }, |
98dbffd39 iommu/ipmmu-vmsa:... |
711 |
{ .soc_id = "r8a77965", }, |
3701c123e iommu/ipmmu-vmsa:... |
712 |
{ .soc_id = "r8a77970", }, |
b0c329121 iommu/ipmmu-vmsa:... |
713 |
{ .soc_id = "r8a77990", }, |
3701c123e iommu/ipmmu-vmsa:... |
714 |
{ .soc_id = "r8a77995", }, |
58b8e8bf4 iommu/ipmmu-vmsa:... |
715 716 |
{ /* sentinel */ } }; |
b7ee92c6f iommu/ipmmu-vmsa:... |
717 |
static const struct soc_device_attribute soc_rcar_gen3_whitelist[] = { |
757f26a3a iommu/ipmmu-vmsa:... |
718 |
{ .soc_id = "r8a774b1", }, |
b6d39cd82 iommu/ipmmu-vmsa:... |
719 |
{ .soc_id = "r8a774c0", }, |
4b2aa7a6f iommu/ipmmu-vmsa:... |
720 |
{ .soc_id = "r8a774e1", }, |
b7ee92c6f iommu/ipmmu-vmsa:... |
721 |
{ .soc_id = "r8a7795", .revision = "ES3.*" }, |
17fe16181 iommu/renesas: Ad... |
722 |
{ .soc_id = "r8a77961", }, |
b7ee92c6f iommu/ipmmu-vmsa:... |
723 724 |
{ .soc_id = "r8a77965", }, { .soc_id = "r8a77990", }, |
3701c123e iommu/ipmmu-vmsa:... |
725 |
{ .soc_id = "r8a77995", }, |
58b8e8bf4 iommu/ipmmu-vmsa:... |
726 727 |
{ /* sentinel */ } }; |
807596491 iommu/ipmmu-vmsa:... |
728 729 |
static const char * const rcar_gen3_slave_whitelist[] = { }; |
b7ee92c6f iommu/ipmmu-vmsa:... |
730 731 |
static bool ipmmu_slave_whitelist(struct device *dev) { |
807596491 iommu/ipmmu-vmsa:... |
732 |
unsigned int i; |
b7ee92c6f iommu/ipmmu-vmsa:... |
733 734 735 736 737 738 739 740 741 742 |
/* * For R-Car Gen3 use a white list to opt-in slave devices. * For Other SoCs, this returns true anyway. */ if (!soc_device_match(soc_rcar_gen3)) return true; /* Check whether this R-Car Gen3 can use the IPMMU correctly or not */ if (!soc_device_match(soc_rcar_gen3_whitelist)) return false; |
807596491 iommu/ipmmu-vmsa:... |
743 744 745 746 747 748 749 |
/* Check whether this slave device can work with the IPMMU */ for (i = 0; i < ARRAY_SIZE(rcar_gen3_slave_whitelist); i++) { if (!strcmp(dev_name(dev), rcar_gen3_slave_whitelist[i])) return true; } /* Otherwise, do not allow use of IPMMU */ |
b7ee92c6f iommu/ipmmu-vmsa:... |
750 751 |
return false; } |
49558da03 iommu/ipmmu-vmsa:... |
752 753 754 |
static int ipmmu_of_xlate(struct device *dev, struct of_phandle_args *spec) { |
b7ee92c6f iommu/ipmmu-vmsa:... |
755 |
if (!ipmmu_slave_whitelist(dev)) |
58b8e8bf4 iommu/ipmmu-vmsa:... |
756 |
return -ENODEV; |
7b2d59611 iommu/ipmmu-vmsa:... |
757 |
iommu_fwspec_add_ids(dev, spec->args, 1); |
49558da03 iommu/ipmmu-vmsa:... |
758 |
/* Initialize once - xlate() will call multiple times */ |
e4efe4a9a iommu/ipmmu-vmsa:... |
759 |
if (to_ipmmu(dev)) |
49558da03 iommu/ipmmu-vmsa:... |
760 |
return 0; |
7b2d59611 iommu/ipmmu-vmsa:... |
761 |
return ipmmu_init_platform_device(dev, spec); |
49558da03 iommu/ipmmu-vmsa:... |
762 |
} |
49c875f03 iommu/ipmmu-vmsa:... |
763 |
static int ipmmu_init_arm_mapping(struct device *dev) |
383fef5f4 iommu/ipmmu-vmsa:... |
764 |
{ |
e4efe4a9a iommu/ipmmu-vmsa:... |
765 |
struct ipmmu_vmsa_device *mmu = to_ipmmu(dev); |
383fef5f4 iommu/ipmmu-vmsa:... |
766 |
int ret; |
d25a2a16f iommu: Add driver... |
767 768 769 770 771 772 773 774 775 776 777 778 779 |
/* * Create the ARM mapping, used by the ARM DMA mapping core to allocate * VAs. This will allocate a corresponding IOMMU domain. * * TODO: * - Create one mapping per context (TLB). * - Make the mapping size configurable ? We currently use a 2GB mapping * at a 1GB offset to ensure that NULL VAs will fault. */ if (!mmu->mapping) { struct dma_iommu_mapping *mapping; mapping = arm_iommu_create_mapping(&platform_bus_type, |
720b0cef7 arm/ipmmu-vmsa: F... |
780 |
SZ_1G, SZ_2G); |
d25a2a16f iommu: Add driver... |
781 782 783 |
if (IS_ERR(mapping)) { dev_err(mmu->dev, "failed to create ARM IOMMU mapping "); |
b8f80bffd iommu/ipmmu-vmsa:... |
784 785 |
ret = PTR_ERR(mapping); goto error; |
d25a2a16f iommu: Add driver... |
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 |
} mmu->mapping = mapping; } /* Attach the ARM VA mapping to the device. */ ret = arm_iommu_attach_device(dev, mmu->mapping); if (ret < 0) { dev_err(dev, "Failed to attach device to VA mapping "); goto error; } return 0; error: |
49c875f03 iommu/ipmmu-vmsa:... |
802 |
if (mmu->mapping) |
383fef5f4 iommu/ipmmu-vmsa:... |
803 |
arm_iommu_release_mapping(mmu->mapping); |
a166d31ee iommu/ipmmu-vmsa:... |
804 |
|
d25a2a16f iommu: Add driver... |
805 806 |
return ret; } |
6580c8a78 iommu/renesas: Co... |
807 |
static struct iommu_device *ipmmu_probe_device(struct device *dev) |
3ae472920 iommu/ipmmu-vmsa:... |
808 |
{ |
80eaa9f55 iommu/ipmmu-vmsa:... |
809 |
struct ipmmu_vmsa_device *mmu = to_ipmmu(dev); |
3ae472920 iommu/ipmmu-vmsa:... |
810 |
|
0fbc8b04c iommu/ipmmu-vmsa:... |
811 812 |
/* * Only let through devices that have been verified in xlate() |
0fbc8b04c iommu/ipmmu-vmsa:... |
813 |
*/ |
80eaa9f55 iommu/ipmmu-vmsa:... |
814 |
if (!mmu) |
6580c8a78 iommu/renesas: Co... |
815 |
return ERR_PTR(-ENODEV); |
3ae472920 iommu/ipmmu-vmsa:... |
816 |
|
6580c8a78 iommu/renesas: Co... |
817 818 819 820 821 822 823 824 |
return &mmu->iommu; } static void ipmmu_probe_finalize(struct device *dev) { int ret = 0; if (IS_ENABLED(CONFIG_ARM) && !IS_ENABLED(CONFIG_IOMMU_DMA)) |
80eaa9f55 iommu/ipmmu-vmsa:... |
825 |
ret = ipmmu_init_arm_mapping(dev); |
3ae472920 iommu/ipmmu-vmsa:... |
826 |
|
6580c8a78 iommu/renesas: Co... |
827 828 829 |
if (ret) dev_err(dev, "Can't create IOMMU mapping - DMA-OPS will not work "); |
3ae472920 iommu/ipmmu-vmsa:... |
830 |
} |
6580c8a78 iommu/renesas: Co... |
831 |
static void ipmmu_release_device(struct device *dev) |
3ae472920 iommu/ipmmu-vmsa:... |
832 |
{ |
49c875f03 iommu/ipmmu-vmsa:... |
833 |
arm_iommu_detach_device(dev); |
3ae472920 iommu/ipmmu-vmsa:... |
834 |
} |
b354c73ed iommu/ipmmu-vmsa:... |
835 |
static struct iommu_group *ipmmu_find_group(struct device *dev) |
3ae472920 iommu/ipmmu-vmsa:... |
836 |
{ |
e4efe4a9a iommu/ipmmu-vmsa:... |
837 |
struct ipmmu_vmsa_device *mmu = to_ipmmu(dev); |
3ae472920 iommu/ipmmu-vmsa:... |
838 |
struct iommu_group *group; |
3ae472920 iommu/ipmmu-vmsa:... |
839 |
|
e4efe4a9a iommu/ipmmu-vmsa:... |
840 841 |
if (mmu->group) return iommu_group_ref_get(mmu->group); |
b354c73ed iommu/ipmmu-vmsa:... |
842 843 844 |
group = iommu_group_alloc(); if (!IS_ERR(group)) |
e4efe4a9a iommu/ipmmu-vmsa:... |
845 |
mmu->group = group; |
3ae472920 iommu/ipmmu-vmsa:... |
846 847 848 |
return group; } |
3ae472920 iommu/ipmmu-vmsa:... |
849 |
static const struct iommu_ops ipmmu_ops = { |
1c7e7c027 iommu/ipmmu-vmsa:... |
850 851 |
.domain_alloc = ipmmu_domain_alloc, .domain_free = ipmmu_domain_free, |
3ae472920 iommu/ipmmu-vmsa:... |
852 853 854 855 |
.attach_dev = ipmmu_attach_device, .detach_dev = ipmmu_detach_device, .map = ipmmu_map, .unmap = ipmmu_unmap, |
56f8af5e9 iommu: Pass struc... |
856 |
.flush_iotlb_all = ipmmu_flush_iotlb_all, |
32b124492 iommu/io-pgtable-... |
857 |
.iotlb_sync = ipmmu_iotlb_sync, |
3ae472920 iommu/ipmmu-vmsa:... |
858 |
.iova_to_phys = ipmmu_iova_to_phys, |
6580c8a78 iommu/renesas: Co... |
859 860 861 |
.probe_device = ipmmu_probe_device, .release_device = ipmmu_release_device, .probe_finalize = ipmmu_probe_finalize, |
2ba20b5a5 iommu/renesas: Fi... |
862 863 |
.device_group = IS_ENABLED(CONFIG_ARM) && !IS_ENABLED(CONFIG_IOMMU_DMA) ? generic_device_group : ipmmu_find_group, |
3ae472920 iommu/ipmmu-vmsa:... |
864 |
.pgsize_bitmap = SZ_1G | SZ_2M | SZ_4K, |
49558da03 iommu/ipmmu-vmsa:... |
865 |
.of_xlate = ipmmu_of_xlate, |
3ae472920 iommu/ipmmu-vmsa:... |
866 |
}; |
d25a2a16f iommu: Add driver... |
867 868 869 870 871 872 873 874 875 |
/* ----------------------------------------------------------------------------- * Probe/remove and init */ static void ipmmu_device_reset(struct ipmmu_vmsa_device *mmu) { unsigned int i; /* Disable all contexts. */ |
5fd163416 iommu/ipmmu-vmsa:... |
876 |
for (i = 0; i < mmu->num_ctx; ++i) |
16d9454f5 iommu/ipmmu-vmsa:... |
877 |
ipmmu_ctx_write(mmu, i, IMCTR, 0); |
d25a2a16f iommu: Add driver... |
878 |
} |
33f3ac9b5 iommu/ipmmu-vmsa:... |
879 880 |
static const struct ipmmu_features ipmmu_features_default = { .use_ns_alias_offset = true, |
fd5140e29 iommu/ipmmu-vmsa:... |
881 |
.has_cache_leaf_nodes = false, |
5fd163416 iommu/ipmmu-vmsa:... |
882 |
.number_of_contexts = 1, /* software only tested with one context */ |
b7f3f047a iommu/ipmmu-vmsa:... |
883 |
.num_utlbs = 32, |
f5c858912 iommu/ipmmu-vmsa:... |
884 |
.setup_imbuscr = true, |
c295f504f iommu/ipmmu-vmsa:... |
885 |
.twobit_imttbcr_sl0 = false, |
2ae869557 iommu/ipmmu-vmsa:... |
886 |
.reserved_context = false, |
3623002f0 iommu/ipmmu-vmsa:... |
887 |
.cache_snoop = true, |
3dc28d9f5 iommu/ipmmu-vmsa:... |
888 889 |
.ctx_offset_base = 0, .ctx_offset_stride = 0x40, |
1289f7f15 iommu/ipmmu-vmsa:... |
890 |
.utlb_offset_base = 0, |
33f3ac9b5 iommu/ipmmu-vmsa:... |
891 |
}; |
0b8ac1409 iommu/ipmmu-vmsa:... |
892 |
static const struct ipmmu_features ipmmu_features_rcar_gen3 = { |
58b8e8bf4 iommu/ipmmu-vmsa:... |
893 894 895 |
.use_ns_alias_offset = false, .has_cache_leaf_nodes = true, .number_of_contexts = 8, |
b7f3f047a iommu/ipmmu-vmsa:... |
896 |
.num_utlbs = 48, |
58b8e8bf4 iommu/ipmmu-vmsa:... |
897 898 |
.setup_imbuscr = false, .twobit_imttbcr_sl0 = true, |
2ae869557 iommu/ipmmu-vmsa:... |
899 |
.reserved_context = true, |
3623002f0 iommu/ipmmu-vmsa:... |
900 |
.cache_snoop = false, |
3dc28d9f5 iommu/ipmmu-vmsa:... |
901 902 |
.ctx_offset_base = 0, .ctx_offset_stride = 0x40, |
1289f7f15 iommu/ipmmu-vmsa:... |
903 |
.utlb_offset_base = 0, |
58b8e8bf4 iommu/ipmmu-vmsa:... |
904 |
}; |
33f3ac9b5 iommu/ipmmu-vmsa:... |
905 906 907 908 909 |
static const struct of_device_id ipmmu_of_ids[] = { { .compatible = "renesas,ipmmu-vmsa", .data = &ipmmu_features_default, }, { |
60fb0083c iommu/ipmmu-vmsa:... |
910 911 912 |
.compatible = "renesas,ipmmu-r8a774a1", .data = &ipmmu_features_rcar_gen3, }, { |
757f26a3a iommu/ipmmu-vmsa:... |
913 914 915 |
.compatible = "renesas,ipmmu-r8a774b1", .data = &ipmmu_features_rcar_gen3, }, { |
b6d39cd82 iommu/ipmmu-vmsa:... |
916 917 918 |
.compatible = "renesas,ipmmu-r8a774c0", .data = &ipmmu_features_rcar_gen3, }, { |
4b2aa7a6f iommu/ipmmu-vmsa:... |
919 920 921 |
.compatible = "renesas,ipmmu-r8a774e1", .data = &ipmmu_features_rcar_gen3, }, { |
58b8e8bf4 iommu/ipmmu-vmsa:... |
922 |
.compatible = "renesas,ipmmu-r8a7795", |
0b8ac1409 iommu/ipmmu-vmsa:... |
923 924 925 926 |
.data = &ipmmu_features_rcar_gen3, }, { .compatible = "renesas,ipmmu-r8a7796", .data = &ipmmu_features_rcar_gen3, |
58b8e8bf4 iommu/ipmmu-vmsa:... |
927 |
}, { |
17fe16181 iommu/renesas: Ad... |
928 929 930 |
.compatible = "renesas,ipmmu-r8a77961", .data = &ipmmu_features_rcar_gen3, }, { |
98dbffd39 iommu/ipmmu-vmsa:... |
931 932 933 |
.compatible = "renesas,ipmmu-r8a77965", .data = &ipmmu_features_rcar_gen3, }, { |
3701c123e iommu/ipmmu-vmsa:... |
934 935 936 |
.compatible = "renesas,ipmmu-r8a77970", .data = &ipmmu_features_rcar_gen3, }, { |
b0c329121 iommu/ipmmu-vmsa:... |
937 938 939 |
.compatible = "renesas,ipmmu-r8a77990", .data = &ipmmu_features_rcar_gen3, }, { |
3701c123e iommu/ipmmu-vmsa:... |
940 941 |
.compatible = "renesas,ipmmu-r8a77995", .data = &ipmmu_features_rcar_gen3, |
58b8e8bf4 iommu/ipmmu-vmsa:... |
942 |
}, { |
33f3ac9b5 iommu/ipmmu-vmsa:... |
943 944 945 |
/* Terminator */ }, }; |
d25a2a16f iommu: Add driver... |
946 947 948 949 950 951 |
static int ipmmu_probe(struct platform_device *pdev) { struct ipmmu_vmsa_device *mmu; struct resource *res; int irq; int ret; |
d25a2a16f iommu: Add driver... |
952 953 954 955 956 957 958 959 |
mmu = devm_kzalloc(&pdev->dev, sizeof(*mmu), GFP_KERNEL); if (!mmu) { dev_err(&pdev->dev, "cannot allocate device data "); return -ENOMEM; } mmu->dev = &pdev->dev; |
dbb706922 iommu/ipmmu-vmsa:... |
960 961 |
spin_lock_init(&mmu->lock); bitmap_zero(mmu->ctx, IPMMU_CTX_MAX); |
33f3ac9b5 iommu/ipmmu-vmsa:... |
962 |
mmu->features = of_device_get_match_data(&pdev->dev); |
da38e9ec9 iommu/ipmmu-vmsa:... |
963 |
memset(mmu->utlb_ctx, IPMMU_CTX_INVALID, mmu->features->num_utlbs); |
1c894225b iommu/ipmmu-vmsa:... |
964 |
dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40)); |
d25a2a16f iommu: Add driver... |
965 966 967 968 969 970 |
/* Map I/O memory and request IRQ. */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mmu->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(mmu->base)) return PTR_ERR(mmu->base); |
275f5053c iommu/ipmmu-vmsa:... |
971 972 973 974 975 976 977 978 979 980 981 982 |
/* * The IPMMU has two register banks, for secure and non-secure modes. * The bank mapped at the beginning of the IPMMU address space * corresponds to the running mode of the CPU. When running in secure * mode the non-secure register bank is also available at an offset. * * Secure mode operation isn't clearly documented and is thus currently * not implemented in the driver. Furthermore, preliminary tests of * non-secure operation with the main register bank were not successful. * Offset the registers base unconditionally to point to the non-secure * alias space for now. */ |
33f3ac9b5 iommu/ipmmu-vmsa:... |
983 984 |
if (mmu->features->use_ns_alias_offset) mmu->base += IM_NS_ALIAS_OFFSET; |
275f5053c iommu/ipmmu-vmsa:... |
985 |
|
b43e0d8a4 iommu/ipmmu-vmsa:... |
986 |
mmu->num_ctx = min(IPMMU_CTX_MAX, mmu->features->number_of_contexts); |
5fd163416 iommu/ipmmu-vmsa:... |
987 |
|
fd5140e29 iommu/ipmmu-vmsa:... |
988 989 990 991 992 993 994 995 996 |
/* * Determine if this IPMMU instance is a root device by checking for * the lack of has_cache_leaf_nodes flag or renesas,ipmmu-main property. */ if (!mmu->features->has_cache_leaf_nodes || !of_find_property(pdev->dev.of_node, "renesas,ipmmu-main", NULL)) mmu->root = mmu; else mmu->root = ipmmu_find_root(); |
d25a2a16f iommu: Add driver... |
997 |
|
fd5140e29 iommu/ipmmu-vmsa:... |
998 999 1000 1001 1002 1003 1004 1005 |
/* * Wait until the root device has been registered for sure. */ if (!mmu->root) return -EPROBE_DEFER; /* Root devices have mandatory IRQs */ if (ipmmu_is_root(mmu)) { |
ec37d4e99 iommu/ipmmu-vmsa:... |
1006 |
irq = platform_get_irq(pdev, 0); |
565d45428 iommu/ipmmu-vmsa:... |
1007 |
if (irq < 0) |
fd5140e29 iommu/ipmmu-vmsa:... |
1008 |
return irq; |
fd5140e29 iommu/ipmmu-vmsa:... |
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 |
ret = devm_request_irq(&pdev->dev, irq, ipmmu_irq, 0, dev_name(&pdev->dev), mmu); if (ret < 0) { dev_err(&pdev->dev, "failed to request IRQ %d ", irq); return ret; } ipmmu_device_reset(mmu); |
2ae869557 iommu/ipmmu-vmsa:... |
1019 1020 1021 1022 1023 1024 |
if (mmu->features->reserved_context) { dev_info(&pdev->dev, "IPMMU context 0 is reserved "); set_bit(0, mmu->ctx); } |
fd5140e29 iommu/ipmmu-vmsa:... |
1025 |
} |
d25a2a16f iommu: Add driver... |
1026 |
|
cda52fcd9 iommu/ipmmu-vmsa:... |
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 |
/* * Register the IPMMU to the IOMMU subsystem in the following cases: * - R-Car Gen2 IPMMU (all devices registered) * - R-Car Gen3 IPMMU (leaf devices only - skip root IPMMU-MM device) */ if (!mmu->features->has_cache_leaf_nodes || !ipmmu_is_root(mmu)) { ret = iommu_device_sysfs_add(&mmu->iommu, &pdev->dev, NULL, dev_name(&pdev->dev)); if (ret) return ret; |
7af9a5fdb iommu/ipmmu-vmsa:... |
1037 |
|
cda52fcd9 iommu/ipmmu-vmsa:... |
1038 1039 1040 |
iommu_device_set_ops(&mmu->iommu, &ipmmu_ops); iommu_device_set_fwnode(&mmu->iommu, &pdev->dev.of_node->fwnode); |
01da21e56 iommu/ipmmu-vmsa:... |
1041 |
|
cda52fcd9 iommu/ipmmu-vmsa:... |
1042 1043 1044 1045 1046 1047 1048 1049 1050 |
ret = iommu_device_register(&mmu->iommu); if (ret) return ret; #if defined(CONFIG_IOMMU_DMA) if (!iommu_present(&platform_bus_type)) bus_set_iommu(&platform_bus_type, &ipmmu_ops); #endif } |
01da21e56 iommu/ipmmu-vmsa:... |
1051 |
|
d25a2a16f iommu: Add driver... |
1052 1053 1054 1055 1056 |
/* * We can't create the ARM mapping here as it requires the bus to have * an IOMMU, which only happens when bus_set_iommu() is called in * ipmmu_init() after the probe function returns. */ |
d25a2a16f iommu: Add driver... |
1057 1058 1059 1060 1061 1062 1063 1064 |
platform_set_drvdata(pdev, mmu); return 0; } static int ipmmu_remove(struct platform_device *pdev) { struct ipmmu_vmsa_device *mmu = platform_get_drvdata(pdev); |
7af9a5fdb iommu/ipmmu-vmsa:... |
1065 |
iommu_device_sysfs_remove(&mmu->iommu); |
01da21e56 iommu/ipmmu-vmsa:... |
1066 |
iommu_device_unregister(&mmu->iommu); |
d25a2a16f iommu: Add driver... |
1067 1068 1069 1070 1071 1072 |
arm_iommu_release_mapping(mmu->mapping); ipmmu_device_reset(mmu); return 0; } |
da38e9ec9 iommu/ipmmu-vmsa:... |
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 |
#ifdef CONFIG_PM_SLEEP static int ipmmu_resume_noirq(struct device *dev) { struct ipmmu_vmsa_device *mmu = dev_get_drvdata(dev); unsigned int i; /* Reset root MMU and restore contexts */ if (ipmmu_is_root(mmu)) { ipmmu_device_reset(mmu); for (i = 0; i < mmu->num_ctx; i++) { if (!mmu->domains[i]) continue; ipmmu_domain_setup_context(mmu->domains[i]); } } /* Re-enable active micro-TLBs */ for (i = 0; i < mmu->features->num_utlbs; i++) { if (mmu->utlb_ctx[i] == IPMMU_CTX_INVALID) continue; ipmmu_utlb_enable(mmu->root->domains[mmu->utlb_ctx[i]], i); } return 0; } static const struct dev_pm_ops ipmmu_pm = { SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, ipmmu_resume_noirq) }; #define DEV_PM_OPS &ipmmu_pm #else #define DEV_PM_OPS NULL #endif /* CONFIG_PM_SLEEP */ |
d25a2a16f iommu: Add driver... |
1109 1110 |
static struct platform_driver ipmmu_driver = { .driver = { |
d25a2a16f iommu: Add driver... |
1111 |
.name = "ipmmu-vmsa", |
275f5053c iommu/ipmmu-vmsa:... |
1112 |
.of_match_table = of_match_ptr(ipmmu_of_ids), |
da38e9ec9 iommu/ipmmu-vmsa:... |
1113 |
.pm = DEV_PM_OPS, |
d25a2a16f iommu: Add driver... |
1114 1115 1116 1117 1118 1119 1120 |
}, .probe = ipmmu_probe, .remove = ipmmu_remove, }; static int __init ipmmu_init(void) { |
5c5c87411 iommu/ipmmu-vmsa:... |
1121 |
struct device_node *np; |
cda52fcd9 iommu/ipmmu-vmsa:... |
1122 |
static bool setup_done; |
d25a2a16f iommu: Add driver... |
1123 |
int ret; |
cda52fcd9 iommu/ipmmu-vmsa:... |
1124 1125 |
if (setup_done) return 0; |
5c5c87411 iommu/ipmmu-vmsa:... |
1126 1127 1128 1129 1130 |
np = of_find_matching_node(NULL, ipmmu_of_ids); if (!np) return 0; of_node_put(np); |
d25a2a16f iommu: Add driver... |
1131 1132 1133 |
ret = platform_driver_register(&ipmmu_driver); if (ret < 0) return ret; |
cda52fcd9 iommu/ipmmu-vmsa:... |
1134 |
#if defined(CONFIG_ARM) && !defined(CONFIG_IOMMU_DMA) |
d25a2a16f iommu: Add driver... |
1135 1136 |
if (!iommu_present(&platform_bus_type)) bus_set_iommu(&platform_bus_type, &ipmmu_ops); |
cda52fcd9 iommu/ipmmu-vmsa:... |
1137 |
#endif |
d25a2a16f iommu: Add driver... |
1138 |
|
cda52fcd9 iommu/ipmmu-vmsa:... |
1139 |
setup_done = true; |
d25a2a16f iommu: Add driver... |
1140 1141 |
return 0; } |
d25a2a16f iommu: Add driver... |
1142 |
subsys_initcall(ipmmu_init); |