Skip to contentSkip to conversation

Add support for Hisilicon SMMU architecture

PATCH Superseded Zhen Lei 2 participants 16 messages Jul 10, 2014 – Jul 22, 2014 1 message not in the archive

Subsystems (3)
lore
Share & tools
lore

Zhen Lei

author
#0 lore raw

v3 cover Add support for Hisilicon SMMU architecture

Changes in v3:
- Split arm-smmu.c into three files: arm-smmu.h arm-smmu-base.c arm-smmu.c. To build stardard arm-smmu(v1,v2) driver, use these three files. To build hisilicon smmu driver, replace arm-smmu.c with hisi-smmu.c. Put all registers(exclude translation table associated) definition into each smmu private file.
Now, hisi smmu driver is not dependent on arm smmu driver. They can seperate exist, or coexist(building and running two drivers at the same time).
- Give up Hisilicon private device tree properties.
- Place hooks from global variable into struct arm_smmu_device. Deleted three hooks: tlb_sync, flush_pgtable and dt_cfg_probe. Add device_remove hook, so hisi-smmu and smmu-v3 can reclaim other resources. like dynamic allocted memory. Now, s1cbt and s2cbt memory are allocated in hisi-smmu driver again(In v2, I hope BIOS to do this).
- Share the codes which are used to limit the size of smmu ias,oas,uas.
- Add two little patchs about code style, variable types, etc.
- Change context_map in struct arm_smmu_device to dynamic memory allocation.

Changes in v2:
- Split Hisilicon smmu implementation in a separate file, hisi-smmu.c
- Refactor arm-smmu.c. Some direct call hardware dependent functions replaced with hooks. And move common struct and marco definition into arm-smmu.h
- Merge the description of Hisilicon private properties into arm,smmu.txt

I tried to merge hisi-smmu driver into arm-smmu.c, but it looks impossible. The biggest problem is that too many registers are diffrent: the base address, the field definition, or present only on one side. And if I use #if, hisi-smmu and arm-smmu can not coexist in one binary file. Almost need 20 #if.

In addition, SMMUv3 is also not compatible with v2. And something is similar with hisi-smmu: registers definition and fault handler is different with v2, but can reuse fdt configuration and memory map. Hence, arm-smmu-base.c and arm-smmu.h should be shared by all SMMUs(v2, v3 and hisi), and each smmu will own a private file, like: arm-smmu.c(for v1 and v2), arm-smmu-v3.c, hisi-smmu.c

All marcos which are not used in arm-smmu-base.c and not shared by all SMMUs, have been placed into each private file, some are duplicated. But I think it will not bring any maintenance headaches, except when need rename the marcos. After all, it is hardware dependent.

Zhen Lei (13):
iommu/arm: change some structure member types in arm_smmu_device iommu/arm: eliminate errors reported by checkpatch iommu/arm: split arm-smmu.c into three files iommu/arm: avoid arm-smmu-base.c bound to a specific smmu iommu/arm: Rename member "cbar" in struct arm_smmu_cfg to "type" iommu/arm: Split arm_smmu_destroy_domain_context to reuse code iommu/arm: Split arm_smmu_tlb_sync to reuse code iommu/arm: Add hook alloc_context
iommu/arm: Change context_map to dynamic memory allocation iommu/arm: Adjust code to reuse more
iommu/arm: Put off the initialization phase of arm_smmu_ops
registration
iommu/hisilicon: Add support for Hisilicon Ltd. System MMU
architecture
documentation/iommu: Add description of Hisilicon SMMU private
binding

 .../devicetree/bindings/iommu/arm,smmu.txt         |    2 +
 drivers/iommu/Kconfig                              |   14 +
 drivers/iommu/Makefile                             |    2 +
 drivers/iommu/arm-smmu-base.c                      | 1085 +++++++++++++++++
 drivers/iommu/arm-smmu.c                           | 1247 +-------------------
 drivers/iommu/arm-smmu.h                           |  258 ++++
 drivers/iommu/hisi-smmu.c                          |  575 +++++++++
 7 files changed, 1985 insertions(+), 1198 deletions(-)
 create mode 100644 drivers/iommu/arm-smmu-base.c
 create mode 100644 drivers/iommu/arm-smmu.h
 create mode 100644 drivers/iommu/hisi-smmu.c

--
1.8.0

author
#1 lore raw

PATCH v3 1/13 iommu/arm: change some structure member types in arm_smmu_device

Some structure members, such as s1_output_size, it's impossible large than 4G. Change unsigned long to u32 can save a few memory on ARM64.

---
 drivers/iommu/arm-smmu.c | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)
