mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-10-31 08:44:41 +00:00 
			
		
		
		
	Merge branches 'x86/amd', 'x86/vt-d', 'arm/exynos', 'arm/mediatek', 'arm/msm', 'arm/rockchip', 'arm/smmu' and 'core' into next
This commit is contained in:
		
							parent
							
								
									523d939ef9
								
							
								
									ffec219770
								
							
								
									5c365d18a7
								
							
								
									6ae5343c26
								
							
								
									131bc8ebb4
								
							
								
									1cb13f7832
								
							
								
									a93db2f22b
								
							
								
									c3928e7515
								
							
								
									feccf398db
								
							
						
					
					
						commit
						f360d3241f
					
				
					 28 changed files with 2330 additions and 1673 deletions
				
			
		|  | @ -1,6 +1,6 @@ | |||
| * ARM SMMUv3 Architecture Implementation | ||||
| 
 | ||||
| The SMMUv3 architecture is a significant deparature from previous | ||||
| The SMMUv3 architecture is a significant departure from previous | ||||
| revisions, replacing the MMIO register interface with in-memory command | ||||
| and event queues and adding support for the ATS and PRI components of | ||||
| the PCIe specification. | ||||
|  |  | |||
|  | @ -1,7 +1,9 @@ | |||
| * Mediatek IOMMU Architecture Implementation | ||||
| 
 | ||||
|   Some Mediatek SOCs contain a Multimedia Memory Management Unit (M4U) which | ||||
| uses the ARM Short-Descriptor translation table format for address translation. | ||||
|   Some Mediatek SOCs contain a Multimedia Memory Management Unit (M4U), and | ||||
| this M4U have two generations of HW architecture. Generation one uses flat | ||||
| pagetable, and only supports 4K size page mapping. Generation two uses the | ||||
| ARM Short-Descriptor translation table format for address translation. | ||||
| 
 | ||||
|   About the M4U Hardware Block Diagram, please check below: | ||||
| 
 | ||||
|  | @ -36,7 +38,9 @@ in each larb. Take a example, There are many ports like MC, PP, VLD in the | |||
| video decode local arbiter, all these ports are according to the video HW. | ||||
| 
 | ||||
| Required properties: | ||||
| - compatible : must be "mediatek,mt8173-m4u". | ||||
| - compatible : must be one of the following string: | ||||
| 	"mediatek,mt2701-m4u" for mt2701 which uses generation one m4u HW. | ||||
| 	"mediatek,mt8173-m4u" for mt8173 which uses generation two m4u HW. | ||||
| - reg : m4u register base and size. | ||||
| - interrupts : the interrupt of m4u. | ||||
| - clocks : must contain one entry for each clock-names. | ||||
|  | @ -46,7 +50,8 @@ Required properties: | |||
| 	according to the local arbiter index, like larb0, larb1, larb2... | ||||
| - iommu-cells : must be 1. This is the mtk_m4u_id according to the HW. | ||||
| 	Specifies the mtk_m4u_id as defined in | ||||
| 	dt-binding/memory/mt8173-larb-port.h. | ||||
| 	dt-binding/memory/mt2701-larb-port.h for mt2701 and | ||||
| 	dt-binding/memory/mt8173-larb-port.h for mt8173 | ||||
| 
 | ||||
| Example: | ||||
| 	iommu: iommu@10205000 { | ||||
|  |  | |||
							
								
								
									
										64
									
								
								Documentation/devicetree/bindings/iommu/msm,iommu-v0.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								Documentation/devicetree/bindings/iommu/msm,iommu-v0.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,64 @@ | |||
| * QCOM IOMMU | ||||
| 
 | ||||
| The MSM IOMMU is an implementation compatible with the ARM VMSA short | ||||
| descriptor page tables. It provides address translation for bus masters outside | ||||
| of the CPU, each connected to the IOMMU through a port called micro-TLB. | ||||
| 
 | ||||
| Required Properties: | ||||
| 
 | ||||
|   - compatible: Must contain "qcom,apq8064-iommu". | ||||
|   - reg: Base address and size of the IOMMU registers. | ||||
|   - interrupts: Specifiers for the MMU fault interrupts. For instances that | ||||
|     support secure mode two interrupts must be specified, for non-secure and | ||||
|     secure mode, in that order. For instances that don't support secure mode a | ||||
|     single interrupt must be specified. | ||||
|   - #iommu-cells: The number of cells needed to specify the stream id. This | ||||
| 		  is always 1. | ||||
|   - qcom,ncb:	  The total number of context banks in the IOMMU. | ||||
|   - clocks	: List of clocks to be used during SMMU register access. See | ||||
| 		  Documentation/devicetree/bindings/clock/clock-bindings.txt | ||||
| 		  for information about the format. For each clock specified | ||||
| 		  here, there must be a corresponding entry in clock-names | ||||
| 		  (see below). | ||||
| 
 | ||||
|   - clock-names	: List of clock names corresponding to the clocks specified in | ||||
| 		  the "clocks" property (above). | ||||
| 		  Should be "smmu_pclk" for specifying the interface clock | ||||
| 		  required for iommu's register accesses. | ||||
| 		  Should be "smmu_clk" for specifying the functional clock | ||||
| 		  required by iommu for bus accesses. | ||||
| 
 | ||||
| Each bus master connected to an IOMMU must reference the IOMMU in its device | ||||
| node with the following property: | ||||
| 
 | ||||
|   - iommus: A reference to the IOMMU in multiple cells. The first cell is a | ||||
| 	    phandle to the IOMMU and the second cell is the stream id. | ||||
| 	    A single master device can be connected to more than one iommu | ||||
| 	    and multiple contexts in each of the iommu. So multiple entries | ||||
| 	    are required to list all the iommus and the stream ids that the | ||||
| 	    master is connected to. | ||||
| 
 | ||||
| Example: mdp iommu and its bus master | ||||
| 
 | ||||
|                 mdp_port0: iommu@7500000 { | ||||
| 			compatible = "qcom,apq8064-iommu"; | ||||
| 			#iommu-cells = <1>; | ||||
| 			clock-names = | ||||
| 			    "smmu_pclk", | ||||
| 			    "smmu_clk"; | ||||
| 			clocks = | ||||
| 			    <&mmcc SMMU_AHB_CLK>, | ||||
| 			    <&mmcc MDP_AXI_CLK>; | ||||
| 			reg = <0x07500000 0x100000>; | ||||
| 			interrupts = | ||||
| 			    <GIC_SPI 63 0>, | ||||
| 			    <GIC_SPI 64 0>; | ||||
| 			qcom,ncb = <2>; | ||||
| 		}; | ||||
| 
 | ||||
| 		mdp: qcom,mdp@5100000 { | ||||
| 			compatible = "qcom,mdp"; | ||||
| 			... | ||||
| 			iommus = <&mdp_port0 0 | ||||
| 				  &mdp_port0 2>; | ||||
| 		}; | ||||
|  | @ -2,16 +2,31 @@ SMI (Smart Multimedia Interface) Common | |||
| 
 | ||||
| The hardware block diagram please check bindings/iommu/mediatek,iommu.txt | ||||
| 
 | ||||
| Mediatek SMI have two generations of HW architecture, mt8173 uses the second | ||||
| generation of SMI HW while mt2701 uses the first generation HW of SMI. | ||||
| 
 | ||||
| There's slight differences between the two SMI, for generation 2, the | ||||
| register which control the iommu port is at each larb's register base. But | ||||
| for generation 1, the register is at smi ao base(smi always on register | ||||
| base). Besides that, the smi async clock should be prepared and enabled for | ||||
| SMI generation 1 to transform the smi clock into emi clock domain, but that is | ||||
| not needed for SMI generation 2. | ||||
| 
 | ||||
| Required properties: | ||||
| - compatible : must be "mediatek,mt8173-smi-common" | ||||
| - compatible : must be one of : | ||||
| 	"mediatek,mt2701-smi-common" | ||||
| 	"mediatek,mt8173-smi-common" | ||||
| - reg : the register and size of the SMI block. | ||||
| - power-domains : a phandle to the power domain of this local arbiter. | ||||
| - clocks : Must contain an entry for each entry in clock-names. | ||||
| - clock-names : must contain 2 entries, as follows: | ||||
| - clock-names : must contain 3 entries for generation 1 smi HW and 2 entries | ||||
|   for generation 2 smi HW as follows: | ||||
|   - "apb" : Advanced Peripheral Bus clock, It's the clock for setting | ||||
| 	    the register. | ||||
|   - "smi" : It's the clock for transfer data and command. | ||||
| 	    They may be the same if both source clocks are the same. | ||||
|   - "async" : asynchronous clock, it help transform the smi clock into the emi | ||||
| 	      clock domain, this clock is only needed by generation 1 smi HW. | ||||
| 
 | ||||
| Example: | ||||
| 	smi_common: smi@14022000 { | ||||
|  |  | |||
|  | @ -3,7 +3,9 @@ SMI (Smart Multimedia Interface) Local Arbiter | |||
| The hardware block diagram please check bindings/iommu/mediatek,iommu.txt | ||||
| 
 | ||||
| Required properties: | ||||
| - compatible : must be "mediatek,mt8173-smi-larb" | ||||
| - compatible : must be one of : | ||||
| 		"mediatek,mt8173-smi-larb" | ||||
| 		"mediatek,mt2701-smi-larb" | ||||
| - reg : the register and size of this local arbiter. | ||||
| - mediatek,smi : a phandle to the smi_common node. | ||||
| - power-domains : a phandle to the power domain of this local arbiter. | ||||
|  |  | |||
|  | @ -6144,6 +6144,7 @@ M:	Joerg Roedel <joro@8bytes.org> | |||
| L:	iommu@lists.linux-foundation.org | ||||
| T:	git git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git | ||||
| S:	Maintained | ||||
| F:	Documentation/devicetree/bindings/iommu/ | ||||
| F:	drivers/iommu/ | ||||
| 
 | ||||
| IP MASQUERADING | ||||
|  |  | |||
|  | @ -89,8 +89,8 @@ config MSM_IOMMU | |||
| 	bool "MSM IOMMU Support" | ||||
| 	depends on ARM | ||||
| 	depends on ARCH_MSM8X60 || ARCH_MSM8960 || COMPILE_TEST | ||||
| 	depends on BROKEN | ||||
| 	select IOMMU_API | ||||
| 	select IOMMU_IO_PGTABLE_ARMV7S | ||||
| 	help | ||||
| 	  Support for the IOMMUs found on certain Qualcomm SOCs. | ||||
| 	  These IOMMUs allow virtualization of the address space used by most | ||||
|  | @ -111,6 +111,7 @@ config AMD_IOMMU | |||
| 	select PCI_PRI | ||||
| 	select PCI_PASID | ||||
| 	select IOMMU_API | ||||
| 	select IOMMU_IOVA | ||||
| 	depends on X86_64 && PCI && ACPI | ||||
| 	---help--- | ||||
| 	  With this option you can enable support for AMD IOMMU hardware in | ||||
|  | @ -343,4 +344,22 @@ config MTK_IOMMU | |||
| 
 | ||||
| 	  If unsure, say N here. | ||||
| 
 | ||||
| config MTK_IOMMU_V1 | ||||
| 	bool "MTK IOMMU Version 1 (M4U gen1) Support" | ||||
| 	depends on ARM | ||||
| 	depends on ARCH_MEDIATEK || COMPILE_TEST | ||||
| 	select ARM_DMA_USE_IOMMU | ||||
| 	select IOMMU_API | ||||
| 	select MEMORY | ||||
| 	select MTK_SMI | ||||
| 	select COMMON_CLK_MT2701_MMSYS | ||||
| 	select COMMON_CLK_MT2701_IMGSYS | ||||
| 	select COMMON_CLK_MT2701_VDECSYS | ||||
| 	help | ||||
| 	  Support for the M4U on certain Mediatek SoCs. M4U generation 1 HW is | ||||
| 	  Multimedia Memory Managememt Unit. This option enables remapping of | ||||
| 	  DMA memory accesses for the multimedia subsystem. | ||||
| 
 | ||||
| 	  if unsure, say N here. | ||||
| 
 | ||||
| endif # IOMMU_SUPPORT | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ obj-$(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) += io-pgtable-arm-v7s.o | |||
| obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o | ||||
| obj-$(CONFIG_IOMMU_IOVA) += iova.o | ||||
| obj-$(CONFIG_OF_IOMMU)	+= of_iommu.o | ||||
| obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o | ||||
| obj-$(CONFIG_MSM_IOMMU) += msm_iommu.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) += arm-smmu.o | ||||
|  | @ -18,6 +18,7 @@ obj-$(CONFIG_INTEL_IOMMU_SVM) += intel-svm.o | |||
| obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o | ||||
| obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o | ||||
| obj-$(CONFIG_MTK_IOMMU) += mtk_iommu.o | ||||
| obj-$(CONFIG_MTK_IOMMU_V1) += mtk_iommu_v1.o | ||||
| obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o | ||||
| obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o | ||||
| obj-$(CONFIG_ROCKCHIP_IOMMU) += rockchip-iommu.o | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -421,7 +421,6 @@ struct protection_domain { | |||
| 	bool updated;		/* complete domain flush required */ | ||||
| 	unsigned dev_cnt;	/* devices assigned to this domain */ | ||||
| 	unsigned dev_iommu[MAX_IOMMUS]; /* per-IOMMU reference count */ | ||||
| 	void *priv;             /* private data */ | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  |  | |||
|  | @ -961,7 +961,7 @@ static int __init amd_iommu_v2_init(void) | |||
| 	spin_lock_init(&state_lock); | ||||
| 
 | ||||
| 	ret = -ENOMEM; | ||||
| 	iommu_wq = create_workqueue("amd_iommu_v2"); | ||||
| 	iommu_wq = alloc_workqueue("amd_iommu_v2", WQ_MEM_RECLAIM, 0); | ||||
| 	if (iommu_wq == NULL) | ||||
| 		goto out; | ||||
| 
 | ||||
|  |  | |||
|  | @ -2687,6 +2687,8 @@ static int __init arm_smmu_init(void) | |||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	pci_request_acs(); | ||||
| 
 | ||||
| 	return bus_set_iommu(&pci_bus_type, &arm_smmu_ops); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -987,8 +987,8 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, | |||
| 	 * handler seeing a half-initialised domain state. | ||||
| 	 */ | ||||
| 	irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx]; | ||||
| 	ret = request_irq(irq, arm_smmu_context_fault, IRQF_SHARED, | ||||
| 			  "arm-smmu-context-fault", domain); | ||||
| 	ret = devm_request_irq(smmu->dev, irq, arm_smmu_context_fault, | ||||
| 			       IRQF_SHARED, "arm-smmu-context-fault", domain); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(smmu->dev, "failed to request context IRQ %d (%u)\n", | ||||
| 			cfg->irptndx, irq); | ||||
|  | @ -1028,7 +1028,7 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain) | |||
| 
 | ||||
| 	if (cfg->irptndx != INVALID_IRPTNDX) { | ||||
| 		irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx]; | ||||
| 		free_irq(irq, domain); | ||||
| 		devm_free_irq(smmu->dev, irq, domain); | ||||
| 	} | ||||
| 
 | ||||
| 	free_io_pgtable_ops(smmu_domain->pgtbl_ops); | ||||
|  | @ -1986,7 +1986,7 @@ static 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], | ||||
| 		err = devm_request_irq(smmu->dev, smmu->irqs[i], | ||||
| 				       arm_smmu_global_fault, | ||||
| 				       IRQF_SHARED, | ||||
| 				       "arm-smmu global fault", | ||||
|  | @ -1994,7 +1994,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) | |||
| 		if (err) { | ||||
| 			dev_err(dev, "failed to request global IRQ %d (%u)\n", | ||||
| 				i, smmu->irqs[i]); | ||||
| 			goto out_free_irqs; | ||||
| 			goto out_put_masters; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -2006,10 +2006,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) | |||
| 	arm_smmu_device_reset(smmu); | ||||
| 	return 0; | ||||
| 
 | ||||
| out_free_irqs: | ||||
| 	while (i--) | ||||
| 		free_irq(smmu->irqs[i], smmu); | ||||
| 
 | ||||
