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

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

Merge "drivers: iommu: dump testbus output on tlb_sync timeouts"

parents 3543251e 9c2fbeb6
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -146,6 +146,13 @@ conditions.
		  we can choose to have a single ASID associated with all domains
		  for a context bank.

- qcom,testbus-version:
		  Testbus implementation is different in some hardware for eg some doesn't
		  have a separate register for programming tbu testbuses so, they share the
		  same register to program both tcu and tbu testbuses. on such hardware this
		  option can be used to specify the testbus version to support testbus interface.
		  Type is <u32>.

- clocks        : List of clocks to be used during SMMU register access. See
                  Documentation/devicetree/bindings/clock/clock-bindings.txt
                  for information about the format. For each clock specified
+10 −0
Original line number Diff line number Diff line
@@ -381,6 +381,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
	bool "Reference counted iommu-mapping support"
	depends on ION
+1 −1
Original line number Diff line number Diff line
@@ -15,7 +15,7 @@ obj-$(CONFIG_IOMMU_DEBUG) += iommu-debug.o
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
obj-$(CONFIG_ARM_SMMU) += arm-smmu.o arm-smmu-errata.o
obj-$(CONFIG_ARM_SMMU) += arm-smmu.o arm-smmu-errata.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
+233 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only 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.
 *
 */
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/device.h>
#include "arm-smmu-regs.h"
#include "arm-smmu-debug.h"

u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base,
		void __iomem *tcu_base, u32 testbus_version,
		bool write, u32 val)
{
	void __iomem *base;
	int offset;

	if (testbus_version == 1) {
		base = tcu_base;
		offset = ARM_SMMU_TESTBUS_SEL_HLOS1_NS;
	} else {
		base = tbu_base;
		offset = DEBUG_TESTBUS_SEL_TBU;
	}

	if (write) {
		writel_relaxed(val, base + offset);
		/* Make sure tbu select register is written to */
		wmb();
	} else {
		return readl_relaxed(base + offset);
	}
	return 0;
}

u32 arm_smmu_debug_tbu_testbus_output(void __iomem *tbu_base,
			u32 testbus_version)
{
	int offset = (testbus_version == 1) ?
			CLIENT_DEBUG_SR_HALT_ACK : DEBUG_TESTBUS_TBU;

	return readl_relaxed(tbu_base + offset);
}

u32 arm_smmu_debug_tcu_testbus_select(void __iomem *base,
		void __iomem *tcu_base, enum tcu_testbus testbus,
		bool write, u32 val)
{
	int offset;

	if (testbus == CLK_TESTBUS) {
		base = tcu_base;
		offset = ARM_SMMU_TESTBUS_SEL_HLOS1_NS;
	} else {
		offset = ARM_SMMU_TESTBUS_SEL;
	}

	if (write) {
		writel_relaxed(val, base + offset);
		/* Make sure tcu select register is written to */
		wmb();
	} else {
		return readl_relaxed(base + offset);
	}

	return 0;
}

u32 arm_smmu_debug_tcu_testbus_output(void __iomem *base)
{
	return readl_relaxed(base + ARM_SMMU_TESTBUS);
}

static void arm_smmu_debug_dump_tbu_qns4_testbus(struct device *dev,
			void __iomem *tbu_base, void __iomem *tcu_base,
			u32  testbus_version)
{
	int i;
	u32 reg;

	for (i = 0 ; i < TBU_QNS4_BRIDGE_SIZE; ++i) {
		reg = arm_smmu_debug_tbu_testbus_select(tbu_base, tcu_base,
				testbus_version, READ, 0);
		reg = (reg & ~GENMASK(4, 0)) | i << 0;
		arm_smmu_debug_tbu_testbus_select(tbu_base, tcu_base,
				testbus_version, WRITE, reg);
		dev_info(dev, "testbus_sel: 0x%lx Index: %d val: 0x%llx\n",
			arm_smmu_debug_tbu_testbus_select(tbu_base, tcu_base,
				testbus_version, READ, 0), i,
			arm_smmu_debug_tbu_testbus_output(tbu_base,
							testbus_version));
	}
}