drivers/iommu/arm-smmu.c +14 -14
index 1599354..d0f0021 100644
@@ -352,8 +352,8 @@ struct arm_smmu_device {
 	struct device_node		*parent_of_node;
 
 	void __iomem			*base;
-	unsigned long			size;
-	unsigned long			pagesize;
+	u32				size;
+	u32				pagesize;
 
 #define ARM_SMMU_FEAT_COHERENT_WALK	(1 << 0)
 #define ARM_SMMU_FEAT_STREAM_MATCH	(1 << 1)
@@ -374,9 +374,9 @@ struct arm_smmu_device {
 	u32				num_mapping_groups;
 	DECLARE_BITMAP(smr_map, ARM_SMMU_MAX_SMRS);
 
-	unsigned long			input_size;
-	unsigned long			s1_output_size;
-	unsigned long			s2_output_size;
+	u32				input_size;
+	u32				s1_output_size;
+	u32				s2_output_size;
 
 	u32				num_global_irqs;
 	u32				num_context_irqs;
@@ -1676,7 +1676,7 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
 	writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
 }
 
-static int arm_smmu_id_size_to_bits(int size)
+static u32 arm_smmu_id_size_to_bits(u32 size)
 {
 	switch (size) {
 	case 0:
@@ -1697,7 +1697,7 @@ static int arm_smmu_id_size_to_bits(int size)
 
 static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 {
-	unsigned long size;
+	u32 size;
 	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
 	u32 id;
 
@@ -1782,8 +1782,8 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 	size = 1 << (((id >> ID1_NUMPAGENDXB_SHIFT) & ID1_NUMPAGENDXB_MASK) + 1);
 	size *= (smmu->pagesize << 1);
 	if (smmu->size != size)
-		dev_warn(smmu->dev, "SMMU address space size (0x%lx) differs "
-			"from mapped region size (0x%lx)!\n", size, smmu->size);
+		dev_warn(smmu->dev, "SMMU address space size (0x%x) differs "
+			"from mapped region size (0x%x)!\n", size, smmu->size);
 
 	smmu->num_s2_context_banks = (id >> ID1_NUMS2CB_SHIFT) &
 				      ID1_NUMS2CB_MASK;
@@ -1804,21 +1804,21 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 	 * allocation (PTRS_PER_PGD).
 	 */
 #ifdef CONFIG_64BIT
-	smmu->s1_output_size = min((unsigned long)VA_BITS, size);
+	smmu->s1_output_size = min((u32)VA_BITS, size);
 #else
-	smmu->s1_output_size = min(32UL, size);
+	smmu->s1_output_size = min(32U, size);
 #endif
 
 	/* The stage-2 output mask is also applied for bypass */
 	size = arm_smmu_id_size_to_bits((id >> ID2_OAS_SHIFT) & ID2_OAS_MASK);
-	smmu->s2_output_size = min((unsigned long)PHYS_MASK_SHIFT, size);
+	smmu->s2_output_size = min((u32)PHYS_MASK_SHIFT, size);
 
 	if (smmu->version == 1) {
 		smmu->input_size = 32;
 	} else {
 #ifdef CONFIG_64BIT
 		size = (id >> ID2_UBS_SHIFT) & ID2_UBS_MASK;
-		size = min(VA_BITS, arm_smmu_id_size_to_bits(size));
+		size = min((u32)VA_BITS, arm_smmu_id_size_to_bits(size));
 #else
 		size = 32;
 #endif
@@ -1834,7 +1834,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 	}
 
 	dev_notice(smmu->dev,
-		   "\t%lu-bit VA, %lu-bit IPA, %lu-bit PA\n",
+		   "\t%u-bit VA, %u-bit IPA, %u-bit PA\n",
 		   smmu->input_size, smmu->s1_output_size, smmu->s2_output_size);
 	return 0;
 }

--
1.8.0

author
#2 lore raw

PATCH v3 2/13 iommu/arm: eliminate errors reported by checkpatch

In reply to Zhen Lei

If run scripts/checkpatch.pl, returns some errors. About code style, etc.

---
 drivers/iommu/arm-smmu.c | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)
drivers/iommu/arm-smmu.c +4 -7
index d0f0021..dfc53e7 100644
@@ -10,10 +10,6 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
  * Copyright (C) 2013 ARM Limited
  *
  * Author: Will Deacon <[email protected]>
@@ -419,7 +415,7 @@ struct arm_smmu_option_prop {
 	const char *prop;
 };
 
-static struct arm_smmu_option_prop arm_smmu_options [] = {
+static struct arm_smmu_option_prop arm_smmu_options[] = {
 	{ ARM_SMMU_OPT_SECURE_CFG_ACCESS, "calxeda,smmu-secure-config-access" },
 	{ 0, NULL},
 };
@@ -1913,7 +1909,8 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	}
 	dev_notice(dev, "registered %d master devices\n", i);
 
-	if ((dev_node = of_parse_phandle(dev->of_node, "smmu-parent", 0)))
+	dev_node = of_parse_phandle(dev->of_node, "smmu-parent", 0);
+	if (dev_node)
 		smmu->parent_of_node = dev_node;
 
 	err = arm_smmu_device_cfg_probe(smmu);
@@ -2006,7 +2003,7 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
 		free_irq(smmu->irqs[i], smmu);
 
 	/* Turn the thing off */
-	writel(sCR0_CLIENTPD,ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
+	writel(sCR0_CLIENTPD, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
 	return 0;
 }
 

--
1.8.0

author
#3 lore raw

PATCH v3 3/13 iommu/arm: split arm-smmu.c into three files

In reply to Zhen Lei

To support other SMMUs(contains SMMUv3) which using incompatible registers definition relate to SMMUv1-2, but choose ARMv8 Translation System. In order to reuse current arm-smmu(SMMUv1-2) code as much as possible, apart arm-smmu.c. Both arm-smmu-base.c and arm-smmu.h are shared by all SMMUs.

This is a intermediate patch, just simpe apart arm-smmu.c, to make subsequent changes clear.

---
 drivers/iommu/Kconfig         |    4 +
 drivers/iommu/Makefile        |    1 +
 drivers/iommu/arm-smmu-base.c | 1009 +++++++++++++++++++++++++++++++++++
 drivers/iommu/arm-smmu.c      | 1164 +----------------------------------------
 drivers/iommu/arm-smmu.h      |  230 ++++++++
 5 files changed, 1257 insertions(+), 1151 deletions(-)
 create mode 100644 drivers/iommu/arm-smmu-base.c
 create mode 100644 drivers/iommu/arm-smmu.h
drivers/iommu/Kconfig +4 -0
index d260605..fad5e38 100644
@@ -292,10 +292,14 @@ config SPAPR_TCE_IOMMU
 	  Enables bits of IOMMU API required by VFIO. The iommu_ops
 	  is not implemented as it is not necessary for VFIO.
 
+config ARM_SMMU_BASE
+	bool
+
 config ARM_SMMU
 	bool "ARM Ltd. System MMU (SMMU) Support"
 	depends on ARM64 || (ARM_LPAE && OF)
 	select IOMMU_API
+	select ARM_SMMU_BASE
 	select ARM_DMA_USE_IOMMU if ARM
 	help
 	  Support for implementations of the ARM System MMU architecture
drivers/iommu/Makefile +1 -0
index 8893bad..717cfa3 100644
@@ -4,6 +4,7 @@ obj-$(CONFIG_OF_IOMMU)	+= of_iommu.o
 obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
 obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
 obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
+obj-$(CONFIG_ARM_SMMU_BASE) += arm-smmu-base.o
 obj-$(CONFIG_ARM_SMMU) += arm-smmu.o
 obj-$(CONFIG_DMAR_TABLE) += dmar.o
 obj-$(CONFIG_INTEL_IOMMU) += iova.o intel-iommu.o
drivers/iommu/arm-smmu-base.c new file +1009 -0

1,009 lines not on this page · raw message

drivers/iommu/arm-smmu.c +13 -1151

1,289 lines not on this page · raw message

drivers/iommu/arm-smmu.h new file +230 -0

230 lines not on this page · raw message

--
1.8.0

author
#4 lore raw

PATCH v3 4/13 iommu/arm: avoid arm-smmu-base.c bound to a specific smmu

In reply to Zhen Lei

To keep all SMMU drivers can be running at the same time, place hwdep-ops hooks in structure arm_smmu_device, so each smmu can correctly invoke the appropriate hooks at running time.

---
 drivers/iommu/arm-smmu-base.c | 28 +++++++++++++++++----------
 drivers/iommu/arm-smmu.c      | 39 ++++++++++++++++++++++++++++----------
 drivers/iommu/arm-smmu.h      | 44 +++++++++++++++++++++++++++++++------------
 3 files changed, 79 insertions(+), 32 deletions(-)
drivers/iommu/arm-smmu-base.c +18 -10
index 2fd29e5..c6824b5 100644
@@ -264,7 +264,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
 	}
 
 	irq = smmu->irqs[smmu->num_global_irqs + root_cfg->irptndx];
-	ret = request_irq(irq, arm_smmu_context_fault, IRQF_SHARED,
+	ret = request_irq(irq, smmu->hwdep_ops->context_fault, IRQF_SHARED,
 			  "arm-smmu-context-fault", domain);
 	if (IS_ERR_VALUE(ret)) {
 		dev_err(smmu->dev, "failed to request context IRQ %d (%u)\n",
@@ -274,7 +274,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
 	}
 
 	root_cfg->smmu = smmu;
-	arm_smmu_init_context_bank(smmu_domain);
+	smmu->hwdep_ops->init_context_bank(smmu_domain);
 	return ret;
 
 out_free_context:
@@ -425,7 +425,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	if (!master)
 		return -ENODEV;
 
-	return arm_smmu_domain_add_master(smmu_domain, master);
+	return device_smmu->hwdep_ops->domain_add_master(smmu_domain, master);
 
 err_unlock:
 	spin_unlock_irqrestore(&smmu_domain->lock, flags);
@@ -436,10 +436,11 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
 {
 	struct arm_smmu_domain *smmu_domain = domain->priv;
 	struct arm_smmu_master *master;
+	struct arm_smmu_device *smmu = smmu_domain->root_cfg.smmu;
 
 	master = find_smmu_master(smmu_domain->leaf_smmu, dev->of_node);
 	if (master)
-		arm_smmu_domain_remove_master(smmu_domain, master);
+		smmu->hwdep_ops->domain_remove_master(smmu_domain, master);
 }
 
 static bool arm_smmu_pte_is_contiguous_range(unsigned long addr,
@@ -694,9 +695,10 @@ static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
 {
 	int ret;
 	struct arm_smmu_domain *smmu_domain = domain->priv;
+	struct arm_smmu_device *smmu = smmu_domain->root_cfg.smmu;
 
 	ret = arm_smmu_handle_mapping(smmu_domain, iova, 0, size, 0);
-	arm_smmu_tlb_inv_context(&smmu_domain->root_cfg);
+	smmu->hwdep_ops->tlb_inv_context(&smmu_domain->root_cfg);
 	return ret ? 0 : size;
 }
 
@@ -823,7 +825,8 @@ static struct iommu_ops arm_smmu_ops = {
 			   PAGE_SIZE),
 };
 
-int arm_smmu_device_dt_probe(struct platform_device *pdev)
+int arm_smmu_device_dt_probe(struct platform_device *pdev,
+			     struct smmu_hwdep_ops *ops)
 {
 	struct resource *res;
 	struct arm_smmu_device *smmu;
@@ -901,7 +904,9 @@ int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	if (dev_node)
 		smmu->parent_of_node = dev_node;
 
-	err = arm_smmu_device_cfg_probe(smmu);
+	smmu->hwdep_ops = ops;
+
+	err = smmu->hwdep_ops->device_cfg_probe(smmu);
 	if (err)
 		goto out_put_parent;
 
@@ -918,7 +923,7 @@ int arm_smmu_device_dt_probe(struct platform_device *pdev)
 
 	for (i = 0; i < smmu->num_global_irqs; ++i) {
 		err = request_irq(smmu->irqs[i],
-				  arm_smmu_global_fault,
+				  smmu->hwdep_ops->global_fault,
 				  IRQF_SHARED,
 				  "arm-smmu global fault",
 				  smmu);
@@ -934,7 +939,10 @@ int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	list_add(&smmu->list, &arm_smmu_devices);
 	spin_unlock(&arm_smmu_devices_lock);
 
-	arm_smmu_device_reset(smmu);
+	err = smmu->hwdep_ops->device_reset(smmu);
+	if (err)
+		goto out_free_irqs;
+
 	return 0;
 
 out_free_irqs:
@@ -991,7 +999,7 @@ int arm_smmu_device_remove(struct platform_device *pdev)
 		free_irq(smmu->irqs[i], smmu);
 
 	/* Turn the thing off */
-	return arm_smmu_device_unload(smmu);
+	return smmu->hwdep_ops->device_remove(smmu);
 }
 
 int __init arm_smmu_ops_init(void)
drivers/iommu/arm-smmu.c +29 -10
index c368e82..d08149d 100644
@@ -240,7 +240,7 @@ static void arm_smmu_tlb_sync(struct arm_smmu_device *smmu)
 	}
 }
 
-void arm_smmu_tlb_inv_context(struct arm_smmu_cfg *cfg)
+static void arm_smmu_tlb_inv_context(struct arm_smmu_cfg *cfg)
 {
 	struct arm_smmu_device *smmu = cfg->smmu;
 	void __iomem *base = ARM_SMMU_GR0(smmu);
@@ -259,7 +259,7 @@ void arm_smmu_tlb_inv_context(struct arm_smmu_cfg *cfg)
 	arm_smmu_tlb_sync(smmu);
 }
 
-irqreturn_t arm_smmu_context_fault(int irq, void *dev)
+static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
 {
 	int flags, ret;
 	u32 fsr, far, fsynr, resume;
@@ -312,7 +312,7 @@ irqreturn_t arm_smmu_context_fault(int irq, void *dev)
 	return ret;
 }
 
-irqreturn_t arm_smmu_global_fault(int irq, void *dev)
+static irqreturn_t arm_smmu_global_fault(int irq, void *dev)
 {
 	u32 gfsr, gfsynr0, gfsynr1, gfsynr2;
 	struct arm_smmu_device *smmu = dev;
@@ -336,7 +336,7 @@ irqreturn_t arm_smmu_global_fault(int irq, void *dev)
 	return IRQ_HANDLED;
 }
 
-void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
+static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
 {
 	u32 reg;
 	bool stage1;
@@ -604,7 +604,7 @@ static void arm_smmu_bypass_stream_mapping(struct arm_smmu_device *smmu,
 	}
 }
 
-int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
+static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 				      struct arm_smmu_master *master)
 {
 	int i, ret;
@@ -642,7 +642,7 @@ int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 	return 0;
 }
 
-void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
+static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
 					  struct arm_smmu_master *master)
 {
 	struct arm_smmu_device *smmu = smmu_domain->root_cfg.smmu;
@@ -655,7 +655,7 @@ void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
 	arm_smmu_master_free_smrs(smmu, master);
 }
 
-void arm_smmu_device_reset(struct arm_smmu_device *smmu)
+static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
 {
 	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
 	void __iomem *cb_base;
@@ -704,6 +704,8 @@ void arm_smmu_device_reset(struct arm_smmu_device *smmu)
 	/* Push the button */
 	arm_smmu_tlb_sync(smmu);
 	writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
+
+	return 0;
 }
 
 static u32 arm_smmu_id_size_to_bits(u32 size)
@@ -725,7 +727,7 @@ static u32 arm_smmu_id_size_to_bits(u32 size)
 	}
 }
 