| out_put_masters: | ||||
| 	for (node = rb_first(&smmu->masters); node; node = rb_next(node)) { | ||||
| 		struct arm_smmu_master *master | ||||
|  | @ -2050,7 +2046,7 @@ static int arm_smmu_device_remove(struct platform_device *pdev) | |||
| 		dev_err(dev, "removing device with active domains!\n"); | ||||
| 
 | ||||
| 	for (i = 0; i < smmu->num_global_irqs; ++i) | ||||
| 		free_irq(smmu->irqs[i], smmu); | ||||
| 		devm_free_irq(smmu->dev, smmu->irqs[i], smmu); | ||||
| 
 | ||||
| 	/* Turn the thing off */ | ||||
| 	writel(sCR0_CLIENTPD, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0); | ||||
|  | @ -2096,8 +2092,10 @@ static int __init arm_smmu_init(void) | |||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_PCI | ||||
| 	if (!iommu_present(&pci_bus_type)) | ||||
| 	if (!iommu_present(&pci_bus_type)) { | ||||
| 		pci_request_acs(); | ||||
| 		bus_set_iommu(&pci_bus_type, &arm_smmu_ops); | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| 	return 0; | ||||
|  |  | |||
|  | @ -241,8 +241,20 @@ int dmar_insert_dev_scope(struct dmar_pci_notify_info *info, | |||
| 		if (!dmar_match_pci_path(info, scope->bus, path, level)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if ((scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT) ^ | ||||
| 		    (info->dev->hdr_type == PCI_HEADER_TYPE_NORMAL)) { | ||||
| 		/*
 | ||||
| 		 * We expect devices with endpoint scope to have normal PCI | ||||
| 		 * headers, and devices with bridge scope to have bridge PCI | ||||
| 		 * headers.  However PCI NTB devices may be listed in the | ||||
| 		 * DMAR table with bridge scope, even though they have a | ||||
| 		 * normal PCI header.  NTB devices are identified by class | ||||
| 		 * "BRIDGE_OTHER" (0680h) - we don't declare a socpe mismatch | ||||
| 		 * for this special case. | ||||
| 		 */ | ||||
| 		if ((scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT && | ||||
| 		     info->dev->hdr_type != PCI_HEADER_TYPE_NORMAL) || | ||||
| 		    (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE && | ||||
| 		     (info->dev->hdr_type == PCI_HEADER_TYPE_NORMAL && | ||||
| 		      info->dev->class >> 8 != PCI_CLASS_BRIDGE_OTHER))) { | ||||
| 			pr_warn("Device scope type does not match for %s\n", | ||||
| 				pci_name(info->dev)); | ||||
| 			return -EINVAL; | ||||
|  | @ -1155,8 +1167,6 @@ static int qi_check_fault(struct intel_iommu *iommu, int index) | |||
| 				(unsigned long long)qi->desc[index].high); | ||||
| 			memcpy(&qi->desc[index], &qi->desc[wait_index], | ||||
| 					sizeof(struct qi_desc)); | ||||
| 			__iommu_flush_cache(iommu, &qi->desc[index], | ||||
| 					sizeof(struct qi_desc)); | ||||
| 			writel(DMA_FSTS_IQE, iommu->reg + DMAR_FSTS_REG); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
|  | @ -1231,9 +1241,6 @@ restart: | |||
| 
 | ||||
| 	hw[wait_index] = wait_desc; | ||||
| 
 | ||||
| 	__iommu_flush_cache(iommu, &hw[index], sizeof(struct qi_desc)); | ||||
| 	__iommu_flush_cache(iommu, &hw[wait_index], sizeof(struct qi_desc)); | ||||
| 
 | ||||
| 	qi->free_head = (qi->free_head + 2) % QI_LENGTH; | ||||
| 	qi->free_cnt -= 2; | ||||
| 
 | ||||
|  |  | |||
|  | @ -54,6 +54,10 @@ typedef u32 sysmmu_pte_t; | |||
| #define lv2ent_small(pent) ((*(pent) & 2) == 2) | ||||
| #define lv2ent_large(pent) ((*(pent) & 3) == 1) | ||||
| 
 | ||||
| #ifdef CONFIG_BIG_ENDIAN | ||||
| #warning "revisit driver if we can enable big-endian ptes" | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * v1.x - v3.x SYSMMU supports 32bit physical and 32bit virtual address spaces | ||||
|  * v5.0 introduced support for 36bit physical address space by shifting | ||||
|  | @ -322,14 +326,27 @@ static void __sysmmu_set_ptbase(struct sysmmu_drvdata *data, phys_addr_t pgd) | |||
| 	__sysmmu_tlb_invalidate(data); | ||||
| } | ||||
| 
 | ||||
| static void __sysmmu_enable_clocks(struct sysmmu_drvdata *data) | ||||
| { | ||||
| 	BUG_ON(clk_prepare_enable(data->clk_master)); | ||||
| 	BUG_ON(clk_prepare_enable(data->clk)); | ||||
| 	BUG_ON(clk_prepare_enable(data->pclk)); | ||||
| 	BUG_ON(clk_prepare_enable(data->aclk)); | ||||
| } | ||||
| 
 | ||||
| static void __sysmmu_disable_clocks(struct sysmmu_drvdata *data) | ||||
| { | ||||
| 	clk_disable_unprepare(data->aclk); | ||||
| 	clk_disable_unprepare(data->pclk); | ||||
| 	clk_disable_unprepare(data->clk); | ||||
| 	clk_disable_unprepare(data->clk_master); | ||||
| } | ||||
| 
 | ||||
| static void __sysmmu_get_version(struct sysmmu_drvdata *data) | ||||
| { | ||||
| 	u32 ver; | ||||
| 
 | ||||
| 	clk_enable(data->clk_master); | ||||
| 	clk_enable(data->clk); | ||||
| 	clk_enable(data->pclk); | ||||
| 	clk_enable(data->aclk); | ||||
| 	__sysmmu_enable_clocks(data); | ||||
| 
 | ||||
| 	ver = readl(data->sfrbase + REG_MMU_VERSION); | ||||
| 
 | ||||
|  | @ -342,10 +359,7 @@ static void __sysmmu_get_version(struct sysmmu_drvdata *data) | |||
| 	dev_dbg(data->sysmmu, "hardware version: %d.%d\n", | ||||
| 		MMU_MAJ_VER(data->version), MMU_MIN_VER(data->version)); | ||||
| 
 | ||||
| 	clk_disable(data->aclk); | ||||
| 	clk_disable(data->pclk); | ||||
| 	clk_disable(data->clk); | ||||
| 	clk_disable(data->clk_master); | ||||
| 	__sysmmu_disable_clocks(data); | ||||
| } | ||||
| 
 | ||||
| static void show_fault_information(struct sysmmu_drvdata *data, | ||||
|  | @ -427,10 +441,7 @@ static void __sysmmu_disable_nocount(struct sysmmu_drvdata *data) | |||
| 	writel(CTRL_DISABLE, data->sfrbase + REG_MMU_CTRL); | ||||
| 	writel(0, data->sfrbase + REG_MMU_CFG); | ||||
| 
 | ||||
| 	clk_disable(data->aclk); | ||||
| 	clk_disable(data->pclk); | ||||
| 	clk_disable(data->clk); | ||||
| 	clk_disable(data->clk_master); | ||||
| 	__sysmmu_disable_clocks(data); | ||||
| } | ||||
| 
 | ||||
| static bool __sysmmu_disable(struct sysmmu_drvdata *data) | ||||
|  | @ -475,10 +486,7 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *data) | |||
| 
 | ||||
| static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data) | ||||
| { | ||||
| 	clk_enable(data->clk_master); | ||||
| 	clk_enable(data->clk); | ||||
| 	clk_enable(data->pclk); | ||||
| 	clk_enable(data->aclk); | ||||
| 	__sysmmu_enable_clocks(data); | ||||
| 
 | ||||
| 	writel(CTRL_BLOCK, data->sfrbase + REG_MMU_CTRL); | ||||
| 
 | ||||
|  | @ -488,6 +496,12 @@ static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data) | |||
| 
 | ||||
| 	writel(CTRL_ENABLE, data->sfrbase + REG_MMU_CTRL); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * SYSMMU driver keeps master's clock enabled only for the short | ||||
| 	 * time, while accessing the registers. For performing address | ||||
| 	 * translation during DMA transaction it relies on the client | ||||
| 	 * driver to enable it. | ||||
| 	 */ | ||||
| 	clk_disable(data->clk_master); | ||||
| } | ||||
| 
 | ||||
|  | @ -524,16 +538,15 @@ static void sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, | |||
| { | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	clk_enable(data->clk_master); | ||||
| 
 | ||||
| 	spin_lock_irqsave(&data->lock, flags); | ||||
| 	if (is_sysmmu_active(data)) { | ||||
| 		if (data->version >= MAKE_MMU_VER(3, 3)) | ||||
| 	if (is_sysmmu_active(data) && data->version >= MAKE_MMU_VER(3, 3)) { | ||||
| 		clk_enable(data->clk_master); | ||||
| 		__sysmmu_tlb_invalidate_entry(data, iova, 1); | ||||
| 		clk_disable(data->clk_master); | ||||
| 	} | ||||
| 	spin_unlock_irqrestore(&data->lock, flags); | ||||
| 
 | ||||
| 	clk_disable(data->clk_master); | ||||
| } | ||||
| 
 | ||||
| static void sysmmu_tlb_invalidate_entry(struct sysmmu_drvdata *data, | ||||
|  | @ -572,6 +585,8 @@ static void sysmmu_tlb_invalidate_entry(struct sysmmu_drvdata *data, | |||
| 	spin_unlock_irqrestore(&data->lock, flags); | ||||
| } | ||||
| 
 | ||||
| static struct iommu_ops exynos_iommu_ops; | ||||
| 
 | ||||
| static int __init exynos_sysmmu_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	int irq, ret; | ||||
|  | @ -602,37 +617,22 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) | |||
| 	} | ||||
| 
 | ||||
| 	data->clk = devm_clk_get(dev, "sysmmu"); | ||||
| 	if (!IS_ERR(data->clk)) { | ||||
| 		ret = clk_prepare(data->clk); | ||||
| 		if (ret) { | ||||
| 			dev_err(dev, "Failed to prepare clk\n"); | ||||
| 			return ret; | ||||
| 		} | ||||
| 	} else { | ||||
| 	if (PTR_ERR(data->clk) == -ENOENT) | ||||
| 		data->clk = NULL; | ||||
| 	} | ||||
| 	else if (IS_ERR(data->clk)) | ||||
| 		return PTR_ERR(data->clk); | ||||
| 
 | ||||
| 	data->aclk = devm_clk_get(dev, "aclk"); | ||||
| 	if (!IS_ERR(data->aclk)) { | ||||
| 		ret = clk_prepare(data->aclk); | ||||
| 		if (ret) { | ||||
| 			dev_err(dev, "Failed to prepare aclk\n"); | ||||
| 			return ret; | ||||
| 		} | ||||
| 	} else { | ||||
| 	if (PTR_ERR(data->aclk) == -ENOENT) | ||||
| 		data->aclk = NULL; | ||||
| 	} | ||||
| 	else if (IS_ERR(data->aclk)) | ||||
| 		return PTR_ERR(data->aclk); | ||||
| 
 | ||||
| 	data->pclk = devm_clk_get(dev, "pclk"); | ||||
| 	if (!IS_ERR(data->pclk)) { | ||||
| 		ret = clk_prepare(data->pclk); | ||||
| 		if (ret) { | ||||
| 			dev_err(dev, "Failed to prepare pclk\n"); | ||||
| 			return ret; | ||||
| 		} | ||||
| 	} else { | ||||
| 	if (PTR_ERR(data->pclk) == -ENOENT) | ||||
| 		data->pclk = NULL; | ||||
| 	} | ||||
| 	else if (IS_ERR(data->pclk)) | ||||
| 		return PTR_ERR(data->pclk); | ||||
| 
 | ||||
| 	if (!data->clk && (!data->aclk || !data->pclk)) { | ||||
| 		dev_err(dev, "Failed to get device clock(s)!\n"); | ||||
|  | @ -640,15 +640,10 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) | |||
| 	} | ||||
| 
 | ||||
| 	data->clk_master = devm_clk_get(dev, "master"); | ||||
| 	if (!IS_ERR(data->clk_master)) { | ||||
| 		ret = clk_prepare(data->clk_master); | ||||
| 		if (ret) { | ||||
| 			dev_err(dev, "Failed to prepare master's clk\n"); | ||||
| 			return ret; | ||||
| 		} | ||||
| 	} else { | ||||
| 	if (PTR_ERR(data->clk_master) == -ENOENT) | ||||
| 		data->clk_master = NULL; | ||||
| 	} | ||||
| 	else if (IS_ERR(data->clk_master)) | ||||
| 		return PTR_ERR(data->clk_master); | ||||
| 
 | ||||
| 	data->sysmmu = dev; | ||||
| 	spin_lock_init(&data->lock); | ||||
|  | @ -665,6 +660,8 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev) | |||
| 
 | ||||
| 	pm_runtime_enable(dev); | ||||
| 
 | ||||
