oct_ilm.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. #include <linux/fs.h>
  3. #include <linux/interrupt.h>
  4. #include <asm/octeon/octeon.h>
  5. #include <asm/octeon/cvmx-ciu-defs.h>
  6. #include <asm/octeon/cvmx.h>
  7. #include <linux/debugfs.h>
  8. #include <linux/kernel.h>
  9. #include <linux/module.h>
  10. #include <linux/seq_file.h>
  11. #define TIMER_NUM 3
  12. static bool reset_stats;
  13. struct latency_info {
  14. u64 io_interval;
  15. u64 cpu_interval;
  16. u64 timer_start1;
  17. u64 timer_start2;
  18. u64 max_latency;
  19. u64 min_latency;
  20. u64 latency_sum;
  21. u64 average_latency;
  22. u64 interrupt_cnt;
  23. };
  24. static struct latency_info li;
  25. static struct dentry *dir;
  26. static int oct_ilm_show(struct seq_file *m, void *v)
  27. {
  28. u64 cpuclk, avg, max, min;
  29. struct latency_info curr_li = li;
  30. cpuclk = octeon_get_clock_rate();
  31. max = (curr_li.max_latency * 1000000000) / cpuclk;
  32. min = (curr_li.min_latency * 1000000000) / cpuclk;
  33. avg = (curr_li.latency_sum * 1000000000) / (cpuclk * curr_li.interrupt_cnt);
  34. seq_printf(m, "cnt: %10lld, avg: %7lld ns, max: %7lld ns, min: %7lld ns\n",
  35. curr_li.interrupt_cnt, avg, max, min);
  36. return 0;
  37. }
  38. DEFINE_SHOW_ATTRIBUTE(oct_ilm);
  39. static int reset_statistics(void *data, u64 value)
  40. {
  41. reset_stats = true;
  42. return 0;
  43. }
  44. DEFINE_DEBUGFS_ATTRIBUTE(reset_statistics_ops, NULL, reset_statistics, "%llu\n");
  45. static void init_debugfs(void)
  46. {
  47. dir = debugfs_create_dir("oct_ilm", 0);
  48. debugfs_create_file("statistics", 0222, dir, NULL, &oct_ilm_fops);
  49. debugfs_create_file("reset", 0222, dir, NULL, &reset_statistics_ops);
  50. }
  51. static void init_latency_info(struct latency_info *li, int startup)
  52. {
  53. /* interval in milli seconds after which the interrupt will
  54. * be triggered
  55. */
  56. int interval = 1;
  57. if (startup) {
  58. /* Calculating by the amounts io clock and cpu clock would
  59. * increment in interval amount of ms
  60. */
  61. li->io_interval = (octeon_get_io_clock_rate() * interval) / 1000;
  62. li->cpu_interval = (octeon_get_clock_rate() * interval) / 1000;
  63. }
  64. li->timer_start1 = 0;
  65. li->timer_start2 = 0;
  66. li->max_latency = 0;
  67. li->min_latency = (u64)-1;
  68. li->latency_sum = 0;
  69. li->interrupt_cnt = 0;
  70. }
  71. static void start_timer(int timer, u64 interval)
  72. {
  73. union cvmx_ciu_timx timx;
  74. unsigned long flags;
  75. timx.u64 = 0;
  76. timx.s.one_shot = 1;
  77. timx.s.len = interval;
  78. raw_local_irq_save(flags);
  79. li.timer_start1 = read_c0_cvmcount();
  80. cvmx_write_csr(CVMX_CIU_TIMX(timer), timx.u64);
  81. /* Read it back to force wait until register is written. */
  82. timx.u64 = cvmx_read_csr(CVMX_CIU_TIMX(timer));
  83. li.timer_start2 = read_c0_cvmcount();
  84. raw_local_irq_restore(flags);
  85. }
  86. static irqreturn_t cvm_oct_ciu_timer_interrupt(int cpl, void *dev_id)
  87. {
  88. u64 last_latency;
  89. u64 last_int_cnt;
  90. if (reset_stats) {
  91. init_latency_info(&li, 0);
  92. reset_stats = false;
  93. } else {
  94. last_int_cnt = read_c0_cvmcount();
  95. last_latency = last_int_cnt - (li.timer_start1 + li.cpu_interval);
  96. li.interrupt_cnt++;
  97. li.latency_sum += last_latency;
  98. if (last_latency > li.max_latency)
  99. li.max_latency = last_latency;
  100. if (last_latency < li.min_latency)
  101. li.min_latency = last_latency;
  102. }
  103. start_timer(TIMER_NUM, li.io_interval);
  104. return IRQ_HANDLED;
  105. }
  106. static void disable_timer(int timer)
  107. {
  108. union cvmx_ciu_timx timx;
  109. timx.s.one_shot = 0;
  110. timx.s.len = 0;
  111. cvmx_write_csr(CVMX_CIU_TIMX(timer), timx.u64);
  112. /* Read it back to force immediate write of timer register*/
  113. timx.u64 = cvmx_read_csr(CVMX_CIU_TIMX(timer));
  114. }
  115. static __init int oct_ilm_module_init(void)
  116. {
  117. int rc;
  118. int irq = OCTEON_IRQ_TIMER0 + TIMER_NUM;
  119. init_debugfs();
  120. rc = request_irq(irq, cvm_oct_ciu_timer_interrupt, IRQF_NO_THREAD,
  121. "oct_ilm", 0);
  122. if (rc) {
  123. WARN(1, "Could not acquire IRQ %d", irq);
  124. goto err_irq;
  125. }
  126. init_latency_info(&li, 1);
  127. start_timer(TIMER_NUM, li.io_interval);
  128. return 0;
  129. err_irq:
  130. debugfs_remove_recursive(dir);
  131. return rc;
  132. }
  133. static __exit void oct_ilm_module_exit(void)
  134. {
  135. disable_timer(TIMER_NUM);
  136. debugfs_remove_recursive(dir);
  137. free_irq(OCTEON_IRQ_TIMER0 + TIMER_NUM, 0);
  138. }
  139. module_exit(oct_ilm_module_exit);
  140. module_init(oct_ilm_module_init);
  141. MODULE_AUTHOR("Venkat Subbiah, Cavium");
  142. MODULE_DESCRIPTION("Measures interrupt latency on Octeon chips.");
  143. MODULE_LICENSE("GPL");