diff options
author | Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | 2014-05-15 06:40:42 -0400 |
---|---|---|
committer | Joerg Roedel <jroedel@suse.de> | 2014-05-26 05:18:22 -0400 |
commit | 192d2045707b25b984436eabfbfd3c8f1ada5a56 (patch) | |
tree | 56c707d26e8e2c35b75f269f87ab8146e4e89fd8 /drivers/iommu | |
parent | d25a2a16f0889de4a1cd8639896f35dc9465f6f5 (diff) |
iommu/ipmmu-vmsa: Refactor micro-TLB lookup
Cache the micro-TLB number in archdata allocated in the .add_device
handler instead of looking it up when the deviced is attached and
detached. This simplifies the .attach_dev and .detach_dev operations and
prepares for DT support.
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
Diffstat (limited to 'drivers/iommu')
-rw-r--r-- | drivers/iommu/ipmmu-vmsa.c | 92 |
1 files changed, 52 insertions, 40 deletions
diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index b084530babf4..1949f3c55679 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c | |||
@@ -44,6 +44,11 @@ struct ipmmu_vmsa_domain { | |||
44 | pgd_t *pgd; | 44 | pgd_t *pgd; |
45 | }; | 45 | }; |
46 | 46 | ||
47 | struct ipmmu_vmsa_archdata { | ||
48 | struct ipmmu_vmsa_device *mmu; | ||
49 | unsigned int utlb; | ||
50 | }; | ||
51 | |||
47 | static DEFINE_SPINLOCK(ipmmu_devices_lock); | 52 | static DEFINE_SPINLOCK(ipmmu_devices_lock); |
48 | static LIST_HEAD(ipmmu_devices); | 53 | static LIST_HEAD(ipmmu_devices); |
49 | 54 | ||
@@ -265,14 +270,19 @@ static void ipmmu_tlb_invalidate(struct ipmmu_vmsa_domain *domain) | |||
265 | * Enable MMU translation for the microTLB. | 270 | * Enable MMU translation for the microTLB. |
266 | */ | 271 | */ |
267 | static void ipmmu_utlb_enable(struct ipmmu_vmsa_domain *domain, | 272 | static void ipmmu_utlb_enable(struct ipmmu_vmsa_domain *domain, |
268 | const struct ipmmu_vmsa_master *master) | 273 | unsigned int utlb) |
269 | { | 274 | { |
270 | struct ipmmu_vmsa_device *mmu = domain->mmu; | 275 | struct ipmmu_vmsa_device *mmu = domain->mmu; |
271 | 276 | ||
277 | /* | ||
278 | * TODO: Reference-count the microTLB as several bus masters can be | ||
279 | * connected to the same microTLB. | ||
280 | */ | ||
281 | |||
272 | /* TODO: What should we set the ASID to ? */ | 282 | /* TODO: What should we set the ASID to ? */ |
273 | ipmmu_write(mmu, IMUASID(master->utlb), 0); | 283 | ipmmu_write(mmu, IMUASID(utlb), 0); |
274 | /* TODO: Do we need to flush the microTLB ? */ | 284 | /* TODO: Do we need to flush the microTLB ? */ |
275 | ipmmu_write(mmu, IMUCTR(master->utlb), | 285 | ipmmu_write(mmu, IMUCTR(utlb), |
276 | IMUCTR_TTSEL_MMU(domain->context_id) | IMUCTR_FLUSH | | 286 | IMUCTR_TTSEL_MMU(domain->context_id) | IMUCTR_FLUSH | |
277 | IMUCTR_MMUEN); | 287 | IMUCTR_MMUEN); |
278 | } | 288 | } |
@@ -281,11 +291,11 @@ static void ipmmu_utlb_enable(struct ipmmu_vmsa_domain *domain, | |||
281 | * Disable MMU translation for the microTLB. | 291 | * Disable MMU translation for the microTLB. |
282 | */ | 292 | */ |
283 | static void ipmmu_utlb_disable(struct ipmmu_vmsa_domain *domain, | 293 | static void ipmmu_utlb_disable(struct ipmmu_vmsa_domain *domain, |
284 | const struct ipmmu_vmsa_master *master) | 294 | unsigned int utlb) |
285 | { | 295 | { |
286 | struct ipmmu_vmsa_device *mmu = domain->mmu; | 296 | struct ipmmu_vmsa_device *mmu = domain->mmu; |
287 | 297 | ||
288 | ipmmu_write(mmu, IMUCTR(master->utlb), 0); | 298 | ipmmu_write(mmu, IMUCTR(utlb), 0); |
289 | } | 299 | } |
290 | 300 | ||
291 | static void ipmmu_flush_pgtable(struct ipmmu_vmsa_device *mmu, void *addr, | 301 | static void ipmmu_flush_pgtable(struct ipmmu_vmsa_device *mmu, void *addr, |
@@ -674,21 +684,6 @@ static int ipmmu_handle_mapping(struct ipmmu_vmsa_domain *domain, | |||
674 | * IOMMU Operations | 684 | * IOMMU Operations |
675 | */ | 685 | */ |
676 | 686 | ||
677 | static const struct ipmmu_vmsa_master * | ||
678 | ipmmu_find_master(struct ipmmu_vmsa_device *ipmmu, struct device *dev) | ||
679 | { | ||
680 | const struct ipmmu_vmsa_master *master = ipmmu->pdata->masters; | ||
681 | const char *devname = dev_name(dev); | ||
682 | unsigned int i; | ||
683 | |||
684 | for (i = 0; i < ipmmu->pdata->num_masters; ++i, ++master) { | ||
685 | if (strcmp(master->name, devname) == 0) | ||
686 | return master; | ||
687 | } | ||
688 | |||
689 | return NULL; | ||
690 | } | ||
691 | |||
692 | static int ipmmu_domain_init(struct iommu_domain *io_domain) | 687 | static int ipmmu_domain_init(struct iommu_domain *io_domain) |
693 | { | 688 | { |
694 | struct ipmmu_vmsa_domain *domain; | 689 | struct ipmmu_vmsa_domain *domain; |
@@ -727,9 +722,9 @@ static void ipmmu_domain_destroy(struct iommu_domain *io_domain) | |||
727 | static int ipmmu_attach_device(struct iommu_domain *io_domain, | 722 | static int ipmmu_attach_device(struct iommu_domain *io_domain, |
728 | struct device *dev) | 723 | struct device *dev) |
729 | { | 724 | { |
730 | struct ipmmu_vmsa_device *mmu = dev->archdata.iommu; | 725 | struct ipmmu_vmsa_archdata *archdata = dev->archdata.iommu; |
726 | struct ipmmu_vmsa_device *mmu = archdata->mmu; | ||
731 | struct ipmmu_vmsa_domain *domain = io_domain->priv; | 727 | struct ipmmu_vmsa_domain *domain = io_domain->priv; |
732 | const struct ipmmu_vmsa_master *master; | ||
733 | unsigned long flags; | 728 | unsigned long flags; |
734 | int ret = 0; | 729 | int ret = 0; |
735 | 730 | ||
@@ -759,11 +754,7 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain, | |||
759 | if (ret < 0) | 754 | if (ret < 0) |
760 | return ret; | 755 | return ret; |
761 | 756 | ||
762 | master = ipmmu_find_master(mmu, dev); | 757 | ipmmu_utlb_enable(domain, archdata->utlb); |
763 | if (!master) | ||
764 | return -EINVAL; | ||
765 | |||
766 | ipmmu_utlb_enable(domain, master); | ||
767 | 758 | ||
768 | return 0; | 759 | return 0; |
769 | } | 760 | } |
@@ -771,14 +762,10 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain, | |||
771 | static void ipmmu_detach_device(struct iommu_domain *io_domain, | 762 | static void ipmmu_detach_device(struct iommu_domain *io_domain, |
772 | struct device *dev) | 763 | struct device *dev) |
773 | { | 764 | { |
765 | struct ipmmu_vmsa_archdata *archdata = dev->archdata.iommu; | ||
774 | struct ipmmu_vmsa_domain *domain = io_domain->priv; | 766 | struct ipmmu_vmsa_domain *domain = io_domain->priv; |
775 | const struct ipmmu_vmsa_master *master; | ||
776 | 767 | ||
777 | master = ipmmu_find_master(domain->mmu, dev); | 768 | ipmmu_utlb_disable(domain, archdata->utlb); |
778 | if (!master) | ||
779 | return; | ||
780 | |||
781 | ipmmu_utlb_disable(domain, master); | ||
782 | 769 | ||
783 | /* | 770 | /* |
784 | * TODO: Optimize by disabling the context when no device is attached. | 771 | * TODO: Optimize by disabling the context when no device is attached. |
@@ -839,11 +826,26 @@ static phys_addr_t ipmmu_iova_to_phys(struct iommu_domain *io_domain, | |||
839 | return __pfn_to_phys(pte_pfn(pte)) | (iova & ~PAGE_MASK); | 826 | return __pfn_to_phys(pte_pfn(pte)) | (iova & ~PAGE_MASK); |
840 | } | 827 | } |
841 | 828 | ||
829 | static int ipmmu_find_utlb(struct ipmmu_vmsa_device *mmu, struct device *dev) | ||
830 | { | ||
831 | const struct ipmmu_vmsa_master *master = mmu->pdata->masters; | ||
832 | const char *devname = dev_name(dev); | ||
833 | unsigned int i; | ||
834 | |||
835 | for (i = 0; i < mmu->pdata->num_masters; ++i, ++master) { | ||
836 | if (strcmp(master->name, devname) == 0) | ||
837 | return master->utlb; | ||
838 | } | ||
839 | |||
840 | return -1; | ||
841 | } | ||
842 | |||
842 | static int ipmmu_add_device(struct device *dev) | 843 | static int ipmmu_add_device(struct device *dev) |
843 | { | 844 | { |
844 | const struct ipmmu_vmsa_master *master = NULL; | 845 | struct ipmmu_vmsa_archdata *archdata; |
845 | struct ipmmu_vmsa_device *mmu; | 846 | struct ipmmu_vmsa_device *mmu; |
846 | struct iommu_group *group; | 847 | struct iommu_group *group; |
848 | int utlb = -1; | ||
847 | int ret; | 849 | int ret; |
848 | 850 | ||
849 | if (dev->archdata.iommu) { | 851 | if (dev->archdata.iommu) { |
@@ -856,10 +858,10 @@ static int ipmmu_add_device(struct device *dev) | |||
856 | spin_lock(&ipmmu_devices_lock); | 858 | spin_lock(&ipmmu_devices_lock); |
857 | 859 | ||
858 | list_for_each_entry(mmu, &ipmmu_devices, list) { | 860 | list_for_each_entry(mmu, &ipmmu_devices, list) { |
859 | master = ipmmu_find_master(mmu, dev); | 861 | utlb = ipmmu_find_utlb(mmu, dev); |
860 | if (master) { | 862 | if (utlb >= 0) { |
861 | /* | 863 | /* |
862 | * TODO Take a reference to the master to protect | 864 | * TODO Take a reference to the MMU to protect |
863 | * against device removal. | 865 | * against device removal. |
864 | */ | 866 | */ |
865 | break; | 867 | break; |
@@ -868,10 +870,10 @@ static int ipmmu_add_device(struct device *dev) | |||
868 | 870 | ||
869 | spin_unlock(&ipmmu_devices_lock); | 871 | spin_unlock(&ipmmu_devices_lock); |
870 | 872 | ||
871 | if (!master) | 873 | if (utlb < 0) |
872 | return -ENODEV; | 874 | return -ENODEV; |
873 | 875 | ||
874 | if (!master->utlb >= mmu->num_utlbs) | 876 | if (utlb >= mmu->num_utlbs) |
875 | return -EINVAL; | 877 | return -EINVAL; |
876 | 878 | ||
877 | /* Create a device group and add the device to it. */ | 879 | /* Create a device group and add the device to it. */ |
@@ -889,7 +891,15 @@ static int ipmmu_add_device(struct device *dev) | |||
889 | return ret; | 891 | return ret; |
890 | } | 892 | } |
891 | 893 | ||
892 | dev->archdata.iommu = mmu; | 894 | archdata = kzalloc(sizeof(*archdata), GFP_KERNEL); |
895 | if (!archdata) { | ||
896 | ret = -ENOMEM; | ||
897 | goto error; | ||
898 | } | ||
899 | |||
900 | archdata->mmu = mmu; | ||
901 | archdata->utlb = utlb; | ||
902 | dev->archdata.iommu = archdata; | ||
893 | 903 | ||
894 | /* | 904 | /* |
895 | * Create the ARM mapping, used by the ARM DMA mapping core to allocate | 905 | * Create the ARM mapping, used by the ARM DMA mapping core to allocate |
@@ -923,6 +933,7 @@ static int ipmmu_add_device(struct device *dev) | |||
923 | return 0; | 933 | return 0; |
924 | 934 | ||
925 | error: | 935 | error: |
936 | kfree(dev->archdata.iommu); | ||
926 | dev->archdata.iommu = NULL; | 937 | dev->archdata.iommu = NULL; |
927 | iommu_group_remove_device(dev); | 938 | iommu_group_remove_device(dev); |
928 | return ret; | 939 | return ret; |
@@ -932,6 +943,7 @@ static void ipmmu_remove_device(struct device *dev) | |||
932 | { | 943 | { |
933 | arm_iommu_detach_device(dev); | 944 | arm_iommu_detach_device(dev); |
934 | iommu_group_remove_device(dev); | 945 | iommu_group_remove_device(dev); |
946 | kfree(dev->archdata.iommu); | ||
935 | dev->archdata.iommu = NULL; | 947 | dev->archdata.iommu = NULL; |
936 | } | 948 | } |
937 | 949 | ||