iommu: arm-smmu: add capturebus support for smmuv500

add a debugfs interface under /sys/kernel/debug/iommu/capturebus
to support capture bus for smmuv500.

Change-Id: Ibf2a2daf87941387c1b3f990f05d86a1cd4dbfff
Signed-off-by: Vijayanand Jitta <vjitta@codeaurora.org>
This commit is contained in:
Vijayanand Jitta 2019-06-26 10:08:14 +05:30
parent 4fea97c643
commit c4a7a0eca9
3 changed files with 393 additions and 2 deletions

View File

@ -223,3 +223,60 @@ void arm_smmu_debug_dump_tcu_testbus(struct device *dev, void __iomem *base,
arm_smmu_debug_tcu_testbus_select(base, tcu_base,
CLK_TESTBUS, READ, 0));
}
void arm_smmu_debug_set_tnx_tcr_cntl(void __iomem *tbu_base, u64 val)
{
writel_relaxed(val, tbu_base + ARM_SMMU_TNX_TCR_CNTL);
}
unsigned long arm_smmu_debug_get_tnx_tcr_cntl(void __iomem *tbu_base)
{
return readl_relaxed(tbu_base + ARM_SMMU_TNX_TCR_CNTL);
}
void arm_smmu_debug_set_mask_and_match(void __iomem *tbu_base, u64 sel,
u64 mask, u64 match)
{
writeq_relaxed(mask, tbu_base + ARM_SMMU_CAPTURE1_MASK(sel));
writeq_relaxed(match, tbu_base + ARM_SMMU_CAPTURE1_MATCH(sel));
}
void arm_smmu_debug_get_mask_and_match(void __iomem *tbu_base, u64 *mask,
u64 *match)
{
int i;
for (i = 0; i < NO_OF_MASK_AND_MATCH; ++i) {
mask[i] = readq_relaxed(tbu_base +
ARM_SMMU_CAPTURE1_MASK(i+1));
match[i] = readq_relaxed(tbu_base +
ARM_SMMU_CAPTURE1_MATCH(i+1));
}
}
void arm_smmu_debug_get_capture_snapshot(void __iomem *tbu_base,
u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT])
{
int valid, i, j;
valid = readl_relaxed(tbu_base + APPS_SMMU_TNX_TCR_CNTL_2);
for (i = 0; i < NO_OF_CAPTURE_POINTS ; ++i) {
if (valid & (1 << i))
for (j = 0; j < REGS_PER_CAPTURE_POINT; ++j)
snapshot[i][j] = readq_relaxed(tbu_base +
ARM_SMMU_CAPTURE_SNAPSHOT(i, j));
else
for (j = 0; j < REGS_PER_CAPTURE_POINT; ++j)
snapshot[i][j] = 0xdededede;
}
}
void arm_smmu_debug_clear_intr_and_validbits(void __iomem *tbu_base)
{
int val = 0;
val |= INTR_CLR;
val |= RESET_VALID;
writel_relaxed(val, tbu_base + ARM_SMMU_TNX_TCR_CNTL);
}

View File

