diff options
Diffstat (limited to 'drivers/iommu/ipmmu-vmsa.c')
| -rw-r--r-- | drivers/iommu/ipmmu-vmsa.c | 91 |
1 files changed, 64 insertions, 27 deletions
diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index b98a03189580..7a4529c61c19 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* | 2 | /* |
| 3 | * IPMMU VMSA | 3 | * IOMMU API for Renesas VMSA-compatible IPMMU |
| 4 | * Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
| 4 | * | 5 | * |
| 5 | * Copyright (C) 2014 Renesas Electronics Corporation | 6 | * Copyright (C) 2014 Renesas Electronics Corporation |
| 6 | */ | 7 | */ |
| @@ -11,10 +12,10 @@ | |||
| 11 | #include <linux/dma-mapping.h> | 12 | #include <linux/dma-mapping.h> |
| 12 | #include <linux/err.h> | 13 | #include <linux/err.h> |
| 13 | #include <linux/export.h> | 14 | #include <linux/export.h> |
| 15 | #include <linux/init.h> | ||
| 14 | #include <linux/interrupt.h> | 16 | #include <linux/interrupt.h> |
| 15 | #include <linux/io.h> | 17 | #include <linux/io.h> |
| 16 | #include <linux/iommu.h> | 18 | #include <linux/iommu.h> |
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/of.h> | 19 | #include <linux/of.h> |
| 19 | #include <linux/of_device.h> | 20 | #include <linux/of_device.h> |
| 20 | #include <linux/of_iommu.h> | 21 | #include <linux/of_iommu.h> |
| @@ -81,7 +82,9 @@ static struct ipmmu_vmsa_domain *to_vmsa_domain(struct iommu_domain *dom) | |||
| 81 | 82 | ||
| 82 | static struct ipmmu_vmsa_device *to_ipmmu(struct device *dev) | 83 | static struct ipmmu_vmsa_device *to_ipmmu(struct device *dev) |
| 83 | { | 84 | { |
| 84 | return dev->iommu_fwspec ? dev->iommu_fwspec->iommu_priv : NULL; | 85 | struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); |
| 86 | |||
| 87 | return fwspec ? fwspec->iommu_priv : NULL; | ||
| 85 | } | 88 | } |
| 86 | 89 | ||
| 87 | #define TLB_LOOP_TIMEOUT 100 /* 100us */ | 90 | #define TLB_LOOP_TIMEOUT 100 /* 100us */ |
| @@ -498,6 +501,9 @@ static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain) | |||
| 498 | 501 | ||
| 499 | static void ipmmu_domain_destroy_context(struct ipmmu_vmsa_domain *domain) | 502 | static void ipmmu_domain_destroy_context(struct ipmmu_vmsa_domain *domain) |
| 500 | { | 503 | { |
| 504 | if (!domain->mmu) | ||
| 505 | return; | ||
| 506 | |||
| 501 | /* | 507 | /* |
| 502 | * Disable the context. Flush the TLB as required when modifying the | 508 | * Disable the context. Flush the TLB as required when modifying the |
| 503 | * context registers. | 509 | * context registers. |
| @@ -640,7 +646,7 @@ static void ipmmu_domain_free(struct iommu_domain *io_domain) | |||
| 640 | static int ipmmu_attach_device(struct iommu_domain *io_domain, | 646 | static int ipmmu_attach_device(struct iommu_domain *io_domain, |
| 641 | struct device *dev) | 647 | struct device *dev) |
| 642 | { | 648 | { |
| 643 | struct iommu_fwspec *fwspec = dev->iommu_fwspec; | 649 | struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); |
| 644 | struct ipmmu_vmsa_device *mmu = to_ipmmu(dev); | 650 | struct ipmmu_vmsa_device *mmu = to_ipmmu(dev); |
| 645 | struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); | 651 | struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); |
| 646 | unsigned int i; | 652 | unsigned int i; |
| @@ -689,7 +695,7 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain, | |||
| 689 | static void ipmmu_detach_device(struct iommu_domain *io_domain, | 695 | static void ipmmu_detach_device(struct iommu_domain *io_domain, |
| 690 | struct device *dev) | 696 | struct device *dev) |
| 691 | { | 697 | { |
| 692 | struct iommu_fwspec *fwspec = dev->iommu_fwspec; | 698 | struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); |
| 693 | struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); | 699 | struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain); |
| 694 | unsigned int i; | 700 | unsigned int i; |
| 695 | 701 | ||
| @@ -741,36 +747,71 @@ static phys_addr_t ipmmu_iova_to_phys(struct iommu_domain *io_domain, | |||
| 741 | static int ipmmu_init_platform_device(struct device *dev, | 747 | static int ipmmu_init_platform_device(struct device *dev, |
| 742 | struct of_phandle_args *args) | 748 | struct of_phandle_args *args) |
| 743 | { | 749 | { |
| 750 | struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); | ||
| 744 | struct platform_device *ipmmu_pdev; | 751 | struct platform_device *ipmmu_pdev; |
| 745 | 752 | ||
| 746 | ipmmu_pdev = of_find_device_by_node(args->np); | 753 | ipmmu_pdev = of_find_device_by_node(args->np); |
| 747 | if (!ipmmu_pdev) | 754 | if (!ipmmu_pdev) |
| 748 | return -ENODEV; | 755 | return -ENODEV; |
| 749 | 756 | ||
| 750 | dev->iommu_fwspec->iommu_priv = platform_get_drvdata(ipmmu_pdev); | 757 | fwspec->iommu_priv = platform_get_drvdata(ipmmu_pdev); |
| 751 | return 0; | ||
| 752 | } | ||
| 753 | 758 | ||
| 754 | static bool ipmmu_slave_whitelist(struct device *dev) | 759 | return 0; |
| 755 | { | ||
| 756 | /* By default, do not allow use of IPMMU */ | ||
| 757 | return false; | ||
| 758 | } | 760 | } |
| 759 | 761 | ||
| 760 | static const struct soc_device_attribute soc_rcar_gen3[] = { | 762 | static const struct soc_device_attribute soc_rcar_gen3[] = { |
| 763 | { .soc_id = "r8a774a1", }, | ||
| 764 | { .soc_id = "r8a774c0", }, | ||
| 761 | { .soc_id = "r8a7795", }, | 765 | { .soc_id = "r8a7795", }, |
| 762 | { .soc_id = "r8a7796", }, | 766 | { .soc_id = "r8a7796", }, |
| 763 | { .soc_id = "r8a77965", }, | 767 | { .soc_id = "r8a77965", }, |
| 764 | { .soc_id = "r8a77970", }, | 768 | { .soc_id = "r8a77970", }, |
| 769 | { .soc_id = "r8a77990", }, | ||
| 770 | { .soc_id = "r8a77995", }, | ||
| 771 | { /* sentinel */ } | ||
| 772 | }; | ||
| 773 | |||
| 774 | static const struct soc_device_attribute soc_rcar_gen3_whitelist[] = { | ||
| 775 | { .soc_id = "r8a774c0", }, | ||
| 776 | { .soc_id = "r8a7795", .revision = "ES3.*" }, | ||
| 777 | { .soc_id = "r8a77965", }, | ||
| 778 | { .soc_id = "r8a77990", }, | ||
| 765 | { .soc_id = "r8a77995", }, | 779 | { .soc_id = "r8a77995", }, |
| 766 | { /* sentinel */ } | 780 | { /* sentinel */ } |
| 767 | }; | 781 | }; |
| 768 | 782 | ||
| 783 | static const char * const rcar_gen3_slave_whitelist[] = { | ||
| 784 | }; | ||
| 785 | |||
| 786 | static bool ipmmu_slave_whitelist(struct device *dev) | ||
| 787 | { | ||
| 788 | unsigned int i; | ||
| 789 | |||
| 790 | /* | ||
| 791 | * For R-Car Gen3 use a white list to opt-in slave devices. | ||
| 792 | * For Other SoCs, this returns true anyway. | ||
| 793 | */ | ||
| 794 | if (!soc_device_match(soc_rcar_gen3)) | ||
| 795 | return true; | ||
| 796 | |||
| 797 | /* Check whether this R-Car Gen3 can use the IPMMU correctly or not */ | ||
| 798 | if (!soc_device_match(soc_rcar_gen3_whitelist)) | ||
| 799 | return false; | ||
| 800 | |||
| 801 | /* Check whether this slave device can work with the IPMMU */ | ||
| 802 | for (i = 0; i < ARRAY_SIZE(rcar_gen3_slave_whitelist); i++) { | ||
| 803 | if (!strcmp(dev_name(dev), rcar_gen3_slave_whitelist[i])) | ||
| 804 | return true; | ||
| 805 | } | ||
| 806 | |||
| 807 | /* Otherwise, do not allow use of IPMMU */ | ||
| 808 | return false; | ||
| 809 | } | ||
| 810 | |||
| 769 | static int ipmmu_of_xlate(struct device *dev, | 811 | static int ipmmu_of_xlate(struct device *dev, |
| 770 | struct of_phandle_args *spec) | 812 | struct of_phandle_args *spec) |
| 771 | { | 813 | { |
| 772 | /* For R-Car Gen3 use a white list to opt-in slave devices */ | 814 | if (!ipmmu_slave_whitelist(dev)) |
| 773 | if (soc_device_match(soc_rcar_gen3) && !ipmmu_slave_whitelist(dev)) | ||
| 774 | return -ENODEV; | 815 | return -ENODEV; |
| 775 | 816 | ||
| 776 | iommu_fwspec_add_ids(dev, spec->args, 1); | 817 | iommu_fwspec_add_ids(dev, spec->args, 1); |
| @@ -938,6 +979,12 @@ static const struct of_device_id ipmmu_of_ids[] = { | |||
| 938 | .compatible = "renesas,ipmmu-vmsa", | 979 | .compatible = "renesas,ipmmu-vmsa", |
| 939 | .data = &ipmmu_features_default, | 980 | .data = &ipmmu_features_default, |
| 940 | }, { | 981 | }, { |
| 982 | .compatible = "renesas,ipmmu-r8a774a1", | ||
| 983 | .data = &ipmmu_features_rcar_gen3, | ||
| 984 | }, { | ||
| 985 | .compatible = "renesas,ipmmu-r8a774c0", | ||
| 986 | .data = &ipmmu_features_rcar_gen3, | ||
| 987 | }, { | ||
| 941 | .compatible = "renesas,ipmmu-r8a7795", | 988 | .compatible = "renesas,ipmmu-r8a7795", |
| 942 | .data = &ipmmu_features_rcar_gen3, | 989 | .data = &ipmmu_features_rcar_gen3, |
| 943 | }, { | 990 | }, { |
| @@ -950,6 +997,9 @@ static const struct of_device_id ipmmu_of_ids[] = { | |||
| 950 | .compatible = "renesas,ipmmu-r8a77970", | 997 | .compatible = "renesas,ipmmu-r8a77970", |
| 951 | .data = &ipmmu_features_rcar_gen3, | 998 | .data = &ipmmu_features_rcar_gen3, |
| 952 | }, { | 999 | }, { |
| 1000 | .compatible = "renesas,ipmmu-r8a77990", | ||
| 1001 | .data = &ipmmu_features_rcar_gen3, | ||
| 1002 | }, { | ||
| 953 | .compatible = "renesas,ipmmu-r8a77995", | 1003 | .compatible = "renesas,ipmmu-r8a77995", |
| 954 | .data = &ipmmu_features_rcar_gen3, | 1004 | .data = &ipmmu_features_rcar_gen3, |
| 955 | }, { | 1005 | }, { |
| @@ -957,8 +1007,6 @@ static const struct of_device_id ipmmu_of_ids[] = { | |||
| 957 | }, | 1007 | }, |
| 958 | }; | 1008 | }; |
| 959 | 1009 | ||
| 960 | MODULE_DEVICE_TABLE(of, ipmmu_of_ids); | ||
| 961 | |||
| 962 | static int ipmmu_probe(struct platform_device *pdev) | 1010 | static int ipmmu_probe(struct platform_device *pdev) |
| 963 | { | 1011 | { |
| 964 | struct ipmmu_vmsa_device *mmu; | 1012 | struct ipmmu_vmsa_device *mmu; |
| @@ -1129,15 +1177,4 @@ static int __init ipmmu_init(void) | |||
| 1129 | setup_done = true; | 1177 | setup_done = true; |
| 1130 | return 0; | 1178 | return 0; |
| 1131 | } | 1179 | } |
| 1132 | |||
| 1133 | static void __exit ipmmu_exit(void) | ||
| 1134 | { | ||
| 1135 | return platform_driver_unregister(&ipmmu_driver); | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | subsys_initcall(ipmmu_init); | 1180 | subsys_initcall(ipmmu_init); |
| 1139 | module_exit(ipmmu_exit); | ||
| 1140 | |||
| 1141 | MODULE_DESCRIPTION("IOMMU API for Renesas VMSA-compatible IPMMU"); | ||
| 1142 | MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); | ||
| 1143 | MODULE_LICENSE("GPL v2"); | ||
