From 39abf8aa7dfad0badb4741373023cda369aacc8e Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Thu, 2 Aug 2012 11:46:40 +0300 Subject: iommu/tegra: smmu: debugfs for TLB/PTC statistics Add debugfs entries to collect TLB/PTC statistics. $ echo "reset" > /sys/kernel/debug/smmu/mc/{tlb,ptc} $ echo "on" > /sys/kernel/debug/smmu/mc/{tlb,ptc} $ echo "off" > /sys/kernel/debug/smmu/mc/{tlb,ptc} $ cat /sys/kernel/debug/smmu/mc/{tlb,ptc} hit:0014910c miss:00014d22 The above format is: hit:miss: fscanf(fp, "hit:%lx miss:%lx", &hit, &miss); Signed-off-by: Hiroshi Doyu Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 202 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 190 insertions(+), 12 deletions(-) (limited to 'drivers/iommu/tegra-smmu.c') diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 4ba325a..95b4698 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include @@ -47,16 +49,29 @@ #define SMMU_CONFIG_DISABLE 0 #define SMMU_CONFIG_ENABLE 1 -#define SMMU_TLB_CONFIG 0x14 -#define SMMU_TLB_CONFIG_STATS__MASK (1 << 31) -#define SMMU_TLB_CONFIG_STATS__ENABLE (1 << 31) +/* REVISIT: To support multiple MCs */ +enum { + _MC = 0, +}; + +enum { + _TLB = 0, + _PTC, +}; + +#define SMMU_CACHE_CONFIG_BASE 0x14 +#define __SMMU_CACHE_CONFIG(mc, cache) (SMMU_CACHE_CONFIG_BASE + 4 * cache) +#define SMMU_CACHE_CONFIG(cache) __SMMU_CACHE_CONFIG(_MC, cache) + +#define SMMU_CACHE_CONFIG_STATS_SHIFT 31 +#define SMMU_CACHE_CONFIG_STATS_ENABLE (1 << SMMU_CACHE_CONFIG_STATS_SHIFT) +#define SMMU_CACHE_CONFIG_STATS_TEST_SHIFT 30 +#define SMMU_CACHE_CONFIG_STATS_TEST (1 << SMMU_CACHE_CONFIG_STATS_TEST_SHIFT) + #define SMMU_TLB_CONFIG_HIT_UNDER_MISS__ENABLE (1 << 29) #define SMMU_TLB_CONFIG_ACTIVE_LINES__VALUE 0x10 #define SMMU_TLB_CONFIG_RESET_VAL 0x20000010 -#define SMMU_PTC_CONFIG 0x18 -#define SMMU_PTC_CONFIG_STATS__MASK (1 << 31) -#define SMMU_PTC_CONFIG_STATS__ENABLE (1 << 31) #define SMMU_PTC_CONFIG_CACHE__ENABLE (1 << 29) #define SMMU_PTC_CONFIG_INDEX_MAP__PATTERN 0x3f #define SMMU_PTC_CONFIG_RESET_VAL 0x2000003f @@ -86,10 +101,10 @@ #define SMMU_ASID_SECURITY 0x38 -#define SMMU_STATS_TLB_HIT_COUNT 0x1f0 -#define SMMU_STATS_TLB_MISS_COUNT 0x1f4 -#define SMMU_STATS_PTC_HIT_COUNT 0x1f8 -#define SMMU_STATS_PTC_MISS_COUNT 0x1fc +#define SMMU_STATS_CACHE_COUNT_BASE 0x1f0 + +#define SMMU_STATS_CACHE_COUNT(mc, cache, hitmiss) \ + (SMMU_STATS_CACHE_COUNT_BASE + 8 * cache + 4 * hitmiss) #define SMMU_TRANSLATION_ENABLE_0 0x228 #define SMMU_TRANSLATION_ENABLE_1 0x22c @@ -251,6 +266,8 @@ struct smmu_device { unsigned long translation_enable_2; unsigned long asid_security; + struct dentry *debugfs_root; + struct device_node *ahb; int num_as; @@ -412,8 +429,8 @@ static int smmu_setup_regs(struct smmu_device *smmu) smmu_write(smmu, smmu->translation_enable_1, SMMU_TRANSLATION_ENABLE_1); smmu_write(smmu, smmu->translation_enable_2, SMMU_TRANSLATION_ENABLE_2); smmu_write(smmu, smmu->asid_security, SMMU_ASID_SECURITY); - smmu_write(smmu, SMMU_TLB_CONFIG_RESET_VAL, SMMU_TLB_CONFIG); - smmu_write(smmu, SMMU_PTC_CONFIG_RESET_VAL, SMMU_PTC_CONFIG); + smmu_write(smmu, SMMU_TLB_CONFIG_RESET_VAL, SMMU_CACHE_CONFIG(_TLB)); + smmu_write(smmu, SMMU_PTC_CONFIG_RESET_VAL, SMMU_CACHE_CONFIG(_PTC)); smmu_flush_regs(smmu, 1); @@ -892,6 +909,164 @@ static struct iommu_ops smmu_iommu_ops = { .pgsize_bitmap = SMMU_IOMMU_PGSIZES, }; +/* Should be in the order of enum */ +static const char * const smmu_debugfs_mc[] = { "mc", }; +static const char * const smmu_debugfs_cache[] = { "tlb", "ptc", }; + +static ssize_t smmu_debugfs_stats_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *pos) +{ + struct smmu_device *smmu; + struct dentry *dent; + int i, cache, mc; + enum { + _OFF = 0, + _ON, + _RESET, + }; + const char * const command[] = { + [_OFF] = "off", + [_ON] = "on", + [_RESET] = "reset", + }; + char str[] = "reset"; + u32 val; + size_t offs; + + count = min_t(size_t, count, sizeof(str)); + if (copy_from_user(str, buffer, count)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(command); i++) + if (strncmp(str, command[i], + strlen(command[i])) == 0) + break; + + if (i == ARRAY_SIZE(command)) + return -EINVAL; + + dent = file->f_dentry; + cache = (int)dent->d_inode->i_private; + mc = (int)dent->d_parent->d_inode->i_private; + smmu = dent->d_parent->d_parent->d_inode->i_private; + + offs = SMMU_CACHE_CONFIG(cache); + val = smmu_read(smmu, offs); + switch (i) { + case _OFF: + val &= ~SMMU_CACHE_CONFIG_STATS_ENABLE; + val &= ~SMMU_CACHE_CONFIG_STATS_TEST; + smmu_write(smmu, val, offs); + break; + case _ON: + val |= SMMU_CACHE_CONFIG_STATS_ENABLE; + val &= ~SMMU_CACHE_CONFIG_STATS_TEST; + smmu_write(smmu, val, offs); + break; + case _RESET: + val |= SMMU_CACHE_CONFIG_STATS_TEST; + smmu_write(smmu, val, offs); + val &= ~SMMU_CACHE_CONFIG_STATS_TEST; + smmu_write(smmu, val, offs); + break; + default: + BUG(); + break; + } + + dev_dbg(smmu->dev, "%s() %08x, %08x @%08x\n", __func__, + val, smmu_read(smmu, offs), offs); + + return count; +} + +static int smmu_debugfs_stats_show(struct seq_file *s, void *v) +{ + struct smmu_device *smmu; + struct dentry *dent; + int i, cache, mc; + const char * const stats[] = { "hit", "miss", }; + + dent = d_find_alias(s->private); + cache = (int)dent->d_inode->i_private; + mc = (int)dent->d_parent->d_inode->i_private; + smmu = dent->d_parent->d_parent->d_inode->i_private; + + for (i = 0; i < ARRAY_SIZE(stats); i++) { + u32 val; + size_t offs; + + offs = SMMU_STATS_CACHE_COUNT(mc, cache, i); + val = smmu_read(smmu, offs); + seq_printf(s, "%s:%08x ", stats[i], val); + + dev_dbg(smmu->dev, "%s() %s %08x @%08x\n", __func__, + stats[i], val, offs); + } + seq_printf(s, "\n"); + + return 0; +} + +static int smmu_debugfs_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, smmu_debugfs_stats_show, inode); +} + +static const struct file_operations smmu_debugfs_stats_fops = { + .open = smmu_debugfs_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = smmu_debugfs_stats_write, +}; + +static void smmu_debugfs_delete(struct smmu_device *smmu) +{ + debugfs_remove_recursive(smmu->debugfs_root); +} + +static void smmu_debugfs_create(struct smmu_device *smmu) +{ + int i; + struct dentry *root; + + root = debugfs_create_file(dev_name(smmu->dev), + S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, + NULL, smmu, NULL); + if (!root) + goto err_out; + smmu->debugfs_root = root; + + for (i = 0; i < ARRAY_SIZE(smmu_debugfs_mc); i++) { + int j; + struct dentry *mc; + + mc = debugfs_create_file(smmu_debugfs_mc[i], + S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, + root, (void *)i, NULL); + if (!mc) + goto err_out; + + for (j = 0; j < ARRAY_SIZE(smmu_debugfs_cache); j++) { + struct dentry *cache; + + cache = debugfs_create_file(smmu_debugfs_cache[j], + S_IWUGO | S_IRUGO, mc, + (void *)j, + &smmu_debugfs_stats_fops); + if (!cache) + goto err_out; + } + } + + return; + +err_out: + smmu_debugfs_delete(smmu); +} + static int tegra_smmu_suspend(struct device *dev) { struct smmu_device *smmu = dev_get_drvdata(dev); @@ -996,6 +1171,7 @@ static int tegra_smmu_probe(struct platform_device *pdev) if (!smmu->avp_vector_page) return -ENOMEM; + smmu_debugfs_create(smmu); smmu_handle = smmu; return 0; } @@ -1005,6 +1181,8 @@ static int tegra_smmu_remove(struct platform_device *pdev) struct smmu_device *smmu = platform_get_drvdata(pdev); int i; + smmu_debugfs_delete(smmu); + smmu_write(smmu, SMMU_CONFIG_DISABLE, SMMU_CONFIG); for (i = 0; i < smmu->num_as; i++) free_pdir(&smmu->as[i]); -- cgit v1.1 From 5a2c937a8be8c9c5a6d23308b5ee841b6394a1cf Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Fri, 14 Sep 2012 10:22:00 -0600 Subject: iommu/tegra: smmu: Use debugfs_create_dir for directory The commit c3b1a35 "debugfs: make sure that debugfs_create_file() gets used only for regulars" doesn't allow to use debugfs_create_file() for dir. Keep debugfs data in smmu_device instead of directory's i_private. Signed-off-by: Hiroshi Doyu Signed-off-by: Stephen Warren Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 52 +++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 17 deletions(-) (limited to 'drivers/iommu/tegra-smmu.c') diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 95b4698..7e42c71 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -246,6 +246,12 @@ struct smmu_as { spinlock_t client_lock; /* for client list */ }; +struct smmu_debugfs_info { + struct smmu_device *smmu; + int mc; + int cache; +}; + /* * Per SMMU device - IOMMU device */ @@ -267,6 +273,7 @@ struct smmu_device { unsigned long asid_security; struct dentry *debugfs_root; + struct smmu_debugfs_info *debugfs_info; struct device_node *ahb; @@ -917,9 +924,10 @@ static ssize_t smmu_debugfs_stats_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos) { + struct smmu_debugfs_info *info; struct smmu_device *smmu; struct dentry *dent; - int i, cache, mc; + int i; enum { _OFF = 0, _ON, @@ -947,11 +955,10 @@ static ssize_t smmu_debugfs_stats_write(struct file *file, return -EINVAL; dent = file->f_dentry; - cache = (int)dent->d_inode->i_private; - mc = (int)dent->d_parent->d_inode->i_private; - smmu = dent->d_parent->d_parent->d_inode->i_private; + info = dent->d_inode->i_private; + smmu = info->smmu; - offs = SMMU_CACHE_CONFIG(cache); + offs = SMMU_CACHE_CONFIG(info->cache); val = smmu_read(smmu, offs); switch (i) { case _OFF: @@ -983,21 +990,21 @@ static ssize_t smmu_debugfs_stats_write(struct file *file, static int smmu_debugfs_stats_show(struct seq_file *s, void *v) { + struct smmu_debugfs_info *info; struct smmu_device *smmu; struct dentry *dent; - int i, cache, mc; + int i; const char * const stats[] = { "hit", "miss", }; dent = d_find_alias(s->private); - cache = (int)dent->d_inode->i_private; - mc = (int)dent->d_parent->d_inode->i_private; - smmu = dent->d_parent->d_parent->d_inode->i_private; + info = dent->d_inode->i_private; + smmu = info->smmu; for (i = 0; i < ARRAY_SIZE(stats); i++) { u32 val; size_t offs; - offs = SMMU_STATS_CACHE_COUNT(mc, cache, i); + offs = SMMU_STATS_CACHE_COUNT(info->mc, info->cache, i); val = smmu_read(smmu, offs); seq_printf(s, "%s:%08x ", stats[i], val); @@ -1025,16 +1032,22 @@ static const struct file_operations smmu_debugfs_stats_fops = { static void smmu_debugfs_delete(struct smmu_device *smmu) { debugfs_remove_recursive(smmu->debugfs_root); + kfree(smmu->debugfs_info); } static void smmu_debugfs_create(struct smmu_device *smmu) { int i; + size_t bytes; struct dentry *root; - root = debugfs_create_file(dev_name(smmu->dev), - S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, - NULL, smmu, NULL); + bytes = ARRAY_SIZE(smmu_debugfs_mc) * ARRAY_SIZE(smmu_debugfs_cache) * + sizeof(*smmu->debugfs_info); + smmu->debugfs_info = kmalloc(bytes, GFP_KERNEL); + if (!smmu->debugfs_info) + return; + + root = debugfs_create_dir(dev_name(smmu->dev), NULL); if (!root) goto err_out; smmu->debugfs_root = root; @@ -1043,18 +1056,23 @@ static void smmu_debugfs_create(struct smmu_device *smmu) int j; struct dentry *mc; - mc = debugfs_create_file(smmu_debugfs_mc[i], - S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, - root, (void *)i, NULL); + mc = debugfs_create_dir(smmu_debugfs_mc[i], root); if (!mc) goto err_out; for (j = 0; j < ARRAY_SIZE(smmu_debugfs_cache); j++) { struct dentry *cache; + struct smmu_debugfs_info *info; + + info = smmu->debugfs_info; + info += i * ARRAY_SIZE(smmu_debugfs_mc) + j; + info->smmu = smmu; + info->mc = i; + info->cache = j; cache = debugfs_create_file(smmu_debugfs_cache[j], S_IWUGO | S_IRUGO, mc, - (void *)j, + (void *)info, &smmu_debugfs_stats_fops); if (!cache) goto err_out; -- cgit v1.1 From e6bc59330e34626082a6b2e4733802e372781c8d Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 4 Sep 2012 16:36:15 -0600 Subject: dma: tegra: move smmu.h into SMMU driver There's no need to place these defines into arch/arm/mach-tegra/include/. Move them into the SMMU driver to clean up mach-tegra, as a pre-requisite for single-zImage. Signed-off-by: Stephen Warren Cc: Hiroshi Doyu Acked-by: Hiroshi Doyu Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) (limited to 'drivers/iommu/tegra-smmu.c') diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 7e42c71..e1d17fe 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -39,9 +39,48 @@ #include #include -#include #include +enum smmu_hwgrp { + HWGRP_AFI, + HWGRP_AVPC, + HWGRP_DC, + HWGRP_DCB, + HWGRP_EPP, + HWGRP_G2, + HWGRP_HC, + HWGRP_HDA, + HWGRP_ISP, + HWGRP_MPE, + HWGRP_NV, + HWGRP_NV2, + HWGRP_PPCS, + HWGRP_SATA, + HWGRP_VDE, + HWGRP_VI, + + HWGRP_COUNT, + + HWGRP_END = ~0, +}; + +#define HWG_AFI (1 << HWGRP_AFI) +#define HWG_AVPC (1 << HWGRP_AVPC) +#define HWG_DC (1 << HWGRP_DC) +#define HWG_DCB (1 << HWGRP_DCB) +#define HWG_EPP (1 << HWGRP_EPP) +#define HWG_G2 (1 << HWGRP_G2) +#define HWG_HC (1 << HWGRP_HC) +#define HWG_HDA (1 << HWGRP_HDA) +#define HWG_ISP (1 << HWGRP_ISP) +#define HWG_MPE (1 << HWGRP_MPE) +#define HWG_NV (1 << HWGRP_NV) +#define HWG_NV2 (1 << HWGRP_NV2) +#define HWG_PPCS (1 << HWGRP_PPCS) +#define HWG_SATA (1 << HWGRP_SATA) +#define HWG_VDE (1 << HWGRP_VDE) +#define HWG_VI (1 << HWGRP_VI) + /* bitmap of the page sizes currently supported */ #define SMMU_IOMMU_PGSIZES (SZ_4K) -- cgit v1.1