oct_ilm.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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 show_latency(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. static int oct_ilm_open(struct inode *inode, struct file *file)
  39. {
  40. return single_open(file, show_latency, NULL);
  41. }
  42. static const struct file_operations oct_ilm_ops = {
  43. .open = oct_ilm_open,
  44. .read = seq_read,
  45. .llseek = seq_lseek,
  46. .release = single_release,
  47. };
  48. static int reset_statistics(void *data, u64 value)
  49. {
  50. reset_stats = true;
  51. return 0;
  52. }
  53. DEFINE_SIMPLE_ATTRIBUTE(reset_statistics_ops, NULL, reset_statistics, "%llu\n");
  54. static void init_debugfs(void)
  55. {
  56. dir = debugfs_create_dir("oct_ilm", 0);
  57. debugfs_create_file("statistics", 0222, dir, NULL, &oct_ilm_ops);
  58. debugfs_create_file("reset", 0222, dir, NULL, &reset_statistics_ops);
  59. }
  60. static void init_latency_info(struct latency_info *li, int startup)
  61. {
  62. /* interval in milli seconds after which the interrupt will
  63. * be triggered
  64. */
  65. int interval = 1;
  66. if (startup) {
  67. /* Calculating by the amounts io clock and cpu clock would
  68. * increment in interval amount of ms
  69. */
  70. li->io_interval = (octeon_get_io_clock_rate() * interval) / 1000;
  71. li->cpu_interval = (octeon_get_clock_rate() * interval) / 1000;
  72. }
  73. li->timer_start1 = 0;
  74. li->timer_start2 = 0;
  75. li->max_latency = 0;
  76. li->min_latency = (u64)-1;
  77. li->latency_sum = 0;
  78. li->interrupt_cnt = 0;
  79. }
  80. static void start_timer(int timer, u64 interval)
  81. {
  82. union cvmx_ciu_timx timx;
  83. unsigned long flags;
  84. timx.u64 = 0;
  85. timx.s.one_shot = 1;
  86. timx.s.len = interval;
  87. raw_local_irq_save(flags);
  88. li.timer_start1 = read_c0_cvmcount();
  89. cvmx_write_csr(CVMX_CIU_TIMX(timer), timx.u64);
  90. /* Read it back to force wait until register is written. */
  91. timx.u64 = cvmx_read_csr(CVMX_CIU_TIMX(timer));
  92. li.timer_start2 = read_c0_cvmcount();
  93. raw_local_irq_restore(flags);
  94. }
  95. static irqreturn_t cvm_oct_ciu_timer_interrupt(int cpl, void *dev_id)
  96. {
  97. u64 last_latency;
  98. u64 last_int_cnt;
  99. if (reset_stats) {
  100. init_latency_info(&li, 0);
  101. reset_stats = false;
  102. } else {
  103. last_int_cnt = read_c0_cvmcount();
  104. last_latency = last_int_cnt - (li.timer_start1 + li.cpu_interval);
  105. li.interrupt_cnt++;
  106. li.latency_sum += last_latency;
  107. if (last_latency > li.max_latency)
  108. li.max_latency = last_latency;
  109. if (last_latency < li.min_latency)
  110. li.min_latency = last_latency;
  111. }
  112. start_timer(TIMER_NUM, li.io_interval);
  113. return IRQ_HANDLED;
  114. }
  115. static void disable_timer(int timer)
  116. {
  117. union cvmx_ciu_timx timx;
  118. timx.s.one_shot = 0;
  119. timx.s.len = 0;
  120. cvmx_write_csr(CVMX_CIU_TIMX(timer), timx.u64);
  121. /* Read it back to force immediate write of timer register*/
  122. timx.u64 = cvmx_read_csr(CVMX_CIU_TIMX(timer));
  123. }
  124. static __init int oct_ilm_module_init(void)
  125. {
  126. int rc;
  127. int irq = OCTEON_IRQ_TIMER0 + TIMER_NUM;
  128. init_debugfs();
  129. rc = request_irq(irq, cvm_oct_ciu_timer_interrupt, IRQF_NO_THREAD,
  130. "oct_ilm", 0);
  131. if (rc) {
  132. WARN(1, "Could not acquire IRQ %d", irq);
  133. goto err_irq;
  134. }
  135. init_latency_info(&li, 1);
  136. start_timer(TIMER_NUM, li.io_interval);
  137. return 0;
  138. err_irq:
  139. debugfs_remove_recursive(dir);
  140. return rc;
  141. }
  142. static __exit void oct_ilm_module_exit(void)
  143. {
  144. disable_timer(TIMER_NUM);
  145. debugfs_remove_recursive(dir);
  146. free_irq(OCTEON_IRQ_TIMER0 + TIMER_NUM, 0);
  147. }
  148. module_exit(oct_ilm_module_exit);
  149. module_init(oct_ilm_module_init);
  150. MODULE_AUTHOR("Venkat Subbiah, Cavium");
  151. MODULE_DESCRIPTION("Measures interrupt latency on Octeon chips.");
  152. MODULE_LICENSE("GPL");