op_model_v7.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  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. /* Initialize & Reset PMNC: C bit and P bit */
  150. armv7_pmnc_write(PMNC_P | PMNC_C);
  151. for (cnt = CCNT; cnt < CNTMAX; cnt++) {
  152. unsigned long event;
  153. u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
  154. /*
  155. * Disable counter
  156. */
  157. armv7_pmnc_disable_counter(cnt);
  158. cnt_en[cnt] = 0;
  159. if (!counter_config[cpu_cnt].enabled)
  160. continue;
  161. event = counter_config[cpu_cnt].event & 255;
  162. /*
  163. * Set event (if destined for PMNx counters)
  164. * We don't need to set the event if it's a cycle count
  165. */
  166. if (cnt != CCNT)
  167. armv7_pmnc_write_evtsel(cnt, event);
  168. /*
  169. * Enable interrupt for this counter
  170. */
  171. armv7_pmnc_enable_intens(cnt);
  172. /*
  173. * Reset counter
  174. */
  175. armv7_pmnc_reset_counter(cnt);
  176. /*
  177. * Enable counter
  178. */
  179. armv7_pmnc_enable_counter(cnt);
  180. cnt_en[cnt] = 1;
  181. }
  182. return 0;
  183. }
  184. static inline void armv7_start_pmnc(void)
  185. {
  186. armv7_pmnc_write(armv7_pmnc_read() | PMNC_E);
  187. }
  188. static inline void armv7_stop_pmnc(void)
  189. {
  190. armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E);
  191. }
  192. /*
  193. * CPU counters' IRQ handler (one IRQ per CPU)
  194. */
  195. static irqreturn_t armv7_pmnc_interrupt(int irq, void *arg)
  196. {
  197. struct pt_regs *regs = get_irq_regs();
  198. unsigned int cnt;
  199. u32 flags;
  200. /*
  201. * Stop IRQ generation
  202. */
  203. armv7_stop_pmnc();
  204. /*
  205. * Get and reset overflow status flags
  206. */
  207. flags = armv7_pmnc_getreset_flags();
  208. /*
  209. * Cycle counter
  210. */
  211. if (flags & FLAG_C) {
  212. u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), CCNT);
  213. armv7_pmnc_reset_counter(CCNT);
  214. oprofile_add_sample(regs, cpu_cnt);
  215. }
  216. /*
  217. * PMNC counters 0:3
  218. */
  219. for (cnt = CNT0; cnt < CNTMAX; cnt++) {
  220. if (flags & (1 << (cnt - CNT0))) {
  221. u32 cpu_cnt = CPU_COUNTER(smp_processor_id(), cnt);
  222. armv7_pmnc_reset_counter(cnt);
  223. oprofile_add_sample(regs, cpu_cnt);
  224. }
  225. }
  226. /*
  227. * Allow IRQ generation
  228. */
  229. armv7_start_pmnc();
  230. return IRQ_HANDLED;
  231. }
  232. int armv7_request_interrupts(int *irqs, int nr)
  233. {
  234. unsigned int i;
  235. int ret = 0;
  236. for (i = 0; i < nr; i++) {
  237. ret = request_irq(irqs[i], armv7_pmnc_interrupt,
  238. IRQF_DISABLED, "CP15 PMNC", NULL);
  239. if (ret != 0) {
  240. printk(KERN_ERR "oprofile: unable to request IRQ%u"
  241. " for ARMv7\n",
  242. irqs[i]);
  243. break;
  244. }
  245. }
  246. if (i != nr)
  247. while (i-- != 0)
  248. free_irq(irqs[i], NULL);
  249. return ret;
  250. }
  251. void armv7_release_interrupts(int *irqs, int nr)
  252. {
  253. unsigned int i;
  254. for (i = 0; i < nr; i++)
  255. free_irq(irqs[i], NULL);
  256. }
  257. #ifdef DEBUG
  258. static void armv7_pmnc_dump_regs(void)
  259. {
  260. u32 val;
  261. unsigned int cnt;
  262. printk(KERN_INFO "PMNC registers dump:\n");
  263. asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
  264. printk(KERN_INFO "PMNC =0x%08x\n", val);
  265. asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val));
  266. printk(KERN_INFO "CNTENS=0x%08x\n", val);
  267. asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val));
  268. printk(KERN_INFO "INTENS=0x%08x\n", val);
  269. asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));
  270. printk(KERN_INFO "FLAGS =0x%08x\n", val);
  271. asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val));
  272. printk(KERN_INFO "SELECT=0x%08x\n", val);
  273. asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
  274. printk(KERN_INFO "CCNT =0x%08x\n", val);
  275. for (cnt = CNT0; cnt < CNTMAX; cnt++) {
  276. armv7_pmnc_select_counter(cnt);
  277. asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val));
  278. printk(KERN_INFO "CNT[%d] count =0x%08x\n", cnt-CNT0, val);
  279. asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val));
  280. printk(KERN_INFO "CNT[%d] evtsel=0x%08x\n", cnt-CNT0, val);
  281. }
  282. }
  283. #endif
  284. static int irqs[] = {
  285. #ifdef CONFIG_ARCH_OMAP3
  286. INT_34XX_BENCH_MPU_EMUL,
  287. #endif
  288. };
  289. static void armv7_pmnc_stop(void)
  290. {
  291. #ifdef DEBUG
  292. armv7_pmnc_dump_regs();
  293. #endif
  294. armv7_stop_pmnc();
  295. armv7_release_interrupts(irqs, ARRAY_SIZE(irqs));
  296. }
  297. static int armv7_pmnc_start(void)
  298. {
  299. int ret;
  300. #ifdef DEBUG
  301. armv7_pmnc_dump_regs();
  302. #endif
  303. ret = armv7_request_interrupts(irqs, ARRAY_SIZE(irqs));
  304. if (ret >= 0)
  305. armv7_start_pmnc();
  306. return ret;
  307. }
  308. static int armv7_detect_pmnc(void)
  309. {
  310. return 0;
  311. }
  312. struct op_arm_model_spec op_armv7_spec = {
  313. .init = armv7_detect_pmnc,
  314. .num_counters = 5,
  315. .setup_ctrs = armv7_setup_pmnc,
  316. .start = armv7_pmnc_start,
  317. .stop = armv7_pmnc_stop,
  318. .name = "arm/armv7",
  319. };