| 	of_iommu_set_ops(dev->of_node, &exynos_iommu_ops); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -709,6 +706,7 @@ static struct platform_driver exynos_sysmmu_driver __refdata = { | |||
| 		.name		= "exynos-sysmmu", | ||||
| 		.of_match_table	= sysmmu_of_match, | ||||
| 		.pm		= &sysmmu_pm_ops, | ||||
| 		.suppress_bind_attrs = true, | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
|  | @ -716,7 +714,7 @@ static inline void update_pte(sysmmu_pte_t *ent, sysmmu_pte_t val) | |||
| { | ||||
| 	dma_sync_single_for_cpu(dma_dev, virt_to_phys(ent), sizeof(*ent), | ||||
| 				DMA_TO_DEVICE); | ||||
| 	*ent = val; | ||||
| 	*ent = cpu_to_le32(val); | ||||
| 	dma_sync_single_for_device(dma_dev, virt_to_phys(ent), sizeof(*ent), | ||||
| 				   DMA_TO_DEVICE); | ||||
| } | ||||
|  | @ -1357,7 +1355,6 @@ static int __init exynos_iommu_of_setup(struct device_node *np) | |||
| 	if (!dma_dev) | ||||
| 		dma_dev = &pdev->dev; | ||||
| 
 | ||||
| 	of_iommu_set_ops(np, &exynos_iommu_ops); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1672,7 +1672,7 @@ static int iommu_init_domains(struct intel_iommu *iommu) | |||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	size = ((ndomains >> 8) + 1) * sizeof(struct dmar_domain **); | ||||
| 	size = (ALIGN(ndomains, 256) >> 8) * sizeof(struct dmar_domain **); | ||||
| 	iommu->domains = kzalloc(size, GFP_KERNEL); | ||||
| 
 | ||||
| 	if (iommu->domains) { | ||||
|  | @ -1737,7 +1737,7 @@ static void disable_dmar_iommu(struct intel_iommu *iommu) | |||
| static void free_dmar_iommu(struct intel_iommu *iommu) | ||||
| { | ||||
| 	if ((iommu->domains) && (iommu->domain_ids)) { | ||||
| 		int elems = (cap_ndoms(iommu->cap) >> 8) + 1; | ||||
| 		int elems = ALIGN(cap_ndoms(iommu->cap), 256) >> 8; | ||||
| 		int i; | ||||
| 
 | ||||
| 		for (i = 0; i < elems; i++) | ||||
|  | @ -2076,7 +2076,7 @@ out_unlock: | |||
| 	spin_unlock(&iommu->lock); | ||||
| 	spin_unlock_irqrestore(&device_domain_lock, flags); | ||||
| 
 | ||||
| 	return 0; | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| struct domain_context_mapping_data { | ||||
|  |  | |||
|  | @ -576,7 +576,7 @@ static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops, | |||
| 	return 0; | ||||
| 
 | ||||
| found_translation: | ||||
| 	iova &= (ARM_LPAE_GRANULE(data) - 1); | ||||
| 	iova &= (ARM_LPAE_BLOCK_SIZE(lvl, data) - 1); | ||||
| 	return ((phys_addr_t)iopte_to_pfn(pte,data) << data->pg_shift) | iova; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -34,8 +34,7 @@ | |||
| #include <trace/events/iommu.h> | ||||
| 
 | ||||
| static struct kset *iommu_group_kset; | ||||
| static struct ida iommu_group_ida; | ||||
| static struct mutex iommu_group_mutex; | ||||
| static DEFINE_IDA(iommu_group_ida); | ||||
| 
 | ||||
| struct iommu_callback_data { | ||||
| 	const struct iommu_ops *ops; | ||||
|  | @ -144,9 +143,7 @@ static void iommu_group_release(struct kobject *kobj) | |||
| 	if (group->iommu_data_release) | ||||
| 		group->iommu_data_release(group->iommu_data); | ||||
| 
 | ||||
| 	mutex_lock(&iommu_group_mutex); | ||||
| 	ida_remove(&iommu_group_ida, group->id); | ||||
| 	mutex_unlock(&iommu_group_mutex); | ||||
| 	ida_simple_remove(&iommu_group_ida, group->id); | ||||
| 
 | ||||
| 	if (group->default_domain) | ||||
| 		iommu_domain_free(group->default_domain); | ||||
|  | @ -186,26 +183,17 @@ struct iommu_group *iommu_group_alloc(void) | |||
| 	INIT_LIST_HEAD(&group->devices); | ||||
| 	BLOCKING_INIT_NOTIFIER_HEAD(&group->notifier); | ||||
| 
 | ||||
| 	mutex_lock(&iommu_group_mutex); | ||||
| 
 | ||||
| again: | ||||
| 	if (unlikely(0 == ida_pre_get(&iommu_group_ida, GFP_KERNEL))) { | ||||
| 	ret = ida_simple_get(&iommu_group_ida, 0, 0, GFP_KERNEL); | ||||
| 	if (ret < 0) { | ||||
| 		kfree(group); | ||||
| 		mutex_unlock(&iommu_group_mutex); | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 		return ERR_PTR(ret); | ||||
| 	} | ||||
| 
 | ||||
| 	if (-EAGAIN == ida_get_new(&iommu_group_ida, &group->id)) | ||||
| 		goto again; | ||||
| 
 | ||||
| 	mutex_unlock(&iommu_group_mutex); | ||||
| 	group->id = ret; | ||||
| 
 | ||||
| 	ret = kobject_init_and_add(&group->kobj, &iommu_group_ktype, | ||||
| 				   NULL, "%d", group->id); | ||||
| 	if (ret) { | ||||
| 		mutex_lock(&iommu_group_mutex); | ||||
| 		ida_remove(&iommu_group_ida, group->id); | ||||
| 		mutex_unlock(&iommu_group_mutex); | ||||
| 		ida_simple_remove(&iommu_group_ida, group->id); | ||||
| 		kfree(group); | ||||
| 		return ERR_PTR(ret); | ||||
| 	} | ||||
|  | @ -348,6 +336,9 @@ static int iommu_group_create_direct_mappings(struct iommu_group *group, | |||
| 	list_for_each_entry(entry, &mappings, list) { | ||||
| 		dma_addr_t start, end, addr; | ||||
| 
 | ||||
| 		if (domain->ops->apply_dm_region) | ||||
| 			domain->ops->apply_dm_region(dev, domain, entry); | ||||
| 
 | ||||
| 		start = ALIGN(entry->start, pg_size); | ||||
| 		end   = ALIGN(entry->start + entry->length, pg_size); | ||||
| 
 | ||||
|  | @ -1483,9 +1474,6 @@ static int __init iommu_init(void) | |||
| { | ||||
| 	iommu_group_kset = kset_create_and_add("iommu_groups", | ||||
| 					       NULL, kernel_kobj); | ||||
| 	ida_init(&iommu_group_ida); | ||||
| 	mutex_init(&iommu_group_mutex); | ||||
| 
 | ||||
| 	BUG_ON(!iommu_group_kset); | ||||
| 
 | ||||
| 	return 0; | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -42,74 +42,53 @@ | |||
|  */ | ||||
| #define MAX_NUM_MIDS	32 | ||||
| 
 | ||||
| /* Maximum number of context banks that can be present in IOMMU */ | ||||
| #define IOMMU_MAX_CBS	128 | ||||
| 
 | ||||
| /**
 | ||||
|  * struct msm_iommu_dev - a single IOMMU hardware instance | ||||
|  * name		Human-readable name given to this IOMMU HW instance | ||||
|  * ncb		Number of context banks present on this IOMMU HW instance | ||||
|  * dev:		IOMMU device | ||||
|  * irq:		Interrupt number | ||||
|  * clk:		The bus clock for this IOMMU hardware instance | ||||
|  * pclk:	The clock for the IOMMU bus interconnect | ||||
|  * dev_node:	list head in qcom_iommu_device_list | ||||
|  * dom_node:	list head for domain | ||||
|  * ctx_list:	list of 'struct msm_iommu_ctx_dev' | ||||
|  * context_map: Bitmap to track allocated context banks | ||||
|  */ | ||||
| struct msm_iommu_dev { | ||||
| 	const char *name; | ||||
| 	void __iomem *base; | ||||
| 	int ncb; | ||||
| 	struct device *dev; | ||||
| 	int irq; | ||||
| 	struct clk *clk; | ||||
| 	struct clk *pclk; | ||||
| 	struct list_head dev_node; | ||||
| 	struct list_head dom_node; | ||||
| 	struct list_head ctx_list; | ||||
| 	DECLARE_BITMAP(context_map, IOMMU_MAX_CBS); | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct msm_iommu_ctx_dev - an IOMMU context bank instance | ||||
|  * name		Human-readable name given to this context bank | ||||
|  * of_node	node ptr of client device | ||||
|  * num		Index of this context bank within the hardware | ||||
|  * mids		List of Machine IDs that are to be mapped into this context | ||||
|  *		bank, terminated by -1. The MID is a set of signals on the | ||||
|  *		AXI bus that identifies the function associated with a specific | ||||
|  *		memory request. (See ARM spec). | ||||
|  * num_mids	Total number of mids | ||||
|  * node		list head in ctx_list | ||||
|  */ | ||||
| struct msm_iommu_ctx_dev { | ||||
| 	const char *name; | ||||
| 	struct device_node *of_node; | ||||
| 	int num; | ||||
| 	int mids[MAX_NUM_MIDS]; | ||||
| 	int num_mids; | ||||
| 	struct list_head list; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * struct msm_iommu_drvdata - A single IOMMU hardware instance | ||||
|  * @base:	IOMMU config port base address (VA) | ||||
|  * @ncb		The number of contexts on this IOMMU | ||||
|  * @irq:	Interrupt number | ||||
|  * @clk:	The bus clock for this IOMMU hardware instance | ||||
|  * @pclk:	The clock for the IOMMU bus interconnect | ||||
|  * | ||||
|  * A msm_iommu_drvdata holds the global driver data about a single piece | ||||
|  * of an IOMMU hardware instance. | ||||
|  */ | ||||
| struct msm_iommu_drvdata { | ||||
| 	void __iomem *base; | ||||
| 	int irq; | ||||
| 	int ncb; | ||||
| 	struct clk *clk; | ||||
| 	struct clk *pclk; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct msm_iommu_ctx_drvdata - an IOMMU context bank instance | ||||
|  * @num:		Hardware context number of this context | ||||
|  * @pdev:		Platform device associated wit this HW instance | ||||
|  * @attached_elm:	List element for domains to track which devices are | ||||
|  *			attached to them | ||||
|  * | ||||
|  * A msm_iommu_ctx_drvdata holds the driver data for a single context bank | ||||
|  * within each IOMMU hardware instance | ||||
|  */ | ||||
| struct msm_iommu_ctx_drvdata { | ||||
| 	int num; | ||||
| 	struct platform_device *pdev; | ||||
| 	struct list_head attached_elm; | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Look up an IOMMU context device by its context name. NULL if none found. | ||||
|  * Useful for testing and drivers that do not yet fully have IOMMU stuff in | ||||
|  * their platform devices. | ||||
|  */ | ||||
| struct device *msm_iommu_get_ctx(const char *ctx_name); | ||||
| 
 | ||||
| /*
 | ||||
|  * Interrupt handler for the IOMMU context fault interrupt. Hooking the | ||||
|  * interrupt is not supported in the API yet, but this will print an error | ||||
|  |  | |||
|  | @ -1,381 +0,0 @@ | |||
| /* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
 | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2 and | ||||
|  * only version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * 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., 51 Franklin Street, Fifth Floor, Boston, MA | ||||
|  * 02110-1301, USA. | ||||
|  */ | ||||
| 
 | ||||
| #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/clk.h> | ||||
| #include <linux/iommu.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/slab.h> | ||||
| 
 | ||||
| #include "msm_iommu_hw-8xxx.h" | ||||
| #include "msm_iommu.h" | ||||
| 
 | ||||
| struct iommu_ctx_iter_data { | ||||
| 	/* input */ | ||||
| 	const char *name; | ||||
| 
 | ||||
| 	/* output */ | ||||
| 	struct device *dev; | ||||
| }; | ||||
| 
 | ||||
| static struct platform_device *msm_iommu_root_dev; | ||||
| 
 | ||||
| static int each_iommu_ctx(struct device *dev, void *data) | ||||
| { | ||||
| 	struct iommu_ctx_iter_data *res = data; | ||||
| 	struct msm_iommu_ctx_dev *c = dev->platform_data; | ||||
| 
 | ||||
| 	if (!res || !c || !c->name || !res->name) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (!strcmp(res->name, c->name)) { | ||||
| 		res->dev = dev; | ||||
| 		return 1; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int each_iommu(struct device *dev, void *data) | ||||
| { | ||||
| 	return device_for_each_child(dev, data, each_iommu_ctx); | ||||
| } | ||||
| 
 | ||||
| struct device *msm_iommu_get_ctx(const char *ctx_name) | ||||
| { | ||||
| 	struct iommu_ctx_iter_data r; | ||||
| 	int found; | ||||
| 
 | ||||
| 	if (!msm_iommu_root_dev) { | ||||
| 		pr_err("No root IOMMU device.\n"); | ||||
| 		goto fail; | ||||
| 	} | ||||
| 
 | ||||
| 	r.name = ctx_name; | ||||
| 	found = device_for_each_child(&msm_iommu_root_dev->dev, &r, each_iommu); | ||||
| 
 | ||||
| 	if (!found) { | ||||
| 		pr_err("Could not find context <%s>\n", ctx_name); | ||||
| 		goto fail; | ||||
| 	} | ||||
| 
 | ||||
| 	return r.dev; | ||||
| fail: | ||||
| 	return NULL; | ||||
| } | ||||
| EXPORT_SYMBOL(msm_iommu_get_ctx); | ||||
| 
 | ||||
| static void msm_iommu_reset(void __iomem *base, int ncb) | ||||
| { | ||||
| 	int ctx; | ||||
| 
 | ||||
| 	SET_RPUE(base, 0); | ||||
| 	SET_RPUEIE(base, 0); | ||||
| 	SET_ESRRESTORE(base, 0); | ||||
| 	SET_TBE(base, 0); | ||||
| 	SET_CR(base, 0); | ||||
| 	SET_SPDMBE(base, 0); | ||||
| 	SET_TESTBUSCR(base, 0); | ||||
| 	SET_TLBRSW(base, 0); | ||||
| 	SET_GLOBAL_TLBIALL(base, 0); | ||||
| 	SET_RPU_ACR(base, 0); | ||||
| 	SET_TLBLKCRWE(base, 1); | ||||
| 
 | ||||
| 	for (ctx = 0; ctx < ncb; ctx++) { | ||||
| 		SET_BPRCOSH(base, ctx, 0); | ||||
| 		SET_BPRCISH(base, ctx, 0); | ||||
| 		SET_BPRCNSH(base, ctx, 0); | ||||
| 		SET_BPSHCFG(base, ctx, 0); | ||||
| 		SET_BPMTCFG(base, ctx, 0); | ||||
| 		SET_ACTLR(base, ctx, 0); | ||||
| 		SET_SCTLR(base, ctx, 0); | ||||
| 		SET_FSRRESTORE(base, ctx, 0); | ||||
| 		SET_TTBR0(base, ctx, 0); | ||||
| 		SET_TTBR1(base, ctx, 0); | ||||
| 		SET_TTBCR(base, ctx, 0); | ||||
| 		SET_BFBCR(base, ctx, 0); | ||||
| 		SET_PAR(base, ctx, 0); | ||||
| 		SET_FAR(base, ctx, 0); | ||||
| 		SET_CTX_TLBIALL(base, ctx, 0); | ||||
| 		SET_TLBFLPTER(base, ctx, 0); | ||||
| 		SET_TLBSLPTER(base, ctx, 0); | ||||
| 		SET_TLBLKCR(base, ctx, 0); | ||||
| 		SET_PRRR(base, ctx, 0); | ||||
| 		SET_NMRR(base, ctx, 0); | ||||
| 		SET_CONTEXTIDR(base, ctx, 0); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int msm_iommu_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct resource *r; | ||||
| 	struct clk *iommu_clk; | ||||
| 	struct clk *iommu_pclk; | ||||
| 	struct msm_iommu_drvdata *drvdata; | ||||
| 	struct msm_iommu_dev *iommu_dev = dev_get_platdata(&pdev->dev); | ||||
| 	void __iomem *regs_base; | ||||
| 	int ret, irq, par; | ||||
| 
 | ||||
| 	if (pdev->id == -1) { | ||||
| 		msm_iommu_root_dev = pdev; | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); | ||||
| 
 | ||||
| 	if (!drvdata) { | ||||
| 		ret = -ENOMEM; | ||||
| 		goto fail; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!iommu_dev) { | ||||
| 		ret = -ENODEV; | ||||
| 		goto fail; | ||||
| 	} | ||||
| 
 | ||||
| 	iommu_pclk = clk_get(NULL, "smmu_pclk"); | ||||
| 	if (IS_ERR(iommu_pclk)) { | ||||
| 		ret = -ENODEV; | ||||
| 		goto fail; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = clk_prepare_enable(iommu_pclk); | ||||
| 	if (ret) | ||||
| 		goto fail_enable; | ||||
| 
 | ||||
| 	iommu_clk = clk_get(&pdev->dev, "iommu_clk"); | ||||
| 
 | ||||
| 	if (!IS_ERR(iommu_clk))	{ | ||||
| 		if (clk_get_rate(iommu_clk) == 0) | ||||
| 			clk_set_rate(iommu_clk, 1); | ||||
| 
 | ||||
| 		ret = clk_prepare_enable(iommu_clk); | ||||
| 		if (ret) { | ||||
| 			clk_put(iommu_clk); | ||||
| 			goto fail_pclk; | ||||
| 		} | ||||
| 	} else | ||||
| 		iommu_clk = NULL; | ||||
| 
 | ||||
| 	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "physbase"); | ||||
| 	regs_base = devm_ioremap_resource(&pdev->dev, r); | ||||
| 	if (IS_ERR(regs_base)) { | ||||
| 		ret = PTR_ERR(regs_base); | ||||
| 		goto fail_clk; | ||||
| 	} | ||||
| 
 | ||||
| 	irq = platform_get_irq_byname(pdev, "secure_irq"); | ||||
| 	if (irq < 0) { | ||||
| 		ret = -ENODEV; | ||||
| 		goto fail_clk; | ||||
| 	} | ||||
| 
 | ||||
| 	msm_iommu_reset(regs_base, iommu_dev->ncb); | ||||
| 
 | ||||
| 	SET_M(regs_base, 0, 1); | ||||
| 	SET_PAR(regs_base, 0, 0); | ||||
| 	SET_V2PCFG(regs_base, 0, 1); | ||||
| 	SET_V2PPR(regs_base, 0, 0); | ||||
| 	par = GET_PAR(regs_base, 0); | ||||
| 	SET_V2PCFG(regs_base, 0, 0); | ||||
| 	SET_M(regs_base, 0, 0); | ||||
| 
 | ||||
| 	if (!par) { | ||||
| 		pr_err("%s: Invalid PAR value detected\n", iommu_dev->name); | ||||
| 		ret = -ENODEV; | ||||
| 		goto fail_clk; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = request_irq(irq, msm_iommu_fault_handler, 0, | ||||
| 			"msm_iommu_secure_irpt_handler", drvdata); | ||||
| 	if (ret) { | ||||
| 		pr_err("Request IRQ %d failed with ret=%d\n", irq, ret); | ||||
| 		goto fail_clk; | ||||
| 	} | ||||
| 
 | ||||
| 
 | ||||
| 	drvdata->pclk = iommu_pclk; | ||||
| 	drvdata->clk = iommu_clk; | ||||
| 	drvdata->base = regs_base; | ||||
| 	drvdata->irq = irq; | ||||
| 	drvdata->ncb = iommu_dev->ncb; | ||||
| 
 | ||||
| 	pr_info("device %s mapped at %p, irq %d with %d ctx banks\n", | ||||
| 		iommu_dev->name, regs_base, irq, iommu_dev->ncb); | ||||
| 
 | ||||
| 	platform_set_drvdata(pdev, drvdata); | ||||
| 
 | ||||
| 	clk_disable(iommu_clk); | ||||
| 
 | ||||
| 	clk_disable(iommu_pclk); | ||||
| 
 | ||||
| 	return 0; | ||||
| fail_clk: | ||||
| 	if (iommu_clk) { | ||||
| 		clk_disable(iommu_clk); | ||||
| 		clk_put(iommu_clk); | ||||
| 	} | ||||
| fail_pclk: | ||||
| 	clk_disable_unprepare(iommu_pclk); | ||||
| fail_enable: | ||||
| 	clk_put(iommu_pclk); | ||||
| fail: | ||||
| 	kfree(drvdata); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int msm_iommu_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	struct msm_iommu_drvdata *drv = NULL; | ||||
| 
 | ||||
| 	drv = platform_get_drvdata(pdev); | ||||
| 	if (drv) { | ||||
| 		if (drv->clk) { | ||||
| 			clk_unprepare(drv->clk); | ||||
| 			clk_put(drv->clk); | ||||
| 		} | ||||
| 		clk_unprepare(drv->pclk); | ||||
| 		clk_put(drv->pclk); | ||||
| 		memset(drv, 0, sizeof(*drv)); | ||||
| 		kfree(drv); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int msm_iommu_ctx_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct msm_iommu_ctx_dev *c = dev_get_platdata(&pdev->dev); | ||||
| 	struct msm_iommu_drvdata *drvdata; | ||||
| 	struct msm_iommu_ctx_drvdata *ctx_drvdata; | ||||
| 	int i, ret; | ||||
| 
 | ||||
| 	if (!c || !pdev->dev.parent) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	drvdata = dev_get_drvdata(pdev->dev.parent); | ||||
| 	if (!drvdata) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	ctx_drvdata = kzalloc(sizeof(*ctx_drvdata), GFP_KERNEL); | ||||
| 	if (!ctx_drvdata) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	ctx_drvdata->num = c->num; | ||||
| 	ctx_drvdata->pdev = pdev; | ||||
| 
 | ||||
| 	INIT_LIST_HEAD(&ctx_drvdata->attached_elm); | ||||
| 	platform_set_drvdata(pdev, ctx_drvdata); | ||||
| 
 | ||||
| 	ret = clk_prepare_enable(drvdata->pclk); | ||||
| 	if (ret) | ||||
| 		goto fail; | ||||
| 
 | ||||
| 	if (drvdata->clk) { | ||||
| 		ret = clk_prepare_enable(drvdata->clk); | ||||
| 		if (ret) { | ||||
| 			clk_disable_unprepare(drvdata->pclk); | ||||
| 			goto fail; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/* Program the M2V tables for this context */ | ||||
| 	for (i = 0; i < MAX_NUM_MIDS; i++) { | ||||
| 		int mid = c->mids[i]; | ||||
| 		if (mid == -1) | ||||
| 			break; | ||||
| 
 | ||||
| 		SET_M2VCBR_N(drvdata->base, mid, 0); | ||||
| 		SET_CBACR_N(drvdata->base, c->num, 0); | ||||
| 
 | ||||
| 		/* Set VMID = 0 */ | ||||
| 		SET_VMID(drvdata->base, mid, 0); | ||||
| 
 | ||||
| 		/* Set the context number for that MID to this context */ | ||||
| 		SET_CBNDX(drvdata->base, mid, c->num); | ||||
| 
 | ||||
| 		/* Set MID associated with this context bank to 0*/ | ||||
| 		SET_CBVMID(drvdata->base, c->num, 0); | ||||
| 
 | ||||
| 		/* Set the ASID for TLB tagging for this context */ | ||||
| 		SET_CONTEXTIDR_ASID(drvdata->base, c->num, c->num); | ||||
| 
 | ||||
| 		/* Set security bit override to be Non-secure */ | ||||
| 		SET_NSCFG(drvdata->base, mid, 3); | ||||
| 	} | ||||
| 
 | ||||
| 	clk_disable(drvdata->clk); | ||||
| 	clk_disable(drvdata->pclk); | ||||
| 
 | ||||
| 	dev_info(&pdev->dev, "context %s using bank %d\n", c->name, c->num); | ||||
| 	return 0; | ||||
| fail: | ||||
| 	kfree(ctx_drvdata); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int msm_iommu_ctx_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	struct msm_iommu_ctx_drvdata *drv = NULL; | ||||
| 	drv = platform_get_drvdata(pdev); | ||||
| 	if (drv) { | ||||
| 		memset(drv, 0, sizeof(struct msm_iommu_ctx_drvdata)); | ||||
| 		kfree(drv); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct platform_driver msm_iommu_driver = { | ||||
| 	.driver = { | ||||
| 		.name	= "msm_iommu", | ||||
| 	}, | ||||
| 	.probe		= msm_iommu_probe, | ||||
| 	.remove		= msm_iommu_remove, | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver msm_iommu_ctx_driver = { | ||||
| 	.driver = { | ||||
| 		.name	= "msm_iommu_ctx", | ||||
| 	}, | ||||
| 	.probe		= msm_iommu_ctx_probe, | ||||
| 	.remove		= msm_iommu_ctx_remove, | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver * const drivers[] = { | ||||
| 	&msm_iommu_driver, | ||||
| 	&msm_iommu_ctx_driver, | ||||
| }; | ||||
| 
 | ||||
| static int __init msm_iommu_driver_init(void) | ||||
| { | ||||
| 	return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); | ||||
| } | ||||
| 
 | ||||
| static void __exit msm_iommu_driver_exit(void) | ||||
| { | ||||
| 	platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); | ||||
| } | ||||
| 
 | ||||
| subsys_initcall(msm_iommu_driver_init); | ||||
| module_exit(msm_iommu_driver_exit); | ||||
| 
 | ||||
| MODULE_LICENSE("GPL v2"); | ||||
| MODULE_AUTHOR("Stepan Moskovchenko <stepanm@codeaurora.org>"); | ||||
|  | @ -34,7 +34,7 @@ | |||
| #include <dt-bindings/memory/mt8173-larb-port.h> | ||||
| #include <soc/mediatek/smi.h> | ||||
| 
 | ||||
| #include "io-pgtable.h" | ||||
| #include "mtk_iommu.h" | ||||
| 
 | ||||
| #define REG_MMU_PT_BASE_ADDR			0x000 | ||||
| 
 | ||||
|  | @ -93,20 +93,6 @@ | |||
| 
 | ||||
| #define MTK_PROTECT_PA_ALIGN			128 | ||||
| 
 | ||||
| struct mtk_iommu_suspend_reg { | ||||
| 	u32				standard_axi_mode; | ||||
| 	u32				dcm_dis; | ||||
| 	u32				ctrl_reg; | ||||
| 	u32				int_control0; | ||||
| 	u32				int_main_control; | ||||
| }; | ||||
| 
 | ||||
| struct mtk_iommu_client_priv { | ||||
| 	struct list_head		client; | ||||
| 	unsigned int			mtk_m4u_id; | ||||
| 	struct device			*m4udev; | ||||
| }; | ||||
| 
 | ||||
| struct mtk_iommu_domain { | ||||
| 	spinlock_t			pgtlock; /* lock for page table */ | ||||
| 
 | ||||
|  | @ -116,19 +102,6 @@ struct mtk_iommu_domain { | |||
| 	struct iommu_domain		domain; | ||||
| }; | ||||
| 
 | ||||
| struct mtk_iommu_data { | ||||
| 	void __iomem			*base; | ||||
| 	int				irq; | ||||
| 	struct device			*dev; | ||||
| 	struct clk			*bclk; | ||||
| 	phys_addr_t			protect_base; /* protect memory base */ | ||||
| 	struct mtk_iommu_suspend_reg	reg; | ||||
| 	struct mtk_iommu_domain		*m4u_dom; | ||||
| 	struct iommu_group		*m4u_group; | ||||
| 	struct mtk_smi_iommu		smi_imu;      /* SMI larb iommu info */ | ||||
| 	bool                            enable_4GB; | ||||
| }; | ||||
| 
 | ||||
| static struct iommu_ops mtk_iommu_ops; | ||||
| 
 | ||||
| static struct mtk_iommu_domain *to_mtk_domain(struct iommu_domain *dom) | ||||
|  | @ -455,7 +428,6 @@ static int mtk_iommu_of_xlate(struct device *dev, struct of_phandle_args *args) | |||
| 	if (!dev->archdata.iommu) { | ||||
| 		/* Get the m4u device */ | ||||
| 		m4updev = of_find_device_by_node(args->np); | ||||
| 		of_node_put(args->np); | ||||
| 		if (WARN_ON(!m4updev)) | ||||
| 			return -EINVAL; | ||||
| 
 | ||||
|  | @ -552,25 +524,6 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int compare_of(struct device *dev, void *data) | ||||
| { | ||||
| 	return dev->of_node == data; | ||||
| } | ||||
| 
 | ||||
| static int mtk_iommu_bind(struct device *dev) | ||||
| { | ||||
| 	struct mtk_iommu_data *data = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	return component_bind_all(dev, &data->smi_imu); | ||||
| } | ||||
| 
 | ||||
| static void mtk_iommu_unbind(struct device *dev) | ||||
| { | ||||
| 	struct mtk_iommu_data *data = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	component_unbind_all(dev, &data->smi_imu); | ||||
| } | ||||
| 
 | ||||
| static const struct component_master_ops mtk_iommu_com_ops = { | ||||
| 	.bind		= mtk_iommu_bind, | ||||
| 	.unbind		= mtk_iommu_unbind, | ||||
|  |  | |||
							
								
								
									
										77
									
								
								drivers/iommu/mtk_iommu.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								drivers/iommu/mtk_iommu.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,77 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2015-2016 MediaTek Inc. | ||||
|  * Author: Honghui Zhang <honghui.zhang@mediatek.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2 as | ||||
|  * published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _MTK_IOMMU_H_ | ||||
| #define _MTK_IOMMU_H_ | ||||
| 
 | ||||
| #include <linux/clk.h> | ||||
| #include <linux/component.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/iommu.h> | ||||
| #include <linux/list.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <soc/mediatek/smi.h> | ||||
| 
 | ||||
| #include "io-pgtable.h" | ||||
| 
 | ||||
| struct mtk_iommu_suspend_reg { | ||||
| 	u32				standard_axi_mode; | ||||
| 	u32				dcm_dis; | ||||
| 	u32				ctrl_reg; | ||||
| 	u32				int_control0; | ||||
| 	u32				int_main_control; | ||||
| }; | ||||
| 
 | ||||
| struct mtk_iommu_client_priv { | ||||
| 	struct list_head		client; | ||||
| 	unsigned int			mtk_m4u_id; | ||||
| 	struct device			*m4udev; | ||||
| }; | ||||
| 
 | ||||
| struct mtk_iommu_domain; | ||||
| 
 | ||||
| struct mtk_iommu_data { | ||||
| 	void __iomem			*base; | ||||
| 	int				irq; | ||||
| 	struct device			*dev; | ||||
| 	struct clk			*bclk; | ||||
| 	phys_addr_t			protect_base; /* protect memory base */ | ||||
| 	struct mtk_iommu_suspend_reg	reg; | ||||
| 	struct mtk_iommu_domain		*m4u_dom; | ||||
| 	struct iommu_group		*m4u_group; | ||||
| 	struct mtk_smi_iommu		smi_imu;      /* SMI larb iommu info */ | ||||
| 	bool                            enable_4GB; | ||||
| }; | ||||
| 
 | ||||
| static int compare_of(struct device *dev, void *data) | ||||
| { | ||||
| 	return dev->of_node == data; | ||||
| } | ||||
| 
 | ||||
| static int mtk_iommu_bind(struct device *dev) | ||||
| { | ||||
| 	struct mtk_iommu_data *data = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	return component_bind_all(dev, &data->smi_imu); | ||||
| } | ||||
| 
 | ||||
| static void mtk_iommu_unbind(struct device *dev) | ||||
| { | ||||
| 	struct mtk_iommu_data *data = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	component_unbind_all(dev, &data->smi_imu); | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										727
									
								
								drivers/iommu/mtk_iommu_v1.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										727
									
								
								drivers/iommu/mtk_iommu_v1.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,727 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2015-2016 MediaTek Inc. | ||||
|  * Author: Honghui Zhang <honghui.zhang@mediatek.com> | ||||
|  * | ||||
|  * Based on driver/iommu/mtk_iommu.c | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2 as | ||||
|  * published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  */ | ||||
| #include <linux/bootmem.h> | ||||
| #include <linux/bug.h> | ||||
| #include <linux/clk.h> | ||||
| #include <linux/component.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/dma-iommu.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/iommu.h> | ||||
| #include <linux/iopoll.h> | ||||
| #include <linux/kmemleak.h> | ||||
| #include <linux/list.h> | ||||
| #include <linux/of_address.h> | ||||
| #include <linux/of_iommu.h> | ||||
| #include <linux/of_irq.h> | ||||
| #include <linux/of_platform.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <asm/barrier.h> | ||||
| #include <asm/dma-iommu.h> | ||||
| #include <linux/module.h> | ||||
| #include <dt-bindings/memory/mt2701-larb-port.h> | ||||
| #include <soc/mediatek/smi.h> | ||||
| #include "mtk_iommu.h" | ||||
| 
 | ||||
| #define REG_MMU_PT_BASE_ADDR			0x000 | ||||
| 
 | ||||
| #define F_ALL_INVLD				0x2 | ||||
| #define F_MMU_INV_RANGE				0x1 | ||||
| #define F_INVLD_EN0				BIT(0) | ||||
| #define F_INVLD_EN1				BIT(1) | ||||
| 
 | ||||
| #define F_MMU_FAULT_VA_MSK			0xfffff000 | ||||
| #define MTK_PROTECT_PA_ALIGN			128 | ||||
| 
 | ||||
| #define REG_MMU_CTRL_REG			0x210 | ||||
| #define F_MMU_CTRL_COHERENT_EN			BIT(8) | ||||
| #define REG_MMU_IVRP_PADDR			0x214 | ||||
| #define REG_MMU_INT_CONTROL			0x220 | ||||
| #define F_INT_TRANSLATION_FAULT			BIT(0) | ||||
| #define F_INT_MAIN_MULTI_HIT_FAULT		BIT(1) | ||||
| #define F_INT_INVALID_PA_FAULT			BIT(2) | ||||
| #define F_INT_ENTRY_REPLACEMENT_FAULT		BIT(3) | ||||
| #define F_INT_TABLE_WALK_FAULT			BIT(4) | ||||
| #define F_INT_TLB_MISS_FAULT			BIT(5) | ||||
| #define F_INT_PFH_DMA_FIFO_OVERFLOW		BIT(6) | ||||
| #define F_INT_MISS_DMA_FIFO_OVERFLOW		BIT(7) | ||||
| 
 | ||||
| #define F_MMU_TF_PROTECT_SEL(prot)		(((prot) & 0x3) << 5) | ||||
| #define F_INT_CLR_BIT				BIT(12) | ||||
| 
 | ||||
| #define REG_MMU_FAULT_ST			0x224 | ||||
| #define REG_MMU_FAULT_VA			0x228 | ||||
| #define REG_MMU_INVLD_PA			0x22C | ||||
| #define REG_MMU_INT_ID				0x388 | ||||
| #define REG_MMU_INVALIDATE			0x5c0 | ||||
| #define REG_MMU_INVLD_START_A			0x5c4 | ||||
| #define REG_MMU_INVLD_END_A			0x5c8 | ||||
| 
 | ||||
| #define REG_MMU_INV_SEL				0x5d8 | ||||
| #define REG_MMU_STANDARD_AXI_MODE		0x5e8 | ||||
| 
 | ||||
| #define REG_MMU_DCM				0x5f0 | ||||
| #define F_MMU_DCM_ON				BIT(1) | ||||
| #define REG_MMU_CPE_DONE			0x60c | ||||
| #define F_DESC_VALID				0x2 | ||||
| #define F_DESC_NONSEC				BIT(3) | ||||
| #define MT2701_M4U_TF_LARB(TF)			(6 - (((TF) >> 13) & 0x7)) | ||||
| #define MT2701_M4U_TF_PORT(TF)			(((TF) >> 8) & 0xF) | ||||
| /* MTK generation one iommu HW only support 4K size mapping */ | ||||
| #define MT2701_IOMMU_PAGE_SHIFT			12 | ||||
| #define MT2701_IOMMU_PAGE_SIZE			(1UL << MT2701_IOMMU_PAGE_SHIFT) | ||||
| 
 | ||||
| /*
 | ||||
|  * MTK m4u support 4GB iova address space, and only support 4K page | ||||
|  * mapping. So the pagetable size should be exactly as 4M. | ||||
|  */ | ||||
| #define M2701_IOMMU_PGT_SIZE			SZ_4M | ||||
| 
 | ||||
| struct mtk_iommu_domain { | ||||
| 	spinlock_t			pgtlock; /* lock for page table */ | ||||
| 	struct iommu_domain		domain; | ||||
| 	u32				*pgt_va; | ||||
| 	dma_addr_t			pgt_pa; | ||||
| 	struct mtk_iommu_data		*data; | ||||
| }; | ||||
| 
 | ||||
| static struct mtk_iommu_domain *to_mtk_domain(struct iommu_domain *dom) | ||||
| { | ||||
| 	return container_of(dom, struct mtk_iommu_domain, domain); | ||||
| } | ||||
| 
 | ||||
| static const int mt2701_m4u_in_larb[] = { | ||||
| 	LARB0_PORT_OFFSET, LARB1_PORT_OFFSET, | ||||
| 	LARB2_PORT_OFFSET, LARB3_PORT_OFFSET | ||||
| }; | ||||
| 
 | ||||
| static inline int mt2701_m4u_to_larb(int id) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = ARRAY_SIZE(mt2701_m4u_in_larb) - 1; i >= 0; i--) | ||||
| 		if ((id) >= mt2701_m4u_in_larb[i]) | ||||
| 			return i; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static inline int mt2701_m4u_to_port(int id) | ||||
| { | ||||
| 	int larb = mt2701_m4u_to_larb(id); | ||||
| 
 | ||||
| 	return id - mt2701_m4u_in_larb[larb]; | ||||
| } | ||||
| 
 | ||||
| static void mtk_iommu_tlb_flush_all(struct mtk_iommu_data *data) | ||||
| { | ||||
| 	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, | ||||
| 			data->base + REG_MMU_INV_SEL); | ||||
| 	writel_relaxed(F_ALL_INVLD, data->base + REG_MMU_INVALIDATE); | ||||
| 	wmb(); /* Make sure the tlb flush all done */ | ||||
| } | ||||
| 
 | ||||
| static void mtk_iommu_tlb_flush_range(struct mtk_iommu_data *data, | ||||
| 				unsigned long iova, size_t size) | ||||
| { | ||||
| 	int ret; | ||||
| 	u32 tmp; | ||||
| 
 | ||||
| 	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, | ||||
| 		data->base + REG_MMU_INV_SEL); | ||||
| 	writel_relaxed(iova & F_MMU_FAULT_VA_MSK, | ||||
| 		data->base + REG_MMU_INVLD_START_A); | ||||
| 	writel_relaxed((iova + size - 1) & F_MMU_FAULT_VA_MSK, | ||||
| 		data->base + REG_MMU_INVLD_END_A); | ||||
| 	writel_relaxed(F_MMU_INV_RANGE, data->base + REG_MMU_INVALIDATE); | ||||
| 
 | ||||
| 	ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE, | ||||
| 				tmp, tmp != 0, 10, 100000); | ||||
| 	if (ret) { | ||||
| 		dev_warn(data->dev, | ||||
| 			 "Partial TLB flush timed out, falling back to full flush\n"); | ||||
| 		mtk_iommu_tlb_flush_all(data); | ||||
| 	} | ||||
| 	/* Clear the CPE status */ | ||||
| 	writel_relaxed(0, data->base + REG_MMU_CPE_DONE); | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t mtk_iommu_isr(int irq, void *dev_id) | ||||
| { | ||||
| 	struct mtk_iommu_data *data = dev_id; | ||||
| 	struct mtk_iommu_domain *dom = data->m4u_dom; | ||||
| 	u32 int_state, regval, fault_iova, fault_pa; | ||||
| 	unsigned int fault_larb, fault_port; | ||||
| 
 | ||||
| 	/* Read error information from registers */ | ||||
| 	int_state = readl_relaxed(data->base + REG_MMU_FAULT_ST); | ||||
| 	fault_iova = readl_relaxed(data->base + REG_MMU_FAULT_VA); | ||||
| 
 | ||||
| 	fault_iova &= F_MMU_FAULT_VA_MSK; | ||||
| 	fault_pa = readl_relaxed(data->base + REG_MMU_INVLD_PA); | ||||
| 	regval = readl_relaxed(data->base + REG_MMU_INT_ID); | ||||
| 	fault_larb = MT2701_M4U_TF_LARB(regval); | ||||
| 	fault_port = MT2701_M4U_TF_PORT(regval); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * MTK v1 iommu HW could not determine whether the fault is read or | ||||
| 	 * write fault, report as read fault. | ||||
| 	 */ | ||||
| 	if (report_iommu_fault(&dom->domain, data->dev, fault_iova, | ||||
| 			IOMMU_FAULT_READ)) | ||||
| 		dev_err_ratelimited(data->dev, | ||||
| 			"fault type=0x%x iova=0x%x pa=0x%x larb=%d port=%d\n", | ||||
| 			int_state, fault_iova, fault_pa, | ||||
| 			fault_larb, fault_port); | ||||
| 
 | ||||
| 	/* Interrupt clear */ | ||||
| 	regval = readl_relaxed(data->base + REG_MMU_INT_CONTROL); | ||||
| 	regval |= F_INT_CLR_BIT; | ||||
| 	writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL); | ||||
| 
 | ||||
| 	mtk_iommu_tlb_flush_all(data); | ||||
| 
 | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static void mtk_iommu_config(struct mtk_iommu_data *data, | ||||
| 			     struct device *dev, bool enable) | ||||
| { | ||||
| 	struct mtk_iommu_client_priv *head, *cur, *next; | ||||
| 	struct mtk_smi_larb_iommu    *larb_mmu; | ||||
| 	unsigned int                 larbid, portid; | ||||
| 
 | ||||
| 	head = dev->archdata.iommu; | ||||
| 	list_for_each_entry_safe(cur, next, &head->client, client) { | ||||
| 		larbid = mt2701_m4u_to_larb(cur->mtk_m4u_id); | ||||
| 		portid = mt2701_m4u_to_port(cur->mtk_m4u_id); | ||||
| 		larb_mmu = &data->smi_imu.larb_imu[larbid]; | ||||
| 
 | ||||
| 		dev_dbg(dev, "%s iommu port: %d\n", | ||||
| 			enable ? "enable" : "disable", portid); | ||||
| 
 | ||||
| 		if (enable) | ||||
| 			larb_mmu->mmu |= MTK_SMI_MMU_EN(portid); | ||||
| 		else | ||||
| 			larb_mmu->mmu &= ~MTK_SMI_MMU_EN(portid); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int mtk_iommu_domain_finalise(struct mtk_iommu_data *data) | ||||
| { | ||||
| 	struct mtk_iommu_domain *dom = data->m4u_dom; | ||||
| 
 | ||||
| 	spin_lock_init(&dom->pgtlock); | ||||
| 
 | ||||
| 	dom->pgt_va = dma_zalloc_coherent(data->dev, | ||||
| 				M2701_IOMMU_PGT_SIZE, | ||||
| 				&dom->pgt_pa, GFP_KERNEL); | ||||
| 	if (!dom->pgt_va) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	writel(dom->pgt_pa, data->base + REG_MMU_PT_BASE_ADDR); | ||||
| 
 | ||||
| 	dom->data = data; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct iommu_domain *mtk_iommu_domain_alloc(unsigned type) | ||||
| { | ||||
| 	struct mtk_iommu_domain *dom; | ||||
| 
 | ||||
| 	if (type != IOMMU_DOMAIN_UNMANAGED) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	dom = kzalloc(sizeof(*dom), GFP_KERNEL); | ||||
| 	if (!dom) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	return &dom->domain; | ||||
| } | ||||
| 
 | ||||
| static void mtk_iommu_domain_free(struct iommu_domain *domain) | ||||
| { | ||||
| 	struct mtk_iommu_domain *dom = to_mtk_domain(domain); | ||||
| 	struct mtk_iommu_data *data = dom->data; | ||||
| 
 | ||||
| 	dma_free_coherent(data->dev, M2701_IOMMU_PGT_SIZE, | ||||
| 			dom->pgt_va, dom->pgt_pa); | ||||
| 	kfree(to_mtk_domain(domain)); | ||||
| } | ||||
| 
 | ||||
| static int mtk_iommu_attach_device(struct iommu_domain *domain, | ||||
| 				   struct device *dev) | ||||
| { | ||||
| 	struct mtk_iommu_domain *dom = to_mtk_domain(domain); | ||||
| 	struct mtk_iommu_client_priv *priv = dev->archdata.iommu; | ||||
| 	struct mtk_iommu_data *data; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!priv) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	data = dev_get_drvdata(priv->m4udev); | ||||
| 	if (!data->m4u_dom) { | ||||
| 		data->m4u_dom = dom; | ||||
| 		ret = mtk_iommu_domain_finalise(data); | ||||
| 		if (ret) { | ||||
| 			data->m4u_dom = NULL; | ||||
| 			return ret; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	mtk_iommu_config(data, dev, true); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void mtk_iommu_detach_device(struct iommu_domain *domain, | ||||
| 				    struct device *dev) | ||||
| { | ||||
| 	struct mtk_iommu_client_priv *priv = dev->archdata.iommu; | ||||
| 	struct mtk_iommu_data *data; | ||||
| 
 | ||||
| 	if (!priv) | ||||
| 		return; | ||||
| 
 | ||||
| 	data = dev_get_drvdata(priv->m4udev); | ||||
| 	mtk_iommu_config(data, dev, false); | ||||
| } | ||||
| 
 | ||||
| static int mtk_iommu_map(struct iommu_domain *domain, unsigned long iova, | ||||
| 			 phys_addr_t paddr, size_t size, int prot) | ||||
| { | ||||
| 	struct mtk_iommu_domain *dom = to_mtk_domain(domain); | ||||
| 	unsigned int page_num = size >> MT2701_IOMMU_PAGE_SHIFT; | ||||
| 	unsigned long flags; | ||||
| 	unsigned int i; | ||||
| 	u32 *pgt_base_iova = dom->pgt_va + (iova  >> MT2701_IOMMU_PAGE_SHIFT); | ||||
| 	u32 pabase = (u32)paddr; | ||||
| 	int map_size = 0; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&dom->pgtlock, flags); | ||||
| 	for (i = 0; i < page_num; i++) { | ||||
| 		if (pgt_base_iova[i]) { | ||||
| 			memset(pgt_base_iova, 0, i * sizeof(u32)); | ||||
| 			break; | ||||
| 		} | ||||
| 		pgt_base_iova[i] = pabase | F_DESC_VALID | F_DESC_NONSEC; | ||||
| 		pabase += MT2701_IOMMU_PAGE_SIZE; | ||||
| 		map_size += MT2701_IOMMU_PAGE_SIZE; | ||||
| 	} | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&dom->pgtlock, flags); | ||||
| 
 | ||||
| 	mtk_iommu_tlb_flush_range(dom->data, iova, size); | ||||
| 
 | ||||
| 	return map_size == size ? 0 : -EEXIST; | ||||
| } | ||||
| 
 | ||||
| static size_t mtk_iommu_unmap(struct iommu_domain *domain, | ||||
| 			      unsigned long iova, size_t size) | ||||
| { | ||||
| 	struct mtk_iommu_domain *dom = to_mtk_domain(domain); | ||||
| 	unsigned long flags; | ||||
| 	u32 *pgt_base_iova = dom->pgt_va + (iova  >> MT2701_IOMMU_PAGE_SHIFT); | ||||
| 	unsigned int page_num = size >> MT2701_IOMMU_PAGE_SHIFT; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&dom->pgtlock, flags); | ||||
| 	memset(pgt_base_iova, 0, page_num * sizeof(u32)); | ||||
| 	spin_unlock_irqrestore(&dom->pgtlock, flags); | ||||
| 
 | ||||
| 	mtk_iommu_tlb_flush_range(dom->data, iova, size); | ||||
| 
 | ||||
| 	return size; | ||||
| } | ||||
| 
 | ||||
| static phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain, | ||||
| 					  dma_addr_t iova) | ||||
| { | ||||
| 	struct mtk_iommu_domain *dom = to_mtk_domain(domain); | ||||
| 	unsigned long flags; | ||||
| 	phys_addr_t pa; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&dom->pgtlock, flags); | ||||
| 	pa = *(dom->pgt_va + (iova >> MT2701_IOMMU_PAGE_SHIFT)); | ||||
| 	pa = pa & (~(MT2701_IOMMU_PAGE_SIZE - 1)); | ||||
| 	spin_unlock_irqrestore(&dom->pgtlock, flags); | ||||
| 
 | ||||
| 	return pa; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * MTK generation one iommu HW only support one iommu domain, and all the client | ||||
|  * sharing the same iova address space. | ||||
|  */ | ||||
| static int mtk_iommu_create_mapping(struct device *dev, | ||||
| 				    struct of_phandle_args *args) | ||||
| { | ||||
| 	struct mtk_iommu_client_priv *head, *priv, *next; | ||||
| 	struct platform_device *m4updev; | ||||
| 	struct dma_iommu_mapping *mtk_mapping; | ||||
| 	struct device *m4udev; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (args->args_count != 1) { | ||||
| 		dev_err(dev, "invalid #iommu-cells(%d) property for IOMMU\n", | ||||
| 			args->args_count); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!dev->archdata.iommu) { | ||||
| 		/* Get the m4u device */ | ||||
| 		m4updev = of_find_device_by_node(args->np); | ||||
| 		if (WARN_ON(!m4updev)) | ||||
| 			return -EINVAL; | ||||
| 
 | ||||
| 		head = kzalloc(sizeof(*head), GFP_KERNEL); | ||||
| 		if (!head) | ||||
| 			return -ENOMEM; | ||||
| 
 | ||||
| 		dev->archdata.iommu = head; | ||||
| 		INIT_LIST_HEAD(&head->client); | ||||
| 		head->m4udev = &m4updev->dev; | ||||
| 	} else { | ||||
| 		head = dev->archdata.iommu; | ||||
| 	} | ||||
| 
 | ||||
| 	priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||||
| 	if (!priv) { | ||||
| 		ret = -ENOMEM; | ||||
| 		goto err_free_mem; | ||||
| 	} | ||||
| 	priv->mtk_m4u_id = args->args[0]; | ||||
| 	list_add_tail(&priv->client, &head->client); | ||||
| 
 | ||||
| 	m4udev = head->m4udev; | ||||
| 	mtk_mapping = m4udev->archdata.iommu; | ||||
| 	if (!mtk_mapping) { | ||||
| 		/* MTK iommu support 4GB iova address space. */ | ||||
| 		mtk_mapping = arm_iommu_create_mapping(&platform_bus_type, | ||||
| 						0, 1ULL << 32); | ||||
| 		if (IS_ERR(mtk_mapping)) { | ||||
| 			ret = PTR_ERR(mtk_mapping); | ||||
| 			goto err_free_mem; | ||||
| 		} | ||||
| 		m4udev->archdata.iommu = mtk_mapping; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = arm_iommu_attach_device(dev, mtk_mapping); | ||||
| 	if (ret) | ||||
| 		goto err_release_mapping; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_release_mapping: | ||||
| 	arm_iommu_release_mapping(mtk_mapping); | ||||
| 	m4udev->archdata.iommu = NULL; | ||||
| err_free_mem: | ||||
| 	list_for_each_entry_safe(priv, next, &head->client, client) | ||||
| 		kfree(priv); | ||||
| 	kfree(head); | ||||
| 	dev->archdata.iommu = NULL; | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int mtk_iommu_add_device(struct device *dev) | ||||
| { | ||||
| 	struct iommu_group *group; | ||||
| 	struct of_phandle_args iommu_spec; | ||||
| 	struct of_phandle_iterator it; | ||||
| 	int err; | ||||
| 
 | ||||
| 	of_for_each_phandle(&it, err, dev->of_node, "iommus", | ||||
| 			"#iommu-cells", 0) { | ||||
| 		int count = of_phandle_iterator_args(&it, iommu_spec.args, | ||||
| 					MAX_PHANDLE_ARGS); | ||||
| 		iommu_spec.np = of_node_get(it.node); | ||||
| 		iommu_spec.args_count = count; | ||||
| 
 | ||||
| 		mtk_iommu_create_mapping(dev, &iommu_spec); | ||||
| 		of_node_put(iommu_spec.np); | ||||
| 	} | ||||
| 
 | ||||
| 	if (!dev->archdata.iommu) /* Not a iommu client device */ | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	group = iommu_group_get_for_dev(dev); | ||||
| 	if (IS_ERR(group)) | ||||
| 		return PTR_ERR(group); | ||||
| 
 | ||||
| 	iommu_group_put(group); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void mtk_iommu_remove_device(struct device *dev) | ||||
| { | ||||
| 	struct mtk_iommu_client_priv *head, *cur, *next; | ||||
| 
 | ||||
| 	head = dev->archdata.iommu; | ||||
| 	if (!head) | ||||
| 		return; | ||||
| 
 | ||||
| 	list_for_each_entry_safe(cur, next, &head->client, client) { | ||||
| 		list_del(&cur->client); | ||||
| 		kfree(cur); | ||||
| 	} | ||||
| 	kfree(head); | ||||
| 	dev->archdata.iommu = NULL; | ||||
| 
 | ||||
| 	iommu_group_remove_device(dev); | ||||
| } | ||||
| 
 | ||||
| static struct iommu_group *mtk_iommu_device_group(struct device *dev) | ||||
| { | ||||
| 	struct mtk_iommu_data *data; | ||||
| 	struct mtk_iommu_client_priv *priv; | ||||
| 
 | ||||
| 	priv = dev->archdata.iommu; | ||||
| 	if (!priv) | ||||
| 		return ERR_PTR(-ENODEV); | ||||
| 
 | ||||
| 	/* All the client devices are in the same m4u iommu-group */ | ||||
| 	data = dev_get_drvdata(priv->m4udev); | ||||
| 	if (!data->m4u_group) { | ||||
| 		data->m4u_group = iommu_group_alloc(); | ||||
| 		if (IS_ERR(data->m4u_group)) | ||||
| 			dev_err(dev, "Failed to allocate M4U IOMMU group\n"); | ||||
| 	} | ||||
| 	return data->m4u_group; | ||||
| } | ||||
| 
 | ||||
| static int mtk_iommu_hw_init(const struct mtk_iommu_data *data) | ||||
| { | ||||
| 	u32 regval; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = clk_prepare_enable(data->bclk); | ||||
| 	if (ret) { | ||||
| 		dev_err(data->dev, "Failed to enable iommu bclk(%d)\n", ret); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	regval = F_MMU_CTRL_COHERENT_EN | F_MMU_TF_PROTECT_SEL(2); | ||||
| 	writel_relaxed(regval, data->base + REG_MMU_CTRL_REG); | ||||
| 
 | ||||
| 	regval = F_INT_TRANSLATION_FAULT | | ||||
| 		F_INT_MAIN_MULTI_HIT_FAULT | | ||||
| 		F_INT_INVALID_PA_FAULT | | ||||
| 		F_INT_ENTRY_REPLACEMENT_FAULT | | ||||
| 		F_INT_TABLE_WALK_FAULT | | ||||
| 		F_INT_TLB_MISS_FAULT | | ||||
| 		F_INT_PFH_DMA_FIFO_OVERFLOW | | ||||
| 		F_INT_MISS_DMA_FIFO_OVERFLOW; | ||||
| 	writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL); | ||||
| 
 | ||||
| 	/* protect memory,hw will write here while translation fault */ | ||||
| 	writel_relaxed(data->protect_base, | ||||
| 			data->base + REG_MMU_IVRP_PADDR); | ||||
| 
 | ||||
| 	writel_relaxed(F_MMU_DCM_ON, data->base + REG_MMU_DCM); | ||||
| 
 | ||||
| 	if (devm_request_irq(data->dev, data->irq, mtk_iommu_isr, 0, | ||||
| 			     dev_name(data->dev), (void *)data)) { | ||||
| 		writel_relaxed(0, data->base + REG_MMU_PT_BASE_ADDR); | ||||
| 		clk_disable_unprepare(data->bclk); | ||||
| 		dev_err(data->dev, "Failed @ IRQ-%d Request\n", data->irq); | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct iommu_ops mtk_iommu_ops = { | ||||
| 	.domain_alloc	= mtk_iommu_domain_alloc, | ||||
| 	.domain_free	= mtk_iommu_domain_free, | ||||
| 	.attach_dev	= mtk_iommu_attach_device, | ||||
| 	.detach_dev	= mtk_iommu_detach_device, | ||||
| 	.map		= mtk_iommu_map, | ||||
| 	.unmap		= mtk_iommu_unmap, | ||||
| 	.map_sg		= default_iommu_map_sg, | ||||
| 	.iova_to_phys	= mtk_iommu_iova_to_phys, | ||||
| 	.add_device	= mtk_iommu_add_device, | ||||
| 	.remove_device	= mtk_iommu_remove_device, | ||||
| 	.device_group	= mtk_iommu_device_group, | ||||
| 	.pgsize_bitmap	= ~0UL << MT2701_IOMMU_PAGE_SHIFT, | ||||
| }; | ||||
| 
 | ||||
| static const struct of_device_id mtk_iommu_of_ids[] = { | ||||
| 	{ .compatible = "mediatek,mt2701-m4u", }, | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
| static const struct component_master_ops mtk_iommu_com_ops = { | ||||
| 	.bind		= mtk_iommu_bind, | ||||
| 	.unbind		= mtk_iommu_unbind, | ||||
| }; | ||||
| 
 | ||||
| static int mtk_iommu_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct mtk_iommu_data		*data; | ||||
| 	struct device			*dev = &pdev->dev; | ||||
| 	struct resource			*res; | ||||
| 	struct component_match		*match = NULL; | ||||
| 	struct of_phandle_args		larb_spec; | ||||
| 	struct of_phandle_iterator	it; | ||||
| 	void				*protect; | ||||
| 	int				larb_nr, ret, err; | ||||
| 
 | ||||
| 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); | ||||
| 	if (!data) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	data->dev = dev; | ||||
| 
 | ||||
| 	/* Protect memory. HW will access here while translation fault.*/ | ||||
| 	protect = devm_kzalloc(dev, MTK_PROTECT_PA_ALIGN * 2, | ||||
| 			GFP_KERNEL | GFP_DMA); | ||||
| 	if (!protect) | ||||
| 		return -ENOMEM; | ||||
| 	data->protect_base = ALIGN(virt_to_phys(protect), MTK_PROTECT_PA_ALIGN); | ||||
| 
 | ||||
| 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| 	data->base = devm_ioremap_resource(dev, res); | ||||
| 	if (IS_ERR(data->base)) | ||||
| 		return PTR_ERR(data->base); | ||||
| 
 | ||||
| 	data->irq = platform_get_irq(pdev, 0); | ||||
| 	if (data->irq < 0) | ||||
| 		return data->irq; | ||||
| 
 | ||||
| 	data->bclk = devm_clk_get(dev, "bclk"); | ||||
| 	if (IS_ERR(data->bclk)) | ||||
| 		return PTR_ERR(data->bclk); | ||||
| 
 | ||||
| 	larb_nr = 0; | ||||
| 	of_for_each_phandle(&it, err, dev->of_node, | ||||
| 			"mediatek,larbs", NULL, 0) { | ||||
| 		struct platform_device *plarbdev; | ||||
| 		int count = of_phandle_iterator_args(&it, larb_spec.args, | ||||
| 					MAX_PHANDLE_ARGS); | ||||
| 
 | ||||
| 		if (count) | ||||
| 			continue; | ||||
| 
 | ||||
| 		larb_spec.np = of_node_get(it.node); | ||||
| 		if (!of_device_is_available(larb_spec.np)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		plarbdev = of_find_device_by_node(larb_spec.np); | ||||
| 		of_node_put(larb_spec.np); | ||||
| 		if (!plarbdev) { | ||||
| 			plarbdev = of_platform_device_create( | ||||
| 						larb_spec.np, NULL, | ||||
| 						platform_bus_type.dev_root); | ||||
| 			if (!plarbdev) | ||||
| 				return -EPROBE_DEFER; | ||||
| 		} | ||||
| 
 | ||||
| 		data->smi_imu.larb_imu[larb_nr].dev = &plarbdev->dev; | ||||
| 		component_match_add(dev, &match, compare_of, larb_spec.np); | ||||
| 		larb_nr++; | ||||
| 	} | ||||
| 
 | ||||
| 	data->smi_imu.larb_nr = larb_nr; | ||||
| 
 | ||||
| 	platform_set_drvdata(pdev, data); | ||||
| 
 | ||||
| 	ret = mtk_iommu_hw_init(data); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	if (!iommu_present(&platform_bus_type)) | ||||
| 		bus_set_iommu(&platform_bus_type,  &mtk_iommu_ops); | ||||
| 
 | ||||
| 	return component_master_add_with_match(dev, &mtk_iommu_com_ops, match); | ||||
| } | ||||
| 
 | ||||
| static int mtk_iommu_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	struct mtk_iommu_data *data = platform_get_drvdata(pdev); | ||||
| 
 | ||||
| 	if (iommu_present(&platform_bus_type)) | ||||
| 		bus_set_iommu(&platform_bus_type, NULL); | ||||
| 
 | ||||
| 	clk_disable_unprepare(data->bclk); | ||||
| 	devm_free_irq(&pdev->dev, data->irq, data); | ||||
| 	component_master_del(&pdev->dev, &mtk_iommu_com_ops); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int __maybe_unused mtk_iommu_suspend(struct device *dev) | ||||
| { | ||||
| 	struct mtk_iommu_data *data = dev_get_drvdata(dev); | ||||
| 	struct mtk_iommu_suspend_reg *reg = &data->reg; | ||||
| 	void __iomem *base = data->base; | ||||
| 
 | ||||
| 	reg->standard_axi_mode = readl_relaxed(base + | ||||
| 					       REG_MMU_STANDARD_AXI_MODE); | ||||
| 	reg->dcm_dis = readl_relaxed(base + REG_MMU_DCM); | ||||
| 	reg->ctrl_reg = readl_relaxed(base + REG_MMU_CTRL_REG); | ||||
| 	reg->int_control0 = readl_relaxed(base + REG_MMU_INT_CONTROL); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int __maybe_unused mtk_iommu_resume(struct device *dev) | ||||
| { | ||||
| 	struct mtk_iommu_data *data = dev_get_drvdata(dev); | ||||
| 	struct mtk_iommu_suspend_reg *reg = &data->reg; | ||||
| 	void __iomem *base = data->base; | ||||
| 
 | ||||
| 	writel_relaxed(data->m4u_dom->pgt_pa, base + REG_MMU_PT_BASE_ADDR); | ||||
| 	writel_relaxed(reg->standard_axi_mode, | ||||
| 		       base + REG_MMU_STANDARD_AXI_MODE); | ||||
| 	writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM); | ||||
| 	writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG); | ||||
| 	writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL); | ||||
| 	writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct dev_pm_ops mtk_iommu_pm_ops = { | ||||
| 	SET_SYSTEM_SLEEP_PM_OPS(mtk_iommu_suspend, mtk_iommu_resume) | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver mtk_iommu_driver = { | ||||
| 	.probe	= mtk_iommu_probe, | ||||
| 	.remove	= mtk_iommu_remove, | ||||
| 	.driver	= { | ||||
| 		.name = "mtk-iommu", | ||||
| 		.of_match_table = mtk_iommu_of_ids, | ||||
| 		.pm = &mtk_iommu_pm_ops, | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| static int __init m4u_init(void) | ||||
| { | ||||
| 	return platform_driver_register(&mtk_iommu_driver); | ||||
| } | ||||
| 
 | ||||
| static void __exit m4u_exit(void) | ||||
| { | ||||
| 	return platform_driver_unregister(&mtk_iommu_driver); | ||||
| } | ||||
| 
 | ||||
| subsys_initcall(m4u_init); | ||||
| module_exit(m4u_exit); | ||||
| 
 | ||||
| MODULE_DESCRIPTION("IOMMU API for MTK architected m4u v1 implementations"); | ||||
| MODULE_AUTHOR("Honghui Zhang <honghui.zhang@mediatek.com>"); | ||||
| MODULE_LICENSE("GPL v2"); | ||||
|  | @ -4,11 +4,10 @@ | |||
|  * published by the Free Software Foundation. | ||||
|  */ | ||||
| 
 | ||||
| #include <asm/cacheflush.h> | ||||
| #include <asm/pgtable.h> | ||||
| #include <linux/compiler.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/dma-iommu.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/io.h> | ||||
|  | @ -77,7 +76,9 @@ | |||
| 
 | ||||
| struct rk_iommu_domain { | ||||
| 	struct list_head iommus; | ||||
| 	struct platform_device *pdev; | ||||
| 	u32 *dt; /* page directory table */ | ||||
| 	dma_addr_t dt_dma; | ||||
| 	spinlock_t iommus_lock; /* lock for iommus list */ | ||||
| 	spinlock_t dt_lock; /* lock for modifying page directory table */ | ||||
| 
 | ||||
|  | @ -93,14 +94,12 @@ struct rk_iommu { | |||
| 	struct iommu_domain *domain; /* domain to which iommu is attached */ | ||||
| }; | ||||
| 
 | ||||
| static inline void rk_table_flush(u32 *va, unsigned int count) | ||||
| static inline void rk_table_flush(struct rk_iommu_domain *dom, dma_addr_t dma, | ||||
| 				  unsigned int count) | ||||
| { | ||||
| 	phys_addr_t pa_start = virt_to_phys(va); | ||||
| 	phys_addr_t pa_end = virt_to_phys(va + count); | ||||
| 	size_t size = pa_end - pa_start; | ||||
| 	size_t size = count * sizeof(u32); /* count of u32 entry */ | ||||
| 
 | ||||
| 	__cpuc_flush_dcache_area(va, size); | ||||
| 	outer_flush_range(pa_start, pa_end); | ||||
| 	dma_sync_single_for_device(&dom->pdev->dev, dma, size, DMA_TO_DEVICE); | ||||
| } | ||||
| 
 | ||||
| static struct rk_iommu_domain *to_rk_domain(struct iommu_domain *dom) | ||||
|  | @ -183,10 +182,9 @@ static inline bool rk_dte_is_pt_valid(u32 dte) | |||
| 	return dte & RK_DTE_PT_VALID; | ||||
| } | ||||
| 
 | ||||
| static u32 rk_mk_dte(u32 *pt) | ||||
| static inline u32 rk_mk_dte(dma_addr_t pt_dma) | ||||
| { | ||||
| 	phys_addr_t pt_phys = virt_to_phys(pt); | ||||
| 	return (pt_phys & RK_DTE_PT_ADDRESS_MASK) | RK_DTE_PT_VALID; | ||||
| 	return (pt_dma & RK_DTE_PT_ADDRESS_MASK) | RK_DTE_PT_VALID; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -603,13 +601,16 @@ static void rk_iommu_zap_iova_first_last(struct rk_iommu_domain *rk_domain, | |||
| static u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain, | ||||
| 				  dma_addr_t iova) | ||||
| { | ||||
| 	struct device *dev = &rk_domain->pdev->dev; | ||||
| 	u32 *page_table, *dte_addr; | ||||
| 	u32 dte; | ||||
| 	u32 dte_index, dte; | ||||
| 	phys_addr_t pt_phys; | ||||
| 	dma_addr_t pt_dma; | ||||
| 
 | ||||
| 	assert_spin_locked(&rk_domain->dt_lock); | ||||
| 
 | ||||
| 	dte_addr = &rk_domain->dt[rk_iova_dte_index(iova)]; | ||||
| 	dte_index = rk_iova_dte_index(iova); | ||||
| 	dte_addr = &rk_domain->dt[dte_index]; | ||||
| 	dte = *dte_addr; | ||||
| 	if (rk_dte_is_pt_valid(dte)) | ||||
| 		goto done; | ||||
|  | @ -618,19 +619,27 @@ static u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain, | |||
| 	if (!page_table) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	dte = rk_mk_dte(page_table); | ||||
| 	pt_dma = dma_map_single(dev, page_table, SPAGE_SIZE, DMA_TO_DEVICE); | ||||
| 	if (dma_mapping_error(dev, pt_dma)) { | ||||
| 		dev_err(dev, "DMA mapping error while allocating page table\n"); | ||||
| 		free_page((unsigned long)page_table); | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 	} | ||||
| 
 | ||||
| 	dte = rk_mk_dte(pt_dma); | ||||
| 	*dte_addr = dte; | ||||
| 
 | ||||
| 	rk_table_flush(page_table, NUM_PT_ENTRIES); | ||||
| 	rk_table_flush(dte_addr, 1); | ||||
| 
 | ||||
| 	rk_table_flush(rk_domain, pt_dma, NUM_PT_ENTRIES); | ||||
| 	rk_table_flush(rk_domain, | ||||
| 		       rk_domain->dt_dma + dte_index * sizeof(u32), 1); | ||||
| done: | ||||
| 	pt_phys = rk_dte_pt_address(dte); | ||||
| 	return (u32 *)phys_to_virt(pt_phys); | ||||
| } | ||||
| 
 | ||||
| static size_t rk_iommu_unmap_iova(struct rk_iommu_domain *rk_domain, | ||||
| 				  u32 *pte_addr, dma_addr_t iova, size_t size) | ||||
| 				  u32 *pte_addr, dma_addr_t pte_dma, | ||||
| 				  size_t size) | ||||
| { | ||||
| 	unsigned int pte_count; | ||||
| 	unsigned int pte_total = size / SPAGE_SIZE; | ||||
|  | @ -645,14 +654,14 @@ static size_t rk_iommu_unmap_iova(struct rk_iommu_domain *rk_domain, | |||
| 		pte_addr[pte_count] = rk_mk_pte_invalid(pte); | ||||
| 	} | ||||
| 
 | ||||
| 	rk_table_flush(pte_addr, pte_count); | ||||
| 	rk_table_flush(rk_domain, pte_dma, pte_count); | ||||
| 
 | ||||
| 	return pte_count * SPAGE_SIZE; | ||||
| } | ||||
| 
 | ||||
| static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr, | ||||
| 			     dma_addr_t iova, phys_addr_t paddr, size_t size, | ||||
| 			     int prot) | ||||
| 			     dma_addr_t pte_dma, dma_addr_t iova, | ||||
| 			     phys_addr_t paddr, size_t size, int prot) | ||||
| { | ||||
| 	unsigned int pte_count; | ||||
| 	unsigned int pte_total = size / SPAGE_SIZE; | ||||
|  | @ -671,7 +680,7 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr, | |||
| 		paddr += SPAGE_SIZE; | ||||
| 	} | ||||
| 
 | ||||
| 	rk_table_flush(pte_addr, pte_count); | ||||
| 	rk_table_flush(rk_domain, pte_dma, pte_total); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Zap the first and last iova to evict from iotlb any previously | ||||
|  | @ -684,7 +693,8 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr, | |||
| 	return 0; | ||||
| unwind: | ||||
| 	/* Unmap the range of iovas that we just mapped */ | ||||
| 	rk_iommu_unmap_iova(rk_domain, pte_addr, iova, pte_count * SPAGE_SIZE); | ||||
| 	rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, | ||||
| 			    pte_count * SPAGE_SIZE); | ||||
| 
 | ||||
| 	iova += pte_count * SPAGE_SIZE; | ||||
| 	page_phys = rk_pte_page_address(pte_addr[pte_count]); | ||||
|  | @ -699,8 +709,9 @@ static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova, | |||
| { | ||||
| 	struct rk_iommu_domain *rk_domain = to_rk_domain(domain); | ||||
| 	unsigned long flags; | ||||
| 	dma_addr_t iova = (dma_addr_t)_iova; | ||||
| 	dma_addr_t pte_dma, iova = (dma_addr_t)_iova; | ||||
| 	u32 *page_table, *pte_addr; | ||||
| 	u32 dte_index, pte_index; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&rk_domain->dt_lock, flags); | ||||
|  | @ -718,8 +729,13 @@ static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova, | |||
| 		return PTR_ERR(page_table); | ||||
| 	} | ||||
| 
 | ||||
| 	pte_addr = &page_table[rk_iova_pte_index(iova)]; | ||||
| 	ret = rk_iommu_map_iova(rk_domain, pte_addr, iova, paddr, size, prot); | ||||
| 	dte_index = rk_domain->dt[rk_iova_dte_index(iova)]; | ||||
| 	pte_index = rk_iova_pte_index(iova); | ||||
| 	pte_addr = &page_table[pte_index]; | ||||
| 	pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32); | ||||
| 	ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova, | ||||
| 				paddr, size, prot); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&rk_domain->dt_lock, flags); | ||||
| 
 | ||||
| 	return ret; | ||||
|  | @ -730,7 +746,7 @@ static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova, | |||
| { | ||||
| 	struct rk_iommu_domain *rk_domain = to_rk_domain(domain); | ||||
| 	unsigned long flags; | ||||
| 	dma_addr_t iova = (dma_addr_t)_iova; | ||||
| 	dma_addr_t pte_dma, iova = (dma_addr_t)_iova; | ||||
| 	phys_addr_t pt_phys; | ||||
| 	u32 dte; | ||||
| 	u32 *pte_addr; | ||||
|  | @ -754,7 +770,8 @@ static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova, | |||
| 
 | ||||
| 	pt_phys = rk_dte_pt_address(dte); | ||||
| 	pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova); | ||||
| 	unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, iova, size); | ||||
| 	pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32); | ||||
| 	unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, size); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&rk_domain->dt_lock, flags); | ||||
| 
 | ||||
|  | @ -787,7 +804,6 @@ static int rk_iommu_attach_device(struct iommu_domain *domain, | |||
| 	struct rk_iommu_domain *rk_domain = to_rk_domain(domain); | ||||
| 	unsigned long flags; | ||||
| 	int ret, i; | ||||
| 	phys_addr_t dte_addr; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Allow 'virtual devices' (e.g., drm) to attach to domain. | ||||
|  | @ -807,14 +823,14 @@ static int rk_iommu_attach_device(struct iommu_domain *domain, | |||
| 
 | ||||
| 	iommu->domain = domain; | ||||
| 
 | ||||
| 	ret = devm_request_irq(dev, iommu->irq, rk_iommu_irq, | ||||
| 	ret = devm_request_irq(iommu->dev, iommu->irq, rk_iommu_irq, | ||||
| 			       IRQF_SHARED, dev_name(dev), iommu); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	dte_addr = virt_to_phys(rk_domain->dt); | ||||
| 	for (i = 0; i < iommu->num_mmu; i++) { | ||||
| 		rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, dte_addr); | ||||
| 		rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, | ||||
| 			       rk_domain->dt_dma); | ||||
| 		rk_iommu_base_command(iommu->bases[i], RK_MMU_CMD_ZAP_CACHE); | ||||
| 		rk_iommu_write(iommu->bases[i], RK_MMU_INT_MASK, RK_MMU_IRQ_MASK); | ||||
| 	} | ||||
|  | @ -860,7 +876,7 @@ static void rk_iommu_detach_device(struct iommu_domain *domain, | |||
| 	} | ||||
| 	rk_iommu_disable_stall(iommu); | ||||
| 
 | ||||
| 	devm_free_irq(dev, iommu->irq, iommu); | ||||
| 	devm_free_irq(iommu->dev, iommu->irq, iommu); | ||||
| 
 | ||||
| 	iommu->domain = NULL; | ||||
| 
 | ||||
|  | @ -870,13 +886,29 @@ static void rk_iommu_detach_device(struct iommu_domain *domain, | |||
| static struct iommu_domain *rk_iommu_domain_alloc(unsigned type) | ||||
| { | ||||
| 	struct rk_iommu_domain *rk_domain; | ||||
| 	struct platform_device *pdev; | ||||
| 	struct device *iommu_dev; | ||||
| 
 | ||||
| 	if (type != IOMMU_DOMAIN_UNMANAGED) | ||||
| 	if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	rk_domain = kzalloc(sizeof(*rk_domain), GFP_KERNEL); | ||||
| 	/* Register a pdev per domain, so DMA API can base on this *dev
 | ||||
| 	 * even some virtual master doesn't have an iommu slave | ||||
| 	 */ | ||||
| 	pdev = platform_device_register_simple("rk_iommu_domain", | ||||
| 					       PLATFORM_DEVID_AUTO, NULL, 0); | ||||
| 	if (IS_ERR(pdev)) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	rk_domain = devm_kzalloc(&pdev->dev, sizeof(*rk_domain), GFP_KERNEL); | ||||
| 	if (!rk_domain) | ||||
| 		return NULL; | ||||
| 		goto err_unreg_pdev; | ||||
| 
 | ||||
| 	rk_domain->pdev = pdev; | ||||
| 
 | ||||
| 	if (type == IOMMU_DOMAIN_DMA && | ||||
| 	    iommu_get_dma_cookie(&rk_domain->domain)) | ||||
| 		goto err_unreg_pdev; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * rk32xx iommus use a 2 level pagetable. | ||||
|  | @ -885,18 +917,36 @@ static struct iommu_domain *rk_iommu_domain_alloc(unsigned type) | |||
| 	 */ | ||||
| 	rk_domain->dt = (u32 *)get_zeroed_page(GFP_KERNEL | GFP_DMA32); | ||||
| 	if (!rk_domain->dt) | ||||
| 		goto err_dt; | ||||
| 		goto err_put_cookie; | ||||
| 
 | ||||
| 	rk_table_flush(rk_domain->dt, NUM_DT_ENTRIES); | ||||
| 	iommu_dev = &pdev->dev; | ||||
| 	rk_domain->dt_dma = dma_map_single(iommu_dev, rk_domain->dt, | ||||
| 					   SPAGE_SIZE, DMA_TO_DEVICE); | ||||
| 	if (dma_mapping_error(iommu_dev, rk_domain->dt_dma)) { | ||||
| 		dev_err(iommu_dev, "DMA map error for DT\n"); | ||||
| 		goto err_free_dt; | ||||
| 	} | ||||
| 
 | ||||
| 	rk_table_flush(rk_domain, rk_domain->dt_dma, NUM_DT_ENTRIES); | ||||
| 
 | ||||
| 	spin_lock_init(&rk_domain->iommus_lock); | ||||
| 	spin_lock_init(&rk_domain->dt_lock); | ||||
| 	INIT_LIST_HEAD(&rk_domain->iommus); | ||||
| 
 | ||||
| 	rk_domain->domain.geometry.aperture_start = 0; | ||||
| 	rk_domain->domain.geometry.aperture_end   = DMA_BIT_MASK(32); | ||||
| 	rk_domain->domain.geometry.force_aperture = true; | ||||
| 
 | ||||
| 	return &rk_domain->domain; | ||||
| 
 | ||||
| err_dt: | ||||
| 	kfree(rk_domain); | ||||
| err_free_dt: | ||||
| 	free_page((unsigned long)rk_domain->dt); | ||||
| err_put_cookie: | ||||
| 	if (type == IOMMU_DOMAIN_DMA) | ||||
| 		iommu_put_dma_cookie(&rk_domain->domain); | ||||
| err_unreg_pdev: | ||||
| 	platform_device_unregister(pdev); | ||||
| 
 | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
|  | @ -912,12 +962,20 @@ static void rk_iommu_domain_free(struct iommu_domain *domain) | |||
| 		if (rk_dte_is_pt_valid(dte)) { | ||||
| 			phys_addr_t pt_phys = rk_dte_pt_address(dte); | ||||
| 			u32 *page_table = phys_to_virt(pt_phys); | ||||
| 			dma_unmap_single(&rk_domain->pdev->dev, pt_phys, | ||||
| 					 SPAGE_SIZE, DMA_TO_DEVICE); | ||||
| 			free_page((unsigned long)page_table); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	dma_unmap_single(&rk_domain->pdev->dev, rk_domain->dt_dma, | ||||
| 			 SPAGE_SIZE, DMA_TO_DEVICE); | ||||
| 	free_page((unsigned long)rk_domain->dt); | ||||
| 	kfree(rk_domain); | ||||
| 
 | ||||
| 	if (domain->type == IOMMU_DOMAIN_DMA) | ||||
| 		iommu_put_dma_cookie(&rk_domain->domain); | ||||
| 
 | ||||
| 	platform_device_unregister(rk_domain->pdev); | ||||
| } | ||||
| 
 | ||||
| static bool rk_iommu_is_dev_iommu_master(struct device *dev) | ||||
|  | @ -1022,17 +1080,43 @@ static const struct iommu_ops rk_iommu_ops = { | |||
| 	.detach_dev = rk_iommu_detach_device, | ||||
| 	.map = rk_iommu_map, | ||||
| 	.unmap = rk_iommu_unmap, | ||||
| 	.map_sg = default_iommu_map_sg, | ||||
| 	.add_device = rk_iommu_add_device, | ||||
| 	.remove_device = rk_iommu_remove_device, | ||||
| 	.iova_to_phys = rk_iommu_iova_to_phys, | ||||
| 	.pgsize_bitmap = RK_IOMMU_PGSIZE_BITMAP, | ||||
| }; | ||||
| 
 | ||||
| static int rk_iommu_domain_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device *dev = &pdev->dev; | ||||
| 
 | ||||
| 	dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms), GFP_KERNEL); | ||||
| 	if (!dev->dma_parms) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	/* Set dma_ops for dev, otherwise it would be dummy_dma_ops */ | ||||
| 	arch_setup_dma_ops(dev, 0, DMA_BIT_MASK(32), NULL, false); | ||||
| 
 | ||||
| 	dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); | ||||
| 	dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct platform_driver rk_iommu_domain_driver = { | ||||
| 	.probe = rk_iommu_domain_probe, | ||||
| 	.driver = { | ||||
| 		   .name = "rk_iommu_domain", | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static int rk_iommu_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device *dev = &pdev->dev; | ||||
| 	struct rk_iommu *iommu; | ||||
| 	struct resource *res; | ||||
| 	int num_res = pdev->num_resources; | ||||
| 	int i; | ||||
| 
 | ||||
| 	iommu = devm_kzalloc(dev, sizeof(*iommu), GFP_KERNEL); | ||||
|  | @ -1042,12 +1126,13 @@ static int rk_iommu_probe(struct platform_device *pdev) | |||
| 	platform_set_drvdata(pdev, iommu); | ||||
| 	iommu->dev = dev; | ||||
| 	iommu->num_mmu = 0; | ||||
| 	iommu->bases = devm_kzalloc(dev, sizeof(*iommu->bases) * iommu->num_mmu, | ||||
| 
 | ||||
| 	iommu->bases = devm_kzalloc(dev, sizeof(*iommu->bases) * num_res, | ||||
| 				    GFP_KERNEL); | ||||
| 	if (!iommu->bases) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	for (i = 0; i < pdev->num_resources; i++) { | ||||
| 	for (i = 0; i < num_res; i++) { | ||||
| 		res = platform_get_resource(pdev, IORESOURCE_MEM, i); | ||||
| 		if (!res) | ||||
| 			continue; | ||||
|  | @ -1103,11 +1188,19 @@ static int __init rk_iommu_init(void) | |||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	return platform_driver_register(&rk_iommu_driver); | ||||
| 	ret = platform_driver_register(&rk_iommu_domain_driver); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = platform_driver_register(&rk_iommu_driver); | ||||
| 	if (ret) | ||||
| 		platform_driver_unregister(&rk_iommu_domain_driver); | ||||
| 	return ret; | ||||
| } | ||||
| static void __exit rk_iommu_exit(void) | ||||
| { | ||||
| 	platform_driver_unregister(&rk_iommu_driver); | ||||
| 	platform_driver_unregister(&rk_iommu_domain_driver); | ||||
| } | ||||
| 
 | ||||
| subsys_initcall(rk_iommu_init); | ||||
|  |  | |||
|  | @ -21,21 +21,52 @@ | |||
| #include <linux/platform_device.h> | ||||
| #include <linux/pm_runtime.h> | ||||
| #include <soc/mediatek/smi.h> | ||||
| #include <dt-bindings/memory/mt2701-larb-port.h> | ||||
| 
 | ||||
| #define SMI_LARB_MMU_EN		0xf00 | ||||
| #define REG_SMI_SECUR_CON_BASE		0x5c0 | ||||
| 
 | ||||
| /* every register control 8 port, register offset 0x4 */ | ||||
| #define REG_SMI_SECUR_CON_OFFSET(id)	(((id) >> 3) << 2) | ||||
| #define REG_SMI_SECUR_CON_ADDR(id)	\ | ||||
| 	(REG_SMI_SECUR_CON_BASE + REG_SMI_SECUR_CON_OFFSET(id)) | ||||
| 
 | ||||
| /*
 | ||||
|  * every port have 4 bit to control, bit[port + 3] control virtual or physical, | ||||
|  * bit[port + 2 : port + 1] control the domain, bit[port] control the security | ||||
|  * or non-security. | ||||
|  */ | ||||
| #define SMI_SECUR_CON_VAL_MSK(id)	(~(0xf << (((id) & 0x7) << 2))) | ||||
| #define SMI_SECUR_CON_VAL_VIRT(id)	BIT((((id) & 0x7) << 2) + 3) | ||||
| /* mt2701 domain should be set to 3 */ | ||||
| #define SMI_SECUR_CON_VAL_DOMAIN(id)	(0x3 << ((((id) & 0x7) << 2) + 1)) | ||||
| 
 | ||||
| struct mtk_smi_larb_gen { | ||||
| 	int port_in_larb[MTK_LARB_NR_MAX + 1]; | ||||
| 	void (*config_port)(struct device *); | ||||
| }; | ||||
| 
 | ||||
| struct mtk_smi { | ||||
| 	struct device			*dev; | ||||
| 	struct clk			*clk_apb, *clk_smi; | ||||
| 	struct clk			*clk_async; /*only needed by mt2701*/ | ||||
| 	void __iomem			*smi_ao_base; | ||||
| }; | ||||
| 
 | ||||
| struct mtk_smi_larb { /* larb: local arbiter */ | ||||
| 	struct mtk_smi			smi; | ||||
| 	void __iomem			*base; | ||||
| 	struct device			*smi_common_dev; | ||||
| 	const struct mtk_smi_larb_gen	*larb_gen; | ||||
| 	int				larbid; | ||||
| 	u32				*mmu; | ||||
| }; | ||||
| 
 | ||||
| enum mtk_smi_gen { | ||||
| 	MTK_SMI_GEN1, | ||||
| 	MTK_SMI_GEN2 | ||||
| }; | ||||
| 
 | ||||
| static int mtk_smi_enable(const struct mtk_smi *smi) | ||||
| { | ||||
| 	int ret; | ||||
|  | @ -71,6 +102,7 @@ static void mtk_smi_disable(const struct mtk_smi *smi) | |||
| int mtk_smi_larb_get(struct device *larbdev) | ||||
| { | ||||
| 	struct mtk_smi_larb *larb = dev_get_drvdata(larbdev); | ||||
| 	const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen; | ||||
| 	struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev); | ||||
| 	int ret; | ||||
| 
 | ||||
|  | @ -87,7 +119,7 @@ int mtk_smi_larb_get(struct device *larbdev) | |||
| 	} | ||||
| 
 | ||||
| 	/* Configure the iommu info for this larb */ | ||||
| 	writel(*larb->mmu, larb->base + SMI_LARB_MMU_EN); | ||||
| 	larb_gen->config_port(larbdev); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -126,6 +158,45 @@ mtk_smi_larb_bind(struct device *dev, struct device *master, void *data) | |||
| 	return -ENODEV; | ||||
| } | ||||
| 
 | ||||
| static void mtk_smi_larb_config_port(struct device *dev) | ||||
| { | ||||
| 	struct mtk_smi_larb *larb = dev_get_drvdata(dev); | ||||
| 
 | ||||
| 	writel(*larb->mmu, larb->base + SMI_LARB_MMU_EN); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void mtk_smi_larb_config_port_gen1(struct device *dev) | ||||
| { | ||||
| 	struct mtk_smi_larb *larb = dev_get_drvdata(dev); | ||||
| 	const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen; | ||||
| 	struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev); | ||||
| 	int i, m4u_port_id, larb_port_num; | ||||
| 	u32 sec_con_val, reg_val; | ||||
| 
 | ||||
| 	m4u_port_id = larb_gen->port_in_larb[larb->larbid]; | ||||
| 	larb_port_num = larb_gen->port_in_larb[larb->larbid + 1] | ||||
| 			- larb_gen->port_in_larb[larb->larbid]; | ||||
| 
 | ||||
| 	for (i = 0; i < larb_port_num; i++, m4u_port_id++) { | ||||
| 		if (*larb->mmu & BIT(i)) { | ||||
| 			/* bit[port + 3] controls the virtual or physical */ | ||||
| 			sec_con_val = SMI_SECUR_CON_VAL_VIRT(m4u_port_id); | ||||
| 		} else { | ||||
| 			/* do not need to enable m4u for this port */ | ||||
| 			continue; | ||||
| 		} | ||||
| 		reg_val = readl(common->smi_ao_base | ||||
| 			+ REG_SMI_SECUR_CON_ADDR(m4u_port_id)); | ||||
| 		reg_val &= SMI_SECUR_CON_VAL_MSK(m4u_port_id); | ||||
| 		reg_val |= sec_con_val; | ||||
| 		reg_val |= SMI_SECUR_CON_VAL_DOMAIN(m4u_port_id); | ||||
| 		writel(reg_val, | ||||
| 			common->smi_ao_base | ||||
| 			+ REG_SMI_SECUR_CON_ADDR(m4u_port_id)); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| mtk_smi_larb_unbind(struct device *dev, struct device *master, void *data) | ||||
| { | ||||
|  | @ -137,6 +208,31 @@ static const struct component_ops mtk_smi_larb_component_ops = { | |||
| 	.unbind = mtk_smi_larb_unbind, | ||||
| }; | ||||
| 
 | ||||
| static const struct mtk_smi_larb_gen mtk_smi_larb_mt8173 = { | ||||
| 	/* mt8173 do not need the port in larb */ | ||||
| 	.config_port = mtk_smi_larb_config_port, | ||||
| }; | ||||
| 
 | ||||
| static const struct mtk_smi_larb_gen mtk_smi_larb_mt2701 = { | ||||
| 	.port_in_larb = { | ||||
| 		LARB0_PORT_OFFSET, LARB1_PORT_OFFSET, | ||||
| 		LARB2_PORT_OFFSET, LARB3_PORT_OFFSET | ||||
| 	}, | ||||
| 	.config_port = mtk_smi_larb_config_port_gen1, | ||||
| }; | ||||
| 
 | ||||
| static const struct of_device_id mtk_smi_larb_of_ids[] = { | ||||
| 	{ | ||||
| 		.compatible = "mediatek,mt8173-smi-larb", | ||||
| 		.data = &mtk_smi_larb_mt8173 | ||||
| 	}, | ||||
| 	{ | ||||
| 		.compatible = "mediatek,mt2701-smi-larb", | ||||
| 		.data = &mtk_smi_larb_mt2701 | ||||
| 	}, | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
| static int mtk_smi_larb_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct mtk_smi_larb *larb; | ||||
|  | @ -144,14 +240,20 @@ static int mtk_smi_larb_probe(struct platform_device *pdev) | |||
| 	struct device *dev = &pdev->dev; | ||||
| 	struct device_node *smi_node; | ||||
| 	struct platform_device *smi_pdev; | ||||
| 	const struct of_device_id *of_id; | ||||
| 
 | ||||
| 	if (!dev->pm_domain) | ||||
| 		return -EPROBE_DEFER; | ||||
| 
 | ||||
| 	of_id = of_match_node(mtk_smi_larb_of_ids, pdev->dev.of_node); | ||||
| 	if (!of_id) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	larb = devm_kzalloc(dev, sizeof(*larb), GFP_KERNEL); | ||||
| 	if (!larb) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	larb->larb_gen = of_id->data; | ||||
| 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| 	larb->base = devm_ioremap_resource(dev, res); | ||||
| 	if (IS_ERR(larb->base)) | ||||
|  | @ -191,11 +293,6 @@ static int mtk_smi_larb_remove(struct platform_device *pdev) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct of_device_id mtk_smi_larb_of_ids[] = { | ||||
| 	{ .compatible = "mediatek,mt8173-smi-larb",}, | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver mtk_smi_larb_driver = { | ||||
| 	.probe	= mtk_smi_larb_probe, | ||||
| 	.remove	= mtk_smi_larb_remove, | ||||
|  | @ -205,10 +302,25 @@ static struct platform_driver mtk_smi_larb_driver = { | |||
| 	} | ||||
| }; | ||||
| 
 | ||||
| static const struct of_device_id mtk_smi_common_of_ids[] = { | ||||
| 	{ | ||||
| 		.compatible = "mediatek,mt8173-smi-common", | ||||
| 		.data = (void *)MTK_SMI_GEN2 | ||||
| 	}, | ||||
| 	{ | ||||
| 		.compatible = "mediatek,mt2701-smi-common", | ||||
| 		.data = (void *)MTK_SMI_GEN1 | ||||
| 	}, | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
| static int mtk_smi_common_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device *dev = &pdev->dev; | ||||
| 	struct mtk_smi *common; | ||||
| 	struct resource *res; | ||||
| 	const struct of_device_id *of_id; | ||||
| 	enum mtk_smi_gen smi_gen; | ||||
| 
 | ||||
| 	if (!dev->pm_domain) | ||||
| 		return -EPROBE_DEFER; | ||||
|  | @ -226,6 +338,29 @@ static int mtk_smi_common_probe(struct platform_device *pdev) | |||
| 	if (IS_ERR(common->clk_smi)) | ||||
| 		return PTR_ERR(common->clk_smi); | ||||
| 
 | ||||
| 	of_id = of_match_node(mtk_smi_common_of_ids, pdev->dev.of_node); | ||||
| 	if (!of_id) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * for mtk smi gen 1, we need to get the ao(always on) base to config | ||||
| 	 * m4u port, and we need to enable the aync clock for transform the smi | ||||
| 	 * clock into emi clock domain, but for mtk smi gen2, there's no smi ao | ||||
| 	 * base. | ||||
| 	 */ | ||||
| 	smi_gen = (enum mtk_smi_gen)of_id->data; | ||||
| 	if (smi_gen == MTK_SMI_GEN1) { | ||||
| 		res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| 		common->smi_ao_base = devm_ioremap_resource(dev, res); | ||||
| 		if (IS_ERR(common->smi_ao_base)) | ||||
| 			return PTR_ERR(common->smi_ao_base); | ||||
| 
 | ||||
| 		common->clk_async = devm_clk_get(dev, "async"); | ||||
| 		if (IS_ERR(common->clk_async)) | ||||
| 			return PTR_ERR(common->clk_async); | ||||
| 
 | ||||
| 		clk_prepare_enable(common->clk_async); | ||||
| 	} | ||||
| 	pm_runtime_enable(dev); | ||||
| 	platform_set_drvdata(pdev, common); | ||||
| 	return 0; | ||||
|  | @ -237,11 +372,6 @@ static int mtk_smi_common_remove(struct platform_device *pdev) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct of_device_id mtk_smi_common_of_ids[] = { | ||||
| 	{ .compatible = "mediatek,mt8173-smi-common", }, | ||||
| 	{} | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver mtk_smi_common_driver = { | ||||
| 	.probe	= mtk_smi_common_probe, | ||||
| 	.remove = mtk_smi_common_remove, | ||||
|  | @ -272,4 +402,5 @@ err_unreg_smi: | |||
| 	platform_driver_unregister(&mtk_smi_common_driver); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| subsys_initcall(mtk_smi_init); | ||||
|  |  | |||
							
								
								
									
										85
									
								
								include/dt-bindings/memory/mt2701-larb-port.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								include/dt-bindings/memory/mt2701-larb-port.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,85 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2015 MediaTek Inc. | ||||
|  * Author: Honghui Zhang <honghui.zhang@mediatek.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2 as | ||||
|  * published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _MT2701_LARB_PORT_H_ | ||||
| #define _MT2701_LARB_PORT_H_ | ||||
| 
 | ||||
| /*
 | ||||
|  * Mediatek m4u generation 1 such as mt2701 has flat m4u port numbers, | ||||
|  * the first port's id for larb[N] would be the last port's id of larb[N - 1] | ||||
|  * plus one while larb[0]'s first port number is 0. The definition of | ||||
|  * MT2701_M4U_ID_LARBx is following HW register spec. | ||||
|  * But m4u generation 2 like mt8173 have different port number, it use fixed | ||||
|  * offset for each larb, the first port's id for larb[N] would be (N * 32). | ||||
|  */ | ||||
| #define LARB0_PORT_OFFSET		0 | ||||
| #define LARB1_PORT_OFFSET		11 | ||||
| #define LARB2_PORT_OFFSET		21 | ||||
| #define LARB3_PORT_OFFSET		43 | ||||
| 
 | ||||
| #define MT2701_M4U_ID_LARB0(port)	((port) + LARB0_PORT_OFFSET) | ||||
| #define MT2701_M4U_ID_LARB1(port)	((port) + LARB1_PORT_OFFSET) | ||||
| #define MT2701_M4U_ID_LARB2(port)	((port) + LARB2_PORT_OFFSET) | ||||
| 
 | ||||
| /* Port define for larb0 */ | ||||
| #define MT2701_M4U_PORT_DISP_OVL_0		MT2701_M4U_ID_LARB0(0) | ||||
| #define MT2701_M4U_PORT_DISP_RDMA1		MT2701_M4U_ID_LARB0(1) | ||||
| #define MT2701_M4U_PORT_DISP_RDMA		MT2701_M4U_ID_LARB0(2) | ||||
| #define MT2701_M4U_PORT_DISP_WDMA		MT2701_M4U_ID_LARB0(3) | ||||
| #define MT2701_M4U_PORT_MM_CMDQ			MT2701_M4U_ID_LARB0(4) | ||||
| #define MT2701_M4U_PORT_MDP_RDMA		MT2701_M4U_ID_LARB0(5) | ||||
| #define MT2701_M4U_PORT_MDP_WDMA		MT2701_M4U_ID_LARB0(6) | ||||
| #define MT2701_M4U_PORT_MDP_ROTO		MT2701_M4U_ID_LARB0(7) | ||||
| #define MT2701_M4U_PORT_MDP_ROTCO		MT2701_M4U_ID_LARB0(8) | ||||
| #define MT2701_M4U_PORT_MDP_ROTVO		MT2701_M4U_ID_LARB0(9) | ||||
| #define MT2701_M4U_PORT_MDP_RDMA1		MT2701_M4U_ID_LARB0(10) | ||||
| 
 | ||||
| /* Port define for larb1 */ | ||||
| #define MT2701_M4U_PORT_VDEC_MC_EXT		MT2701_M4U_ID_LARB1(0) | ||||
| #define MT2701_M4U_PORT_VDEC_PP_EXT		MT2701_M4U_ID_LARB1(1) | ||||
| #define MT2701_M4U_PORT_VDEC_PPWRAP_EXT		MT2701_M4U_ID_LARB1(2) | ||||
| #define MT2701_M4U_PORT_VDEC_AVC_MV_EXT		MT2701_M4U_ID_LARB1(3) | ||||
| #define MT2701_M4U_PORT_VDEC_PRED_RD_EXT	MT2701_M4U_ID_LARB1(4) | ||||
| #define MT2701_M4U_PORT_VDEC_PRED_WR_EXT	MT2701_M4U_ID_LARB1(5) | ||||
| #define MT2701_M4U_PORT_VDEC_VLD_EXT		MT2701_M4U_ID_LARB1(6) | ||||
| #define MT2701_M4U_PORT_VDEC_VLD2_EXT		MT2701_M4U_ID_LARB1(7) | ||||
| #define MT2701_M4U_PORT_VDEC_TILE_EXT		MT2701_M4U_ID_LARB1(8) | ||||
| #define MT2701_M4U_PORT_VDEC_IMG_RESZ_EXT	MT2701_M4U_ID_LARB1(9) | ||||
| 
 | ||||
| /* Port define for larb2 */ | ||||
| #define MT2701_M4U_PORT_VENC_RCPU		MT2701_M4U_ID_LARB2(0) | ||||
| #define MT2701_M4U_PORT_VENC_REC_FRM		MT2701_M4U_ID_LARB2(1) | ||||
| #define MT2701_M4U_PORT_VENC_BSDMA		MT2701_M4U_ID_LARB2(2) | ||||
| #define MT2701_M4U_PORT_JPGENC_RDMA		MT2701_M4U_ID_LARB2(3) | ||||
| #define MT2701_M4U_PORT_VENC_LT_RCPU		MT2701_M4U_ID_LARB2(4) | ||||
| #define MT2701_M4U_PORT_VENC_LT_REC_FRM		MT2701_M4U_ID_LARB2(5) | ||||
| #define MT2701_M4U_PORT_VENC_LT_BSDMA		MT2701_M4U_ID_LARB2(6) | ||||
| #define MT2701_M4U_PORT_JPGDEC_BSDMA		MT2701_M4U_ID_LARB2(7) | ||||
| #define MT2701_M4U_PORT_VENC_SV_COMV		MT2701_M4U_ID_LARB2(8) | ||||
| #define MT2701_M4U_PORT_VENC_RD_COMV		MT2701_M4U_ID_LARB2(9) | ||||
| #define MT2701_M4U_PORT_JPGENC_BSDMA		MT2701_M4U_ID_LARB2(10) | ||||
| #define MT2701_M4U_PORT_VENC_CUR_LUMA		MT2701_M4U_ID_LARB2(11) | ||||
| #define MT2701_M4U_PORT_VENC_CUR_CHROMA		MT2701_M4U_ID_LARB2(12) | ||||
| #define MT2701_M4U_PORT_VENC_REF_LUMA		MT2701_M4U_ID_LARB2(13) | ||||
| #define MT2701_M4U_PORT_VENC_REF_CHROMA		MT2701_M4U_ID_LARB2(14) | ||||
| #define MT2701_M4U_PORT_IMG_RESZ		MT2701_M4U_ID_LARB2(15) | ||||
| #define MT2701_M4U_PORT_VENC_LT_SV_COMV		MT2701_M4U_ID_LARB2(16) | ||||
| #define MT2701_M4U_PORT_VENC_LT_RD_COMV		MT2701_M4U_ID_LARB2(17) | ||||
| #define MT2701_M4U_PORT_VENC_LT_CUR_LUMA	MT2701_M4U_ID_LARB2(18) | ||||
| #define MT2701_M4U_PORT_VENC_LT_CUR_CHROMA	MT2701_M4U_ID_LARB2(19) | ||||
| #define MT2701_M4U_PORT_VENC_LT_REF_LUMA	MT2701_M4U_ID_LARB2(20) | ||||
| #define MT2701_M4U_PORT_VENC_LT_REF_CHROMA	MT2701_M4U_ID_LARB2(21) | ||||
| #define MT2701_M4U_PORT_JPGDEC_WDMA		MT2701_M4U_ID_LARB2(22) | ||||
| 
 | ||||
| #endif | ||||
|  | @ -152,6 +152,7 @@ struct iommu_dm_region { | |||
|  * @domain_set_attr: Change domain attributes | ||||
|  * @get_dm_regions: Request list of direct mapping requirements for a device | ||||
|  * @put_dm_regions: Free list of direct mapping requirements for a device | ||||
|  * @apply_dm_region: Temporary helper call-back for iova reserved ranges | ||||
|  * @domain_window_enable: Configure and enable a particular window for a domain | ||||
|  * @domain_window_disable: Disable a particular window for a domain | ||||
|  * @domain_set_windows: Set the number of windows for a domain | ||||
|  | @ -186,6 +187,8 @@ struct iommu_ops { | |||
| 	/* Request/Free a list of direct mapping requirements for a device */ | ||||
| 	void (*get_dm_regions)(struct device *dev, struct list_head *list); | ||||
| 	void (*put_dm_regions)(struct device *dev, struct list_head *list); | ||||
| 	void (*apply_dm_region)(struct device *dev, struct iommu_domain *domain, | ||||
| 				struct iommu_dm_region *region); | ||||
| 
 | ||||
| 	/* Window handling functions */ | ||||
| 	int (*domain_window_enable)(struct iommu_domain *domain, u32 wnd_nr, | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Joerg Roedel
						Joerg Roedel