IOMMU Updates for Linux v5.1
Including: - A big cleanup and optimization patch-set for the Tegra GART driver - Documentation updates and fixes for the IOMMU-API - Support for page request in Intel VT-d scalable mode - Intel VT-d dma_[un]map_resource() support - Updates to the ATS enabling code for PCI (acked by Bjorn) and Intel VT-d to align with the latest version of the ATS spec - Relaxed IRQ source checking in the Intel VT-d driver for some aliased devices, needed for future devices which send IRQ messages from more than on request-ID - IRQ remapping driver for Hyper-V - Patches to make generic IOVA and IO-Page-Table code usable outside of the IOMMU code - Various other small fixes and cleanups -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEr9jSbILcajRFYWYyK/BELZcBGuMFAlyCNlIACgkQK/BELZcB GuNDiRAAscgYj0BdqpZVUNHl4PySR12QJpS1myl/OC4HEbdB/EOh+bYT4Q1vptCU GNK6Gt9SVfcbtWrLiGfcP9ODXmbqZ6AIOIbHKv9cvw1mnyYAtVvT/kck7B/W5jEr /aP/5RTO7XcqscWO44zBkrtLFupegtpQFB0jXYTJYTrwQoNKRqCUqfetZGzMkXjL x/h7kFTTIRcVP8RFcOeAMwC6EieaI8z8HN976Gu7xSV8g0VJqoNsBN8jbUuBh5AN oPyd9nl1KBcIQEC1HsbN8I5wIhTh1sJ2UDqFHAgtlnO59zWHORuFUUt6SXbC9UqJ okJTzFp9Dh2BqmFPXxBTxAf3j+eJP2XPpDI9Ask6SytEPhgw39fdlOOn2MWfSFoW TaBJ4ww/r98GzVxCP7Up98xFZuHGDICL3/M7Mk3mRac/lgbNRbtfcBa5NV4fyQhY 184t656Zm/9gdWgGAvYQtApr6/iI+wRMLkIwuw63wqH09yfbDcpTOo6DEQE3B5KR 4H1qSIiVGVVZlWQateR6N32ZmY4dWzpnL2b8CfsdBytzHHFb/c3dPnZB8fxx9mwF onyvjg9nkIiv7mdcN4Ox2WXrAExTeSftyPajN0WWawNJU3uPTBgNrqNHyWSkiaN4 dAvEepfGuFQGz2Fj03Pv7OqY8veyRezErVRLwiMJRNyy7pi6Wng= =cKsD -----END PGP SIGNATURE----- Merge tag 'iommu-updates-v5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu Pull IOMMU updates from Joerg Roedel: - A big cleanup and optimization patch-set for the Tegra GART driver - Documentation updates and fixes for the IOMMU-API - Support for page request in Intel VT-d scalable mode - Intel VT-d dma_[un]map_resource() support - Updates to the ATS enabling code for PCI (acked by Bjorn) and Intel VT-d to align with the latest version of the ATS spec - Relaxed IRQ source checking in the Intel VT-d driver for some aliased devices, needed for future devices which send IRQ messages from more than on request-ID - IRQ remapping driver for Hyper-V - Patches to make generic IOVA and IO-Page-Table code usable outside of the IOMMU code - Various other small fixes and cleanups * tag 'iommu-updates-v5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu: (60 commits) iommu/vt-d: Get domain ID before clear pasid entry iommu/vt-d: Fix NULL pointer reference in intel_svm_bind_mm() iommu/vt-d: Set context field after value initialized iommu/vt-d: Disable ATS support on untrusted devices iommu/mediatek: Fix semicolon code style issue MAINTAINERS: Add Hyper-V IOMMU driver into Hyper-V CORE AND DRIVERS scope iommu/hyper-v: Add Hyper-V stub IOMMU driver x86/Hyper-V: Set x2apic destination mode to physical when x2apic is available PCI/ATS: Add inline to pci_prg_resp_pasid_required() iommu/vt-d: Check identity map for hot-added devices iommu: Fix IOMMU debugfs fallout iommu: Document iommu_ops.is_attach_deferred() iommu: Document iommu_ops.iotlb_sync_map() iommu/vt-d: Enable ATS only if the device uses page aligned address. PCI/ATS: Add pci_ats_page_aligned() interface iommu/vt-d: Fix PRI/PASID dependency issue. PCI/ATS: Add pci_prg_resp_pasid_required() interface. iommu/vt-d: Allow interrupts from the entire bus for aliased devices iommu/vt-d: Add helper to set an IRTE to verify only the bus number iommu: Fix flush_tlb_all typo ...
This commit is contained in:
		
						commit
						bb97be23db
					
				| @ -1,14 +0,0 @@ | ||||
| NVIDIA Tegra 20 GART | ||||
| 
 | ||||
| Required properties: | ||||
| - compatible: "nvidia,tegra20-gart" | ||||
| - reg: Two pairs of cells specifying the physical address and size of | ||||
|   the memory controller registers and the GART aperture respectively. | ||||
| 
 | ||||
| Example: | ||||
| 
 | ||||
| 	gart { | ||||
| 		compatible = "nvidia,tegra20-gart"; | ||||
| 		reg = <0x7000f024 0x00000018	/* controller registers */ | ||||
| 		       0x58000000 0x02000000>;	/* GART aperture */ | ||||
| 	}; | ||||
| @ -1,26 +1,37 @@ | ||||
| NVIDIA Tegra20 MC(Memory Controller) | ||||
| 
 | ||||
| Required properties: | ||||
| - compatible : "nvidia,tegra20-mc" | ||||
| - reg : Should contain 2 register ranges(address and length); see the | ||||
|   example below. Note that the MC registers are interleaved with the | ||||
|   GART registers, and hence must be represented as multiple ranges. | ||||
| - compatible : "nvidia,tegra20-mc-gart" | ||||
| - reg : Should contain 2 register ranges: physical base address and length of | ||||
|   the controller's registers and the GART aperture respectively. | ||||
| - clocks: Must contain an entry for each entry in clock-names. | ||||
|   See ../clocks/clock-bindings.txt for details. | ||||
| - clock-names: Must include the following entries: | ||||
|   - mc: the module's clock input | ||||
| - interrupts : Should contain MC General interrupt. | ||||
| - #reset-cells : Should be 1. This cell represents memory client module ID. | ||||
|   The assignments may be found in header file <dt-bindings/memory/tegra20-mc.h> | ||||
|   or in the TRM documentation. | ||||
| - #iommu-cells: Should be 0. This cell represents the number of cells in an | ||||
|   IOMMU specifier needed to encode an address. GART supports only a single | ||||
|   address space that is shared by all devices, therefore no additional | ||||
|   information needed for the address encoding. | ||||
| 
 | ||||
| Example: | ||||
| 	mc: memory-controller@7000f000 { | ||||
| 		compatible = "nvidia,tegra20-mc"; | ||||
| 		reg = <0x7000f000 0x024 | ||||
| 		       0x7000f03c 0x3c4>; | ||||
| 		interrupts = <0 77 0x04>; | ||||
| 		compatible = "nvidia,tegra20-mc-gart"; | ||||
| 		reg = <0x7000f000 0x400		/* controller registers */ | ||||
| 		       0x58000000 0x02000000>;	/* GART aperture */ | ||||
| 		clocks = <&tegra_car TEGRA20_CLK_MC>; | ||||
| 		clock-names = "mc"; | ||||
| 		interrupts = <GIC_SPI 77 0x04>; | ||||
| 		#reset-cells = <1>; | ||||
| 		#iommu-cells = <0>; | ||||
| 	}; | ||||
| 
 | ||||
| 	video-codec@6001a000 { | ||||
| 		compatible = "nvidia,tegra20-vde"; | ||||
| 		... | ||||
| 		resets = <&mc TEGRA20_MC_RESET_VDE>; | ||||
| 		iommus = <&mc>; | ||||
| 	}; | ||||
|  | ||||
| @ -7170,6 +7170,7 @@ F:	drivers/net/hyperv/ | ||||
| F:	drivers/scsi/storvsc_drv.c | ||||
| F:	drivers/uio/uio_hv_generic.c | ||||
| F:	drivers/video/fbdev/hyperv_fb.c | ||||
| F:	drivers/iommu/hyperv_iommu.c | ||||
| F:	net/vmw_vsock/hyperv_transport.c | ||||
| F:	include/linux/hyperv.h | ||||
| F:	include/uapi/linux/hyperv.h | ||||
|  | ||||
| @ -616,17 +616,14 @@ | ||||
| 	}; | ||||
| 
 | ||||
| 	mc: memory-controller@7000f000 { | ||||
| 		compatible = "nvidia,tegra20-mc"; | ||||
| 		reg = <0x7000f000 0x024 | ||||
| 		       0x7000f03c 0x3c4>; | ||||
| 		compatible = "nvidia,tegra20-mc-gart"; | ||||
| 		reg = <0x7000f000 0x400		/* controller registers */ | ||||
| 		       0x58000000 0x02000000>;	/* GART aperture */ | ||||
| 		clocks = <&tegra_car TEGRA20_CLK_MC>; | ||||
| 		clock-names = "mc"; | ||||
| 		interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>; | ||||
| 		#reset-cells = <1>; | ||||
| 	}; | ||||
| 
 | ||||
| 	iommu@7000f024 { | ||||
| 		compatible = "nvidia,tegra20-gart"; | ||||
| 		reg = <0x7000f024 0x00000018	/* controller registers */ | ||||
| 		       0x58000000 0x02000000>;	/* GART aperture */ | ||||
| 		#iommu-cells = <0>; | ||||
| 	}; | ||||
| 
 | ||||
| 	memory-controller@7000f400 { | ||||
|  | ||||
| @ -328,6 +328,18 @@ static void __init ms_hyperv_init_platform(void) | ||||
| # ifdef CONFIG_SMP | ||||
| 	smp_ops.smp_prepare_boot_cpu = hv_smp_prepare_boot_cpu; | ||||
| # endif | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Hyper-V doesn't provide irq remapping for IO-APIC. To enable x2apic, | ||||
| 	 * set x2apic destination mode to physcial mode when x2apic is available | ||||
| 	 * and Hyper-V IOMMU driver makes sure cpus assigned with IO-APIC irqs | ||||
| 	 * have 8-bit APIC id. | ||||
| 	 */ | ||||
| # ifdef CONFIG_X86_X2APIC | ||||
| 	if (x2apic_supported()) | ||||
| 		x2apic_phys = 1; | ||||
| # endif | ||||
| 
 | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -56,7 +56,7 @@ obj-y				+= tty/ | ||||
| obj-y				+= char/ | ||||
| 
 | ||||
| # iommu/ comes before gpu as gpu are using iommu controllers
 | ||||
| obj-$(CONFIG_IOMMU_SUPPORT)	+= iommu/ | ||||
| obj-y				+= iommu/ | ||||
| 
 | ||||
| # gpu/ comes after char for AGP vs DRM startup and after iommu
 | ||||
| obj-y				+= gpu/ | ||||
|  | ||||
| @ -1,3 +1,7 @@ | ||||
| # The IOVA library may also be used by non-IOMMU_API users | ||||
| config IOMMU_IOVA | ||||
| 	tristate | ||||
| 
 | ||||
| # IOMMU_API always gets selected by whoever wants it. | ||||
| config IOMMU_API | ||||
| 	bool | ||||
| @ -81,9 +85,6 @@ config IOMMU_DEFAULT_PASSTHROUGH | ||||
| 
 | ||||
| 	  If unsure, say N here. | ||||
| 
 | ||||
| config IOMMU_IOVA | ||||
| 	tristate | ||||
| 
 | ||||
| config OF_IOMMU | ||||
|        def_bool y | ||||
|        depends on OF && IOMMU_API | ||||
| @ -282,6 +283,7 @@ config ROCKCHIP_IOMMU | ||||
| config TEGRA_IOMMU_GART | ||||
| 	bool "Tegra GART IOMMU Support" | ||||
| 	depends on ARCH_TEGRA_2x_SOC | ||||
| 	depends on TEGRA_MC | ||||
| 	select IOMMU_API | ||||
| 	help | ||||
| 	  Enables support for remapping discontiguous physical memory | ||||
| @ -435,4 +437,13 @@ config QCOM_IOMMU | ||||
| 	help | ||||
| 	  Support for IOMMU on certain Qualcomm SoCs. | ||||
| 
 | ||||
| config HYPERV_IOMMU | ||||
| 	bool "Hyper-V x2APIC IRQ Handling" | ||||
| 	depends on HYPERV | ||||
| 	select IOMMU_API | ||||
| 	default HYPERV | ||||
| 	help | ||||
| 	  Stub IOMMU driver to handle IRQs as to allow Hyper-V Linux | ||||
| 	  guests to run with x2APIC mode enabled. | ||||
| 
 | ||||
| endif # IOMMU_SUPPORT | ||||
|  | ||||
| @ -32,3 +32,4 @@ obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o | ||||
| obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o | ||||
| obj-$(CONFIG_S390_IOMMU) += s390-iommu.o | ||||
| obj-$(CONFIG_QCOM_IOMMU) += qcom_iommu.o | ||||
| obj-$(CONFIG_HYPERV_IOMMU) += hyperv-iommu.o | ||||
|  | ||||
| @ -18,6 +18,7 @@ | ||||
|  */ | ||||
| 
 | ||||
| #define pr_fmt(fmt)     "AMD-Vi: " fmt | ||||
| #define dev_fmt(fmt)    pr_fmt(fmt) | ||||
| 
 | ||||
| #include <linux/ratelimit.h> | ||||
| #include <linux/pci.h> | ||||
| @ -279,10 +280,10 @@ static u16 get_alias(struct device *dev) | ||||
| 		return pci_alias; | ||||
| 	} | ||||
| 
 | ||||
| 	pr_info("Using IVRS reported alias %02x:%02x.%d " | ||||
| 		"for device %s[%04x:%04x], kernel reported alias " | ||||
| 	pci_info(pdev, "Using IVRS reported alias %02x:%02x.%d " | ||||
| 		"for device [%04x:%04x], kernel reported alias " | ||||
| 		"%02x:%02x.%d\n", PCI_BUS_NUM(ivrs_alias), PCI_SLOT(ivrs_alias), | ||||
| 		PCI_FUNC(ivrs_alias), dev_name(dev), pdev->vendor, pdev->device, | ||||
| 		PCI_FUNC(ivrs_alias), pdev->vendor, pdev->device, | ||||
| 		PCI_BUS_NUM(pci_alias), PCI_SLOT(pci_alias), | ||||
| 		PCI_FUNC(pci_alias)); | ||||
| 
 | ||||
| @ -293,9 +294,8 @@ static u16 get_alias(struct device *dev) | ||||
| 	if (pci_alias == devid && | ||||
| 	    PCI_BUS_NUM(ivrs_alias) == pdev->bus->number) { | ||||
| 		pci_add_dma_alias(pdev, ivrs_alias & 0xff); | ||||
| 		pr_info("Added PCI DMA alias %02x.%d for %s\n", | ||||
| 			PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias), | ||||
| 			dev_name(dev)); | ||||
| 		pci_info(pdev, "Added PCI DMA alias %02x.%d\n", | ||||
| 			PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias)); | ||||
| 	} | ||||
| 
 | ||||