static void arm_smmu_debug_program_tbu_testbus(void __iomem *tbu_base,
				void __iomem *tcu_base, u32 testbus_version,
				int tbu_testbus)
{
	u32 reg;

	reg = arm_smmu_debug_tbu_testbus_select(tbu_base, tcu_base,
						testbus_version, READ, 0);
	if (testbus_version == 1)
		reg = (reg & ~GENMASK(9, 0));
	else
		reg = (reg & ~GENMASK(7, 0));

	reg |= tbu_testbus;
	arm_smmu_debug_tbu_testbus_select(tbu_base, tcu_base,
						testbus_version, WRITE, reg);
}

void arm_smmu_debug_dump_tbu_testbus(struct device *dev, void __iomem *tbu_base,
		void __iomem *tcu_base, int tbu_testbus_sel,
		u32 testbus_version)
{
	if (tbu_testbus_sel & TBU_CLK_GATE_CONTROLLER_TESTBUS_SEL) {
		dev_info(dev, "Dumping TBU clk gate controller:");
		arm_smmu_debug_program_tbu_testbus(tbu_base, tcu_base,
				testbus_version,
				TBU_CLK_GATE_CONTROLLER_TESTBUS);
		dev_info(dev, "testbus_sel: 0x%lx val: 0x%llx\n",
			arm_smmu_debug_tbu_testbus_select(tbu_base, tcu_base,
						testbus_version, READ, 0),
			arm_smmu_debug_tbu_testbus_output(tbu_base,
							testbus_version));
	}

	if (tbu_testbus_sel & TBU_QNS4_A2Q_TESTBUS_SEL) {
		dev_info(dev, "Dumping TBU qns4 a2q test bus");
		arm_smmu_debug_program_tbu_testbus(tbu_base, tcu_base,
				testbus_version, TBU_QNS4_A2Q_TESTBUS);
		arm_smmu_debug_dump_tbu_qns4_testbus(dev, tbu_base,
				tcu_base, testbus_version);
	}

	if (tbu_testbus_sel & TBU_QNS4_Q2A_TESTBUS_SEL) {
		dev_info(dev, "Dumping qns4 q2a test bus");
		arm_smmu_debug_program_tbu_testbus(tbu_base, tcu_base,
				testbus_version, TBU_QNS4_Q2A_TESTBUS);
		arm_smmu_debug_dump_tbu_qns4_testbus(dev, tbu_base,
				tcu_base, testbus_version);
	}

	if (tbu_testbus_sel & TBU_MULTIMASTER_QCHANNEL_TESTBUS_SEL) {
		dev_info(dev, "Dumping multi master qchannel:");
		arm_smmu_debug_program_tbu_testbus(tbu_base, tcu_base,
				testbus_version,
				TBU_MULTIMASTER_QCHANNEL_TESTBUS);
		dev_info(dev, "testbus_sel: 0x%lx val: 0x%llx\n",
			arm_smmu_debug_tbu_testbus_select(tbu_base,
				tcu_base, testbus_version, READ, 0),
			arm_smmu_debug_tbu_testbus_output(tbu_base,
							testbus_version));
	}
}

static void arm_smmu_debug_program_tcu_testbus(struct device *dev,
		void __iomem *base, 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(base, tcu_base,
				PTW_AND_CACHE_TESTBUS, READ, 0);
		reg &= mask;
		reg |= i << shift;
		arm_smmu_debug_tcu_testbus_select(base, 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(base,
				 tcu_base, PTW_AND_CACHE_TESTBUS, READ, 0),
				 i, arm_smmu_debug_tcu_testbus_output(base));
	}
}

void arm_smmu_debug_dump_tcu_testbus(struct device *dev, void __iomem *base,
			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, base, tcu_base,
				TCU_CACHE_TESTBUS, 0, 1, 0, false);
		arm_smmu_debug_program_tcu_testbus(dev, base, tcu_base,
				~GENMASK(7, 0), 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, base, tcu_base, 1,
				TCU_PTW_TESTBUS, TCU_PTW_TESTBUS + 1, 0, false);

		arm_smmu_debug_program_tcu_testbus(dev, base, tcu_base,
				~GENMASK(7, 2), 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, base, tcu_base,
					~GENMASK(7, 0), i, i + 1, 2, true);
			arm_smmu_debug_program_tcu_testbus(dev, base, tcu_base,
					~GENMASK(1, 0), 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(base,
				 tcu_base, PTW_AND_CACHE_TESTBUS, READ, 0),
				 i, arm_smmu_debug_tcu_testbus_output(base));
		}
	}

	/* program ARM_SMMU_TESTBUS_SEL_HLOS1_NS to select TCU clk testbus*/
	arm_smmu_debug_tcu_testbus_select(base, 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(base, tcu_base,
						CLK_TESTBUS, READ, 0));
}
+103 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only 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.
 *
 */

