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