| 	return ivrs_alias; | ||||
| @ -545,7 +545,7 @@ static void amd_iommu_report_page_fault(u16 devid, u16 domain_id, | ||||
| 		dev_data = get_dev_data(&pdev->dev); | ||||
| 
 | ||||
| 	if (dev_data && __ratelimit(&dev_data->rs)) { | ||||
| 		dev_err(&pdev->dev, "Event logged [IO_PAGE_FAULT domain=0x%04x address=0x%llx flags=0x%04x]\n", | ||||
| 		pci_err(pdev, "Event logged [IO_PAGE_FAULT domain=0x%04x address=0x%llx flags=0x%04x]\n", | ||||
| 			domain_id, address, flags); | ||||
| 	} else if (printk_ratelimit()) { | ||||
| 		pr_err("Event logged [IO_PAGE_FAULT device=%02x:%02x.%x domain=0x%04x address=0x%llx flags=0x%04x]\n", | ||||
| @ -2258,8 +2258,7 @@ static int amd_iommu_add_device(struct device *dev) | ||||
| 	ret = iommu_init_device(dev); | ||||
| 	if (ret) { | ||||
| 		if (ret != -ENOTSUPP) | ||||
| 			pr_err("Failed to initialize device %s - trying to proceed anyway\n", | ||||
| 				dev_name(dev)); | ||||
| 			dev_err(dev, "Failed to initialize - trying to proceed anyway\n"); | ||||
| 
 | ||||
| 		iommu_ignore_device(dev); | ||||
| 		dev->dma_ops = NULL; | ||||
| @ -2569,6 +2568,7 @@ static int map_sg(struct device *dev, struct scatterlist *sglist, | ||||
| 	struct scatterlist *s; | ||||
| 	unsigned long address; | ||||
| 	u64 dma_mask; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	domain = get_domain(dev); | ||||
| 	if (IS_ERR(domain)) | ||||
| @ -2591,7 +2591,6 @@ static int map_sg(struct device *dev, struct scatterlist *sglist, | ||||
| 
 | ||||
| 		for (j = 0; j < pages; ++j) { | ||||
| 			unsigned long bus_addr, phys_addr; | ||||
| 			int ret; | ||||
| 
 | ||||
| 			bus_addr  = address + s->dma_address + (j << PAGE_SHIFT); | ||||
| 			phys_addr = (sg_phys(s) & PAGE_MASK) + (j << PAGE_SHIFT); | ||||
| @ -2612,8 +2611,8 @@ static int map_sg(struct device *dev, struct scatterlist *sglist, | ||||
| 	return nelems; | ||||
| 
 | ||||
| out_unmap: | ||||
| 	pr_err("%s: IOMMU mapping error in map_sg (io-pages: %d)\n", | ||||
| 	       dev_name(dev), npages); | ||||
| 	dev_err(dev, "IOMMU mapping error in map_sg (io-pages: %d reason: %d)\n", | ||||
| 		npages, ret); | ||||
| 
 | ||||
| 	for_each_sg(sglist, s, nelems, i) { | ||||
| 		int j, pages = iommu_num_pages(sg_phys(s), s->length, PAGE_SIZE); | ||||
| @ -2807,7 +2806,7 @@ static int init_reserved_iova_ranges(void) | ||||
| 					   IOVA_PFN(r->start), | ||||
| 					   IOVA_PFN(r->end)); | ||||
| 			if (!val) { | ||||
| 				pr_err("Reserve pci-resource range failed\n"); | ||||
| 				pci_err(pdev, "Reserve pci-resource range %pR failed\n", r); | ||||
| 				return -ENOMEM; | ||||
| 			} | ||||
| 		} | ||||
| @ -3177,8 +3176,7 @@ static void amd_iommu_get_resv_regions(struct device *dev, | ||||
| 						 length, prot, | ||||
| 						 IOMMU_RESV_DIRECT); | ||||
| 		if (!region) { | ||||
| 			pr_err("Out of memory allocating dm-regions for %s\n", | ||||
| 				dev_name(dev)); | ||||
| 			dev_err(dev, "Out of memory allocating dm-regions\n"); | ||||
| 			return; | ||||
| 		} | ||||
| 		list_add_tail(®ion->list, head); | ||||
|  | ||||
| @ -18,6 +18,7 @@ | ||||
|  */ | ||||
| 
 | ||||
| #define pr_fmt(fmt)     "AMD-Vi: " fmt | ||||
| #define dev_fmt(fmt)    pr_fmt(fmt) | ||||
| 
 | ||||
| #include <linux/pci.h> | ||||
| #include <linux/acpi.h> | ||||
| @ -1457,8 +1458,7 @@ static void amd_iommu_erratum_746_workaround(struct amd_iommu *iommu) | ||||
| 	pci_write_config_dword(iommu->dev, 0xf0, 0x90 | (1 << 8)); | ||||
| 
 | ||||
| 	pci_write_config_dword(iommu->dev, 0xf4, value | 0x4); | ||||
| 	pr_info("Applying erratum 746 workaround for IOMMU at %s\n", | ||||
| 		dev_name(&iommu->dev->dev)); | ||||
| 	pci_info(iommu->dev, "Applying erratum 746 workaround\n"); | ||||
| 
 | ||||
| 	/* Clear the enable writing bit */ | ||||
| 	pci_write_config_dword(iommu->dev, 0xf0, 0x90); | ||||
| @ -1488,8 +1488,7 @@ static void amd_iommu_ats_write_check_workaround(struct amd_iommu *iommu) | ||||
| 	/* Set L2_DEBUG_3[AtsIgnoreIWDis] = 1 */ | ||||
| 	iommu_write_l2(iommu, 0x47, value | BIT(0)); | ||||
| 
 | ||||
| 	pr_info("Applying ATS write check workaround for IOMMU at %s\n", | ||||
| 		dev_name(&iommu->dev->dev)); | ||||
| 	pci_info(iommu->dev, "Applying ATS write check workaround\n"); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| @ -1665,6 +1664,7 @@ static int iommu_pc_get_set_reg(struct amd_iommu *iommu, u8 bank, u8 cntr, | ||||
| 
 | ||||
| static void init_iommu_perf_ctr(struct amd_iommu *iommu) | ||||
| { | ||||
| 	struct pci_dev *pdev = iommu->dev; | ||||
| 	u64 val = 0xabcd, val2 = 0; | ||||
| 
 | ||||
| 	if (!iommu_feature(iommu, FEATURE_PC)) | ||||
| @ -1676,12 +1676,12 @@ static void init_iommu_perf_ctr(struct amd_iommu *iommu) | ||||
| 	if ((iommu_pc_get_set_reg(iommu, 0, 0, 0, &val, true)) || | ||||
| 	    (iommu_pc_get_set_reg(iommu, 0, 0, 0, &val2, false)) || | ||||
| 	    (val != val2)) { | ||||
| 		pr_err("Unable to write to IOMMU perf counter.\n"); | ||||
| 		pci_err(pdev, "Unable to write to IOMMU perf counter.\n"); | ||||
| 		amd_iommu_pc_present = false; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	pr_info("IOMMU performance counters supported\n"); | ||||
| 	pci_info(pdev, "IOMMU performance counters supported\n"); | ||||
| 
 | ||||
| 	val = readl(iommu->mmio_base + MMIO_CNTR_CONF_OFFSET); | ||||
| 	iommu->max_banks = (u8) ((val >> 12) & 0x3f); | ||||
| @ -1840,14 +1840,14 @@ static void print_iommu_info(void) | ||||
| 	struct amd_iommu *iommu; | ||||
| 
 | ||||
| 	for_each_iommu(iommu) { | ||||
| 		struct pci_dev *pdev = iommu->dev; | ||||
| 		int i; | ||||
| 
 | ||||
| 		pr_info("Found IOMMU at %s cap 0x%hx\n", | ||||
| 			dev_name(&iommu->dev->dev), iommu->cap_ptr); | ||||
| 		pci_info(pdev, "Found IOMMU cap 0x%hx\n", iommu->cap_ptr); | ||||
| 
 | ||||
| 		if (iommu->cap & (1 << IOMMU_CAP_EFR)) { | ||||
| 			pr_info("Extended features (%#llx):\n", | ||||
| 				iommu->features); | ||||
| 			pci_info(pdev, "Extended features (%#llx):\n", | ||||
| 				 iommu->features); | ||||
| 			for (i = 0; i < ARRAY_SIZE(feat_str); ++i) { | ||||
| 				if (iommu_feature(iommu, (1ULL << i))) | ||||
| 					pr_cont(" %s", feat_str[i]); | ||||
|  | ||||
| @ -370,29 +370,6 @@ static struct pasid_state *mn_to_state(struct mmu_notifier *mn) | ||||
| 	return container_of(mn, struct pasid_state, mn); | ||||
| } | ||||
| 
 | ||||
| static void __mn_flush_page(struct mmu_notifier *mn, | ||||
| 			    unsigned long address) | ||||
| { | ||||
| 	struct pasid_state *pasid_state; | ||||
| 	struct device_state *dev_state; | ||||
| 
 | ||||
| 	pasid_state = mn_to_state(mn); | ||||
| 	dev_state   = pasid_state->device_state; | ||||
| 
 | ||||
| 	amd_iommu_flush_page(dev_state->domain, pasid_state->pasid, address); | ||||
| } | ||||
| 
 | ||||
| static int mn_clear_flush_young(struct mmu_notifier *mn, | ||||
| 				struct mm_struct *mm, | ||||
| 				unsigned long start, | ||||
| 				unsigned long end) | ||||
| { | ||||
| 	for (; start < end; start += PAGE_SIZE) | ||||
| 		__mn_flush_page(mn, start); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void mn_invalidate_range(struct mmu_notifier *mn, | ||||
| 				struct mm_struct *mm, | ||||
| 				unsigned long start, unsigned long end) | ||||
| @ -430,7 +407,6 @@ static void mn_release(struct mmu_notifier *mn, struct mm_struct *mm) | ||||
| 
 | ||||
| static const struct mmu_notifier_ops iommu_mn = { | ||||
| 	.release		= mn_release, | ||||
| 	.clear_flush_young      = mn_clear_flush_young, | ||||
| 	.invalidate_range       = mn_invalidate_range, | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -18,6 +18,7 @@ | ||||
| #include <linux/dma-iommu.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/io-pgtable.h> | ||||
| #include <linux/iommu.h> | ||||
| #include <linux/iopoll.h> | ||||
| #include <linux/init.h> | ||||
| @ -32,8 +33,6 @@ | ||||
| 
 | ||||
| #include <linux/amba/bus.h> | ||||
| 
 | ||||
| #include "io-pgtable.h" | ||||
| 
 | ||||
| /* MMIO registers */ | ||||
| #define ARM_SMMU_IDR0			0x0 | ||||
| #define IDR0_ST_LVL			GENMASK(28, 27) | ||||
|  | ||||
| @ -39,6 +39,7 @@ | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/io-64-nonatomic-hi-lo.h> | ||||
| #include <linux/io-pgtable.h> | ||||
| #include <linux/iommu.h> | ||||
| #include <linux/iopoll.h> | ||||
| #include <linux/init.h> | ||||
| @ -56,7 +57,6 @@ | ||||
| #include <linux/amba/bus.h> | ||||
| #include <linux/fsl/mc.h> | ||||
| 
 | ||||
| #include "io-pgtable.h" | ||||
| #include "arm-smmu-regs.h" | ||||
| 
 | ||||
| #define ARM_MMU500_ACTLR_CPRE		(1 << 1) | ||||
|  | ||||
| @ -289,7 +289,7 @@ int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, | ||||
| { | ||||
| 	struct iommu_dma_cookie *cookie = domain->iova_cookie; | ||||
| 	struct iova_domain *iovad = &cookie->iovad; | ||||
| 	unsigned long order, base_pfn, end_pfn; | ||||
| 	unsigned long order, base_pfn; | ||||
| 	int attr; | ||||
| 
 | ||||
| 	if (!cookie || cookie->type != IOMMU_DMA_IOVA_COOKIE) | ||||
| @ -298,7 +298,6 @@ int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, | ||||
| 	/* Use the smallest supported page size for IOVA granularity */ | ||||
| 	order = __ffs(domain->pgsize_bitmap); | ||||
| 	base_pfn = max_t(unsigned long, 1, base >> order); | ||||
| 	end_pfn = (base + size - 1) >> order; | ||||
| 
 | ||||
| 	/* Check the domain allows at least some access to the device... */ | ||||
| 	if (domain->geometry.force_aperture) { | ||||
|  | ||||
							
								
								
									
										196
									
								
								drivers/iommu/hyperv-iommu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								drivers/iommu/hyperv-iommu.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,196 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Hyper-V stub IOMMU driver. | ||||
|  * | ||||
|  * Copyright (C) 2019, Microsoft, Inc. | ||||
|  * | ||||
|  * Author : Lan Tianyu <Tianyu.Lan@microsoft.com> | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/types.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/irq.h> | ||||
| #include <linux/iommu.h> | ||||
| #include <linux/module.h> | ||||
| 
 | ||||
| #include <asm/apic.h> | ||||
| #include <asm/cpu.h> | ||||
| #include <asm/hw_irq.h> | ||||
| #include <asm/io_apic.h> | ||||
| #include <asm/irq_remapping.h> | ||||
| #include <asm/hypervisor.h> | ||||
| 
 | ||||
| #include "irq_remapping.h" | ||||
| 
 | ||||
| #ifdef CONFIG_IRQ_REMAP | ||||
| 
 | ||||
| /*
 | ||||
|  * According 82093AA IO-APIC spec , IO APIC has a 24-entry Interrupt | ||||
|  * Redirection Table. Hyper-V exposes one single IO-APIC and so define | ||||
|  * 24 IO APIC remmapping entries. | ||||
|  */ | ||||
| #define IOAPIC_REMAPPING_ENTRY 24 | ||||
| 
 | ||||
| static cpumask_t ioapic_max_cpumask = { CPU_BITS_NONE }; | ||||
| static struct irq_domain *ioapic_ir_domain; | ||||
| 
 | ||||
| static int hyperv_ir_set_affinity(struct irq_data *data, | ||||
| 		const struct cpumask *mask, bool force) | ||||
| { | ||||
| 	struct irq_data *parent = data->parent_data; | ||||
| 	struct irq_cfg *cfg = irqd_cfg(data); | ||||
| 	struct IO_APIC_route_entry *entry; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/* Return error If new irq affinity is out of ioapic_max_cpumask. */ | ||||
| 	if (!cpumask_subset(mask, &ioapic_max_cpumask)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	ret = parent->chip->irq_set_affinity(parent, mask, force); | ||||
| 	if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	entry = data->chip_data; | ||||
| 	entry->dest = cfg->dest_apicid; | ||||
| 	entry->vector = cfg->vector; | ||||
| 	send_cleanup_vector(cfg); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct irq_chip hyperv_ir_chip = { | ||||
| 	.name			= "HYPERV-IR", | ||||
| 	.irq_ack		= apic_ack_irq, | ||||
| 	.irq_set_affinity	= hyperv_ir_set_affinity, | ||||
| }; | ||||
| 
 | ||||
| static int hyperv_irq_remapping_alloc(struct irq_domain *domain, | ||||
| 				     unsigned int virq, unsigned int nr_irqs, | ||||
| 				     void *arg) | ||||
| { | ||||
| 	struct irq_alloc_info *info = arg; | ||||
| 	struct irq_data *irq_data; | ||||
| 	struct irq_desc *desc; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	if (!info || info->type != X86_IRQ_ALLOC_TYPE_IOAPIC || nr_irqs > 1) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	irq_data = irq_domain_get_irq_data(domain, virq); | ||||
| 	if (!irq_data) { | ||||
| 		irq_domain_free_irqs_common(domain, virq, nr_irqs); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	irq_data->chip = &hyperv_ir_chip; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If there is interrupt remapping function of IOMMU, setting irq | ||||
| 	 * affinity only needs to change IRTE of IOMMU. But Hyper-V doesn't | ||||
| 	 * support interrupt remapping function, setting irq affinity of IO-APIC | ||||
| 	 * interrupts still needs to change IO-APIC registers. But ioapic_ | ||||
| 	 * configure_entry() will ignore value of cfg->vector and cfg-> | ||||
| 	 * dest_apicid when IO-APIC's parent irq domain is not the vector | ||||
| 	 * domain.(See ioapic_configure_entry()) In order to setting vector | ||||
| 	 * and dest_apicid to IO-APIC register, IO-APIC entry pointer is saved | ||||
| 	 * in the chip_data and hyperv_irq_remapping_activate()/hyperv_ir_set_ | ||||
| 	 * affinity() set vector and dest_apicid directly into IO-APIC entry. | ||||
| 	 */ | ||||
| 	irq_data->chip_data = info->ioapic_entry; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Hypver-V IO APIC irq affinity should be in the scope of | ||||
| 	 * ioapic_max_cpumask because no irq remapping support. | ||||
| 	 */ | ||||
| 	desc = irq_data_to_desc(irq_data); | ||||
| 	cpumask_copy(desc->irq_common_data.affinity, &ioapic_max_cpumask); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void hyperv_irq_remapping_free(struct irq_domain *domain, | ||||
| 				 unsigned int virq, unsigned int nr_irqs) | ||||
| { | ||||
| 	irq_domain_free_irqs_common(domain, virq, nr_irqs); | ||||
| } | ||||
| 
 | ||||
| static int hyperv_irq_remapping_activate(struct irq_domain *domain, | ||||
| 			  struct irq_data *irq_data, bool reserve) | ||||
| { | ||||
| 	struct irq_cfg *cfg = irqd_cfg(irq_data); | ||||
| 	struct IO_APIC_route_entry *entry = irq_data->chip_data; | ||||
| 
 | ||||
| 	entry->dest = cfg->dest_apicid; | ||||
| 	entry->vector = cfg->vector; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static struct irq_domain_ops hyperv_ir_domain_ops = { | ||||
| 	.alloc = hyperv_irq_remapping_alloc, | ||||
| 	.free = hyperv_irq_remapping_free, | ||||
| 	.activate = hyperv_irq_remapping_activate, | ||||
| }; | ||||
| 
 | ||||
| static int __init hyperv_prepare_irq_remapping(void) | ||||
| { | ||||
| 	struct fwnode_handle *fn; | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (!hypervisor_is_type(X86_HYPER_MS_HYPERV) || | ||||
| 	    !x2apic_supported()) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	fn = irq_domain_alloc_named_id_fwnode("HYPERV-IR", 0); | ||||
| 	if (!fn) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	ioapic_ir_domain = | ||||
| 		irq_domain_create_hierarchy(arch_get_ir_parent_domain(), | ||||
| 				0, IOAPIC_REMAPPING_ENTRY, fn, | ||||
| 				&hyperv_ir_domain_ops, NULL); | ||||
| 
 | ||||
| 	irq_domain_free_fwnode(fn); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Hyper-V doesn't provide irq remapping function for | ||||
| 	 * IO-APIC and so IO-APIC only accepts 8-bit APIC ID. | ||||
| 	 * Cpu's APIC ID is read from ACPI MADT table and APIC IDs | ||||
| 	 * in the MADT table on Hyper-v are sorted monotonic increasingly. | ||||
| 	 * APIC ID reflects cpu topology. There maybe some APIC ID | ||||
| 	 * gaps when cpu number in a socket is not power of two. Prepare | ||||
| 	 * max cpu affinity for IOAPIC irqs. Scan cpu 0-255 and set cpu | ||||
| 	 * into ioapic_max_cpumask if its APIC ID is less than 256. | ||||
| 	 */ | ||||
| 	for (i = min_t(unsigned int, num_possible_cpus() - 1, 255); i >= 0; i--) | ||||
| 		if (cpu_physical_id(i) < 256) | ||||
| 			cpumask_set_cpu(i, &ioapic_max_cpumask); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int __init hyperv_enable_irq_remapping(void) | ||||
| { | ||||
| 	return IRQ_REMAP_X2APIC_MODE; | ||||
| } | ||||
| 
 | ||||
| static struct irq_domain *hyperv_get_ir_irq_domain(struct irq_alloc_info *info) | ||||
| { | ||||
| 	if (info->type == X86_IRQ_ALLOC_TYPE_IOAPIC) | ||||
| 		return ioapic_ir_domain; | ||||
| 	else | ||||
| 		return NULL; | ||||
| } | ||||
| 
 | ||||
| struct irq_remap_ops hyperv_irq_remap_ops = { | ||||
| 	.prepare		= hyperv_prepare_irq_remapping, | ||||
| 	.enable			= hyperv_enable_irq_remapping, | ||||
| 	.get_ir_irq_domain	= hyperv_get_ir_irq_domain, | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
| @ -19,6 +19,7 @@ | ||||
|  */ | ||||
| 
 | ||||
| #define pr_fmt(fmt)     "DMAR: " fmt | ||||
| #define dev_fmt(fmt)    pr_fmt(fmt) | ||||
| 
 | ||||
| #include <linux/init.h> | ||||
| #include <linux/bitmap.h> | ||||
| @ -343,8 +344,7 @@ static int g_num_of_iommus; | ||||
| 
 | ||||
| static void domain_exit(struct dmar_domain *domain); | ||||
| static void domain_remove_dev_info(struct dmar_domain *domain); | ||||
| static void dmar_remove_one_dev_info(struct dmar_domain *domain, | ||||
| 				     struct device *dev); | ||||
| static void dmar_remove_one_dev_info(struct device *dev); | ||||
| static void __dmar_remove_one_dev_info(struct device_domain_info *info); | ||||
| static void domain_context_clear(struct intel_iommu *iommu, | ||||
| 				 struct device *dev); | ||||
| @ -865,7 +865,7 @@ out: | ||||
| static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain, | ||||
| 				      unsigned long pfn, int *target_level) | ||||
| { | ||||
| 	struct dma_pte *parent, *pte = NULL; | ||||
| 	struct dma_pte *parent, *pte; | ||||
| 	int level = agaw_to_level(domain->agaw); | ||||
| 	int offset; | ||||
| 
 | ||||
| @ -922,7 +922,7 @@ static struct dma_pte *dma_pfn_level_pte(struct dmar_domain *domain, | ||||
| 					 unsigned long pfn, | ||||
| 					 int level, int *large_page) | ||||
| { | ||||
| 	struct dma_pte *parent, *pte = NULL; | ||||
| 	struct dma_pte *parent, *pte; | ||||
| 	int total = agaw_to_level(domain->agaw); | ||||
| 	int offset; | ||||
| 
 | ||||
| @ -954,7 +954,7 @@ static void dma_pte_clear_range(struct dmar_domain *domain, | ||||
| 				unsigned long start_pfn, | ||||
| 				unsigned long last_pfn) | ||||
| { | ||||
| 	unsigned int large_page = 1; | ||||
| 	unsigned int large_page; | ||||
| 	struct dma_pte *first_pte, *pte; | ||||
| 
 | ||||
| 	BUG_ON(!domain_pfn_supported(domain, start_pfn)); | ||||
| @ -1132,7 +1132,7 @@ static struct page *domain_unmap(struct dmar_domain *domain, | ||||
| 				 unsigned long start_pfn, | ||||
| 				 unsigned long last_pfn) | ||||
| { | ||||
| 	struct page *freelist = NULL; | ||||
| 	struct page *freelist; | ||||
| 
 | ||||
| 	BUG_ON(!domain_pfn_supported(domain, start_pfn)); | ||||
| 	BUG_ON(!domain_pfn_supported(domain, last_pfn)); | ||||
| @ -1403,10 +1403,13 @@ static void iommu_enable_dev_iotlb(struct device_domain_info *info) | ||||
| 	if (info->pasid_supported && !pci_enable_pasid(pdev, info->pasid_supported & ~1)) | ||||
| 		info->pasid_enabled = 1; | ||||
| 
 | ||||
| 	if (info->pri_supported && !pci_reset_pri(pdev) && !pci_enable_pri(pdev, 32)) | ||||
| 	if (info->pri_supported && | ||||
| 	    (info->pasid_enabled ? pci_prg_resp_pasid_required(pdev) : 1)  && | ||||
| 	    !pci_reset_pri(pdev) && !pci_enable_pri(pdev, 32)) | ||||
| 		info->pri_enabled = 1; | ||||
| #endif | ||||
| 	if (!pdev->untrusted && info->ats_supported && | ||||
| 	    pci_ats_page_aligned(pdev) && | ||||
| 	    !pci_enable_ats(pdev, VTD_PAGE_SHIFT)) { | ||||
| 		info->ats_enabled = 1; | ||||
| 		domain_update_iotlb(info->domain); | ||||
| @ -1763,7 +1766,7 @@ static int domain_attach_iommu(struct dmar_domain *domain, | ||||
| static int domain_detach_iommu(struct dmar_domain *domain, | ||||
| 			       struct intel_iommu *iommu) | ||||
| { | ||||
| 	int num, count = INT_MAX; | ||||
| 	int num, count; | ||||
| 
 | ||||
| 	assert_spin_locked(&device_domain_lock); | ||||
| 	assert_spin_locked(&iommu->lock); | ||||
| @ -1816,7 +1819,7 @@ static int dmar_init_reserved_ranges(void) | ||||
| 					    IOVA_PFN(r->start), | ||||
| 					    IOVA_PFN(r->end)); | ||||
| 			if (!iova) { | ||||
| 				pr_err("Reserve iova failed\n"); | ||||
| 				pci_err(pdev, "Reserve iova for %pR failed\n", r); | ||||
| 				return -ENODEV; | ||||
| 			} | ||||
| 		} | ||||
| @ -1902,11 +1905,7 @@ static int domain_init(struct dmar_domain *domain, struct intel_iommu *iommu, | ||||
| 
 | ||||
| static void domain_exit(struct dmar_domain *domain) | ||||
| { | ||||
| 	struct page *freelist = NULL; | ||||
| 
 | ||||
| 	/* Domain 0 is reserved, so dont process it */ | ||||
| 	if (!domain) | ||||
| 		return; | ||||
| 	struct page *freelist; | ||||
| 
 | ||||
| 	/* Remove associated devices and clear attached or cached domains */ | ||||
| 	rcu_read_lock(); | ||||
| @ -2058,7 +2057,6 @@ static int domain_context_mapping_one(struct dmar_domain *domain, | ||||
| 		int agaw; | ||||
| 
 | ||||
| 		context_set_domain_id(context, did); | ||||
| 		context_set_translation_type(context, translation); | ||||
| 
 | ||||
| 		if (translation != CONTEXT_TT_PASS_THROUGH) { | ||||
| 			/*
 | ||||
| @ -2088,6 +2086,8 @@ static int domain_context_mapping_one(struct dmar_domain *domain, | ||||
| 			 */ | ||||
| 			context_set_address_width(context, iommu->msagaw); | ||||
| 		} | ||||
| 
 | ||||
| 		context_set_translation_type(context, translation); | ||||
| 	} | ||||
| 
 | ||||
| 	context_set_fault_enable(context); | ||||
| @ -2486,7 +2486,8 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu, | ||||
| 	if (dev && dev_is_pci(dev)) { | ||||
| 		struct pci_dev *pdev = to_pci_dev(info->dev); | ||||
| 
 | ||||
| 		if (!pci_ats_disabled() && | ||||
| 		if (!pdev->untrusted && | ||||
| 		    !pci_ats_disabled() && | ||||
| 		    ecap_dev_iotlb_support(iommu->ecap) && | ||||
| 		    pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ATS) && | ||||
| 		    dmar_find_matched_atsr_unit(pdev)) | ||||
| @ -2545,9 +2546,8 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu, | ||||
| 	if (dev && dev_is_pci(dev) && sm_supported(iommu)) { | ||||
| 		ret = intel_pasid_alloc_table(dev); | ||||
| 		if (ret) { | ||||
| 			pr_err("PASID table allocation for %s failed\n", | ||||
| 			       dev_name(dev)); | ||||
| 			dmar_remove_one_dev_info(domain, dev); | ||||
| 			dev_err(dev, "PASID table allocation failed\n"); | ||||
| 			dmar_remove_one_dev_info(dev); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 
 | ||||
| @ -2561,16 +2561,15 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu, | ||||
| 					dev, PASID_RID2PASID); | ||||
| 		spin_unlock(&iommu->lock); | ||||
| 		if (ret) { | ||||
| 			pr_err("Setup RID2PASID for %s failed\n", | ||||
| 			       dev_name(dev)); | ||||
| 			dmar_remove_one_dev_info(domain, dev); | ||||
| 			dev_err(dev, "Setup RID2PASID failed\n"); | ||||
| 			dmar_remove_one_dev_info(dev); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (dev && domain_context_mapping(domain, dev)) { | ||||
| 		pr_err("Domain context map for %s failed\n", dev_name(dev)); | ||||
| 		dmar_remove_one_dev_info(domain, dev); | ||||
| 		dev_err(dev, "Domain context map failed\n"); | ||||
| 		dmar_remove_one_dev_info(dev); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| @ -2585,7 +2584,7 @@ static int get_last_alias(struct pci_dev *pdev, u16 alias, void *opaque) | ||||
| 
 | ||||
| static struct dmar_domain *find_or_alloc_domain(struct device *dev, int gaw) | ||||
| { | ||||
| 	struct device_domain_info *info = NULL; | ||||
| 	struct device_domain_info *info; | ||||
| 	struct dmar_domain *domain = NULL; | ||||
| 	struct intel_iommu *iommu; | ||||
| 	u16 dma_alias; | ||||
| @ -2724,13 +2723,12 @@ static int domain_prepare_identity_map(struct device *dev, | ||||
| 	   range which is reserved in E820, so which didn't get set | ||||
| 	   up to start with in si_domain */ | ||||
| 	if (domain == si_domain && hw_pass_through) { | ||||
| 		pr_warn("Ignoring identity map for HW passthrough device %s [0x%Lx - 0x%Lx]\n", | ||||
| 			dev_name(dev), start, end); | ||||
| 		dev_warn(dev, "Ignoring identity map for HW passthrough [0x%Lx - 0x%Lx]\n", | ||||
| 			 start, end); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	pr_info("Setting identity map for device %s [0x%Lx - 0x%Lx]\n", | ||||
| 		dev_name(dev), start, end); | ||||
| 	dev_info(dev, "Setting identity map [0x%Lx - 0x%Lx]\n", start, end); | ||||
| 
 | ||||
| 	if (end < start) { | ||||
| 		WARN(1, "Your BIOS is broken; RMRR ends before it starts!\n" | ||||
| @ -2810,7 +2808,7 @@ static int md_domain_init(struct dmar_domain *domain, int guest_width); | ||||
| 
 | ||||
| static int __init si_domain_init(int hw) | ||||
| { | ||||
| 	int nid, ret = 0; | ||||
| 	int nid, ret; | ||||
| 
 | ||||
| 	si_domain = alloc_domain(DOMAIN_FLAG_STATIC_IDENTITY); | ||||
| 	if (!si_domain) | ||||
| @ -2934,7 +2932,6 @@ static bool device_is_rmrr_locked(struct device *dev) | ||||
| 
 | ||||
| static int iommu_should_identity_map(struct device *dev, int startup) | ||||
| { | ||||
| 
 | ||||
| 	if (dev_is_pci(dev)) { | ||||
| 		struct pci_dev *pdev = to_pci_dev(dev); | ||||
| 
 | ||||
| @ -3017,8 +3014,8 @@ static int __init dev_prepare_static_identity_mapping(struct device *dev, int hw | ||||
| 
 | ||||
| 	ret = domain_add_dev_info(si_domain, dev); | ||||
| 	if (!ret) | ||||
| 		pr_info("%s identity mapping for device %s\n", | ||||
| 			hw ? "Hardware" : "Software", dev_name(dev)); | ||||
| 		dev_info(dev, "%s identity mapping\n", | ||||
| 			 hw ? "Hardware" : "Software"); | ||||
| 	else if (ret == -ENODEV) | ||||
| 		/* device not associated with an iommu */ | ||||
| 		ret = 0; | ||||
| @ -3530,7 +3527,7 @@ static unsigned long intel_alloc_iova(struct device *dev, | ||||
| 				     struct dmar_domain *domain, | ||||
| 				     unsigned long nrpages, uint64_t dma_mask) | ||||
| { | ||||
| 	unsigned long iova_pfn = 0; | ||||
| 	unsigned long iova_pfn; | ||||
| 
 | ||||
| 	/* Restrict dma_mask to the width that the iommu can handle */ | ||||
| 	dma_mask = min_t(uint64_t, DOMAIN_MAX_ADDR(domain->gaw), dma_mask); | ||||
| @ -3551,8 +3548,7 @@ static unsigned long intel_alloc_iova(struct device *dev, | ||||
| 	iova_pfn = alloc_iova_fast(&domain->iovad, nrpages, | ||||
| 				   IOVA_PFN(dma_mask), true); | ||||
| 	if (unlikely(!iova_pfn)) { | ||||
| 		pr_err("Allocating %ld-page iova for %s failed", | ||||
| 		       nrpages, dev_name(dev)); | ||||
| 		dev_err(dev, "Allocating %ld-page iova failed", nrpages); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| @ -3600,7 +3596,7 @@ struct dmar_domain *get_valid_domain_for_dev(struct device *dev) | ||||
| out: | ||||
| 
 | ||||
| 	if (!domain) | ||||
| 		pr_err("Allocating domain for %s failed\n", dev_name(dev)); | ||||
| 		dev_err(dev, "Allocating domain failed\n"); | ||||
| 
 | ||||
| 
 | ||||
| 	return domain; | ||||
| @ -3626,9 +3622,8 @@ static int iommu_no_mapping(struct device *dev) | ||||
| 			 * 32 bit DMA is removed from si_domain and fall back | ||||
| 			 * to non-identity mapping. | ||||
| 			 */ | ||||
| 			dmar_remove_one_dev_info(si_domain, dev); | ||||
| 			pr_info("32bit %s uses non-identity mapping\n", | ||||
| 				dev_name(dev)); | ||||
| 			dmar_remove_one_dev_info(dev); | ||||
| 			dev_info(dev, "32bit DMA uses non-identity mapping\n"); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} else { | ||||
| @ -3640,8 +3635,7 @@ static int iommu_no_mapping(struct device *dev) | ||||
| 			int ret; | ||||
| 			ret = domain_add_dev_info(si_domain, dev); | ||||
| 			if (!ret) { | ||||
| 				pr_info("64bit %s uses identity mapping\n", | ||||
| 					dev_name(dev)); | ||||
| 				dev_info(dev, "64bit DMA uses identity mapping\n"); | ||||
| 				return 1; | ||||
| 			} | ||||
| 		} | ||||
| @ -3650,11 +3644,9 @@ static int iommu_no_mapping(struct device *dev) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static dma_addr_t __intel_map_page(struct device *dev, struct page *page, | ||||
| 				   unsigned long offset, size_t size, int dir, | ||||
| 				   u64 dma_mask) | ||||
| static dma_addr_t __intel_map_single(struct device *dev, phys_addr_t paddr, | ||||
| 				     size_t size, int dir, u64 dma_mask) | ||||
| { | ||||
| 	phys_addr_t paddr = page_to_phys(page) + offset; | ||||
| 	struct dmar_domain *domain; | ||||
| 	phys_addr_t start_paddr; | ||||
| 	unsigned long iova_pfn; | ||||
| @ -3706,8 +3698,8 @@ static dma_addr_t __intel_map_page(struct device *dev, struct page *page, | ||||
| error: | ||||
| 	if (iova_pfn) | ||||
| 		free_iova_fast(&domain->iovad, iova_pfn, dma_to_mm_pfn(size)); | ||||
| 	pr_err("Device %s request: %zx@%llx dir %d --- failed\n", | ||||
| 		dev_name(dev), size, (unsigned long long)paddr, dir); | ||||
| 	dev_err(dev, "Device request: %zx@%llx dir %d --- failed\n", | ||||
| 		size, (unsigned long long)paddr, dir); | ||||
| 	return DMA_MAPPING_ERROR; | ||||
| } | ||||
| 
 | ||||
| @ -3716,7 +3708,15 @@ static dma_addr_t intel_map_page(struct device *dev, struct page *page, | ||||
| 				 enum dma_data_direction dir, | ||||
| 				 unsigned long attrs) | ||||
| { | ||||
| 	return __intel_map_page(dev, page, offset, size, dir, *dev->dma_mask); | ||||
| 	return __intel_map_single(dev, page_to_phys(page) + offset, size, | ||||
| 				  dir, *dev->dma_mask); | ||||
| } | ||||
| 
 | ||||
| static dma_addr_t intel_map_resource(struct device *dev, phys_addr_t phys_addr, | ||||
| 				     size_t size, enum dma_data_direction dir, | ||||
| 				     unsigned long attrs) | ||||
| { | ||||
| 	return __intel_map_single(dev, phys_addr, size, dir, *dev->dma_mask); | ||||
| } | ||||
| 
 | ||||
| static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size) | ||||
| @ -3742,8 +3742,7 @@ static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size) | ||||
| 	start_pfn = mm_to_dma_pfn(iova_pfn); | ||||
| 	last_pfn = start_pfn + nrpages - 1; | ||||
| 
 | ||||
| 	pr_debug("Device %s unmapping: pfn %lx-%lx\n", | ||||
| 		 dev_name(dev), start_pfn, last_pfn); | ||||
| 	dev_dbg(dev, "Device unmapping: pfn %lx-%lx\n", start_pfn, last_pfn); | ||||
| 
 | ||||
| 	freelist = domain_unmap(domain, start_pfn, last_pfn); | ||||
| 
 | ||||
| @ -3807,8 +3806,9 @@ static void *intel_alloc_coherent(struct device *dev, size_t size, | ||||
| 		return NULL; | ||||
| 	memset(page_address(page), 0, size); | ||||
| 
 | ||||
| 	*dma_handle = __intel_map_page(dev, page, 0, size, DMA_BIDIRECTIONAL, | ||||
| 				       dev->coherent_dma_mask); | ||||
| 	*dma_handle = __intel_map_single(dev, page_to_phys(page), size, | ||||
| 					 DMA_BIDIRECTIONAL, | ||||
| 					 dev->coherent_dma_mask); | ||||
| 	if (*dma_handle != DMA_MAPPING_ERROR) | ||||
| 		return page_address(page); | ||||
| 	if (!dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT)) | ||||
| @ -3925,6 +3925,8 @@ static const struct dma_map_ops intel_dma_ops = { | ||||
| 	.unmap_sg = intel_unmap_sg, | ||||
| 	.map_page = intel_map_page, | ||||
| 	.unmap_page = intel_unmap_page, | ||||
| 	.map_resource = intel_map_resource, | ||||
| 	.unmap_resource = intel_unmap_page, | ||||
| 	.dma_supported = dma_direct_supported, | ||||
| }; | ||||
| 
 | ||||
| @ -4340,7 +4342,7 @@ int dmar_check_one_atsr(struct acpi_dmar_header *hdr, void *arg) | ||||
| 
 | ||||
| static int intel_iommu_add(struct dmar_drhd_unit *dmaru) | ||||
| { | ||||
| 	int sp, ret = 0; | ||||
| 	int sp, ret; | ||||
| 	struct intel_iommu *iommu = dmaru->iommu; | ||||
| 
 | ||||
| 	if (g_iommus[iommu->seq_id]) | ||||
| @ -4504,7 +4506,7 @@ out: | ||||
| 
 | ||||
| int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	int ret; | ||||
| 	struct dmar_rmrr_unit *rmrru; | ||||
| 	struct dmar_atsr_unit *atsru; | ||||
| 	struct acpi_dmar_atsr *atsr; | ||||
| @ -4521,7 +4523,7 @@ int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info) | ||||
| 				((void *)rmrr) + rmrr->header.length, | ||||
| 				rmrr->segment, rmrru->devices, | ||||
| 				rmrru->devices_cnt); | ||||
| 			if(ret < 0) | ||||
| 			if (ret < 0) | ||||
| 				return ret; | ||||
| 		} else if (info->event == BUS_NOTIFY_REMOVED_DEVICE) { | ||||
| 			dmar_remove_dev_scope(info, rmrr->segment, | ||||
| @ -4541,7 +4543,7 @@ int dmar_iommu_notify_scope_dev(struct dmar_pci_notify_info *info) | ||||
| 					atsru->devices_cnt); | ||||
| 			if (ret > 0) | ||||
| 				break; | ||||
| 			else if(ret < 0) | ||||
| 			else if (ret < 0) | ||||
| 				return ret; | ||||
| 		} else if (info->event == BUS_NOTIFY_REMOVED_DEVICE) { | ||||
| 			if (dmar_remove_dev_scope(info, atsr->segment, | ||||
| @ -4568,16 +4570,19 @@ static int device_notifier(struct notifier_block *nb, | ||||
| 	if (iommu_dummy(dev)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	if (action != BUS_NOTIFY_REMOVED_DEVICE) | ||||
| 		return 0; | ||||
| 	if (action == BUS_NOTIFY_REMOVED_DEVICE) { | ||||
| 		domain = find_domain(dev); | ||||
| 		if (!domain) | ||||
| 			return 0; | ||||
| 
 | ||||
| 	domain = find_domain(dev); | ||||
| 	if (!domain) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	dmar_remove_one_dev_info(domain, dev); | ||||
| 	if (!domain_type_is_vm_or_si(domain) && list_empty(&domain->devices)) | ||||
| 		domain_exit(domain); | ||||
| 		dmar_remove_one_dev_info(dev); | ||||
| 		if (!domain_type_is_vm_or_si(domain) && | ||||
| 		    list_empty(&domain->devices)) | ||||
| 			domain_exit(domain); | ||||
| 	} else if (action == BUS_NOTIFY_ADD_DEVICE) { | ||||
| 		if (iommu_should_identity_map(dev, 1)) | ||||
| 			domain_add_dev_info(si_domain, dev); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| @ -4988,8 +4993,7 @@ static void __dmar_remove_one_dev_info(struct device_domain_info *info) | ||||
| 	free_devinfo_mem(info); | ||||
| } | ||||
| 
 | ||||
| static void dmar_remove_one_dev_info(struct dmar_domain *domain, | ||||
| 				     struct device *dev) | ||||
| static void dmar_remove_one_dev_info(struct device *dev) | ||||
| { | ||||
| 	struct device_domain_info *info; | ||||
| 	unsigned long flags; | ||||
| @ -5078,7 +5082,7 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, | ||||
| 		old_domain = find_domain(dev); | ||||
| 		if (old_domain) { | ||||
| 			rcu_read_lock(); | ||||
| 			dmar_remove_one_dev_info(old_domain, dev); | ||||
| 			dmar_remove_one_dev_info(dev); | ||||
| 			rcu_read_unlock(); | ||||
| 
 | ||||
| 			if (!domain_type_is_vm_or_si(old_domain) && | ||||
| @ -5097,9 +5101,9 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, | ||||
| 		addr_width = cap_mgaw(iommu->cap); | ||||
| 
 | ||||
| 	if (dmar_domain->max_addr > (1LL << addr_width)) { | ||||
| 		pr_err("%s: iommu width (%d) is not " | ||||
| 		       "sufficient for the mapped address (%llx)\n", | ||||
| 		       __func__, addr_width, dmar_domain->max_addr); | ||||
| 		dev_err(dev, "%s: iommu width (%d) is not " | ||||
| 		        "sufficient for the mapped address (%llx)\n", | ||||
| 		        __func__, addr_width, dmar_domain->max_addr); | ||||
| 		return -EFAULT; | ||||
| 	} | ||||
| 	dmar_domain->gaw = addr_width; | ||||
| @ -5125,7 +5129,7 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, | ||||
| static void intel_iommu_detach_device(struct iommu_domain *domain, | ||||
| 				      struct device *dev) | ||||
| { | ||||
| 	dmar_remove_one_dev_info(to_dmar_domain(domain), dev); | ||||
| 	dmar_remove_one_dev_info(dev); | ||||
| } | ||||
| 
 | ||||
| static int intel_iommu_map(struct iommu_domain *domain, | ||||
| @ -5400,7 +5404,7 @@ const struct iommu_ops intel_iommu_ops = { | ||||
| static void quirk_iommu_g4x_gfx(struct pci_dev *dev) | ||||
| { | ||||
| 	/* G4x/GM45 integrated gfx dmar support is totally busted. */ | ||||
| 	pr_info("Disabling IOMMU for graphics on this chipset\n"); | ||||
| 	pci_info(dev, "Disabling IOMMU for graphics on this chipset\n"); | ||||
| 	dmar_map_gfx = 0; | ||||
| } | ||||
| 
 | ||||
| @ -5418,7 +5422,7 @@ static void quirk_iommu_rwbf(struct pci_dev *dev) | ||||
| 	 * Mobile 4 Series Chipset neglects to set RWBF capability, | ||||
| 	 * but needs it. Same seems to hold for the desktop versions. | ||||
| 	 */ | ||||
| 	pr_info("Forcing write-buffer flush capability\n"); | ||||
| 	pci_info(dev, "Forcing write-buffer flush capability\n"); | ||||
| 	rwbf_quirk = 1; | ||||
| } | ||||
| 
 | ||||
| @ -5448,11 +5452,11 @@ static void quirk_calpella_no_shadow_gtt(struct pci_dev *dev) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (!(ggc & GGC_MEMORY_VT_ENABLED)) { | ||||
| 		pr_info("BIOS has allocated no shadow GTT; disabling IOMMU for graphics\n"); | ||||
| 		pci_info(dev, "BIOS has allocated no shadow GTT; disabling IOMMU for graphics\n"); | ||||
| 		dmar_map_gfx = 0; | ||||
| 	} else if (dmar_map_gfx) { | ||||
| 		/* we have to ensure the gfx device is idle before we flush */ | ||||
| 		pr_info("Disabling batched IOTLB flush on Ironlake\n"); | ||||
| 		pci_info(dev, "Disabling batched IOTLB flush on Ironlake\n"); | ||||
| 		intel_iommu_strict = 1; | ||||
|        } | ||||
| } | ||||
|  | ||||
| @ -466,8 +466,8 @@ void intel_pasid_tear_down_entry(struct intel_iommu *iommu, | ||||
| 	if (WARN_ON(!pte)) | ||||
| 		return; | ||||
| 
 | ||||
| 	intel_pasid_clear_entry(dev, pasid); | ||||
| 	did = pasid_get_domain_id(pte); | ||||
| 	intel_pasid_clear_entry(dev, pasid); | ||||
| 
 | ||||
| 	if (!ecap_coherent(iommu->ecap)) | ||||
| 		clflush_cache_range(pte, sizeof(*pte)); | ||||
|  | ||||
| @ -180,14 +180,6 @@ static void intel_flush_svm_range(struct intel_svm *svm, unsigned long address, | ||||
| 	rcu_read_unlock(); | ||||
| } | ||||
| 
 | ||||
| static void intel_change_pte(struct mmu_notifier *mn, struct mm_struct *mm, | ||||
| 			     unsigned long address, pte_t pte) | ||||
| { | ||||
| 	struct intel_svm *svm = container_of(mn, struct intel_svm, notifier); | ||||
| 
 | ||||
| 	intel_flush_svm_range(svm, address, 1, 1, 0); | ||||
| } | ||||
| 
 | ||||
| /* Pages have been freed at this point */ | ||||
| static void intel_invalidate_range(struct mmu_notifier *mn, | ||||
| 				   struct mm_struct *mm, | ||||
| @ -227,7 +219,6 @@ static void intel_mm_release(struct mmu_notifier *mn, struct mm_struct *mm) | ||||
| 
 | ||||
| static const struct mmu_notifier_ops intel_mmuops = { | ||||
| 	.release = intel_mm_release, | ||||
| 	.change_pte = intel_change_pte, | ||||
| 	.invalidate_range = intel_invalidate_range, | ||||
| }; | ||||
| 
 | ||||
| @ -243,7 +234,7 @@ int intel_svm_bind_mm(struct device *dev, int *pasid, int flags, struct svm_dev_ | ||||
| 	int pasid_max; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!iommu) | ||||
| 	if (!iommu || dmar_disabled) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (dev_is_pci(dev)) { | ||||
| @ -470,20 +461,31 @@ EXPORT_SYMBOL_GPL(intel_svm_is_pasid_valid); | ||||
| 
 | ||||
| /* Page request queue descriptor */ | ||||
| struct page_req_dsc { | ||||
| 	u64 srr:1; | ||||
| 	u64 bof:1; | ||||
| 	u64 pasid_present:1; | ||||
| 	u64 lpig:1; | ||||
| 	u64 pasid:20; | ||||
| 	u64 bus:8; | ||||
| 	u64 private:23; | ||||
| 	u64 prg_index:9; | ||||
| 	u64 rd_req:1; | ||||
| 	u64 wr_req:1; | ||||
| 	u64 exe_req:1; | ||||
| 	u64 priv_req:1; | ||||
| 	u64 devfn:8; | ||||
| 	u64 addr:52; | ||||
| 	union { | ||||
| 		struct { | ||||
| 			u64 type:8; | ||||
| 			u64 pasid_present:1; | ||||
| 			u64 priv_data_present:1; | ||||
| 			u64 rsvd:6; | ||||
| 			u64 rid:16; | ||||
| 			u64 pasid:20; | ||||
| 			u64 exe_req:1; | ||||
| 			u64 pm_req:1; | ||||
| 			u64 rsvd2:10; | ||||
| 		}; | ||||
| 		u64 qw_0; | ||||
| 	}; | ||||
| 	union { | ||||
| 		struct { | ||||
| 			u64 rd_req:1; | ||||
| 			u64 wr_req:1; | ||||
| 			u64 lpig:1; | ||||
| 			u64 prg_index:9; | ||||
| 			u64 addr:52; | ||||
| 		}; | ||||
| 		u64 qw_1; | ||||
| 	}; | ||||
| 	u64 priv_data[2]; | ||||
| }; | ||||
| 
 | ||||
| #define PRQ_RING_MASK ((0x1000 << PRQ_ORDER) - 0x10) | ||||
| @ -596,7 +598,7 @@ static irqreturn_t prq_event_thread(int irq, void *d) | ||||
| 		/* Accounting for major/minor faults? */ | ||||
| 		rcu_read_lock(); | ||||
| 		list_for_each_entry_rcu(sdev, &svm->devs, list) { | ||||
| 			if (sdev->sid == PCI_DEVID(req->bus, req->devfn)) | ||||
| 			if (sdev->sid == req->rid) | ||||
| 				break; | ||||
| 		} | ||||
| 		/* Other devices can go away, but the drivers are not permitted
 | ||||
| @ -609,33 +611,35 @@ static irqreturn_t prq_event_thread(int irq, void *d) | ||||
| 
 | ||||
| 		if (sdev && sdev->ops && sdev->ops->fault_cb) { | ||||
| 			int rwxp = (req->rd_req << 3) | (req->wr_req << 2) | | ||||
| 				(req->exe_req << 1) | (req->priv_req); | ||||
| 			sdev->ops->fault_cb(sdev->dev, req->pasid, req->addr, req->private, rwxp, result); | ||||
| 				(req->exe_req << 1) | (req->pm_req); | ||||
| 			sdev->ops->fault_cb(sdev->dev, req->pasid, req->addr, | ||||
| 					    req->priv_data, rwxp, result); | ||||
| 		} | ||||
| 		/* We get here in the error case where the PASID lookup failed,
 | ||||
| 		   and these can be NULL. Do not use them below this point! */ | ||||
| 		sdev = NULL; | ||||
| 		svm = NULL; | ||||
| 	no_pasid: | ||||
| 		if (req->lpig) { | ||||
| 			/* Page Group Response */ | ||||
| 		if (req->lpig || req->priv_data_present) { | ||||
| 			/*
 | ||||
| 			 * Per VT-d spec. v3.0 ch7.7, system software must | ||||
| 			 * respond with page group response if private data | ||||
| 			 * is present (PDP) or last page in group (LPIG) bit | ||||
| 			 * is set. This is an additional VT-d feature beyond | ||||
| 			 * PCI ATS spec. | ||||
| 			 */ | ||||
| 			resp.qw0 = QI_PGRP_PASID(req->pasid) | | ||||
| 				QI_PGRP_DID((req->bus << 8) | req->devfn) | | ||||
| 				QI_PGRP_DID(req->rid) | | ||||
| 				QI_PGRP_PASID_P(req->pasid_present) | | ||||
| 				QI_PGRP_PDP(req->pasid_present) | | ||||
| 				QI_PGRP_RESP_CODE(result) | | ||||
| 				QI_PGRP_RESP_TYPE; | ||||
| 			resp.qw1 = QI_PGRP_IDX(req->prg_index) | | ||||
| 				QI_PGRP_PRIV(req->private) | | ||||
| 				QI_PGRP_RESP_CODE(result); | ||||
| 		} else if (req->srr) { | ||||
| 			/* Page Stream Response */ | ||||
| 			resp.qw0 = QI_PSTRM_IDX(req->prg_index) | | ||||
| 				QI_PSTRM_PRIV(req->private) | | ||||
| 				QI_PSTRM_BUS(req->bus) | | ||||
| 				QI_PSTRM_PASID(req->pasid) | | ||||
| 				QI_PSTRM_RESP_TYPE; | ||||
| 			resp.qw1 = QI_PSTRM_ADDR(address) | | ||||
| 				QI_PSTRM_DEVFN(req->devfn) | | ||||
| 				QI_PSTRM_RESP_CODE(result); | ||||
| 				QI_PGRP_LPIG(req->lpig); | ||||
| 
 | ||||
| 			if (req->priv_data_present) | ||||
| 				memcpy(&resp.qw2, req->priv_data, | ||||
| 				       sizeof(req->priv_data)); | ||||
| 		} | ||||
| 		resp.qw2 = 0; | ||||
| 		resp.qw3 = 0; | ||||
|  | ||||
| @ -294,6 +294,18 @@ static void set_irte_sid(struct irte *irte, unsigned int svt, | ||||
| 	irte->sid = sid; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Set an IRTE to match only the bus number. Interrupt requests that reference | ||||
|  * this IRTE must have a requester-id whose bus number is between or equal | ||||
|  * to the start_bus and end_bus arguments. | ||||
|  */ | ||||
| static void set_irte_verify_bus(struct irte *irte, unsigned int start_bus, | ||||
| 				unsigned int end_bus) | ||||
| { | ||||
| 	set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16, | ||||
| 		     (start_bus << 8) | end_bus); | ||||
| } | ||||
| 
 | ||||
| static int set_ioapic_sid(struct irte *irte, int apic) | ||||
| { | ||||
| 	int i; | ||||
| @ -356,6 +368,8 @@ static int set_hpet_sid(struct irte *irte, u8 id) | ||||
| struct set_msi_sid_data { | ||||
| 	struct pci_dev *pdev; | ||||
| 	u16 alias; | ||||
| 	int count; | ||||
| 	int busmatch_count; | ||||
| }; | ||||
| 
 | ||||
| static int set_msi_sid_cb(struct pci_dev *pdev, u16 alias, void *opaque) | ||||
| @ -364,6 +378,10 @@ static int set_msi_sid_cb(struct pci_dev *pdev, u16 alias, void *opaque) | ||||
| 
 | ||||
| 	data->pdev = pdev; | ||||
| 	data->alias = alias; | ||||
| 	data->count++; | ||||
| 
 | ||||
| 	if (PCI_BUS_NUM(alias) == pdev->bus->number) | ||||
| 		data->busmatch_count++; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| @ -375,6 +393,8 @@ static int set_msi_sid(struct irte *irte, struct pci_dev *dev) | ||||
| 	if (!irte || !dev) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	data.count = 0; | ||||
| 	data.busmatch_count = 0; | ||||
| 	pci_for_each_dma_alias(dev, set_msi_sid_cb, &data); | ||||
| 
 | ||||
| 	/*
 | ||||
| @ -383,6 +403,11 @@ static int set_msi_sid(struct irte *irte, struct pci_dev *dev) | ||||
| 	 * device is the case of a PCIe-to-PCI bridge, where the alias is for | ||||
| 	 * the subordinate bus.  In this case we can only verify the bus. | ||||
| 	 * | ||||
| 	 * If there are multiple aliases, all with the same bus number, | ||||
| 	 * then all we can do is verify the bus. This is typical in NTB | ||||
| 	 * hardware which use proxy IDs where the device will generate traffic | ||||
| 	 * from multiple devfn numbers on the same bus. | ||||
| 	 * | ||||
| 	 * If the alias device is on a different bus than our source device | ||||
| 	 * then we have a topology based alias, use it. | ||||
| 	 * | ||||
| @ -391,9 +416,10 @@ static int set_msi_sid(struct irte *irte, struct pci_dev *dev) | ||||
| 	 * original device. | ||||
| 	 */ | ||||
| 	if (PCI_BUS_NUM(data.alias) != data.pdev->bus->number) | ||||
| 		set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16, | ||||
| 			     PCI_DEVID(PCI_BUS_NUM(data.alias), | ||||
| 				       dev->bus->number)); | ||||
| 		set_irte_verify_bus(irte, PCI_BUS_NUM(data.alias), | ||||
| 				    dev->bus->number); | ||||
| 	else if (data.count >= 2 && data.busmatch_count == data.count) | ||||
| 		set_irte_verify_bus(irte, dev->bus->number, dev->bus->number); | ||||
| 	else if (data.pdev->bus->number != dev->bus->number) | ||||
| 		set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16, data.alias); | ||||
| 	else | ||||
|  | ||||
| @ -35,6 +35,7 @@ | ||||
| #include <linux/atomic.h> | ||||
| #include <linux/dma-mapping.h> | ||||
| #include <linux/gfp.h> | ||||
| #include <linux/io-pgtable.h> | ||||
| #include <linux/iommu.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/kmemleak.h> | ||||
| @ -45,8 +46,6 @@ | ||||
| 
 | ||||
| #include <asm/barrier.h> | ||||
| 
 | ||||
| #include "io-pgtable.h" | ||||
| 
 | ||||
| /* Struct accessors */ | ||||
| #define io_pgtable_to_data(x)						\ | ||||
| 	container_of((x), struct arm_v7s_io_pgtable, iop) | ||||
| @ -217,7 +216,8 @@ static void *__arm_v7s_alloc_table(int lvl, gfp_t gfp, | ||||
| 		if (dma != phys) | ||||
| 			goto out_unmap; | ||||
| 	} | ||||
| 	kmemleak_ignore(table); | ||||
| 	if (lvl == 2) | ||||
| 		kmemleak_ignore(table); | ||||
| 	return table; | ||||
| 
 | ||||
| out_unmap: | ||||
|  | ||||
| @ -22,6 +22,7 @@ | ||||
| 
 | ||||
| #include <linux/atomic.h> | ||||
| #include <linux/bitops.h> | ||||
| #include <linux/io-pgtable.h> | ||||
| #include <linux/iommu.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/sizes.h> | ||||
| @ -31,8 +32,6 @@ | ||||
| 
 | ||||
| #include <asm/barrier.h> | ||||
| 
 | ||||
| #include "io-pgtable.h" | ||||
| 
 | ||||
| #define ARM_LPAE_MAX_ADDR_BITS		52 | ||||
| #define ARM_LPAE_S2_MAX_CONCAT_PAGES	16 | ||||
| #define ARM_LPAE_MAX_LEVELS		4 | ||||
|  | ||||
| @ -19,11 +19,10 @@ | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/bug.h> | ||||
| #include <linux/io-pgtable.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/types.h> | ||||
| 
 | ||||
| #include "io-pgtable.h" | ||||
| 
 | ||||
| static const struct io_pgtable_init_fns * | ||||
| io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = { | ||||
| #ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE | ||||
| @ -61,6 +60,7 @@ struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt, | ||||
| 
 | ||||
| 	return &iop->ops; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(alloc_io_pgtable_ops); | ||||
| 
 | ||||
| /*
 | ||||
|  * It is the IOMMU driver's responsibility to ensure that the page table | ||||
| @ -77,3 +77,4 @@ void free_io_pgtable_ops(struct io_pgtable_ops *ops) | ||||
| 	io_pgtable_tlb_flush_all(iop); | ||||
| 	io_pgtable_init_table[iop->fmt]->free(iop); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(free_io_pgtable_ops); | ||||
|  | ||||
| @ -12,6 +12,7 @@ | ||||
| #include <linux/debugfs.h> | ||||
| 
 | ||||
| struct dentry *iommu_debugfs_dir; | ||||
| EXPORT_SYMBOL_GPL(iommu_debugfs_dir); | ||||
| 
 | ||||
| /**
 | ||||
|  * iommu_debugfs_setup - create the top-level iommu directory in debugfs | ||||
| @ -23,9 +24,9 @@ struct dentry *iommu_debugfs_dir; | ||||
|  * Emit a strong warning at boot time to indicate that this feature is | ||||
|  * enabled. | ||||
|  * | ||||
|  * This function is called from iommu_init; drivers may then call | ||||
|  * iommu_debugfs_new_driver_dir() to instantiate a vendor-specific | ||||
|  * directory to be used to expose internal data. | ||||
|  * This function is called from iommu_init; drivers may then use | ||||
|  * iommu_debugfs_dir to instantiate a vendor-specific directory to be used | ||||
|  * to expose internal data. | ||||
|  */ | ||||
| void iommu_debugfs_setup(void) | ||||
| { | ||||
| @ -48,19 +49,3 @@ void iommu_debugfs_setup(void) | ||||
| 		pr_warn("*************************************************************\n"); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * iommu_debugfs_new_driver_dir - create a vendor directory under debugfs/iommu | ||||
|  * @vendor: name of the vendor-specific subdirectory to create | ||||
|  * | ||||
|  * This function is called by an IOMMU driver to create the top-level debugfs | ||||
|  * directory for that driver. | ||||
|  * | ||||
|  * Return: upon success, a pointer to the dentry for the new directory. | ||||
|  *         NULL in case of failure. | ||||
|  */ | ||||
| struct dentry *iommu_debugfs_new_driver_dir(const char *vendor) | ||||
| { | ||||
| 	return debugfs_create_dir(vendor, iommu_debugfs_dir); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(iommu_debugfs_new_driver_dir); | ||||
|  | ||||
| @ -668,7 +668,7 @@ rename: | ||||
| 
 | ||||
| 	trace_add_device_to_group(group->id, dev); | ||||
| 
 | ||||
| 	pr_info("Adding device %s to group %d\n", dev_name(dev), group->id); | ||||
| 	dev_info(dev, "Adding to iommu group %d\n", group->id); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| @ -684,7 +684,7 @@ err_remove_link: | ||||
| 	sysfs_remove_link(&dev->kobj, "iommu_group"); | ||||
| err_free_device: | ||||
| 	kfree(device); | ||||
| 	pr_err("Failed to add device %s to group %d: %d\n", dev_name(dev), group->id, ret); | ||||
| 	dev_err(dev, "Failed to add to iommu group %d: %d\n", group->id, ret); | ||||
| 	return ret; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(iommu_group_add_device); | ||||
| @ -701,7 +701,7 @@ void iommu_group_remove_device(struct device *dev) | ||||
| 	struct iommu_group *group = dev->iommu_group; | ||||
| 	struct group_device *tmp_device, *device = NULL; | ||||
| 
 | ||||
| 	pr_info("Removing device %s from group %d\n", dev_name(dev), group->id); | ||||
| 	dev_info(dev, "Removing from iommu group %d\n", group->id); | ||||
| 
 | ||||
| 	/* Pre-notify listeners that a device is being removed. */ | ||||
| 	blocking_notifier_call_chain(&group->notifier, | ||||
| @ -1585,13 +1585,14 @@ static size_t iommu_pgsize(struct iommu_domain *domain, | ||||
| int iommu_map(struct iommu_domain *domain, unsigned long iova, | ||||
| 	      phys_addr_t paddr, size_t size, int prot) | ||||
| { | ||||
| 	const struct iommu_ops *ops = domain->ops; | ||||
| 	unsigned long orig_iova = iova; | ||||
| 	unsigned int min_pagesz; | ||||
| 	size_t orig_size = size; | ||||
| 	phys_addr_t orig_paddr = paddr; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	if (unlikely(domain->ops->map == NULL || | ||||
| 	if (unlikely(ops->map == NULL || | ||||
| 		     domain->pgsize_bitmap == 0UL)) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| @ -1620,7 +1621,7 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova, | ||||
| 		pr_debug("mapping: iova 0x%lx pa %pa pgsize 0x%zx\n", | ||||
| 			 iova, &paddr, pgsize); | ||||
| 
 | ||||
| 		ret = domain->ops->map(domain, iova, paddr, pgsize, prot); | ||||
| 		ret = ops->map(domain, iova, paddr, pgsize, prot); | ||||
| 		if (ret) | ||||
| 			break; | ||||
| 
 | ||||
| @ -1629,6 +1630,9 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova, | ||||
| 		size -= pgsize; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ops->iotlb_sync_map) | ||||
| 		ops->iotlb_sync_map(domain); | ||||
| 
 | ||||
| 	/* unroll mapping in case something went wrong */ | ||||
| 	if (ret) | ||||
| 		iommu_unmap(domain, orig_iova, orig_size - size); | ||||
| @ -1951,7 +1955,7 @@ int iommu_request_dm_for_dev(struct device *dev) | ||||
| 		iommu_domain_free(group->default_domain); | ||||
| 	group->default_domain = dm_domain; | ||||
| 
 | ||||
| 	pr_info("Using direct mapping for device %s\n", dev_name(dev)); | ||||
| 	dev_info(dev, "Using iommu direct mapping\n"); | ||||
| 
 | ||||
| 	ret = 0; | ||||
| out: | ||||
|  | ||||
| @ -15,6 +15,7 @@ | ||||
| #include <linux/init.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/io-pgtable.h> | ||||
| #include <linux/iommu.h> | ||||
| #include <linux/of.h> | ||||
| #include <linux/of_device.h> | ||||
| @ -35,8 +36,6 @@ | ||||
| #define arm_iommu_detach_device(...)	do {} while (0) | ||||
| #endif | ||||
| 
 | ||||
| #include "io-pgtable.h" | ||||
| 
 | ||||
| #define IPMMU_CTX_MAX 8 | ||||
| 
 | ||||
| struct ipmmu_features { | ||||
|  | ||||
| @ -103,6 +103,9 @@ int __init irq_remapping_prepare(void) | ||||
| 	else if (IS_ENABLED(CONFIG_AMD_IOMMU) && | ||||
| 		 amd_iommu_irq_ops.prepare() == 0) | ||||
| 		remap_ops = &amd_iommu_irq_ops; | ||||
| 	else if (IS_ENABLED(CONFIG_HYPERV_IOMMU) && | ||||
| 		 hyperv_irq_remap_ops.prepare() == 0) | ||||
| 		remap_ops = &hyperv_irq_remap_ops; | ||||
| 	else | ||||
| 		return -ENOSYS; | ||||
| 
 | ||||
|  | ||||
| @ -64,6 +64,7 @@ struct irq_remap_ops { | ||||
| 
 | ||||
| extern struct irq_remap_ops intel_irq_remap_ops; | ||||
| extern struct irq_remap_ops amd_iommu_irq_ops; | ||||
| extern struct irq_remap_ops hyperv_irq_remap_ops; | ||||
| 
 | ||||
| #else  /* CONFIG_IRQ_REMAP */ | ||||
| 
 | ||||
|  | ||||
| @ -23,6 +23,7 @@ | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/io-pgtable.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/list.h> | ||||
| #include <linux/spinlock.h> | ||||
| @ -37,7 +38,6 @@ | ||||
| 
 | ||||
| #include "msm_iommu_hw-8xxx.h" | ||||
| #include "msm_iommu.h" | ||||
| #include "io-pgtable.h" | ||||
| 
 | ||||
| #define MRC(reg, processor, op1, crn, crm, op2)				\ | ||||
| __asm__ __volatile__ (							\ | ||||
| @ -461,10 +461,10 @@ static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) | ||||
| 				master->num = | ||||
| 					msm_iommu_alloc_ctx(iommu->context_map, | ||||
| 							    0, iommu->ncb); | ||||
| 					if (IS_ERR_VALUE(master->num)) { | ||||
| 						ret = -ENODEV; | ||||
| 						goto fail; | ||||
| 					} | ||||
| 				if (IS_ERR_VALUE(master->num)) { | ||||
| 					ret = -ENODEV; | ||||
| 					goto fail; | ||||
| 				} | ||||
| 				config_mids(iommu, master); | ||||
| 				__program_context(iommu->base, master->num, | ||||
| 						  priv); | ||||
|  | ||||
| @ -19,13 +19,12 @@ | ||||
| #include <linux/component.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/io-pgtable.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; | ||||
|  | ||||
| @ -474,7 +474,7 @@ static int mtk_iommu_add_device(struct device *dev) | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	return iommu_device_link(&data->iommu, dev);; | ||||
| 	return iommu_device_link(&data->iommu, dev); | ||||
| } | ||||
| 
 | ||||
| static void mtk_iommu_remove_device(struct device *dev) | ||||
|  | ||||
| @ -26,6 +26,7 @@ | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/io-64-nonatomic-hi-lo.h> | ||||
| #include <linux/io-pgtable.h> | ||||
| #include <linux/iommu.h> | ||||
| #include <linux/iopoll.h> | ||||
| #include <linux/kconfig.h> | ||||
| @ -42,7 +43,6 @@ | ||||
| #include <linux/slab.h> | ||||
| #include <linux/spinlock.h> | ||||
| 
 | ||||
| #include "io-pgtable.h" | ||||
| #include "arm-smmu-regs.h" | ||||
| 
 | ||||
| #define SMMU_INTR_SEL_NS     0x2000 | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| /*
 | ||||
|  * IOMMU API for GART in Tegra20 | ||||
|  * IOMMU API for Graphics Address Relocation Table on Tegra20 | ||||
|  * | ||||
|  * Copyright (c) 2010-2012, NVIDIA CORPORATION.  All rights reserved. | ||||
|  * | ||||
| @ -19,101 +19,75 @@ | ||||
|  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  */ | ||||
| 
 | ||||
| #define pr_fmt(fmt)	"%s(): " fmt, __func__ | ||||
| #define dev_fmt(fmt)	"gart: " fmt | ||||
| 
 | ||||
| #include <linux/init.h> | ||||
| #include <linux/moduleparam.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/vmalloc.h> | ||||
| #include <linux/mm.h> | ||||
| #include <linux/list.h> | ||||
| #include <linux/device.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/iommu.h> | ||||
| #include <linux/of.h> | ||||
| #include <linux/moduleparam.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <linux/vmalloc.h> | ||||
| 
 | ||||
| #include <asm/cacheflush.h> | ||||
| 
 | ||||
| /* bitmap of the page sizes currently supported */ | ||||
| #define GART_IOMMU_PGSIZES	(SZ_4K) | ||||
| #include <soc/tegra/mc.h> | ||||
| 
 | ||||
| #define GART_REG_BASE		0x24 | ||||
| #define GART_CONFIG		(0x24 - GART_REG_BASE) | ||||
| #define GART_ENTRY_ADDR		(0x28 - GART_REG_BASE) | ||||
| #define GART_ENTRY_DATA		(0x2c - GART_REG_BASE) | ||||
| #define GART_ENTRY_PHYS_ADDR_VALID	(1 << 31) | ||||
| 
 | ||||
| #define GART_ENTRY_PHYS_ADDR_VALID	BIT(31) | ||||
| 
 | ||||
| #define GART_PAGE_SHIFT		12 | ||||
| #define GART_PAGE_SIZE		(1 << GART_PAGE_SHIFT) | ||||
| #define GART_PAGE_MASK						\ | ||||
| 	(~(GART_PAGE_SIZE - 1) & ~GART_ENTRY_PHYS_ADDR_VALID) | ||||
| #define GART_PAGE_MASK		GENMASK(30, GART_PAGE_SHIFT) | ||||
| 
 | ||||
| struct gart_client { | ||||
| 	struct device		*dev; | ||||
| 	struct list_head	list; | ||||
| }; | ||||
| /* bitmap of the page sizes currently supported */ | ||||
| #define GART_IOMMU_PGSIZES	(GART_PAGE_SIZE) | ||||
| 
 | ||||
| struct gart_device { | ||||
| 	void __iomem		*regs; | ||||
| 	u32			*savedata; | ||||
| 	u32			page_count;	/* total remappable size */ | ||||
| 	dma_addr_t		iovmm_base;	/* offset to vmm_area */ | ||||
| 	unsigned long		iovmm_base;	/* offset to vmm_area start */ | ||||
| 	unsigned long		iovmm_end;	/* offset to vmm_area end */ | ||||
| 	spinlock_t		pte_lock;	/* for pagetable */ | ||||
| 	struct list_head	client; | ||||
| 	spinlock_t		client_lock;	/* for client list */ | ||||
| 	struct device		*dev; | ||||
| 
 | ||||
| 	spinlock_t		dom_lock;	/* for active domain */ | ||||
| 	unsigned int		active_devices;	/* number of active devices */ | ||||
| 	struct iommu_domain	*active_domain;	/* current active domain */ | ||||
| 	struct iommu_device	iommu;		/* IOMMU Core handle */ | ||||
| }; | ||||
| 
 | ||||
| struct gart_domain { | ||||
| 	struct iommu_domain domain;		/* generic domain handle */ | ||||
| 	struct gart_device *gart;		/* link to gart device   */ | ||||
| 	struct device		*dev; | ||||
| }; | ||||
| 
 | ||||
| static struct gart_device *gart_handle; /* unique for a system */ | ||||
| 
 | ||||
| static bool gart_debug; | ||||
| 
 | ||||
| #define GART_PTE(_pfn)						\ | ||||
| 	(GART_ENTRY_PHYS_ADDR_VALID | ((_pfn) << PAGE_SHIFT)) | ||||
| 
 | ||||
| static struct gart_domain *to_gart_domain(struct iommu_domain *dom) | ||||
| { | ||||
| 	return container_of(dom, struct gart_domain, domain); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Any interaction between any block on PPSB and a block on APB or AHB | ||||
|  * must have these read-back to ensure the APB/AHB bus transaction is | ||||
|  * complete before initiating activity on the PPSB block. | ||||
|  */ | ||||
| #define FLUSH_GART_REGS(gart)	((void)readl((gart)->regs + GART_CONFIG)) | ||||
| #define FLUSH_GART_REGS(gart)	readl_relaxed((gart)->regs + GART_CONFIG) | ||||
| 
 | ||||
| #define for_each_gart_pte(gart, iova)					\ | ||||
| 	for (iova = gart->iovmm_base;					\ | ||||
| 	     iova < gart->iovmm_base + GART_PAGE_SIZE * gart->page_count; \ | ||||
| 	     iova < gart->iovmm_end;					\ | ||||
| 	     iova += GART_PAGE_SIZE) | ||||
| 
 | ||||
| static inline void gart_set_pte(struct gart_device *gart, | ||||
| 				unsigned long offs, u32 pte) | ||||
| 				unsigned long iova, unsigned long pte) | ||||
| { | ||||
| 	writel(offs, gart->regs + GART_ENTRY_ADDR); | ||||
| 	writel(pte, gart->regs + GART_ENTRY_DATA); | ||||
| 
 | ||||
| 	dev_dbg(gart->dev, "%s %08lx:%08x\n", | ||||
| 		 pte ? "map" : "unmap", offs, pte & GART_PAGE_MASK); | ||||
| 	writel_relaxed(iova, gart->regs + GART_ENTRY_ADDR); | ||||
| 	writel_relaxed(pte, gart->regs + GART_ENTRY_DATA); | ||||
| } | ||||
| 
 | ||||
| static inline unsigned long gart_read_pte(struct gart_device *gart, | ||||
| 					  unsigned long offs) | ||||
| 					  unsigned long iova) | ||||
| { | ||||
| 	unsigned long pte; | ||||
| 
 | ||||
| 	writel(offs, gart->regs + GART_ENTRY_ADDR); | ||||
| 	pte = readl(gart->regs + GART_ENTRY_DATA); | ||||
| 	writel_relaxed(iova, gart->regs + GART_ENTRY_ADDR); | ||||
| 	pte = readl_relaxed(gart->regs + GART_ENTRY_DATA); | ||||
| 
 | ||||
| 	return pte; | ||||
| } | ||||
| @ -125,224 +99,155 @@ static void do_gart_setup(struct gart_device *gart, const u32 *data) | ||||
| 	for_each_gart_pte(gart, iova) | ||||
| 		gart_set_pte(gart, iova, data ? *(data++) : 0); | ||||
| 
 | ||||
| 	writel(1, gart->regs + GART_CONFIG); | ||||
| 	writel_relaxed(1, gart->regs + GART_CONFIG); | ||||
| 	FLUSH_GART_REGS(gart); | ||||
| } | ||||
| 
 | ||||
| #ifdef DEBUG | ||||
| static void gart_dump_table(struct gart_device *gart) | ||||
| static inline bool gart_iova_range_invalid(struct gart_device *gart, | ||||
| 					   unsigned long iova, size_t bytes) | ||||
| { | ||||
| 	unsigned long iova; | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&gart->pte_lock, flags); | ||||
| 	for_each_gart_pte(gart, iova) { | ||||
| 		unsigned long pte; | ||||
| 
 | ||||
| 		pte = gart_read_pte(gart, iova); | ||||
| 
 | ||||
| 		dev_dbg(gart->dev, "%s %08lx:%08lx\n", | ||||
| 			(GART_ENTRY_PHYS_ADDR_VALID & pte) ? "v" : " ", | ||||
| 			iova, pte & GART_PAGE_MASK); | ||||
| 	} | ||||
| 	spin_unlock_irqrestore(&gart->pte_lock, flags); | ||||
| 	return unlikely(iova < gart->iovmm_base || bytes != GART_PAGE_SIZE || | ||||
| 			iova + bytes > gart->iovmm_end); | ||||
| } | ||||
| #else | ||||
| static inline void gart_dump_table(struct gart_device *gart) | ||||
| 
 | ||||
| static inline bool gart_pte_valid(struct gart_device *gart, unsigned long iova) | ||||
| { | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| static inline bool gart_iova_range_valid(struct gart_device *gart, | ||||
| 					 unsigned long iova, size_t bytes) | ||||
| { | ||||
| 	unsigned long iova_start, iova_end, gart_start, gart_end; | ||||
| 
 | ||||
| 	iova_start = iova; | ||||
| 	iova_end = iova_start + bytes - 1; | ||||
| 	gart_start = gart->iovmm_base; | ||||
| 	gart_end = gart_start + gart->page_count * GART_PAGE_SIZE - 1; | ||||
| 
 | ||||
| 	if (iova_start < gart_start) | ||||
| 		return false; | ||||
| 	if (iova_end > gart_end) | ||||
| 		return false; | ||||
| 	return true; | ||||
| 	return !!(gart_read_pte(gart, iova) & GART_ENTRY_PHYS_ADDR_VALID); | ||||
| } | ||||
| 
 | ||||
| static int gart_iommu_attach_dev(struct iommu_domain *domain, | ||||
| 				 struct device *dev) | ||||
| { | ||||
| 	struct gart_domain *gart_domain = to_gart_domain(domain); | ||||
| 	struct gart_device *gart = gart_domain->gart; | ||||
| 	struct gart_client *client, *c; | ||||
| 	int err = 0; | ||||
| 	struct gart_device *gart = gart_handle; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	client = devm_kzalloc(gart->dev, sizeof(*c), GFP_KERNEL); | ||||
| 	if (!client) | ||||
| 		return -ENOMEM; | ||||
| 	client->dev = dev; | ||||
| 	spin_lock(&gart->dom_lock); | ||||
| 
 | ||||
| 	spin_lock(&gart->client_lock); | ||||
| 	list_for_each_entry(c, &gart->client, list) { | ||||
| 		if (c->dev == dev) { | ||||
| 			dev_err(gart->dev, | ||||
| 				"%s is already attached\n", dev_name(dev)); | ||||
| 			err = -EINVAL; | ||||
| 			goto fail; | ||||
| 		} | ||||
| 	if (gart->active_domain && gart->active_domain != domain) { | ||||
| 		ret = -EBUSY; | ||||
| 	} else if (dev->archdata.iommu != domain) { | ||||
| 		dev->archdata.iommu = domain; | ||||
| 		gart->active_domain = domain; | ||||
| 		gart->active_devices++; | ||||
| 	} | ||||
| 	list_add(&client->list, &gart->client); | ||||
| 	spin_unlock(&gart->client_lock); | ||||
| 	dev_dbg(gart->dev, "Attached %s\n", dev_name(dev)); | ||||
| 	return 0; | ||||
| 
 | ||||
| fail: | ||||
| 	devm_kfree(gart->dev, client); | ||||
| 	spin_unlock(&gart->client_lock); | ||||
| 	return err; | ||||
| 	spin_unlock(&gart->dom_lock); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static void gart_iommu_detach_dev(struct iommu_domain *domain, | ||||
| 				  struct device *dev) | ||||
| { | ||||
| 	struct gart_domain *gart_domain = to_gart_domain(domain); | ||||
| 	struct gart_device *gart = gart_domain->gart; | ||||
| 	struct gart_client *c; | ||||
| 	struct gart_device *gart = gart_handle; | ||||
| 
 | ||||
| 	spin_lock(&gart->client_lock); | ||||
| 	spin_lock(&gart->dom_lock); | ||||
| 
 | ||||
| 	list_for_each_entry(c, &gart->client, list) { | ||||
| 		if (c->dev == dev) { | ||||
| 			list_del(&c->list); | ||||
| 			devm_kfree(gart->dev, c); | ||||
| 			dev_dbg(gart->dev, "Detached %s\n", dev_name(dev)); | ||||
| 			goto out; | ||||
| 		} | ||||
| 	if (dev->archdata.iommu == domain) { | ||||
| 		dev->archdata.iommu = NULL; | ||||
| 
 | ||||
| 		if (--gart->active_devices == 0) | ||||
| 			gart->active_domain = NULL; | ||||
| 	} | ||||
| 	dev_err(gart->dev, "Couldn't find\n"); | ||||
| out: | ||||
| 	spin_unlock(&gart->client_lock); | ||||
| 
 | ||||
| 	spin_unlock(&gart->dom_lock); | ||||
| } | ||||
| 
 | ||||
| static struct iommu_domain *gart_iommu_domain_alloc(unsigned type) | ||||
| { | ||||
| 	struct gart_domain *gart_domain; | ||||
| 	struct gart_device *gart; | ||||
| 	struct iommu_domain *domain; | ||||
| 
 | ||||
| 	if (type != IOMMU_DOMAIN_UNMANAGED) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	gart = gart_handle; | ||||
| 	if (!gart) | ||||
| 		return NULL; | ||||
| 	domain = kzalloc(sizeof(*domain), GFP_KERNEL); | ||||
| 	if (domain) { | ||||
| 		domain->geometry.aperture_start = gart_handle->iovmm_base; | ||||
| 		domain->geometry.aperture_end = gart_handle->iovmm_end - 1; | ||||
| 		domain->geometry.force_aperture = true; | ||||
| 	} | ||||
| 
 | ||||
| 	gart_domain = kzalloc(sizeof(*gart_domain), GFP_KERNEL); | ||||
| 	if (!gart_domain) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	gart_domain->gart = gart; | ||||
| 	gart_domain->domain.geometry.aperture_start = gart->iovmm_base; | ||||
| 	gart_domain->domain.geometry.aperture_end = gart->iovmm_base + | ||||
| 					gart->page_count * GART_PAGE_SIZE - 1; | ||||
| 	gart_domain->domain.geometry.force_aperture = true; | ||||
| 
 | ||||
| 	return &gart_domain->domain; | ||||
| 	return domain; | ||||
| } | ||||
| 
 | ||||
| static void gart_iommu_domain_free(struct iommu_domain *domain) | ||||
| { | ||||
| 	struct gart_domain *gart_domain = to_gart_domain(domain); | ||||
| 	struct gart_device *gart = gart_domain->gart; | ||||
| 	WARN_ON(gart_handle->active_domain == domain); | ||||
| 	kfree(domain); | ||||
| } | ||||
| 
 | ||||
| 	if (gart) { | ||||
| 		spin_lock(&gart->client_lock); | ||||
| 		if (!list_empty(&gart->client)) { | ||||
| 			struct gart_client *c; | ||||
| 
 | ||||
| 			list_for_each_entry(c, &gart->client, list) | ||||
| 				gart_iommu_detach_dev(domain, c->dev); | ||||
| 		} | ||||
| 		spin_unlock(&gart->client_lock); | ||||
| static inline int __gart_iommu_map(struct gart_device *gart, unsigned long iova, | ||||
| 				   unsigned long pa) | ||||
| { | ||||
| 	if (unlikely(gart_debug && gart_pte_valid(gart, iova))) { | ||||
| 		dev_err(gart->dev, "Page entry is in-use\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	kfree(gart_domain); | ||||
| 	gart_set_pte(gart, iova, GART_ENTRY_PHYS_ADDR_VALID | pa); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova, | ||||
| 			  phys_addr_t pa, size_t bytes, int prot) | ||||
| { | ||||
| 	struct gart_domain *gart_domain = to_gart_domain(domain); | ||||
| 	struct gart_device *gart = gart_domain->gart; | ||||
| 	unsigned long flags; | ||||
| 	unsigned long pfn; | ||||
| 	unsigned long pte; | ||||
| 	struct gart_device *gart = gart_handle; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!gart_iova_range_valid(gart, iova, bytes)) | ||||
| 	if (gart_iova_range_invalid(gart, iova, bytes)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&gart->pte_lock, flags); | ||||
| 	pfn = __phys_to_pfn(pa); | ||||
| 	if (!pfn_valid(pfn)) { | ||||
| 		dev_err(gart->dev, "Invalid page: %pa\n", &pa); | ||||
| 		spin_unlock_irqrestore(&gart->pte_lock, flags); | ||||
| 	spin_lock(&gart->pte_lock); | ||||
| 	ret = __gart_iommu_map(gart, iova, (unsigned long)pa); | ||||
| 	spin_unlock(&gart->pte_lock); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static inline int __gart_iommu_unmap(struct gart_device *gart, | ||||
| 				     unsigned long iova) | ||||
| { | ||||
| 	if (unlikely(gart_debug && !gart_pte_valid(gart, iova))) { | ||||
| 		dev_err(gart->dev, "Page entry is invalid\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	if (gart_debug) { | ||||
| 		pte = gart_read_pte(gart, iova); | ||||
| 		if (pte & GART_ENTRY_PHYS_ADDR_VALID) { | ||||
| 			spin_unlock_irqrestore(&gart->pte_lock, flags); | ||||
| 			dev_err(gart->dev, "Page entry is in-use\n"); | ||||
| 			return -EBUSY; | ||||
| 		} | ||||
| 	} | ||||
| 	gart_set_pte(gart, iova, GART_PTE(pfn)); | ||||
| 	FLUSH_GART_REGS(gart); | ||||
| 	spin_unlock_irqrestore(&gart->pte_lock, flags); | ||||
| 
 | ||||
| 	gart_set_pte(gart, iova, 0); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova, | ||||
| 			       size_t bytes) | ||||
| { | ||||
| 	struct gart_domain *gart_domain = to_gart_domain(domain); | ||||
| 	struct gart_device *gart = gart_domain->gart; | ||||
| 	unsigned long flags; | ||||
| 	struct gart_device *gart = gart_handle; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (!gart_iova_range_valid(gart, iova, bytes)) | ||||
| 	if (gart_iova_range_invalid(gart, iova, bytes)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&gart->pte_lock, flags); | ||||
| 	gart_set_pte(gart, iova, 0); | ||||
| 	FLUSH_GART_REGS(gart); | ||||
| 	spin_unlock_irqrestore(&gart->pte_lock, flags); | ||||
| 	return bytes; | ||||
| 	spin_lock(&gart->pte_lock); | ||||
| 	err = __gart_iommu_unmap(gart, iova); | ||||
| 	spin_unlock(&gart->pte_lock); | ||||
| 
 | ||||
| 	return err ? 0 : bytes; | ||||
| } | ||||
| 
 | ||||
| static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain, | ||||
| 					   dma_addr_t iova) | ||||
| { | ||||
| 	struct gart_domain *gart_domain = to_gart_domain(domain); | ||||
| 	struct gart_device *gart = gart_domain->gart; | ||||
| 	struct gart_device *gart = gart_handle; | ||||
| 	unsigned long pte; | ||||
| 	phys_addr_t pa; | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	if (!gart_iova_range_valid(gart, iova, 0)) | ||||
| 	if (gart_iova_range_invalid(gart, iova, GART_PAGE_SIZE)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&gart->pte_lock, flags); | ||||
| 	spin_lock(&gart->pte_lock); | ||||
| 	pte = gart_read_pte(gart, iova); | ||||
| 	spin_unlock_irqrestore(&gart->pte_lock, flags); | ||||
| 	spin_unlock(&gart->pte_lock); | ||||
| 
 | ||||
| 	pa = (pte & GART_PAGE_MASK); | ||||
| 	if (!pfn_valid(__phys_to_pfn(pa))) { | ||||
| 		dev_err(gart->dev, "No entry for %08llx:%pa\n", | ||||
| 			 (unsigned long long)iova, &pa); | ||||
| 		gart_dump_table(gart); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	return pa; | ||||
| 	return pte & GART_PAGE_MASK; | ||||
| } | ||||
| 
 | ||||
| static bool gart_iommu_capable(enum iommu_cap cap) | ||||
| @ -352,8 +257,12 @@ static bool gart_iommu_capable(enum iommu_cap cap) | ||||
| 
 | ||||
| static int gart_iommu_add_device(struct device *dev) | ||||
| { | ||||
| 	struct iommu_group *group = iommu_group_get_for_dev(dev); | ||||
| 	struct iommu_group *group; | ||||
| 
 | ||||
| 	if (!dev->iommu_fwspec) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	group = iommu_group_get_for_dev(dev); | ||||
| 	if (IS_ERR(group)) | ||||
| 		return PTR_ERR(group); | ||||
| 
 | ||||
| @ -370,6 +279,17 @@ static void gart_iommu_remove_device(struct device *dev) | ||||
| 	iommu_device_unlink(&gart_handle->iommu, dev); | ||||
| } | ||||
| 
 | ||||
| static int gart_iommu_of_xlate(struct device *dev, | ||||
| 			       struct of_phandle_args *args) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void gart_iommu_sync(struct iommu_domain *domain) | ||||
| { | ||||
| 	FLUSH_GART_REGS(gart_handle); | ||||
| } | ||||
| 
 | ||||
| static const struct iommu_ops gart_iommu_ops = { | ||||
| 	.capable	= gart_iommu_capable, | ||||
| 	.domain_alloc	= gart_iommu_domain_alloc, | ||||
| @ -383,129 +303,96 @@ static const struct iommu_ops gart_iommu_ops = { | ||||
| 	.unmap		= gart_iommu_unmap, | ||||
| 	.iova_to_phys	= gart_iommu_iova_to_phys, | ||||
| 	.pgsize_bitmap	= GART_IOMMU_PGSIZES, | ||||
| 	.of_xlate	= gart_iommu_of_xlate, | ||||
| 	.iotlb_sync_map	= gart_iommu_sync, | ||||
| 	.iotlb_sync	= gart_iommu_sync, | ||||
| }; | ||||
| 
 | ||||
| static int tegra_gart_suspend(struct device *dev) | ||||
| int tegra_gart_suspend(struct gart_device *gart) | ||||
| { | ||||
| 	struct gart_device *gart = dev_get_drvdata(dev); | ||||
| 	unsigned long iova; | ||||
| 	u32 *data = gart->savedata; | ||||
| 	unsigned long flags; | ||||
| 	unsigned long iova; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * All GART users shall be suspended at this point. Disable | ||||
| 	 * address translation to trap all GART accesses as invalid | ||||
| 	 * memory accesses. | ||||
| 	 */ | ||||
| 	writel_relaxed(0, gart->regs + GART_CONFIG); | ||||
| 	FLUSH_GART_REGS(gart); | ||||
| 
 | ||||
| 	spin_lock_irqsave(&gart->pte_lock, flags); | ||||
| 	for_each_gart_pte(gart, iova) | ||||
| 		*(data++) = gart_read_pte(gart, iova); | ||||
| 	spin_unlock_irqrestore(&gart->pte_lock, flags); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int tegra_gart_resume(struct device *dev) | ||||
| int tegra_gart_resume(struct gart_device *gart) | ||||
| { | ||||
| 	struct gart_device *gart = dev_get_drvdata(dev); | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&gart->pte_lock, flags); | ||||
| 	do_gart_setup(gart, gart->savedata); | ||||
| 	spin_unlock_irqrestore(&gart->pte_lock, flags); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int tegra_gart_probe(struct platform_device *pdev) | ||||
| struct gart_device *tegra_gart_probe(struct device *dev, struct tegra_mc *mc) | ||||
| { | ||||
| 	struct gart_device *gart; | ||||
| 	struct resource *res, *res_remap; | ||||
| 	void __iomem *gart_regs; | ||||
| 	struct device *dev = &pdev->dev; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (gart_handle) | ||||
| 		return -EIO; | ||||
| 	struct resource *res; | ||||
| 	int err; | ||||
| 
 | ||||
| 	BUILD_BUG_ON(PAGE_SHIFT != GART_PAGE_SHIFT); | ||||
| 
 | ||||
| 	/* the GART memory aperture is required */ | ||||
| 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| 	res_remap = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||||
| 	if (!res || !res_remap) { | ||||
| 		dev_err(dev, "GART memory aperture expected\n"); | ||||
| 		return -ENXIO; | ||||
| 	res = platform_get_resource(to_platform_device(dev), IORESOURCE_MEM, 1); | ||||
| 	if (!res) { | ||||
| 		dev_err(dev, "Memory aperture resource unavailable\n"); | ||||
| 		return ERR_PTR(-ENXIO); | ||||
| 	} | ||||
| 
 | ||||
| 	gart = devm_kzalloc(dev, sizeof(*gart), GFP_KERNEL); | ||||
| 	if (!gart) { | ||||
| 		dev_err(dev, "failed to allocate gart_device\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	gart_regs = devm_ioremap(dev, res->start, resource_size(res)); | ||||
| 	if (!gart_regs) { | ||||
| 		dev_err(dev, "failed to remap GART registers\n"); | ||||
| 		return -ENXIO; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = iommu_device_sysfs_add(&gart->iommu, &pdev->dev, NULL, | ||||
| 				     dev_name(&pdev->dev)); | ||||
| 	if (ret) { | ||||
| 		dev_err(dev, "Failed to register IOMMU in sysfs\n"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	iommu_device_set_ops(&gart->iommu, &gart_iommu_ops); | ||||
| 
 | ||||
| 	ret = iommu_device_register(&gart->iommu); | ||||
| 	if (ret) { | ||||
| 		dev_err(dev, "Failed to register IOMMU\n"); | ||||
| 		iommu_device_sysfs_remove(&gart->iommu); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	gart->dev = &pdev->dev; | ||||
| 	spin_lock_init(&gart->pte_lock); | ||||
| 	spin_lock_init(&gart->client_lock); | ||||
| 	INIT_LIST_HEAD(&gart->client); | ||||
| 	gart->regs = gart_regs; | ||||
| 	gart->iovmm_base = (dma_addr_t)res_remap->start; | ||||
| 	gart->page_count = (resource_size(res_remap) >> GART_PAGE_SHIFT); | ||||
| 
 | ||||
| 	gart->savedata = vmalloc(array_size(sizeof(u32), gart->page_count)); | ||||
| 	if (!gart->savedata) { | ||||
| 		dev_err(dev, "failed to allocate context save area\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 
 | ||||
| 	platform_set_drvdata(pdev, gart); | ||||
| 	do_gart_setup(gart, NULL); | ||||
| 	gart = kzalloc(sizeof(*gart), GFP_KERNEL); | ||||
| 	if (!gart) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 
 | ||||
| 	gart_handle = gart; | ||||
| 
 | ||||
| 	return 0; | ||||
| 	gart->dev = dev; | ||||
| 	gart->regs = mc->regs + GART_REG_BASE; | ||||
| 	gart->iovmm_base = res->start; | ||||
| 	gart->iovmm_end = res->end + 1; | ||||
| 	spin_lock_init(&gart->pte_lock); | ||||
| 	spin_lock_init(&gart->dom_lock); | ||||
| 
 | ||||
| 	do_gart_setup(gart, NULL); | ||||
| 
 | ||||
| 	err = iommu_device_sysfs_add(&gart->iommu, dev, NULL, "gart"); | ||||
| 	if (err) | ||||
| 		goto free_gart; | ||||
| 
 | ||||
| 	iommu_device_set_ops(&gart->iommu, &gart_iommu_ops); | ||||
| 	iommu_device_set_fwnode(&gart->iommu, dev->fwnode); | ||||
| 
 | ||||
| 	err = iommu_device_register(&gart->iommu); | ||||
| 	if (err) | ||||
| 		goto remove_sysfs; | ||||
| 
 | ||||
| 	gart->savedata = vmalloc(resource_size(res) / GART_PAGE_SIZE * | ||||
| 				 sizeof(u32)); | ||||
| 	if (!gart->savedata) { | ||||
| 		err = -ENOMEM; | ||||
| 		goto unregister_iommu; | ||||
| 	} | ||||
| 
 | ||||
| 	return gart; | ||||
| 
 | ||||
| unregister_iommu: | ||||
| 	iommu_device_unregister(&gart->iommu); | ||||
| remove_sysfs: | ||||
| 	iommu_device_sysfs_remove(&gart->iommu); | ||||
| free_gart: | ||||
| 	kfree(gart); | ||||
| 
 | ||||
| 	return ERR_PTR(err); | ||||
| } | ||||
| 
 | ||||
| static const struct dev_pm_ops tegra_gart_pm_ops = { | ||||
| 	.suspend	= tegra_gart_suspend, | ||||
| 	.resume		= tegra_gart_resume, | ||||
| }; | ||||
| 
 | ||||
| static const struct of_device_id tegra_gart_of_match[] = { | ||||
| 	{ .compatible = "nvidia,tegra20-gart", }, | ||||
| 	{ }, | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver tegra_gart_driver = { | ||||
| 	.probe		= tegra_gart_probe, | ||||
| 	.driver = { | ||||
| 		.name	= "tegra-gart", | ||||
| 		.pm	= &tegra_gart_pm_ops, | ||||
| 		.of_match_table = tegra_gart_of_match, | ||||
| 		.suppress_bind_attrs = true, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static int __init tegra_gart_init(void) | ||||
| { | ||||
| 	return platform_driver_register(&tegra_gart_driver); | ||||
| } | ||||
| subsys_initcall(tegra_gart_init); | ||||
| 
 | ||||
| module_param(gart_debug, bool, 0644); | ||||
| MODULE_PARM_DESC(gart_debug, "Enable GART debugging"); | ||||
|  | ||||
| @ -982,10 +982,6 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev, | ||||
| 	u32 value; | ||||
| 	int err; | ||||
| 
 | ||||
| 	/* This can happen on Tegra20 which doesn't have an SMMU */ | ||||
| 	if (!soc) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); | ||||
| 	if (!smmu) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
|  | ||||
| @ -12,6 +12,7 @@ | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/of.h> | ||||
| #include <linux/of_device.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/sort.h> | ||||
| @ -38,6 +39,7 @@ | ||||
| 
 | ||||
| #define MC_ERR_ADR 0x0c | ||||
| 
 | ||||
| #define MC_GART_ERROR_REQ		0x30 | ||||
| #define MC_DECERR_EMEM_OTHERS_STATUS	0x58 | ||||
| #define MC_SECURITY_VIOLATION_STATUS	0x74 | ||||
| 
 | ||||
| @ -51,7 +53,7 @@ | ||||
| 
 | ||||
| static const struct of_device_id tegra_mc_of_match[] = { | ||||
| #ifdef CONFIG_ARCH_TEGRA_2x_SOC | ||||
| 	{ .compatible = "nvidia,tegra20-mc", .data = &tegra20_mc_soc }, | ||||
| 	{ .compatible = "nvidia,tegra20-mc-gart", .data = &tegra20_mc_soc }, | ||||
| #endif | ||||
| #ifdef CONFIG_ARCH_TEGRA_3x_SOC | ||||
| 	{ .compatible = "nvidia,tegra30-mc", .data = &tegra30_mc_soc }, | ||||
| @ -161,7 +163,7 @@ static int tegra_mc_hotreset_assert(struct reset_controller_dev *rcdev, | ||||
| 		/* block clients DMA requests */ | ||||
| 		err = rst_ops->block_dma(mc, rst); | ||||
| 		if (err) { | ||||
| 			dev_err(mc->dev, "Failed to block %s DMA: %d\n", | ||||
| 			dev_err(mc->dev, "failed to block %s DMA: %d\n", | ||||
| 				rst->name, err); | ||||
| 			return err; | ||||
| 		} | ||||
| @ -171,7 +173,7 @@ static int tegra_mc_hotreset_assert(struct reset_controller_dev *rcdev, | ||||
| 		/* wait for completion of the outstanding DMA requests */ | ||||
| 		while (!rst_ops->dma_idling(mc, rst)) { | ||||
| 			if (!retries--) { | ||||
| 				dev_err(mc->dev, "Failed to flush %s DMA\n", | ||||
| 				dev_err(mc->dev, "failed to flush %s DMA\n", | ||||
| 					rst->name); | ||||
| 				return -EBUSY; | ||||
| 			} | ||||
| @ -184,7 +186,7 @@ static int tegra_mc_hotreset_assert(struct reset_controller_dev *rcdev, | ||||
| 		/* clear clients DMA requests sitting before arbitration */ | ||||
| 		err = rst_ops->hotreset_assert(mc, rst); | ||||
| 		if (err) { | ||||
| 			dev_err(mc->dev, "Failed to hot reset %s: %d\n", | ||||
| 			dev_err(mc->dev, "failed to hot reset %s: %d\n", | ||||
| 				rst->name, err); | ||||
| 			return err; | ||||
| 		} | ||||
| @ -213,7 +215,7 @@ static int tegra_mc_hotreset_deassert(struct reset_controller_dev *rcdev, | ||||
| 		/* take out client from hot reset */ | ||||
| 		err = rst_ops->hotreset_deassert(mc, rst); | ||||
| 		if (err) { | ||||
| 			dev_err(mc->dev, "Failed to deassert hot reset %s: %d\n", | ||||
| 			dev_err(mc->dev, "failed to deassert hot reset %s: %d\n", | ||||
| 				rst->name, err); | ||||
| 			return err; | ||||
| 		} | ||||
| @ -223,7 +225,7 @@ static int tegra_mc_hotreset_deassert(struct reset_controller_dev *rcdev, | ||||
| 		/* allow new DMA requests to proceed to arbitration */ | ||||
| 		err = rst_ops->unblock_dma(mc, rst); | ||||
| 		if (err) { | ||||
| 			dev_err(mc->dev, "Failed to unblock %s DMA : %d\n", | ||||
| 			dev_err(mc->dev, "failed to unblock %s DMA : %d\n", | ||||
| 				rst->name, err); | ||||
| 			return err; | ||||
| 		} | ||||
| @ -575,8 +577,15 @@ static __maybe_unused irqreturn_t tegra20_mc_irq(int irq, void *data) | ||||
| 			break; | ||||
| 
 | ||||
| 		case MC_INT_INVALID_GART_PAGE: | ||||
| 			dev_err_ratelimited(mc->dev, "%s\n", error); | ||||
| 			continue; | ||||
| 			reg = MC_GART_ERROR_REQ; | ||||
| 			value = mc_readl(mc, reg); | ||||
| 
 | ||||
| 			id = (value >> 1) & mc->soc->client_id_mask; | ||||
| 			desc = error_names[2]; | ||||
| 
 | ||||
| 			if (value & BIT(0)) | ||||
| 				direction = "write"; | ||||
| 			break; | ||||
| 
 | ||||
| 		case MC_INT_SECURITY_VIOLATION: | ||||
| 			reg = MC_SECURITY_VIOLATION_STATUS; | ||||
| @ -611,23 +620,18 @@ static __maybe_unused irqreturn_t tegra20_mc_irq(int irq, void *data) | ||||
| 
 | ||||
| static int tegra_mc_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	const struct of_device_id *match; | ||||
| 	struct resource *res; | ||||
| 	struct tegra_mc *mc; | ||||
| 	void *isr; | ||||
| 	int err; | ||||
| 
 | ||||
| 	match = of_match_node(tegra_mc_of_match, pdev->dev.of_node); | ||||
| 	if (!match) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); | ||||
| 	if (!mc) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	platform_set_drvdata(pdev, mc); | ||||
| 	spin_lock_init(&mc->lock); | ||||
| 	mc->soc = match->data; | ||||
| 	mc->soc = of_device_get_match_data(&pdev->dev); | ||||
| 	mc->dev = &pdev->dev; | ||||
| 
 | ||||
| 	/* length of MC tick in nanoseconds */ | ||||
| @ -638,38 +642,35 @@ static int tegra_mc_probe(struct platform_device *pdev) | ||||
| 	if (IS_ERR(mc->regs)) | ||||
| 		return PTR_ERR(mc->regs); | ||||
| 
 | ||||
| 	mc->clk = devm_clk_get(&pdev->dev, "mc"); | ||||
| 	if (IS_ERR(mc->clk)) { | ||||
| 		dev_err(&pdev->dev, "failed to get MC clock: %ld\n", | ||||
| 			PTR_ERR(mc->clk)); | ||||
| 		return PTR_ERR(mc->clk); | ||||
| 	} | ||||
| 
 | ||||
| #ifdef CONFIG_ARCH_TEGRA_2x_SOC | ||||
| 	if (mc->soc == &tegra20_mc_soc) { | ||||
| 		res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||||
| 		mc->regs2 = devm_ioremap_resource(&pdev->dev, res); | ||||
| 		if (IS_ERR(mc->regs2)) | ||||
| 			return PTR_ERR(mc->regs2); | ||||
| 
 | ||||
| 		isr = tegra20_mc_irq; | ||||
| 	} else | ||||
| #endif | ||||
| 	{ | ||||
| 		mc->clk = devm_clk_get(&pdev->dev, "mc"); | ||||
| 		if (IS_ERR(mc->clk)) { | ||||
| 			dev_err(&pdev->dev, "failed to get MC clock: %ld\n", | ||||
| 				PTR_ERR(mc->clk)); | ||||
| 			return PTR_ERR(mc->clk); | ||||
| 		} | ||||
| 
 | ||||
| 		err = tegra_mc_setup_latency_allowance(mc); | ||||
| 		if (err < 0) { | ||||
| 			dev_err(&pdev->dev, "failed to setup latency allowance: %d\n", | ||||
| 			dev_err(&pdev->dev, | ||||
| 				"failed to setup latency allowance: %d\n", | ||||
| 				err); | ||||
| 			return err; | ||||
| 		} | ||||
| 
 | ||||
| 		isr = tegra_mc_irq; | ||||
| 	} | ||||
| 
 | ||||
| 	err = tegra_mc_setup_timings(mc); | ||||
| 	if (err < 0) { | ||||
| 		dev_err(&pdev->dev, "failed to setup timings: %d\n", err); | ||||
| 		return err; | ||||
| 		err = tegra_mc_setup_timings(mc); | ||||
| 		if (err < 0) { | ||||
| 			dev_err(&pdev->dev, "failed to setup timings: %d\n", | ||||
| 				err); | ||||
| 			return err; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	mc->irq = platform_get_irq(pdev, 0); | ||||
| @ -678,11 +679,11 @@ static int tegra_mc_probe(struct platform_device *pdev) | ||||
| 		return mc->irq; | ||||
| 	} | ||||
| 
 | ||||
| 	WARN(!mc->soc->client_id_mask, "Missing client ID mask for this SoC\n"); | ||||
| 	WARN(!mc->soc->client_id_mask, "missing client ID mask for this SoC\n"); | ||||
| 
 | ||||
| 	mc_writel(mc, mc->soc->intmask, MC_INTMASK); | ||||
| 
 | ||||
| 	err = devm_request_irq(&pdev->dev, mc->irq, isr, IRQF_SHARED, | ||||
| 	err = devm_request_irq(&pdev->dev, mc->irq, isr, 0, | ||||
| 			       dev_name(&pdev->dev), mc); | ||||
| 	if (err < 0) { | ||||
| 		dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", mc->irq, | ||||
| @ -695,20 +696,65 @@ static int tegra_mc_probe(struct platform_device *pdev) | ||||
| 		dev_err(&pdev->dev, "failed to register reset controller: %d\n", | ||||
| 			err); | ||||
| 
 | ||||
| 	if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU)) { | ||||
| 	if (IS_ENABLED(CONFIG_TEGRA_IOMMU_SMMU) && mc->soc->smmu) { | ||||
| 		mc->smmu = tegra_smmu_probe(&pdev->dev, mc->soc->smmu, mc); | ||||
| 		if (IS_ERR(mc->smmu)) | ||||
| 		if (IS_ERR(mc->smmu)) { | ||||
| 			dev_err(&pdev->dev, "failed to probe SMMU: %ld\n", | ||||
| 				PTR_ERR(mc->smmu)); | ||||
| 			mc->smmu = NULL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && !mc->soc->smmu) { | ||||
| 		mc->gart = tegra_gart_probe(&pdev->dev, mc); | ||||
| 		if (IS_ERR(mc->gart)) { | ||||
| 			dev_err(&pdev->dev, "failed to probe GART: %ld\n", | ||||
| 				PTR_ERR(mc->gart)); | ||||
| 			mc->gart = NULL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int tegra_mc_suspend(struct device *dev) | ||||
| { | ||||
| 	struct tegra_mc *mc = dev_get_drvdata(dev); | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && mc->gart) { | ||||
| 		err = tegra_gart_suspend(mc->gart); | ||||
| 		if (err) | ||||
| 			return err; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int tegra_mc_resume(struct device *dev) | ||||
| { | ||||
| 	struct tegra_mc *mc = dev_get_drvdata(dev); | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (IS_ENABLED(CONFIG_TEGRA_IOMMU_GART) && mc->gart) { | ||||
| 		err = tegra_gart_resume(mc->gart); | ||||
| 		if (err) | ||||
| 			return err; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct dev_pm_ops tegra_mc_pm_ops = { | ||||
| 	.suspend = tegra_mc_suspend, | ||||
| 	.resume = tegra_mc_resume, | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver tegra_mc_driver = { | ||||
| 	.driver = { | ||||
| 		.name = "tegra-mc", | ||||
| 		.of_match_table = tegra_mc_of_match, | ||||
| 		.pm = &tegra_mc_pm_ops, | ||||
| 		.suppress_bind_attrs = true, | ||||
| 	}, | ||||
| 	.prevent_deferred_probe = true, | ||||
|  | ||||
| @ -26,19 +26,13 @@ | ||||
| 
 | ||||
| static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset) | ||||
| { | ||||
| 	if (mc->regs2 && offset >= 0x24) | ||||
| 		return readl(mc->regs2 + offset - 0x3c); | ||||
| 
 | ||||
| 	return readl(mc->regs + offset); | ||||
| 	return readl_relaxed(mc->regs + offset); | ||||
| } | ||||
| 
 | ||||
| static inline void mc_writel(struct tegra_mc *mc, u32 value, | ||||
| 			     unsigned long offset) | ||||
| { | ||||
| 	if (mc->regs2 && offset >= 0x24) | ||||
| 		return writel(value, mc->regs2 + offset - 0x3c); | ||||
| 
 | ||||
| 	writel(value, mc->regs + offset); | ||||
| 	writel_relaxed(value, mc->regs + offset); | ||||
| } | ||||
| 
 | ||||
| extern const struct tegra_mc_reset_ops terga_mc_reset_ops_common; | ||||
|  | ||||
| @ -142,6 +142,33 @@ int pci_ats_queue_depth(struct pci_dev *dev) | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(pci_ats_queue_depth); | ||||
| 
 | ||||
| /**
 | ||||
|  * pci_ats_page_aligned - Return Page Aligned Request bit status. | ||||
|  * @pdev: the PCI device | ||||
|  * | ||||
|  * Returns 1, if the Untranslated Addresses generated by the device | ||||
|  * are always aligned or 0 otherwise. | ||||
|  * | ||||
|  * Per PCIe spec r4.0, sec 10.5.1.2, if the Page Aligned Request bit | ||||
|  * is set, it indicates the Untranslated Addresses generated by the | ||||
|  * device are always aligned to a 4096 byte boundary. | ||||
|  */ | ||||
| int pci_ats_page_aligned(struct pci_dev *pdev) | ||||
| { | ||||
| 	u16 cap; | ||||
| 
 | ||||
| 	if (!pdev->ats_cap) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	pci_read_config_word(pdev, pdev->ats_cap + PCI_ATS_CAP, &cap); | ||||
| 
 | ||||
| 	if (cap & PCI_ATS_CAP_PAGE_ALIGNED) | ||||
| 		return 1; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(pci_ats_page_aligned); | ||||
| 
 | ||||
| #ifdef CONFIG_PCI_PRI | ||||
| /**
 | ||||
|  * pci_enable_pri - Enable PRI capability | ||||
| @ -368,6 +395,36 @@ int pci_pasid_features(struct pci_dev *pdev) | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(pci_pasid_features); | ||||
| 
 | ||||
| /**
 | ||||
|  * pci_prg_resp_pasid_required - Return PRG Response PASID Required bit | ||||
|  *				 status. | ||||
|  * @pdev: PCI device structure | ||||
|  * | ||||
|  * Returns 1 if PASID is required in PRG Response Message, 0 otherwise. | ||||
|  * | ||||
|  * Even though the PRG response PASID status is read from PRI Status | ||||
|  * Register, since this API will mainly be used by PASID users, this | ||||
|  * function is defined within #ifdef CONFIG_PCI_PASID instead of | ||||
|  * CONFIG_PCI_PRI. | ||||
|  */ | ||||
| int pci_prg_resp_pasid_required(struct pci_dev *pdev) | ||||
| { | ||||
| 	u16 status; | ||||
| 	int pos; | ||||
| 
 | ||||
| 	pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); | ||||
| 	if (!pos) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status); | ||||
| 
 | ||||
| 	if (status & PCI_PRI_STATUS_PASID) | ||||
| 		return 1; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(pci_prg_resp_pasid_required); | ||||
| 
 | ||||
| #define PASID_NUMBER_SHIFT	8 | ||||
| #define PASID_NUMBER_MASK	(0x1f << PASID_NUMBER_SHIFT) | ||||
| /**
 | ||||
|  | ||||
| @ -374,20 +374,17 @@ enum { | ||||
| #define QI_DEV_EIOTLB_PFSID(pfsid) (((u64)(pfsid & 0xf) << 12) | ((u64)(pfsid & 0xfff) << 52)) | ||||
| #define QI_DEV_EIOTLB_MAX_INVS	32 | ||||
| 
 | ||||
| #define QI_PGRP_IDX(idx)	(((u64)(idx)) << 55) | ||||
| #define QI_PGRP_PRIV(priv)	(((u64)(priv)) << 32) | ||||
| #define QI_PGRP_RESP_CODE(res)	((u64)(res)) | ||||
| #define QI_PGRP_PASID(pasid)	(((u64)(pasid)) << 32) | ||||
| #define QI_PGRP_DID(did)	(((u64)(did)) << 16) | ||||
| /* Page group response descriptor QW0 */ | ||||
| #define QI_PGRP_PASID_P(p)	(((u64)(p)) << 4) | ||||
| #define QI_PGRP_PDP(p)		(((u64)(p)) << 5) | ||||
| #define QI_PGRP_RESP_CODE(res)	(((u64)(res)) << 12) | ||||
| #define QI_PGRP_DID(rid)	(((u64)(rid)) << 16) | ||||
| #define QI_PGRP_PASID(pasid)	(((u64)(pasid)) << 32) | ||||
| 
 | ||||
| /* Page group response descriptor QW1 */ | ||||
| #define QI_PGRP_LPIG(x)		(((u64)(x)) << 2) | ||||
| #define QI_PGRP_IDX(idx)	(((u64)(idx)) << 3) | ||||
| 
 | ||||
| #define QI_PSTRM_ADDR(addr)	(((u64)(addr)) & VTD_PAGE_MASK) | ||||
| #define QI_PSTRM_DEVFN(devfn)	(((u64)(devfn)) << 4) | ||||
| #define QI_PSTRM_RESP_CODE(res)	((u64)(res)) | ||||
| #define QI_PSTRM_IDX(idx)	(((u64)(idx)) << 55) | ||||
| #define QI_PSTRM_PRIV(priv)	(((u64)(priv)) << 32) | ||||
| #define QI_PSTRM_BUS(bus)	(((u64)(bus)) << 24) | ||||
| #define QI_PSTRM_PASID(pasid)	(((u64)(pasid)) << 4) | ||||
| 
 | ||||
| #define QI_RESP_SUCCESS		0x0 | ||||
| #define QI_RESP_INVALID		0x1 | ||||
|  | ||||
| @ -20,7 +20,7 @@ struct device; | ||||
| 
 | ||||
| struct svm_dev_ops { | ||||
| 	void (*fault_cb)(struct device *dev, int pasid, u64 address, | ||||
| 			 u32 private, int rwxp, int response); | ||||
| 			 void *private, int rwxp, int response); | ||||
| }; | ||||
| 
 | ||||
| /* Values for rxwp in fault_cb callback */ | ||||
|  | ||||
| @ -167,8 +167,9 @@ struct iommu_resv_region { | ||||
|  * @detach_dev: detach device from an iommu domain | ||||
|  * @map: map a physically contiguous memory region to an iommu domain | ||||
|  * @unmap: unmap a physically contiguous memory region from an iommu domain | ||||
|  * @flush_tlb_all: Synchronously flush all hardware TLBs for this domain | ||||
|  * @flush_iotlb_all: Synchronously flush all hardware TLBs for this domain | ||||
|  * @iotlb_range_add: Add a given iova range to the flush queue for this domain | ||||
|  * @iotlb_sync_map: Sync mappings created recently using @map to the hardware | ||||
|  * @iotlb_sync: Flush all queued ranges from the hardware TLBs and empty flush | ||||
|  *            queue | ||||
|  * @iova_to_phys: translate iova to physical address | ||||
| @ -183,6 +184,8 @@ struct iommu_resv_region { | ||||
|  * @domain_window_enable: Configure and enable a particular window for a domain | ||||
|  * @domain_window_disable: Disable a particular window for a domain | ||||
|  * @of_xlate: add OF master IDs to iommu grouping | ||||
|  * @is_attach_deferred: Check if domain attach should be deferred from iommu | ||||
|  *                      driver init to device driver init (default no) | ||||
|  * @pgsize_bitmap: bitmap of all possible supported page sizes | ||||
|  */ | ||||
| struct iommu_ops { | ||||
| @ -201,6 +204,7 @@ struct iommu_ops { | ||||
| 	void (*flush_iotlb_all)(struct iommu_domain *domain); | ||||
| 	void (*iotlb_range_add)(struct iommu_domain *domain, | ||||
| 				unsigned long iova, size_t size); | ||||
| 	void (*iotlb_sync_map)(struct iommu_domain *domain); | ||||
| 	void (*iotlb_sync)(struct iommu_domain *domain); | ||||
| 	phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t iova); | ||||
| 	int (*add_device)(struct device *dev); | ||||
|  | ||||
| @ -40,6 +40,7 @@ void pci_disable_pasid(struct pci_dev *pdev); | ||||
| void pci_restore_pasid_state(struct pci_dev *pdev); | ||||
| int pci_pasid_features(struct pci_dev *pdev); | ||||
| int pci_max_pasids(struct pci_dev *pdev); | ||||
| int pci_prg_resp_pasid_required(struct pci_dev *pdev); | ||||
| 
 | ||||
| #else  /* CONFIG_PCI_PASID */ | ||||
| 
 | ||||
| @ -66,6 +67,10 @@ static inline int pci_max_pasids(struct pci_dev *pdev) | ||||
| 	return -EINVAL; | ||||
| } | ||||
| 
 | ||||
| static inline int pci_prg_resp_pasid_required(struct pci_dev *pdev) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| #endif /* CONFIG_PCI_PASID */ | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -1527,11 +1527,13 @@ void pci_ats_init(struct pci_dev *dev); | ||||
| int pci_enable_ats(struct pci_dev *dev, int ps); | ||||
| void pci_disable_ats(struct pci_dev *dev); | ||||
| int pci_ats_queue_depth(struct pci_dev *dev); | ||||
| int pci_ats_page_aligned(struct pci_dev *dev); | ||||
| #else | ||||
| static inline void pci_ats_init(struct pci_dev *d) { } | ||||
| static inline int pci_enable_ats(struct pci_dev *d, int ps) { return -ENODEV; } | ||||
| static inline void pci_disable_ats(struct pci_dev *d) { } | ||||
| static inline int pci_ats_queue_depth(struct pci_dev *d) { return -ENODEV; } | ||||
| static inline int pci_ats_page_aligned(struct pci_dev *dev) { return 0; } | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_PCIE_PTM | ||||
|  | ||||
| @ -9,6 +9,7 @@ | ||||
| #ifndef __SOC_TEGRA_MC_H__ | ||||
| #define __SOC_TEGRA_MC_H__ | ||||
| 
 | ||||
| #include <linux/err.h> | ||||
| #include <linux/reset-controller.h> | ||||
| #include <linux/types.h> | ||||
| 
 | ||||
| @ -77,6 +78,7 @@ struct tegra_smmu_soc { | ||||
| 
 | ||||
| struct tegra_mc; | ||||
| struct tegra_smmu; | ||||
| struct gart_device; | ||||
| 
 | ||||
| #ifdef CONFIG_TEGRA_IOMMU_SMMU | ||||
| struct tegra_smmu *tegra_smmu_probe(struct device *dev, | ||||
| @ -96,6 +98,28 @@ static inline void tegra_smmu_remove(struct tegra_smmu *smmu) | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_TEGRA_IOMMU_GART | ||||
| struct gart_device *tegra_gart_probe(struct device *dev, struct tegra_mc *mc); | ||||
| int tegra_gart_suspend(struct gart_device *gart); | ||||
| int tegra_gart_resume(struct gart_device *gart); | ||||
| #else | ||||
| static inline struct gart_device * | ||||
| tegra_gart_probe(struct device *dev, struct tegra_mc *mc) | ||||
| { | ||||
| 	return ERR_PTR(-ENODEV); | ||||
| } | ||||
| 
 | ||||
| static inline int tegra_gart_suspend(struct gart_device *gart) | ||||
| { | ||||
| 	return -ENODEV; | ||||
| } | ||||
| 
 | ||||
| static inline int tegra_gart_resume(struct gart_device *gart) | ||||
| { | ||||
| 	return -ENODEV; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| struct tegra_mc_reset { | ||||
| 	const char *name; | ||||
| 	unsigned long id; | ||||
| @ -144,7 +168,8 @@ struct tegra_mc_soc { | ||||
| struct tegra_mc { | ||||
| 	struct device *dev; | ||||
| 	struct tegra_smmu *smmu; | ||||
| 	void __iomem *regs, *regs2; | ||||
| 	struct gart_device *gart; | ||||
| 	void __iomem *regs; | ||||
| 	struct clk *clk; | ||||
| 	int irq; | ||||
| 
 | ||||
|  | ||||
| @ -866,6 +866,7 @@ | ||||
| #define PCI_ATS_CAP		0x04	/* ATS Capability Register */ | ||||
| #define  PCI_ATS_CAP_QDEP(x)	((x) & 0x1f)	/* Invalidate Queue Depth */ | ||||
| #define  PCI_ATS_MAX_QDEP	32	/* Max Invalidate Queue Depth */ | ||||
| #define  PCI_ATS_CAP_PAGE_ALIGNED	0x0020 /* Page Aligned Request */ | ||||
| #define PCI_ATS_CTRL		0x06	/* ATS Control Register */ | ||||
| #define  PCI_ATS_CTRL_ENABLE	0x8000	/* ATS Enable */ | ||||
| #define  PCI_ATS_CTRL_STU(x)	((x) & 0x1f)	/* Smallest Translation Unit */ | ||||
| @ -880,6 +881,7 @@ | ||||
| #define  PCI_PRI_STATUS_RF	0x001	/* Response Failure */ | ||||
| #define  PCI_PRI_STATUS_UPRGI	0x002	/* Unexpected PRG index */ | ||||
| #define  PCI_PRI_STATUS_STOPPED	0x100	/* PRI Stopped */ | ||||
| #define  PCI_PRI_STATUS_PASID	0x8000	/* PRG Response PASID Required */ | ||||
| #define PCI_PRI_MAX_REQ		0x08	/* PRI max reqs supported */ | ||||
| #define PCI_PRI_ALLOC_REQ	0x0c	/* PRI max reqs allowed */ | ||||
| #define PCI_EXT_CAP_PRI_SIZEOF	16 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user