From b7ab4072a1d4c50d80056f552a62121499414680 Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Tue, 18 Jul 2017 19:37:53 +0100 Subject: [PATCH] Add fix for Tegra GPU display with IOMMU, Add QCom IOMMU for Dragonboard display --- arm-tegra-fix-gpu-iommu.patch | 39 + baseconfig/arm/arm64/CONFIG_QCOM_IOMMU | 1 + baseconfig/arm/armv7/armv7/CONFIG_QCOM_IOMMU | 1 + kernel-aarch64-debug.config | 1 + kernel-aarch64.config | 1 + kernel-armv7hl-debug.config | 1 + kernel-armv7hl.config | 1 + kernel.spec | 25 +- qcom-display-iommu.patch | 1960 ++++++++++++++++++ 9 files changed, 2023 insertions(+), 7 deletions(-) create mode 100644 arm-tegra-fix-gpu-iommu.patch create mode 100644 baseconfig/arm/arm64/CONFIG_QCOM_IOMMU create mode 100644 baseconfig/arm/armv7/armv7/CONFIG_QCOM_IOMMU create mode 100644 qcom-display-iommu.patch 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 +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 , + Stephen Warren , + Mikko Perttunen , + Paul Kocialkowski , + Jonathan Hunter +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 +--- + 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 +- Add fix for Tegra GPU display with IOMMU +- Add QCom IOMMU for Dragonboard display + * Tue Jul 18 2017 Laura Abbott - 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 +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 , + Rob Herring , Will Deacon , + Sricharan , + Mark Rutland , Robin Murphy , + Rob Clark , devicetree@vger.kernel.org +Date: Thu, 13 Jul 2017 08:07:44 -0400 + +Cc: devicetree@vger.kernel.org +Signed-off-by: Rob Clark +Reviewed-by: Rob Herring +--- + .../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 = ; ++ }; ++ ++ // venus_ns: ++ iommu-ctx@5000 { ++ compatible = "qcom,msm-iommu-v1-sec"; ++ reg = <0x5000 0x1000>; ++ interrupts = ; ++ }; ++ }; ++ ++ 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 = ; ++ }; ++ ++ // gfx3d_priv: ++ iommu-ctx@2000 { ++ compatible = "qcom,msm-iommu-v1-ns"; ++ reg = <0x2000 0x1000>; ++ interrupts = ; ++ }; ++ }; ++ ++ ... ++ ++ 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 +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 , + Rob Herring , Will Deacon , + Sricharan , + Mark Rutland , Robin Murphy , + Rob Clark +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 +--- + 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 ++ */ ++ ++#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 + + #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 +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 , + Rob Herring , Will Deacon , + Sricharan , + Mark Rutland , Robin Murphy , + Rob Clark +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 +Tested-by: Riku Voipio +--- +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 . ++ * ++ * Copyright (C) 2013 ARM Limited ++ * Copyright (C) 2017 Red Hat ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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, ®)) ++ 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 +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 , + Rob Herring , Will Deacon , + Sricharan , + Mark Rutland , Robin Murphy , + Stanimir Varbanov , + Rob Clark +Date: Thu, 13 Jul 2017 08:07:47 -0400 + +From: Stanimir Varbanov + +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 +Signed-off-by: Rob Clark +--- + 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 +X-Patchwork-Id: 9781057 +Message-Id: <20170612124317.29313-1-robdclark@gmail.com> +To: linux-arm-msm@vger.kernel.org +Cc: Stephen Boyd , Andy Gross , + Stanimir Varbanov , + Rob Clark +Date: Mon, 12 Jun 2017 08:43:15 -0400 + +Signed-off-by: Rob Clark +Reviewed-by: Stephen Boyd +--- + 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 = , +@@ -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 = ; ++ 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 +X-Patchwork-Id: 9781059 +Message-Id: <20170612124317.29313-2-robdclark@gmail.com> +To: linux-arm-msm@vger.kernel.org +Cc: Stephen Boyd , Andy Gross , + Stanimir Varbanov , + Rob Clark +Date: Mon, 12 Jun 2017 08:43:16 -0400 + +From: Stanimir Varbanov + +Signed-off-by: Stanimir Varbanov +Signed-off-by: Rob Clark +Reviewed-by: Stephen Boyd +--- + 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 = ; ++ 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 +X-Patchwork-Id: 9781061 +Message-Id: <20170612124317.29313-3-robdclark@gmail.com> +To: linux-arm-msm@vger.kernel.org +Cc: Stephen Boyd , Andy Gross , + Stanimir Varbanov , + Rob Clark +Date: Mon, 12 Jun 2017 08:43:17 -0400 + +Signed-off-by: Rob Clark +Reviewed-by: Stephen Boyd +--- + 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 = ; ++ }; ++ ++ // venus_ns: ++ iommu-ctx@5000 { ++ compatible = "qcom,msm-iommu-v1-sec"; ++ reg = <0x5000 0x1000>; ++ interrupts = ; ++ }; ++ }; ++ ++ 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 = ; ++ }; ++ ++ // gfx3d_priv: ++ iommu-ctx@2000 { ++ compatible = "qcom,msm-iommu-v1-ns"; ++ reg = <0x2000 0x1000>; ++ interrupts = ; ++ }; ++ }; ++ + 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"; +