Blame view
drivers/clocksource/arc_timer.c
9.02 KB
d2912cb15 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
d8005e6b9 ARC: Timers/count... |
2 |
/* |
c4c9a040e clocksource: impo... |
3 |
* Copyright (C) 2016-17 Synopsys, Inc. (www.synopsys.com) |
d8005e6b9 ARC: Timers/count... |
4 |
* Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) |
d8005e6b9 ARC: Timers/count... |
5 |
*/ |
c4c9a040e clocksource: impo... |
6 7 8 |
/* ARC700 has two 32bit independent prog Timers: TIMER0 and TIMER1, Each can be * programmed to go from @count to @limit and optionally interrupt. * We've designated TIMER0 for clockevents and TIMER1 for clocksource |
d8005e6b9 ARC: Timers/count... |
9 |
* |
c4c9a040e clocksource: impo... |
10 11 |
* ARCv2 based HS38 cores have RTC (in-core) and GFRC (inside ARConnect/MCIP) * which are suitable for UP and SMP based clocksources respectively |
d8005e6b9 ARC: Timers/count... |
12 |
*/ |
d8005e6b9 ARC: Timers/count... |
13 |
#include <linux/interrupt.h> |
93665ab06 clocksource/drive... |
14 |
#include <linux/bits.h> |
69fbd0987 ARC: clockevent: ... |
15 16 |
#include <linux/clk.h> #include <linux/clk-provider.h> |
d8005e6b9 ARC: Timers/count... |
17 18 |
#include <linux/clocksource.h> #include <linux/clockchips.h> |
eec3c58ef ARC: clockevent: ... |
19 |
#include <linux/cpu.h> |
77c8d0d6b ARC: clockevent: ... |
20 21 |
#include <linux/of.h> #include <linux/of_irq.h> |
bf287607c clocksource/drive... |
22 |
#include <linux/sched_clock.h> |
d8005e6b9 ARC: Timers/count... |
23 |
|
b26c2e382 ARC: breakout tim... |
24 |
#include <soc/arc/timers.h> |
2d7f5c48c ARC: move mcip.h ... |
25 |
#include <soc/arc/mcip.h> |
72d728806 ARCv2: SMP: clock... |
26 |
|
d8005e6b9 ARC: Timers/count... |
27 |
|
77c8d0d6b ARC: clockevent: ... |
28 29 30 31 32 33 34 35 36 |
static unsigned long arc_timer_freq; static int noinline arc_get_timer_clk(struct device_node *node) { struct clk *clk; int ret; clk = of_clk_get(node, 0); if (IS_ERR(clk)) { |
ac9ce6d1a clocksource: Add ... |
37 38 |
pr_err("timer missing clk "); |
77c8d0d6b ARC: clockevent: ... |
39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
return PTR_ERR(clk); } ret = clk_prepare_enable(clk); if (ret) { pr_err("Couldn't enable parent clk "); return ret; } arc_timer_freq = clk_get_rate(clk); return 0; } |
d8005e6b9 ARC: Timers/count... |
53 |
/********** Clock Source Device *********/ |
044214200 ARC: timer: gfrc,... |
54 |
#ifdef CONFIG_ARC_TIMERS_64BIT |
72d728806 ARCv2: SMP: clock... |
55 |
|
a5a1d1c29 clocksource: Use ... |
56 |
static u64 arc_read_gfrc(struct clocksource *cs) |
72d728806 ARCv2: SMP: clock... |
57 58 |
{ unsigned long flags; |
2cd690ea6 ARC: timer: gfrc,... |
59 |
u32 l, h; |
72d728806 ARCv2: SMP: clock... |
60 |
|
6bd9549d8 clocksource/drive... |
61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
/* * From a programming model pov, there seems to be just one instance of * MCIP_CMD/MCIP_READBACK however micro-architecturally there's * an instance PER ARC CORE (not per cluster), and there are dedicated * hardware decode logic (per core) inside ARConnect to handle * simultaneous read/write accesses from cores via those two registers. * So several concurrent commands to ARConnect are OK if they are * trying to access two different sub-components (like GFRC, * inter-core interrupt, etc...). HW also supports simultaneously * accessing GFRC by multiple cores. * That's why it is safe to disable hard interrupts on the local CPU * before access to GFRC instead of taking global MCIP spinlock * defined in arch/arc/kernel/mcip.c */ |
72d728806 ARCv2: SMP: clock... |
75 |
local_irq_save(flags); |
d584f0fb0 ARCv2: clocksourc... |
76 |
__mcip_cmd(CMD_GFRC_READ_LO, 0); |
2cd690ea6 ARC: timer: gfrc,... |
77 |
l = read_aux_reg(ARC_REG_MCIP_READBACK); |
72d728806 ARCv2: SMP: clock... |
78 |
|
d584f0fb0 ARCv2: clocksourc... |
79 |
__mcip_cmd(CMD_GFRC_READ_HI, 0); |
2cd690ea6 ARC: timer: gfrc,... |
80 |
h = read_aux_reg(ARC_REG_MCIP_READBACK); |
72d728806 ARCv2: SMP: clock... |
81 82 |
local_irq_restore(flags); |
a5a1d1c29 clocksource: Use ... |
83 |
return (((u64)h) << 32) | l; |
72d728806 ARCv2: SMP: clock... |
84 |
} |
bf287607c clocksource/drive... |
85 86 87 88 |
static notrace u64 arc_gfrc_clock_read(void) { return arc_read_gfrc(NULL); } |
e608b53ea ARC: clocksource:... |
89 |
static struct clocksource arc_counter_gfrc = { |
d584f0fb0 ARCv2: clocksourc... |
90 |
.name = "ARConnect GFRC", |
72d728806 ARCv2: SMP: clock... |
91 |
.rating = 400, |
e608b53ea ARC: clocksource:... |
92 |
.read = arc_read_gfrc, |
72d728806 ARCv2: SMP: clock... |
93 94 95 |
.mask = CLOCKSOURCE_MASK(64), .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; |
43d756049 clocksource/drive... |
96 |
static int __init arc_cs_setup_gfrc(struct device_node *node) |
e608b53ea ARC: clocksource:... |
97 |
{ |
ec7cb87bf ARC: timer: gfrc,... |
98 |
struct mcip_bcr mp; |
e608b53ea ARC: clocksource:... |
99 |
int ret; |
ec7cb87bf ARC: timer: gfrc,... |
100 101 |
READ_BCR(ARC_REG_MCIP_BCR, mp); if (!mp.gfrc) { |
ac9ce6d1a clocksource: Add ... |
102 103 |
pr_warn("Global-64-bit-Ctr clocksource not detected "); |
43d756049 clocksource/drive... |
104 |
return -ENXIO; |
ec7cb87bf ARC: timer: gfrc,... |
105 |
} |
e608b53ea ARC: clocksource:... |
106 107 108 |
ret = arc_get_timer_clk(node); if (ret) |
43d756049 clocksource/drive... |
109 |
return ret; |
e608b53ea ARC: clocksource:... |
110 |
|
bf287607c clocksource/drive... |
111 |
sched_clock_register(arc_gfrc_clock_read, 64, arc_timer_freq); |
43d756049 clocksource/drive... |
112 |
return clocksource_register_hz(&arc_counter_gfrc, arc_timer_freq); |
e608b53ea ARC: clocksource:... |
113 |
} |
172733959 clocksource/drive... |
114 |
TIMER_OF_DECLARE(arc_gfrc, "snps,archs-timer-gfrc", arc_cs_setup_gfrc); |
e608b53ea ARC: clocksource:... |
115 |
|
aa93e8ef9 ARCv2: clocksourc... |
116 117 118 |
#define AUX_RTC_CTRL 0x103 #define AUX_RTC_LOW 0x104 #define AUX_RTC_HIGH 0x105 |
a5a1d1c29 clocksource: Use ... |
119 |
static u64 arc_read_rtc(struct clocksource *cs) |
aa93e8ef9 ARCv2: clocksourc... |
120 121 |
{ unsigned long status; |
2cd690ea6 ARC: timer: gfrc,... |
122 |
u32 l, h; |
aa93e8ef9 ARCv2: clocksourc... |
123 |
|
922cc1719 ARC: timer: rtc: ... |
124 125 126 127 128 129 130 |
/* * hardware has an internal state machine which tracks readout of * low/high and updates the CTRL.status if * - interrupt/exception taken between the two reads * - high increments after low has been read */ do { |
2cd690ea6 ARC: timer: gfrc,... |
131 132 |
l = read_aux_reg(AUX_RTC_LOW); h = read_aux_reg(AUX_RTC_HIGH); |
922cc1719 ARC: timer: rtc: ... |
133 |
status = read_aux_reg(AUX_RTC_CTRL); |
93665ab06 clocksource/drive... |
134 |
} while (!(status & BIT(31))); |
aa93e8ef9 ARCv2: clocksourc... |
135 |
|
a5a1d1c29 clocksource: Use ... |
136 |
return (((u64)h) << 32) | l; |
aa93e8ef9 ARCv2: clocksourc... |
137 |
} |
bf287607c clocksource/drive... |
138 139 140 141 |
static notrace u64 arc_rtc_clock_read(void) { return arc_read_rtc(NULL); } |
e608b53ea ARC: clocksource:... |
142 |
static struct clocksource arc_counter_rtc = { |
aa93e8ef9 ARCv2: clocksourc... |
143 144 |
.name = "ARCv2 RTC", .rating = 350, |
e608b53ea ARC: clocksource:... |
145 |
.read = arc_read_rtc, |
aa93e8ef9 ARCv2: clocksourc... |
146 147 148 |
.mask = CLOCKSOURCE_MASK(64), .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; |
43d756049 clocksource/drive... |
149 |
static int __init arc_cs_setup_rtc(struct device_node *node) |
d8005e6b9 ARC: Timers/count... |
150 |
{ |
ec7cb87bf ARC: timer: gfrc,... |
151 |
struct bcr_timer timer; |
e608b53ea ARC: clocksource:... |
152 |
int ret; |
ec7cb87bf ARC: timer: gfrc,... |
153 154 |
READ_BCR(ARC_REG_TIMERS_BCR, timer); if (!timer.rtc) { |
ac9ce6d1a clocksource: Add ... |
155 156 |
pr_warn("Local-64-bit-Ctr clocksource not detected "); |
43d756049 clocksource/drive... |
157 |
return -ENXIO; |
ec7cb87bf ARC: timer: gfrc,... |
158 |
} |
e608b53ea ARC: clocksource:... |
159 160 |
/* Local to CPU hence not usable in SMP */ |
ec7cb87bf ARC: timer: gfrc,... |
161 |
if (IS_ENABLED(CONFIG_SMP)) { |
ac9ce6d1a clocksource: Add ... |
162 163 |
pr_warn("Local-64-bit-Ctr not usable in SMP "); |
43d756049 clocksource/drive... |
164 |
return -EINVAL; |
ec7cb87bf ARC: timer: gfrc,... |
165 |
} |
e608b53ea ARC: clocksource:... |
166 167 168 |
ret = arc_get_timer_clk(node); if (ret) |
43d756049 clocksource/drive... |
169 |
return ret; |
d8005e6b9 ARC: Timers/count... |
170 |
|
e608b53ea ARC: clocksource:... |
171 |
write_aux_reg(AUX_RTC_CTRL, 1); |
bf287607c clocksource/drive... |
172 |
sched_clock_register(arc_rtc_clock_read, 64, arc_timer_freq); |
43d756049 clocksource/drive... |
173 |
return clocksource_register_hz(&arc_counter_rtc, arc_timer_freq); |
d8005e6b9 ARC: Timers/count... |
174 |
} |
172733959 clocksource/drive... |
175 |
TIMER_OF_DECLARE(arc_rtc, "snps,archs-timer-rtc", arc_cs_setup_rtc); |
e608b53ea ARC: clocksource:... |
176 177 |
#endif |
d8005e6b9 ARC: Timers/count... |
178 |
|
e608b53ea ARC: clocksource:... |
179 180 181 |
/* * 32bit TIMER1 to keep counting monotonically and wraparound */ |
a5a1d1c29 clocksource: Use ... |
182 |
static u64 arc_read_timer1(struct clocksource *cs) |
d8005e6b9 ARC: Timers/count... |
183 |
{ |
a5a1d1c29 clocksource: Use ... |
184 |
return (u64) read_aux_reg(ARC_REG_TIMER1_CNT); |
d8005e6b9 ARC: Timers/count... |
185 |
} |
bf287607c clocksource/drive... |
186 187 188 189 |
static notrace u64 arc_timer1_clock_read(void) { return arc_read_timer1(NULL); } |
e608b53ea ARC: clocksource:... |
190 |
static struct clocksource arc_counter_timer1 = { |
d8005e6b9 ARC: Timers/count... |
191 192 |
.name = "ARC Timer1", .rating = 300, |
e608b53ea ARC: clocksource:... |
193 |
.read = arc_read_timer1, |
d8005e6b9 ARC: Timers/count... |
194 195 196 |
.mask = CLOCKSOURCE_MASK(32), .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; |
43d756049 clocksource/drive... |
197 |
static int __init arc_cs_setup_timer1(struct device_node *node) |
e608b53ea ARC: clocksource:... |
198 199 200 201 202 |
{ int ret; /* Local to CPU hence not usable in SMP */ if (IS_ENABLED(CONFIG_SMP)) |
43d756049 clocksource/drive... |
203 |
return -EINVAL; |
e608b53ea ARC: clocksource:... |
204 205 206 |
ret = arc_get_timer_clk(node); if (ret) |
43d756049 clocksource/drive... |
207 |
return ret; |
e608b53ea ARC: clocksource:... |
208 |
|
b26c2e382 ARC: breakout tim... |
209 |
write_aux_reg(ARC_REG_TIMER1_LIMIT, ARC_TIMERN_MAX); |
e608b53ea ARC: clocksource:... |
210 211 |
write_aux_reg(ARC_REG_TIMER1_CNT, 0); write_aux_reg(ARC_REG_TIMER1_CTRL, TIMER_CTRL_NH); |
bf287607c clocksource/drive... |
212 |
sched_clock_register(arc_timer1_clock_read, 32, arc_timer_freq); |
43d756049 clocksource/drive... |
213 |
return clocksource_register_hz(&arc_counter_timer1, arc_timer_freq); |
e608b53ea ARC: clocksource:... |
214 |
} |
aa93e8ef9 ARCv2: clocksourc... |
215 |
|
d8005e6b9 ARC: Timers/count... |
216 |
/********** Clock Event Device *********/ |
77c8d0d6b ARC: clockevent: ... |
217 |
static int arc_timer_irq; |
eec3c58ef ARC: clockevent: ... |
218 |
|
d8005e6b9 ARC: Timers/count... |
219 |
/* |
c9a98e184 ARC: update some ... |
220 |
* Arm the timer to interrupt after @cycles |
d8005e6b9 ARC: Timers/count... |
221 222 |
* The distinction for oneshot/periodic is done in arc_event_timer_ack() below */ |
c9a98e184 ARC: update some ... |
223 |
static void arc_timer_event_setup(unsigned int cycles) |
d8005e6b9 ARC: Timers/count... |
224 |
{ |
c9a98e184 ARC: update some ... |
225 |
write_aux_reg(ARC_REG_TIMER0_LIMIT, cycles); |
d8005e6b9 ARC: Timers/count... |
226 227 228 229 |
write_aux_reg(ARC_REG_TIMER0_CNT, 0); /* start from 0 */ write_aux_reg(ARC_REG_TIMER0_CTRL, TIMER_CTRL_IE | TIMER_CTRL_NH); } |
d8005e6b9 ARC: Timers/count... |
230 231 232 233 234 235 236 |
static int arc_clkevent_set_next_event(unsigned long delta, struct clock_event_device *dev) { arc_timer_event_setup(delta); return 0; } |
aeec6cdad ARC/time: Migrate... |
237 |
static int arc_clkevent_set_periodic(struct clock_event_device *dev) |
d8005e6b9 ARC: Timers/count... |
238 |
{ |
aeec6cdad ARC/time: Migrate... |
239 240 241 242 |
/* * At X Hz, 1 sec = 1000ms -> X cycles; * 10ms -> X / 100 cycles */ |
77c8d0d6b ARC: clockevent: ... |
243 |
arc_timer_event_setup(arc_timer_freq / HZ); |
aeec6cdad ARC/time: Migrate... |
244 |
return 0; |
d8005e6b9 ARC: Timers/count... |
245 246 247 |
} static DEFINE_PER_CPU(struct clock_event_device, arc_clockevent_device) = { |
aeec6cdad ARC/time: Migrate... |
248 249 250 251 |
.name = "ARC Timer0", .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, .rating = 300, |
aeec6cdad ARC/time: Migrate... |
252 253 |
.set_next_event = arc_clkevent_set_next_event, .set_state_periodic = arc_clkevent_set_periodic, |
d8005e6b9 ARC: Timers/count... |
254 255 256 257 |
}; static irqreturn_t timer_irq_handler(int irq, void *dev_id) { |
f8b34c3fd ARC: [clockevent]... |
258 259 260 261 262 |
/* * Note that generic IRQ core could have passed @evt for @dev_id if * irq_set_chip_and_handler() asked for handle_percpu_devid_irq() */ struct clock_event_device *evt = this_cpu_ptr(&arc_clockevent_device); |
aeec6cdad ARC/time: Migrate... |
263 |
int irq_reenable = clockevent_state_periodic(evt); |
f8b34c3fd ARC: [clockevent]... |
264 265 |
/* |
a4f538573 clocksource/drive... |
266 267 268 269 270 271 272 273 |
* 1. ACK the interrupt * - For ARC700, any write to CTRL reg ACKs it, so just rewrite * Count when [N]ot [H]alted bit. * - For HS3x, it is a bit subtle. On taken count-down interrupt, * IP bit [3] is set, which needs to be cleared for ACK'ing. * The write below can only update the other two bits, hence * explicitly clears IP bit * 2. Re-arm interrupt if periodic by writing to IE bit [0] |
f8b34c3fd ARC: [clockevent]... |
274 275 276 277 |
*/ write_aux_reg(ARC_REG_TIMER0_CTRL, irq_reenable | TIMER_CTRL_NH); evt->event_handler(evt); |
d8005e6b9 ARC: Timers/count... |
278 |
|
d8005e6b9 ARC: Timers/count... |
279 280 |
return IRQ_HANDLED; } |
ecd8081f6 ARC/time: Convert... |
281 282 |
static int arc_timer_starting_cpu(unsigned int cpu) |
eec3c58ef ARC: clockevent: ... |
283 284 285 286 |
{ struct clock_event_device *evt = this_cpu_ptr(&arc_clockevent_device); evt->cpumask = cpumask_of(smp_processor_id()); |
b26c2e382 ARC: breakout tim... |
287 |
clockevents_config_and_register(evt, arc_timer_freq, 0, ARC_TIMERN_MAX); |
ecd8081f6 ARC/time: Convert... |
288 289 |
enable_percpu_irq(arc_timer_irq, 0); return 0; |
eec3c58ef ARC: clockevent: ... |
290 |
} |
ecd8081f6 ARC/time: Convert... |
291 292 293 294 295 |
static int arc_timer_dying_cpu(unsigned int cpu) { disable_percpu_irq(arc_timer_irq); return 0; } |
eec3c58ef ARC: clockevent: ... |
296 |
|
d8005e6b9 ARC: Timers/count... |
297 |
/* |
eec3c58ef ARC: clockevent: ... |
298 |
* clockevent setup for boot CPU |
d8005e6b9 ARC: Timers/count... |
299 |
*/ |
43d756049 clocksource/drive... |
300 |
static int __init arc_clockevent_setup(struct device_node *node) |
d8005e6b9 ARC: Timers/count... |
301 |
{ |
2d4899f6b ARC: arc_local_ti... |
302 |
struct clock_event_device *evt = this_cpu_ptr(&arc_clockevent_device); |
eec3c58ef ARC: clockevent: ... |
303 |
int ret; |
d8005e6b9 ARC: Timers/count... |
304 |
|
77c8d0d6b ARC: clockevent: ... |
305 |
arc_timer_irq = irq_of_parse_and_map(node, 0); |
43d756049 clocksource/drive... |
306 |
if (arc_timer_irq <= 0) { |
ac9ce6d1a clocksource: Add ... |
307 308 |
pr_err("clockevent: missing irq "); |
43d756049 clocksource/drive... |
309 310 |
return -EINVAL; } |
77c8d0d6b ARC: clockevent: ... |
311 312 |
ret = arc_get_timer_clk(node); |
311fb70aa clocksource/drive... |
313 |
if (ret) |
43d756049 clocksource/drive... |
314 |
return ret; |
77c8d0d6b ARC: clockevent: ... |
315 |
|
eec3c58ef ARC: clockevent: ... |
316 317 318 |
/* Needs apriori irq_set_percpu_devid() done in intc map function */ ret = request_percpu_irq(arc_timer_irq, timer_irq_handler, "Timer0 (per-cpu-tick)", evt); |
43d756049 clocksource/drive... |
319 320 321 322 323 |
if (ret) { pr_err("clockevent: unable to request irq "); return ret; } |
569579401 ARC: opencode arc... |
324 |
|
ecd8081f6 ARC/time: Convert... |
325 |
ret = cpuhp_setup_state(CPUHP_AP_ARC_TIMER_STARTING, |
73c1b41e6 cpu/hotplug: Clea... |
326 |
"clockevents/arc/timer:starting", |
ecd8081f6 ARC/time: Convert... |
327 328 329 |
arc_timer_starting_cpu, arc_timer_dying_cpu); if (ret) { |
ac9ce6d1a clocksource: Add ... |
330 331 |
pr_err("Failed to setup hotplug state "); |
ecd8081f6 ARC/time: Convert... |
332 333 |
return ret; } |
43d756049 clocksource/drive... |
334 |
return 0; |
d8005e6b9 ARC: Timers/count... |
335 |
} |
e608b53ea ARC: clocksource:... |
336 |
|
43d756049 clocksource/drive... |
337 |
static int __init arc_of_timer_init(struct device_node *np) |
e608b53ea ARC: clocksource:... |
338 339 |
{ static int init_count = 0; |
43d756049 clocksource/drive... |
340 |
int ret; |
e608b53ea ARC: clocksource:... |
341 342 343 |
if (!init_count) { init_count = 1; |
43d756049 clocksource/drive... |
344 |
ret = arc_clockevent_setup(np); |
e608b53ea ARC: clocksource:... |
345 |
} else { |
43d756049 clocksource/drive... |
346 |
ret = arc_cs_setup_timer1(np); |
e608b53ea ARC: clocksource:... |
347 |
} |
43d756049 clocksource/drive... |
348 349 |
return ret; |
e608b53ea ARC: clocksource:... |
350 |
} |
172733959 clocksource/drive... |
351 |
TIMER_OF_DECLARE(arc_clkevt, "snps,arc-timer", arc_of_timer_init); |