diff options
author | Hiroshi Doyu <hdoyu@nvidia.com> | 2012-08-02 04:46:40 -0400 |
---|---|---|
committer | Joerg Roedel <joerg.roedel@amd.com> | 2012-08-03 11:53:55 -0400 |
commit | 39abf8aa7dfad0badb4741373023cda369aacc8e (patch) | |
tree | b1e845941ab5a29f4907922a6406d2db13bc38e5 /drivers/iommu | |
parent | 0d7614f09c1ebdbaa1599a5aba7593f147bf96ee (diff) |
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:<HIT count><SPC>miss:<MISS count><SPC><CR+LF>
fscanf(fp, "hit:%lx miss:%lx", &hit, &miss);
Signed-off-by: Hiroshi Doyu <hdoyu@nvidia.com>
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
Diffstat (limited to 'drivers/iommu')
-rw-r--r-- | drivers/iommu/tegra-smmu.c | 202 |
1 files changed, 190 insertions, 12 deletions
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 4ba325ab6262..95b4698abd3e 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c | |||
@@ -32,6 +32,8 @@ | |||
32 | #include <linux/io.h> | 32 | #include <linux/io.h> |
33 | #include <linux/of.h> | 33 | #include <linux/of.h> |
34 | #include <linux/of_iommu.h> | 34 | #include <linux/of_iommu.h> |
35 | #include <linux/debugfs.h> | ||
36 | #include <linux/seq_file.h> | ||
35 | 37 | ||
36 | #include <asm/page.h> | 38 | #include <asm/page.h> |
37 | #include <asm/cacheflush.h> | 39 | #include <asm/cacheflush.h> |
@@ -47,16 +49,29 @@ | |||
47 | #define SMMU_CONFIG_DISABLE 0 | 49 | #define SMMU_CONFIG_DISABLE 0 |
48 | #define SMMU_CONFIG_ENABLE 1 | 50 | #define SMMU_CONFIG_ENABLE 1 |
49 | 51 | ||
50 | #define SMMU_TLB_CONFIG 0x14 | 52 | /* REVISIT: To support multiple MCs */ |
51 | #define SMMU_TLB_CONFIG_STATS__MASK (1 << 31) | 53 | enum { |
52 | #define SMMU_TLB_CONFIG_STATS__ENABLE (1 << 31) | 54 | _MC = 0, |
55 | }; | ||
56 | |||
57 | enum { | ||
58 | _TLB = 0, | ||
59 | _PTC, | ||
60 | }; | ||
61 | |||
62 | #define SMMU_CACHE_CONFIG_BASE 0x14 | ||
63 | #define __SMMU_CACHE_CONFIG(mc, cache) (SMMU_CACHE_CONFIG_BASE + 4 * cache) | ||
64 | #define SMMU_CACHE_CONFIG(cache) __SMMU_CACHE_CONFIG(_MC, cache) | ||
65 | |||
66 | #define SMMU_CACHE_CONFIG_STATS_SHIFT 31 | ||
67 | #define SMMU_CACHE_CONFIG_STATS_ENABLE (1 << SMMU_CACHE_CONFIG_STATS_SHIFT) | ||
68 | #define SMMU_CACHE_CONFIG_STATS_TEST_SHIFT 30 | ||
69 | #define SMMU_CACHE_CONFIG_STATS_TEST (1 << SMMU_CACHE_CONFIG_STATS_TEST_SHIFT) | ||
70 | |||
53 | #define SMMU_TLB_CONFIG_HIT_UNDER_MISS__ENABLE (1 << 29) | 71 | #define SMMU_TLB_CONFIG_HIT_UNDER_MISS__ENABLE (1 << 29) |
54 | #define SMMU_TLB_CONFIG_ACTIVE_LINES__VALUE 0x10 | 72 | #define SMMU_TLB_CONFIG_ACTIVE_LINES__VALUE 0x10 |
55 | #define SMMU_TLB_CONFIG_RESET_VAL 0x20000010 | 73 | #define SMMU_TLB_CONFIG_RESET_VAL 0x20000010 |
56 | 74 | ||
57 | #define SMMU_PTC_CONFIG 0x18 | ||
58 | #define SMMU_PTC_CONFIG_STATS__MASK (1 << 31) | ||
59 | #define SMMU_PTC_CONFIG_STATS__ENABLE (1 << 31) | ||
60 | #define SMMU_PTC_CONFIG_CACHE__ENABLE (1 << 29) | 75 | #define SMMU_PTC_CONFIG_CACHE__ENABLE (1 << 29) |
61 | #define SMMU_PTC_CONFIG_INDEX_MAP__PATTERN 0x3f | 76 | #define SMMU_PTC_CONFIG_INDEX_MAP__PATTERN 0x3f |
62 | #define SMMU_PTC_CONFIG_RESET_VAL 0x2000003f | 77 | #define SMMU_PTC_CONFIG_RESET_VAL 0x2000003f |
@@ -86,10 +101,10 @@ | |||
86 | 101 | ||
87 | #define SMMU_ASID_SECURITY 0x38 | 102 | #define SMMU_ASID_SECURITY 0x38 |
88 | 103 | ||
89 | #define SMMU_STATS_TLB_HIT_COUNT 0x1f0 | 104 | #define SMMU_STATS_CACHE_COUNT_BASE 0x1f0 |
90 | #define SMMU_STATS_TLB_MISS_COUNT 0x1f4 | 105 | |
91 | #define SMMU_STATS_PTC_HIT_COUNT 0x1f8 | 106 | #define SMMU_STATS_CACHE_COUNT(mc, cache, hitmiss) \ |
92 | #define SMMU_STATS_PTC_MISS_COUNT 0x1fc | 107 | (SMMU_STATS_CACHE_COUNT_BASE + 8 * cache + 4 * hitmiss) |
93 | 108 | ||
94 | #define SMMU_TRANSLATION_ENABLE_0 0x228 | 109 | #define SMMU_TRANSLATION_ENABLE_0 0x228 |
95 | #define SMMU_TRANSLATION_ENABLE_1 0x22c | 110 | #define SMMU_TRANSLATION_ENABLE_1 0x22c |
@@ -251,6 +266,8 @@ struct smmu_device { | |||
251 | unsigned long translation_enable_2; | 266 | unsigned long translation_enable_2; |
252 | unsigned long asid_security; | 267 | unsigned long asid_security; |
253 | 268 | ||
269 | struct dentry *debugfs_root; | ||
270 | |||
254 | struct device_node *ahb; | 271 | struct device_node *ahb; |
255 | 272 | ||
256 | int num_as; | 273 | int num_as; |
@@ -412,8 +429,8 @@ static int smmu_setup_regs(struct smmu_device *smmu) | |||
412 | smmu_write(smmu, smmu->translation_enable_1, SMMU_TRANSLATION_ENABLE_1); | 429 | smmu_write(smmu, smmu->translation_enable_1, SMMU_TRANSLATION_ENABLE_1); |
413 | smmu_write(smmu, smmu->translation_enable_2, SMMU_TRANSLATION_ENABLE_2); | 430 | smmu_write(smmu, smmu->translation_enable_2, SMMU_TRANSLATION_ENABLE_2); |
414 | smmu_write(smmu, smmu->asid_security, SMMU_ASID_SECURITY); | 431 | smmu_write(smmu, smmu->asid_security, SMMU_ASID_SECURITY); |
415 | smmu_write(smmu, SMMU_TLB_CONFIG_RESET_VAL, SMMU_TLB_CONFIG); | 432 | smmu_write(smmu, SMMU_TLB_CONFIG_RESET_VAL, SMMU_CACHE_CONFIG(_TLB)); |
416 | smmu_write(smmu, SMMU_PTC_CONFIG_RESET_VAL, SMMU_PTC_CONFIG); | 433 | smmu_write(smmu, SMMU_PTC_CONFIG_RESET_VAL, SMMU_CACHE_CONFIG(_PTC)); |
417 | 434 | ||
418 | smmu_flush_regs(smmu, 1); | 435 | smmu_flush_regs(smmu, 1); |
419 | 436 | ||
@@ -892,6 +909,164 @@ static struct iommu_ops smmu_iommu_ops = { | |||
892 | .pgsize_bitmap = SMMU_IOMMU_PGSIZES, | 909 | .pgsize_bitmap = SMMU_IOMMU_PGSIZES, |
893 | }; | 910 | }; |
894 | 911 | ||
912 | /* Should be in the order of enum */ | ||
913 | static const char * const smmu_debugfs_mc[] = { "mc", }; | ||
914 | static const char * const smmu_debugfs_cache[] = { "tlb", "ptc", }; | ||
915 | |||
916 | static ssize_t smmu_debugfs_stats_write(struct file *file, | ||
917 | const char __user *buffer, | ||
918 | size_t count, loff_t *pos) | ||
919 | { | ||
920 | struct smmu_device *smmu; | ||
921 | struct dentry *dent; | ||
922 | int i, cache, mc; | ||
923 | enum { | ||
924 | _OFF = 0, | ||
925 | _ON, | ||
926 | _RESET, | ||
927 | }; | ||
928 | const char * const command[] = { | ||
929 | [_OFF] = "off", | ||
930 | [_ON] = "on", | ||
931 | [_RESET] = "reset", | ||
932 | }; | ||
933 | char str[] = "reset"; | ||
934 | u32 val; | ||
935 | size_t offs; | ||
936 | |||
937 | count = min_t(size_t, count, sizeof(str)); | ||
938 | if (copy_from_user(str, buffer, count)) | ||
939 | return -EINVAL; | ||
940 | |||
941 | for (i = 0; i < ARRAY_SIZE(command); i++) | ||
942 | if (strncmp(str, command[i], | ||
943 | strlen(command[i])) == 0) | ||
944 | break; | ||
945 | |||
946 | if (i == ARRAY_SIZE(command)) | ||
947 | return -EINVAL; | ||
948 | |||
949 | dent = file->f_dentry; | ||
950 | cache = (int)dent->d_inode->i_private; | ||
951 | mc = (int)dent->d_parent->d_inode->i_private; | ||
952 | smmu = dent->d_parent->d_parent->d_inode->i_private; | ||
953 | |||
954 | offs = SMMU_CACHE_CONFIG(cache); | ||
955 | val = smmu_read(smmu, offs); | ||
956 | switch (i) { | ||
957 | case _OFF: | ||
958 | val &= ~SMMU_CACHE_CONFIG_STATS_ENABLE; | ||
959 | val &= ~SMMU_CACHE_CONFIG_STATS_TEST; | ||
960 | smmu_write(smmu, val, offs); | ||
961 | break; | ||
962 | case _ON: | ||
963 | val |= SMMU_CACHE_CONFIG_STATS_ENABLE; | ||
964 | val &= ~SMMU_CACHE_CONFIG_STATS_TEST; | ||
965 | smmu_write(smmu, val, offs); | ||
966 | break; | ||
967 | case _RESET: | ||
968 | val |= SMMU_CACHE_CONFIG_STATS_TEST; | ||
969 | smmu_write(smmu, val, offs); | ||
970 | val &= ~SMMU_CACHE_CONFIG_STATS_TEST; | ||
971 | smmu_write(smmu, val, offs); | ||
972 | break; | ||
973 | default: | ||
974 | BUG(); | ||
975 | break; | ||
976 | } | ||
977 | |||
978 | dev_dbg(smmu->dev, "%s() %08x, %08x @%08x\n", __func__, | ||
979 | val, smmu_read(smmu, offs), offs); | ||
980 | |||
981 | return count; | ||
982 | } | ||
983 | |||
984 | static int smmu_debugfs_stats_show(struct seq_file *s, void *v) | ||
985 | { | ||
986 | struct smmu_device *smmu; | ||
987 | struct dentry *dent; | ||
988 | int i, cache, mc; | ||
989 | const char * const stats[] = { "hit", "miss", }; | ||
990 | |||
991 | dent = d_find_alias(s->private); | ||
992 | cache = (int)dent->d_inode->i_private; | ||
993 | mc = (int)dent->d_parent->d_inode->i_private; | ||
994 | smmu = dent->d_parent->d_parent->d_inode->i_private; | ||
995 | |||
996 | for (i = 0; i < ARRAY_SIZE(stats); i++) { | ||
997 | u32 val; | ||
998 | size_t offs; | ||
999 | |||
1000 | offs = SMMU_STATS_CACHE_COUNT(mc, cache, i); | ||
1001 | val = smmu_read(smmu, offs); | ||
1002 | seq_printf(s, "%s:%08x ", stats[i], val); | ||
1003 | |||
1004 | dev_dbg(smmu->dev, "%s() %s %08x @%08x\n", __func__, | ||
1005 | stats[i], val, offs); | ||
1006 | } | ||
1007 | seq_printf(s, "\n"); | ||
1008 | |||
1009 | return 0; | ||
1010 | } | ||
1011 | |||
1012 | static int smmu_debugfs_stats_open(struct inode *inode, struct file *file) | ||
1013 | { | ||
1014 | return single_open(file, smmu_debugfs_stats_show, inode); | ||
1015 | } | ||
1016 | |||
1017 | static const struct file_operations smmu_debugfs_stats_fops = { | ||
1018 | .open = smmu_debugfs_stats_open, | ||
1019 | .read = seq_read, | ||
1020 | .llseek = seq_lseek, | ||
1021 | .release = single_release, | ||
1022 | .write = smmu_debugfs_stats_write, | ||
1023 | }; | ||
1024 | |||
1025 | static void smmu_debugfs_delete(struct smmu_device *smmu) | ||
1026 | { | ||
1027 | debugfs_remove_recursive(smmu->debugfs_root); | ||
1028 | } | ||
1029 | |||
1030 | static void smmu_debugfs_create(struct smmu_device *smmu) | ||
1031 | { | ||
1032 | int i; | ||
1033 | struct dentry *root; | ||
1034 | |||
1035 | root = debugfs_create_file(dev_name(smmu->dev), | ||
1036 | S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, | ||
1037 | NULL, smmu, NULL); | ||
1038 | if (!root) | ||
1039 | goto err_out; | ||
1040 | smmu->debugfs_root = root; | ||
1041 | |||
1042 | for (i = 0; i < ARRAY_SIZE(smmu_debugfs_mc); i++) { | ||
1043 | int j; | ||
1044 | struct dentry *mc; | ||
1045 | |||
1046 | mc = debugfs_create_file(smmu_debugfs_mc[i], | ||
1047 | S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, | ||
1048 | root, (void *)i, NULL); | ||
1049 | if (!mc) | ||
1050 | goto err_out; | ||
1051 | |||
1052 | for (j = 0; j < ARRAY_SIZE(smmu_debugfs_cache); j++) { | ||
1053 | struct dentry *cache; | ||
1054 | |||
1055 | cache = debugfs_create_file(smmu_debugfs_cache[j], | ||
1056 | S_IWUGO | S_IRUGO, mc, | ||
1057 | (void *)j, | ||
1058 | &smmu_debugfs_stats_fops); | ||
1059 | if (!cache) | ||
1060 | goto err_out; | ||
1061 | } | ||
1062 | } | ||
1063 | |||
1064 | return; | ||
1065 | |||
1066 | err_out: | ||
1067 | smmu_debugfs_delete(smmu); | ||
1068 | } | ||
1069 | |||
895 | static int tegra_smmu_suspend(struct device *dev) | 1070 | static int tegra_smmu_suspend(struct device *dev) |
896 | { | 1071 | { |
897 | struct smmu_device *smmu = dev_get_drvdata(dev); | 1072 | struct smmu_device *smmu = dev_get_drvdata(dev); |
@@ -996,6 +1171,7 @@ static int tegra_smmu_probe(struct platform_device *pdev) | |||
996 | if (!smmu->avp_vector_page) | 1171 | if (!smmu->avp_vector_page) |
997 | return -ENOMEM; | 1172 | return -ENOMEM; |
998 | 1173 | ||
1174 | smmu_debugfs_create(smmu); | ||
999 | smmu_handle = smmu; | 1175 | smmu_handle = smmu; |
1000 | return 0; | 1176 | return 0; |
1001 | } | 1177 | } |
@@ -1005,6 +1181,8 @@ static int tegra_smmu_remove(struct platform_device *pdev) | |||
1005 | struct smmu_device *smmu = platform_get_drvdata(pdev); | 1181 | struct smmu_device *smmu = platform_get_drvdata(pdev); |
1006 | int i; | 1182 | int i; |
1007 | 1183 | ||
1184 | smmu_debugfs_delete(smmu); | ||
1185 | |||
1008 | smmu_write(smmu, SMMU_CONFIG_DISABLE, SMMU_CONFIG); | 1186 | smmu_write(smmu, SMMU_CONFIG_DISABLE, SMMU_CONFIG); |
1009 | for (i = 0; i < smmu->num_as; i++) | 1187 | for (i = 0; i < smmu->num_as; i++) |
1010 | free_pdir(&smmu->as[i]); | 1188 | free_pdir(&smmu->as[i]); |