@ -46,6 +46,18 @@ enum testbus_ops {
TESTBUS_OUTPUT,
};
#define ARM_SMMU_TNX_TCR_CNTL 0x130
#define ARM_SMMU_CAPTURE1_MASK(i) (0x100 + (0x8)*(i-1))
#define ARM_SMMU_CAPTURE1_MATCH(i) (0x118 + (0x8)*(i-1))
#define ARM_SMMU_CAPTURE_SNAPSHOT(i, j) ((0x138 + (0x10)*i) + j*0x8)
#define APPS_SMMU_TNX_TCR_CNTL_2 0x178
#define NO_OF_MASK_AND_MATCH 0x3
#define NO_OF_CAPTURE_POINTS 0x4
#define REGS_PER_CAPTURE_POINT 0x2
#define INTR_CLR (1 << 0)
#define RESET_VALID (1 << 7)
#ifdef CONFIG_ARM_SMMU
u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base,
@ -62,7 +74,16 @@ void arm_smmu_debug_dump_tbu_testbus(struct device *dev, void __iomem *tbu_base,
u32 testbus_version);
void arm_smmu_debug_dump_tcu_testbus(struct device *dev, void __iomem *base,
void __iomem *tcu_base, int tcu_testbus_sel);
void arm_smmu_debug_set_tnx_tcr_cntl(void __iomem *tbu_base, u64 val);
unsigned long arm_smmu_debug_get_tnx_tcr_cntl(void __iomem *tbu_base);
unsigned long arm_smmu_debug_get_tnx_tcr_cntl_2(void __iomem *tbu_base);
void arm_smmu_debug_set_mask_and_match(void __iomem *tbu_base, u64 sel,
u64 mask, u64 match);
void arm_smmu_debug_get_mask_and_match(void __iomem *tbu_base,
u64 *mask, u64 *match);
void arm_smmu_debug_get_capture_snapshot(void __iomem *tbu_base,
u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT]);
void arm_smmu_debug_clear_intr_and_validbits(void __iomem *tbu_base);
#else
static inline u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base,
void __iomem *tcu_base, u32 testbus_version, bool write,
@ -91,4 +112,28 @@ static inline void arm_smmu_debug_dump_tcu_testbus(struct device *dev,
int tcu_testbus_sel)
{
}
void arm_smmu_debug_set_tnx_tcr_cntl(void __iomem *tbu_base, u64 val)
{
}
unsigned long arm_smmu_debug_get_tnx_tcr_cntl(void __iomem *tbu_base)
{
}
unsigned long arm_smmu_debug_get_tnx_tcr_cntl_2(void __iomem *tbu_base)
{
}
void arm_smmu_debug_set_mask_and_match(void __iomem *tbu_base, u64 sel,
u64 mask, u64 match)
{
}
void arm_smmu_debug_get_mask_and_match(void __iomem *tbu_base,
u64 *mask, u64 *match)
{
}
void arm_smmu_debug_get_capture_snapshot(void __iomem *tbu_base,
u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT])
{
}
void arm_smmu_debug_clear_intr_and_validbits(void __iomem *tbu_base)
{
}
#endif

View File