-int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
+static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 {
 	u32 size;
 	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
@@ -869,13 +871,25 @@ int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 	return 0;
 }
 
-int arm_smmu_device_unload(struct arm_smmu_device *smmu)
+static int arm_smmu_device_unload(struct arm_smmu_device *smmu)
 {
 	writel(sCR0_CLIENTPD, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
 
 	return 0;
 }
 
+static struct smmu_hwdep_ops arm_smmu_hwdep_ops = {
+	.tlb_inv_context	= arm_smmu_tlb_inv_context,
+	.context_fault		= arm_smmu_context_fault,
+	.global_fault		= arm_smmu_global_fault,
+	.init_context_bank	= arm_smmu_init_context_bank,
+	.domain_add_master	= arm_smmu_domain_add_master,
+	.domain_remove_master	= arm_smmu_domain_remove_master,
+	.device_reset		= arm_smmu_device_reset,
+	.device_cfg_probe	= arm_smmu_device_cfg_probe,
+	.device_remove		= arm_smmu_device_unload,
+};
+
 #ifdef CONFIG_OF
 static struct of_device_id arm_smmu_of_match[] = {
 	{ .compatible = "arm,smmu-v1", },
@@ -887,13 +901,18 @@ static struct of_device_id arm_smmu_of_match[] = {
 MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
 #endif
 
+static int arm_smmu_device_probe(struct platform_device *pdev)
+{
+	return arm_smmu_device_dt_probe(pdev, &arm_smmu_hwdep_ops);
+}
+
 static struct platform_driver arm_smmu_driver = {
 	.driver	= {
 		.owner		= THIS_MODULE,
 		.name		= "arm-smmu",
 		.of_match_table	= of_match_ptr(arm_smmu_of_match),
 	},
-	.probe	= arm_smmu_device_dt_probe,
+	.probe	= arm_smmu_device_probe,
 	.remove	= arm_smmu_device_remove,
 };
 
drivers/iommu/arm-smmu.h +32 -12

67 lines not on this page · raw message

--
1.8.0

author
#5 lore raw

PATCH v3 5/13 iommu/arm: Rename member "cbar" in struct arm_smmu_cfg to "type"

In reply to Zhen Lei

Some SMMUs may not contain register CBAR, or the "type" field definition may be different.

---
 drivers/iommu/arm-smmu-base.c |  8 ++++----
 drivers/iommu/arm-smmu.c      |  8 +++++---
 drivers/iommu/arm-smmu.h      | 11 ++++-------
 3 files changed, 13 insertions(+), 14 deletions(-)
drivers/iommu/arm-smmu-base.c +4 -4
index c6824b5..0b02da8 100644
@@ -240,13 +240,13 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
 		 * We will likely want to change this if/when KVM gets
 		 * involved.
 		 */
-		root_cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS;
+		root_cfg->type = TYPE_S1_TRANS_S2_BYPASS;
 		start = smmu->num_s2_context_banks;
 	} else if (smmu->features & ARM_SMMU_FEAT_TRANS_S2) {
-		root_cfg->cbar = CBAR_TYPE_S2_TRANS;
+		root_cfg->type = TYPE_S2_TRANS;
 		start = 0;
 	} else {
-		root_cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS;
+		root_cfg->type = TYPE_S1_TRANS_S2_BYPASS;
 		start = smmu->num_s2_context_banks;
 	}
 
@@ -633,7 +633,7 @@ static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
 	struct arm_smmu_device *smmu = root_cfg->smmu;
 	unsigned long flags;
 
-	if (root_cfg->cbar == CBAR_TYPE_S2_TRANS) {
+	if (root_cfg->type == TYPE_S2_TRANS) {
 		stage = 2;
 		output_mask = (1ULL << smmu->s2_output_size) - 1;
 	} else {
drivers/iommu/arm-smmu.c +5 -3
index d08149d..b9538f6 100644
@@ -153,6 +153,8 @@
 #define CBAR_S1_MEMATTR_SHIFT		12
 #define CBAR_S1_MEMATTR_MASK		0xf
 #define CBAR_S1_MEMATTR_WB		0xf
+#define CBAR_TYPE_SHIFT			16
+#define CBAR_TYPE_MASK			0x3
 #define CBAR_IRPTNDX_SHIFT		24
 #define CBAR_IRPTNDX_MASK		0xff
 
@@ -244,7 +246,7 @@ static void arm_smmu_tlb_inv_context(struct arm_smmu_cfg *cfg)
 {
 	struct arm_smmu_device *smmu = cfg->smmu;
 	void __iomem *base = ARM_SMMU_GR0(smmu);
-	bool stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS;
+	bool stage1 = cfg->type != TYPE_S2_TRANS;
 
 	if (stage1) {
 		base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
@@ -346,11 +348,11 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
 
 	gr0_base = ARM_SMMU_GR0(smmu);
 	gr1_base = ARM_SMMU_GR1(smmu);
-	stage1 = root_cfg->cbar != CBAR_TYPE_S2_TRANS;
+	stage1 = root_cfg->type != TYPE_S2_TRANS;
 	cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, root_cfg->cbndx);
 
 	/* CBAR */
-	reg = root_cfg->cbar;
+	reg = root_cfg->type << CBAR_TYPE_SHIFT;
 	if (smmu->version == 1)
 	      reg |= root_cfg->irptndx << CBAR_IRPTNDX_SHIFT;
 
drivers/iommu/arm-smmu.h +4 -7
index ca35694..d66a8c4 100644
@@ -68,12 +68,9 @@
 
 #define TLB_LOOP_TIMEOUT		1000000	/* 1s! */
 
-#define CBAR_TYPE_SHIFT			16
-#define CBAR_TYPE_MASK			0x3
-#define CBAR_TYPE_S2_TRANS		(0 << CBAR_TYPE_SHIFT)
-#define CBAR_TYPE_S1_TRANS_S2_BYPASS	(1 << CBAR_TYPE_SHIFT)
-#define CBAR_TYPE_S1_TRANS_S2_FAULT	(2 << CBAR_TYPE_SHIFT)
-#define CBAR_TYPE_S1_TRANS_S2_TRANS	(3 << CBAR_TYPE_SHIFT)
+#define TYPE_S2_TRANS			0
+#define TYPE_S1_TRANS_S2_BYPASS		1
+#define TYPE_S1_TRANS_S2_TRANS		3
 
 #define RESUME_RETRY			(0 << 0)
 #define RESUME_TERMINATE		(1 << 0)
@@ -189,7 +186,7 @@ struct arm_smmu_cfg {
 	struct arm_smmu_device		*smmu;
 	u8				cbndx;
 	u8				irptndx;
-	u32				cbar;
+	u32				type;
 	pgd_t				*pgd;
 };
 #define INVALID_IRPTNDX			0xff

--
1.8.0

author
#6 lore raw

PATCH v3 6/13 iommu/arm: Split arm_smmu_destroy_domain_context to reuse code

In reply to Zhen Lei

Free IRQ and context bank is a common operation, should be shared by all SMMUs.

---
 drivers/iommu/arm-smmu-base.c | 20 ++++++++++++++++++++
 drivers/iommu/arm-smmu.c      | 15 ++-------------
 drivers/iommu/arm-smmu.h      |  3 ++-
 3 files changed, 24 insertions(+), 14 deletions(-)
drivers/iommu/arm-smmu-base.c +20 -0
index 0b02da8..4d1c511 100644
@@ -282,6 +282,26 @@ out_free_context:
 	return ret;
 }
 
+static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
+{
+	struct arm_smmu_domain *smmu_domain = domain->priv;
+	struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg;
+	struct arm_smmu_device *smmu = root_cfg->smmu;
+	int irq;
+
+	if (!smmu)
+		return;
+
+	smmu->hwdep_ops->destroy_context_bank(smmu_domain);
+
+	if (root_cfg->irptndx != INVALID_IRPTNDX) {
+		irq = smmu->irqs[smmu->num_global_irqs + root_cfg->irptndx];
+		free_irq(irq, domain);
+	}
+
+	__arm_smmu_free_bitmap(smmu->context_map, root_cfg->cbndx);
+}
+
 static int arm_smmu_domain_init(struct iommu_domain *domain)
 {
 	struct arm_smmu_domain *smmu_domain;
drivers/iommu/arm-smmu.c +2 -13
index b9538f6..d2fc14c 100644
@@ -498,28 +498,16 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
 	writel_relaxed(reg, cb_base + ARM_SMMU_CB_SCTLR);
 }
 
-void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
+static void arm_smmu_destroy_context_bank(struct arm_smmu_domain *smmu_domain)
 {
-	struct arm_smmu_domain *smmu_domain = domain->priv;
 	struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg;
 	struct arm_smmu_device *smmu = root_cfg->smmu;
 	void __iomem *cb_base;
-	int irq;
-
-	if (!smmu)
-		return;
 
 	/* Disable the context bank and nuke the TLB before freeing it. */
 	cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, root_cfg->cbndx);
 	writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR);
 	arm_smmu_tlb_inv_context(root_cfg);
-
-	if (root_cfg->irptndx != INVALID_IRPTNDX) {
-		irq = smmu->irqs[smmu->num_global_irqs + root_cfg->irptndx];
-		free_irq(irq, domain);
-	}
-
-	__arm_smmu_free_bitmap(smmu->context_map, root_cfg->cbndx);
 }
 
 static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu,
@@ -885,6 +873,7 @@ static struct smmu_hwdep_ops arm_smmu_hwdep_ops = {
 	.context_fault		= arm_smmu_context_fault,
 	.global_fault		= arm_smmu_global_fault,
 	.init_context_bank	= arm_smmu_init_context_bank,
+	.destroy_context_bank	= arm_smmu_destroy_context_bank,
 	.domain_add_master	= arm_smmu_domain_add_master,
 	.domain_remove_master	= arm_smmu_domain_remove_master,
 	.device_reset		= arm_smmu_device_reset,
drivers/iommu/arm-smmu.h +2 -1
index d66a8c4..190f77c 100644
@@ -213,6 +213,7 @@ struct arm_smmu_domain {
  * @context_fault: context fault handler
  * @global_fault: global fault handler
  * @init_context_bank: init a context bank
+ * @destroy_context_bank: disable a context bank and invalid the TLBs
  * @domain_add_master: add a master into a domain
  * @domain_remove_master: remove a master from a domain
  * @device_reset: initialize a smmu
@@ -224,6 +225,7 @@ struct smmu_hwdep_ops {
 	irqreturn_t (*context_fault)(int irq, void *dev);
 	irqreturn_t (*global_fault)(int irq, void *dev);
 	void (*init_context_bank)(struct arm_smmu_domain *smmu_domain);
+	void (*destroy_context_bank)(struct arm_smmu_domain *smmu_domain);
 	int (*domain_add_master)(struct arm_smmu_domain *smmu_domain,
 					struct arm_smmu_master *master);
 	void (*domain_remove_master)(struct arm_smmu_domain *smmu_domain,
@@ -242,6 +244,5 @@ extern int arm_smmu_device_dt_probe(struct platform_device *pdev,
 					struct smmu_hwdep_ops *ops);
 extern int arm_smmu_device_remove(struct platform_device *pdev);
 
-extern void arm_smmu_destroy_domain_context(struct iommu_domain *domain);
 extern int __init arm_smmu_ops_init(void);
 #endif

--
1.8.0

author
#7 lore raw

PATCH v3 7/13 iommu/arm: Split arm_smmu_tlb_sync to reuse code

In reply to Zhen Lei

TLB sync wait is a common operation, should be shared by all SMMUs.

---
 drivers/iommu/arm-smmu-base.c | 15 +++++++++++++++
 drivers/iommu/arm-smmu.c      | 23 ++++++++++++-----------
 drivers/iommu/arm-smmu.h      |  3 +++
 3 files changed, 30 insertions(+), 11 deletions(-)
drivers/iommu/arm-smmu-base.c +15 -0
index 4d1c511..53f7907 100644
@@ -187,6 +187,21 @@ void __arm_smmu_free_bitmap(unsigned long *map, int idx)
 	clear_bit(idx, map);
 }
 
+void arm_smmu_tlb_sync_wait(struct arm_smmu_device *smmu)
+{
+	int count = 0;
+
+	while (!smmu->hwdep_ops->tlb_sync_finished(smmu)) {
+		cpu_relax();
+		if (++count == TLB_LOOP_TIMEOUT) {
+			dev_err_ratelimited(smmu->dev,
+			"TLB sync timed out -- SMMU may be deadlocked\n");
+			return;
+		}
+		udelay(1);
+	}
+}
+
 void arm_smmu_flush_pgtable(struct arm_smmu_device *smmu, void *addr,
 				   size_t size)
 {
drivers/iommu/arm-smmu.c +12 -11
index d2fc14c..fa65c7c 100644
@@ -223,23 +223,23 @@
 
 #define FSYNR0_WNR			(1 << 4)
 
+static int arm_smmu_tlb_sync_finished(struct arm_smmu_device *smmu)
+{
+	u32 reg;
+	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+
+	reg = readl_relaxed(gr0_base + ARM_SMMU_GR0_sTLBGSTATUS);
+
+	return !(reg & sTLBGSTATUS_GSACTIVE);
+}
+
 /* Wait for any pending TLB invalidations to complete */
 static void arm_smmu_tlb_sync(struct arm_smmu_device *smmu)
 {
-	int count = 0;
 	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
 
 	writel_relaxed(0, gr0_base + ARM_SMMU_GR0_sTLBGSYNC);
-	while (readl_relaxed(gr0_base + ARM_SMMU_GR0_sTLBGSTATUS)
-	       & sTLBGSTATUS_GSACTIVE) {
-		cpu_relax();
-		if (++count == TLB_LOOP_TIMEOUT) {
-			dev_err_ratelimited(smmu->dev,
-			"TLB sync timed out -- SMMU may be deadlocked\n");
-			return;
-		}
-		udelay(1);
-	}
+	arm_smmu_tlb_sync_wait(smmu);
 }
 
 static void arm_smmu_tlb_inv_context(struct arm_smmu_cfg *cfg)
@@ -869,6 +869,7 @@ static int arm_smmu_device_unload(struct arm_smmu_device *smmu)
 }
 
 static struct smmu_hwdep_ops arm_smmu_hwdep_ops = {
+	.tlb_sync_finished	= arm_smmu_tlb_sync_finished,
 	.tlb_inv_context	= arm_smmu_tlb_inv_context,
 	.context_fault		= arm_smmu_context_fault,
 	.global_fault		= arm_smmu_global_fault,
drivers/iommu/arm-smmu.h +3 -0
index 190f77c..8332ff0 100644
@@ -209,6 +209,7 @@ struct arm_smmu_domain {
 
 /**
  * struct smmu_hwdep_ops - smmu hardware dependent ops
+ * @tlb_sync_finished: check whether tlb sync operation is finished
  * @tlb_inv_context: invalid smmu context bank tlb
  * @context_fault: context fault handler
  * @global_fault: global fault handler
@@ -221,6 +222,7 @@ struct arm_smmu_domain {
  * @device_remove: turn off a smmu and reclaim associated resources
  */
 struct smmu_hwdep_ops {
+	int (*tlb_sync_finished)(struct arm_smmu_device *smmu);
 	void (*tlb_inv_context)(struct arm_smmu_cfg *cfg);
 	irqreturn_t (*context_fault)(int irq, void *dev);
 	irqreturn_t (*global_fault)(int irq, void *dev);
@@ -237,6 +239,7 @@ struct smmu_hwdep_ops {
 
 extern int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end);
 extern void __arm_smmu_free_bitmap(unsigned long *map, int idx);
+extern void arm_smmu_tlb_sync_wait(struct arm_smmu_device *smmu);
 extern struct arm_smmu_device *find_parent_smmu(struct arm_smmu_device *smmu);
 extern void arm_smmu_flush_pgtable(struct arm_smmu_device *smmu, void *addr,
 				   size_t size);

--
1.8.0

author
#8 lore raw

PATCH v3 8/13 iommu/arm: Add hook alloc_context

In reply to Zhen Lei

In some SMMUs(like hisi-smmu and SMMUv3), a given StreamID is bound to a fixed context bank.

---
 drivers/iommu/arm-smmu-base.c | 8 +++++---
 drivers/iommu/arm-smmu.c      | 8 ++++++++
 drivers/iommu/arm-smmu.h      | 3 +++
 3 files changed, 16 insertions(+), 3 deletions(-)
drivers/iommu/arm-smmu-base.c +5 -3
index 53f7907..be73eba 100644
@@ -231,6 +231,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
 	struct arm_smmu_domain *smmu_domain = domain->priv;
 	struct arm_smmu_cfg *root_cfg = &smmu_domain->root_cfg;
 	struct arm_smmu_device *smmu, *parent;
+	struct arm_smmu_master *master;
 
 	/*
 	 * Walk the SMMU chain to find the root device for this chain.
@@ -245,7 +246,8 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
 		smmu_domain->output_mask &= (1ULL << smmu->s2_output_size) - 1;
 	} while ((parent = find_parent_smmu(smmu)));
 
-	if (!find_smmu_master(smmu, dev->of_node)) {
+	master = find_smmu_master(smmu, dev->of_node);
+	if (!master) {
 		dev_err(dev, "unable to find root SMMU for device\n");
 		return -ENODEV;
 	}
@@ -265,8 +267,8 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
 		start = smmu->num_s2_context_banks;
 	}
 
-	ret = __arm_smmu_alloc_bitmap(smmu->context_map, start,
-				      smmu->num_context_banks);
+	ret = smmu->hwdep_ops->alloc_context(smmu, start,
+					smmu->num_context_banks, master);
 	if (IS_ERR_VALUE(ret))
 		return ret;
 
drivers/iommu/arm-smmu.c +8 -0
index fa65c7c..dbd9c60 100644
@@ -223,6 +223,13 @@
 
 #define FSYNR0_WNR			(1 << 4)
 
+
+static int arm_smmu_alloc_context(struct arm_smmu_device *smmu,
+			int start, int end, struct arm_smmu_master *master)
+{
+	return __arm_smmu_alloc_bitmap(smmu->context_map, start, end);
+}
+
 static int arm_smmu_tlb_sync_finished(struct arm_smmu_device *smmu)
 {
 	u32 reg;
@@ -869,6 +876,7 @@ static int arm_smmu_device_unload(struct arm_smmu_device *smmu)
 }
 
 static struct smmu_hwdep_ops arm_smmu_hwdep_ops = {
+	.alloc_context		= arm_smmu_alloc_context,
 	.tlb_sync_finished	= arm_smmu_tlb_sync_finished,
 	.tlb_inv_context	= arm_smmu_tlb_inv_context,
 	.context_fault		= arm_smmu_context_fault,
drivers/iommu/arm-smmu.h +3 -0
index 8332ff0..f7346d89 100644
@@ -209,6 +209,7 @@ struct arm_smmu_domain {
 
 /**
  * struct smmu_hwdep_ops - smmu hardware dependent ops
+ * @alloc_context: alloc a free context bank
  * @tlb_sync_finished: check whether tlb sync operation is finished
  * @tlb_inv_context: invalid smmu context bank tlb
  * @context_fault: context fault handler
@@ -222,6 +223,8 @@ struct arm_smmu_domain {
  * @device_remove: turn off a smmu and reclaim associated resources
  */
 struct smmu_hwdep_ops {
+	int (*alloc_context)(struct arm_smmu_device *smmu,
+			int start, int end, struct arm_smmu_master *master);
 	int (*tlb_sync_finished)(struct arm_smmu_device *smmu);
 	void (*tlb_inv_context)(struct arm_smmu_cfg *cfg);
 	irqreturn_t (*context_fault)(int irq, void *dev);

--
1.8.0

author
#9 lore raw

PATCH v3 9/13 iommu/arm: Change context_map to dynamic memory allocation

In reply to Zhen Lei

In some SMMUs(like hisi-smmu and SMMUv3), the number of context banks is more than 128. Static array size does not fit.

---
 drivers/iommu/arm-smmu-base.c | 9 ++++++++-
 drivers/iommu/arm-smmu.h      | 5 +----
 2 files changed, 9 insertions(+), 5 deletions(-)
drivers/iommu/arm-smmu-base.c +8 -1
index be73eba..400de39 100644
@@ -947,6 +947,13 @@ int arm_smmu_device_dt_probe(struct platform_device *pdev,
 	if (err)
 		goto out_put_parent;
 
+	smmu->context_map = devm_kzalloc(dev,
+			BITS_TO_LONGS(smmu->num_context_banks), GFP_KERNEL);
+	if (!smmu->context_map) {
+		dev_err(dev, "failed to allocate context map\n");
+		return -ENOMEM;
+	}
+
 	parse_driver_options(smmu);
 
 	if (smmu->version > 1 &&
@@ -1029,7 +1036,7 @@ int arm_smmu_device_remove(struct platform_device *pdev)
 		of_node_put(master->of_node);
 	}
 
-	if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS))
+	if (!bitmap_empty(smmu->context_map, smmu->num_context_banks))
 		dev_err(dev, "removing device with active domains!\n");
 
 	for (i = 0; i < smmu->num_global_irqs; ++i)
drivers/iommu/arm-smmu.h +1 -4
index f7346d89..fff2b98 100644
@@ -26,9 +26,6 @@
 /* Maximum number of stream IDs assigned to a single device */
 #define MAX_MASTER_STREAMIDS		MAX_PHANDLE_ARGS
 
-/* Maximum number of context banks per SMMU */
-#define ARM_SMMU_MAX_CBS		128
-
 /* Maximum number of mapping groups per SMMU */
 #define ARM_SMMU_MAX_SMRS		128
 
@@ -164,7 +161,7 @@ struct arm_smmu_device {
 
 	u32				num_context_banks;
 	u32				num_s2_context_banks;
-	DECLARE_BITMAP(context_map, ARM_SMMU_MAX_CBS);
+	unsigned long		       *context_map;
 	atomic_t			irptndx;
 
 	u32				num_mapping_groups;

--
1.8.0

author
#10 lore raw

PATCH v3 10/13 iommu/arm: Adjust code to reuse more

In reply to Zhen Lei

Limit smmu ias,oas,uas size is a common operation, the value of register MAIR0 should be consistent across all SMMUs.

---
 drivers/iommu/arm-smmu-base.c | 20 ++++++++++++++++++++
 drivers/iommu/arm-smmu.c      | 27 ++++-----------------------
 drivers/iommu/arm-smmu.h      |  7 +++++++
 3 files changed, 31 insertions(+), 23 deletions(-)
drivers/iommu/arm-smmu-base.c +20 -0
index 400de39..8013751 100644
@@ -954,6 +954,26 @@ int arm_smmu_device_dt_probe(struct platform_device *pdev,
 		return -ENOMEM;
 	}
 
+	/*
+	 * Stage-1 output limited by stage-2 input size due to pgd
+	 * allocation (PTRS_PER_PGD).
+	 */
+#ifdef CONFIG_64BIT
+	smmu->s1_output_size = min((u32)VA_BITS, smmu->s1_output_size);
+	smmu->input_size = min((u32)VA_BITS, smmu->input_size);
+#else
+	smmu->s1_output_size = min(32U, smmu->s1_output_size);
+	smmu->input_size = 32;
+#endif
+
+	/* The stage-2 output mask is also applied for bypass */
+	smmu->s2_output_size = min((u32)PHYS_MASK_SHIFT, smmu->s2_output_size);
+
+	dev_notice(smmu->dev,
+		   "\t%u-bit VA, %u-bit IPA, %u-bit PA\n",
+		   smmu->input_size,
+		   smmu->s1_output_size, smmu->s2_output_size);
+
 	parse_driver_options(smmu);
 
 	if (smmu->version > 1 &&
drivers/iommu/arm-smmu.c +4 -23
index dbd9c60..6971e11 100644
@@ -489,9 +489,7 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
 
 	/* MAIR0 (stage-1 only) */
 	if (stage1) {
-		reg = (MAIR_ATTR_NC << MAIR_ATTR_SHIFT(MAIR_ATTR_IDX_NC)) |
-		      (MAIR_ATTR_WBRWA << MAIR_ATTR_SHIFT(MAIR_ATTR_IDX_CACHE)) |
-		      (MAIR_ATTR_DEVICE << MAIR_ATTR_SHIFT(MAIR_ATTR_IDX_DEV));
+		reg = MAIR0_STAGE1;
 		writel_relaxed(reg, cb_base + ARM_SMMU_CB_S1_MAIR0);
 	}
 
@@ -828,30 +826,16 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 	id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID2);
 	size = arm_smmu_id_size_to_bits((id >> ID2_IAS_SHIFT) & ID2_IAS_MASK);
 
-	/*
-	 * Stage-1 output limited by stage-2 input size due to pgd
-	 * allocation (PTRS_PER_PGD).
-	 */
-#ifdef CONFIG_64BIT
-	smmu->s1_output_size = min((u32)VA_BITS, size);
-#else
-	smmu->s1_output_size = min(32U, size);
-#endif
+	smmu->s1_output_size = size;
 
-	/* The stage-2 output mask is also applied for bypass */
 	size = arm_smmu_id_size_to_bits((id >> ID2_OAS_SHIFT) & ID2_OAS_MASK);
-	smmu->s2_output_size = min((u32)PHYS_MASK_SHIFT, size);
+	smmu->s2_output_size = size;
 
 	if (smmu->version == 1) {
 		smmu->input_size = 32;
 	} else {
-#ifdef CONFIG_64BIT
 		size = (id >> ID2_UBS_SHIFT) & ID2_UBS_MASK;
-		size = min((u32)VA_BITS, arm_smmu_id_size_to_bits(size));
-#else
-		size = 32;
-#endif
-		smmu->input_size = size;
+		smmu->input_size = arm_smmu_id_size_to_bits(size);
 
 		if ((PAGE_SIZE == SZ_4K && !(id & ID2_PTFS_4K)) ||
 		    (PAGE_SIZE == SZ_64K && !(id & ID2_PTFS_64K)) ||
@@ -862,9 +846,6 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 		}
 	}
 
-	dev_notice(smmu->dev,
-		   "\t%u-bit VA, %u-bit IPA, %u-bit PA\n",
-		   smmu->input_size, smmu->s1_output_size, smmu->s2_output_size);
 	return 0;
 }
 
drivers/iommu/arm-smmu.h +7 -0
index fff2b98..3164ba78 100644
@@ -72,6 +72,7 @@
 #define RESUME_RETRY			(0 << 0)
 #define RESUME_TERMINATE		(1 << 0)
 
+/* In SMMUv2, this register is named SMMU_CBn_TCR */
 #define TTBCR_EAE			(1 << 31)
 
 #define TTBCR_PASIZE_SHIFT		16
@@ -112,6 +113,12 @@
 #define MAIR_ATTR_IDX_CACHE		1
 #define MAIR_ATTR_IDX_DEV		2
 
+#define MAIR0_STAGE1	\
+	((MAIR_ATTR_NC << MAIR_ATTR_SHIFT(MAIR_ATTR_IDX_NC)) |	\
+	(MAIR_ATTR_WBRWA << MAIR_ATTR_SHIFT(MAIR_ATTR_IDX_CACHE)) |	\
+	(MAIR_ATTR_DEVICE << MAIR_ATTR_SHIFT(MAIR_ATTR_IDX_DEV)))
+
+
 struct arm_smmu_smr {
 	u8				idx;
 	u16				mask;

--
1.8.0

author
#11 lore raw

PATCH v3 11/13 iommu/arm: Put off the initialization phase of arm_smmu_ops registration

In reply to Zhen Lei

When more than two SMMU drivers running at the same, should make sure all SMMUs device tree nodes can be probed before arm_smmu_ops registered.

---
 drivers/iommu/arm-smmu-base.c |  3 ++-
 drivers/iommu/arm-smmu.c      | 12 ++----------
 drivers/iommu/arm-smmu.h      |  2 --
 3 files changed, 4 insertions(+), 13 deletions(-)
drivers/iommu/arm-smmu-base.c +2 -1
index 8013751..d71af43 100644
@@ -1066,7 +1066,7 @@ int arm_smmu_device_remove(struct platform_device *pdev)
 	return smmu->hwdep_ops->device_remove(smmu);
 }
 
-int __init arm_smmu_ops_init(void)
+static int __init arm_smmu_ops_init(void)
 {
 	/* Oh, for a proper bus abstraction */
 	if (!iommu_present(&platform_bus_type))
@@ -1079,3 +1079,4 @@ int __init arm_smmu_ops_init(void)
 
 	return 0;
 }
+subsys_initcall_sync(arm_smmu_ops_init);
drivers/iommu/arm-smmu.c +2 -10
index 6971e11..868e9ac 100644
@@ -728,7 +728,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
 	u32 id;
 
-	dev_notice(smmu->dev, "probing hardware configuration...\n");
+	dev_notice(smmu->dev, "probing arm-smmu hardware configuration...\n");
 
 	/* Primecell ID */
 	id = readl_relaxed(gr0_base + ARM_SMMU_GR0_PIDR2);
@@ -899,15 +899,7 @@ static struct platform_driver arm_smmu_driver = {
 
 static int __init arm_smmu_init(void)
 {
-	int ret;
-
-	ret = platform_driver_register(&arm_smmu_driver);
-	if (ret)
-		return ret;
-
-	arm_smmu_ops_init();
-
-	return 0;
+	return platform_driver_register(&arm_smmu_driver);
 }
 
 static void __exit arm_smmu_exit(void)
drivers/iommu/arm-smmu.h +0 -2
index 3164ba78..9847a33 100644
@@ -253,6 +253,4 @@ extern void arm_smmu_flush_pgtable(struct arm_smmu_device *smmu, void *addr,
 extern int arm_smmu_device_dt_probe(struct platform_device *pdev,
 					struct smmu_hwdep_ops *ops);
 extern int arm_smmu_device_remove(struct platform_device *pdev);
-
-extern int __init arm_smmu_ops_init(void);
 #endif

--
1.8.0

author
#12 lore raw

PATCH v3 12/13 iommu/hisilicon: Add support for Hisilicon Ltd. System MMU architecture

In reply to Zhen Lei

Some Hisilicon smmu features are list below:
1. StreamID is 16 bits, highest 8 bits is VMID, lowest 8 bits is ASID. StreamID match is not support, so direct use VMID and ASID to index context bank. First use VMID to index stage2 context bank, then use ASID to index stage1 context bank. In fact, max 256 stage2 context banks, each stage2 context bank relate to 256 stage1 context banks.

|-----------------|            |-----------------|
|stage2 CB VMID0  |----------->|stage1 CB ASID0  |
|-----------------|            |-----------------|
|   ......        |            |   ......        |
|-----------------|            |-----------------|
|stage2 CB VMID255|-----|      |stage2 CB ASID255|
|-----------------|     |      |-----------------|
                        |
                        |
                        |
                        |----->|-----------------|
                               |stage1 CB ASID0  |
                               |-----------------|
                               |   ......        |
                               |-----------------|
                               |stage2 CB ASID255|
                               |-----------------|

2. The base address of stage2 context bank is stored in SMMU_CFG_S2CTBAR, and the base address of stage1 context bank is stored in S2_S1CTBAR(locate in stage2 context bank).

3. All context bank fault share 8 groups of context fault registers. That is, max record 8 context faults. Fault syndrome register recorded StreamID to help software determine which context bank issue fault.

---
 drivers/iommu/Kconfig     |  10 +
 drivers/iommu/Makefile    |   1 +
 drivers/iommu/arm-smmu.h  |   2 +
 drivers/iommu/hisi-smmu.c | 575 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 588 insertions(+)
 create mode 100644 drivers/iommu/hisi-smmu.c
drivers/iommu/Kconfig +10 -0
index fad5e38..716b0ab 100644
@@ -309,4 +309,14 @@ config ARM_SMMU
 	  Say Y here if your SoC includes an IOMMU device implementing
 	  the ARM SMMU architecture.
 
+config HISI_SMMU
+	bool "Hisilicon Ltd. System MMU (SMMU) Support"
+	depends on ARM64 || (ARM_LPAE && OF)
+	select IOMMU_API
+	select ARM_SMMU_BASE
+	select ARM_DMA_USE_IOMMU if ARM
+	help
+	  Say Y here if your SoC includes an IOMMU device implementing
+	  the Hisilicon SMMU architecture.
+
 endif # IOMMU_SUPPORT
drivers/iommu/Makefile +1 -0
index 717cfa3..ef932f2 100644
@@ -6,6 +6,7 @@ obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
 obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
 obj-$(CONFIG_ARM_SMMU_BASE) += arm-smmu-base.o
 obj-$(CONFIG_ARM_SMMU) += arm-smmu.o
+obj-$(CONFIG_HISI_SMMU) += hisi-smmu.o
 obj-$(CONFIG_DMAR_TABLE) += dmar.o
 obj-$(CONFIG_INTEL_IOMMU) += iova.o intel-iommu.o
 obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o
drivers/iommu/arm-smmu.h +2 -0
index 9847a33..b88b5de 100644
@@ -151,6 +151,8 @@ struct arm_smmu_device {
 
 	struct smmu_hwdep_ops		*hwdep_ops;
 
+	void __iomem			*s1cbt;
+	void __iomem			*s2cbt;
 	void __iomem			*base;
 	u32				size;
 	u32				pagesize;
drivers/iommu/hisi-smmu.c new file +575 -0

575 lines not on this page · raw message

--
1.8.0

author
#13 lore raw

PATCH v3 13/13 documentation/iommu: Add description of Hisilicon SMMU private binding

In reply to Zhen Lei

This patch adds a description of private properties for the Hisilicon System MMU architecture.

---
 Documentation/devicetree/bindings/iommu/arm,smmu.txt | 2 ++
 drivers/iommu/arm-smmu-base.c                        | 3 +++
 2 files changed, 5 insertions(+)
Documentation/devicetree/bindings/iommu/arm,smmu.txt +2 -0
index f284b99..23035ce 100644
@@ -1,4 +1,5 @@
 * ARM System MMU Architecture Implementation
+* Hisilicon System MMU Architecture Implementation
 
 ARM SoCs may contain an implementation of the ARM System Memory
 Management Unit Architecture, which can be used to provide 1 or 2 stages
@@ -15,6 +16,7 @@ conditions.
                         "arm,smmu-v2"
                         "arm,mmu-400"
                         "arm,mmu-500"
+                        "hisilicon,smmu-v1"
 
                   depending on the particular implementation and/or the
                   version of the architecture implemented.
drivers/iommu/arm-smmu-base.c +3 -0
index d71af43..a0d7c6f 100644
@@ -22,6 +22,9 @@
  *	- 4k and 64k pages, with contiguous pte hints.
  *	- Up to 42-bit addressing (dependent on VA_BITS)
  *	- Context fault reporting
+ *
+ * Additional supports:
+ *	- Hisilicon smmu-v1 implementation
  */
 
 #define pr_fmt(fmt) "arm-smmu: " fmt

--
1.8.0

A message that is not in the archive (sent off-list or not yet received): [email protected]

#15 lore raw

v3 cover Add support for Hisilicon SMMU architecture

[re-adding the lists]

Quoted 1 line · leizhen

Hi, Will

Hello,

Quoted 4 lines

I have sent this patch a week ago. I saw that you and Mitchel Humpherys had sent some patches which will impact my code. I list below:
iommu/arm-smmu: remove support for chained SMMUs iommu/arm-smmu: fix some checkpatch issues

There's also all of the development work I'm doing for VFIO and the page-table rework that Varun's working on, so these patches are going to be difficult to merge if we ever get to that point. I think Mitchel and Olav are also busy trying to get their SMMU supported by the driver. Given that they've actually bothered to follow the architecture, I don't see why they should be messed about by your patches causing conflicts all over the place.

Quoted 5 lines

I can ajust my patch because of above. I known this patch is not follow what you suggested before(fit into arm-smmu.c). But I found when we need to support ARM SMMUv3, maybe we should do like this. I really want to know whether or not you agree the software framework in this patch? Or what do you think about SMMUv3?

The only code I foresee sharing between SMMUv2 and SMMUv3 is the page-table creation code. The two architectures are significantly different, so I don't think your split really helps us there. For example, you've left the probing code in smmu-base.c. Of course, if HiSilicon follow the SMMUv3 spec as well they did with SMMUv2, then your point is moot anyway and we may as well go home.

Quoted 4 lines
Quoted 4 lines

I tried to merge hisi-smmu driver into arm-smmu.c, but it looks impossible. The biggest problem is that too many registers are diffrent: the base address, the field definition, or present only on one side. And if I use #if, hisi-smmu and arm-smmu can not coexist in one binary file. Almost need 20 #if.

I don't think #ifdefs are necessarily the right way to go. There are IMPDEF parts of a compliant SMMU (e.g. power management), so having a new compatible string and a set of internal ops which can be overridden by a particular implementation wouldn't be the end of the world and would be far less invasive.

The problem you have is that your SMMU isn't architecturally compliant, so you'll end up having to restructure parts of the driver so you can add internal hooks for operations that aren't actually IMPDEF. That's the part I'm worried about and we will have to draw the line somewhere, especially when other people are trying to work with this code for compliant implementations.

So, NAK to your current approach. You need to try something like I said above and, if you want to spit the file up, start with the page-table code.

Will

author
#16 lore raw

v3 cover Add support for Hisilicon SMMU architecture

Quoted 50 lines · Will Deacon

[re-adding the lists]

Quoted 1 line · leizhen

Hi, Will

Hello,

Quoted 4 lines

I have sent this patch a week ago. I saw that you and Mitchel Humpherys had sent some patches which will impact my code. I list below:
iommu/arm-smmu: remove support for chained SMMUs iommu/arm-smmu: fix some checkpatch issues

There's also all of the development work I'm doing for VFIO and the page-table rework that Varun's working on, so these patches are going to be difficult to merge if we ever get to that point. I think Mitchel and Olav are also busy trying to get their SMMU supported by the driver. Given that they've actually bothered to follow the architecture, I don't see why they should be messed about by your patches causing conflicts all over the place.

Quoted 5 lines

I can ajust my patch because of above. I known this patch is not follow what you suggested before(fit into arm-smmu.c). But I found when we need to support ARM SMMUv3, maybe we should do like this. I really want to know whether or not you agree the software framework in this patch? Or what do you think about SMMUv3?

The only code I foresee sharing between SMMUv2 and SMMUv3 is the page-table creation code. The two architectures are significantly different, so I don't think your split really helps us there. For example, you've left the probing code in smmu-base.c. Of course, if HiSilicon follow the SMMUv3 spec as well they did with SMMUv2, then your point is moot anyway and we may as well go home.

Quoted 4 lines
Quoted 4 lines

I tried to merge hisi-smmu driver into arm-smmu.c, but it looks impossible. The biggest problem is that too many registers are diffrent: the base address, the field definition, or present only on one side. And if I use #if, hisi-smmu and arm-smmu can not coexist in one binary file. Almost need 20 #if.

I don't think #ifdefs are necessarily the right way to go. There are IMPDEF parts of a compliant SMMU (e.g. power management), so having a new compatible string and a set of internal ops which can be overridden by a particular implementation wouldn't be the end of the world and would be far less invasive.

The problem you have is that your SMMU isn't architecturally compliant, so you'll end up having to restructure parts of the driver so you can add internal hooks for operations that aren't actually IMPDEF. That's the part I'm worried about and we will have to draw the line somewhere, especially when other people are trying to work with this code for compliant implementations.

Quoted 7 lines

So, NAK to your current approach. You need to try something like I said above and, if you want to spit the file up, start with the page-table code.

Will

.

OK. We have decided to discard current SMMU hardware design, and will completely follow the SMMUv3 specification.