Loading arch/arm64/configs/vendor/holi_debug.config +2 −0 Original line number Diff line number Diff line CONFIG_LOCALVERSION="-qgki-debug" CONFIG_IOMMU_TLBSYNC_DEBUG=y CONFIG_ARM_SMMU_TESTBUS_DUMP=y CONFIG_ARM_SMMU_TESTBUS_DEBUGFS=y CONFIG_IO_PGTABLE_PAGE_ACCOUNTING=y CONFIG_CMA_DEBUGFS=y CONFIG_CMA_DEBUG=y Loading arch/arm64/configs/vendor/lahaina_consolidate.config +2 −0 Original line number Diff line number Diff line Loading @@ -2,6 +2,8 @@ CONFIG_LOCALVERSION="-qgki-consolidate" CONFIG_IOMMU_TLBSYNC_DEBUG=y CONFIG_IOMMU_DEBUG=y CONFIG_IOMMU_TESTS=y CONFIG_ARM_SMMU_TESTBUS_DEBUGFS=y CONFIG_ARM_SMMU_TESTBUS_DUMP=y CONFIG_IO_PGTABLE_PAGE_ACCOUNTING=y CONFIG_CMA_DEBUGFS=y CONFIG_CMA_DEBUG=y Loading drivers/iommu/Kconfig +19 −0 Original line number Diff line number Diff line Loading @@ -529,6 +529,15 @@ config ARM_SMMU_DISABLE_BYPASS_BY_DEFAULT 'arm-smmu.disable_bypass' will continue to override this config. config ARM_SMMU_TESTBUS_DEBUGFS bool "Expose testbus control debugfs nodes" depends on ARM_SMMU && IOMMU_DEBUGFS help Support for exposing debugfs nodes to set testbus select values for selecting a testbus to inspect for a particular TCU/TBU on an SMMU. This also exposes debugfs nodes to read testbus output after the output has been selected. config ARM_SMMU_V3 tristate "ARM Ltd. System MMU Version 3 (SMMUv3) Support" depends on ARM64 Loading Loading @@ -563,6 +572,16 @@ config IOMMU_TLBSYNC_DEBUG If unsure, say N here. config ARM_SMMU_TESTBUS_DUMP bool "ARM SMMU testbus dump" depends on ARM_SMMU help Enables testbus dump collection on arm smmu right after TLB sync timeout failure. Note to use this only on debug builds. If unsure, say N here. config QCOM_LAZY_MAPPING tristate "Reference counted iommu-mapping support" depends on ION Loading drivers/iommu/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o amd_iommu_quirks.o obj-$(CONFIG_AMD_IOMMU_DEBUGFS) += amd_iommu_debugfs.o obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o obj-$(CONFIG_ARM_SMMU) += qcom-arm-smmu-mod.o qcom-arm-smmu-mod-objs += arm-smmu.o arm-smmu-impl.o arm-smmu-qcom.o qcom-arm-smmu-mod-objs += arm-smmu.o arm-smmu-impl.o arm-smmu-qcom.o arm-smmu-debug.o obj-$(CONFIG_ARM_SMMU_V3) += arm-smmu-v3.o obj-$(CONFIG_DMAR_TABLE) += dmar.o obj-$(CONFIG_INTEL_IOMMU) += intel-iommu.o intel-pasid.o Loading drivers/iommu/arm-smmu-debug.c 0 → 100644 +225 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */ #include <linux/kernel.h> #include <linux/io.h> #include <linux/device.h> #include "arm-smmu.h" #include "arm-smmu-debug.h" #include <linux/qcom_scm.h> u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base, bool write, u32 val) { if (write) { writel_relaxed(val, tbu_base + DEBUG_TESTBUS_SEL_TBU); /* Make sure tbu select register is written to */ wmb(); } else { return readl_relaxed(tbu_base + DEBUG_TESTBUS_SEL_TBU); } return 0; } u32 arm_smmu_debug_tbu_testbus_output(void __iomem *tbu_base) { return readl_relaxed(tbu_base + DEBUG_TESTBUS_TBU); } u32 arm_smmu_debug_tcu_testbus_select(phys_addr_t phys_addr, void __iomem *tcu_base, enum tcu_testbus testbus, bool write, u32 val) { int offset; u32 testbus_sel; int ret = 0; if (testbus == CLK_TESTBUS) { offset = ARM_SMMU_TESTBUS_SEL_HLOS1_NS; if (write) { writel_relaxed(val, tcu_base + offset); /* Make sure tcu select register is written to */ wmb(); } else { return readl_relaxed(tcu_base + offset); } } else { offset = ARM_SMMU_TESTBUS_SEL; if (write) { ret = qcom_scm_io_writel((phys_addr + offset), val); if (ret) pr_err_ratelimited("SCM write of TESTBUS SEL fails: %d\n", ret); /* Make sure tcu select register is written to */ wmb(); } else { ret = qcom_scm_io_readl(phys_addr + offset, &testbus_sel); if (ret) pr_err_ratelimited("SCM write of TESTBUS SEL fails: %d\n", ret); else return testbus_sel; } } return 0; } u32 arm_smmu_debug_tcu_testbus_output(phys_addr_t phys_addr) { u32 testbus_output; int ret; ret = qcom_scm_io_readl(phys_addr + ARM_SMMU_TESTBUS, &testbus_output); if (!ret) return testbus_output; pr_err_ratelimited("SCM write of TESTBUS output fails: %d\n", ret); return 0; } static void arm_smmu_debug_dump_tbu_qns4_testbus(struct device *dev, void __iomem *tbu_base) { int i; u32 reg; for (i = 0 ; i < TBU_QNS4_BRIDGE_SIZE; ++i) { reg = arm_smmu_debug_tbu_testbus_select(tbu_base, READ, 0); reg = (reg & ~TBU_QNS4_BRIDGE_MASK) | i << 0; arm_smmu_debug_tbu_testbus_select(tbu_base, WRITE, reg); dev_info(dev, "testbus_sel: 0x%lx Index: %d val: 0x%llx\n", arm_smmu_debug_tbu_testbus_select(tbu_base, READ, 0), i, arm_smmu_debug_tbu_testbus_output(tbu_base)); } } static void arm_smmu_debug_program_tbu_testbus(void __iomem *tbu_base, int tbu_testbus) { u32 reg; reg = arm_smmu_debug_tbu_testbus_select(tbu_base, READ, 0); reg = (reg & ~TCU_PTW_QUEUE_MASK) | tbu_testbus; arm_smmu_debug_tbu_testbus_select(tbu_base, WRITE, reg); } void arm_smmu_debug_dump_tbu_testbus(struct device *dev, void __iomem *tbu_base, int tbu_testbus_sel) { if (tbu_testbus_sel & TBU_CLK_GATE_CONTROLLER_TESTBUS_SEL) { dev_info(dev, "Dumping TBU clk gate controller:\n"); arm_smmu_debug_program_tbu_testbus(tbu_base, TBU_CLK_GATE_CONTROLLER_TESTBUS); dev_info(dev, "testbus_sel: 0x%lx val: 0x%llx\n", arm_smmu_debug_tbu_testbus_select(tbu_base, READ, 0), arm_smmu_debug_tbu_testbus_output(tbu_base)); } if (tbu_testbus_sel & TBU_QNS4_A2Q_TESTBUS_SEL) { dev_info(dev, "Dumping TBU qns4 a2q test bus:\n"); arm_smmu_debug_program_tbu_testbus(tbu_base, TBU_QNS4_A2Q_TESTBUS); arm_smmu_debug_dump_tbu_qns4_testbus(dev, tbu_base); } if (tbu_testbus_sel & TBU_QNS4_Q2A_TESTBUS_SEL) { dev_info(dev, "Dumping qns4 q2a test bus:\n"); arm_smmu_debug_program_tbu_testbus(tbu_base, TBU_QNS4_Q2A_TESTBUS); arm_smmu_debug_dump_tbu_qns4_testbus(dev, tbu_base); } if (tbu_testbus_sel & TBU_MULTIMASTER_QCHANNEL_TESTBUS_SEL) { dev_info(dev, "Dumping multi master qchannel:\n"); arm_smmu_debug_program_tbu_testbus(tbu_base, TBU_MULTIMASTER_QCHANNEL_TESTBUS); dev_info(dev, "testbus_sel: 0x%lx val: 0x%llx\n", arm_smmu_debug_tbu_testbus_select(tbu_base, READ, 0), arm_smmu_debug_tbu_testbus_output(tbu_base)); } } static void arm_smmu_debug_program_tcu_testbus(struct device *dev, phys_addr_t phys_addr, void __iomem *tcu_base, unsigned long mask, int start, int end, int shift, bool print) { u32 reg; int i; for (i = start; i < end; i++) { reg = arm_smmu_debug_tcu_testbus_select(phys_addr, tcu_base, PTW_AND_CACHE_TESTBUS, READ, 0); reg &= mask; reg |= i << shift; arm_smmu_debug_tcu_testbus_select(phys_addr, tcu_base, PTW_AND_CACHE_TESTBUS, WRITE, reg); if (print) dev_info(dev, "testbus_sel: 0x%lx Index: %d val: 0x%lx\n", arm_smmu_debug_tcu_testbus_select(phys_addr, tcu_base, PTW_AND_CACHE_TESTBUS, READ, 0), i, arm_smmu_debug_tcu_testbus_output(phys_addr)); } } void arm_smmu_debug_dump_tcu_testbus(struct device *dev, phys_addr_t phys_addr, void __iomem *tcu_base, int tcu_testbus_sel) { int i; if (tcu_testbus_sel & TCU_CACHE_TESTBUS_SEL) { dev_info(dev, "Dumping TCU cache testbus:\n"); arm_smmu_debug_program_tcu_testbus(dev, phys_addr, tcu_base, TCU_CACHE_TESTBUS, 0, 1, 0, false); arm_smmu_debug_program_tcu_testbus(dev, phys_addr, tcu_base, ~TCU_PTW_QUEUE_MASK, 0, TCU_CACHE_LOOKUP_QUEUE_SIZE, 2, true); } if (tcu_testbus_sel & TCU_PTW_TESTBUS_SEL) { dev_info(dev, "Dumping TCU PTW test bus:\n"); arm_smmu_debug_program_tcu_testbus(dev, phys_addr, tcu_base, 1, TCU_PTW_TESTBUS, TCU_PTW_TESTBUS + 1, 0, false); arm_smmu_debug_program_tcu_testbus(dev, phys_addr, tcu_base, ~TCU_PTW_INTERNAL_STATES_MASK, 0, TCU_PTW_INTERNAL_STATES, 2, true); for (i = TCU_PTW_QUEUE_START; i < TCU_PTW_QUEUE_START + TCU_PTW_QUEUE_SIZE; ++i) { arm_smmu_debug_program_tcu_testbus(dev, phys_addr, tcu_base, ~TCU_PTW_QUEUE_MASK, i, i + 1, 2, true); arm_smmu_debug_program_tcu_testbus(dev, phys_addr, tcu_base, ~TCU_PTW_TESTBUS_SEL2_MASK, TCU_PTW_TESTBUS_SEL2, TCU_PTW_TESTBUS_SEL2 + 1, 0, false); dev_info(dev, "testbus_sel: 0x%lx Index: %d val: 0x%lx\n", arm_smmu_debug_tcu_testbus_select(phys_addr, tcu_base, PTW_AND_CACHE_TESTBUS, READ, 0), i, arm_smmu_debug_tcu_testbus_output(phys_addr)); } } /* program ARM_SMMU_TESTBUS_SEL_HLOS1_NS to select TCU clk testbus*/ arm_smmu_debug_tcu_testbus_select(phys_addr, tcu_base, CLK_TESTBUS, WRITE, TCU_CLK_TESTBUS_SEL); dev_info(dev, "Programming Tcu clk gate controller: testbus_sel: 0x%lx\n", arm_smmu_debug_tcu_testbus_select(phys_addr, tcu_base, CLK_TESTBUS, READ, 0)); } Loading
arch/arm64/configs/vendor/holi_debug.config +2 −0 Original line number Diff line number Diff line CONFIG_LOCALVERSION="-qgki-debug" CONFIG_IOMMU_TLBSYNC_DEBUG=y CONFIG_ARM_SMMU_TESTBUS_DUMP=y CONFIG_ARM_SMMU_TESTBUS_DEBUGFS=y CONFIG_IO_PGTABLE_PAGE_ACCOUNTING=y CONFIG_CMA_DEBUGFS=y CONFIG_CMA_DEBUG=y Loading
arch/arm64/configs/vendor/lahaina_consolidate.config +2 −0 Original line number Diff line number Diff line Loading @@ -2,6 +2,8 @@ CONFIG_LOCALVERSION="-qgki-consolidate" CONFIG_IOMMU_TLBSYNC_DEBUG=y CONFIG_IOMMU_DEBUG=y CONFIG_IOMMU_TESTS=y CONFIG_ARM_SMMU_TESTBUS_DEBUGFS=y CONFIG_ARM_SMMU_TESTBUS_DUMP=y CONFIG_IO_PGTABLE_PAGE_ACCOUNTING=y CONFIG_CMA_DEBUGFS=y CONFIG_CMA_DEBUG=y Loading
drivers/iommu/Kconfig +19 −0 Original line number Diff line number Diff line Loading @@ -529,6 +529,15 @@ config ARM_SMMU_DISABLE_BYPASS_BY_DEFAULT 'arm-smmu.disable_bypass' will continue to override this config. config ARM_SMMU_TESTBUS_DEBUGFS bool "Expose testbus control debugfs nodes" depends on ARM_SMMU && IOMMU_DEBUGFS help Support for exposing debugfs nodes to set testbus select values for selecting a testbus to inspect for a particular TCU/TBU on an SMMU. This also exposes debugfs nodes to read testbus output after the output has been selected. config ARM_SMMU_V3 tristate "ARM Ltd. System MMU Version 3 (SMMUv3) Support" depends on ARM64 Loading Loading @@ -563,6 +572,16 @@ config IOMMU_TLBSYNC_DEBUG If unsure, say N here. config ARM_SMMU_TESTBUS_DUMP bool "ARM SMMU testbus dump" depends on ARM_SMMU help Enables testbus dump collection on arm smmu right after TLB sync timeout failure. Note to use this only on debug builds. If unsure, say N here. config QCOM_LAZY_MAPPING tristate "Reference counted iommu-mapping support" depends on ION Loading
drivers/iommu/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o amd_iommu_quirks.o obj-$(CONFIG_AMD_IOMMU_DEBUGFS) += amd_iommu_debugfs.o obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o obj-$(CONFIG_ARM_SMMU) += qcom-arm-smmu-mod.o qcom-arm-smmu-mod-objs += arm-smmu.o arm-smmu-impl.o arm-smmu-qcom.o qcom-arm-smmu-mod-objs += arm-smmu.o arm-smmu-impl.o arm-smmu-qcom.o arm-smmu-debug.o obj-$(CONFIG_ARM_SMMU_V3) += arm-smmu-v3.o obj-$(CONFIG_DMAR_TABLE) += dmar.o obj-$(CONFIG_INTEL_IOMMU) += intel-iommu.o intel-pasid.o Loading
drivers/iommu/arm-smmu-debug.c 0 → 100644 +225 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */ #include <linux/kernel.h> #include <linux/io.h> #include <linux/device.h> #include "arm-smmu.h" #include "arm-smmu-debug.h" #include <linux/qcom_scm.h> u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base, bool write, u32 val) { if (write) { writel_relaxed(val, tbu_base + DEBUG_TESTBUS_SEL_TBU); /* Make sure tbu select register is written to */ wmb(); } else { return readl_relaxed(tbu_base + DEBUG_TESTBUS_SEL_TBU); } return 0; } u32 arm_smmu_debug_tbu_testbus_output(void __iomem *tbu_base) { return readl_relaxed(tbu_base + DEBUG_TESTBUS_TBU); } u32 arm_smmu_debug_tcu_testbus_select(phys_addr_t phys_addr, void __iomem *tcu_base, enum tcu_testbus testbus, bool write, u32 val) { int offset; u32 testbus_sel; int ret = 0; if (testbus == CLK_TESTBUS) { offset = ARM_SMMU_TESTBUS_SEL_HLOS1_NS; if (write) { writel_relaxed(val, tcu_base + offset); /* Make sure tcu select register is written to */ wmb(); } else { return readl_relaxed(tcu_base + offset); } } else { offset = ARM_SMMU_TESTBUS_SEL; if (write) { ret = qcom_scm_io_writel((phys_addr + offset), val); if (ret) pr_err_ratelimited("SCM write of TESTBUS SEL fails: %d\n", ret); /* Make sure tcu select register is written to */ wmb(); } else { ret = qcom_scm_io_readl(phys_addr + offset, &testbus_sel); if (ret) pr_err_ratelimited("SCM write of TESTBUS SEL fails: %d\n", ret); else return testbus_sel; } } return 0; } u32 arm_smmu_debug_tcu_testbus_output(phys_addr_t phys_addr) { u32 testbus_output; int ret; ret = qcom_scm_io_readl(phys_addr + ARM_SMMU_TESTBUS, &testbus_output); if (!ret) return testbus_output; pr_err_ratelimited("SCM write of TESTBUS output fails: %d\n", ret); return 0; } static void arm_smmu_debug_dump_tbu_qns4_testbus(struct device *dev, void __iomem *tbu_base) { int i; u32 reg; for (i = 0 ; i < TBU_QNS4_BRIDGE_SIZE; ++i) { reg = arm_smmu_debug_tbu_testbus_select(tbu_base, READ, 0); reg = (reg & ~TBU_QNS4_BRIDGE_MASK) | i << 0; arm_smmu_debug_tbu_testbus_select(tbu_base, WRITE, reg); dev_info(dev, "testbus_sel: 0x%lx Index: %d val: 0x%llx\n", arm_smmu_debug_tbu_testbus_select(tbu_base, READ, 0), i, arm_smmu_debug_tbu_testbus_output(tbu_base)); } } static void arm_smmu_debug_program_tbu_testbus(void __iomem *tbu_base, int tbu_testbus) { u32 reg; reg = arm_smmu_debug_tbu_testbus_select(tbu_base, READ, 0); reg = (reg & ~TCU_PTW_QUEUE_MASK) | tbu_testbus; arm_smmu_debug_tbu_testbus_select(tbu_base, WRITE, reg); } void arm_smmu_debug_dump_tbu_testbus(struct device *dev, void __iomem *tbu_base, int tbu_testbus_sel) { if (tbu_testbus_sel & TBU_CLK_GATE_CONTROLLER_TESTBUS_SEL) { dev_info(dev, "Dumping TBU clk gate controller:\n"); arm_smmu_debug_program_tbu_testbus(tbu_base, TBU_CLK_GATE_CONTROLLER_TESTBUS); dev_info(dev, "testbus_sel: 0x%lx val: 0x%llx\n", arm_smmu_debug_tbu_testbus_select(tbu_base, READ, 0), arm_smmu_debug_tbu_testbus_output(tbu_base)); } if (tbu_testbus_sel & TBU_QNS4_A2Q_TESTBUS_SEL) { dev_info(dev, "Dumping TBU qns4 a2q test bus:\n"); arm_smmu_debug_program_tbu_testbus(tbu_base, TBU_QNS4_A2Q_TESTBUS); arm_smmu_debug_dump_tbu_qns4_testbus(dev, tbu_base); } if (tbu_testbus_sel & TBU_QNS4_Q2A_TESTBUS_SEL) { dev_info(dev, "Dumping qns4 q2a test bus:\n"); arm_smmu_debug_program_tbu_testbus(tbu_base, TBU_QNS4_Q2A_TESTBUS); arm_smmu_debug_dump_tbu_qns4_testbus(dev, tbu_base); } if (tbu_testbus_sel & TBU_MULTIMASTER_QCHANNEL_TESTBUS_SEL) { dev_info(dev, "Dumping multi master qchannel:\n"); arm_smmu_debug_program_tbu_testbus(tbu_base, TBU_MULTIMASTER_QCHANNEL_TESTBUS); dev_info(dev, "testbus_sel: 0x%lx val: 0x%llx\n", arm_smmu_debug_tbu_testbus_select(tbu_base, READ, 0), arm_smmu_debug_tbu_testbus_output(tbu_base)); } } static void arm_smmu_debug_program_tcu_testbus(struct device *dev, phys_addr_t phys_addr, void __iomem *tcu_base, unsigned long mask, int start, int end, int shift, bool print) { u32 reg; int i; for (i = start; i < end; i++) { reg = arm_smmu_debug_tcu_testbus_select(phys_addr, tcu_base, PTW_AND_CACHE_TESTBUS, READ, 0); reg &= mask; reg |= i << shift; arm_smmu_debug_tcu_testbus_select(phys_addr, tcu_base, PTW_AND_CACHE_TESTBUS, WRITE, reg); if (print) dev_info(dev, "testbus_sel: 0x%lx Index: %d val: 0x%lx\n", arm_smmu_debug_tcu_testbus_select(phys_addr, tcu_base, PTW_AND_CACHE_TESTBUS, READ, 0), i, arm_smmu_debug_tcu_testbus_output(phys_addr)); } } void arm_smmu_debug_dump_tcu_testbus(struct device *dev, phys_addr_t phys_addr, void __iomem *tcu_base, int tcu_testbus_sel) { int i; if (tcu_testbus_sel & TCU_CACHE_TESTBUS_SEL) { dev_info(dev, "Dumping TCU cache testbus:\n"); arm_smmu_debug_program_tcu_testbus(dev, phys_addr, tcu_base, TCU_CACHE_TESTBUS, 0, 1, 0, false); arm_smmu_debug_program_tcu_testbus(dev, phys_addr, tcu_base, ~TCU_PTW_QUEUE_MASK, 0, TCU_CACHE_LOOKUP_QUEUE_SIZE, 2, true); } if (tcu_testbus_sel & TCU_PTW_TESTBUS_SEL) { dev_info(dev, "Dumping TCU PTW test bus:\n"); arm_smmu_debug_program_tcu_testbus(dev, phys_addr, tcu_base, 1, TCU_PTW_TESTBUS, TCU_PTW_TESTBUS + 1, 0, false); arm_smmu_debug_program_tcu_testbus(dev, phys_addr, tcu_base, ~TCU_PTW_INTERNAL_STATES_MASK, 0, TCU_PTW_INTERNAL_STATES, 2, true); for (i = TCU_PTW_QUEUE_START; i < TCU_PTW_QUEUE_START + TCU_PTW_QUEUE_SIZE; ++i) { arm_smmu_debug_program_tcu_testbus(dev, phys_addr, tcu_base, ~TCU_PTW_QUEUE_MASK, i, i + 1, 2, true); arm_smmu_debug_program_tcu_testbus(dev, phys_addr, tcu_base, ~TCU_PTW_TESTBUS_SEL2_MASK, TCU_PTW_TESTBUS_SEL2, TCU_PTW_TESTBUS_SEL2 + 1, 0, false); dev_info(dev, "testbus_sel: 0x%lx Index: %d val: 0x%lx\n", arm_smmu_debug_tcu_testbus_select(phys_addr, tcu_base, PTW_AND_CACHE_TESTBUS, READ, 0), i, arm_smmu_debug_tcu_testbus_output(phys_addr)); } } /* program ARM_SMMU_TESTBUS_SEL_HLOS1_NS to select TCU clk testbus*/ arm_smmu_debug_tcu_testbus_select(phys_addr, tcu_base, CLK_TESTBUS, WRITE, TCU_CLK_TESTBUS_SEL); dev_info(dev, "Programming Tcu clk gate controller: testbus_sel: 0x%lx\n", arm_smmu_debug_tcu_testbus_select(phys_addr, tcu_base, CLK_TESTBUS, READ, 0)); }