Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/mips/cavium-octeon/oct_ilm.c
26442 views
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 oct_ilm_show(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
DEFINE_SHOW_ATTRIBUTE(oct_ilm);
47
48
static int reset_statistics(void *data, u64 value)
49
{
50
reset_stats = true;
51
return 0;
52
}
53
54
DEFINE_DEBUGFS_ATTRIBUTE(reset_statistics_ops, NULL, reset_statistics, "%llu\n");
55
56
static void init_debugfs(void)
57
{
58
dir = debugfs_create_dir("oct_ilm", 0);
59
debugfs_create_file("statistics", 0222, dir, NULL, &oct_ilm_fops);
60
debugfs_create_file("reset", 0222, dir, NULL, &reset_statistics_ops);
61
}
62
63
static void init_latency_info(struct latency_info *li, int startup)
64
{
65
/* interval in milli seconds after which the interrupt will
66
* be triggered
67
*/
68
int interval = 1;
69
70
if (startup) {
71
/* Calculating by the amounts io clock and cpu clock would
72
* increment in interval amount of ms
73
*/
74
li->io_interval = (octeon_get_io_clock_rate() * interval) / 1000;
75
li->cpu_interval = (octeon_get_clock_rate() * interval) / 1000;
76
}
77
li->timer_start1 = 0;
78
li->timer_start2 = 0;
79
li->max_latency = 0;
80
li->min_latency = (u64)-1;
81
li->latency_sum = 0;
82
li->interrupt_cnt = 0;
83
}
84
85
86
static void start_timer(int timer, u64 interval)
87
{
88
union cvmx_ciu_timx timx;
89
unsigned long flags;
90
91
timx.u64 = 0;
92
timx.s.one_shot = 1;
93
timx.s.len = interval;
94
raw_local_irq_save(flags);
95
li.timer_start1 = read_c0_cvmcount();
96
cvmx_write_csr(CVMX_CIU_TIMX(timer), timx.u64);
97
/* Read it back to force wait until register is written. */
98
timx.u64 = cvmx_read_csr(CVMX_CIU_TIMX(timer));
99
li.timer_start2 = read_c0_cvmcount();
100
raw_local_irq_restore(flags);
101
}
102
103
104
static irqreturn_t cvm_oct_ciu_timer_interrupt(int cpl, void *dev_id)
105
{
106
u64 last_latency;
107
u64 last_int_cnt;
108
109
if (reset_stats) {
110
init_latency_info(&li, 0);
111
reset_stats = false;
112
} else {
113
last_int_cnt = read_c0_cvmcount();
114
last_latency = last_int_cnt - (li.timer_start1 + li.cpu_interval);
115
li.interrupt_cnt++;
116
li.latency_sum += last_latency;
117
if (last_latency > li.max_latency)
118
li.max_latency = last_latency;
119
if (last_latency < li.min_latency)
120
li.min_latency = last_latency;
121
}
122
start_timer(TIMER_NUM, li.io_interval);
123
return IRQ_HANDLED;
124
}
125
126
static void disable_timer(int timer)
127
{
128
union cvmx_ciu_timx timx;
129
130
timx.s.one_shot = 0;
131
timx.s.len = 0;
132
cvmx_write_csr(CVMX_CIU_TIMX(timer), timx.u64);
133
/* Read it back to force immediate write of timer register*/
134
timx.u64 = cvmx_read_csr(CVMX_CIU_TIMX(timer));
135
}
136
137
static __init int oct_ilm_module_init(void)
138
{
139
int rc;
140
int irq = OCTEON_IRQ_TIMER0 + TIMER_NUM;
141
142
init_debugfs();
143
144
rc = request_irq(irq, cvm_oct_ciu_timer_interrupt, IRQF_NO_THREAD,
145
"oct_ilm", 0);
146
if (rc) {
147
WARN(1, "Could not acquire IRQ %d", irq);
148
goto err_irq;
149
}
150
151
init_latency_info(&li, 1);
152
start_timer(TIMER_NUM, li.io_interval);
153
154
return 0;
155
err_irq:
156
debugfs_remove_recursive(dir);
157
return rc;
158
}
159
160
static __exit void oct_ilm_module_exit(void)
161
{
162
disable_timer(TIMER_NUM);
163
debugfs_remove_recursive(dir);
164
free_irq(OCTEON_IRQ_TIMER0 + TIMER_NUM, 0);
165
}
166
167
module_exit(oct_ilm_module_exit);
168
module_init(oct_ilm_module_init);
169
MODULE_AUTHOR("Venkat Subbiah, Cavium");
170
MODULE_DESCRIPTION("Measures interrupt latency on Octeon chips.");
171
MODULE_LICENSE("GPL");
172
173