#define ARM_SMMU_TESTBUS_SEL			0x25E4
#define ARM_SMMU_TESTBUS			0x25E8
#define ARM_SMMU_TESTBUS_SEL_HLOS1_NS		0x8
#define DEBUG_TESTBUS_SEL_TBU			0x50
#define DEBUG_TESTBUS_TBU			0x58
#define CLIENT_DEBUG_SR_HALT_ACK		0x24

#define TCU_PTW_TESTBUS				(0x1 << 8)
#define TCU_CACHE_TESTBUS			~TCU_PTW_TESTBUS
#define TCU_PTW_TESTBUS_SEL			(0x1 << 1)
#define TCU_PTW_INTERNAL_STATES			3
#define TCU_PTW_TESTBUS_SEL2			3
#define TCU_PTW_QUEUE_START			32
#define TCU_PTW_QUEUE_SIZE			32
#define TCU_CACHE_TESTBUS_SEL			0x1
#define TCU_CACHE_LOOKUP_QUEUE_SIZE		32
#define TCU_CLK_TESTBUS_SEL			0x200

#define TBU_CLK_GATE_CONTROLLER_TESTBUS_SEL	0x1
#define TBU_QNS4_A2Q_TESTBUS_SEL		(0x1 << 1)
#define TBU_QNS4_Q2A_TESTBUS_SEL		(0x1 << 2)
#define TBU_MULTIMASTER_QCHANNEL_TESTBUS_SEL	(0x1 << 3)
#define TBU_CLK_GATE_CONTROLLER_TESTBUS		(0x1 << 6)
#define TBU_QNS4_A2Q_TESTBUS			(0x2 << 6)
#define TBU_QNS4_Q2A_TESTBUS			(0x5 << 5)
#define TBU_MULTIMASTER_QCHANNEL_TESTBUS	(0x3 << 6)
#define TBU_QNS4_BRIDGE_SIZE			32

enum tcu_testbus {
	PTW_AND_CACHE_TESTBUS,
	CLK_TESTBUS,
};

enum testbus_sel {
	SEL_TCU,
	SEL_TBU,
};

enum testbus_ops {
	TESTBUS_SELECT,
	TESTBUS_OUTPUT,
};

#ifdef CONFIG_ARM_SMMU

u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base,
		void __iomem *tcu_base, u32 testbus_version,
		bool write, u32 reg);
u32 arm_smmu_debug_tbu_testbus_output(void __iomem *tbu_base,
					u32 testbus_version);
u32 arm_smmu_debug_tcu_testbus_select(void __iomem *base,
		void __iomem *tcu_base, enum tcu_testbus testbus,
		bool write, u32 val);
u32 arm_smmu_debug_tcu_testbus_output(void __iomem *base);
void arm_smmu_debug_dump_tbu_testbus(struct device *dev, void __iomem *tbu_base,
		void __iomem *tcu_base, int tbu_testbus_sel,
		u32 testbus_version);
void arm_smmu_debug_dump_tcu_testbus(struct device *dev, void __iomem *base,
			void __iomem *tcu_base, int tcu_testbus_sel);

#else
static inline u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base,
		void __iomem *tcu_base,	u32 testbus_version, bool write,
		u32 val)
{
}
static inline u32 arm_smmu_debug_tbu_testbus_output(void __iomem *tbu_base,
						u32 testbus_version)
{
}
u32 arm_smmu_debug_tcu_testbus_select(void __iomem *base,
		void __iomem *tcu_base, enum tcu_testbus testbus,
		bool write, u32 val)
{
}
static inline u32 arm_smmu_debug_tcu_testbus_output(void __iomem *base)
{
}
static inline void arm_smmu_debug_dump_tbu_testbus(struct device *dev,
			void __iomem *tbu_base, void __iomem *tcu_base,
			int tbu_testbus_sel, u32 testbus_version)
{
}
static inline void arm_smmu_debug_dump_tcu_testbus(struct device *dev,
			void __iomem *base, void __iomem *tcu_base,
			int tcu_testbus_sel)
{
}
#endif
Loading