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 | |
| 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>
| -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 | ||
