op_model_v7.c 8.1 KB

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