op_model_sh7750.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /*
  2. * arch/sh/oprofile/op_model_sh7750.c
  3. *
  4. * OProfile support for SH7750/SH7750S Performance Counters
  5. *
  6. * Copyright (C) 2003, 2004 Paul Mundt
  7. *
  8. * This file is subject to the terms and conditions of the GNU General Public
  9. * License. See the file "COPYING" in the main directory of this archive
  10. * for more details.
  11. */
  12. #include <linux/kernel.h>
  13. #include <linux/oprofile.h>
  14. #include <linux/profile.h>
  15. #include <linux/init.h>
  16. #include <linux/errno.h>
  17. #include <linux/interrupt.h>
  18. #include <linux/fs.h>
  19. #include <asm/uaccess.h>
  20. #include <asm/io.h>
  21. #define PM_CR_BASE 0xff000084 /* 16-bit */
  22. #define PM_CTR_BASE 0xff100004 /* 32-bit */
  23. #define PMCR1 (PM_CR_BASE + 0x00)
  24. #define PMCR2 (PM_CR_BASE + 0x04)
  25. #define PMCTR1H (PM_CTR_BASE + 0x00)
  26. #define PMCTR1L (PM_CTR_BASE + 0x04)
  27. #define PMCTR2H (PM_CTR_BASE + 0x08)
  28. #define PMCTR2L (PM_CTR_BASE + 0x0c)
  29. #define PMCR_PMM_MASK 0x0000003f
  30. #define PMCR_CLKF 0x00000100
  31. #define PMCR_PMCLR 0x00002000
  32. #define PMCR_PMST 0x00004000
  33. #define PMCR_PMEN 0x00008000
  34. #define PMCR_ENABLE (PMCR_PMST | PMCR_PMEN)
  35. /*
  36. * SH7750/SH7750S have 2 perf counters
  37. */
  38. #define NR_CNTRS 2
  39. struct op_counter_config {
  40. unsigned long enabled;
  41. unsigned long event;
  42. unsigned long count;
  43. /* Dummy values for userspace tool compliance */
  44. unsigned long kernel;
  45. unsigned long user;
  46. unsigned long unit_mask;
  47. };
  48. static struct op_counter_config ctr[NR_CNTRS];
  49. /*
  50. * There are a number of events supported by each counter (33 in total).
  51. * Since we have 2 counters, each counter will take the event code as it
  52. * corresponds to the PMCR PMM setting. Each counter can be configured
  53. * independently.
  54. *
  55. * Event Code Description
  56. * ---------- -----------
  57. *
  58. * 0x01 Operand read access
  59. * 0x02 Operand write access
  60. * 0x03 UTLB miss
  61. * 0x04 Operand cache read miss
  62. * 0x05 Operand cache write miss
  63. * 0x06 Instruction fetch (w/ cache)
  64. * 0x07 Instruction TLB miss
  65. * 0x08 Instruction cache miss
  66. * 0x09 All operand accesses
  67. * 0x0a All instruction accesses
  68. * 0x0b OC RAM operand access
  69. * 0x0d On-chip I/O space access
  70. * 0x0e Operand access (r/w)
  71. * 0x0f Operand cache miss (r/w)
  72. * 0x10 Branch instruction
  73. * 0x11 Branch taken
  74. * 0x12 BSR/BSRF/JSR
  75. * 0x13 Instruction execution
  76. * 0x14 Instruction execution in parallel
  77. * 0x15 FPU Instruction execution
  78. * 0x16 Interrupt
  79. * 0x17 NMI
  80. * 0x18 trapa instruction execution
  81. * 0x19 UBCA match
  82. * 0x1a UBCB match
  83. * 0x21 Instruction cache fill
  84. * 0x22 Operand cache fill
  85. * 0x23 Elapsed time
  86. * 0x24 Pipeline freeze by I-cache miss
  87. * 0x25 Pipeline freeze by D-cache miss
  88. * 0x27 Pipeline freeze by branch instruction
  89. * 0x28 Pipeline freeze by CPU register
  90. * 0x29 Pipeline freeze by FPU
  91. *
  92. * Unfortunately we don't have a native exception or interrupt for counter
  93. * overflow (although since these counters can run for 16.3 days without
  94. * overflowing, it's not really necessary).
  95. *
  96. * OProfile on the other hand likes to have samples taken periodically, so
  97. * for now we just piggyback the timer interrupt to get the expected
  98. * behavior.
  99. */
  100. static int sh7750_timer_notify(struct pt_regs *regs)
  101. {
  102. oprofile_add_sample(regs, 0);
  103. return 0;
  104. }
  105. static u64 sh7750_read_counter(int counter)
  106. {
  107. u32 hi, lo;
  108. hi = (counter == 0) ? ctrl_inl(PMCTR1H) : ctrl_inl(PMCTR2H);
  109. lo = (counter == 0) ? ctrl_inl(PMCTR1L) : ctrl_inl(PMCTR2L);
  110. return (u64)((u64)(hi & 0xffff) << 32) | lo;
  111. }
  112. /*
  113. * Files will be in a path like:
  114. *
  115. * /<oprofilefs mount point>/<counter number>/<file>
  116. *
  117. * So when dealing with <file>, we look to the parent dentry for the counter
  118. * number.
  119. */
  120. static inline int to_counter(struct file *file)
  121. {
  122. const unsigned char *name = file->f_path.dentry->d_parent->d_name.name;
  123. return (int)simple_strtol(name, NULL, 10);
  124. }
  125. /*
  126. * XXX: We have 48-bit counters, so we're probably going to want something
  127. * more along the lines of oprofilefs_ullong_to_user().. Truncating to
  128. * unsigned long works fine for now though, as long as we don't attempt to
  129. * profile for too horribly long.
  130. */
  131. static ssize_t sh7750_read_count(struct file *file, char __user *buf,
  132. size_t count, loff_t *ppos)
  133. {
  134. int counter = to_counter(file);
  135. u64 val = sh7750_read_counter(counter);
  136. return oprofilefs_ulong_to_user((unsigned long)val, buf, count, ppos);
  137. }
  138. static ssize_t sh7750_write_count(struct file *file, const char __user *buf,
  139. size_t count, loff_t *ppos)
  140. {
  141. int counter = to_counter(file);
  142. unsigned long val;
  143. if (oprofilefs_ulong_from_user(&val, buf, count))
  144. return -EFAULT;
  145. /*
  146. * Any write will clear the counter, although only 0 should be
  147. * written for this purpose, as we do not support setting the
  148. * counter to an arbitrary value.
  149. */
  150. WARN_ON(val != 0);
  151. if (counter == 0) {
  152. ctrl_outw(ctrl_inw(PMCR1) | PMCR_PMCLR, PMCR1);
  153. } else {
  154. ctrl_outw(ctrl_inw(PMCR2) | PMCR_PMCLR, PMCR2);
  155. }
  156. return count;
  157. }
  158. static const struct file_operations count_fops = {
  159. .read = sh7750_read_count,
  160. .write = sh7750_write_count,
  161. };
  162. static int sh7750_perf_counter_create_files(struct super_block *sb, struct dentry *root)
  163. {
  164. int i;
  165. for (i = 0; i < NR_CNTRS; i++) {
  166. struct dentry *dir;
  167. char buf[4];
  168. snprintf(buf, sizeof(buf), "%d", i);
  169. dir = oprofilefs_mkdir(sb, root, buf);
  170. oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
  171. oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
  172. oprofilefs_create_file(sb, dir, "count", &count_fops);
  173. /* Dummy entries */
  174. oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
  175. oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
  176. oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask);
  177. }
  178. return 0;
  179. }
  180. static int sh7750_perf_counter_start(void)
  181. {
  182. u16 pmcr;
  183. /* Enable counter 1 */
  184. if (ctr[0].enabled) {
  185. pmcr = ctrl_inw(PMCR1);
  186. WARN_ON(pmcr & PMCR_PMEN);
  187. pmcr &= ~PMCR_PMM_MASK;
  188. pmcr |= ctr[0].event;
  189. ctrl_outw(pmcr | PMCR_ENABLE, PMCR1);
  190. }
  191. /* Enable counter 2 */
  192. if (ctr[1].enabled) {
  193. pmcr = ctrl_inw(PMCR2);
  194. WARN_ON(pmcr & PMCR_PMEN);
  195. pmcr &= ~PMCR_PMM_MASK;
  196. pmcr |= ctr[1].event;
  197. ctrl_outw(pmcr | PMCR_ENABLE, PMCR2);
  198. }
  199. return register_timer_hook(sh7750_timer_notify);
  200. }
  201. static void sh7750_perf_counter_stop(void)
  202. {
  203. ctrl_outw(ctrl_inw(PMCR1) & ~PMCR_PMEN, PMCR1);
  204. ctrl_outw(ctrl_inw(PMCR2) & ~PMCR_PMEN, PMCR2);
  205. unregister_timer_hook(sh7750_timer_notify);
  206. }
  207. static struct oprofile_operations sh7750_perf_counter_ops = {
  208. .create_files = sh7750_perf_counter_create_files,
  209. .start = sh7750_perf_counter_start,
  210. .stop = sh7750_perf_counter_stop,
  211. };
  212. int __init oprofile_arch_init(struct oprofile_operations *ops)
  213. {
  214. if (!(current_cpu_data.flags & CPU_HAS_PERF_COUNTER))
  215. return -ENODEV;
  216. ops = &sh7750_perf_counter_ops;
  217. ops->cpu_type = (char *)get_cpu_subtype(&current_cpu_data);
  218. printk(KERN_INFO "oprofile: using SH-4 (%s) performance monitoring.\n",
  219. sh7750_perf_counter_ops.cpu_type);
  220. /* Clear the counters */
  221. ctrl_outw(ctrl_inw(PMCR1) | PMCR_PMCLR, PMCR1);
  222. ctrl_outw(ctrl_inw(PMCR2) | PMCR_PMCLR, PMCR2);
  223. return 0;
  224. }
  225. void oprofile_arch_exit(void)
  226. {
  227. }