Loading MAINTAINERS +1 −2 Original line number Diff line number Diff line Loading @@ -1350,8 +1350,7 @@ M: Will Deacon <will@kernel.org> R: Robin Murphy <robin.murphy@arm.com> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: drivers/iommu/arm-smmu.c F: drivers/iommu/arm-smmu-v3.c F: drivers/iommu/arm-smmu* F: drivers/iommu/io-pgtable-arm.c F: drivers/iommu/io-pgtable-arm-v7s.c Loading drivers/gpu/drm/panfrost/panfrost_mmu.c +16 −8 Original line number Diff line number Diff line Loading @@ -222,7 +222,7 @@ void panfrost_mmu_unmap(struct panfrost_gem_object *bo) size_t unmapped_page; size_t pgsize = get_pgsize(iova, len - unmapped_len); unmapped_page = ops->unmap(ops, iova, pgsize); unmapped_page = ops->unmap(ops, iova, pgsize, NULL); if (!unmapped_page) break; Loading @@ -247,20 +247,28 @@ static void mmu_tlb_inv_context_s1(void *cookie) mmu_hw_do_operation(pfdev, 0, 0, ~0UL, AS_COMMAND_FLUSH_MEM); } static void mmu_tlb_inv_range_nosync(unsigned long iova, size_t size, size_t granule, bool leaf, void *cookie) {} static void mmu_tlb_sync_context(void *cookie) { //struct panfrost_device *pfdev = cookie; // TODO: Wait 1000 GPU cycles for HW_ISSUE_6367/T60X } static const struct iommu_gather_ops mmu_tlb_ops = { static void mmu_tlb_flush_walk(unsigned long iova, size_t size, size_t granule, void *cookie) { mmu_tlb_sync_context(cookie); } static void mmu_tlb_flush_leaf(unsigned long iova, size_t size, size_t granule, void *cookie) { mmu_tlb_sync_context(cookie); } static const struct iommu_flush_ops mmu_tlb_ops = { .tlb_flush_all = mmu_tlb_inv_context_s1, .tlb_add_flush = mmu_tlb_inv_range_nosync, .tlb_sync = mmu_tlb_sync_context, .tlb_flush_walk = mmu_tlb_flush_walk, .tlb_flush_leaf = mmu_tlb_flush_leaf, }; static const char *access_type_name(struct panfrost_device *pfdev, Loading drivers/iommu/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -13,7 +13,7 @@ obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o obj-$(CONFIG_AMD_IOMMU_DEBUGFS) += amd_iommu_debugfs.o obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o obj-$(CONFIG_ARM_SMMU) += arm-smmu.o obj-$(CONFIG_ARM_SMMU) += arm-smmu.o arm-smmu-impl.o obj-$(CONFIG_ARM_SMMU_V3) += arm-smmu-v3.o obj-$(CONFIG_DMAR_TABLE) += dmar.o obj-$(CONFIG_INTEL_IOMMU) += intel-iommu.o intel-pasid.o Loading drivers/iommu/amd_iommu.c +6 −5 Original line number Diff line number Diff line Loading @@ -3055,7 +3055,8 @@ static int amd_iommu_map(struct iommu_domain *dom, unsigned long iova, } static size_t amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova, size_t page_size) size_t page_size, struct iommu_iotlb_gather *gather) { struct protection_domain *domain = to_pdomain(dom); size_t unmap_size; Loading Loading @@ -3196,9 +3197,10 @@ static void amd_iommu_flush_iotlb_all(struct iommu_domain *domain) domain_flush_complete(dom); } static void amd_iommu_iotlb_range_add(struct iommu_domain *domain, unsigned long iova, size_t size) static void amd_iommu_iotlb_sync(struct iommu_domain *domain, struct iommu_iotlb_gather *gather) { amd_iommu_flush_iotlb_all(domain); } const struct iommu_ops amd_iommu_ops = { Loading @@ -3219,8 +3221,7 @@ const struct iommu_ops amd_iommu_ops = { .is_attach_deferred = amd_iommu_is_attach_deferred, .pgsize_bitmap = AMD_IOMMU_PGSIZES, .flush_iotlb_all = amd_iommu_flush_iotlb_all, .iotlb_range_add = amd_iommu_iotlb_range_add, .iotlb_sync = amd_iommu_flush_iotlb_all, .iotlb_sync = amd_iommu_iotlb_sync, }; /***************************************************************************** Loading drivers/iommu/arm-smmu-impl.c 0 → 100644 +174 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only // Miscellaneous Arm SMMU implementation and integration quirks // Copyright (C) 2019 Arm Limited #define pr_fmt(fmt) "arm-smmu: " fmt #include <linux/bitfield.h> #include <linux/of.h> #include "arm-smmu.h" static int arm_smmu_gr0_ns(int offset) { switch(offset) { case ARM_SMMU_GR0_sCR0: case ARM_SMMU_GR0_sACR: case ARM_SMMU_GR0_sGFSR: case ARM_SMMU_GR0_sGFSYNR0: case ARM_SMMU_GR0_sGFSYNR1: case ARM_SMMU_GR0_sGFSYNR2: return offset + 0x400; default: return offset; } } static u32 arm_smmu_read_ns(struct arm_smmu_device *smmu, int page, int offset) { if (page == ARM_SMMU_GR0) offset = arm_smmu_gr0_ns(offset); return readl_relaxed(arm_smmu_page(smmu, page) + offset); } static void arm_smmu_write_ns(struct arm_smmu_device *smmu, int page, int offset, u32 val) { if (page == ARM_SMMU_GR0) offset = arm_smmu_gr0_ns(offset); writel_relaxed(val, arm_smmu_page(smmu, page) + offset); } /* Since we don't care for sGFAR, we can do without 64-bit accessors */ static const struct arm_smmu_impl calxeda_impl = { .read_reg = arm_smmu_read_ns, .write_reg = arm_smmu_write_ns, }; struct cavium_smmu { struct arm_smmu_device smmu; u32 id_base; }; static int cavium_cfg_probe(struct arm_smmu_device *smmu) { static atomic_t context_count = ATOMIC_INIT(0); struct cavium_smmu *cs = container_of(smmu, struct cavium_smmu, smmu); /* * Cavium CN88xx erratum #27704. * Ensure ASID and VMID allocation is unique across all SMMUs in * the system. */ cs->id_base = atomic_fetch_add(smmu->num_context_banks, &context_count); dev_notice(smmu->dev, "\tenabling workaround for Cavium erratum 27704\n"); return 0; } static int cavium_init_context(struct arm_smmu_domain *smmu_domain) { struct cavium_smmu *cs = container_of(smmu_domain->smmu, struct cavium_smmu, smmu); if (smmu_domain->stage == ARM_SMMU_DOMAIN_S2) smmu_domain->cfg.vmid += cs->id_base; else smmu_domain->cfg.asid += cs->id_base; return 0; } static const struct arm_smmu_impl cavium_impl = { .cfg_probe = cavium_cfg_probe, .init_context = cavium_init_context, }; static struct arm_smmu_device *cavium_smmu_impl_init(struct arm_smmu_device *smmu) { struct cavium_smmu *cs; cs = devm_kzalloc(smmu->dev, sizeof(*cs), GFP_KERNEL); if (!cs) return ERR_PTR(-ENOMEM); cs->smmu = *smmu; cs->smmu.impl = &cavium_impl; devm_kfree(smmu->dev, smmu); return &cs->smmu; } #define ARM_MMU500_ACTLR_CPRE (1 << 1) #define ARM_MMU500_ACR_CACHE_LOCK (1 << 26) #define ARM_MMU500_ACR_S2CRB_TLBEN (1 << 10) #define ARM_MMU500_ACR_SMTNMB_TLBEN (1 << 8) static int arm_mmu500_reset(struct arm_smmu_device *smmu) { u32 reg, major; int i; /* * On MMU-500 r2p0 onwards we need to clear ACR.CACHE_LOCK before * writes to the context bank ACTLRs will stick. And we just hope that * Secure has also cleared SACR.CACHE_LOCK for this to take effect... */ reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_ID7); major = FIELD_GET(ID7_MAJOR, reg); reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sACR); if (major >= 2) reg &= ~ARM_MMU500_ACR_CACHE_LOCK; /* * Allow unmatched Stream IDs to allocate bypass * TLB entries for reduced latency. */ reg |= ARM_MMU500_ACR_SMTNMB_TLBEN | ARM_MMU500_ACR_S2CRB_TLBEN; arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sACR, reg); /* * Disable MMU-500's not-particularly-beneficial next-page * prefetcher for the sake of errata #841119 and #826419. */ for (i = 0; i < smmu->num_context_banks; ++i) { reg = arm_smmu_cb_read(smmu, i, ARM_SMMU_CB_ACTLR); reg &= ~ARM_MMU500_ACTLR_CPRE; arm_smmu_cb_write(smmu, i, ARM_SMMU_CB_ACTLR, reg); } return 0; } static const struct arm_smmu_impl arm_mmu500_impl = { .reset = arm_mmu500_reset, }; struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu) { /* * We will inevitably have to combine model-specific implementation * quirks with platform-specific integration quirks, but everything * we currently support happens to work out as straightforward * mutually-exclusive assignments. */ switch (smmu->model) { case ARM_MMU500: smmu->impl = &arm_mmu500_impl; break; case CAVIUM_SMMUV2: return cavium_smmu_impl_init(smmu); default: break; } if (of_property_read_bool(smmu->dev->of_node, "calxeda,smmu-secure-config-access")) smmu->impl = &calxeda_impl; return smmu; } Loading
MAINTAINERS +1 −2 Original line number Diff line number Diff line Loading @@ -1350,8 +1350,7 @@ M: Will Deacon <will@kernel.org> R: Robin Murphy <robin.murphy@arm.com> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: drivers/iommu/arm-smmu.c F: drivers/iommu/arm-smmu-v3.c F: drivers/iommu/arm-smmu* F: drivers/iommu/io-pgtable-arm.c F: drivers/iommu/io-pgtable-arm-v7s.c Loading
drivers/gpu/drm/panfrost/panfrost_mmu.c +16 −8 Original line number Diff line number Diff line Loading @@ -222,7 +222,7 @@ void panfrost_mmu_unmap(struct panfrost_gem_object *bo) size_t unmapped_page; size_t pgsize = get_pgsize(iova, len - unmapped_len); unmapped_page = ops->unmap(ops, iova, pgsize); unmapped_page = ops->unmap(ops, iova, pgsize, NULL); if (!unmapped_page) break; Loading @@ -247,20 +247,28 @@ static void mmu_tlb_inv_context_s1(void *cookie) mmu_hw_do_operation(pfdev, 0, 0, ~0UL, AS_COMMAND_FLUSH_MEM); } static void mmu_tlb_inv_range_nosync(unsigned long iova, size_t size, size_t granule, bool leaf, void *cookie) {} static void mmu_tlb_sync_context(void *cookie) { //struct panfrost_device *pfdev = cookie; // TODO: Wait 1000 GPU cycles for HW_ISSUE_6367/T60X } static const struct iommu_gather_ops mmu_tlb_ops = { static void mmu_tlb_flush_walk(unsigned long iova, size_t size, size_t granule, void *cookie) { mmu_tlb_sync_context(cookie); } static void mmu_tlb_flush_leaf(unsigned long iova, size_t size, size_t granule, void *cookie) { mmu_tlb_sync_context(cookie); } static const struct iommu_flush_ops mmu_tlb_ops = { .tlb_flush_all = mmu_tlb_inv_context_s1, .tlb_add_flush = mmu_tlb_inv_range_nosync, .tlb_sync = mmu_tlb_sync_context, .tlb_flush_walk = mmu_tlb_flush_walk, .tlb_flush_leaf = mmu_tlb_flush_leaf, }; static const char *access_type_name(struct panfrost_device *pfdev, Loading
drivers/iommu/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -13,7 +13,7 @@ obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o obj-$(CONFIG_AMD_IOMMU_DEBUGFS) += amd_iommu_debugfs.o obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o obj-$(CONFIG_ARM_SMMU) += arm-smmu.o obj-$(CONFIG_ARM_SMMU) += arm-smmu.o arm-smmu-impl.o obj-$(CONFIG_ARM_SMMU_V3) += arm-smmu-v3.o obj-$(CONFIG_DMAR_TABLE) += dmar.o obj-$(CONFIG_INTEL_IOMMU) += intel-iommu.o intel-pasid.o Loading
drivers/iommu/amd_iommu.c +6 −5 Original line number Diff line number Diff line Loading @@ -3055,7 +3055,8 @@ static int amd_iommu_map(struct iommu_domain *dom, unsigned long iova, } static size_t amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova, size_t page_size) size_t page_size, struct iommu_iotlb_gather *gather) { struct protection_domain *domain = to_pdomain(dom); size_t unmap_size; Loading Loading @@ -3196,9 +3197,10 @@ static void amd_iommu_flush_iotlb_all(struct iommu_domain *domain) domain_flush_complete(dom); } static void amd_iommu_iotlb_range_add(struct iommu_domain *domain, unsigned long iova, size_t size) static void amd_iommu_iotlb_sync(struct iommu_domain *domain, struct iommu_iotlb_gather *gather) { amd_iommu_flush_iotlb_all(domain); } const struct iommu_ops amd_iommu_ops = { Loading @@ -3219,8 +3221,7 @@ const struct iommu_ops amd_iommu_ops = { .is_attach_deferred = amd_iommu_is_attach_deferred, .pgsize_bitmap = AMD_IOMMU_PGSIZES, .flush_iotlb_all = amd_iommu_flush_iotlb_all, .iotlb_range_add = amd_iommu_iotlb_range_add, .iotlb_sync = amd_iommu_flush_iotlb_all, .iotlb_sync = amd_iommu_iotlb_sync, }; /***************************************************************************** Loading
drivers/iommu/arm-smmu-impl.c 0 → 100644 +174 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only // Miscellaneous Arm SMMU implementation and integration quirks // Copyright (C) 2019 Arm Limited #define pr_fmt(fmt) "arm-smmu: " fmt #include <linux/bitfield.h> #include <linux/of.h> #include "arm-smmu.h" static int arm_smmu_gr0_ns(int offset) { switch(offset) { case ARM_SMMU_GR0_sCR0: case ARM_SMMU_GR0_sACR: case ARM_SMMU_GR0_sGFSR: case ARM_SMMU_GR0_sGFSYNR0: case ARM_SMMU_GR0_sGFSYNR1: case ARM_SMMU_GR0_sGFSYNR2: return offset + 0x400; default: return offset; } } static u32 arm_smmu_read_ns(struct arm_smmu_device *smmu, int page, int offset) { if (page == ARM_SMMU_GR0) offset = arm_smmu_gr0_ns(offset); return readl_relaxed(arm_smmu_page(smmu, page) + offset); } static void arm_smmu_write_ns(struct arm_smmu_device *smmu, int page, int offset, u32 val) { if (page == ARM_SMMU_GR0) offset = arm_smmu_gr0_ns(offset); writel_relaxed(val, arm_smmu_page(smmu, page) + offset); } /* Since we don't care for sGFAR, we can do without 64-bit accessors */ static const struct arm_smmu_impl calxeda_impl = { .read_reg = arm_smmu_read_ns, .write_reg = arm_smmu_write_ns, }; struct cavium_smmu { struct arm_smmu_device smmu; u32 id_base; }; static int cavium_cfg_probe(struct arm_smmu_device *smmu) { static atomic_t context_count = ATOMIC_INIT(0); struct cavium_smmu *cs = container_of(smmu, struct cavium_smmu, smmu); /* * Cavium CN88xx erratum #27704. * Ensure ASID and VMID allocation is unique across all SMMUs in * the system. */ cs->id_base = atomic_fetch_add(smmu->num_context_banks, &context_count); dev_notice(smmu->dev, "\tenabling workaround for Cavium erratum 27704\n"); return 0; } static int cavium_init_context(struct arm_smmu_domain *smmu_domain) { struct cavium_smmu *cs = container_of(smmu_domain->smmu, struct cavium_smmu, smmu); if (smmu_domain->stage == ARM_SMMU_DOMAIN_S2) smmu_domain->cfg.vmid += cs->id_base; else smmu_domain->cfg.asid += cs->id_base; return 0; } static const struct arm_smmu_impl cavium_impl = { .cfg_probe = cavium_cfg_probe, .init_context = cavium_init_context, }; static struct arm_smmu_device *cavium_smmu_impl_init(struct arm_smmu_device *smmu) { struct cavium_smmu *cs; cs = devm_kzalloc(smmu->dev, sizeof(*cs), GFP_KERNEL); if (!cs) return ERR_PTR(-ENOMEM); cs->smmu = *smmu; cs->smmu.impl = &cavium_impl; devm_kfree(smmu->dev, smmu); return &cs->smmu; } #define ARM_MMU500_ACTLR_CPRE (1 << 1) #define ARM_MMU500_ACR_CACHE_LOCK (1 << 26) #define ARM_MMU500_ACR_S2CRB_TLBEN (1 << 10) #define ARM_MMU500_ACR_SMTNMB_TLBEN (1 << 8) static int arm_mmu500_reset(struct arm_smmu_device *smmu) { u32 reg, major; int i; /* * On MMU-500 r2p0 onwards we need to clear ACR.CACHE_LOCK before * writes to the context bank ACTLRs will stick. And we just hope that * Secure has also cleared SACR.CACHE_LOCK for this to take effect... */ reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_ID7); major = FIELD_GET(ID7_MAJOR, reg); reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sACR); if (major >= 2) reg &= ~ARM_MMU500_ACR_CACHE_LOCK; /* * Allow unmatched Stream IDs to allocate bypass * TLB entries for reduced latency. */ reg |= ARM_MMU500_ACR_SMTNMB_TLBEN | ARM_MMU500_ACR_S2CRB_TLBEN; arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sACR, reg); /* * Disable MMU-500's not-particularly-beneficial next-page * prefetcher for the sake of errata #841119 and #826419. */ for (i = 0; i < smmu->num_context_banks; ++i) { reg = arm_smmu_cb_read(smmu, i, ARM_SMMU_CB_ACTLR); reg &= ~ARM_MMU500_ACTLR_CPRE; arm_smmu_cb_write(smmu, i, ARM_SMMU_CB_ACTLR, reg); } return 0; } static const struct arm_smmu_impl arm_mmu500_impl = { .reset = arm_mmu500_reset, }; struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu) { /* * We will inevitably have to combine model-specific implementation * quirks with platform-specific integration quirks, but everything * we currently support happens to work out as straightforward * mutually-exclusive assignments. */ switch (smmu->model) { case ARM_MMU500: smmu->impl = &arm_mmu500_impl; break; case CAVIUM_SMMUV2: return cavium_smmu_impl_init(smmu); default: break; } if (of_property_read_bool(smmu->dev->of_node, "calxeda,smmu-secure-config-access")) smmu->impl = &calxeda_impl; return smmu; }