mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-12 07:37:31 -04:00
iommu: arm-smmu: add testbus support for smmuv500
add a debugfs interface to support testbus also add an interface for dumping testbus information, module parameters tcu_testbus_sel and tbu_testbus_sel can be used for selecting different testbuses to dump on tcu and tbu respectively. Change-Id: I1bf420db053458b2ce1f43dc544c0776a3f4d176 Signed-off-by: Vijayanand Jitta <vjitta@codeaurora.org> [isaacm@codeaurora.org: introduced CONFIG_ARM_SMMU_TESTBUS_DEBUGFS] Signed-off-by: Isaac J. Manjarres <isaacm@codeaurora.org> [guptap@codeaurora.org: resolved merge conflict] Signed-off-by: Prakash Gupta <guptap@codeaurora.org>
This commit is contained in:
parent
65d212e2e0
commit
9d67a36c0c
6 changed files with 659 additions and 1 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
193
drivers/iommu/arm-smmu-debug.c
Normal file
193
drivers/iommu/arm-smmu-debug.c
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2019, 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"
|
||||
|
||||
|
||||
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(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)
|
||||
{
|
||||
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,
|
||||
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,
|
||||
~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, base, tcu_base, 1,
|
||||
TCU_PTW_TESTBUS, TCU_PTW_TESTBUS + 1, 0, false);
|
||||
|
||||
arm_smmu_debug_program_tcu_testbus(dev, base, 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, base, tcu_base,
|
||||
~TCU_PTW_QUEUE_MASK, i, i + 1, 2, true);
|
||||
arm_smmu_debug_program_tcu_testbus(dev, base, 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(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));
|
||||
}
|
||||
99
drivers/iommu/arm-smmu-debug.h
Normal file
99
drivers/iommu/arm-smmu-debug.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#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 TCU_PTW_TESTBUS BIT(8)
|
||||
#define TCU_CACHE_TESTBUS ~TCU_PTW_TESTBUS
|
||||
#define TCU_PTW_TESTBUS_SEL BIT(1)
|
||||
#define TCU_PTW_INTERNAL_STATES 3
|
||||
#define TCU_PTW_INTERNAL_STATES_MASK GENMASK(7, 2)
|
||||
#define TCU_PTW_TESTBUS_SEL2 3
|
||||
#define TCU_PTW_TESTBUS_SEL2_MASK GENMASK(1, 0)
|
||||
#define TCU_PTW_QUEUE_START 32
|
||||
#define TCU_PTW_QUEUE_SIZE 32
|
||||
#define TCU_PTW_QUEUE_MASK GENMASK(7, 0)
|
||||
#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 BIT(1)
|
||||
#define TBU_QNS4_Q2A_TESTBUS_SEL BIT(2)
|
||||
#define TBU_MULTIMASTER_QCHANNEL_TESTBUS_SEL BIT(3)
|
||||
#define TBU_CLK_GATE_CONTROLLER_TESTBUS BIT(6)
|
||||
#define TBU_QNS4_A2Q_TESTBUS BIT(7)
|
||||
#define TBU_QNS4_Q2A_TESTBUS (BIT(5) | BIT(7))
|
||||
#define TBU_MULTIMASTER_QCHANNEL_TESTBUS GENMASK(7, 6)
|
||||
#define TBU_QNS4_BRIDGE_SIZE 32
|
||||
#define TBU_QNS4_BRIDGE_MASK GENMASK(4, 0)
|
||||
|
||||
extern int tbu_testbus_sel;
|
||||
extern int tcu_testbus_sel;
|
||||
|
||||
enum tcu_testbus {
|
||||
PTW_AND_CACHE_TESTBUS,
|
||||
CLK_TESTBUS,
|
||||
};
|
||||
|
||||
enum testbus_sel {
|
||||
SEL_TCU,
|
||||
SEL_TBU,
|
||||
};
|
||||
|
||||
enum testbus_ops {
|
||||
TESTBUS_SELECT,
|
||||
TESTBUS_OUTPUT,
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_ARM_SMMU)
|
||||
|
||||
u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base,
|
||||
bool write, u32 val);
|
||||
u32 arm_smmu_debug_tbu_testbus_output(void __iomem *tbu_base);
|
||||
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,
|
||||
int tbu_testbus_sel);
|
||||
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,
|
||||
bool write, u32 val)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline u32 arm_smmu_debug_tbu_testbus_output(void __iomem *tbu_base)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
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)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void arm_smmu_debug_dump_tbu_testbus(struct device *dev,
|
||||
void __iomem *tbu_base, int tbu_testbus_sel)
|
||||
{
|
||||
}
|
||||
static inline void arm_smmu_debug_dump_tcu_testbus(struct device *dev,
|
||||
void __iomem *base, void __iomem *tcu_base,
|
||||
int tcu_testbus_sel)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -14,6 +14,9 @@
|
|||
#include <linux/workqueue.h>
|
||||
|
||||
#include "arm-smmu.h"
|
||||
#include "arm-smmu-debug.h"
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define ARM_SMMU_IMPL_DEF1 6
|
||||
|
||||
|
|
@ -421,6 +424,331 @@ static struct qsmmuv500_tbu_device *qsmmuv500_find_tbu(
|
|||
* snapshot capture feature.
|
||||
*/
|
||||
static DEFINE_MUTEX(capture_reg_lock);
|
||||
static DEFINE_SPINLOCK(testbus_lock);
|
||||
|
||||
#ifdef CONFIG_ARM_SMMU_TESTBUS_DEBUGFS
|
||||
static struct dentry *debugfs_testbus_dir;
|
||||
|
||||
static ssize_t arm_smmu_debug_testbus_read(struct file *file,
|
||||
char __user *ubuf, size_t count, loff_t *offset,
|
||||
enum testbus_sel tbu, enum testbus_ops ops)
|
||||
|
||||
{
|
||||
char buf[100];
|
||||
ssize_t retval;
|
||||
size_t buflen;
|
||||
int buf_len = sizeof(buf);
|
||||
|
||||
if (*offset)
|
||||
return 0;
|
||||
|
||||
memset(buf, 0, buf_len);
|
||||
|
||||
if (tbu == SEL_TBU) {
|
||||
struct qsmmuv500_tbu_device *tbu = file->private_data;
|
||||
void __iomem *tbu_base = tbu->base;
|
||||
long val;
|
||||
|
||||
arm_smmu_power_on(tbu->pwr);
|
||||
if (ops == TESTBUS_SELECT)
|
||||
val = arm_smmu_debug_tbu_testbus_select(tbu_base,
|
||||
READ, 0);
|
||||
else
|
||||
val = arm_smmu_debug_tbu_testbus_output(tbu_base);
|
||||
arm_smmu_power_off(tbu->smmu, tbu->pwr);
|
||||
|
||||
scnprintf(buf, buf_len, "0x%0x\n", val);
|
||||
} else {
|
||||
|
||||
struct arm_smmu_device *smmu = file->private_data;
|
||||
struct qsmmuv500_archdata *data = to_qsmmuv500_archdata(smmu);
|
||||
void __iomem *base = arm_smmu_page(smmu, ARM_SMMU_GR0);
|
||||
void __iomem *tcu_base = data->tcu_base;
|
||||
|
||||
arm_smmu_power_on(smmu->pwr);
|
||||
|
||||
if (ops == TESTBUS_SELECT) {
|
||||
scnprintf(buf, buf_len, "TCU clk testbus sel: 0x%0x\n",
|
||||
arm_smmu_debug_tcu_testbus_select(base,
|
||||
tcu_base, CLK_TESTBUS, READ, 0));
|
||||
scnprintf(buf + strlen(buf), buf_len - strlen(buf),
|
||||
"TCU testbus sel : 0x%0x\n",
|
||||
arm_smmu_debug_tcu_testbus_select(base,
|
||||
tcu_base, PTW_AND_CACHE_TESTBUS,
|
||||
READ, 0));
|
||||
} else {
|
||||
scnprintf(buf, buf_len, "0x%0x\n",
|
||||
arm_smmu_debug_tcu_testbus_output(base));
|
||||
}
|
||||
|
||||
arm_smmu_power_off(smmu, smmu->pwr);
|
||||
}
|
||||
buflen = min(count, strlen(buf));
|
||||
if (copy_to_user(ubuf, buf, buflen)) {
|
||||
pr_err_ratelimited("Couldn't copy_to_user\n");
|
||||
retval = -EFAULT;
|
||||
} else {
|
||||
*offset = 1;
|
||||
retval = buflen;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t arm_smmu_debug_tcu_testbus_sel_write(struct file *file,
|
||||
const char __user *ubuf, size_t count, loff_t *offset)
|
||||
{
|
||||
struct arm_smmu_device *smmu = file->private_data;
|
||||
struct qsmmuv500_archdata *data = to_qsmmuv500_archdata(smmu);
|
||||
void __iomem *tcu_base = data->tcu_base;
|
||||
void __iomem *base = arm_smmu_page(smmu, ARM_SMMU_GR0);
|
||||
char *comma;
|
||||
char buf[100];
|
||||
u64 sel, val;
|
||||
|
||||
if (count >= 100) {
|
||||
pr_err_ratelimited("Value too large\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(buf, 0, 100);
|
||||
|
||||
if (copy_from_user(buf, ubuf, count)) {
|
||||
pr_err_ratelimited("Couldn't copy from user\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
comma = strnchr(buf, count, ',');
|
||||
if (!comma)
|
||||
goto invalid_format;
|
||||
|
||||
/* split up the words */
|
||||
*comma = '\0';
|
||||
|
||||
if (kstrtou64(buf, 0, &sel))
|
||||
goto invalid_format;
|
||||
|
||||
if (sel != 1 && sel != 2)
|
||||
goto invalid_format;
|
||||
|
||||
if (kstrtou64(comma + 1, 0, &val))
|
||||
goto invalid_format;
|
||||
|
||||
arm_smmu_power_on(smmu->pwr);
|
||||
|
||||
if (sel == 1)
|
||||
arm_smmu_debug_tcu_testbus_select(base,
|
||||
tcu_base, CLK_TESTBUS, WRITE, val);
|
||||
else if (sel == 2)
|
||||
arm_smmu_debug_tcu_testbus_select(base,
|
||||
tcu_base, PTW_AND_CACHE_TESTBUS, WRITE, val);
|
||||
|
||||
arm_smmu_power_off(smmu, smmu->pwr);
|
||||
|
||||
return count;
|
||||
|
||||
invalid_format:
|
||||
pr_err_ratelimited("Invalid format. Expected: <1, testbus select> for tcu CLK testbus (or) <2, testbus select> for tcu PTW/CACHE testbuses\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t arm_smmu_debug_tcu_testbus_sel_read(struct file *file,
|
||||
char __user *ubuf, size_t count, loff_t *offset)
|
||||
{
|
||||
return arm_smmu_debug_testbus_read(file, ubuf,
|
||||
count, offset, SEL_TCU, TESTBUS_SELECT);
|
||||
}
|
||||
|
||||
static const struct file_operations arm_smmu_debug_tcu_testbus_sel_fops = {
|
||||
.open = simple_open,
|
||||
.write = arm_smmu_debug_tcu_testbus_sel_write,
|
||||
.read = arm_smmu_debug_tcu_testbus_sel_read,
|
||||
};
|
||||
|
||||
static ssize_t arm_smmu_debug_tcu_testbus_read(struct file *file,
|
||||
char __user *ubuf, size_t count, loff_t *offset)
|
||||
{
|
||||
return arm_smmu_debug_testbus_read(file, ubuf,
|
||||
count, offset, SEL_TCU, TESTBUS_OUTPUT);
|
||||
}
|
||||
|
||||
static const struct file_operations arm_smmu_debug_tcu_testbus_fops = {
|
||||
.open = simple_open,
|
||||
.read = arm_smmu_debug_tcu_testbus_read,
|
||||
};
|
||||
|
||||
static int qsmmuv500_tcu_testbus_init(struct arm_smmu_device *smmu)
|
||||
{
|
||||
struct dentry *testbus_dir;
|
||||
|
||||
if (!iommu_debugfs_dir)
|
||||
return 0;
|
||||
|
||||
if (!debugfs_testbus_dir) {
|
||||
debugfs_testbus_dir = debugfs_create_dir("testbus",
|
||||
iommu_debugfs_dir);
|
||||
if (IS_ERR(debugfs_testbus_dir)) {
|
||||
pr_err_ratelimited("Couldn't create iommu/testbus debugfs directory\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
testbus_dir = debugfs_create_dir(dev_name(smmu->dev),
|
||||
debugfs_testbus_dir);
|
||||
|
||||
if (IS_ERR(testbus_dir)) {
|
||||
pr_err_ratelimited("Couldn't create iommu/testbus/%s debugfs directory\n",
|
||||
dev_name(smmu->dev));
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (IS_ERR(debugfs_create_file("tcu_testbus_sel", 0400,
|
||||
testbus_dir, smmu,
|
||||
&arm_smmu_debug_tcu_testbus_sel_fops))) {
|
||||
pr_err_ratelimited("Couldn't create iommu/testbus/%s/tcu_testbus_sel debugfs file\n",
|
||||
dev_name(smmu->dev));
|
||||
goto err_rmdir;
|
||||
}
|
||||
|
||||
if (IS_ERR(debugfs_create_file("tcu_testbus_output", 0400,
|
||||
testbus_dir, smmu,
|
||||
&arm_smmu_debug_tcu_testbus_fops))) {
|
||||
pr_err_ratelimited("Couldn't create iommu/testbus/%s/tcu_testbus_output debugfs file\n",
|
||||
dev_name(smmu->dev));
|
||||
goto err_rmdir;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_rmdir:
|
||||
debugfs_remove_recursive(testbus_dir);
|
||||
err:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t arm_smmu_debug_tbu_testbus_sel_write(struct file *file,
|
||||
const char __user *ubuf, size_t count, loff_t *offset)
|
||||
{
|
||||
struct qsmmuv500_tbu_device *tbu = file->private_data;
|
||||
void __iomem *tbu_base = tbu->base;
|
||||
u64 val;
|
||||
|
||||
if (kstrtoull_from_user(ubuf, count, 0, &val)) {
|
||||
pr_err_ratelimited("Invalid format for tbu testbus select\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
arm_smmu_power_on(tbu->pwr);
|
||||
arm_smmu_debug_tbu_testbus_select(tbu_base, WRITE, val);
|
||||
arm_smmu_power_off(tbu->smmu, tbu->pwr);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t arm_smmu_debug_tbu_testbus_sel_read(struct file *file,
|
||||
char __user *ubuf, size_t count, loff_t *offset)
|
||||
{
|
||||
return arm_smmu_debug_testbus_read(file, ubuf,
|
||||
count, offset, SEL_TBU, TESTBUS_SELECT);
|
||||
}
|
||||
|
||||
static const struct file_operations arm_smmu_debug_tbu_testbus_sel_fops = {
|
||||
.open = simple_open,
|
||||
.write = arm_smmu_debug_tbu_testbus_sel_write,
|
||||
.read = arm_smmu_debug_tbu_testbus_sel_read,
|
||||
};
|
||||
|
||||
static ssize_t arm_smmu_debug_tbu_testbus_read(struct file *file,
|
||||
char __user *ubuf, size_t count, loff_t *offset)
|
||||
{
|
||||
return arm_smmu_debug_testbus_read(file, ubuf,
|
||||
count, offset, SEL_TBU, TESTBUS_OUTPUT);
|
||||
}
|
||||
|
||||
static const struct file_operations arm_smmu_debug_tbu_testbus_fops = {
|
||||
.open = simple_open,
|
||||
.read = arm_smmu_debug_tbu_testbus_read,
|
||||
};
|
||||
|
||||
static int qsmmuv500_tbu_testbus_init(struct qsmmuv500_tbu_device *tbu)
|
||||
{
|
||||
struct dentry *testbus_dir;
|
||||
|
||||
if (!iommu_debugfs_dir)
|
||||
return 0;
|
||||
|
||||
if (!debugfs_testbus_dir) {
|
||||
debugfs_testbus_dir = debugfs_create_dir("testbus",
|
||||
iommu_debugfs_dir);
|
||||
if (IS_ERR(debugfs_testbus_dir)) {
|
||||
pr_err_ratelimited("Couldn't create iommu/testbus debugfs directory\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
testbus_dir = debugfs_create_dir(dev_name(tbu->dev),
|
||||
debugfs_testbus_dir);
|
||||
|
||||
if (IS_ERR(testbus_dir)) {
|
||||
pr_err_ratelimited("Couldn't create iommu/testbus/%s debugfs directory\n",
|
||||
dev_name(tbu->dev));
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (IS_ERR(debugfs_create_file("tbu_testbus_sel", 0400,
|
||||
testbus_dir, tbu,
|
||||
&arm_smmu_debug_tbu_testbus_sel_fops))) {
|
||||
pr_err_ratelimited("Couldn't create iommu/testbus/%s/tbu_testbus_sel debugfs file\n",
|
||||
dev_name(tbu->dev));
|
||||
goto err_rmdir;
|
||||
}
|
||||
|
||||
if (IS_ERR(debugfs_create_file("tbu_testbus_output", 0400,
|
||||
testbus_dir, tbu,
|
||||
&arm_smmu_debug_tbu_testbus_fops))) {
|
||||
pr_err_ratelimited("Couldn't create iommu/testbus/%s/tbu_testbus_output debugfs file\n",
|
||||
dev_name(tbu->dev));
|
||||
goto err_rmdir;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_rmdir:
|
||||
debugfs_remove_recursive(testbus_dir);
|
||||
err:
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int qsmmuv500_tcu_testbus_init(struct arm_smmu_device *smmu)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qsmmuv500_tbu_testbus_init(struct qsmmuv500_tbu_device *tbu)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void arm_smmu_testbus_dump(struct arm_smmu_device *smmu, u16 sid)
|
||||
{
|
||||
if (smmu->model == QCOM_SMMUV500 &&
|
||||
IS_ENABLED(CONFIG_ARM_SMMU_TESTBUS_DUMP)) {
|
||||
struct qsmmuv500_archdata *data = to_qsmmuv500_archdata(smmu);
|
||||
struct qsmmuv500_tbu_device *tbu;
|
||||
|
||||
tbu = qsmmuv500_find_tbu(smmu, sid);
|
||||
spin_lock(&testbus_lock);
|
||||
if (tbu)
|
||||
arm_smmu_debug_dump_tbu_testbus(tbu->dev,
|
||||
tbu->base,
|
||||
tbu_testbus_sel);
|
||||
else
|
||||
arm_smmu_debug_dump_tcu_testbus(smmu->dev,
|
||||
smmu->base,
|
||||
data->tcu_base,
|
||||
tcu_testbus_sel);
|
||||
spin_unlock(&testbus_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void qsmmuv500_log_outstanding_transactions(struct work_struct *work)
|
||||
{
|
||||
|
|
@ -583,8 +911,14 @@ static void qsmmuv500_tlb_sync_timeout(struct arm_smmu_device *smmu)
|
|||
tbu_sync_pending ?
|
||||
"check pending transactions on TBU"
|
||||
: "check for TBU power status");
|
||||
arm_smmu_testbus_dump(smmu,
|
||||
(u16)(tbu_id << TBUID_SHIFT));
|
||||
}
|
||||
}
|
||||
|
||||
/*dump TCU testbus*/
|
||||
arm_smmu_testbus_dump(smmu, U16_MAX);
|
||||
|
||||
}
|
||||
|
||||
if (tcu_sync_pending) {
|
||||
|
|
@ -1064,6 +1398,8 @@ static int qsmmuv500_tbu_register(struct device *dev, void *cookie)
|
|||
INIT_LIST_HEAD(&tbu->list);
|
||||
tbu->smmu = &data->smmu;
|
||||
list_add(&tbu->list, &data->tbus);
|
||||
|
||||
qsmmuv500_tbu_testbus_init(tbu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1156,6 +1492,8 @@ struct arm_smmu_device *qsmmuv500_impl_init(struct arm_smmu_device *smmu)
|
|||
data->smmu = *smmu;
|
||||
data->smmu.impl = &qsmmuv500_impl;
|
||||
|
||||
qsmmuv500_tcu_testbus_init(&data->smmu);
|
||||
|
||||
ret = qsmmuv500_read_actlr_tbl(data);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
|
|
|||
|
|
@ -110,6 +110,15 @@ struct arm_smmu_cb {
|
|||
#define ARM_SMMU_CB_VMID(smmu, cfg) ((u16)(smmu)->cavium_id_base + \
|
||||
(cfg)->cbndx + 1)
|
||||
|
||||
#define TCU_TESTBUS_SEL_ALL 0x3
|
||||
#define TBU_TESTBUS_SEL_ALL 0xf
|
||||
|
||||
int tbu_testbus_sel = TBU_TESTBUS_SEL_ALL;
|
||||
int tcu_testbus_sel = TCU_TESTBUS_SEL_ALL;
|
||||
|
||||
module_param_named(tcu_testbus_sel, tcu_testbus_sel, int, 0644);
|
||||
module_param_named(tbu_testbus_sel, tbu_testbus_sel, int, 0644);
|
||||
|
||||
struct arm_smmu_pte_info {
|
||||
void *virt_addr;
|
||||
size_t size;
|
||||
|
|
|
|||
Loading…
Reference in a new issue