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

This commit is contained in:
qctecmdr 2020-09-17 15:21:26 -07:00 • committed by Gerrit - the friendly Code Review server
commit 7c7105f9f7
8 changed files with 695 additions and 1 deletions

View file

@ -1,5 +1,7 @@
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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,225 @@
// 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));
}

View file

@ -0,0 +1,99 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2020, 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(phys_addr_t phys_addr,
void __iomem *tcu_base, enum tcu_testbus testbus,
bool write, u32 val);
u32 arm_smmu_debug_tcu_testbus_output(phys_addr_t phys_addr);
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, phys_addr_t phys_addr,
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(phys_addr_t phys_addr,
void __iomem *tcu_base, enum tcu_testbus testbus,
bool write, u32 val)
{
}
static inline u32 arm_smmu_debug_tcu_testbus_output(phys_addr_t phys_addr)
{
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,
phys_addr_t phys_addr, void __iomem *tcu_base,
int tcu_testbus_sel)
{
}
#endif

View file

@ -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);
phys_addr_t phys_addr = smmu->phys_addr;
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(phys_addr,
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(phys_addr,
tcu_base, PTW_AND_CACHE_TESTBUS,
READ, 0));
} else {
scnprintf(buf, buf_len, "0x%0x\n",
arm_smmu_debug_tcu_testbus_output(phys_addr));
}
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;
phys_addr_t phys_addr = smmu->phys_addr;
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(phys_addr,
tcu_base, CLK_TESTBUS, WRITE, val);
else if (sel == 2)
arm_smmu_debug_tcu_testbus_select(phys_addr,
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->phys_addr,
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);

View file

@ -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;