| .. | .. |
|---|
| 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"); |
|---|