op_model_v7.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. /**
  2. * op_model_v7.c
  3. * ARM V7 (Cortex A8) Event Monitor Driver
  4. *
  5. * Copyright 2008 Jean Pihet <jpihet@mvista.com>
  6. * Copyright 2004 ARM SMP Development Team
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License version 2 as
  10. * published by the Free Software Foundation.
  11. */
  12. #include <linux/types.h>
  13. #include <linux/errno.h>
  14. #include <linux/oprofile.h>
  15. #include <linux/interrupt.h>
  16. #include <linux/irq.h>
  17. #include <linux/smp.h>
  18. #include "op_counter.h"
  19. #include "op_arm_model.h"
  20. #include "op_model_v7.h"
  21. /* #define DEBUG */
  22. /*
  23. * ARM V7 PMNC support
  24. */
  25. static u32 cnt_en[CNTMAX];
  26. static inline void armv7_pmnc_write(u32 val)
  27. {
  28. val &= PMNC_MASK;
  29. asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
  30. }
  31. static inline u32 armv7_pmnc_read(void)
  32. {
  33. u32 val;
  34. asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
  35. return val;
  36. }
  37. static inline u32 armv7_pmnc_enable_counter(unsigned int cnt)
  38. {
  39. u32 val;
  40. if (cnt >= CNTMAX) {
  41. printk(KERN_ERR "oprofile: CPU%u enabling wrong PMNC counter"
  42. " %d\n", smp_processor_id(), cnt);
  43. return -1;
  44. }
  45. if (cnt == CCNT)
  46. val = CNTENS_C;
  47. else
  48. val = (1 << (cnt - CNT0));
  49. val &= CNTENS_MASK;
  50. asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
  51. return cnt;
  52. }
  53. static inline u32 armv7_pmnc_disable_counter(unsigned int cnt)
  54. {
  55. u32 val;
  56. if (cnt >= CNTMAX) {
  57. printk(KERN_ERR "oprofile: CPU%u disabling wrong PMNC counter"
  58. " %d\n", smp_processor_id(), cnt);
  59. return -1;
  60. }
  61. if (cnt == CCNT)
  62. val = CNTENC_C;
  63. else
  64. val = (1 << (cnt - CNT0));
  65. val &= CNTENC_MASK;
  66. asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val));
  67. return cnt;
  68. }
  69. static inline u32 armv7_pmnc_enable_intens(unsigned int cnt)
  70. {
  71. u32 val;
  72. if (cnt >= CNTMAX) {
  73. printk(KERN_ERR "oprofile: CPU%u enabling wrong PMNC counter"
  74. " interrupt enable %d\n", smp_processor_id(), cnt);
  75. return -1;
  76. }
  77. if (cnt == CCNT)
  78. val = INTENS_C;
  79. else
  80. val = (1 << (cnt - CNT0));
  81. val &= INTENS_MASK;
  82. asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (val));
  83. return cnt;
  84. }
  85. static inline u32 armv7_pmnc_getreset_flags(void)
  86. {
  87. u32 val;
  88. /* Read */
  89. asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
  90. /* Write to clear flags */
  91. val &= FLAG_MASK;
  92. asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (val));
  93. return val;
  94. }
  95. static inline int armv7_pmnc_select_counter(unsigned int cnt)
  96. {
  97. u32 val;
  98. if ((cnt == CCNT) || (cnt >= CNTMAX)) {
  99. printk(KERN_ERR "oprofile: CPU%u selecting wrong PMNC counteri"
  100. " %d\n", smp_processor_id(), cnt);
  101. return -1;
  102. }
  103. val = (cnt - CNT0) & SELECT_MASK;
  104. asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val));
  105. return cnt;
  106. }
  107. static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val)
  108. {
  109. if (armv7_pmnc_select_counter(cnt) == cnt) {
  110. val &= EVTSEL_MASK;
  111. asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
  112. }
  113. }
  114. static void armv7_pmnc_reset_counter(unsigned int cnt)
  115. {
  116. u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
  117. u32 val = -(u32)counter_config[cpu_cnt].count;
  118. switch (cnt) {
  119. case CCNT:
  120. armv7_pmnc_disable_counter(cnt);
  121. asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val));
  122. if (cnt_en[cnt] != 0)
  123. armv7_pmnc_enable_counter(cnt);
  124. break;
  125. case CNT0:
  126. case CNT1:
  127. case CNT2:
  128. case CNT3:
  129. armv7_pmnc_disable_counter(cnt);
  130. if (armv7_pmnc_select_counter(cnt) == cnt)
  131. asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val));
  132. if (cnt_en[cnt] != 0)
  133. armv7_pmnc_enable_counter(cnt);
  134. break;
  135. default:
  136. printk(KERN_ERR "oprofile: CPU%u resetting wrong PMNC counter"
  137. " %d\n", smp_processor_id(), cnt);
  138. break;
  139. }
  140. }
  141. int armv7_setup_pmnc(void)
  142. {
  143. unsigned int cnt;
  144. if (armv7_pmnc_read() & PMNC_E) {
  145. printk(KERN_ERR "oprofile: CPU%u PMNC still enabled when setup"
  146. " new event counter.\n", smp_processor_id());
  147. return -EBUSY;
  148. }
  149. /*
  150. * Initialize & Reset PMNC: C bit, D bit and P bit.
  151. * Note: Using a slower count for CCNT (D bit: divide by 64) results
  152. * in a more stable system
  153. */
  154. armv7_pmnc_write(PMNC_P | PMNC_C | PMNC_D);
  155. for (cnt = CCNT; cnt < CNTMAX; cnt++) {
  156. unsigned long event;
  157. u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
  158. /*
  159. * Disable counter
  160. */
  161. armv7_pmnc_disable_counter(cnt);
  162. cnt_en[cnt] = 0;
  163. if (!counter_config[cpu_cnt].enabled)
  164. continue;
  165. event = counter_config[cpu_cnt].event & 255;
  166. /*
  167. * Set event (if destined for PMNx counters)
  168. * We don't need to set the event if it's a cycle count
  169. */
  170. if (cnt != CCNT)
  171. armv7_pmnc_write_evtsel(cnt, event);
  172. /*
  173. * Enable interrupt for this counter
  174. */
  175. armv7_pmnc_enable_intens(cnt);
  176. /*
  177. * Reset counter
  178. */
  179. armv7_pmnc_reset_counter(cnt);
  180. /*
  181. * Enable counter
  182. */
  183. armv7_pmnc_enable_counter(cnt);
  184. cnt_en[cnt] = 1;
  185. }
  186. return 0;
  187. }
  188. static inline void armv7_start_pmnc(void)
  189. {
  190. armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
  191. }
  192. static inline void armv7_stop_pmnc(void)
  193. {
  194. armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
  195. }
  196. /*
  197. * CPU counters' IRQ handler (one IRQ per CPU)
  198. */
  199. static irqreturn_t armv7_pmnc_interrupt(int irq, void *arg)
  200. {
  201. struct pt_regs *regs = get_irq_regs();
  202. unsigned int cnt;
  203. u32 flags;
  204. /*
  205. * Stop IRQ generation
  206. */
  207. armv7_stop_pmnc();
  208. /*
  209. * Get and reset overflow status flags
  210. */
  211. flags = armv7_pmnc_getreset_flags();
  212. /*
  213. * Cycle counter
  214. */
  215. if (flags & FLAG_C) {
  216. u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), CCNT);
  217. armv7_pmnc_reset_counter(CCNT);
  218. oprofile_add_sample(regs, cpu_cnt);
  219. }
  220. /*
  221. * PMNC counters 0:3
  222. */
  223. for (cnt = CNT0; cnt < CNTMAX; cnt++) {
  224. if (flags & (1 << (cnt - CNT0))) {
  225. u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
  226. armv7_pmnc_reset_counter(cnt);
  227. oprofile_add_sample(regs, cpu_cnt);
  228. }
  229. }
  230. /*
  231. * Allow IRQ generation
  232. */
  233. armv7_start_pmnc();
  234. return IRQ_HANDLED;
  235. }
  236. int armv7_request_interrupts(int *irqs, int nr)
  237. {
  238. unsigned int i;
  239. int ret = 0;
  240. for (i = 0; i < nr; i++) {
  241. ret = request_irq(irqs[i], armv7_pmnc_interrupt,
  242. IRQF_DISABLED, "CP15 PMNC", NULL);
  243. if (ret != 0) {
  244. printk(KERN_ERR "oprofile: unable to request IRQ%u"
  245. " for ARMv7\n",
  246. irqs[i]);
  247. break;
  248. }
  249. }
  250. if (i != nr)
  251. while (i-- != 0)
  252. free_irq(irqs[i], NULL);
  253. return ret;
  254. }
  255. void armv7_release_interrupts(int *irqs, int nr)
  256. {
  257. unsigned int i;
  258. for (i = 0; i < nr; i++)
  259. free_irq(irqs[i], NULL);
  260. }
  261. #ifdef DEBUG
  262. static void armv7_pmnc_dump_regs(void)
  263. {
  264. u32 val;
  265. unsigned int cnt;
  266. printk(KERN_INFO "PMNC registers dump:\n");
  267. asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
  268. printk(KERN_INFO "PMNC =0x%08x\n", val);
  269. asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val));
  270. printk(KERN_INFO "CNTENS=0x%08x\n", val);
  271. asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val));
  272. printk(KERN_INFO "INTENS=0x%08x\n", val);
  273. asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
  274. printk(KERN_INFO "FLAGS =0x%08x\n", val);
  275. asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val));
  276. printk(KERN_INFO "SELECT=0x%08x\n", val);
  277. asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
  278. printk(KERN_INFO "CCNT =0x%08x\n", val);
  279. for (cnt = CNT0; cnt < CNTMAX; cnt++) {
  280. armv7_pmnc_select_counter(cnt);
  281. asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val));
  282. printk(KERN_INFO "CNT[%d] count =0x%08x\n", cnt-CNT0, val);
  283. asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val));
  284. printk(KERN_INFO "CNT[%d] evtsel=0x%08x\n", cnt-CNT0, val);
  285. }
  286. }
  287. #endif
  288. static int irqs[] = {
  289. #ifdef CONFIG_ARCH_OMAP3
  290. INT_34XX_BENCH_MPU_EMUL,
  291. #endif
  292. };
  293. static void armv7_pmnc_stop(void)
  294. {
  295. #ifdef DEBUG
  296. armv7_pmnc_dump_regs();
  297. #endif
  298. armv7_stop_pmnc();
  299. armv7_release_interrupts(irqs, ARRAY_SIZE(irqs));
  300. }
  301. static int armv7_pmnc_start(void)
  302. {
  303. int ret;
  304. #ifdef DEBUG
  305. armv7_pmnc_dump_regs();
  306. #endif
  307. ret = armv7_request_interrupts(irqs, ARRAY_SIZE(irqs));
  308. if (ret >= 0)
  309. armv7_start_pmnc();
  310. return ret;
  311. }
  312. static int armv7_detect_pmnc(void)
  313. {
  314. return 0;
  315. }
  316. struct op_arm_model_spec op_armv7_spec = {
  317. .init = armv7_detect_pmnc,
  318. .num_counters = 5,
  319. .setup_ctrs = armv7_setup_pmnc,
  320. .start = armv7_pmnc_start,
  321. .stop = armv7_pmnc_stop,
  322. .name = "arm/armv7",
  323. };