root/arch/mips/cavium-octeon/oct_ilm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. show_latency
  2. oct_ilm_open
  3. reset_statistics
  4. init_debugfs
  5. init_latency_info
  6. start_timer
  7. cvm_oct_ciu_timer_interrupt
  8. disable_timer
  9. oct_ilm_module_init
  10. oct_ilm_module_exit

   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 
  12 #define TIMER_NUM 3
  13 
  14 static bool reset_stats;
  15 
  16 struct latency_info {
  17         u64 io_interval;
  18         u64 cpu_interval;
  19         u64 timer_start1;
  20         u64 timer_start2;
  21         u64 max_latency;
  22         u64 min_latency;
  23         u64 latency_sum;
  24         u64 average_latency;
  25         u64 interrupt_cnt;
  26 };
  27 
  28 static struct latency_info li;
  29 static struct dentry *dir;
  30 
  31 static int show_latency(struct seq_file *m, void *v)
  32 {
  33         u64 cpuclk, avg, max, min;
  34         struct latency_info curr_li = li;
  35 
  36         cpuclk = octeon_get_clock_rate();
  37 
  38         max = (curr_li.max_latency * 1000000000) / cpuclk;
  39         min = (curr_li.min_latency * 1000000000) / cpuclk;
  40         avg = (curr_li.latency_sum * 1000000000) / (cpuclk * curr_li.interrupt_cnt);
  41 
  42         seq_printf(m, "cnt: %10lld, avg: %7lld ns, max: %7lld ns, min: %7lld ns\n",
  43                    curr_li.interrupt_cnt, avg, max, min);
  44         return 0;
  45 }
  46 
  47 static int oct_ilm_open(struct inode *inode, struct file *file)
  48 {
  49         return single_open(file, show_latency, NULL);
  50 }
  51 
  52 static const struct file_operations oct_ilm_ops = {
  53         .open = oct_ilm_open,
  54         .read = seq_read,
  55         .llseek = seq_lseek,
  56         .release = single_release,
  57 };
  58 
  59 static int reset_statistics(void *data, u64 value)
  60 {
  61         reset_stats = true;
  62         return 0;
  63 }
  64 
  65 DEFINE_SIMPLE_ATTRIBUTE(reset_statistics_ops, NULL, reset_statistics, "%llu\n");
  66 
  67 static void init_debugfs(void)
  68 {
  69         dir = debugfs_create_dir("oct_ilm", 0);
  70         debugfs_create_file("statistics", 0222, dir, NULL, &oct_ilm_ops);
  71         debugfs_create_file("reset", 0222, dir, NULL, &reset_statistics_ops);
  72 }
  73 
  74 static void init_latency_info(struct latency_info *li, int startup)
  75 {
  76         /* interval in milli seconds after which the interrupt will
  77          * be triggered
  78          */
  79         int interval = 1;
  80 
  81         if (startup) {
  82                 /* Calculating by the amounts io clock and cpu clock would
  83                  *  increment in interval amount of ms
  84                  */
  85                 li->io_interval = (octeon_get_io_clock_rate() * interval) / 1000;
  86                 li->cpu_interval = (octeon_get_clock_rate() * interval) / 1000;
  87         }
  88         li->timer_start1 = 0;
  89         li->timer_start2 = 0;
  90         li->max_latency = 0;
  91         li->min_latency = (u64)-1;
  92         li->latency_sum = 0;
  93         li->interrupt_cnt = 0;
  94 }
  95 
  96 
  97 static void start_timer(int timer, u64 interval)
  98 {
  99         union cvmx_ciu_timx timx;
 100         unsigned long flags;
 101 
 102         timx.u64 = 0;
 103         timx.s.one_shot = 1;
 104         timx.s.len = interval;
 105         raw_local_irq_save(flags);
 106         li.timer_start1 = read_c0_cvmcount();
 107         cvmx_write_csr(CVMX_CIU_TIMX(timer), timx.u64);
 108         /* Read it back to force wait until register is written. */
 109         timx.u64 = cvmx_read_csr(CVMX_CIU_TIMX(timer));
 110         li.timer_start2 = read_c0_cvmcount();
 111         raw_local_irq_restore(flags);
 112 }
 113 
 114 
 115 static irqreturn_t cvm_oct_ciu_timer_interrupt(int cpl, void *dev_id)
 116 {
 117         u64 last_latency;
 118         u64 last_int_cnt;
 119 
 120         if (reset_stats) {
 121                 init_latency_info(&li, 0);
 122                 reset_stats = false;
 123         } else {
 124                 last_int_cnt = read_c0_cvmcount();
 125                 last_latency = last_int_cnt - (li.timer_start1 + li.cpu_interval);
 126                 li.interrupt_cnt++;
 127                 li.latency_sum += last_latency;
 128                 if (last_latency > li.max_latency)
 129                         li.max_latency = last_latency;
 130                 if (last_latency < li.min_latency)
 131                         li.min_latency = last_latency;
 132         }
 133         start_timer(TIMER_NUM, li.io_interval);
 134         return IRQ_HANDLED;
 135 }
 136 
 137 static void disable_timer(int timer)
 138 {
 139         union cvmx_ciu_timx timx;
 140 
 141         timx.s.one_shot = 0;
 142         timx.s.len = 0;
 143         cvmx_write_csr(CVMX_CIU_TIMX(timer), timx.u64);
 144         /* Read it back to force immediate write of timer register*/
 145         timx.u64 = cvmx_read_csr(CVMX_CIU_TIMX(timer));
 146 }
 147 
 148 static __init int oct_ilm_module_init(void)
 149 {
 150         int rc;
 151         int irq = OCTEON_IRQ_TIMER0 + TIMER_NUM;
 152 
 153         init_debugfs();
 154 
 155         rc = request_irq(irq, cvm_oct_ciu_timer_interrupt, IRQF_NO_THREAD,
 156                          "oct_ilm", 0);
 157         if (rc) {
 158                 WARN(1, "Could not acquire IRQ %d", irq);
 159                 goto err_irq;
 160         }
 161 
 162         init_latency_info(&li, 1);
 163         start_timer(TIMER_NUM, li.io_interval);
 164 
 165         return 0;
 166 err_irq:
 167         debugfs_remove_recursive(dir);
 168         return rc;
 169 }
 170 
 171 static __exit void oct_ilm_module_exit(void)
 172 {
 173         disable_timer(TIMER_NUM);
 174         debugfs_remove_recursive(dir);
 175         free_irq(OCTEON_IRQ_TIMER0 + TIMER_NUM, 0);
 176 }
 177 
 178 module_exit(oct_ilm_module_exit);
 179 module_init(oct_ilm_module_init);
 180 MODULE_AUTHOR("Venkat Subbiah, Cavium");
 181 MODULE_DESCRIPTION("Measures interrupt latency on Octeon chips.");
 182 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */