Loading drivers/iommu/arm-smmu-debug.c +63 −0 Original line number Original line Diff line number Diff line Loading @@ -223,3 +223,66 @@ void arm_smmu_debug_dump_tcu_testbus(struct device *dev, phys_addr_t phys_addr, arm_smmu_debug_tcu_testbus_select(phys_addr, tcu_base, arm_smmu_debug_tcu_testbus_select(phys_addr, tcu_base, CLK_TESTBUS, READ, 0)); CLK_TESTBUS, READ, 0)); } } void arm_smmu_debug_set_tnx_tcr_cntl(void __iomem *tbu_base, u64 val) { u64 tcr_cntl_val = readq_relaxed(tbu_base + TNX_TCR_CNTL); /* Don't override OT_CAPTURE configuration*/ if (!(tcr_cntl_val & TNX_TCR_CNTL_TBU_OT_CAPTURE_EN)) writeq_relaxed(val, tbu_base + TNX_TCR_CNTL); else pr_err_ratelimited("OT capture enbl, skip TCR CNTL write\n"); } u64 arm_smmu_debug_get_tnx_tcr_cntl(void __iomem *tbu_base) { return readq_relaxed(tbu_base + 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 i, j; u64 valid; valid = readl_relaxed(tbu_base + TNX_TCR_CNTL_2); for (i = 0; i < NO_OF_CAPTURE_POINTS ; ++i) { if (valid & BIT(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) { u64 val = 0; val |= INTR_CLR | RESET_VALID; writeq_relaxed(val, tbu_base + TNX_TCR_CNTL); } drivers/iommu/arm-smmu-debug.h +56 −1 Original line number Original line Diff line number Diff line Loading @@ -38,6 +38,28 @@ extern int tbu_testbus_sel; extern int tbu_testbus_sel; extern int tcu_testbus_sel; extern int tcu_testbus_sel; #define TNX_TCR_CNTL 0x130 #define TNX_TCR_CNTL_TBU_OT_CAPTURE_EN BIT(18) #define TNX_TCR_CNTL_ALWAYS_CAPTURE BIT(15) #define TNX_TCR_CNTL_MATCH_MASK_UPD BIT(7) #define TNX_TCR_CNTL_MATCH_MASK_VALID BIT(6) #define CAPTURE1_SNAPSHOT_1 0x138 #define TNX_TCR_CNTL_2 0x178 #define TNX_TCR_CNTL_2_CAP1_VALID BIT(0) #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) (CAPTURE1_SNAPSHOT_1 + \ (0x10 * (i)) + ((j) * 0x8)) #define NO_OF_MASK_AND_MATCH 0x3 #define NO_OF_CAPTURE_POINTS 0x4 #define REGS_PER_CAPTURE_POINT 0x2 #define INTR_CLR BIT(0) #define RESET_VALID BIT(7) enum tcu_testbus { enum tcu_testbus { PTW_AND_CACHE_TESTBUS, PTW_AND_CACHE_TESTBUS, CLK_TESTBUS, CLK_TESTBUS, Loading Loading @@ -66,7 +88,15 @@ void arm_smmu_debug_dump_tbu_testbus(struct device *dev, void __iomem *tbu_base, int tbu_testbus_sel); int tbu_testbus_sel); void arm_smmu_debug_dump_tcu_testbus(struct device *dev, phys_addr_t 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); void __iomem *tcu_base, int tcu_testbus_sel); void arm_smmu_debug_set_tnx_tcr_cntl(void __iomem *tbu_base, u64 val); u64 arm_smmu_debug_get_tnx_tcr_cntl(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, bool write, u32 val) bool write, u32 val) Loading Loading @@ -95,5 +125,30 @@ static inline void arm_smmu_debug_dump_tcu_testbus(struct device *dev, int tcu_testbus_sel) int tcu_testbus_sel) { { } } static inline void arm_smmu_debug_set_tnx_tcr_cntl(void __iomem *tbu_base, u64 val) { } static inline u64 arm_smmu_debug_get_tnx_tcr_cntl(void __iomem *tbu_base) { return 0; } static inline void arm_smmu_debug_set_mask_and_match(void __iomem *tbu_base, u64 sel, u64 mask, u64 match) { } static inline void arm_smmu_debug_get_mask_and_match(void __iomem *tbu_base, u64 *mask, u64 *match) { } static inline void arm_smmu_debug_get_capture_snapshot(void __iomem *tbu_base, u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT]) { } static inline void arm_smmu_debug_clear_intr_and_validbits(void __iomem *tbu_base) { } #endif #endif drivers/iommu/arm-smmu-qcom.c +356 −10 Original line number Original line Diff line number Diff line Loading @@ -4,6 +4,7 @@ */ */ #include <linux/bitfield.h> #include <linux/bitfield.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/io.h> #include <linux/iopoll.h> #include <linux/iopoll.h> #include <linux/of.h> #include <linux/of.h> Loading Loading @@ -378,16 +379,6 @@ static const struct arm_smmu_impl qcom_smmu_impl = { #define TBU_DBG_TIMEOUT_US 100 #define TBU_DBG_TIMEOUT_US 100 #define TNX_TCR_CNTL 0x130 #define TNX_TCR_CNTL_TBU_OT_CAPTURE_EN BIT(18) #define TNX_TCR_CNTL_ALWAYS_CAPTURE BIT(15) #define TNX_TCR_CNTL_MATCH_MASK_UPD BIT(7) #define TNX_TCR_CNTL_MATCH_MASK_VALID BIT(6) #define CAPTURE1_SNAPSHOT_1 0x138 #define TNX_TCR_CNTL_2 0x178 #define TNX_TCR_CNTL_2_CAP1_VALID BIT(0) struct actlr_setting { struct actlr_setting { struct arm_smmu_smr smr; struct arm_smmu_smr smr; Loading Loading @@ -426,6 +417,10 @@ static struct qsmmuv500_tbu_device *qsmmuv500_find_tbu( static DEFINE_MUTEX(capture_reg_lock); static DEFINE_MUTEX(capture_reg_lock); static DEFINE_SPINLOCK(testbus_lock); static DEFINE_SPINLOCK(testbus_lock); #ifdef CONFIG_IOMMU_DEBUGFS static struct dentry *debugfs_capturebus_dir; #endif #ifdef CONFIG_ARM_SMMU_TESTBUS_DEBUGFS #ifdef CONFIG_ARM_SMMU_TESTBUS_DEBUGFS static struct dentry *debugfs_testbus_dir; static struct dentry *debugfs_testbus_dir; Loading Loading @@ -829,6 +824,325 @@ static void qsmmuv500_log_outstanding_transactions(struct work_struct *work) BUG_ON(IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG)); BUG_ON(IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG)); } } 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); if (arm_smmu_power_on(smmu->pwr)) return -EINVAL; if (arm_smmu_power_on(tbu->pwr)) { arm_smmu_power_off(smmu, smmu->pwr); return -EINVAL; } if (!mutex_trylock(&capture_reg_lock)) { dev_warn_ratelimited(smmu->dev, "capture bus regs in use, not dumping it.\n"); return -EBUSY; } arm_smmu_debug_get_capture_snapshot(tbu_base, snapshot); mutex_unlock(&capture_reg_lock); arm_smmu_power_off(tbu->smmu, tbu->pwr); arm_smmu_power_off(smmu, smmu->pwr); for (i = 0; i < NO_OF_CAPTURE_POINTS ; ++i) { for (j = 0; j < REGS_PER_CAPTURE_POINT; ++j) { scnprintf(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)) { dev_err_ratelimited(smmu->dev, "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 >= sizeof(buf)) { dev_err_ratelimited(smmu->dev, "Input too large\n"); goto invalid_format; } memset(buf, 0, sizeof(buf)); if (copy_from_user(buf, ubuf, count)) { dev_err_ratelimited(smmu->dev, "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: if (arm_smmu_power_on(smmu->pwr)) return -EINVAL; if (arm_smmu_power_on(tbu->pwr)) { arm_smmu_power_off(smmu, smmu->pwr); return -EINVAL; } if (!mutex_trylock(&capture_reg_lock)) { dev_warn_ratelimited(smmu->dev, "capture bus regs in use, not configuring it.\n"); return -EBUSY; } 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); mutex_unlock(&capture_reg_lock); arm_smmu_power_off(tbu->smmu, tbu->pwr); arm_smmu_power_off(smmu, smmu->pwr); return count; invalid_format: dev_err_ratelimited(smmu->dev, "Invalid format\n"); dev_err_ratelimited(smmu->dev, "Expected:<1/2/3,Mask,Match> <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; u64 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); if (arm_smmu_power_on(smmu->pwr)) return -EINVAL; if (arm_smmu_power_on(tbu->pwr)) { arm_smmu_power_off(smmu, smmu->pwr); return -EINVAL; } if (!mutex_trylock(&capture_reg_lock)) { dev_warn_ratelimited(smmu->dev, "capture bus regs in use, not configuring it.\n"); return -EBUSY; } arm_smmu_debug_get_mask_and_match(tbu_base, mask, match); val = arm_smmu_debug_get_tnx_tcr_cntl(tbu_base); mutex_unlock(&capture_reg_lock); arm_smmu_power_off(tbu->smmu, tbu->pwr); arm_smmu_power_off(smmu, smmu->pwr); for (i = 0; i < NO_OF_MASK_AND_MATCH; ++i) { scnprintf(buf + strlen(buf), buf_len - strlen(buf), "Mask_%d : 0x%0llx\t", i+1, mask[i]); scnprintf(buf + strlen(buf), buf_len - strlen(buf), "Match_%d : 0x%0llx\n", i+1, match[i]); } scnprintf(buf + strlen(buf), buf_len - strlen(buf), "0x%0lx\n", val); buflen = min(count, strlen(buf)); if (copy_to_user(ubuf, buf, buflen)) { dev_err_ratelimited(smmu->dev, "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, }; #ifdef CONFIG_IOMMU_DEBUGFS static int qsmmuv500_capturebus_init(struct qsmmuv500_tbu_device *tbu) { struct dentry *capturebus_dir; if (!iommu_debugfs_dir) return 0; if (!debugfs_capturebus_dir) { debugfs_capturebus_dir = debugfs_create_dir( "capturebus", iommu_debugfs_dir); if (IS_ERR(debugfs_capturebus_dir)) { dev_err_ratelimited(tbu->dev, "Couldn't create iommu/capturebus debugfs directory\n"); return PTR_ERR(debugfs_capturebus_dir); } } capturebus_dir = debugfs_create_dir(dev_name(tbu->dev), debugfs_capturebus_dir); if (IS_ERR(capturebus_dir)) { dev_err_ratelimited(tbu->dev, "Couldn't create iommu/capturebus/%s debugfs directory\n", dev_name(tbu->dev)); goto err; } if (IS_ERR(debugfs_create_file("config", 0400, capturebus_dir, tbu, &arm_smmu_debug_capturebus_config_fops))) { dev_err_ratelimited(tbu->dev, "Couldn't create iommu/capturebus/%s/config debugfs file\n", dev_name(tbu->dev)); goto err_rmdir; } if (IS_ERR(debugfs_create_file("snapshot", 0400, capturebus_dir, tbu, &arm_smmu_debug_capturebus_snapshot_fops))) { dev_err_ratelimited(tbu->dev, "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 -ENODEV; } #else static int qsmmuv500_capturebus_init(struct qsmmuv500_tbu_device *tbu) { return 0; } #endif 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; u64 val; if (arm_smmu_power_on(smmu->pwr)) return IRQ_NONE; if (arm_smmu_power_on(tbu->pwr)) { arm_smmu_power_off(smmu, smmu->pwr); return IRQ_NONE; } if (!mutex_trylock(&capture_reg_lock)) { dev_warn_ratelimited(smmu->dev, "capture bus regs in use, not dumping it.\n"); 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); mutex_unlock(&capture_reg_lock); arm_smmu_power_off(tbu->smmu, tbu->pwr); arm_smmu_power_off(smmu, 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 void qsmmuv500_tlb_sync_timeout(struct arm_smmu_device *smmu) static void qsmmuv500_tlb_sync_timeout(struct arm_smmu_device *smmu) { { u32 sync_inv_ack, tbu_pwr_status, sync_inv_progress; u32 sync_inv_ack, tbu_pwr_status, sync_inv_progress; Loading Loading @@ -1385,8 +1699,11 @@ static void qsmmuv500_init_cb(struct arm_smmu_domain *smmu_domain, static int qsmmuv500_tbu_register(struct device *dev, void *cookie) static int qsmmuv500_tbu_register(struct device *dev, void *cookie) { { struct resource *res; struct qsmmuv500_tbu_device *tbu; struct qsmmuv500_tbu_device *tbu; struct qsmmuv500_archdata *data = cookie; struct qsmmuv500_archdata *data = cookie; struct platform_device *pdev = to_platform_device(dev); int i, err, num_irqs = 0; if (!dev->driver) { if (!dev->driver) { dev_err(dev, "TBU failed probe, QSMMUV500 cannot continue!\n"); dev_err(dev, "TBU failed probe, QSMMUV500 cannot continue!\n"); Loading @@ -1399,7 +1716,36 @@ static int qsmmuv500_tbu_register(struct device *dev, void *cookie) tbu->smmu = &data->smmu; tbu->smmu = &data->smmu; list_add(&tbu->list, &data->tbus); list_add(&tbu->list, &data->tbus); 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; } } qsmmuv500_tbu_testbus_init(tbu); qsmmuv500_tbu_testbus_init(tbu); qsmmuv500_capturebus_init(tbu); return 0; return 0; } } Loading drivers/iommu/arm-smmu.h +1 −0 Original line number Original line Diff line number Diff line Loading @@ -418,6 +418,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; }; }; struct arm_smmu_master_cfg { struct arm_smmu_master_cfg { Loading Loading
drivers/iommu/arm-smmu-debug.c +63 −0 Original line number Original line Diff line number Diff line Loading @@ -223,3 +223,66 @@ void arm_smmu_debug_dump_tcu_testbus(struct device *dev, phys_addr_t phys_addr, arm_smmu_debug_tcu_testbus_select(phys_addr, tcu_base, arm_smmu_debug_tcu_testbus_select(phys_addr, tcu_base, CLK_TESTBUS, READ, 0)); CLK_TESTBUS, READ, 0)); } } void arm_smmu_debug_set_tnx_tcr_cntl(void __iomem *tbu_base, u64 val) { u64 tcr_cntl_val = readq_relaxed(tbu_base + TNX_TCR_CNTL); /* Don't override OT_CAPTURE configuration*/ if (!(tcr_cntl_val & TNX_TCR_CNTL_TBU_OT_CAPTURE_EN)) writeq_relaxed(val, tbu_base + TNX_TCR_CNTL); else pr_err_ratelimited("OT capture enbl, skip TCR CNTL write\n"); } u64 arm_smmu_debug_get_tnx_tcr_cntl(void __iomem *tbu_base) { return readq_relaxed(tbu_base + 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 i, j; u64 valid; valid = readl_relaxed(tbu_base + TNX_TCR_CNTL_2); for (i = 0; i < NO_OF_CAPTURE_POINTS ; ++i) { if (valid & BIT(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) { u64 val = 0; val |= INTR_CLR | RESET_VALID; writeq_relaxed(val, tbu_base + TNX_TCR_CNTL); }
drivers/iommu/arm-smmu-debug.h +56 −1 Original line number Original line Diff line number Diff line Loading @@ -38,6 +38,28 @@ extern int tbu_testbus_sel; extern int tbu_testbus_sel; extern int tcu_testbus_sel; extern int tcu_testbus_sel; #define TNX_TCR_CNTL 0x130 #define TNX_TCR_CNTL_TBU_OT_CAPTURE_EN BIT(18) #define TNX_TCR_CNTL_ALWAYS_CAPTURE BIT(15) #define TNX_TCR_CNTL_MATCH_MASK_UPD BIT(7) #define TNX_TCR_CNTL_MATCH_MASK_VALID BIT(6) #define CAPTURE1_SNAPSHOT_1 0x138 #define TNX_TCR_CNTL_2 0x178 #define TNX_TCR_CNTL_2_CAP1_VALID BIT(0) #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) (CAPTURE1_SNAPSHOT_1 + \ (0x10 * (i)) + ((j) * 0x8)) #define NO_OF_MASK_AND_MATCH 0x3 #define NO_OF_CAPTURE_POINTS 0x4 #define REGS_PER_CAPTURE_POINT 0x2 #define INTR_CLR BIT(0) #define RESET_VALID BIT(7) enum tcu_testbus { enum tcu_testbus { PTW_AND_CACHE_TESTBUS, PTW_AND_CACHE_TESTBUS, CLK_TESTBUS, CLK_TESTBUS, Loading Loading @@ -66,7 +88,15 @@ void arm_smmu_debug_dump_tbu_testbus(struct device *dev, void __iomem *tbu_base, int tbu_testbus_sel); int tbu_testbus_sel); void arm_smmu_debug_dump_tcu_testbus(struct device *dev, phys_addr_t 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); void __iomem *tcu_base, int tcu_testbus_sel); void arm_smmu_debug_set_tnx_tcr_cntl(void __iomem *tbu_base, u64 val); u64 arm_smmu_debug_get_tnx_tcr_cntl(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, bool write, u32 val) bool write, u32 val) Loading Loading @@ -95,5 +125,30 @@ static inline void arm_smmu_debug_dump_tcu_testbus(struct device *dev, int tcu_testbus_sel) int tcu_testbus_sel) { { } } static inline void arm_smmu_debug_set_tnx_tcr_cntl(void __iomem *tbu_base, u64 val) { } static inline u64 arm_smmu_debug_get_tnx_tcr_cntl(void __iomem *tbu_base) { return 0; } static inline void arm_smmu_debug_set_mask_and_match(void __iomem *tbu_base, u64 sel, u64 mask, u64 match) { } static inline void arm_smmu_debug_get_mask_and_match(void __iomem *tbu_base, u64 *mask, u64 *match) { } static inline void arm_smmu_debug_get_capture_snapshot(void __iomem *tbu_base, u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT]) { } static inline void arm_smmu_debug_clear_intr_and_validbits(void __iomem *tbu_base) { } #endif #endif
drivers/iommu/arm-smmu-qcom.c +356 −10 Original line number Original line Diff line number Diff line Loading @@ -4,6 +4,7 @@ */ */ #include <linux/bitfield.h> #include <linux/bitfield.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/io.h> #include <linux/iopoll.h> #include <linux/iopoll.h> #include <linux/of.h> #include <linux/of.h> Loading Loading @@ -378,16 +379,6 @@ static const struct arm_smmu_impl qcom_smmu_impl = { #define TBU_DBG_TIMEOUT_US 100 #define TBU_DBG_TIMEOUT_US 100 #define TNX_TCR_CNTL 0x130 #define TNX_TCR_CNTL_TBU_OT_CAPTURE_EN BIT(18) #define TNX_TCR_CNTL_ALWAYS_CAPTURE BIT(15) #define TNX_TCR_CNTL_MATCH_MASK_UPD BIT(7) #define TNX_TCR_CNTL_MATCH_MASK_VALID BIT(6) #define CAPTURE1_SNAPSHOT_1 0x138 #define TNX_TCR_CNTL_2 0x178 #define TNX_TCR_CNTL_2_CAP1_VALID BIT(0) struct actlr_setting { struct actlr_setting { struct arm_smmu_smr smr; struct arm_smmu_smr smr; Loading Loading @@ -426,6 +417,10 @@ static struct qsmmuv500_tbu_device *qsmmuv500_find_tbu( static DEFINE_MUTEX(capture_reg_lock); static DEFINE_MUTEX(capture_reg_lock); static DEFINE_SPINLOCK(testbus_lock); static DEFINE_SPINLOCK(testbus_lock); #ifdef CONFIG_IOMMU_DEBUGFS static struct dentry *debugfs_capturebus_dir; #endif #ifdef CONFIG_ARM_SMMU_TESTBUS_DEBUGFS #ifdef CONFIG_ARM_SMMU_TESTBUS_DEBUGFS static struct dentry *debugfs_testbus_dir; static struct dentry *debugfs_testbus_dir; Loading Loading @@ -829,6 +824,325 @@ static void qsmmuv500_log_outstanding_transactions(struct work_struct *work) BUG_ON(IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG)); BUG_ON(IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG)); } } 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); if (arm_smmu_power_on(smmu->pwr)) return -EINVAL; if (arm_smmu_power_on(tbu->pwr)) { arm_smmu_power_off(smmu, smmu->pwr); return -EINVAL; } if (!mutex_trylock(&capture_reg_lock)) { dev_warn_ratelimited(smmu->dev, "capture bus regs in use, not dumping it.\n"); return -EBUSY; } arm_smmu_debug_get_capture_snapshot(tbu_base, snapshot); mutex_unlock(&capture_reg_lock); arm_smmu_power_off(tbu->smmu, tbu->pwr); arm_smmu_power_off(smmu, smmu->pwr); for (i = 0; i < NO_OF_CAPTURE_POINTS ; ++i) { for (j = 0; j < REGS_PER_CAPTURE_POINT; ++j) { scnprintf(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)) { dev_err_ratelimited(smmu->dev, "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 >= sizeof(buf)) { dev_err_ratelimited(smmu->dev, "Input too large\n"); goto invalid_format; } memset(buf, 0, sizeof(buf)); if (copy_from_user(buf, ubuf, count)) { dev_err_ratelimited(smmu->dev, "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: if (arm_smmu_power_on(smmu->pwr)) return -EINVAL; if (arm_smmu_power_on(tbu->pwr)) { arm_smmu_power_off(smmu, smmu->pwr); return -EINVAL; } if (!mutex_trylock(&capture_reg_lock)) { dev_warn_ratelimited(smmu->dev, "capture bus regs in use, not configuring it.\n"); return -EBUSY; } 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); mutex_unlock(&capture_reg_lock); arm_smmu_power_off(tbu->smmu, tbu->pwr); arm_smmu_power_off(smmu, smmu->pwr); return count; invalid_format: dev_err_ratelimited(smmu->dev, "Invalid format\n"); dev_err_ratelimited(smmu->dev, "Expected:<1/2/3,Mask,Match> <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; u64 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); if (arm_smmu_power_on(smmu->pwr)) return -EINVAL; if (arm_smmu_power_on(tbu->pwr)) { arm_smmu_power_off(smmu, smmu->pwr); return -EINVAL; } if (!mutex_trylock(&capture_reg_lock)) { dev_warn_ratelimited(smmu->dev, "capture bus regs in use, not configuring it.\n"); return -EBUSY; } arm_smmu_debug_get_mask_and_match(tbu_base, mask, match); val = arm_smmu_debug_get_tnx_tcr_cntl(tbu_base); mutex_unlock(&capture_reg_lock); arm_smmu_power_off(tbu->smmu, tbu->pwr); arm_smmu_power_off(smmu, smmu->pwr); for (i = 0; i < NO_OF_MASK_AND_MATCH; ++i) { scnprintf(buf + strlen(buf), buf_len - strlen(buf), "Mask_%d : 0x%0llx\t", i+1, mask[i]); scnprintf(buf + strlen(buf), buf_len - strlen(buf), "Match_%d : 0x%0llx\n", i+1, match[i]); } scnprintf(buf + strlen(buf), buf_len - strlen(buf), "0x%0lx\n", val); buflen = min(count, strlen(buf)); if (copy_to_user(ubuf, buf, buflen)) { dev_err_ratelimited(smmu->dev, "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, }; #ifdef CONFIG_IOMMU_DEBUGFS static int qsmmuv500_capturebus_init(struct qsmmuv500_tbu_device *tbu) { struct dentry *capturebus_dir; if (!iommu_debugfs_dir) return 0; if (!debugfs_capturebus_dir) { debugfs_capturebus_dir = debugfs_create_dir( "capturebus", iommu_debugfs_dir); if (IS_ERR(debugfs_capturebus_dir)) { dev_err_ratelimited(tbu->dev, "Couldn't create iommu/capturebus debugfs directory\n"); return PTR_ERR(debugfs_capturebus_dir); } } capturebus_dir = debugfs_create_dir(dev_name(tbu->dev), debugfs_capturebus_dir); if (IS_ERR(capturebus_dir)) { dev_err_ratelimited(tbu->dev, "Couldn't create iommu/capturebus/%s debugfs directory\n", dev_name(tbu->dev)); goto err; } if (IS_ERR(debugfs_create_file("config", 0400, capturebus_dir, tbu, &arm_smmu_debug_capturebus_config_fops))) { dev_err_ratelimited(tbu->dev, "Couldn't create iommu/capturebus/%s/config debugfs file\n", dev_name(tbu->dev)); goto err_rmdir; } if (IS_ERR(debugfs_create_file("snapshot", 0400, capturebus_dir, tbu, &arm_smmu_debug_capturebus_snapshot_fops))) { dev_err_ratelimited(tbu->dev, "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 -ENODEV; } #else static int qsmmuv500_capturebus_init(struct qsmmuv500_tbu_device *tbu) { return 0; } #endif 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; u64 val; if (arm_smmu_power_on(smmu->pwr)) return IRQ_NONE; if (arm_smmu_power_on(tbu->pwr)) { arm_smmu_power_off(smmu, smmu->pwr); return IRQ_NONE; } if (!mutex_trylock(&capture_reg_lock)) { dev_warn_ratelimited(smmu->dev, "capture bus regs in use, not dumping it.\n"); 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); mutex_unlock(&capture_reg_lock); arm_smmu_power_off(tbu->smmu, tbu->pwr); arm_smmu_power_off(smmu, 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 void qsmmuv500_tlb_sync_timeout(struct arm_smmu_device *smmu) static void qsmmuv500_tlb_sync_timeout(struct arm_smmu_device *smmu) { { u32 sync_inv_ack, tbu_pwr_status, sync_inv_progress; u32 sync_inv_ack, tbu_pwr_status, sync_inv_progress; Loading Loading @@ -1385,8 +1699,11 @@ static void qsmmuv500_init_cb(struct arm_smmu_domain *smmu_domain, static int qsmmuv500_tbu_register(struct device *dev, void *cookie) static int qsmmuv500_tbu_register(struct device *dev, void *cookie) { { struct resource *res; struct qsmmuv500_tbu_device *tbu; struct qsmmuv500_tbu_device *tbu; struct qsmmuv500_archdata *data = cookie; struct qsmmuv500_archdata *data = cookie; struct platform_device *pdev = to_platform_device(dev); int i, err, num_irqs = 0; if (!dev->driver) { if (!dev->driver) { dev_err(dev, "TBU failed probe, QSMMUV500 cannot continue!\n"); dev_err(dev, "TBU failed probe, QSMMUV500 cannot continue!\n"); Loading @@ -1399,7 +1716,36 @@ static int qsmmuv500_tbu_register(struct device *dev, void *cookie) tbu->smmu = &data->smmu; tbu->smmu = &data->smmu; list_add(&tbu->list, &data->tbus); list_add(&tbu->list, &data->tbus); 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; } } qsmmuv500_tbu_testbus_init(tbu); qsmmuv500_tbu_testbus_init(tbu); qsmmuv500_capturebus_init(tbu); return 0; return 0; } } Loading
drivers/iommu/arm-smmu.h +1 −0 Original line number Original line Diff line number Diff line Loading @@ -418,6 +418,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; }; }; struct arm_smmu_master_cfg { struct arm_smmu_master_cfg { Loading