op_model_sh7750.c 7.1 KB

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