op_model_sh7750.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. /*
  2. * arch/sh/oprofile/op_model_sh7750.c
  3. *
  4. * OProfile support for SH7750/SH7750S Performance Counters
  5. *
  6. * Copyright (C) 2003 - 2008 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/io.h>
  19. #include <linux/fs.h>
  20. #include "op_impl.h"
  21. #define PM_CR_BASE 0xff000084 /* 16-bit */
  22. #define PM_CTR_BASE 0xff100004 /* 32-bit */
  23. #define PMCR(n) (PM_CR_BASE + ((n) * 0x04))
  24. #define PMCTRH(n) (PM_CTR_BASE + 0x00 + ((n) * 0x08))
  25. #define PMCTRL(n) (PM_CTR_BASE + 0x04 + ((n) * 0x08))
  26. #define PMCR_PMM_MASK 0x0000003f
  27. #define PMCR_CLKF 0x00000100
  28. #define PMCR_PMCLR 0x00002000
  29. #define PMCR_PMST 0x00004000
  30. #define PMCR_PMEN 0x00008000
  31. struct op_sh_model op_model_sh7750_ops;
  32. #define NR_CNTRS 2
  33. static struct sh7750_ppc_register_config {
  34. unsigned int ctrl;
  35. unsigned long cnt_hi;
  36. unsigned long cnt_lo;
  37. } regcache[NR_CNTRS];
  38. /*
  39. * There are a number of events supported by each counter (33 in total).
  40. * Since we have 2 counters, each counter will take the event code as it
  41. * corresponds to the PMCR PMM setting. Each counter can be configured
  42. * independently.
  43. *
  44. * Event Code Description
  45. * ---------- -----------
  46. *
  47. * 0x01 Operand read access
  48. * 0x02 Operand write access
  49. * 0x03 UTLB miss
  50. * 0x04 Operand cache read miss
  51. * 0x05 Operand cache write miss
  52. * 0x06 Instruction fetch (w/ cache)
  53. * 0x07 Instruction TLB miss
  54. * 0x08 Instruction cache miss
  55. * 0x09 All operand accesses
  56. * 0x0a All instruction accesses
  57. * 0x0b OC RAM operand access
  58. * 0x0d On-chip I/O space access
  59. * 0x0e Operand access (r/w)
  60. * 0x0f Operand cache miss (r/w)
  61. * 0x10 Branch instruction
  62. * 0x11 Branch taken
  63. * 0x12 BSR/BSRF/JSR
  64. * 0x13 Instruction execution
  65. * 0x14 Instruction execution in parallel
  66. * 0x15 FPU Instruction execution
  67. * 0x16 Interrupt
  68. * 0x17 NMI
  69. * 0x18 trapa instruction execution
  70. * 0x19 UBCA match
  71. * 0x1a UBCB match
  72. * 0x21 Instruction cache fill
  73. * 0x22 Operand cache fill
  74. * 0x23 Elapsed time
  75. * 0x24 Pipeline freeze by I-cache miss
  76. * 0x25 Pipeline freeze by D-cache miss
  77. * 0x27 Pipeline freeze by branch instruction
  78. * 0x28 Pipeline freeze by CPU register
  79. * 0x29 Pipeline freeze by FPU
  80. *
  81. * Unfortunately we don't have a native exception or interrupt for counter
  82. * overflow (although since these counters can run for 16.3 days without
  83. * overflowing, it's not really necessary).
  84. *
  85. * OProfile on the other hand likes to have samples taken periodically, so
  86. * for now we just piggyback the timer interrupt to get the expected
  87. * behavior.
  88. */
  89. static int sh7750_timer_notify(struct pt_regs *regs)
  90. {
  91. oprofile_add_sample(regs, 0);
  92. return 0;
  93. }
  94. static u64 sh7750_read_counter(int counter)
  95. {
  96. return (u64)((u64)(__raw_readl(PMCTRH(counter)) & 0xffff) << 32) |
  97. __raw_readl(PMCTRL(counter));
  98. }
  99. /*
  100. * Files will be in a path like:
  101. *
  102. * /<oprofilefs mount point>/<counter number>/<file>
  103. *
  104. * So when dealing with <file>, we look to the parent dentry for the counter
  105. * number.
  106. */
  107. static inline int to_counter(struct file *file)
  108. {
  109. const unsigned char *name = file->f_path.dentry->d_parent->d_name.name;
  110. return (int)simple_strtol(name, NULL, 10);
  111. }
  112. /*
  113. * XXX: We have 48-bit counters, so we're probably going to want something
  114. * more along the lines of oprofilefs_ullong_to_user().. Truncating to
  115. * unsigned long works fine for now though, as long as we don't attempt to
  116. * profile for too horribly long.
  117. */
  118. static ssize_t sh7750_read_count(struct file *file, char __user *buf,
  119. size_t count, loff_t *ppos)
  120. {
  121. int counter = to_counter(file);
  122. u64 val = sh7750_read_counter(counter);
  123. return oprofilefs_ulong_to_user((unsigned long)val, buf, count, ppos);
  124. }
  125. static ssize_t sh7750_write_count(struct file *file, const char __user *buf,
  126. size_t count, loff_t *ppos)
  127. {
  128. int counter = to_counter(file);
  129. unsigned long val;
  130. if (oprofilefs_ulong_from_user(&val, buf, count))
  131. return -EFAULT;
  132. /*
  133. * Any write will clear the counter, although only 0 should be
  134. * written for this purpose, as we do not support setting the
  135. * counter to an arbitrary value.
  136. */
  137. WARN_ON(val != 0);
  138. __raw_writew(__raw_readw(PMCR(counter)) | PMCR_PMCLR, PMCR(counter));
  139. return count;
  140. }
  141. static const struct file_operations count_fops = {
  142. .read = sh7750_read_count,
  143. .write = sh7750_write_count,
  144. };
  145. static int sh7750_ppc_create_files(struct super_block *sb, struct dentry *dir)
  146. {
  147. return oprofilefs_create_file(sb, dir, "count", &count_fops);
  148. }
  149. static void sh7750_ppc_reg_setup(struct op_counter_config *ctr)
  150. {
  151. unsigned int counters = op_model_sh7750_ops.num_counters;
  152. int i;
  153. for (i = 0; i < counters; i++) {
  154. regcache[i].ctrl = 0;
  155. regcache[i].cnt_hi = 0;
  156. regcache[i].cnt_lo = 0;
  157. if (!ctr[i].enabled)
  158. continue;
  159. regcache[i].ctrl |= ctr[i].event | PMCR_PMEN | PMCR_PMST;
  160. regcache[i].cnt_hi = (unsigned long)((ctr->count >> 32) & 0xffff);
  161. regcache[i].cnt_lo = (unsigned long)(ctr->count & 0xffffffff);
  162. }
  163. }
  164. static void sh7750_ppc_cpu_setup(void *args)
  165. {
  166. unsigned int counters = op_model_sh7750_ops.num_counters;
  167. int i;
  168. for (i = 0; i < counters; i++) {
  169. __raw_writew(0, PMCR(i));
  170. __raw_writel(regcache[i].cnt_hi, PMCTRH(i));
  171. __raw_writel(regcache[i].cnt_lo, PMCTRL(i));
  172. }
  173. }
  174. static void sh7750_ppc_cpu_start(void *args)
  175. {
  176. unsigned int counters = op_model_sh7750_ops.num_counters;
  177. int i;
  178. for (i = 0; i < counters; i++)
  179. __raw_writew(regcache[i].ctrl, PMCR(i));
  180. }
  181. static void sh7750_ppc_cpu_stop(void *args)
  182. {
  183. unsigned int counters = op_model_sh7750_ops.num_counters;
  184. int i;
  185. /* Disable the counters */
  186. for (i = 0; i < counters; i++)
  187. __raw_writew(__raw_readw(PMCR(i)) & ~PMCR_PMEN, PMCR(i));
  188. }
  189. static inline void sh7750_ppc_reset(void)
  190. {
  191. unsigned int counters = op_model_sh7750_ops.num_counters;
  192. int i;
  193. /* Clear the counters */
  194. for (i = 0; i < counters; i++)
  195. __raw_writew(__raw_readw(PMCR(i)) | PMCR_PMCLR, PMCR(i));
  196. }
  197. static int sh7750_ppc_init(void)
  198. {
  199. sh7750_ppc_reset();
  200. return register_timer_hook(sh7750_timer_notify);
  201. }
  202. static void sh7750_ppc_exit(void)
  203. {
  204. unregister_timer_hook(sh7750_timer_notify);
  205. sh7750_ppc_reset();
  206. }
  207. struct op_sh_model op_model_sh7750_ops = {
  208. .cpu_type = "sh/sh7750",
  209. .num_counters = NR_CNTRS,
  210. .reg_setup = sh7750_ppc_reg_setup,
  211. .cpu_setup = sh7750_ppc_cpu_setup,
  212. .cpu_start = sh7750_ppc_cpu_start,
  213. .cpu_stop = sh7750_ppc_cpu_stop,
  214. .init = sh7750_ppc_init,
  215. .exit = sh7750_ppc_exit,
  216. .create_files = sh7750_ppc_create_files,
  217. };