Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7c7105f9 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "iommu: arm-smmu: update access to SMMU TCU testbus registers with scm call"

parents b8e9c349 47b2cb7a
Loading
Loading
Loading
Loading
+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
+2 −0
Original line number Diff line number Diff line
@@ -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
+19 −0
Original line number Diff line number Diff line
@@ -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
@@ -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
+1 −1
Original line number Diff line number Diff line
@@ -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
+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