diff options
-rw-r--r-- | drivers/iommu/tegra-gart.c | 155 |
1 files changed, 40 insertions, 115 deletions
diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index 74c9be13f043..ad348c61d5e7 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c | |||
@@ -23,7 +23,6 @@ | |||
23 | 23 | ||
24 | #include <linux/io.h> | 24 | #include <linux/io.h> |
25 | #include <linux/iommu.h> | 25 | #include <linux/iommu.h> |
26 | #include <linux/list.h> | ||
27 | #include <linux/moduleparam.h> | 26 | #include <linux/moduleparam.h> |
28 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
29 | #include <linux/slab.h> | 28 | #include <linux/slab.h> |
@@ -46,30 +45,20 @@ | |||
46 | #define GART_PAGE_MASK \ | 45 | #define GART_PAGE_MASK \ |
47 | (~(GART_PAGE_SIZE - 1) & ~GART_ENTRY_PHYS_ADDR_VALID) | 46 | (~(GART_PAGE_SIZE - 1) & ~GART_ENTRY_PHYS_ADDR_VALID) |
48 | 47 | ||
49 | struct gart_client { | ||
50 | struct device *dev; | ||
51 | struct list_head list; | ||
52 | }; | ||
53 | |||
54 | struct gart_device { | 48 | struct gart_device { |
55 | void __iomem *regs; | 49 | void __iomem *regs; |
56 | u32 *savedata; | 50 | u32 *savedata; |
57 | u32 page_count; /* total remappable size */ | 51 | u32 page_count; /* total remappable size */ |
58 | dma_addr_t iovmm_base; /* offset to vmm_area */ | 52 | dma_addr_t iovmm_base; /* offset to vmm_area */ |
59 | spinlock_t pte_lock; /* for pagetable */ | 53 | spinlock_t pte_lock; /* for pagetable */ |
60 | struct list_head client; | 54 | spinlock_t dom_lock; /* for active domain */ |
61 | spinlock_t client_lock; /* for client list */ | 55 | unsigned int active_devices; /* number of active devices */ |
62 | struct iommu_domain *active_domain; /* current active domain */ | 56 | struct iommu_domain *active_domain; /* current active domain */ |
63 | struct device *dev; | 57 | struct device *dev; |
64 | 58 | ||
65 | struct iommu_device iommu; /* IOMMU Core handle */ | 59 | struct iommu_device iommu; /* IOMMU Core handle */ |
66 | }; | 60 | }; |
67 | 61 | ||
68 | struct gart_domain { | ||
69 | struct iommu_domain domain; /* generic domain handle */ | ||
70 | struct gart_device *gart; /* link to gart device */ | ||
71 | }; | ||
72 | |||
73 | static struct gart_device *gart_handle; /* unique for a system */ | 62 | static struct gart_device *gart_handle; /* unique for a system */ |
74 | 63 | ||
75 | static bool gart_debug; | 64 | static bool gart_debug; |
@@ -77,11 +66,6 @@ static bool gart_debug; | |||
77 | #define GART_PTE(_pfn) \ | 66 | #define GART_PTE(_pfn) \ |
78 | (GART_ENTRY_PHYS_ADDR_VALID | ((_pfn) << PAGE_SHIFT)) | 67 | (GART_ENTRY_PHYS_ADDR_VALID | ((_pfn) << PAGE_SHIFT)) |
79 | 68 | ||
80 | static struct gart_domain *to_gart_domain(struct iommu_domain *dom) | ||
81 | { | ||
82 | return container_of(dom, struct gart_domain, domain); | ||
83 | } | ||
84 | |||
85 | /* | 69 | /* |
86 | * Any interaction between any block on PPSB and a block on APB or AHB | 70 | * Any interaction between any block on PPSB and a block on APB or AHB |
87 | * must have these read-back to ensure the APB/AHB bus transaction is | 71 | * must have these read-back to ensure the APB/AHB bus transaction is |
@@ -170,125 +154,70 @@ static inline bool gart_iova_range_valid(struct gart_device *gart, | |||
170 | static int gart_iommu_attach_dev(struct iommu_domain *domain, | 154 | static int gart_iommu_attach_dev(struct iommu_domain *domain, |
171 | struct device *dev) | 155 | struct device *dev) |
172 | { | 156 | { |
173 | struct gart_domain *gart_domain = to_gart_domain(domain); | ||
174 | struct gart_device *gart = gart_handle; | 157 | struct gart_device *gart = gart_handle; |
175 | struct gart_client *client, *c; | 158 | int ret = 0; |
176 | int err = 0; | ||
177 | |||
178 | client = kzalloc(sizeof(*c), GFP_KERNEL); | ||
179 | if (!client) | ||
180 | return -ENOMEM; | ||
181 | client->dev = dev; | ||
182 | |||
183 | spin_lock(&gart->client_lock); | ||
184 | list_for_each_entry(c, &gart->client, list) { | ||
185 | if (c->dev == dev) { | ||
186 | dev_err(gart->dev, | ||
187 | "%s is already attached\n", dev_name(dev)); | ||
188 | err = -EINVAL; | ||
189 | goto fail; | ||
190 | } | ||
191 | } | ||
192 | if (gart->active_domain && gart->active_domain != domain) { | ||
193 | dev_err(gart->dev, "Only one domain can be active at a time\n"); | ||
194 | err = -EINVAL; | ||
195 | goto fail; | ||
196 | } | ||
197 | gart->active_domain = domain; | ||
198 | gart_domain->gart = gart; | ||
199 | list_add(&client->list, &gart->client); | ||
200 | spin_unlock(&gart->client_lock); | ||
201 | dev_dbg(gart->dev, "Attached %s\n", dev_name(dev)); | ||
202 | return 0; | ||
203 | 159 | ||
204 | fail: | 160 | spin_lock(&gart->dom_lock); |
205 | kfree(client); | ||
206 | spin_unlock(&gart->client_lock); | ||
207 | return err; | ||
208 | } | ||
209 | 161 | ||
210 | static void __gart_iommu_detach_dev(struct iommu_domain *domain, | 162 | if (gart->active_domain && gart->active_domain != domain) { |
211 | struct device *dev) | 163 | ret = -EBUSY; |
212 | { | 164 | } else if (dev->archdata.iommu != domain) { |
213 | struct gart_domain *gart_domain = to_gart_domain(domain); | 165 | dev->archdata.iommu = domain; |
214 | struct gart_device *gart = gart_domain->gart; | 166 | gart->active_domain = domain; |
215 | struct gart_client *c; | 167 | gart->active_devices++; |
216 | |||
217 | list_for_each_entry(c, &gart->client, list) { | ||
218 | if (c->dev == dev) { | ||
219 | list_del(&c->list); | ||
220 | kfree(c); | ||
221 | if (list_empty(&gart->client)) { | ||
222 | gart->active_domain = NULL; | ||
223 | gart_domain->gart = NULL; | ||
224 | } | ||
225 | dev_dbg(gart->dev, "Detached %s\n", dev_name(dev)); | ||
226 | return; | ||
227 | } | ||
228 | } | 168 | } |
229 | 169 | ||
230 | dev_err(gart->dev, "Couldn't find %s to detach\n", dev_name(dev)); | 170 | spin_unlock(&gart->dom_lock); |
171 | |||
172 | return ret; | ||
231 | } | 173 | } |
232 | 174 | ||
233 | static void gart_iommu_detach_dev(struct iommu_domain *domain, | 175 | static void gart_iommu_detach_dev(struct iommu_domain *domain, |
234 | struct device *dev) | 176 | struct device *dev) |
235 | { | 177 | { |
236 | struct gart_domain *gart_domain = to_gart_domain(domain); | 178 | struct gart_device *gart = gart_handle; |
237 | struct gart_device *gart = gart_domain->gart; | 179 | |
180 | spin_lock(&gart->dom_lock); | ||
238 | 181 | ||
239 | spin_lock(&gart->client_lock); | 182 | if (dev->archdata.iommu == domain) { |
240 | __gart_iommu_detach_dev(domain, dev); | 183 | dev->archdata.iommu = NULL; |
241 | spin_unlock(&gart->client_lock); | 184 | |
185 | if (--gart->active_devices == 0) | ||
186 | gart->active_domain = NULL; | ||
187 | } | ||
188 | |||
189 | spin_unlock(&gart->dom_lock); | ||
242 | } | 190 | } |
243 | 191 | ||
244 | static struct iommu_domain *gart_iommu_domain_alloc(unsigned type) | 192 | static struct iommu_domain *gart_iommu_domain_alloc(unsigned type) |
245 | { | 193 | { |
246 | struct gart_domain *gart_domain; | 194 | struct gart_device *gart = gart_handle; |
247 | struct gart_device *gart; | 195 | struct iommu_domain *domain; |
248 | 196 | ||
249 | if (type != IOMMU_DOMAIN_UNMANAGED) | 197 | if (type != IOMMU_DOMAIN_UNMANAGED) |
250 | return NULL; | 198 | return NULL; |
251 | 199 | ||
252 | gart = gart_handle; | 200 | domain = kzalloc(sizeof(*domain), GFP_KERNEL); |
253 | if (!gart) | 201 | if (domain) { |
254 | return NULL; | 202 | domain->geometry.aperture_start = gart->iovmm_base; |
255 | 203 | domain->geometry.aperture_end = gart->iovmm_base + | |
256 | gart_domain = kzalloc(sizeof(*gart_domain), GFP_KERNEL); | ||
257 | if (!gart_domain) | ||
258 | return NULL; | ||
259 | |||
260 | gart_domain->domain.geometry.aperture_start = gart->iovmm_base; | ||
261 | gart_domain->domain.geometry.aperture_end = gart->iovmm_base + | ||
262 | gart->page_count * GART_PAGE_SIZE - 1; | 204 | gart->page_count * GART_PAGE_SIZE - 1; |
263 | gart_domain->domain.geometry.force_aperture = true; | 205 | domain->geometry.force_aperture = true; |
206 | } | ||
264 | 207 | ||
265 | return &gart_domain->domain; | 208 | return domain; |
266 | } | 209 | } |
267 | 210 | ||
268 | static void gart_iommu_domain_free(struct iommu_domain *domain) | 211 | static void gart_iommu_domain_free(struct iommu_domain *domain) |
269 | { | 212 | { |
270 | struct gart_domain *gart_domain = to_gart_domain(domain); | 213 | WARN_ON(gart_handle->active_domain == domain); |
271 | struct gart_device *gart = gart_domain->gart; | 214 | kfree(domain); |
272 | |||
273 | if (gart) { | ||
274 | spin_lock(&gart->client_lock); | ||
275 | if (!list_empty(&gart->client)) { | ||
276 | struct gart_client *c, *tmp; | ||
277 | |||
278 | list_for_each_entry_safe(c, tmp, &gart->client, list) | ||
279 | __gart_iommu_detach_dev(domain, c->dev); | ||
280 | } | ||
281 | spin_unlock(&gart->client_lock); | ||
282 | } | ||
283 | |||
284 | kfree(gart_domain); | ||
285 | } | 215 | } |
286 | 216 | ||
287 | static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova, | 217 | static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova, |
288 | phys_addr_t pa, size_t bytes, int prot) | 218 | phys_addr_t pa, size_t bytes, int prot) |
289 | { | 219 | { |
290 | struct gart_domain *gart_domain = to_gart_domain(domain); | 220 | struct gart_device *gart = gart_handle; |
291 | struct gart_device *gart = gart_domain->gart; | ||
292 | unsigned long flags; | 221 | unsigned long flags; |
293 | unsigned long pfn; | 222 | unsigned long pfn; |
294 | unsigned long pte; | 223 | unsigned long pte; |
@@ -319,8 +248,7 @@ static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova, | |||
319 | static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova, | 248 | static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova, |
320 | size_t bytes) | 249 | size_t bytes) |
321 | { | 250 | { |
322 | struct gart_domain *gart_domain = to_gart_domain(domain); | 251 | struct gart_device *gart = gart_handle; |
323 | struct gart_device *gart = gart_domain->gart; | ||
324 | unsigned long flags; | 252 | unsigned long flags; |
325 | 253 | ||
326 | if (!gart_iova_range_valid(gart, iova, bytes)) | 254 | if (!gart_iova_range_valid(gart, iova, bytes)) |
@@ -335,8 +263,7 @@ static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova, | |||
335 | static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain, | 263 | static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain, |
336 | dma_addr_t iova) | 264 | dma_addr_t iova) |
337 | { | 265 | { |
338 | struct gart_domain *gart_domain = to_gart_domain(domain); | 266 | struct gart_device *gart = gart_handle; |
339 | struct gart_device *gart = gart_domain->gart; | ||
340 | unsigned long pte; | 267 | unsigned long pte; |
341 | phys_addr_t pa; | 268 | phys_addr_t pa; |
342 | unsigned long flags; | 269 | unsigned long flags; |
@@ -395,8 +322,7 @@ static int gart_iommu_of_xlate(struct device *dev, | |||
395 | 322 | ||
396 | static void gart_iommu_sync(struct iommu_domain *domain) | 323 | static void gart_iommu_sync(struct iommu_domain *domain) |
397 | { | 324 | { |
398 | struct gart_domain *gart_domain = to_gart_domain(domain); | 325 | struct gart_device *gart = gart_handle; |
399 | struct gart_device *gart = gart_domain->gart; | ||
400 | 326 | ||
401 | FLUSH_GART_REGS(gart); | 327 | FLUSH_GART_REGS(gart); |
402 | } | 328 | } |
@@ -483,8 +409,7 @@ struct gart_device *tegra_gart_probe(struct device *dev, struct tegra_mc *mc) | |||
483 | gart->dev = dev; | 409 | gart->dev = dev; |
484 | gart_regs = mc->regs + GART_REG_BASE; | 410 | gart_regs = mc->regs + GART_REG_BASE; |
485 | spin_lock_init(&gart->pte_lock); | 411 | spin_lock_init(&gart->pte_lock); |
486 | spin_lock_init(&gart->client_lock); | 412 | spin_lock_init(&gart->dom_lock); |
487 | INIT_LIST_HEAD(&gart->client); | ||
488 | gart->regs = gart_regs; | 413 | gart->regs = gart_regs; |
489 | gart->iovmm_base = (dma_addr_t)res_remap->start; | 414 | gart->iovmm_base = (dma_addr_t)res_remap->start; |
490 | gart->page_count = (resource_size(res_remap) >> GART_PAGE_SHIFT); | 415 | gart->page_count = (resource_size(res_remap) >> GART_PAGE_SHIFT); |