Loading drivers/iommu/arm-smmu-debug.c +57 −0 Original line number Original line Diff line number Diff line Loading @@ -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, arm_smmu_debug_tcu_testbus_select(base, tcu_base, CLK_TESTBUS, READ, 0)); 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); } drivers/iommu/arm-smmu-debug.h +46 −1 Original line number Original line Diff line number Diff line Loading @@ -46,6 +46,18 @@ enum testbus_ops { TESTBUS_OUTPUT, 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 #ifdef CONFIG_ARM_SMMU u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base, u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base, Loading @@ -62,7 +74,16 @@ void arm_smmu_debug_dump_tbu_testbus(struct device *dev, void __iomem *tbu_base, u32 testbus_version); u32 testbus_version); void arm_smmu_debug_dump_tcu_testbus(struct device *dev, void __iomem *base, void arm_smmu_debug_dump_tcu_testbus(struct device *dev, void __iomem *base, void __iomem *tcu_base, int tcu_testbus_sel); 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 #else static inline u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base, static inline u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base, void __iomem *tcu_base, u32 testbus_version, bool write, void __iomem *tcu_base, u32 testbus_version, bool write, Loading Loading @@ -91,4 +112,28 @@ static inline void arm_smmu_debug_dump_tcu_testbus(struct device *dev, int tcu_testbus_sel) 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 #endif drivers/iommu/arm-smmu.c +290 −1 Original line number Original line Diff line number Diff line Loading @@ -355,6 +355,7 @@ static int tbu_testbus_sel = TBU_TESTBUS_SEL_ALL; static int tcu_testbus_sel = TCU_TESTBUS_SEL_ALL; static int tcu_testbus_sel = TCU_TESTBUS_SEL_ALL; static struct dentry *debugfs_testbus_dir; static struct dentry *debugfs_testbus_dir; static DEFINE_SPINLOCK(testbus_lock); 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(tcu_testbus_sel, tcu_testbus_sel, int, 0644); module_param_named(tbu_testbus_sel, tbu_testbus_sel, int, 0644); module_param_named(tbu_testbus_sel, tbu_testbus_sel, int, 0644); Loading Loading @@ -431,6 +432,7 @@ struct qsmmuv500_tbu_device { /* Protects halt count */ /* Protects halt count */ spinlock_t halt_lock; spinlock_t halt_lock; u32 halt_count; u32 halt_count; unsigned int *irqs; }; }; static atomic_t cavium_smmu_context_count = ATOMIC_INIT(0); static atomic_t cavium_smmu_context_count = ATOMIC_INIT(0); Loading Loading @@ -5924,6 +5926,263 @@ static int qsmmuv500_tbu_testbus_init(struct qsmmuv500_tbu_device *tbu) return 0; 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) static int qsmmuv500_arch_init(struct arm_smmu_device *smmu) { { struct resource *res; struct resource *res; Loading Loading @@ -6008,7 +6267,7 @@ static int qsmmuv500_tbu_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device *dev = &pdev->dev; struct qsmmuv500_tbu_device *tbu; struct qsmmuv500_tbu_device *tbu; const __be32 *cell; const __be32 *cell; int len; int len, i, err, num_irqs = 0; tbu = devm_kzalloc(dev, sizeof(*tbu), GFP_KERNEL); tbu = devm_kzalloc(dev, sizeof(*tbu), GFP_KERNEL); if (!tbu) if (!tbu) Loading @@ -6035,12 +6294,42 @@ static int qsmmuv500_tbu_probe(struct platform_device *pdev) tbu->sid_start = of_read_number(cell, 1); tbu->sid_start = of_read_number(cell, 1); tbu->num_sids = of_read_number(cell + 1, 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); tbu->pwr = arm_smmu_init_power_resources(pdev); if (IS_ERR(tbu->pwr)) if (IS_ERR(tbu->pwr)) return PTR_ERR(tbu->pwr); return PTR_ERR(tbu->pwr); dev_set_drvdata(dev, tbu); dev_set_drvdata(dev, tbu); qsmmuv500_tbu_testbus_init(tbu); qsmmuv500_tbu_testbus_init(tbu); qsmmuv500_capturebus_init(tbu); return 0; return 0; } } Loading Loading
drivers/iommu/arm-smmu-debug.c +57 −0 Original line number Original line Diff line number Diff line Loading @@ -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, arm_smmu_debug_tcu_testbus_select(base, tcu_base, CLK_TESTBUS, READ, 0)); 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); }
drivers/iommu/arm-smmu-debug.h +46 −1 Original line number Original line Diff line number Diff line Loading @@ -46,6 +46,18 @@ enum testbus_ops { TESTBUS_OUTPUT, 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 #ifdef CONFIG_ARM_SMMU u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base, u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base, Loading @@ -62,7 +74,16 @@ void arm_smmu_debug_dump_tbu_testbus(struct device *dev, void __iomem *tbu_base, u32 testbus_version); u32 testbus_version); void arm_smmu_debug_dump_tcu_testbus(struct device *dev, void __iomem *base, void arm_smmu_debug_dump_tcu_testbus(struct device *dev, void __iomem *base, void __iomem *tcu_base, int tcu_testbus_sel); 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 #else static inline u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base, static inline u32 arm_smmu_debug_tbu_testbus_select(void __iomem *tbu_base, void __iomem *tcu_base, u32 testbus_version, bool write, void __iomem *tcu_base, u32 testbus_version, bool write, Loading Loading @@ -91,4 +112,28 @@ static inline void arm_smmu_debug_dump_tcu_testbus(struct device *dev, int tcu_testbus_sel) 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 #endif
drivers/iommu/arm-smmu.c +290 −1 Original line number Original line Diff line number Diff line Loading @@ -355,6 +355,7 @@ static int tbu_testbus_sel = TBU_TESTBUS_SEL_ALL; static int tcu_testbus_sel = TCU_TESTBUS_SEL_ALL; static int tcu_testbus_sel = TCU_TESTBUS_SEL_ALL; static struct dentry *debugfs_testbus_dir; static struct dentry *debugfs_testbus_dir; static DEFINE_SPINLOCK(testbus_lock); 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(tcu_testbus_sel, tcu_testbus_sel, int, 0644); module_param_named(tbu_testbus_sel, tbu_testbus_sel, int, 0644); module_param_named(tbu_testbus_sel, tbu_testbus_sel, int, 0644); Loading Loading @@ -431,6 +432,7 @@ struct qsmmuv500_tbu_device { /* Protects halt count */ /* Protects halt count */ spinlock_t halt_lock; spinlock_t halt_lock; u32 halt_count; u32 halt_count; unsigned int *irqs; }; }; static atomic_t cavium_smmu_context_count = ATOMIC_INIT(0); static atomic_t cavium_smmu_context_count = ATOMIC_INIT(0); Loading Loading @@ -5924,6 +5926,263 @@ static int qsmmuv500_tbu_testbus_init(struct qsmmuv500_tbu_device *tbu) return 0; 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) static int qsmmuv500_arch_init(struct arm_smmu_device *smmu) { { struct resource *res; struct resource *res; Loading Loading @@ -6008,7 +6267,7 @@ static int qsmmuv500_tbu_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device *dev = &pdev->dev; struct qsmmuv500_tbu_device *tbu; struct qsmmuv500_tbu_device *tbu; const __be32 *cell; const __be32 *cell; int len; int len, i, err, num_irqs = 0; tbu = devm_kzalloc(dev, sizeof(*tbu), GFP_KERNEL); tbu = devm_kzalloc(dev, sizeof(*tbu), GFP_KERNEL); if (!tbu) if (!tbu) Loading @@ -6035,12 +6294,42 @@ static int qsmmuv500_tbu_probe(struct platform_device *pdev) tbu->sid_start = of_read_number(cell, 1); tbu->sid_start = of_read_number(cell, 1); tbu->num_sids = of_read_number(cell + 1, 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); tbu->pwr = arm_smmu_init_power_resources(pdev); if (IS_ERR(tbu->pwr)) if (IS_ERR(tbu->pwr)) return PTR_ERR(tbu->pwr); return PTR_ERR(tbu->pwr); dev_set_drvdata(dev, tbu); dev_set_drvdata(dev, tbu); qsmmuv500_tbu_testbus_init(tbu); qsmmuv500_tbu_testbus_init(tbu); qsmmuv500_capturebus_init(tbu); return 0; return 0; } } Loading