| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. |
|---|
| 2 | 3 | * |
|---|
| 3 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 4 | | - * it under the terms of the GNU General Public License version 2 and |
|---|
| 5 | | - * only version 2 as published by the Free Software Foundation. |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 8 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 9 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 10 | | - * GNU General Public License for more details. |
|---|
| 11 | | - * |
|---|
| 12 | | - * You should have received a copy of the GNU General Public License |
|---|
| 13 | | - * along with this program; if not, write to the Free Software |
|---|
| 14 | | - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|---|
| 15 | | - * 02110-1301, USA. |
|---|
| 4 | + * Author: Stepan Moskovchenko <stepanm@codeaurora.org> |
|---|
| 16 | 5 | */ |
|---|
| 17 | 6 | |
|---|
| 18 | 7 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
|---|
| 19 | 8 | #include <linux/kernel.h> |
|---|
| 20 | | -#include <linux/module.h> |
|---|
| 9 | +#include <linux/init.h> |
|---|
| 21 | 10 | #include <linux/platform_device.h> |
|---|
| 22 | 11 | #include <linux/errno.h> |
|---|
| 23 | 12 | #include <linux/io.h> |
|---|
| .. | .. |
|---|
| 32 | 21 | #include <linux/of_iommu.h> |
|---|
| 33 | 22 | |
|---|
| 34 | 23 | #include <asm/cacheflush.h> |
|---|
| 35 | | -#include <asm/sizes.h> |
|---|
| 24 | +#include <linux/sizes.h> |
|---|
| 36 | 25 | |
|---|
| 37 | 26 | #include "msm_iommu_hw-8xxx.h" |
|---|
| 38 | 27 | #include "msm_iommu.h" |
|---|
| .. | .. |
|---|
| 45 | 34 | /* bitmap of the page sizes currently supported */ |
|---|
| 46 | 35 | #define MSM_IOMMU_PGSIZES (SZ_4K | SZ_64K | SZ_1M | SZ_16M) |
|---|
| 47 | 36 | |
|---|
| 48 | | -DEFINE_SPINLOCK(msm_iommu_lock); |
|---|
| 37 | +static DEFINE_SPINLOCK(msm_iommu_lock); |
|---|
| 49 | 38 | static LIST_HEAD(qcom_iommu_devices); |
|---|
| 50 | 39 | static struct iommu_ops msm_iommu_ops; |
|---|
| 51 | 40 | |
|---|
| .. | .. |
|---|
| 179 | 168 | return; |
|---|
| 180 | 169 | } |
|---|
| 181 | 170 | |
|---|
| 182 | | -static void __flush_iotlb_sync(void *cookie) |
|---|
| 171 | +static void __flush_iotlb_walk(unsigned long iova, size_t size, |
|---|
| 172 | + size_t granule, void *cookie) |
|---|
| 183 | 173 | { |
|---|
| 184 | | - /* |
|---|
| 185 | | - * Nothing is needed here, the barrier to guarantee |
|---|
| 186 | | - * completion of the tlb sync operation is implicitly |
|---|
| 187 | | - * taken care when the iommu client does a writel before |
|---|
| 188 | | - * kick starting the other master. |
|---|
| 189 | | - */ |
|---|
| 174 | + __flush_iotlb_range(iova, size, granule, false, cookie); |
|---|
| 190 | 175 | } |
|---|
| 191 | 176 | |
|---|
| 192 | | -static const struct iommu_gather_ops msm_iommu_gather_ops = { |
|---|
| 177 | +static void __flush_iotlb_page(struct iommu_iotlb_gather *gather, |
|---|
| 178 | + unsigned long iova, size_t granule, void *cookie) |
|---|
| 179 | +{ |
|---|
| 180 | + __flush_iotlb_range(iova, granule, granule, true, cookie); |
|---|
| 181 | +} |
|---|
| 182 | + |
|---|
| 183 | +static const struct iommu_flush_ops msm_iommu_flush_ops = { |
|---|
| 193 | 184 | .tlb_flush_all = __flush_iotlb, |
|---|
| 194 | | - .tlb_add_flush = __flush_iotlb_range, |
|---|
| 195 | | - .tlb_sync = __flush_iotlb_sync, |
|---|
| 185 | + .tlb_flush_walk = __flush_iotlb_walk, |
|---|
| 186 | + .tlb_add_page = __flush_iotlb_page, |
|---|
| 196 | 187 | }; |
|---|
| 197 | 188 | |
|---|
| 198 | 189 | static int msm_iommu_alloc_ctx(unsigned long *map, int start, int end) |
|---|
| .. | .. |
|---|
| 281 | 272 | SET_V2PCFG(base, ctx, 0x3); |
|---|
| 282 | 273 | |
|---|
| 283 | 274 | SET_TTBCR(base, ctx, priv->cfg.arm_v7s_cfg.tcr); |
|---|
| 284 | | - SET_TTBR0(base, ctx, priv->cfg.arm_v7s_cfg.ttbr[0]); |
|---|
| 285 | | - SET_TTBR1(base, ctx, priv->cfg.arm_v7s_cfg.ttbr[1]); |
|---|
| 275 | + SET_TTBR0(base, ctx, priv->cfg.arm_v7s_cfg.ttbr); |
|---|
| 276 | + SET_TTBR1(base, ctx, 0); |
|---|
| 286 | 277 | |
|---|
| 287 | 278 | /* Set prrr and nmrr */ |
|---|
| 288 | 279 | SET_PRRR(base, ctx, priv->cfg.arm_v7s_cfg.prrr); |
|---|
| .. | .. |
|---|
| 352 | 343 | spin_lock_init(&priv->pgtlock); |
|---|
| 353 | 344 | |
|---|
| 354 | 345 | priv->cfg = (struct io_pgtable_cfg) { |
|---|
| 355 | | - .quirks = IO_PGTABLE_QUIRK_TLBI_ON_MAP, |
|---|
| 356 | 346 | .pgsize_bitmap = msm_iommu_ops.pgsize_bitmap, |
|---|
| 357 | 347 | .ias = 32, |
|---|
| 358 | 348 | .oas = 32, |
|---|
| 359 | | - .tlb = &msm_iommu_gather_ops, |
|---|
| 349 | + .tlb = &msm_iommu_flush_ops, |
|---|
| 360 | 350 | .iommu_dev = priv->dev, |
|---|
| 361 | 351 | }; |
|---|
| 362 | 352 | |
|---|
| .. | .. |
|---|
| 390 | 380 | return ret; |
|---|
| 391 | 381 | } |
|---|
| 392 | 382 | |
|---|
| 393 | | -static int msm_iommu_add_device(struct device *dev) |
|---|
| 383 | +static struct iommu_device *msm_iommu_probe_device(struct device *dev) |
|---|
| 394 | 384 | { |
|---|
| 395 | 385 | struct msm_iommu_dev *iommu; |
|---|
| 396 | | - struct iommu_group *group; |
|---|
| 397 | 386 | unsigned long flags; |
|---|
| 398 | 387 | |
|---|
| 399 | 388 | spin_lock_irqsave(&msm_iommu_lock, flags); |
|---|
| 400 | 389 | iommu = find_iommu_for_dev(dev); |
|---|
| 401 | 390 | spin_unlock_irqrestore(&msm_iommu_lock, flags); |
|---|
| 402 | 391 | |
|---|
| 403 | | - if (iommu) |
|---|
| 404 | | - iommu_device_link(&iommu->iommu, dev); |
|---|
| 405 | | - else |
|---|
| 406 | | - return -ENODEV; |
|---|
| 392 | + if (!iommu) |
|---|
| 393 | + return ERR_PTR(-ENODEV); |
|---|
| 407 | 394 | |
|---|
| 408 | | - group = iommu_group_get_for_dev(dev); |
|---|
| 409 | | - if (IS_ERR(group)) |
|---|
| 410 | | - return PTR_ERR(group); |
|---|
| 411 | | - |
|---|
| 412 | | - iommu_group_put(group); |
|---|
| 413 | | - |
|---|
| 414 | | - return 0; |
|---|
| 395 | + return &iommu->iommu; |
|---|
| 415 | 396 | } |
|---|
| 416 | 397 | |
|---|
| 417 | | -static void msm_iommu_remove_device(struct device *dev) |
|---|
| 398 | +static void msm_iommu_release_device(struct device *dev) |
|---|
| 418 | 399 | { |
|---|
| 419 | | - struct msm_iommu_dev *iommu; |
|---|
| 420 | | - unsigned long flags; |
|---|
| 421 | | - |
|---|
| 422 | | - spin_lock_irqsave(&msm_iommu_lock, flags); |
|---|
| 423 | | - iommu = find_iommu_for_dev(dev); |
|---|
| 424 | | - spin_unlock_irqrestore(&msm_iommu_lock, flags); |
|---|
| 425 | | - |
|---|
| 426 | | - if (iommu) |
|---|
| 427 | | - iommu_device_unlink(&iommu->iommu, dev); |
|---|
| 428 | | - |
|---|
| 429 | | - iommu_group_remove_device(dev); |
|---|
| 430 | 400 | } |
|---|
| 431 | 401 | |
|---|
| 432 | 402 | static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) |
|---|
| .. | .. |
|---|
| 459 | 429 | master->num = |
|---|
| 460 | 430 | msm_iommu_alloc_ctx(iommu->context_map, |
|---|
| 461 | 431 | 0, iommu->ncb); |
|---|
| 462 | | - if (IS_ERR_VALUE(master->num)) { |
|---|
| 463 | | - ret = -ENODEV; |
|---|
| 464 | | - goto fail; |
|---|
| 465 | | - } |
|---|
| 432 | + if (IS_ERR_VALUE(master->num)) { |
|---|
| 433 | + ret = -ENODEV; |
|---|
| 434 | + goto fail; |
|---|
| 435 | + } |
|---|
| 466 | 436 | config_mids(iommu, master); |
|---|
| 467 | 437 | __program_context(iommu->base, master->num, |
|---|
| 468 | 438 | priv); |
|---|
| .. | .. |
|---|
| 506 | 476 | } |
|---|
| 507 | 477 | |
|---|
| 508 | 478 | static int msm_iommu_map(struct iommu_domain *domain, unsigned long iova, |
|---|
| 509 | | - phys_addr_t pa, size_t len, int prot) |
|---|
| 479 | + phys_addr_t pa, size_t len, int prot, gfp_t gfp) |
|---|
| 510 | 480 | { |
|---|
| 511 | 481 | struct msm_priv *priv = to_msm_priv(domain); |
|---|
| 512 | 482 | unsigned long flags; |
|---|
| 513 | 483 | int ret; |
|---|
| 514 | 484 | |
|---|
| 515 | 485 | spin_lock_irqsave(&priv->pgtlock, flags); |
|---|
| 516 | | - ret = priv->iop->map(priv->iop, iova, pa, len, prot); |
|---|
| 486 | + ret = priv->iop->map(priv->iop, iova, pa, len, prot, GFP_ATOMIC); |
|---|
| 517 | 487 | spin_unlock_irqrestore(&priv->pgtlock, flags); |
|---|
| 518 | 488 | |
|---|
| 519 | 489 | return ret; |
|---|
| 520 | 490 | } |
|---|
| 521 | 491 | |
|---|
| 492 | +static void msm_iommu_sync_map(struct iommu_domain *domain, unsigned long iova, |
|---|
| 493 | + size_t size) |
|---|
| 494 | +{ |
|---|
| 495 | + struct msm_priv *priv = to_msm_priv(domain); |
|---|
| 496 | + |
|---|
| 497 | + __flush_iotlb_range(iova, size, SZ_4K, false, priv); |
|---|
| 498 | +} |
|---|
| 499 | + |
|---|
| 522 | 500 | static size_t msm_iommu_unmap(struct iommu_domain *domain, unsigned long iova, |
|---|
| 523 | | - size_t len) |
|---|
| 501 | + size_t len, struct iommu_iotlb_gather *gather) |
|---|
| 524 | 502 | { |
|---|
| 525 | 503 | struct msm_priv *priv = to_msm_priv(domain); |
|---|
| 526 | 504 | unsigned long flags; |
|---|
| 527 | 505 | |
|---|
| 528 | 506 | spin_lock_irqsave(&priv->pgtlock, flags); |
|---|
| 529 | | - len = priv->iop->unmap(priv->iop, iova, len); |
|---|
| 507 | + len = priv->iop->unmap(priv->iop, iova, len, gather); |
|---|
| 530 | 508 | spin_unlock_irqrestore(&priv->pgtlock, flags); |
|---|
| 531 | 509 | |
|---|
| 532 | 510 | return len; |
|---|
| .. | .. |
|---|
| 615 | 593 | struct msm_iommu_dev **iommu, |
|---|
| 616 | 594 | struct of_phandle_args *spec) |
|---|
| 617 | 595 | { |
|---|
| 618 | | - struct msm_iommu_ctx_dev *master = dev->archdata.iommu; |
|---|
| 596 | + struct msm_iommu_ctx_dev *master = dev_iommu_priv_get(dev); |
|---|
| 619 | 597 | int sid; |
|---|
| 620 | 598 | |
|---|
| 621 | 599 | if (list_empty(&(*iommu)->ctx_list)) { |
|---|
| 622 | 600 | master = kzalloc(sizeof(*master), GFP_ATOMIC); |
|---|
| 623 | 601 | master->of_node = dev->of_node; |
|---|
| 624 | 602 | list_add(&master->list, &(*iommu)->ctx_list); |
|---|
| 625 | | - dev->archdata.iommu = master; |
|---|
| 603 | + dev_iommu_priv_set(dev, master); |
|---|
| 626 | 604 | } |
|---|
| 627 | 605 | |
|---|
| 628 | 606 | for (sid = 0; sid < master->num_mids; sid++) |
|---|
| .. | .. |
|---|
| 638 | 616 | static int qcom_iommu_of_xlate(struct device *dev, |
|---|
| 639 | 617 | struct of_phandle_args *spec) |
|---|
| 640 | 618 | { |
|---|
| 641 | | - struct msm_iommu_dev *iommu; |
|---|
| 619 | + struct msm_iommu_dev *iommu = NULL, *iter; |
|---|
| 642 | 620 | unsigned long flags; |
|---|
| 643 | 621 | int ret = 0; |
|---|
| 644 | 622 | |
|---|
| 645 | 623 | spin_lock_irqsave(&msm_iommu_lock, flags); |
|---|
| 646 | | - list_for_each_entry(iommu, &qcom_iommu_devices, dev_node) |
|---|
| 647 | | - if (iommu->dev->of_node == spec->np) |
|---|
| 624 | + list_for_each_entry(iter, &qcom_iommu_devices, dev_node) { |
|---|
| 625 | + if (iter->dev->of_node == spec->np) { |
|---|
| 626 | + iommu = iter; |
|---|
| 648 | 627 | break; |
|---|
| 628 | + } |
|---|
| 629 | + } |
|---|
| 649 | 630 | |
|---|
| 650 | | - if (!iommu || iommu->dev->of_node != spec->np) { |
|---|
| 631 | + if (!iommu) { |
|---|
| 651 | 632 | ret = -ENODEV; |
|---|
| 652 | 633 | goto fail; |
|---|
| 653 | 634 | } |
|---|
| .. | .. |
|---|
| 702 | 683 | .detach_dev = msm_iommu_detach_dev, |
|---|
| 703 | 684 | .map = msm_iommu_map, |
|---|
| 704 | 685 | .unmap = msm_iommu_unmap, |
|---|
| 686 | + /* |
|---|
| 687 | + * Nothing is needed here, the barrier to guarantee |
|---|
| 688 | + * completion of the tlb sync operation is implicitly |
|---|
| 689 | + * taken care when the iommu client does a writel before |
|---|
| 690 | + * kick starting the other master. |
|---|
| 691 | + */ |
|---|
| 692 | + .iotlb_sync = NULL, |
|---|
| 693 | + .iotlb_sync_map = msm_iommu_sync_map, |
|---|
| 705 | 694 | .iova_to_phys = msm_iommu_iova_to_phys, |
|---|
| 706 | | - .add_device = msm_iommu_add_device, |
|---|
| 707 | | - .remove_device = msm_iommu_remove_device, |
|---|
| 695 | + .probe_device = msm_iommu_probe_device, |
|---|
| 696 | + .release_device = msm_iommu_release_device, |
|---|
| 708 | 697 | .device_group = generic_device_group, |
|---|
| 709 | 698 | .pgsize_bitmap = MSM_IOMMU_PGSIZES, |
|---|
| 710 | 699 | .of_xlate = qcom_iommu_of_xlate, |
|---|
| .. | .. |
|---|
| 761 | 750 | |
|---|
| 762 | 751 | iommu->irq = platform_get_irq(pdev, 0); |
|---|
| 763 | 752 | if (iommu->irq < 0) { |
|---|
| 764 | | - dev_err(iommu->dev, "could not get iommu irq\n"); |
|---|
| 765 | 753 | ret = -ENODEV; |
|---|
| 766 | 754 | goto fail; |
|---|
| 767 | 755 | } |
|---|
| .. | .. |
|---|
| 861 | 849 | |
|---|
| 862 | 850 | return ret; |
|---|
| 863 | 851 | } |
|---|
| 864 | | - |
|---|
| 865 | | -static void __exit msm_iommu_driver_exit(void) |
|---|
| 866 | | -{ |
|---|
| 867 | | - platform_driver_unregister(&msm_iommu_driver); |
|---|
| 868 | | -} |
|---|
| 869 | | - |
|---|
| 870 | 852 | subsys_initcall(msm_iommu_driver_init); |
|---|
| 871 | | -module_exit(msm_iommu_driver_exit); |
|---|
| 872 | 853 | |
|---|
| 873 | | -MODULE_LICENSE("GPL v2"); |
|---|
| 874 | | -MODULE_AUTHOR("Stepan Moskovchenko <stepanm@codeaurora.org>"); |
|---|