@ -355,6 +355,7 @@ static int tbu_testbus_sel = TBU_TESTBUS_SEL_ALL;
static int tcu_testbus_sel = TCU_TESTBUS_SEL_ALL;
static struct dentry *debugfs_testbus_dir;
static DEFINE_SPINLOCK(testbus_lock);
static struct dentry *debugfs_capturebus_dir;
module_param_named(tcu_testbus_sel, tcu_testbus_sel, int, 0644);
module_param_named(tbu_testbus_sel, tbu_testbus_sel, int, 0644);
@ -431,6 +432,7 @@ struct qsmmuv500_tbu_device {
/* Protects halt count */
spinlock_t halt_lock;
u32 halt_count;
unsigned int *irqs;
};
static atomic_t cavium_smmu_context_count = ATOMIC_INIT(0);
@ -5924,6 +5926,263 @@ err:
return 0;
}
static ssize_t arm_smmu_debug_capturebus_snapshot_read(struct file *file,
char __user *ubuf, size_t count, loff_t *offset)
{
struct qsmmuv500_tbu_device *tbu = file->private_data;
struct arm_smmu_device *smmu = tbu->smmu;
void __iomem *tbu_base = tbu->base;
u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT];
char buf[400];
ssize_t retval;
size_t buflen;
int buf_len = sizeof(buf);
int i, j;
if (*offset)
return 0;
memset(buf, 0, buf_len);
arm_smmu_power_on(smmu->pwr);
arm_smmu_power_on(tbu->pwr);
arm_smmu_debug_get_capture_snapshot(tbu_base, snapshot);
arm_smmu_power_off(tbu->pwr);
arm_smmu_power_off(smmu->pwr);
for (i = 0; i < NO_OF_CAPTURE_POINTS ; ++i) {
for (j = 0; j < REGS_PER_CAPTURE_POINT; ++j) {
snprintf(buf + strlen(buf), buf_len - strlen(buf),
"Capture_%d_Snapshot_%d : 0x%0llx\n",
i+1, j+1, snapshot[i][j]);
}
}
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 const struct file_operations arm_smmu_debug_capturebus_snapshot_fops = {
.open = simple_open,
.read = arm_smmu_debug_capturebus_snapshot_read,
};
static ssize_t arm_smmu_debug_capturebus_config_write(struct file *file,
const char __user *ubuf, size_t count, loff_t *offset)
{
struct qsmmuv500_tbu_device *tbu = file->private_data;
struct arm_smmu_device *smmu = tbu->smmu;
void __iomem *tbu_base = tbu->base;
char *comma1, *comma2;
char buf[100];
u64 sel, mask, match, val;
if (count >= 100) {
pr_err_ratelimited("Input too large\n");
goto invalid_format;
}
memset(buf, 0, 100);
if (copy_from_user(buf, ubuf, count)) {
pr_err_ratelimited("Couldn't copy from user\n");
return -EFAULT;
}
comma1 = strnchr(buf, count, ',');
if (!comma1)
goto invalid_format;
*comma1 = '\0';
if (kstrtou64(buf, 0, &sel))
goto invalid_format;
if (sel > 4) {
goto invalid_format;
} else if (sel == 4) {
if (kstrtou64(comma1 + 1, 0, &val))
goto invalid_format;
goto program_capturebus;
}
comma2 = strnchr(comma1 + 1, count, ',');
if (!comma2)
goto invalid_format;
/* split up the words */
*comma2 = '\0';
if (kstrtou64(comma1 + 1, 0, &mask))
goto invalid_format;
if (kstrtou64(comma2 + 1, 0, &match))
goto invalid_format;
program_capturebus:
arm_smmu_power_on(smmu->pwr);
arm_smmu_power_on(tbu->pwr);
if (sel == 4)
arm_smmu_debug_set_tnx_tcr_cntl(tbu_base, val);
else
arm_smmu_debug_set_mask_and_match(tbu_base, sel, mask, match);
arm_smmu_power_off(tbu->pwr);
arm_smmu_power_off(smmu->pwr);
return count;
invalid_format:
pr_err_ratelimited("Invalid format. Expected: <1/2/3,Mask,Match> (or) <4,TNX_TCR_CNTL>>\n");
return -EINVAL;
}
static ssize_t arm_smmu_debug_capturebus_config_read(struct file *file,
char __user *ubuf, size_t count, loff_t *offset)
{
struct qsmmuv500_tbu_device *tbu = file->private_data;
struct arm_smmu_device *smmu = tbu->smmu;
void __iomem *tbu_base = tbu->base;
unsigned long val;
u64 mask[NO_OF_MASK_AND_MATCH], match[NO_OF_MASK_AND_MATCH];
char buf[400];
ssize_t retval;
size_t buflen;
int buf_len = sizeof(buf);
int i;
if (*offset)
return 0;
memset(buf, 0, buf_len);
arm_smmu_power_on(smmu->pwr);
arm_smmu_power_on(tbu->pwr);
arm_smmu_debug_get_mask_and_match(tbu_base,
mask, match);
val = arm_smmu_debug_get_tnx_tcr_cntl(tbu_base);
arm_smmu_power_off(tbu->pwr);
arm_smmu_power_off(smmu->pwr);
for (i = 0; i < NO_OF_MASK_AND_MATCH; ++i) {
snprintf(buf + strlen(buf), buf_len - strlen(buf),
"Mask_%d : 0x%0llx\t", i+1, mask[i]);
snprintf(buf + strlen(buf), buf_len - strlen(buf),
"Match_%d : 0x%0llx\n", i+1, match[i]);
}
snprintf(buf + strlen(buf), buf_len - strlen(buf), "0x%0x\n", val);
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 const struct file_operations arm_smmu_debug_capturebus_config_fops = {
.open = simple_open,
.write = arm_smmu_debug_capturebus_config_write,
.read = arm_smmu_debug_capturebus_config_read,
};
static int qsmmuv500_capturebus_init(struct qsmmuv500_tbu_device *tbu)
{
struct dentry *capturebus_dir;
if (!iommu_debugfs_top)
return 0;
if (!debugfs_capturebus_dir) {
debugfs_capturebus_dir = debugfs_create_dir(
"capturebus", iommu_debugfs_top);
if (!debugfs_capturebus_dir) {
pr_err_ratelimited("Couldn't create iommu/capturebus debugfs directory\n");
return -ENODEV;
}
}
capturebus_dir = debugfs_create_dir(dev_name(tbu->dev),
debugfs_capturebus_dir);
if (!capturebus_dir) {
pr_err_ratelimited("Couldn't create iommu/capturebus/%s debugfs directory\n",
dev_name(tbu->dev));
goto err;
}
if (!debugfs_create_file("config", 0400, capturebus_dir, tbu,
&arm_smmu_debug_capturebus_config_fops)) {
pr_err_ratelimited("Couldn't create iommu/capturebus/%s/config debugfs file\n",
dev_name(tbu->dev));
goto err_rmdir;
}
if (!debugfs_create_file("snapshot", 0400, capturebus_dir, tbu,
&arm_smmu_debug_capturebus_snapshot_fops)) {
pr_err_ratelimited("Couldn't create iommu/capturebus/%s/snapshot debugfs file\n",
dev_name(tbu->dev));
goto err_rmdir;
}
return 0;
err_rmdir:
debugfs_remove_recursive(capturebus_dir);
err:
return 0;
}
static irqreturn_t arm_smmu_debug_capture_bus_match(int irq, void *dev)
{
struct qsmmuv500_tbu_device *tbu = dev;
struct arm_smmu_device *smmu = tbu->smmu;
void __iomem *tbu_base = tbu->base;
u64 mask[NO_OF_MASK_AND_MATCH], match[NO_OF_MASK_AND_MATCH];
u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT];
int i, j, val;
if (arm_smmu_power_on(smmu->pwr) || arm_smmu_power_on(tbu->pwr))
return IRQ_NONE;
val = arm_smmu_debug_get_tnx_tcr_cntl(tbu_base);
arm_smmu_debug_get_mask_and_match(tbu_base, mask, match);
arm_smmu_debug_get_capture_snapshot(tbu_base, snapshot);
arm_smmu_debug_clear_intr_and_validbits(tbu_base);
arm_smmu_power_off(tbu->pwr);
arm_smmu_power_off(smmu->pwr);
dev_info(tbu->dev, "TNX_TCR_CNTL : 0x%0llx\n", val);
for (i = 0; i < NO_OF_MASK_AND_MATCH; ++i) {
dev_info(tbu->dev,
"Mask_%d : 0x%0llx\n", i+1, mask[i]);
dev_info(tbu->dev,
"Match_%d : 0x%0llx\n", i+1, match[i]);
}
for (i = 0; i < NO_OF_CAPTURE_POINTS ; ++i) {
for (j = 0; j < REGS_PER_CAPTURE_POINT; ++j) {
dev_info(tbu->dev,
"Capture_%d_Snapshot_%d : 0x%0llx\n",
i+1, j+1, snapshot[i][j]);
}
}
return IRQ_HANDLED;
}
static int qsmmuv500_arch_init(struct arm_smmu_device *smmu)
{
struct resource *res;
@ -6008,7 +6267,7 @@ static int qsmmuv500_tbu_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct qsmmuv500_tbu_device *tbu;
const __be32 *cell;
int len;
int len, i, err, num_irqs = 0;
tbu = devm_kzalloc(dev, sizeof(*tbu), GFP_KERNEL);
if (!tbu)
@ -6035,12 +6294,42 @@ static int qsmmuv500_tbu_probe(struct platform_device *pdev)
tbu->sid_start = of_read_number(cell, 1);
tbu->num_sids = of_read_number(cell + 1, 1);
while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, num_irqs)))
num_irqs++;
tbu->irqs = devm_kzalloc(dev, sizeof(*tbu->irqs) * num_irqs,
GFP_KERNEL);
if (!tbu->irqs)
return -ENOMEM;
for (i = 0; i < num_irqs; ++i) {
int irq = platform_get_irq(pdev, i);
if (irq < 0) {
dev_err(dev, "failed to get irq index %d\n", i);
return -ENODEV;
}
tbu->irqs[i] = irq;
err = devm_request_threaded_irq(tbu->dev, tbu->irqs[i],
NULL, arm_smmu_debug_capture_bus_match,
IRQF_ONESHOT | IRQF_SHARED,
"capture bus", tbu);
if (err) {
dev_err(dev, "failed to request capture bus irq%d (%u)\n",
i, tbu->irqs[i]);
return err;
}
}
tbu->pwr = arm_smmu_init_power_resources(pdev);
if (IS_ERR(tbu->pwr))
return PTR_ERR(tbu->pwr);
dev_set_drvdata(dev, tbu);
qsmmuv500_tbu_testbus_init(tbu);
qsmmuv500_capturebus_init(tbu);
return 0;
}