Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/mips/kernel/cevt-r4k.c
50518 views
1
/*
2
* This file is subject to the terms and conditions of the GNU General Public
3
* License. See the file "COPYING" in the main directory of this archive
4
* for more details.
5
*
6
* Copyright (C) 2007 MIPS Technologies, Inc.
7
* Copyright (C) 2007 Ralf Baechle <[email protected]>
8
*/
9
#include <linux/clockchips.h>
10
#include <linux/interrupt.h>
11
#include <linux/cpufreq.h>
12
#include <linux/percpu.h>
13
#include <linux/smp.h>
14
#include <linux/irq.h>
15
16
#include <asm/time.h>
17
#include <asm/cevt-r4k.h>
18
19
static int mips_next_event(unsigned long delta,
20
struct clock_event_device *evt)
21
{
22
unsigned int cnt;
23
int res;
24
25
cnt = read_c0_count();
26
cnt += delta;
27
write_c0_compare(cnt);
28
res = ((int)(read_c0_count() - cnt) >= 0) ? -ETIME : 0;
29
return res;
30
}
31
32
/**
33
* calculate_min_delta() - Calculate a good minimum delta for mips_next_event().
34
*
35
* Running under virtualisation can introduce overhead into mips_next_event() in
36
* the form of hypervisor emulation of CP0_Count/CP0_Compare registers,
37
* potentially with an unnatural frequency, which makes a fixed min_delta_ns
38
* value inappropriate as it may be too small.
39
*
40
* It can also introduce occasional latency from the guest being descheduled.
41
*
42
* This function calculates a good minimum delta based roughly on the 75th
43
* percentile of the time taken to do the mips_next_event() sequence, in order
44
* to handle potentially higher overhead while also eliminating outliers due to
45
* unpredictable hypervisor latency (which can be handled by retries).
46
*
47
* Return: An appropriate minimum delta for the clock event device.
48
*/
49
static unsigned int calculate_min_delta(void)
50
{
51
unsigned int cnt, i, j, k, l;
52
unsigned int buf1[4], buf2[3];
53
unsigned int min_delta;
54
55
/*
56
* Calculate the median of 5 75th percentiles of 5 samples of how long
57
* it takes to set CP0_Compare = CP0_Count + delta.
58
*/
59
for (i = 0; i < 5; ++i) {
60
for (j = 0; j < 5; ++j) {
61
/*
62
* This is like the code in mips_next_event(), and
63
* directly measures the borderline "safe" delta.
64
*/
65
cnt = read_c0_count();
66
write_c0_compare(cnt);
67
cnt = read_c0_count() - cnt;
68
69
/* Sorted insert into buf1 */
70
for (k = 0; k < j; ++k) {
71
if (cnt < buf1[k]) {
72
l = min_t(unsigned int,
73
j, ARRAY_SIZE(buf1) - 1);
74
for (; l > k; --l)
75
buf1[l] = buf1[l - 1];
76
break;
77
}
78
}
79
if (k < ARRAY_SIZE(buf1))
80
buf1[k] = cnt;
81
}
82
83
/* Sorted insert of 75th percentile into buf2 */
84
for (k = 0; k < i && k < ARRAY_SIZE(buf2); ++k) {
85
if (buf1[ARRAY_SIZE(buf1) - 1] < buf2[k]) {
86
l = min_t(unsigned int,
87
i, ARRAY_SIZE(buf2) - 1);
88
for (; l > k; --l)
89
buf2[l] = buf2[l - 1];
90
break;
91
}
92
}
93
if (k < ARRAY_SIZE(buf2))
94
buf2[k] = buf1[ARRAY_SIZE(buf1) - 1];
95
}
96
97
/* Use 2 * median of 75th percentiles */
98
min_delta = buf2[ARRAY_SIZE(buf2) - 1] * 2;
99
100
/* Don't go too low */
101
if (min_delta < 0x300)
102
min_delta = 0x300;
103
104
pr_debug("%s: median 75th percentile=%#x, min_delta=%#x\n",
105
__func__, buf2[ARRAY_SIZE(buf2) - 1], min_delta);
106
return min_delta;
107
}
108
109
DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device);
110
int cp0_timer_irq_installed;
111
112
/*
113
* Possibly handle a performance counter interrupt.
114
* Return true if the timer interrupt should not be checked
115
*/
116
static inline int handle_perf_irq(int r2)
117
{
118
/*
119
* The performance counter overflow interrupt may be shared with the
120
* timer interrupt (cp0_perfcount_irq < 0). If it is and a
121
* performance counter has overflowed (perf_irq() == IRQ_HANDLED)
122
* and we can't reliably determine if a counter interrupt has also
123
* happened (!r2) then don't check for a timer interrupt.
124
*/
125
return (cp0_perfcount_irq < 0) &&
126
perf_irq() == IRQ_HANDLED &&
127
!r2;
128
}
129
130
irqreturn_t c0_compare_interrupt(int irq, void *dev_id)
131
{
132
const int r2 = cpu_has_mips_r2_r6;
133
struct clock_event_device *cd;
134
int cpu = smp_processor_id();
135
136
/*
137
* Suckage alert:
138
* Before R2 of the architecture there was no way to see if a
139
* performance counter interrupt was pending, so we have to run
140
* the performance counter interrupt handler anyway.
141
*/
142
if (handle_perf_irq(r2))
143
return IRQ_HANDLED;
144
145
/*
146
* The same applies to performance counter interrupts. But with the
147
* above we now know that the reason we got here must be a timer
148
* interrupt. Being the paranoiacs we are we check anyway.
149
*/
150
if (!r2 || (read_c0_cause() & CAUSEF_TI)) {
151
/* Clear Count/Compare Interrupt */
152
write_c0_compare(read_c0_compare());
153
cd = &per_cpu(mips_clockevent_device, cpu);
154
cd->event_handler(cd);
155
156
return IRQ_HANDLED;
157
}
158
159
return IRQ_NONE;
160
}
161
162
void mips_event_handler(struct clock_event_device *dev)
163
{
164
}
165
166
/*
167
* FIXME: This doesn't hold for the relocated E9000 compare interrupt.
168
*/
169
static int c0_compare_int_pending(void)
170
{
171
/* When cpu_has_mips_r2, this checks Cause.TI instead of Cause.IP7 */
172
return (read_c0_cause() >> cp0_compare_irq_shift) & (1ul << CAUSEB_IP);
173
}
174
175
/*
176
* Compare interrupt can be routed and latched outside the core,
177
* so wait up to worst case number of cycle counter ticks for timer interrupt
178
* changes to propagate to the cause register.
179
*/
180
#define COMPARE_INT_SEEN_TICKS 50
181
182
int c0_compare_int_usable(void)
183
{
184
unsigned int delta;
185
unsigned int cnt;
186
187
/*
188
* IP7 already pending? Try to clear it by acking the timer.
189
*/
190
if (c0_compare_int_pending()) {
191
cnt = read_c0_count();
192
write_c0_compare(cnt - 1);
193
back_to_back_c0_hazard();
194
while (read_c0_count() < (cnt + COMPARE_INT_SEEN_TICKS))
195
if (!c0_compare_int_pending())
196
break;
197
if (c0_compare_int_pending())
198
return 0;
199
}
200
201
for (delta = 0x10; delta <= 0x400000; delta <<= 1) {
202
cnt = read_c0_count();
203
cnt += delta;
204
write_c0_compare(cnt);
205
back_to_back_c0_hazard();
206
if ((int)(read_c0_count() - cnt) < 0)
207
break;
208
/* increase delta if the timer was already expired */
209
}
210
211
while ((int)(read_c0_count() - cnt) <= 0)
212
; /* Wait for expiry */
213
214
while (read_c0_count() < (cnt + COMPARE_INT_SEEN_TICKS))
215
if (c0_compare_int_pending())
216
break;
217
if (!c0_compare_int_pending())
218
return 0;
219
cnt = read_c0_count();
220
write_c0_compare(cnt - 1);
221
back_to_back_c0_hazard();
222
while (read_c0_count() < (cnt + COMPARE_INT_SEEN_TICKS))
223
if (!c0_compare_int_pending())
224
break;
225
if (c0_compare_int_pending())
226
return 0;
227
228
/*
229
* Feels like a real count / compare timer.
230
*/
231
return 1;
232
}
233
234
unsigned int __weak get_c0_compare_int(void)
235
{
236
return MIPS_CPU_IRQ_BASE + cp0_compare_irq;
237
}
238
239
#ifdef CONFIG_CPU_FREQ
240
241
static unsigned long mips_ref_freq;
242
243
static int r4k_cpufreq_callback(struct notifier_block *nb,
244
unsigned long val, void *data)
245
{
246
struct cpufreq_freqs *freq = data;
247
struct clock_event_device *cd;
248
unsigned long rate;
249
int cpu;
250
251
if (!mips_ref_freq)
252
mips_ref_freq = freq->old;
253
254
if (val == CPUFREQ_POSTCHANGE) {
255
rate = cpufreq_scale(mips_hpt_frequency, mips_ref_freq,
256
freq->new);
257
258
for_each_cpu(cpu, freq->policy->cpus) {
259
cd = &per_cpu(mips_clockevent_device, cpu);
260
261
clockevents_update_freq(cd, rate);
262
}
263
}
264
265
return 0;
266
}
267
268
static struct notifier_block r4k_cpufreq_notifier = {
269
.notifier_call = r4k_cpufreq_callback,
270
};
271
272
static int __init r4k_register_cpufreq_notifier(void)
273
{
274
return cpufreq_register_notifier(&r4k_cpufreq_notifier,
275
CPUFREQ_TRANSITION_NOTIFIER);
276
277
}
278
core_initcall(r4k_register_cpufreq_notifier);
279
280
#endif /* !CONFIG_CPU_FREQ */
281
282
int r4k_clockevent_init(void)
283
{
284
unsigned long flags = IRQF_PERCPU | IRQF_TIMER | IRQF_SHARED;
285
unsigned int cpu = smp_processor_id();
286
struct clock_event_device *cd;
287
unsigned int irq, min_delta;
288
289
if (!cpu_has_counter || !mips_hpt_frequency)
290
return -ENXIO;
291
292
if (!c0_compare_int_usable())
293
return -ENXIO;
294
295
cd = &per_cpu(mips_clockevent_device, cpu);
296
297
cd->name = "MIPS";
298
cd->features = CLOCK_EVT_FEAT_ONESHOT |
299
CLOCK_EVT_FEAT_C3STOP |
300
CLOCK_EVT_FEAT_PERCPU;
301
302
min_delta = calculate_min_delta();
303
304
cd->rating = 300;
305
cd->cpumask = cpumask_of(cpu);
306
cd->set_next_event = mips_next_event;
307
cd->event_handler = mips_event_handler;
308
309
clockevents_config_and_register(cd, mips_hpt_frequency, min_delta, 0x7fffffff);
310
311
if (cp0_timer_irq_installed)
312
return 0;
313
314
cp0_timer_irq_installed = 1;
315
316
/*
317
* With vectored interrupts things are getting platform specific.
318
* get_c0_compare_int is a hook to allow a platform to return the
319
* interrupt number of its liking.
320
*/
321
irq = get_c0_compare_int();
322
323
if (request_irq(irq, c0_compare_interrupt, flags, "timer",
324
c0_compare_interrupt))
325
pr_err("Failed to request irq %d (timer)\n", irq);
326
327
return 0;
328
}
329
330
331