.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
| 3 | + * IOMMU API for MTK architected m4u v1 implementations |
---|
| 4 | + * |
---|
2 | 5 | * Copyright (c) 2015-2016 MediaTek Inc. |
---|
3 | 6 | * Author: Honghui Zhang <honghui.zhang@mediatek.com> |
---|
4 | 7 | * |
---|
5 | 8 | * Based on driver/iommu/mtk_iommu.c |
---|
6 | | - * |
---|
7 | | - * This program is free software; you can redistribute it and/or modify |
---|
8 | | - * it under the terms of the GNU General Public License version 2 as |
---|
9 | | - * published by the Free Software Foundation. |
---|
10 | | - * |
---|
11 | | - * This program is distributed in the hope that it will be useful, |
---|
12 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
13 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
14 | | - * GNU General Public License for more details. |
---|
15 | 9 | */ |
---|
16 | | -#include <linux/bootmem.h> |
---|
| 10 | +#include <linux/memblock.h> |
---|
17 | 11 | #include <linux/bug.h> |
---|
18 | 12 | #include <linux/clk.h> |
---|
19 | 13 | #include <linux/component.h> |
---|
.. | .. |
---|
35 | 29 | #include <linux/spinlock.h> |
---|
36 | 30 | #include <asm/barrier.h> |
---|
37 | 31 | #include <asm/dma-iommu.h> |
---|
38 | | -#include <linux/module.h> |
---|
| 32 | +#include <linux/init.h> |
---|
39 | 33 | #include <dt-bindings/memory/mt2701-larb-port.h> |
---|
40 | 34 | #include <soc/mediatek/smi.h> |
---|
41 | 35 | #include "mtk_iommu.h" |
---|
.. | .. |
---|
206 | 200 | { |
---|
207 | 201 | struct mtk_smi_larb_iommu *larb_mmu; |
---|
208 | 202 | unsigned int larbid, portid; |
---|
209 | | - struct iommu_fwspec *fwspec = dev->iommu_fwspec; |
---|
| 203 | + struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); |
---|
210 | 204 | int i; |
---|
211 | 205 | |
---|
212 | 206 | for (i = 0; i < fwspec->num_ids; ++i) { |
---|
213 | 207 | larbid = mt2701_m4u_to_larb(fwspec->ids[i]); |
---|
214 | 208 | portid = mt2701_m4u_to_port(fwspec->ids[i]); |
---|
215 | | - larb_mmu = &data->smi_imu.larb_imu[larbid]; |
---|
| 209 | + larb_mmu = &data->larb_imu[larbid]; |
---|
216 | 210 | |
---|
217 | 211 | dev_dbg(dev, "%s iommu port: %d\n", |
---|
218 | 212 | enable ? "enable" : "disable", portid); |
---|
.. | .. |
---|
230 | 224 | |
---|
231 | 225 | spin_lock_init(&dom->pgtlock); |
---|
232 | 226 | |
---|
233 | | - dom->pgt_va = dma_zalloc_coherent(data->dev, |
---|
234 | | - M2701_IOMMU_PGT_SIZE, |
---|
235 | | - &dom->pgt_pa, GFP_KERNEL); |
---|
| 227 | + dom->pgt_va = dma_alloc_coherent(data->dev, M2701_IOMMU_PGT_SIZE, |
---|
| 228 | + &dom->pgt_pa, GFP_KERNEL); |
---|
236 | 229 | if (!dom->pgt_va) |
---|
237 | 230 | return -ENOMEM; |
---|
238 | 231 | |
---|
.. | .. |
---|
270 | 263 | static int mtk_iommu_attach_device(struct iommu_domain *domain, |
---|
271 | 264 | struct device *dev) |
---|
272 | 265 | { |
---|
| 266 | + struct mtk_iommu_data *data = dev_iommu_priv_get(dev); |
---|
273 | 267 | struct mtk_iommu_domain *dom = to_mtk_domain(domain); |
---|
274 | | - struct mtk_iommu_data *data = dev->iommu_fwspec->iommu_priv; |
---|
| 268 | + struct dma_iommu_mapping *mtk_mapping; |
---|
275 | 269 | int ret; |
---|
276 | 270 | |
---|
277 | | - if (!data) |
---|
278 | | - return -ENODEV; |
---|
| 271 | + /* Only allow the domain created internally. */ |
---|
| 272 | + mtk_mapping = data->mapping; |
---|
| 273 | + if (mtk_mapping->domain != domain) |
---|
| 274 | + return 0; |
---|
279 | 275 | |
---|
280 | 276 | if (!data->m4u_dom) { |
---|
281 | 277 | data->m4u_dom = dom; |
---|
.. | .. |
---|
293 | 289 | static void mtk_iommu_detach_device(struct iommu_domain *domain, |
---|
294 | 290 | struct device *dev) |
---|
295 | 291 | { |
---|
296 | | - struct mtk_iommu_data *data = dev->iommu_fwspec->iommu_priv; |
---|
297 | | - |
---|
298 | | - if (!data) |
---|
299 | | - return; |
---|
| 292 | + struct mtk_iommu_data *data = dev_iommu_priv_get(dev); |
---|
300 | 293 | |
---|
301 | 294 | mtk_iommu_config(data, dev, false); |
---|
302 | 295 | } |
---|
303 | 296 | |
---|
304 | 297 | static int mtk_iommu_map(struct iommu_domain *domain, unsigned long iova, |
---|
305 | | - phys_addr_t paddr, size_t size, int prot) |
---|
| 298 | + phys_addr_t paddr, size_t size, int prot, gfp_t gfp) |
---|
306 | 299 | { |
---|
307 | 300 | struct mtk_iommu_domain *dom = to_mtk_domain(domain); |
---|
308 | 301 | unsigned int page_num = size >> MT2701_IOMMU_PAGE_SHIFT; |
---|
.. | .. |
---|
331 | 324 | } |
---|
332 | 325 | |
---|
333 | 326 | static size_t mtk_iommu_unmap(struct iommu_domain *domain, |
---|
334 | | - unsigned long iova, size_t size) |
---|
| 327 | + unsigned long iova, size_t size, |
---|
| 328 | + struct iommu_iotlb_gather *gather) |
---|
335 | 329 | { |
---|
336 | 330 | struct mtk_iommu_domain *dom = to_mtk_domain(domain); |
---|
337 | 331 | unsigned long flags; |
---|
.. | .. |
---|
362 | 356 | return pa; |
---|
363 | 357 | } |
---|
364 | 358 | |
---|
365 | | -static struct iommu_ops mtk_iommu_ops; |
---|
| 359 | +static const struct iommu_ops mtk_iommu_ops; |
---|
366 | 360 | |
---|
367 | 361 | /* |
---|
368 | 362 | * MTK generation one iommu HW only support one iommu domain, and all the client |
---|
.. | .. |
---|
371 | 365 | static int mtk_iommu_create_mapping(struct device *dev, |
---|
372 | 366 | struct of_phandle_args *args) |
---|
373 | 367 | { |
---|
| 368 | + struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); |
---|
374 | 369 | struct mtk_iommu_data *data; |
---|
375 | 370 | struct platform_device *m4updev; |
---|
376 | 371 | struct dma_iommu_mapping *mtk_mapping; |
---|
377 | | - struct device *m4udev; |
---|
378 | 372 | int ret; |
---|
379 | 373 | |
---|
380 | 374 | if (args->args_count != 1) { |
---|
.. | .. |
---|
383 | 377 | return -EINVAL; |
---|
384 | 378 | } |
---|
385 | 379 | |
---|
386 | | - if (!dev->iommu_fwspec) { |
---|
| 380 | + if (!fwspec) { |
---|
387 | 381 | ret = iommu_fwspec_init(dev, &args->np->fwnode, &mtk_iommu_ops); |
---|
388 | 382 | if (ret) |
---|
389 | 383 | return ret; |
---|
390 | | - } else if (dev->iommu_fwspec->ops != &mtk_iommu_ops) { |
---|
| 384 | + fwspec = dev_iommu_fwspec_get(dev); |
---|
| 385 | + } else if (dev_iommu_fwspec_get(dev)->ops != &mtk_iommu_ops) { |
---|
391 | 386 | return -EINVAL; |
---|
392 | 387 | } |
---|
393 | 388 | |
---|
394 | | - if (!dev->iommu_fwspec->iommu_priv) { |
---|
| 389 | + if (!dev_iommu_priv_get(dev)) { |
---|
395 | 390 | /* Get the m4u device */ |
---|
396 | 391 | m4updev = of_find_device_by_node(args->np); |
---|
397 | 392 | if (WARN_ON(!m4updev)) |
---|
398 | 393 | return -EINVAL; |
---|
399 | 394 | |
---|
400 | | - dev->iommu_fwspec->iommu_priv = platform_get_drvdata(m4updev); |
---|
| 395 | + dev_iommu_priv_set(dev, platform_get_drvdata(m4updev)); |
---|
401 | 396 | } |
---|
402 | 397 | |
---|
403 | 398 | ret = iommu_fwspec_add_ids(dev, args->args, 1); |
---|
404 | 399 | if (ret) |
---|
405 | 400 | return ret; |
---|
406 | 401 | |
---|
407 | | - data = dev->iommu_fwspec->iommu_priv; |
---|
408 | | - m4udev = data->dev; |
---|
409 | | - mtk_mapping = m4udev->archdata.iommu; |
---|
| 402 | + data = dev_iommu_priv_get(dev); |
---|
| 403 | + mtk_mapping = data->mapping; |
---|
410 | 404 | if (!mtk_mapping) { |
---|
411 | 405 | /* MTK iommu support 4GB iova address space. */ |
---|
412 | 406 | mtk_mapping = arm_iommu_create_mapping(&platform_bus_type, |
---|
.. | .. |
---|
414 | 408 | if (IS_ERR(mtk_mapping)) |
---|
415 | 409 | return PTR_ERR(mtk_mapping); |
---|
416 | 410 | |
---|
417 | | - m4udev->archdata.iommu = mtk_mapping; |
---|
| 411 | + data->mapping = mtk_mapping; |
---|
418 | 412 | } |
---|
419 | 413 | |
---|
420 | 414 | return 0; |
---|
421 | 415 | } |
---|
422 | 416 | |
---|
423 | | -static int mtk_iommu_add_device(struct device *dev) |
---|
| 417 | +static int mtk_iommu_def_domain_type(struct device *dev) |
---|
424 | 418 | { |
---|
425 | | - struct dma_iommu_mapping *mtk_mapping; |
---|
| 419 | + return IOMMU_DOMAIN_UNMANAGED; |
---|
| 420 | +} |
---|
| 421 | + |
---|
| 422 | +static struct iommu_device *mtk_iommu_probe_device(struct device *dev) |
---|
| 423 | +{ |
---|
| 424 | + struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); |
---|
426 | 425 | struct of_phandle_args iommu_spec; |
---|
427 | 426 | struct of_phandle_iterator it; |
---|
428 | 427 | struct mtk_iommu_data *data; |
---|
429 | | - struct iommu_group *group; |
---|
430 | 428 | int err; |
---|
431 | 429 | |
---|
432 | 430 | of_for_each_phandle(&it, err, dev->of_node, "iommus", |
---|
433 | | - "#iommu-cells", 0) { |
---|
| 431 | + "#iommu-cells", -1) { |
---|
434 | 432 | int count = of_phandle_iterator_args(&it, iommu_spec.args, |
---|
435 | 433 | MAX_PHANDLE_ARGS); |
---|
436 | 434 | iommu_spec.np = of_node_get(it.node); |
---|
437 | 435 | iommu_spec.args_count = count; |
---|
438 | 436 | |
---|
439 | 437 | mtk_iommu_create_mapping(dev, &iommu_spec); |
---|
| 438 | + |
---|
| 439 | + /* dev->iommu_fwspec might have changed */ |
---|
| 440 | + fwspec = dev_iommu_fwspec_get(dev); |
---|
| 441 | + |
---|
440 | 442 | of_node_put(iommu_spec.np); |
---|
441 | 443 | } |
---|
442 | 444 | |
---|
443 | | - if (!dev->iommu_fwspec || dev->iommu_fwspec->ops != &mtk_iommu_ops) |
---|
444 | | - return -ENODEV; /* Not a iommu client device */ |
---|
| 445 | + if (!fwspec || fwspec->ops != &mtk_iommu_ops) |
---|
| 446 | + return ERR_PTR(-ENODEV); /* Not a iommu client device */ |
---|
445 | 447 | |
---|
446 | | - /* |
---|
447 | | - * This is a short-term bodge because the ARM DMA code doesn't |
---|
448 | | - * understand multi-device groups, but we have to call into it |
---|
449 | | - * successfully (and not just rely on a normal IOMMU API attach |
---|
450 | | - * here) in order to set the correct DMA API ops on @dev. |
---|
451 | | - */ |
---|
452 | | - group = iommu_group_alloc(); |
---|
453 | | - if (IS_ERR(group)) |
---|
454 | | - return PTR_ERR(group); |
---|
| 448 | + data = dev_iommu_priv_get(dev); |
---|
455 | 449 | |
---|
456 | | - err = iommu_group_add_device(group, dev); |
---|
457 | | - iommu_group_put(group); |
---|
458 | | - if (err) |
---|
459 | | - return err; |
---|
460 | | - |
---|
461 | | - data = dev->iommu_fwspec->iommu_priv; |
---|
462 | | - mtk_mapping = data->dev->archdata.iommu; |
---|
463 | | - err = arm_iommu_attach_device(dev, mtk_mapping); |
---|
464 | | - if (err) { |
---|
465 | | - iommu_group_remove_device(dev); |
---|
466 | | - return err; |
---|
467 | | - } |
---|
468 | | - |
---|
469 | | - return iommu_device_link(&data->iommu, dev);; |
---|
| 450 | + return &data->iommu; |
---|
470 | 451 | } |
---|
471 | 452 | |
---|
472 | | -static void mtk_iommu_remove_device(struct device *dev) |
---|
| 453 | +static void mtk_iommu_probe_finalize(struct device *dev) |
---|
473 | 454 | { |
---|
| 455 | + struct dma_iommu_mapping *mtk_mapping; |
---|
474 | 456 | struct mtk_iommu_data *data; |
---|
| 457 | + int err; |
---|
475 | 458 | |
---|
476 | | - if (!dev->iommu_fwspec || dev->iommu_fwspec->ops != &mtk_iommu_ops) |
---|
| 459 | + data = dev_iommu_priv_get(dev); |
---|
| 460 | + mtk_mapping = data->mapping; |
---|
| 461 | + |
---|
| 462 | + err = arm_iommu_attach_device(dev, mtk_mapping); |
---|
| 463 | + if (err) |
---|
| 464 | + dev_err(dev, "Can't create IOMMU mapping - DMA-OPS will not work\n"); |
---|
| 465 | +} |
---|
| 466 | + |
---|
| 467 | +static void mtk_iommu_release_device(struct device *dev) |
---|
| 468 | +{ |
---|
| 469 | + struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); |
---|
| 470 | + |
---|
| 471 | + if (!fwspec || fwspec->ops != &mtk_iommu_ops) |
---|
477 | 472 | return; |
---|
478 | 473 | |
---|
479 | | - data = dev->iommu_fwspec->iommu_priv; |
---|
480 | | - iommu_device_unlink(&data->iommu, dev); |
---|
481 | | - |
---|
482 | | - iommu_group_remove_device(dev); |
---|
483 | 474 | iommu_fwspec_free(dev); |
---|
484 | 475 | } |
---|
485 | 476 | |
---|
.. | .. |
---|
524 | 515 | return 0; |
---|
525 | 516 | } |
---|
526 | 517 | |
---|
527 | | -static struct iommu_ops mtk_iommu_ops = { |
---|
| 518 | +static const struct iommu_ops mtk_iommu_ops = { |
---|
528 | 519 | .domain_alloc = mtk_iommu_domain_alloc, |
---|
529 | 520 | .domain_free = mtk_iommu_domain_free, |
---|
530 | 521 | .attach_dev = mtk_iommu_attach_device, |
---|
.. | .. |
---|
532 | 523 | .map = mtk_iommu_map, |
---|
533 | 524 | .unmap = mtk_iommu_unmap, |
---|
534 | 525 | .iova_to_phys = mtk_iommu_iova_to_phys, |
---|
535 | | - .add_device = mtk_iommu_add_device, |
---|
536 | | - .remove_device = mtk_iommu_remove_device, |
---|
| 526 | + .probe_device = mtk_iommu_probe_device, |
---|
| 527 | + .probe_finalize = mtk_iommu_probe_finalize, |
---|
| 528 | + .release_device = mtk_iommu_release_device, |
---|
| 529 | + .def_domain_type = mtk_iommu_def_domain_type, |
---|
| 530 | + .device_group = generic_device_group, |
---|
537 | 531 | .pgsize_bitmap = ~0UL << MT2701_IOMMU_PAGE_SHIFT, |
---|
538 | 532 | }; |
---|
539 | 533 | |
---|
.. | .. |
---|
609 | 603 | } |
---|
610 | 604 | } |
---|
611 | 605 | |
---|
612 | | - data->smi_imu.larb_imu[larb_nr].dev = &plarbdev->dev; |
---|
| 606 | + data->larb_imu[larb_nr].dev = &plarbdev->dev; |
---|
613 | 607 | component_match_add_release(dev, &match, release_of, |
---|
614 | 608 | compare_of, larb_spec.np); |
---|
615 | 609 | larb_nr++; |
---|
616 | 610 | } |
---|
617 | | - |
---|
618 | | - data->smi_imu.larb_nr = larb_nr; |
---|
619 | 611 | |
---|
620 | 612 | platform_set_drvdata(pdev, data); |
---|
621 | 613 | |
---|
.. | .. |
---|
626 | 618 | ret = iommu_device_sysfs_add(&data->iommu, &pdev->dev, NULL, |
---|
627 | 619 | dev_name(&pdev->dev)); |
---|
628 | 620 | if (ret) |
---|
629 | | - return ret; |
---|
| 621 | + goto out_clk_unprepare; |
---|
630 | 622 | |
---|
631 | 623 | iommu_device_set_ops(&data->iommu, &mtk_iommu_ops); |
---|
632 | 624 | |
---|
633 | 625 | ret = iommu_device_register(&data->iommu); |
---|
634 | 626 | if (ret) |
---|
635 | | - return ret; |
---|
| 627 | + goto out_sysfs_remove; |
---|
636 | 628 | |
---|
637 | | - if (!iommu_present(&platform_bus_type)) |
---|
638 | | - bus_set_iommu(&platform_bus_type, &mtk_iommu_ops); |
---|
| 629 | + if (!iommu_present(&platform_bus_type)) { |
---|
| 630 | + ret = bus_set_iommu(&platform_bus_type, &mtk_iommu_ops); |
---|
| 631 | + if (ret) |
---|
| 632 | + goto out_dev_unreg; |
---|
| 633 | + } |
---|
639 | 634 | |
---|
640 | | - return component_master_add_with_match(dev, &mtk_iommu_com_ops, match); |
---|
| 635 | + ret = component_master_add_with_match(dev, &mtk_iommu_com_ops, match); |
---|
| 636 | + if (ret) |
---|
| 637 | + goto out_bus_set_null; |
---|
| 638 | + return ret; |
---|
| 639 | + |
---|
| 640 | +out_bus_set_null: |
---|
| 641 | + bus_set_iommu(&platform_bus_type, NULL); |
---|
| 642 | +out_dev_unreg: |
---|
| 643 | + iommu_device_unregister(&data->iommu); |
---|
| 644 | +out_sysfs_remove: |
---|
| 645 | + iommu_device_sysfs_remove(&data->iommu); |
---|
| 646 | +out_clk_unprepare: |
---|
| 647 | + clk_disable_unprepare(data->bclk); |
---|
| 648 | + return ret; |
---|
641 | 649 | } |
---|
642 | 650 | |
---|
643 | 651 | static int mtk_iommu_remove(struct platform_device *pdev) |
---|
.. | .. |
---|
704 | 712 | { |
---|
705 | 713 | return platform_driver_register(&mtk_iommu_driver); |
---|
706 | 714 | } |
---|
707 | | - |
---|
708 | | -static void __exit m4u_exit(void) |
---|
709 | | -{ |
---|
710 | | - return platform_driver_unregister(&mtk_iommu_driver); |
---|
711 | | -} |
---|
712 | | - |
---|
713 | 715 | subsys_initcall(m4u_init); |
---|
714 | | -module_exit(m4u_exit); |
---|
715 | | - |
---|
716 | | -MODULE_DESCRIPTION("IOMMU API for MTK architected m4u v1 implementations"); |
---|
717 | | -MODULE_AUTHOR("Honghui Zhang <honghui.zhang@mediatek.com>"); |
---|
718 | | -MODULE_LICENSE("GPL v2"); |
---|