summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Robinson <pbrobinson@gmail.com>2017-07-18 19:37:53 +0100
committerPeter Robinson <pbrobinson@gmail.com>2017-07-18 19:37:53 +0100
commitb7ab4072a1d4c50d80056f552a62121499414680 (patch)
treebdef2bd2e306c7dac7434a25dfa595b0222ff9cf
parent3ca7c65713740d6fbbe71af6d35eb7df30356f2c (diff)
downloadkernel-b7ab4072a1d4c50d80056f552a62121499414680.tar.gz
kernel-b7ab4072a1d4c50d80056f552a62121499414680.tar.xz
kernel-b7ab4072a1d4c50d80056f552a62121499414680.zip
Add fix for Tegra GPU display with IOMMU, Add QCom IOMMU for Dragonboard display
-rw-r--r--arm-tegra-fix-gpu-iommu.patch39
-rw-r--r--baseconfig/arm/arm64/CONFIG_QCOM_IOMMU1
-rw-r--r--baseconfig/arm/armv7/armv7/CONFIG_QCOM_IOMMU1
-rw-r--r--kernel-aarch64-debug.config1
-rw-r--r--kernel-aarch64.config1
-rw-r--r--kernel-armv7hl-debug.config1
-rw-r--r--kernel-armv7hl.config1
-rw-r--r--kernel.spec25
-rw-r--r--qcom-display-iommu.patch1960
9 files changed, 2023 insertions, 7 deletions
diff --git a/arm-tegra-fix-gpu-iommu.patch b/arm-tegra-fix-gpu-iommu.patch
new file mode 100644
index 000000000..c775c88b4
--- /dev/null
+++ b/arm-tegra-fix-gpu-iommu.patch
@@ -0,0 +1,39 @@
+From patchwork Sun Jul 9 16:36:14 2017
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: ARM: tegra: Register host1x node with iommu binding on tegra124
+From: Paul Kocialkowski <contact@paulk.fr>
+X-Patchwork-Id: 9831825
+Message-Id: <20170709163614.6746-1-contact@paulk.fr>
+To: linux-arm-kernel@lists.infradead.org, linux-tegra@vger.kernel.org,
+ linux-kernel@vger.kernel.org
+Cc: Thierry Reding <thierry.reding@gmail.com>,
+ Stephen Warren <swarren@wwwdotorg.org>,
+ Mikko Perttunen <mperttunen@nvidia.com>,
+ Paul Kocialkowski <contact@paulk.fr>,
+ Jonathan Hunter <jonathanh@nvidia.com>
+Date: Sun, 9 Jul 2017 19:36:14 +0300
+
+This registers the host1x node with the SMMU (as HC swgroup) to allow
+the host1x code to attach to it. It avoid failing the probe sequence,
+which resulted in the tegra drm driver not probing and thus nothing
+being displayed on-screen.
+
+Signed-off-by: Paul Kocialkowski <contact@paulk.fr>
+---
+ arch/arm/boot/dts/tegra124.dtsi | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/arch/arm/boot/dts/tegra124.dtsi b/arch/arm/boot/dts/tegra124.dtsi
+index 187a36c6d0fc..b3b89befffeb 100644
+--- a/arch/arm/boot/dts/tegra124.dtsi
++++ b/arch/arm/boot/dts/tegra124.dtsi
+@@ -85,6 +85,7 @@
+ clocks = <&tegra_car TEGRA124_CLK_HOST1X>;
+ resets = <&tegra_car 28>;
+ reset-names = "host1x";
++ iommus = <&mc TEGRA_SWGROUP_HC>;
+
+ #address-cells = <2>;
+ #size-cells = <2>;
diff --git a/baseconfig/arm/arm64/CONFIG_QCOM_IOMMU b/baseconfig/arm/arm64/CONFIG_QCOM_IOMMU
new file mode 100644
index 000000000..b7e99b882
--- /dev/null
+++ b/baseconfig/arm/arm64/CONFIG_QCOM_IOMMU
@@ -0,0 +1 @@
+CONFIG_QCOM_IOMMU=y
diff --git a/baseconfig/arm/armv7/armv7/CONFIG_QCOM_IOMMU b/baseconfig/arm/armv7/armv7/CONFIG_QCOM_IOMMU
new file mode 100644
index 000000000..b7e99b882
--- /dev/null
+++ b/baseconfig/arm/armv7/armv7/CONFIG_QCOM_IOMMU
@@ -0,0 +1 @@
+CONFIG_QCOM_IOMMU=y
diff --git a/kernel-aarch64-debug.config b/kernel-aarch64-debug.config
index 57313bb64..b0c0ae65e 100644
--- a/kernel-aarch64-debug.config
+++ b/kernel-aarch64-debug.config
@@ -4200,6 +4200,7 @@ CONFIG_QCOM_FALKOR_ERRATUM_1009=y
CONFIG_QCOM_GSBI=y
CONFIG_QCOM_HIDMA=m
CONFIG_QCOM_HIDMA_MGMT=m
+CONFIG_QCOM_IOMMU=y
CONFIG_QCOM_IRQ_COMBINER=y
CONFIG_QCOM_L2_PMU=y
CONFIG_QCOM_L3_PMU=y
diff --git a/kernel-aarch64.config b/kernel-aarch64.config
index 719afb8a2..907202d06 100644
--- a/kernel-aarch64.config
+++ b/kernel-aarch64.config
@@ -4178,6 +4178,7 @@ CONFIG_QCOM_FALKOR_ERRATUM_1009=y
CONFIG_QCOM_GSBI=y
CONFIG_QCOM_HIDMA=m
CONFIG_QCOM_HIDMA_MGMT=m
+CONFIG_QCOM_IOMMU=y
CONFIG_QCOM_IRQ_COMBINER=y
CONFIG_QCOM_L2_PMU=y
CONFIG_QCOM_L3_PMU=y
diff --git a/kernel-armv7hl-debug.config b/kernel-armv7hl-debug.config
index 9defeffb3..310ec6e6f 100644
--- a/kernel-armv7hl-debug.config
+++ b/kernel-armv7hl-debug.config
@@ -4520,6 +4520,7 @@ CONFIG_QCOM_COINCELL=m
CONFIG_QCOM_GSBI=m
# CONFIG_QCOM_HIDMA is not set
# CONFIG_QCOM_HIDMA_MGMT is not set
+CONFIG_QCOM_IOMMU=y
CONFIG_QCOM_PM8XXX_XOADC=m
CONFIG_QCOM_PM=y
CONFIG_QCOM_Q6V5_PIL=m
diff --git a/kernel-armv7hl.config b/kernel-armv7hl.config
index da264bb89..9c021ec15 100644
--- a/kernel-armv7hl.config
+++ b/kernel-armv7hl.config
@@ -4498,6 +4498,7 @@ CONFIG_QCOM_COINCELL=m
CONFIG_QCOM_GSBI=m
# CONFIG_QCOM_HIDMA is not set
# CONFIG_QCOM_HIDMA_MGMT is not set
+CONFIG_QCOM_IOMMU=y
CONFIG_QCOM_PM8XXX_XOADC=m
CONFIG_QCOM_PM=y
CONFIG_QCOM_Q6V5_PIL=m
diff --git a/kernel.spec b/kernel.spec
index 3f7ff3756..d8c0d8a45 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -585,21 +585,21 @@ Patch302: usb-phy-tegra-Add-38.4MHz-clock-table-entry.patch
Patch303: arm-revert-mmc-omap_hsmmc-Use-dma_request_chan-for-reque.patch
# http://patchwork.ozlabs.org/patch/587554/
-Patch305: ARM-tegra-usb-no-reset.patch
+Patch304: ARM-tegra-usb-no-reset.patch
-Patch306: AllWinner-net-emac.patch
+Patch305: AllWinner-net-emac.patch
# http://www.spinics.net/lists/dri-devel/msg132235.html
-Patch309: drm-vc4-Fix-OOPSes-from-trying-to-cache-a-partially-constructed-BO..patch
+Patch306: drm-vc4-Fix-OOPSes-from-trying-to-cache-a-partially-constructed-BO..patch
# https://www.spinics.net/lists/arm-kernel/msg554183.html
-Patch311: arm-imx6-hummingboard2.patch
+Patch307: arm-imx6-hummingboard2.patch
-Patch312: arm64-Add-option-of-13-for-FORCE_MAX_ZONEORDER.patch
+Patch308: arm64-Add-option-of-13-for-FORCE_MAX_ZONEORDER.patch
# https://patchwork.freedesktop.org/patch/163300/
# https://patchwork.freedesktop.org/patch/161978/
-Patch315: bcm283x-vc4-fix-vblank.patch
+Patch309: bcm283x-vc4-fix-vblank.patch
# https://patchwork.kernel.org/patch/9815555/
# https://patchwork.kernel.org/patch/9815651/
@@ -607,7 +607,14 @@ Patch315: bcm283x-vc4-fix-vblank.patch
# https://patchwork.kernel.org/patch/9820417/
# https://patchwork.kernel.org/patch/9821151/
# https://patchwork.kernel.org/patch/9821157/
-Patch316: qcom-msm89xx-fixes.patch
+Patch310: qcom-msm89xx-fixes.patch
+
+# https://patchwork.kernel.org/patch/9831825/
+# https://patchwork.kernel.org/patch/9833721/
+Patch311: arm-tegra-fix-gpu-iommu.patch
+
+# https://www.spinics.net/lists/linux-arm-msm/msg28203.html
+Patch312: qcom-display-iommu.patch
# 400 - IBM (ppc/s390x) patches
@@ -2180,6 +2187,10 @@ fi
#
#
%changelog
+* Tue Jul 18 2017 Peter Robinson <pbrobinson@fedoraproject.org>
+- Add fix for Tegra GPU display with IOMMU
+- Add QCom IOMMU for Dragonboard display
+
* Tue Jul 18 2017 Laura Abbott <labbott@fedoraproject.org> - 4.13.0-0.rc1.git1.1
- Linux v4.13-rc1-24-gcb8c65ccff7f
diff --git a/qcom-display-iommu.patch b/qcom-display-iommu.patch
new file mode 100644
index 000000000..87d4473c8
--- /dev/null
+++ b/qcom-display-iommu.patch
@@ -0,0 +1,1960 @@
+From patchwork Thu Jul 13 12:07:44 2017
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [RESEND,1/4] Docs: dt: document qcom iommu bindings
+From: Rob Clark <robdclark@gmail.com>
+X-Patchwork-Id: 9838369
+Message-Id: <20170713120747.20490-2-robdclark@gmail.com>
+To: iommu@lists.linux-foundation.org
+Cc: linux-arm-msm@vger.kernel.org, Archit Taneja <architt@codeaurora.org>,
+ Rob Herring <robh@kernel.org>, Will Deacon <will.deacon@arm.com>,
+ Sricharan <sricharan@codeaurora.org>,
+ Mark Rutland <mark.rutland@arm.com>, Robin Murphy <robin.murphy@arm.com>,
+ Rob Clark <robdclark@gmail.com>, devicetree@vger.kernel.org
+Date: Thu, 13 Jul 2017 08:07:44 -0400
+
+Cc: devicetree@vger.kernel.org
+Signed-off-by: Rob Clark <robdclark@gmail.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+---
+ .../devicetree/bindings/iommu/qcom,iommu.txt | 121 +++++++++++++++++++++
+ 1 file changed, 121 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/iommu/qcom,iommu.txt
+
+diff --git a/Documentation/devicetree/bindings/iommu/qcom,iommu.txt b/Documentation/devicetree/bindings/iommu/qcom,iommu.txt
+new file mode 100644
+index 000000000000..b2641ceb2b40
+--- /dev/null
++++ b/Documentation/devicetree/bindings/iommu/qcom,iommu.txt
+@@ -0,0 +1,121 @@
++* QCOM IOMMU v1 Implementation
++
++Qualcomm "B" family devices which are not compatible with arm-smmu have
++a similar looking IOMMU but without access to the global register space,
++and optionally requiring additional configuration to route context irqs
++to non-secure vs secure interrupt line.
++
++** Required properties:
++
++- compatible : Should be one of:
++
++ "qcom,msm8916-iommu"
++
++ Followed by "qcom,msm-iommu-v1".
++
++- clock-names : Should be a pair of "iface" (required for IOMMUs
++ register group access) and "bus" (required for
++ the IOMMUs underlying bus access).
++
++- clocks : Phandles for respective clocks described by
++ clock-names.
++
++- #address-cells : must be 1.
++
++- #size-cells : must be 1.
++
++- #iommu-cells : Must be 1. Index identifies the context-bank #.
++
++- ranges : Base address and size of the iommu context banks.
++
++- qcom,iommu-secure-id : secure-id.
++
++- List of sub-nodes, one per translation context bank. Each sub-node
++ has the following required properties:
++
++ - compatible : Should be one of:
++ - "qcom,msm-iommu-v1-ns" : non-secure context bank
++ - "qcom,msm-iommu-v1-sec" : secure context bank
++ - reg : Base address and size of context bank within the iommu
++ - interrupts : The context fault irq.
++
++** Optional properties:
++
++- reg : Base address and size of the SMMU local base, should
++ be only specified if the iommu requires configuration
++ for routing of context bank irq's to secure vs non-
++ secure lines. (Ie. if the iommu contains secure
++ context banks)
++
++
++** Examples:
++
++ apps_iommu: iommu@1e20000 {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ #iommu-cells = <1>;
++ compatible = "qcom,msm8916-iommu", "qcom,msm-iommu-v1";
++ ranges = <0 0x1e20000 0x40000>;
++ reg = <0x1ef0000 0x3000>;
++ clocks = <&gcc GCC_SMMU_CFG_CLK>,
++ <&gcc GCC_APSS_TCU_CLK>;
++ clock-names = "iface", "bus";
++ qcom,iommu-secure-id = <17>;
++
++ // mdp_0:
++ iommu-ctx@4000 {
++ compatible = "qcom,msm-iommu-v1-ns";
++ reg = <0x4000 0x1000>;
++ interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ // venus_ns:
++ iommu-ctx@5000 {
++ compatible = "qcom,msm-iommu-v1-sec";
++ reg = <0x5000 0x1000>;
++ interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
++ };
++ };
++
++ gpu_iommu: iommu@1f08000 {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ #iommu-cells = <1>;
++ compatible = "qcom,msm8916-iommu", "qcom,msm-iommu-v1";
++ ranges = <0 0x1f08000 0x10000>;
++ clocks = <&gcc GCC_SMMU_CFG_CLK>,
++ <&gcc GCC_GFX_TCU_CLK>;
++ clock-names = "iface", "bus";
++ qcom,iommu-secure-id = <18>;
++
++ // gfx3d_user:
++ iommu-ctx@1000 {
++ compatible = "qcom,msm-iommu-v1-ns";
++ reg = <0x1000 0x1000>;
++ interrupts = <GIC_SPI 241 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ // gfx3d_priv:
++ iommu-ctx@2000 {
++ compatible = "qcom,msm-iommu-v1-ns";
++ reg = <0x2000 0x1000>;
++ interrupts = <GIC_SPI 242 IRQ_TYPE_LEVEL_HIGH>;
++ };
++ };
++
++ ...
++
++ venus: video-codec@1d00000 {
++ ...
++ iommus = <&apps_iommu 5>;
++ };
++
++ mdp: mdp@1a01000 {
++ ...
++ iommus = <&apps_iommu 4>;
++ };
++
++ gpu@01c00000 {
++ ...
++ iommus = <&gpu_iommu 1>, <&gpu_iommu 2>;
++ };
+From patchwork Thu Jul 13 12:07:45 2017
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [RESEND,2/4] iommu: arm-smmu: split out register defines
+From: Rob Clark <robdclark@gmail.com>
+X-Patchwork-Id: 9838371
+Message-Id: <20170713120747.20490-3-robdclark@gmail.com>
+To: iommu@lists.linux-foundation.org
+Cc: linux-arm-msm@vger.kernel.org, Archit Taneja <architt@codeaurora.org>,
+ Rob Herring <robh@kernel.org>, Will Deacon <will.deacon@arm.com>,
+ Sricharan <sricharan@codeaurora.org>,
+ Mark Rutland <mark.rutland@arm.com>, Robin Murphy <robin.murphy@arm.com>,
+ Rob Clark <robdclark@gmail.com>
+Date: Thu, 13 Jul 2017 08:07:45 -0400
+
+I want to re-use some of these for qcom_iommu, which has (roughly) the
+same context-bank registers.
+
+Signed-off-by: Rob Clark <robdclark@gmail.com>
+---
+ drivers/iommu/arm-smmu-regs.h | 227 ++++++++++++++++++++++++++++++++++++++++++
+ drivers/iommu/arm-smmu.c | 203 +------------------------------------
+ 2 files changed, 228 insertions(+), 202 deletions(-)
+ create mode 100644 drivers/iommu/arm-smmu-regs.h
+
+diff --git a/drivers/iommu/arm-smmu-regs.h b/drivers/iommu/arm-smmu-regs.h
+new file mode 100644
+index 000000000000..87589c863068
+--- /dev/null
++++ b/drivers/iommu/arm-smmu-regs.h
+@@ -0,0 +1,227 @@
++/*
++ * IOMMU API for ARM architected SMMU implementations.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * Copyright (C) 2013 ARM Limited
++ *
++ * Author: Will Deacon <will.deacon@arm.com>
++ */
++
++#ifndef _ARM_SMMU_REGS_H
++#define _ARM_SMMU_REGS_H
++
++/* Configuration registers */
++#define ARM_SMMU_GR0_sCR0 0x0
++#define sCR0_CLIENTPD (1 << 0)
++#define sCR0_GFRE (1 << 1)
++#define sCR0_GFIE (1 << 2)
++#define sCR0_EXIDENABLE (1 << 3)
++#define sCR0_GCFGFRE (1 << 4)
++#define sCR0_GCFGFIE (1 << 5)
++#define sCR0_USFCFG (1 << 10)
++#define sCR0_VMIDPNE (1 << 11)
++#define sCR0_PTM (1 << 12)
++#define sCR0_FB (1 << 13)
++#define sCR0_VMID16EN (1 << 31)
++#define sCR0_BSU_SHIFT 14
++#define sCR0_BSU_MASK 0x3
++
++/* Auxiliary Configuration register */
++#define ARM_SMMU_GR0_sACR 0x10
++
++/* Identification registers */
++#define ARM_SMMU_GR0_ID0 0x20
++#define ARM_SMMU_GR0_ID1 0x24
++#define ARM_SMMU_GR0_ID2 0x28
++#define ARM_SMMU_GR0_ID3 0x2c
++#define ARM_SMMU_GR0_ID4 0x30
++#define ARM_SMMU_GR0_ID5 0x34
++#define ARM_SMMU_GR0_ID6 0x38
++#define ARM_SMMU_GR0_ID7 0x3c
++#define ARM_SMMU_GR0_sGFSR 0x48
++#define ARM_SMMU_GR0_sGFSYNR0 0x50
++#define ARM_SMMU_GR0_sGFSYNR1 0x54
++#define ARM_SMMU_GR0_sGFSYNR2 0x58
++
++#define ID0_S1TS (1 << 30)
++#define ID0_S2TS (1 << 29)
++#define ID0_NTS (1 << 28)
++#define ID0_SMS (1 << 27)
++#define ID0_ATOSNS (1 << 26)
++#define ID0_PTFS_NO_AARCH32 (1 << 25)
++#define ID0_PTFS_NO_AARCH32S (1 << 24)
++#define ID0_CTTW (1 << 14)
++#define ID0_NUMIRPT_SHIFT 16
++#define ID0_NUMIRPT_MASK 0xff
++#define ID0_NUMSIDB_SHIFT 9
++#define ID0_NUMSIDB_MASK 0xf
++#define ID0_EXIDS (1 << 8)
++#define ID0_NUMSMRG_SHIFT 0
++#define ID0_NUMSMRG_MASK 0xff
++
++#define ID1_PAGESIZE (1 << 31)
++#define ID1_NUMPAGENDXB_SHIFT 28
++#define ID1_NUMPAGENDXB_MASK 7
++#define ID1_NUMS2CB_SHIFT 16
++#define ID1_NUMS2CB_MASK 0xff
++#define ID1_NUMCB_SHIFT 0
++#define ID1_NUMCB_MASK 0xff
++
++#define ID2_OAS_SHIFT 4
++#define ID2_OAS_MASK 0xf
++#define ID2_IAS_SHIFT 0
++#define ID2_IAS_MASK 0xf
++#define ID2_UBS_SHIFT 8
++#define ID2_UBS_MASK 0xf
++#define ID2_PTFS_4K (1 << 12)
++#define ID2_PTFS_16K (1 << 13)
++#define ID2_PTFS_64K (1 << 14)
++#define ID2_VMID16 (1 << 15)
++
++#define ID7_MAJOR_SHIFT 4
++#define ID7_MAJOR_MASK 0xf
++
++/* Global TLB invalidation */
++#define ARM_SMMU_GR0_TLBIVMID 0x64
++#define ARM_SMMU_GR0_TLBIALLNSNH 0x68
++#define ARM_SMMU_GR0_TLBIALLH 0x6c
++#define ARM_SMMU_GR0_sTLBGSYNC 0x70
++#define ARM_SMMU_GR0_sTLBGSTATUS 0x74
++#define sTLBGSTATUS_GSACTIVE (1 << 0)
++#define TLB_LOOP_TIMEOUT 1000000 /* 1s! */
++#define TLB_SPIN_COUNT 10
++
++/* Stream mapping registers */
++#define ARM_SMMU_GR0_SMR(n) (0x800 + ((n) << 2))
++#define SMR_VALID (1 << 31)
++#define SMR_MASK_SHIFT 16
++#define SMR_ID_SHIFT 0
++
++#define ARM_SMMU_GR0_S2CR(n) (0xc00 + ((n) << 2))
++#define S2CR_CBNDX_SHIFT 0
++#define S2CR_CBNDX_MASK 0xff
++#define S2CR_EXIDVALID (1 << 10)
++#define S2CR_TYPE_SHIFT 16
++#define S2CR_TYPE_MASK 0x3
++enum arm_smmu_s2cr_type {
++ S2CR_TYPE_TRANS,
++ S2CR_TYPE_BYPASS,
++ S2CR_TYPE_FAULT,
++};
++
++#define S2CR_PRIVCFG_SHIFT 24
++#define S2CR_PRIVCFG_MASK 0x3
++enum arm_smmu_s2cr_privcfg {
++ S2CR_PRIVCFG_DEFAULT,
++ S2CR_PRIVCFG_DIPAN,
++ S2CR_PRIVCFG_UNPRIV,
++ S2CR_PRIVCFG_PRIV,
++};
++
++/* Context bank attribute registers */
++#define ARM_SMMU_GR1_CBAR(n) (0x0 + ((n) << 2))
++#define CBAR_VMID_SHIFT 0
++#define CBAR_VMID_MASK 0xff
++#define CBAR_S1_BPSHCFG_SHIFT 8
++#define CBAR_S1_BPSHCFG_MASK 3
++#define CBAR_S1_BPSHCFG_NSH 3
++#define CBAR_S1_MEMATTR_SHIFT 12
++#define CBAR_S1_MEMATTR_MASK 0xf
++#define CBAR_S1_MEMATTR_WB 0xf
++#define CBAR_TYPE_SHIFT 16
++#define CBAR_TYPE_MASK 0x3
++#define CBAR_TYPE_S2_TRANS (0 << CBAR_TYPE_SHIFT)
++#define CBAR_TYPE_S1_TRANS_S2_BYPASS (1 << CBAR_TYPE_SHIFT)
++#define CBAR_TYPE_S1_TRANS_S2_FAULT (2 << CBAR_TYPE_SHIFT)
++#define CBAR_TYPE_S1_TRANS_S2_TRANS (3 << CBAR_TYPE_SHIFT)
++#define CBAR_IRPTNDX_SHIFT 24
++#define CBAR_IRPTNDX_MASK 0xff
++
++#define ARM_SMMU_GR1_CBA2R(n) (0x800 + ((n) << 2))
++#define CBA2R_RW64_32BIT (0 << 0)
++#define CBA2R_RW64_64BIT (1 << 0)
++#define CBA2R_VMID_SHIFT 16
++#define CBA2R_VMID_MASK 0xffff
++
++#define ARM_SMMU_CB_SCTLR 0x0
++#define ARM_SMMU_CB_ACTLR 0x4
++#define ARM_SMMU_CB_RESUME 0x8
++#define ARM_SMMU_CB_TTBCR2 0x10
++#define ARM_SMMU_CB_TTBR0 0x20
++#define ARM_SMMU_CB_TTBR1 0x28
++#define ARM_SMMU_CB_TTBCR 0x30
++#define ARM_SMMU_CB_CONTEXTIDR 0x34
++#define ARM_SMMU_CB_S1_MAIR0 0x38
++#define ARM_SMMU_CB_S1_MAIR1 0x3c
++#define ARM_SMMU_CB_PAR 0x50
++#define ARM_SMMU_CB_FSR 0x58
++#define ARM_SMMU_CB_FAR 0x60
++#define ARM_SMMU_CB_FSYNR0 0x68
++#define ARM_SMMU_CB_S1_TLBIVA 0x600
++#define ARM_SMMU_CB_S1_TLBIASID 0x610
++#define ARM_SMMU_CB_S1_TLBIVAL 0x620
++#define ARM_SMMU_CB_S2_TLBIIPAS2 0x630
++#define ARM_SMMU_CB_S2_TLBIIPAS2L 0x638
++#define ARM_SMMU_CB_TLBSYNC 0x7f0
++#define ARM_SMMU_CB_TLBSTATUS 0x7f4
++#define ARM_SMMU_CB_ATS1PR 0x800
++#define ARM_SMMU_CB_ATSR 0x8f0
++
++#define SCTLR_S1_ASIDPNE (1 << 12)
++#define SCTLR_CFCFG (1 << 7)
++#define SCTLR_CFIE (1 << 6)
++#define SCTLR_CFRE (1 << 5)
++#define SCTLR_E (1 << 4)
++#define SCTLR_AFE (1 << 2)
++#define SCTLR_TRE (1 << 1)
++#define SCTLR_M (1 << 0)
++
++#define ARM_MMU500_ACTLR_CPRE (1 << 1)
++
++#define ARM_MMU500_ACR_CACHE_LOCK (1 << 26)
++#define ARM_MMU500_ACR_SMTNMB_TLBEN (1 << 8)
++
++#define CB_PAR_F (1 << 0)
++
++#define ATSR_ACTIVE (1 << 0)
++
++#define RESUME_RETRY (0 << 0)
++#define RESUME_TERMINATE (1 << 0)
++
++#define TTBCR2_SEP_SHIFT 15
++#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT)
++#define TTBCR2_AS (1 << 4)
++
++#define TTBRn_ASID_SHIFT 48
++
++#define FSR_MULTI (1 << 31)
++#define FSR_SS (1 << 30)
++#define FSR_UUT (1 << 8)
++#define FSR_ASF (1 << 7)
++#define FSR_TLBLKF (1 << 6)
++#define FSR_TLBMCF (1 << 5)
++#define FSR_EF (1 << 4)
++#define FSR_PF (1 << 3)
++#define FSR_AFF (1 << 2)
++#define FSR_TF (1 << 1)
++
++#define FSR_IGN (FSR_AFF | FSR_ASF | \
++ FSR_TLBMCF | FSR_TLBLKF)
++#define FSR_FAULT (FSR_MULTI | FSR_SS | FSR_UUT | \
++ FSR_EF | FSR_PF | FSR_TF | FSR_IGN)
++
++#define FSYNR0_WNR (1 << 4)
++
++#endif /* _ARM_SMMU_REGS_H */
+diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
+index 7ec30b08b3bd..ca9c20f915a8 100644
+--- a/drivers/iommu/arm-smmu.c
++++ b/drivers/iommu/arm-smmu.c
+@@ -54,6 +54,7 @@
+ #include <linux/amba/bus.h>
+
+ #include "io-pgtable.h"
++#include "arm-smmu-regs.h"
+
+ /* Maximum number of context banks per SMMU */
+ #define ARM_SMMU_MAX_CBS 128
+@@ -83,211 +84,9 @@
+ #define smmu_write_atomic_lq writel_relaxed
+ #endif
+
+-/* Configuration registers */
+-#define ARM_SMMU_GR0_sCR0 0x0
+-#define sCR0_CLIENTPD (1 << 0)
+-#define sCR0_GFRE (1 << 1)
+-#define sCR0_GFIE (1 << 2)
+-#define sCR0_EXIDENABLE (1 << 3)
+-#define sCR0_GCFGFRE (1 << 4)
+-#define sCR0_GCFGFIE (1 << 5)
+-#define sCR0_USFCFG (1 << 10)
+-#define sCR0_VMIDPNE (1 << 11)
+-#define sCR0_PTM (1 << 12)
+-#define sCR0_FB (1 << 13)
+-#define sCR0_VMID16EN (1 << 31)
+-#define sCR0_BSU_SHIFT 14
+-#define sCR0_BSU_MASK 0x3
+-
+-/* Auxiliary Configuration register */
+-#define ARM_SMMU_GR0_sACR 0x10
+-
+-/* Identification registers */
+-#define ARM_SMMU_GR0_ID0 0x20
+-#define ARM_SMMU_GR0_ID1 0x24
+-#define ARM_SMMU_GR0_ID2 0x28
+-#define ARM_SMMU_GR0_ID3 0x2c
+-#define ARM_SMMU_GR0_ID4 0x30
+-#define ARM_SMMU_GR0_ID5 0x34
+-#define ARM_SMMU_GR0_ID6 0x38
+-#define ARM_SMMU_GR0_ID7 0x3c
+-#define ARM_SMMU_GR0_sGFSR 0x48
+-#define ARM_SMMU_GR0_sGFSYNR0 0x50
+-#define ARM_SMMU_GR0_sGFSYNR1 0x54
+-#define ARM_SMMU_GR0_sGFSYNR2 0x58
+-
+-#define ID0_S1TS (1 << 30)
+-#define ID0_S2TS (1 << 29)
+-#define ID0_NTS (1 << 28)
+-#define ID0_SMS (1 << 27)
+-#define ID0_ATOSNS (1 << 26)
+-#define ID0_PTFS_NO_AARCH32 (1 << 25)
+-#define ID0_PTFS_NO_AARCH32S (1 << 24)
+-#define ID0_CTTW (1 << 14)
+-#define ID0_NUMIRPT_SHIFT 16
+-#define ID0_NUMIRPT_MASK 0xff
+-#define ID0_NUMSIDB_SHIFT 9
+-#define ID0_NUMSIDB_MASK 0xf
+-#define ID0_EXIDS (1 << 8)
+-#define ID0_NUMSMRG_SHIFT 0
+-#define ID0_NUMSMRG_MASK 0xff
+-
+-#define ID1_PAGESIZE (1 << 31)
+-#define ID1_NUMPAGENDXB_SHIFT 28
+-#define ID1_NUMPAGENDXB_MASK 7
+-#define ID1_NUMS2CB_SHIFT 16
+-#define ID1_NUMS2CB_MASK 0xff
+-#define ID1_NUMCB_SHIFT 0
+-#define ID1_NUMCB_MASK 0xff
+-
+-#define ID2_OAS_SHIFT 4
+-#define ID2_OAS_MASK 0xf
+-#define ID2_IAS_SHIFT 0
+-#define ID2_IAS_MASK 0xf
+-#define ID2_UBS_SHIFT 8
+-#define ID2_UBS_MASK 0xf
+-#define ID2_PTFS_4K (1 << 12)
+-#define ID2_PTFS_16K (1 << 13)
+-#define ID2_PTFS_64K (1 << 14)
+-#define ID2_VMID16 (1 << 15)
+-
+-#define ID7_MAJOR_SHIFT 4
+-#define ID7_MAJOR_MASK 0xf
+-
+-/* Global TLB invalidation */
+-#define ARM_SMMU_GR0_TLBIVMID 0x64
+-#define ARM_SMMU_GR0_TLBIALLNSNH 0x68
+-#define ARM_SMMU_GR0_TLBIALLH 0x6c
+-#define ARM_SMMU_GR0_sTLBGSYNC 0x70
+-#define ARM_SMMU_GR0_sTLBGSTATUS 0x74
+-#define sTLBGSTATUS_GSACTIVE (1 << 0)
+-#define TLB_LOOP_TIMEOUT 1000000 /* 1s! */
+-#define TLB_SPIN_COUNT 10
+-
+-/* Stream mapping registers */
+-#define ARM_SMMU_GR0_SMR(n) (0x800 + ((n) << 2))
+-#define SMR_VALID (1 << 31)
+-#define SMR_MASK_SHIFT 16
+-#define SMR_ID_SHIFT 0
+-
+-#define ARM_SMMU_GR0_S2CR(n) (0xc00 + ((n) << 2))
+-#define S2CR_CBNDX_SHIFT 0
+-#define S2CR_CBNDX_MASK 0xff
+-#define S2CR_EXIDVALID (1 << 10)
+-#define S2CR_TYPE_SHIFT 16
+-#define S2CR_TYPE_MASK 0x3
+-enum arm_smmu_s2cr_type {
+- S2CR_TYPE_TRANS,
+- S2CR_TYPE_BYPASS,
+- S2CR_TYPE_FAULT,
+-};
+-
+-#define S2CR_PRIVCFG_SHIFT 24
+-#define S2CR_PRIVCFG_MASK 0x3
+-enum arm_smmu_s2cr_privcfg {
+- S2CR_PRIVCFG_DEFAULT,
+- S2CR_PRIVCFG_DIPAN,
+- S2CR_PRIVCFG_UNPRIV,
+- S2CR_PRIVCFG_PRIV,
+-};
+-
+-/* Context bank attribute registers */
+-#define ARM_SMMU_GR1_CBAR(n) (0x0 + ((n) << 2))
+-#define CBAR_VMID_SHIFT 0
+-#define CBAR_VMID_MASK 0xff
+-#define CBAR_S1_BPSHCFG_SHIFT 8
+-#define CBAR_S1_BPSHCFG_MASK 3
+-#define CBAR_S1_BPSHCFG_NSH 3
+-#define CBAR_S1_MEMATTR_SHIFT 12
+-#define CBAR_S1_MEMATTR_MASK 0xf
+-#define CBAR_S1_MEMATTR_WB 0xf
+-#define CBAR_TYPE_SHIFT 16
+-#define CBAR_TYPE_MASK 0x3
+-#define CBAR_TYPE_S2_TRANS (0 << CBAR_TYPE_SHIFT)
+-#define CBAR_TYPE_S1_TRANS_S2_BYPASS (1 << CBAR_TYPE_SHIFT)
+-#define CBAR_TYPE_S1_TRANS_S2_FAULT (2 << CBAR_TYPE_SHIFT)
+-#define CBAR_TYPE_S1_TRANS_S2_TRANS (3 << CBAR_TYPE_SHIFT)
+-#define CBAR_IRPTNDX_SHIFT 24
+-#define CBAR_IRPTNDX_MASK 0xff
+-
+-#define ARM_SMMU_GR1_CBA2R(n) (0x800 + ((n) << 2))
+-#define CBA2R_RW64_32BIT (0 << 0)
+-#define CBA2R_RW64_64BIT (1 << 0)
+-#define CBA2R_VMID_SHIFT 16
+-#define CBA2R_VMID_MASK 0xffff
+-
+ /* Translation context bank */
+ #define ARM_SMMU_CB(smmu, n) ((smmu)->cb_base + ((n) << (smmu)->pgshift))
+
+-#define ARM_SMMU_CB_SCTLR 0x0
+-#define ARM_SMMU_CB_ACTLR 0x4
+-#define ARM_SMMU_CB_RESUME 0x8
+-#define ARM_SMMU_CB_TTBCR2 0x10
+-#define ARM_SMMU_CB_TTBR0 0x20
+-#define ARM_SMMU_CB_TTBR1 0x28
+-#define ARM_SMMU_CB_TTBCR 0x30
+-#define ARM_SMMU_CB_CONTEXTIDR 0x34
+-#define ARM_SMMU_CB_S1_MAIR0 0x38
+-#define ARM_SMMU_CB_S1_MAIR1 0x3c
+-#define ARM_SMMU_CB_PAR 0x50
+-#define ARM_SMMU_CB_FSR 0x58
+-#define ARM_SMMU_CB_FAR 0x60
+-#define ARM_SMMU_CB_FSYNR0 0x68
+-#define ARM_SMMU_CB_S1_TLBIVA 0x600
+-#define ARM_SMMU_CB_S1_TLBIASID 0x610
+-#define ARM_SMMU_CB_S1_TLBIVAL 0x620
+-#define ARM_SMMU_CB_S2_TLBIIPAS2 0x630
+-#define ARM_SMMU_CB_S2_TLBIIPAS2L 0x638
+-#define ARM_SMMU_CB_TLBSYNC 0x7f0
+-#define ARM_SMMU_CB_TLBSTATUS 0x7f4
+-#define ARM_SMMU_CB_ATS1PR 0x800
+-#define ARM_SMMU_CB_ATSR 0x8f0
+-
+-#define SCTLR_S1_ASIDPNE (1 << 12)
+-#define SCTLR_CFCFG (1 << 7)
+-#define SCTLR_CFIE (1 << 6)
+-#define SCTLR_CFRE (1 << 5)
+-#define SCTLR_E (1 << 4)
+-#define SCTLR_AFE (1 << 2)
+-#define SCTLR_TRE (1 << 1)
+-#define SCTLR_M (1 << 0)
+-
+-#define ARM_MMU500_ACTLR_CPRE (1 << 1)
+-
+-#define ARM_MMU500_ACR_CACHE_LOCK (1 << 26)
+-#define ARM_MMU500_ACR_SMTNMB_TLBEN (1 << 8)
+-
+-#define CB_PAR_F (1 << 0)
+-
+-#define ATSR_ACTIVE (1 << 0)
+-
+-#define RESUME_RETRY (0 << 0)
+-#define RESUME_TERMINATE (1 << 0)
+-
+-#define TTBCR2_SEP_SHIFT 15
+-#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT)
+-#define TTBCR2_AS (1 << 4)
+-
+-#define TTBRn_ASID_SHIFT 48
+-
+-#define FSR_MULTI (1 << 31)
+-#define FSR_SS (1 << 30)
+-#define FSR_UUT (1 << 8)
+-#define FSR_ASF (1 << 7)
+-#define FSR_TLBLKF (1 << 6)
+-#define FSR_TLBMCF (1 << 5)
+-#define FSR_EF (1 << 4)
+-#define FSR_PF (1 << 3)
+-#define FSR_AFF (1 << 2)
+-#define FSR_TF (1 << 1)
+-
+-#define FSR_IGN (FSR_AFF | FSR_ASF | \
+- FSR_TLBMCF | FSR_TLBLKF)
+-#define FSR_FAULT (FSR_MULTI | FSR_SS | FSR_UUT | \
+- FSR_EF | FSR_PF | FSR_TF | FSR_IGN)
+-
+-#define FSYNR0_WNR (1 << 4)
+-
+ #define MSI_IOVA_BASE 0x8000000
+ #define MSI_IOVA_LENGTH 0x100000
+
+From patchwork Thu Jul 13 12:07:46 2017
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [RESEND,3/4] iommu: add qcom_iommu
+From: Rob Clark <robdclark@gmail.com>
+X-Patchwork-Id: 9838375
+Message-Id: <20170713120747.20490-4-robdclark@gmail.com>
+To: iommu@lists.linux-foundation.org
+Cc: linux-arm-msm@vger.kernel.org, Archit Taneja <architt@codeaurora.org>,
+ Rob Herring <robh@kernel.org>, Will Deacon <will.deacon@arm.com>,
+ Sricharan <sricharan@codeaurora.org>,
+ Mark Rutland <mark.rutland@arm.com>, Robin Murphy <robin.murphy@arm.com>,
+ Rob Clark <robdclark@gmail.com>
+Date: Thu, 13 Jul 2017 08:07:46 -0400
+
+An iommu driver for Qualcomm "B" family devices which do implement the
+ARM SMMU spec, but not in a way that is compatible with how the arm-smmu
+driver is designed. It seems SMMU_SCR1.GASRAE=1 so the global register
+space is not accessible. This means it needs to get configuration from
+devicetree instead of setting it up dynamically.
+
+In the end, other than register definitions, there is not much code to
+share with arm-smmu (other than what has already been refactored out
+into the pgtable helpers).
+
+Signed-off-by: Rob Clark <robdclark@gmail.com>
+Tested-by: Riku Voipio <riku.voipio@linaro.org>
+---
+v1: original
+v2: bindings cleanups and kconfig issues that kbuild robot pointed out
+v3: fix issues pointed out by Rob H. and actually make device removal
+ work
+v4: fix WARN_ON() splats reported by Archit
+v5: some fixes to build as a module.. note that it cannot actually
+ be built as a module yet (at minimum a bunch of other iommu syms
+ that are needed are not exported, but there may be more to it
+ than that), but at least qcom_iommu is ready should it become
+ possible to build iommu drivers as modules.
+v6: Add additional pm-runtime get/puts around paths that can hit
+ TLB inv, to avoid unclocked register access if device using the
+ iommu is not powered on. And pre-emptively clear interrupts
+ before registering IRQ handler just in case the bootloader has
+ left us a surpise.
+v7: Address review comments from Robin (don't associate iommu_group
+ with context bank, table lookup instead of list to find context
+ bank, etc)
+v8: Fix silly bug on detach. Actually Robin already pointed it out
+ but I somehow overlooked that comment when preparing v7.
+
+ drivers/iommu/Kconfig | 10 +
+ drivers/iommu/Makefile | 1 +
+ drivers/iommu/qcom_iommu.c | 868 +++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 879 insertions(+)
+ create mode 100644 drivers/iommu/qcom_iommu.c
+
+diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
+index 6ee3a25ae731..aa4b62893fe1 100644
+--- a/drivers/iommu/Kconfig
++++ b/drivers/iommu/Kconfig
+@@ -367,4 +367,14 @@ config MTK_IOMMU_V1
+
+ if unsure, say N here.
+
++config QCOM_IOMMU
++ # Note: iommu drivers cannot (yet?) be built as modules
++ bool "Qualcomm IOMMU Support"
++ depends on ARCH_QCOM || COMPILE_TEST
++ select IOMMU_API
++ select IOMMU_IO_PGTABLE_LPAE
++ select ARM_DMA_USE_IOMMU
++ help
++ Support for IOMMU on certain Qualcomm SoCs.
++
+ endif # IOMMU_SUPPORT
+diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
+index 195f7b997d8e..b910aea813a1 100644
+--- a/drivers/iommu/Makefile
++++ b/drivers/iommu/Makefile
+@@ -27,3 +27,4 @@ obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o
+ 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
+diff --git a/drivers/iommu/qcom_iommu.c b/drivers/iommu/qcom_iommu.c
+new file mode 100644
+index 000000000000..860cad1cb167
+--- /dev/null
++++ b/drivers/iommu/qcom_iommu.c
+@@ -0,0 +1,868 @@
++/*
++ * IOMMU API for QCOM secure IOMMUs. Somewhat based on arm-smmu.c
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ *
++ * Copyright (C) 2013 ARM Limited
++ * Copyright (C) 2017 Red Hat
++ */
++
++#include <linux/atomic.h>
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/dma-iommu.h>
++#include <linux/dma-mapping.h>
++#include <linux/err.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/io-64-nonatomic-hi-lo.h>
++#include <linux/iommu.h>
++#include <linux/iopoll.h>
++#include <linux/kconfig.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/of_device.h>
++#include <linux/of_iommu.h>
++#include <linux/platform_device.h>
++#include <linux/pm.h>
++#include <linux/pm_runtime.h>
++#include <linux/qcom_scm.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++
++#include "io-pgtable.h"
++#include "arm-smmu-regs.h"
++
++#define SMMU_INTR_SEL_NS 0x2000
++
++struct qcom_iommu_ctx;
++
++struct qcom_iommu_dev {
++ /* IOMMU core code handle */
++ struct iommu_device iommu;
++ struct device *dev;
++ struct clk *iface_clk;
++ struct clk *bus_clk;
++ void __iomem *local_base;
++ u32 sec_id;
++ u8 num_ctxs;
++ struct qcom_iommu_ctx *ctxs[0]; /* indexed by asid-1 */
++};
++
++struct qcom_iommu_ctx {
++ struct device *dev;
++ void __iomem *base;
++ bool secure_init;
++ u8 asid; /* asid and ctx bank # are 1:1 */
++};
++
++struct qcom_iommu_domain {
++ struct io_pgtable_ops *pgtbl_ops;
++ spinlock_t pgtbl_lock;
++ struct mutex init_mutex; /* Protects iommu pointer */
++ struct iommu_domain domain;
++ struct qcom_iommu_dev *iommu;
++};
++
++static struct qcom_iommu_domain *to_qcom_iommu_domain(struct iommu_domain *dom)
++{
++ return container_of(dom, struct qcom_iommu_domain, domain);
++}
++
++static const struct iommu_ops qcom_iommu_ops;
++
++static struct qcom_iommu_dev * to_iommu(struct iommu_fwspec *fwspec)
++{
++ if (!fwspec || fwspec->ops != &qcom_iommu_ops)
++ return NULL;
++ return fwspec->iommu_priv;
++}
++
++static struct qcom_iommu_ctx * to_ctx(struct iommu_fwspec *fwspec, unsigned asid)
++{
++ struct qcom_iommu_dev *qcom_iommu = to_iommu(fwspec);
++ if (!qcom_iommu)
++ return NULL;
++ return qcom_iommu->ctxs[asid - 1];
++}
++
++static inline void
++iommu_writel(struct qcom_iommu_ctx *ctx, unsigned reg, u32 val)
++{
++ writel_relaxed(val, ctx->base + reg);
++}
++
++static inline void
++iommu_writeq(struct qcom_iommu_ctx *ctx, unsigned reg, u64 val)
++{
++ writeq_relaxed(val, ctx->base + reg);
++}
++
++static inline u32
++iommu_readl(struct qcom_iommu_ctx *ctx, unsigned reg)
++{
++ return readl_relaxed(ctx->base + reg);
++}
++
++static inline u64
++iommu_readq(struct qcom_iommu_ctx *ctx, unsigned reg)
++{
++ return readq_relaxed(ctx->base + reg);
++}
++
++static void qcom_iommu_tlb_sync(void *cookie)
++{
++ struct iommu_fwspec *fwspec = cookie;
++ unsigned i;
++
++ for (i = 0; i < fwspec->num_ids; i++) {
++ struct qcom_iommu_ctx *ctx = to_ctx(fwspec, fwspec->ids[i]);
++ unsigned int val, ret;
++
++ iommu_writel(ctx, ARM_SMMU_CB_TLBSYNC, 0);
++
++ ret = readl_poll_timeout(ctx->base + ARM_SMMU_CB_TLBSTATUS, val,
++ (val & 0x1) == 0, 0, 5000000);
++ if (ret)
++ dev_err(ctx->dev, "timeout waiting for TLB SYNC\n");
++ }
++}
++
++static void qcom_iommu_tlb_inv_context(void *cookie)
++{
++ struct iommu_fwspec *fwspec = cookie;
++ unsigned i;
++
++ for (i = 0; i < fwspec->num_ids; i++) {
++ struct qcom_iommu_ctx *ctx = to_ctx(fwspec, fwspec->ids[i]);
++ iommu_writel(ctx, ARM_SMMU_CB_S1_TLBIASID, ctx->asid);
++ }
++
++ qcom_iommu_tlb_sync(cookie);
++}
++
++static void qcom_iommu_tlb_inv_range_nosync(unsigned long iova, size_t size,
++ size_t granule, bool leaf, void *cookie)
++{
++ struct iommu_fwspec *fwspec = cookie;
++ unsigned i, reg;
++
++ reg = leaf ? ARM_SMMU_CB_S1_TLBIVAL : ARM_SMMU_CB_S1_TLBIVA;
++
++ for (i = 0; i < fwspec->num_ids; i++) {
++ struct qcom_iommu_ctx *ctx = to_ctx(fwspec, fwspec->ids[i]);
++ size_t s = size;
++
++ iova &= ~12UL;
++ iova |= ctx->asid;
++ do {
++ iommu_writel(ctx, reg, iova);
++ iova += granule;
++ } while (s -= granule);
++ }
++}
++
++static const struct iommu_gather_ops qcom_gather_ops = {
++ .tlb_flush_all = qcom_iommu_tlb_inv_context,
++ .tlb_add_flush = qcom_iommu_tlb_inv_range_nosync,
++ .tlb_sync = qcom_iommu_tlb_sync,
++};
++
++static irqreturn_t qcom_iommu_fault(int irq, void *dev)
++{
++ struct qcom_iommu_ctx *ctx = dev;
++ u32 fsr, fsynr;
++ u64 iova;
++
++ fsr = iommu_readl(ctx, ARM_SMMU_CB_FSR);
++
++ if (!(fsr & FSR_FAULT))
++ return IRQ_NONE;
++
++ fsynr = iommu_readl(ctx, ARM_SMMU_CB_FSYNR0);
++ iova = iommu_readq(ctx, ARM_SMMU_CB_FAR);
++
++ dev_err_ratelimited(ctx->dev,
++ "Unhandled context fault: fsr=0x%x, "
++ "iova=0x%016llx, fsynr=0x%x, cb=%d\n",
++ fsr, iova, fsynr, ctx->asid);
++
++ iommu_writel(ctx, ARM_SMMU_CB_FSR, fsr);
++
++ return IRQ_HANDLED;
++}
++
++static int qcom_iommu_init_domain(struct iommu_domain *domain,
++ struct qcom_iommu_dev *qcom_iommu,
++ struct iommu_fwspec *fwspec)
++{
++ struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
++ struct io_pgtable_ops *pgtbl_ops;
++ struct io_pgtable_cfg pgtbl_cfg;
++ int i, ret = 0;
++ u32 reg;
++
++ mutex_lock(&qcom_domain->init_mutex);
++ if (qcom_domain->iommu)
++ goto out_unlock;
++
++ pgtbl_cfg = (struct io_pgtable_cfg) {
++ .pgsize_bitmap = qcom_iommu_ops.pgsize_bitmap,
++ .ias = 32,
++ .oas = 40,
++ .tlb = &qcom_gather_ops,
++ .iommu_dev = qcom_iommu->dev,
++ };
++
++ qcom_domain->iommu = qcom_iommu;
++ pgtbl_ops = alloc_io_pgtable_ops(ARM_32_LPAE_S1, &pgtbl_cfg, fwspec);
++ if (!pgtbl_ops) {
++ dev_err(qcom_iommu->dev, "failed to allocate pagetable ops\n");
++ ret = -ENOMEM;
++ goto out_clear_iommu;
++ }
++
++ /* Update the domain's page sizes to reflect the page table format */
++ domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
++ domain->geometry.aperture_end = (1ULL << pgtbl_cfg.ias) - 1;
++ domain->geometry.force_aperture = true;
++
++ for (i = 0; i < fwspec->num_ids; i++) {
++ struct qcom_iommu_ctx *ctx = to_ctx(fwspec, fwspec->ids[i]);
++
++ if (!ctx->secure_init) {
++ ret = qcom_scm_restore_sec_cfg(qcom_iommu->sec_id, ctx->asid);
++ if (ret) {
++ dev_err(qcom_iommu->dev, "secure init failed: %d\n", ret);
++ goto out_clear_iommu;
++ }
++ ctx->secure_init = true;
++ }
++
++ /* TTBRs */
++ iommu_writeq(ctx, ARM_SMMU_CB_TTBR0,
++ pgtbl_cfg.arm_lpae_s1_cfg.ttbr[0] |
++ ((u64)ctx->asid << TTBRn_ASID_SHIFT));
++ iommu_writeq(ctx, ARM_SMMU_CB_TTBR1,
++ pgtbl_cfg.arm_lpae_s1_cfg.ttbr[1] |
++ ((u64)ctx->asid << TTBRn_ASID_SHIFT));
++
++ /* TTBCR */
++ iommu_writel(ctx, ARM_SMMU_CB_TTBCR2,
++ (pgtbl_cfg.arm_lpae_s1_cfg.tcr >> 32) |
++ TTBCR2_SEP_UPSTREAM);
++ iommu_writel(ctx, ARM_SMMU_CB_TTBCR,
++ pgtbl_cfg.arm_lpae_s1_cfg.tcr);
++
++ /* MAIRs (stage-1 only) */
++ iommu_writel(ctx, ARM_SMMU_CB_S1_MAIR0,
++ pgtbl_cfg.arm_lpae_s1_cfg.mair[0]);
++ iommu_writel(ctx, ARM_SMMU_CB_S1_MAIR1,
++ pgtbl_cfg.arm_lpae_s1_cfg.mair[1]);
++
++ /* SCTLR */
++ reg = SCTLR_CFIE | SCTLR_CFRE | SCTLR_AFE | SCTLR_TRE |
++ SCTLR_M | SCTLR_S1_ASIDPNE;
++
++ if (IS_ENABLED(CONFIG_BIG_ENDIAN))
++ reg |= SCTLR_E;
++
++ iommu_writel(ctx, ARM_SMMU_CB_SCTLR, reg);
++ }
++
++ mutex_unlock(&qcom_domain->init_mutex);
++
++ /* Publish page table ops for map/unmap */
++ qcom_domain->pgtbl_ops = pgtbl_ops;
++
++ return 0;
++
++out_clear_iommu:
++ qcom_domain->iommu = NULL;
++out_unlock:
++ mutex_unlock(&qcom_domain->init_mutex);
++ return ret;
++}
++
++static struct iommu_domain *qcom_iommu_domain_alloc(unsigned type)
++{
++ struct qcom_iommu_domain *qcom_domain;
++
++ if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA)
++ return NULL;
++ /*
++ * Allocate the domain and initialise some of its data structures.
++ * We can't really do anything meaningful until we've added a
++ * master.
++ */
++ qcom_domain = kzalloc(sizeof(*qcom_domain), GFP_KERNEL);
++ if (!qcom_domain)
++ return NULL;
++
++ if (type == IOMMU_DOMAIN_DMA &&
++ iommu_get_dma_cookie(&qcom_domain->domain)) {
++ kfree(qcom_domain);
++ return NULL;
++ }
++
++ mutex_init(&qcom_domain->init_mutex);
++ spin_lock_init(&qcom_domain->pgtbl_lock);
++
++ return &qcom_domain->domain;
++}
++
++static void qcom_iommu_domain_free(struct iommu_domain *domain)
++{
++ struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
++
++ if (WARN_ON(qcom_domain->iommu)) /* forgot to detach? */
++ return;
++
++ iommu_put_dma_cookie(domain);
++
++ /* NOTE: unmap can be called after client device is powered off,
++ * for example, with GPUs or anything involving dma-buf. So we
++ * cannot rely on the device_link. Make sure the IOMMU is on to
++ * avoid unclocked accesses in the TLB inv path:
++ */
++ pm_runtime_get_sync(qcom_domain->iommu->dev);
++
++ free_io_pgtable_ops(qcom_domain->pgtbl_ops);
++
++ pm_runtime_put_sync(qcom_domain->iommu->dev);
++
++ kfree(qcom_domain);
++}
++
++static int qcom_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
++{
++ struct qcom_iommu_dev *qcom_iommu = to_iommu(dev->iommu_fwspec);
++ struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
++ int ret;
++
++ if (!qcom_iommu) {
++ dev_err(dev, "cannot attach to IOMMU, is it on the same bus?\n");
++ return -ENXIO;
++ }
++
++ /* Ensure that the domain is finalized */
++ pm_runtime_get_sync(qcom_iommu->dev);
++ ret = qcom_iommu_init_domain(domain, qcom_iommu, dev->iommu_fwspec);
++ pm_runtime_put_sync(qcom_iommu->dev);
++ if (ret < 0)
++ return ret;
++
++ /*
++ * Sanity check the domain. We don't support domains across
++ * different IOMMUs.
++ */
++ if (qcom_domain->iommu != qcom_iommu) {
++ dev_err(dev, "cannot attach to IOMMU %s while already "
++ "attached to domain on IOMMU %s\n",
++ dev_name(qcom_domain->iommu->dev),
++ dev_name(qcom_iommu->dev));
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static void qcom_iommu_detach_dev(struct iommu_domain *domain, struct device *dev)
++{
++ struct iommu_fwspec *fwspec = dev->iommu_fwspec;
++ struct qcom_iommu_dev *qcom_iommu = to_iommu(fwspec);
++ struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
++ unsigned i;
++
++ if (!qcom_domain->iommu)
++ return;
++
++ pm_runtime_get_sync(qcom_iommu->dev);
++ for (i = 0; i < fwspec->num_ids; i++) {
++ struct qcom_iommu_ctx *ctx = to_ctx(fwspec, fwspec->ids[i]);
++
++ /* Disable the context bank: */
++ iommu_writel(ctx, ARM_SMMU_CB_SCTLR, 0);
++ }
++ pm_runtime_put_sync(qcom_iommu->dev);
++
++ qcom_domain->iommu = NULL;
++}
++
++static int qcom_iommu_map(struct iommu_domain *domain, unsigned long iova,
++ phys_addr_t paddr, size_t size, int prot)
++{
++ int ret;
++ unsigned long flags;
++ struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
++ struct io_pgtable_ops *ops = qcom_domain->pgtbl_ops;
++
++ if (!ops)
++ return -ENODEV;
++
++ spin_lock_irqsave(&qcom_domain->pgtbl_lock, flags);
++ ret = ops->map(ops, iova, paddr, size, prot);
++ spin_unlock_irqrestore(&qcom_domain->pgtbl_lock, flags);
++ return ret;
++}
++
++static size_t qcom_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
++ size_t size)
++{
++ size_t ret;
++ unsigned long flags;
++ struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
++ struct io_pgtable_ops *ops = qcom_domain->pgtbl_ops;
++
++ if (!ops)
++ return 0;
++
++ /* NOTE: unmap can be called after client device is powered off,
++ * for example, with GPUs or anything involving dma-buf. So we
++ * cannot rely on the device_link. Make sure the IOMMU is on to
++ * avoid unclocked accesses in the TLB inv path:
++ */
++ pm_runtime_get_sync(qcom_domain->iommu->dev);
++ spin_lock_irqsave(&qcom_domain->pgtbl_lock, flags);
++ ret = ops->unmap(ops, iova, size);
++ spin_unlock_irqrestore(&qcom_domain->pgtbl_lock, flags);
++ pm_runtime_put_sync(qcom_domain->iommu->dev);
++
++ return ret;
++}
++
++static phys_addr_t qcom_iommu_iova_to_phys(struct iommu_domain *domain,
++ dma_addr_t iova)
++{
++ phys_addr_t ret;
++ unsigned long flags;
++ struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
++ struct io_pgtable_ops *ops = qcom_domain->pgtbl_ops;
++
++ if (!ops)
++ return 0;
++
++ spin_lock_irqsave(&qcom_domain->pgtbl_lock, flags);
++ ret = ops->iova_to_phys(ops, iova);
++ spin_unlock_irqrestore(&qcom_domain->pgtbl_lock, flags);
++
++ return ret;
++}
++
++static bool qcom_iommu_capable(enum iommu_cap cap)
++{
++ switch (cap) {
++ case IOMMU_CAP_CACHE_COHERENCY:
++ /*
++ * Return true here as the SMMU can always send out coherent
++ * requests.
++ */
++ return true;
++ case IOMMU_CAP_NOEXEC:
++ return true;
++ default:
++ return false;
++ }
++}
++
++static int qcom_iommu_add_device(struct device *dev)
++{
++ struct qcom_iommu_dev *qcom_iommu = to_iommu(dev->iommu_fwspec);
++ struct iommu_group *group;
++ struct device_link *link;
++
++ if (!qcom_iommu)
++ return -ENODEV;
++
++ /*
++ * Establish the link between iommu and master, so that the
++ * iommu gets runtime enabled/disabled as per the master's
++ * needs.
++ */
++ link = device_link_add(dev, qcom_iommu->dev, DL_FLAG_PM_RUNTIME);
++ if (!link) {
++ dev_err(qcom_iommu->dev, "Unable to create device link between %s and %s\n",
++ dev_name(qcom_iommu->dev), dev_name(dev));
++ return -ENODEV;
++ }
++
++ group = iommu_group_get_for_dev(dev);
++ if (IS_ERR_OR_NULL(group))
++ return PTR_ERR_OR_ZERO(group);
++
++ iommu_group_put(group);
++ iommu_device_link(&qcom_iommu->iommu, dev);
++
++ return 0;
++}
++
++static void qcom_iommu_remove_device(struct device *dev)
++{
++ struct qcom_iommu_dev *qcom_iommu = to_iommu(dev->iommu_fwspec);
++
++ if (!qcom_iommu)
++ return;
++
++ iommu_device_unlink(&qcom_iommu->iommu, dev);
++ iommu_group_remove_device(dev);
++ iommu_fwspec_free(dev);
++}
++
++static int qcom_iommu_of_xlate(struct device *dev, struct of_phandle_args *args)
++{
++ struct qcom_iommu_dev *qcom_iommu;
++ struct platform_device *iommu_pdev;
++ unsigned asid = args->args[0];
++
++ if (args->args_count != 1) {
++ dev_err(dev, "incorrect number of iommu params found for %s "
++ "(found %d, expected 1)\n",
++ args->np->full_name, args->args_count);
++ return -EINVAL;
++ }
++
++ iommu_pdev = of_find_device_by_node(args->np);
++ if (WARN_ON(!iommu_pdev))
++ return -EINVAL;
++
++ qcom_iommu = platform_get_drvdata(iommu_pdev);
++
++ /* make sure the asid specified in dt is valid, so we don't have
++ * to sanity check this elsewhere, since 'asid - 1' is used to
++ * index into qcom_iommu->ctxs:
++ */
++ if (WARN_ON(asid < 1) ||
++ WARN_ON(asid > qcom_iommu->num_ctxs))
++ return -EINVAL;
++
++ if (!dev->iommu_fwspec->iommu_priv) {
++ dev->iommu_fwspec->iommu_priv = qcom_iommu;
++ } else {
++ /* make sure devices iommus dt node isn't referring to
++ * multiple different iommu devices. Multiple context
++ * banks are ok, but multiple devices are not:
++ */
++ if (WARN_ON(qcom_iommu != dev->iommu_fwspec->iommu_priv))
++ return -EINVAL;
++ }
++
++ return iommu_fwspec_add_ids(dev, &asid, 1);
++}
++
++static const struct iommu_ops qcom_iommu_ops = {
++ .capable = qcom_iommu_capable,
++ .domain_alloc = qcom_iommu_domain_alloc,
++ .domain_free = qcom_iommu_domain_free,
++ .attach_dev = qcom_iommu_attach_dev,
++ .detach_dev = qcom_iommu_detach_dev,
++ .map = qcom_iommu_map,
++ .unmap = qcom_iommu_unmap,
++ .map_sg = default_iommu_map_sg,
++ .iova_to_phys = qcom_iommu_iova_to_phys,
++ .add_device = qcom_iommu_add_device,
++ .remove_device = qcom_iommu_remove_device,
++ .device_group = generic_device_group,
++ .of_xlate = qcom_iommu_of_xlate,
++ .pgsize_bitmap = SZ_4K | SZ_64K | SZ_1M | SZ_16M,
++};
++
++static int qcom_iommu_enable_clocks(struct qcom_iommu_dev *qcom_iommu)
++{
++ int ret;
++
++ ret = clk_prepare_enable(qcom_iommu->iface_clk);
++ if (ret) {
++ dev_err(qcom_iommu->dev, "Couldn't enable iface_clk\n");
++ return ret;
++ }
++
++ ret = clk_prepare_enable(qcom_iommu->bus_clk);
++ if (ret) {
++ dev_err(qcom_iommu->dev, "Couldn't enable bus_clk\n");
++ clk_disable_unprepare(qcom_iommu->iface_clk);
++ return ret;
++ }
++
++ return 0;
++}
++
++static void qcom_iommu_disable_clocks(struct qcom_iommu_dev *qcom_iommu)
++{
++ clk_disable_unprepare(qcom_iommu->bus_clk);
++ clk_disable_unprepare(qcom_iommu->iface_clk);
++}
++
++static int get_asid(const struct device_node *np)
++{
++ u32 reg;
++
++ /* read the "reg" property directly to get the relative address
++ * of the context bank, and calculate the asid from that:
++ */
++ if (of_property_read_u32_index(np, "reg", 0, &reg))
++ return -ENODEV;
++
++ return reg / 0x1000; /* context banks are 0x1000 apart */
++}
++
++static int qcom_iommu_ctx_probe(struct platform_device *pdev)
++{
++ struct qcom_iommu_ctx *ctx;
++ struct device *dev = &pdev->dev;
++ struct qcom_iommu_dev *qcom_iommu = dev_get_drvdata(dev->parent);
++ struct resource *res;
++ int ret, irq;
++
++ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
++ if (!ctx)
++ return -ENOMEM;
++
++ ctx->dev = dev;
++ platform_set_drvdata(pdev, ctx);
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ ctx->base = devm_ioremap_resource(dev, res);
++ if (IS_ERR(ctx->base))
++ return PTR_ERR(ctx->base);
++
++ irq = platform_get_irq(pdev, 0);
++ if (irq < 0) {
++ dev_err(dev, "failed to get irq\n");
++ return -ENODEV;
++ }
++
++ /* clear IRQs before registering fault handler, just in case the
++ * boot-loader left us a surprise:
++ */
++ iommu_writel(ctx, ARM_SMMU_CB_FSR, iommu_readl(ctx, ARM_SMMU_CB_FSR));
++
++ ret = devm_request_irq(dev, irq,
++ qcom_iommu_fault,
++ IRQF_SHARED,
++ "qcom-iommu-fault",
++ ctx);
++ if (ret) {
++ dev_err(dev, "failed to request IRQ %u\n", irq);
++ return ret;
++ }
++
++ ret = get_asid(dev->of_node);
++ if (ret < 0) {
++ dev_err(dev, "missing reg property\n");
++ return ret;
++ }
++
++ ctx->asid = ret;
++
++ dev_dbg(dev, "found asid %u\n", ctx->asid);
++
++ qcom_iommu->ctxs[ctx->asid - 1] = ctx;
++
++ return 0;
++}
++
++static int qcom_iommu_ctx_remove(struct platform_device *pdev)
++{
++ struct qcom_iommu_dev *qcom_iommu = dev_get_drvdata(pdev->dev.parent);
++ struct qcom_iommu_ctx *ctx = platform_get_drvdata(pdev);
++
++ platform_set_drvdata(pdev, NULL);
++
++ qcom_iommu->ctxs[ctx->asid - 1] = NULL;
++
++ return 0;
++}
++
++static const struct of_device_id ctx_of_match[] = {
++ { .compatible = "qcom,msm-iommu-v1-ns" },
++ { .compatible = "qcom,msm-iommu-v1-sec" },
++ { /* sentinel */ }
++};
++
++static struct platform_driver qcom_iommu_ctx_driver = {
++ .driver = {
++ .name = "qcom-iommu-ctx",
++ .of_match_table = of_match_ptr(ctx_of_match),
++ },
++ .probe = qcom_iommu_ctx_probe,
++ .remove = qcom_iommu_ctx_remove,
++};
++
++static int qcom_iommu_device_probe(struct platform_device *pdev)
++{
++ struct device_node *child;
++ struct qcom_iommu_dev *qcom_iommu;
++ struct device *dev = &pdev->dev;
++ struct resource *res;
++ int ret, sz, max_asid = 0;
++
++ /* find the max asid (which is 1:1 to ctx bank idx), so we know how
++ * many child ctx devices we have:
++ */
++ for_each_child_of_node(dev->of_node, child)
++ max_asid = max(max_asid, get_asid(child));
++
++ sz = sizeof(*qcom_iommu) + (max_asid * sizeof(qcom_iommu->ctxs[0]));
++
++ qcom_iommu = devm_kzalloc(dev, sz, GFP_KERNEL);
++ if (!qcom_iommu)
++ return -ENOMEM;
++ qcom_iommu->num_ctxs = max_asid;
++ qcom_iommu->dev = dev;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (res)
++ qcom_iommu->local_base = devm_ioremap_resource(dev, res);
++
++ qcom_iommu->iface_clk = devm_clk_get(dev, "iface");
++ if (IS_ERR(qcom_iommu->iface_clk)) {
++ dev_err(dev, "failed to get iface clock\n");
++ return PTR_ERR(qcom_iommu->iface_clk);
++ }
++
++ qcom_iommu->bus_clk = devm_clk_get(dev, "bus");
++ if (IS_ERR(qcom_iommu->bus_clk)) {
++ dev_err(dev, "failed to get bus clock\n");
++ return PTR_ERR(qcom_iommu->bus_clk);
++ }
++
++ if (of_property_read_u32(dev->of_node, "qcom,iommu-secure-id",
++ &qcom_iommu->sec_id)) {
++ dev_err(dev, "missing qcom,iommu-secure-id property\n");
++ return -ENODEV;
++ }
++
++ platform_set_drvdata(pdev, qcom_iommu);
++
++ pm_runtime_enable(dev);
++
++ /* register context bank devices, which are child nodes: */
++ ret = devm_of_platform_populate(dev);
++ if (ret) {
++ dev_err(dev, "Failed to populate iommu contexts\n");
++ return ret;
++ }
++
++ ret = iommu_device_sysfs_add(&qcom_iommu->iommu, dev, NULL,
++ dev_name(dev));
++ if (ret) {
++ dev_err(dev, "Failed to register iommu in sysfs\n");
++ return ret;
++ }
++
++ iommu_device_set_ops(&qcom_iommu->iommu, &qcom_iommu_ops);
++ iommu_device_set_fwnode(&qcom_iommu->iommu, dev->fwnode);
++
++ ret = iommu_device_register(&qcom_iommu->iommu);
++ if (ret) {
++ dev_err(dev, "Failed to register iommu\n");
++ return ret;
++ }
++
++ bus_set_iommu(&platform_bus_type, &qcom_iommu_ops);
++
++ if (qcom_iommu->local_base) {
++ pm_runtime_get_sync(dev);
++ writel_relaxed(0xffffffff, qcom_iommu->local_base + SMMU_INTR_SEL_NS);
++ pm_runtime_put_sync(dev);
++ }
++
++ return 0;
++}
++
++static int qcom_iommu_device_remove(struct platform_device *pdev)
++{
++ struct qcom_iommu_dev *qcom_iommu = platform_get_drvdata(pdev);
++
++ bus_set_iommu(&platform_bus_type, NULL);
++
++ pm_runtime_force_suspend(&pdev->dev);
++ platform_set_drvdata(pdev, NULL);
++ iommu_device_sysfs_remove(&qcom_iommu->iommu);
++ iommu_device_unregister(&qcom_iommu->iommu);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int qcom_iommu_resume(struct device *dev)
++{
++ struct platform_device *pdev = to_platform_device(dev);
++ struct qcom_iommu_dev *qcom_iommu = platform_get_drvdata(pdev);
++
++ return qcom_iommu_enable_clocks(qcom_iommu);
++}
++
++static int qcom_iommu_suspend(struct device *dev)
++{
++ struct platform_device *pdev = to_platform_device(dev);
++ struct qcom_iommu_dev *qcom_iommu = platform_get_drvdata(pdev);
++
++ qcom_iommu_disable_clocks(qcom_iommu);
++
++ return 0;
++}
++#endif
++
++static const struct dev_pm_ops qcom_iommu_pm_ops = {
++ SET_RUNTIME_PM_OPS(qcom_iommu_suspend, qcom_iommu_resume, NULL)
++ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
++ pm_runtime_force_resume)
++};
++
++static const struct of_device_id qcom_iommu_of_match[] = {
++ { .compatible = "qcom,msm-iommu-v1" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, qcom_iommu_of_match);
++
++static struct platform_driver qcom_iommu_driver = {
++ .driver = {
++ .name = "qcom-iommu",
++ .of_match_table = of_match_ptr(qcom_iommu_of_match),
++ .pm = &qcom_iommu_pm_ops,
++ },
++ .probe = qcom_iommu_device_probe,
++ .remove = qcom_iommu_device_remove,
++};
++
++static int __init qcom_iommu_init(void)
++{
++ int ret;
++
++ ret = platform_driver_register(&qcom_iommu_ctx_driver);
++ if (ret)
++ return ret;
++
++ ret = platform_driver_register(&qcom_iommu_driver);
++ if (ret)
++ platform_driver_unregister(&qcom_iommu_ctx_driver);
++
++ return ret;
++}
++
++static void __exit qcom_iommu_exit(void)
++{
++ platform_driver_unregister(&qcom_iommu_driver);
++ platform_driver_unregister(&qcom_iommu_ctx_driver);
++}
++
++module_init(qcom_iommu_init);
++module_exit(qcom_iommu_exit);
++
++IOMMU_OF_DECLARE(qcom_iommu_dev, "qcom,msm-iommu-v1", NULL);
++
++MODULE_DESCRIPTION("IOMMU API for QCOM IOMMU v1 implementations");
++MODULE_LICENSE("GPL v2");
+From patchwork Thu Jul 13 12:07:47 2017
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [RESEND,4/4] iommu: qcom: initialize secure page table
+From: Rob Clark <robdclark@gmail.com>
+X-Patchwork-Id: 9838373
+Message-Id: <20170713120747.20490-5-robdclark@gmail.com>
+To: iommu@lists.linux-foundation.org
+Cc: linux-arm-msm@vger.kernel.org, Archit Taneja <architt@codeaurora.org>,
+ Rob Herring <robh@kernel.org>, Will Deacon <will.deacon@arm.com>,
+ Sricharan <sricharan@codeaurora.org>,
+ Mark Rutland <mark.rutland@arm.com>, Robin Murphy <robin.murphy@arm.com>,
+ Stanimir Varbanov <stanimir.varbanov@linaro.org>,
+ Rob Clark <robdclark@gmail.com>
+Date: Thu, 13 Jul 2017 08:07:47 -0400
+
+From: Stanimir Varbanov <stanimir.varbanov@linaro.org>
+
+This basically gets the secure page table size, allocates memory for
+secure pagetables and passes the physical address to the trusted zone.
+
+Signed-off-by: Stanimir Varbanov <stanimir.varbanov@linaro.org>
+Signed-off-by: Rob Clark <robdclark@gmail.com>
+---
+ drivers/iommu/qcom_iommu.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 64 insertions(+)
+
+diff --git a/drivers/iommu/qcom_iommu.c b/drivers/iommu/qcom_iommu.c
+index 860cad1cb167..48b62aa52787 100644
+--- a/drivers/iommu/qcom_iommu.c
++++ b/drivers/iommu/qcom_iommu.c
+@@ -604,6 +604,51 @@ static void qcom_iommu_disable_clocks(struct qcom_iommu_dev *qcom_iommu)
+ clk_disable_unprepare(qcom_iommu->iface_clk);
+ }
+
++static int qcom_iommu_sec_ptbl_init(struct device *dev)
++{
++ size_t psize = 0;
++ unsigned int spare = 0;
++ void *cpu_addr;
++ dma_addr_t paddr;
++ unsigned long attrs;
++ static bool allocated = false;
++ int ret;
++
++ if (allocated)
++ return 0;
++
++ ret = qcom_scm_iommu_secure_ptbl_size(spare, &psize);
++ if (ret) {
++ dev_err(dev, "failed to get iommu secure pgtable size (%d)\n",
++ ret);
++ return ret;
++ }
++
++ dev_info(dev, "iommu sec: pgtable size: %zu\n", psize);
++
++ attrs = DMA_ATTR_NO_KERNEL_MAPPING;
++
++ cpu_addr = dma_alloc_attrs(dev, psize, &paddr, GFP_KERNEL, attrs);
++ if (!cpu_addr) {
++ dev_err(dev, "failed to allocate %zu bytes for pgtable\n",
++ psize);
++ return -ENOMEM;
++ }
++
++ ret = qcom_scm_iommu_secure_ptbl_init(paddr, psize, spare);
++ if (ret) {
++ dev_err(dev, "failed to init iommu pgtable (%d)\n", ret);
++ goto free_mem;
++ }
++
++ allocated = true;
++ return 0;
++
++free_mem:
++ dma_free_attrs(dev, psize, cpu_addr, paddr, attrs);
++ return ret;
++}
++
+ static int get_asid(const struct device_node *np)
+ {
+ u32 reg;
+@@ -700,6 +745,17 @@ static struct platform_driver qcom_iommu_ctx_driver = {
+ .remove = qcom_iommu_ctx_remove,
+ };
+
++static bool qcom_iommu_has_secure_context(struct qcom_iommu_dev *qcom_iommu)
++{
++ struct device_node *child;
++
++ for_each_child_of_node(qcom_iommu->dev->of_node, child)
++ if (of_device_is_compatible(child, "qcom,msm-iommu-v1-sec"))
++ return true;
++
++ return false;
++}
++
+ static int qcom_iommu_device_probe(struct platform_device *pdev)
+ {
+ struct device_node *child;
+@@ -744,6 +800,14 @@ static int qcom_iommu_device_probe(struct platform_device *pdev)
+ return -ENODEV;
+ }
+
++ if (qcom_iommu_has_secure_context(qcom_iommu)) {
++ ret = qcom_iommu_sec_ptbl_init(dev);
++ if (ret) {
++ dev_err(dev, "cannot init secure pg table(%d)\n", ret);
++ return ret;
++ }
++ }
++
+ platform_set_drvdata(pdev, qcom_iommu);
+
+ pm_runtime_enable(dev);
+From patchwork Mon Jun 12 12:43:15 2017
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [1/3] ARM64: DT: add gpu for msm8916
+From: Rob Clark <robdclark@gmail.com>
+X-Patchwork-Id: 9781057
+Message-Id: <20170612124317.29313-1-robdclark@gmail.com>
+To: linux-arm-msm@vger.kernel.org
+Cc: Stephen Boyd <sboyd@codeaurora.org>, Andy Gross <agross@codeaurora.org>,
+ Stanimir Varbanov <stanimir.varbanov@linaro.org>,
+ Rob Clark <robdclark@gmail.com>
+Date: Mon, 12 Jun 2017 08:43:15 -0400
+
+Signed-off-by: Rob Clark <robdclark@gmail.com>
+Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ arch/arm64/boot/dts/qcom/msm8916.dtsi | 35 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi
+index ab30939..24c24ab 100644
+--- a/arch/arm64/boot/dts/qcom/msm8916.dtsi
++++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi
+@@ -204,6 +204,17 @@
+
+ };
+
++ gpu_opp_table: opp_table {
++ compatible = "operating-points-v2";
++
++ opp-400000000 {
++ opp-hz = /bits/ 64 <400000000>;
++ };
++ opp-19200000 {
++ opp-hz = /bits/ 64 <19200000>;
++ };
++ };
++
+ timer {
+ compatible = "arm,armv8-timer";
+ interrupts = <GIC_PPI 2 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
+@@ -698,6 +709,30 @@
+ #thermal-sensor-cells = <1>;
+ };
+
++ gpu@1c00000 {
++ compatible = "qcom,adreno-306.0", "qcom,adreno";
++ reg = <0x01c00000 0x20000>;
++ reg-names = "kgsl_3d0_reg_memory";
++ interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "kgsl_3d0_irq";
++ clock-names =
++ "core",
++ "iface",
++ "mem",
++ "mem_iface",
++ "alt_mem_iface",
++ "gfx3d";
++ clocks =
++ <&gcc GCC_OXILI_GFX3D_CLK>,
++ <&gcc GCC_OXILI_AHB_CLK>,
++ <&gcc GCC_OXILI_GMEM_CLK>,
++ <&gcc GCC_BIMC_GFX_CLK>,
++ <&gcc GCC_BIMC_GPU_CLK>,
++ <&gcc GFX3D_CLK_SRC>;
++ power-domains = <&gcc OXILI_GDSC>;
++ operating-points-v2 = <&gpu_opp_table>;
++ };
++
+ mdss: mdss@1a00000 {
+ compatible = "qcom,mdss";
+ reg = <0x1a00000 0x1000>,
+From patchwork Mon Jun 12 12:43:16 2017
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [2/3] ARM64: DT: add video codec devicetree node
+From: Rob Clark <robdclark@gmail.com>
+X-Patchwork-Id: 9781059
+Message-Id: <20170612124317.29313-2-robdclark@gmail.com>
+To: linux-arm-msm@vger.kernel.org
+Cc: Stephen Boyd <sboyd@codeaurora.org>, Andy Gross <agross@codeaurora.org>,
+ Stanimir Varbanov <stanimir.varbanov@linaro.org>,
+ Rob Clark <robdclark@gmail.com>
+Date: Mon, 12 Jun 2017 08:43:16 -0400
+
+From: Stanimir Varbanov <stanimir.varbanov@linaro.org>
+
+Signed-off-by: Stanimir Varbanov <stanimir.varbanov@linaro.org>
+Signed-off-by: Rob Clark <robdclark@gmail.com>
+Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ arch/arm64/boot/dts/qcom/msm8916.dtsi | 28 ++++++++++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi
+index 24c24ab..1dcd632 100644
+--- a/arch/arm64/boot/dts/qcom/msm8916.dtsi
++++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi
+@@ -88,6 +88,13 @@
+ no-map;
+ };
+
++ venus_mem: venus@89900000 {
++ compatible = "shared-dma-pool";
++ reg = <0x0 0x89900000 0x0 0x800000>;
++ alignment = <0x1000>;
++ no-map;
++ };
++
+ mba_mem: mba@8ea00000 {
+ no-map;
+ reg = <0 0x8ea00000 0 0x100000>;
+@@ -1214,6 +1221,27 @@
+ };
+ };
+ };
++
++ venus: video-codec@1d00000 {
++ compatible = "qcom,msm8916-venus";
++ reg = <0x01d00000 0xff000>;
++ interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
++ power-domains = <&gcc VENUS_GDSC>;
++ clocks = <&gcc GCC_VENUS0_VCODEC0_CLK>,
++ <&gcc GCC_VENUS0_AHB_CLK>,
++ <&gcc GCC_VENUS0_AXI_CLK>;
++ clock-names = "core", "iface", "bus";
++ memory-region = <&venus_mem>;
++ status = "okay";
++
++ video-decoder {
++ compatible = "venus-decoder";
++ };
++
++ video-encoder {
++ compatible = "venus-encoder";
++ };
++ };
+ };
+
+ smd {
+From patchwork Mon Jun 12 12:43:17 2017
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [3/3] ARM64: DT: add iommu for msm8916
+From: Rob Clark <robdclark@gmail.com>
+X-Patchwork-Id: 9781061
+Message-Id: <20170612124317.29313-3-robdclark@gmail.com>
+To: linux-arm-msm@vger.kernel.org
+Cc: Stephen Boyd <sboyd@codeaurora.org>, Andy Gross <agross@codeaurora.org>,
+ Stanimir Varbanov <stanimir.varbanov@linaro.org>,
+ Rob Clark <robdclark@gmail.com>
+Date: Mon, 12 Jun 2017 08:43:17 -0400
+
+Signed-off-by: Rob Clark <robdclark@gmail.com>
+Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
+---
+ arch/arm64/boot/dts/qcom/msm8916.dtsi | 57 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 57 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi
+index 1dcd632..9a1d7ef 100644
+--- a/arch/arm64/boot/dts/qcom/msm8916.dtsi
++++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi
+@@ -716,6 +716,59 @@
+ #thermal-sensor-cells = <1>;
+ };
+
++ apps_iommu: iommu@1ef0000 {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ #iommu-cells = <1>;
++ compatible = "qcom,msm8916-iommu", "qcom,msm-iommu-v1";
++ ranges = <0 0x1e20000 0x40000>;
++ reg = <0x1ef0000 0x3000>;
++ clocks = <&gcc GCC_SMMU_CFG_CLK>,
++ <&gcc GCC_APSS_TCU_CLK>;
++ clock-names = "iface", "bus";
++ qcom,iommu-secure-id = <17>;
++
++ // mdp_0:
++ iommu-ctx@4000 {
++ compatible = "qcom,msm-iommu-v1-ns";
++ reg = <0x4000 0x1000>;
++ interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ // venus_ns:
++ iommu-ctx@5000 {
++ compatible = "qcom,msm-iommu-v1-sec";
++ reg = <0x5000 0x1000>;
++ interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
++ };
++ };
++
++ gpu_iommu: iommu@1f08000 {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ #iommu-cells = <1>;
++ compatible = "qcom,msm8916-iommu", "qcom,msm-iommu-v1";
++ ranges = <0 0x1f08000 0x10000>;
++ clocks = <&gcc GCC_SMMU_CFG_CLK>,
++ <&gcc GCC_GFX_TCU_CLK>;
++ clock-names = "iface", "bus";
++ qcom,iommu-secure-id = <18>;
++
++ // gfx3d_user:
++ iommu-ctx@1000 {
++ compatible = "qcom,msm-iommu-v1-ns";
++ reg = <0x1000 0x1000>;
++ interrupts = <GIC_SPI 241 IRQ_TYPE_LEVEL_HIGH>;
++ };
++
++ // gfx3d_priv:
++ iommu-ctx@2000 {
++ compatible = "qcom,msm-iommu-v1-ns";
++ reg = <0x2000 0x1000>;
++ interrupts = <GIC_SPI 242 0>;
++ };
++ };
++
+ gpu@1c00000 {
+ compatible = "qcom,adreno-306.0", "qcom,adreno";
+ reg = <0x01c00000 0x20000>;
+@@ -738,6 +791,7 @@
+ <&gcc GFX3D_CLK_SRC>;
+ power-domains = <&gcc OXILI_GDSC>;
+ operating-points-v2 = <&gpu_opp_table>;
++ iommus = <&gpu_iommu 1>, <&gpu_iommu 2>;
+ };
+
+ mdss: mdss@1a00000 {
+@@ -781,6 +835,8 @@
+ "core_clk",
+ "vsync_clk";
+
++ iommus = <&apps_iommu 4>;
++
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+@@ -1231,6 +1287,7 @@
+ <&gcc GCC_VENUS0_AHB_CLK>,
+ <&gcc GCC_VENUS0_AXI_CLK>;
+ clock-names = "core", "iface", "bus";
++ iommus = <&apps_iommu 5>;
+ memory-region = <&venus_mem>;
+ status = "okay";
+