1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * linux/arch/arm/kernel/smp_twd.c |
4 | * |
5 | * Copyright (C) 2002 ARM Ltd. |
6 | * All Rights Reserved |
7 | */ |
8 | #include <linux/init.h> |
9 | #include <linux/kernel.h> |
10 | #include <linux/clk.h> |
11 | #include <linux/cpu.h> |
12 | #include <linux/delay.h> |
13 | #include <linux/device.h> |
14 | #include <linux/err.h> |
15 | #include <linux/smp.h> |
16 | #include <linux/jiffies.h> |
17 | #include <linux/clockchips.h> |
18 | #include <linux/interrupt.h> |
19 | #include <linux/io.h> |
20 | #include <linux/of_irq.h> |
21 | #include <linux/of_address.h> |
22 | |
23 | #include <asm/smp_twd.h> |
24 | |
25 | /* set up by the platform code */ |
26 | static void __iomem *twd_base; |
27 | |
28 | static struct clk *twd_clk; |
29 | static unsigned long twd_timer_rate; |
30 | static DEFINE_PER_CPU(bool, percpu_setup_called); |
31 | |
32 | static struct clock_event_device __percpu *twd_evt; |
33 | static unsigned int twd_features = |
34 | CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; |
35 | static int twd_ppi; |
36 | |
37 | static int twd_shutdown(struct clock_event_device *clk) |
38 | { |
39 | writel_relaxed(0, twd_base + TWD_TIMER_CONTROL); |
40 | return 0; |
41 | } |
42 | |
43 | static int twd_set_oneshot(struct clock_event_device *clk) |
44 | { |
45 | /* period set, and timer enabled in 'next_event' hook */ |
46 | writel_relaxed(TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT, |
47 | twd_base + TWD_TIMER_CONTROL); |
48 | return 0; |
49 | } |
50 | |
51 | static int twd_set_periodic(struct clock_event_device *clk) |
52 | { |
53 | unsigned long ctrl = TWD_TIMER_CONTROL_ENABLE | |
54 | TWD_TIMER_CONTROL_IT_ENABLE | |
55 | TWD_TIMER_CONTROL_PERIODIC; |
56 | |
57 | writel_relaxed(DIV_ROUND_CLOSEST(twd_timer_rate, HZ), |
58 | twd_base + TWD_TIMER_LOAD); |
59 | writel_relaxed(ctrl, twd_base + TWD_TIMER_CONTROL); |
60 | return 0; |
61 | } |
62 | |
63 | static int twd_set_next_event(unsigned long evt, |
64 | struct clock_event_device *unused) |
65 | { |
66 | unsigned long ctrl = readl_relaxed(twd_base + TWD_TIMER_CONTROL); |
67 | |
68 | ctrl |= TWD_TIMER_CONTROL_ENABLE; |
69 | |
70 | writel_relaxed(evt, twd_base + TWD_TIMER_COUNTER); |
71 | writel_relaxed(ctrl, twd_base + TWD_TIMER_CONTROL); |
72 | |
73 | return 0; |
74 | } |
75 | |
76 | /* |
77 | * local_timer_ack: checks for a local timer interrupt. |
78 | * |
79 | * If a local timer interrupt has occurred, acknowledge and return 1. |
80 | * Otherwise, return 0. |
81 | */ |
82 | static int twd_timer_ack(void) |
83 | { |
84 | if (readl_relaxed(twd_base + TWD_TIMER_INTSTAT)) { |
85 | writel_relaxed(1, twd_base + TWD_TIMER_INTSTAT); |
86 | return 1; |
87 | } |
88 | |
89 | return 0; |
90 | } |
91 | |
92 | static void twd_timer_stop(void) |
93 | { |
94 | struct clock_event_device *clk = raw_cpu_ptr(twd_evt); |
95 | |
96 | twd_shutdown(clk); |
97 | disable_percpu_irq(irq: clk->irq); |
98 | } |
99 | |
100 | /* |
101 | * Updates clockevent frequency when the cpu frequency changes. |
102 | * Called on the cpu that is changing frequency with interrupts disabled. |
103 | */ |
104 | static void twd_update_frequency(void *new_rate) |
105 | { |
106 | twd_timer_rate = *((unsigned long *) new_rate); |
107 | |
108 | clockevents_update_freq(raw_cpu_ptr(twd_evt), freq: twd_timer_rate); |
109 | } |
110 | |
111 | static int twd_rate_change(struct notifier_block *nb, |
112 | unsigned long flags, void *data) |
113 | { |
114 | struct clk_notifier_data *cnd = data; |
115 | |
116 | /* |
117 | * The twd clock events must be reprogrammed to account for the new |
118 | * frequency. The timer is local to a cpu, so cross-call to the |
119 | * changing cpu. |
120 | */ |
121 | if (flags == POST_RATE_CHANGE) |
122 | on_each_cpu(func: twd_update_frequency, |
123 | info: (void *)&cnd->new_rate, wait: 1); |
124 | |
125 | return NOTIFY_OK; |
126 | } |
127 | |
128 | static struct notifier_block twd_clk_nb = { |
129 | .notifier_call = twd_rate_change, |
130 | }; |
131 | |
132 | static int twd_clk_init(void) |
133 | { |
134 | if (twd_evt && raw_cpu_ptr(twd_evt) && !IS_ERR(ptr: twd_clk)) |
135 | return clk_notifier_register(clk: twd_clk, nb: &twd_clk_nb); |
136 | |
137 | return 0; |
138 | } |
139 | core_initcall(twd_clk_init); |
140 | |
141 | static void twd_calibrate_rate(void) |
142 | { |
143 | unsigned long count; |
144 | u64 waitjiffies; |
145 | |
146 | /* |
147 | * If this is the first time round, we need to work out how fast |
148 | * the timer ticks |
149 | */ |
150 | if (twd_timer_rate == 0) { |
151 | pr_info("Calibrating local timer... " ); |
152 | |
153 | /* Wait for a tick to start */ |
154 | waitjiffies = get_jiffies_64() + 1; |
155 | |
156 | while (get_jiffies_64() < waitjiffies) |
157 | udelay(10); |
158 | |
159 | /* OK, now the tick has started, let's get the timer going */ |
160 | waitjiffies += 5; |
161 | |
162 | /* enable, no interrupt or reload */ |
163 | writel_relaxed(0x1, twd_base + TWD_TIMER_CONTROL); |
164 | |
165 | /* maximum value */ |
166 | writel_relaxed(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER); |
167 | |
168 | while (get_jiffies_64() < waitjiffies) |
169 | udelay(10); |
170 | |
171 | count = readl_relaxed(twd_base + TWD_TIMER_COUNTER); |
172 | |
173 | twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5); |
174 | |
175 | pr_cont("%lu.%02luMHz.\n" , twd_timer_rate / 1000000, |
176 | (twd_timer_rate / 10000) % 100); |
177 | } |
178 | } |
179 | |
180 | static irqreturn_t twd_handler(int irq, void *dev_id) |
181 | { |
182 | struct clock_event_device *evt = dev_id; |
183 | |
184 | if (twd_timer_ack()) { |
185 | evt->event_handler(evt); |
186 | return IRQ_HANDLED; |
187 | } |
188 | |
189 | return IRQ_NONE; |
190 | } |
191 | |
192 | static void twd_get_clock(struct device_node *np) |
193 | { |
194 | int err; |
195 | |
196 | if (np) |
197 | twd_clk = of_clk_get(np, index: 0); |
198 | else |
199 | twd_clk = clk_get_sys(dev_id: "smp_twd" , NULL); |
200 | |
201 | if (IS_ERR(ptr: twd_clk)) { |
202 | pr_err("smp_twd: clock not found %d\n" , (int) PTR_ERR(twd_clk)); |
203 | return; |
204 | } |
205 | |
206 | err = clk_prepare_enable(clk: twd_clk); |
207 | if (err) { |
208 | pr_err("smp_twd: clock failed to prepare+enable: %d\n" , err); |
209 | clk_put(clk: twd_clk); |
210 | return; |
211 | } |
212 | |
213 | twd_timer_rate = clk_get_rate(clk: twd_clk); |
214 | } |
215 | |
216 | /* |
217 | * Setup the local clock events for a CPU. |
218 | */ |
219 | static void twd_timer_setup(void) |
220 | { |
221 | struct clock_event_device *clk = raw_cpu_ptr(twd_evt); |
222 | int cpu = smp_processor_id(); |
223 | |
224 | /* |
225 | * If the basic setup for this CPU has been done before don't |
226 | * bother with the below. |
227 | */ |
228 | if (per_cpu(percpu_setup_called, cpu)) { |
229 | writel_relaxed(0, twd_base + TWD_TIMER_CONTROL); |
230 | clockevents_register_device(dev: clk); |
231 | enable_percpu_irq(irq: clk->irq, type: 0); |
232 | return; |
233 | } |
234 | per_cpu(percpu_setup_called, cpu) = true; |
235 | |
236 | twd_calibrate_rate(); |
237 | |
238 | /* |
239 | * The following is done once per CPU the first time .setup() is |
240 | * called. |
241 | */ |
242 | writel_relaxed(0, twd_base + TWD_TIMER_CONTROL); |
243 | |
244 | clk->name = "local_timer" ; |
245 | clk->features = twd_features; |
246 | clk->rating = 350; |
247 | clk->set_state_shutdown = twd_shutdown; |
248 | clk->set_state_periodic = twd_set_periodic; |
249 | clk->set_state_oneshot = twd_set_oneshot; |
250 | clk->tick_resume = twd_shutdown; |
251 | clk->set_next_event = twd_set_next_event; |
252 | clk->irq = twd_ppi; |
253 | clk->cpumask = cpumask_of(cpu); |
254 | |
255 | clockevents_config_and_register(dev: clk, freq: twd_timer_rate, |
256 | min_delta: 0xf, max_delta: 0xffffffff); |
257 | enable_percpu_irq(irq: clk->irq, type: 0); |
258 | } |
259 | |
260 | static int twd_timer_starting_cpu(unsigned int cpu) |
261 | { |
262 | twd_timer_setup(); |
263 | return 0; |
264 | } |
265 | |
266 | static int twd_timer_dying_cpu(unsigned int cpu) |
267 | { |
268 | twd_timer_stop(); |
269 | return 0; |
270 | } |
271 | |
272 | static int __init twd_local_timer_common_register(struct device_node *np) |
273 | { |
274 | int err; |
275 | |
276 | twd_evt = alloc_percpu(struct clock_event_device); |
277 | if (!twd_evt) { |
278 | err = -ENOMEM; |
279 | goto out_free; |
280 | } |
281 | |
282 | err = request_percpu_irq(irq: twd_ppi, handler: twd_handler, devname: "twd" , percpu_dev_id: twd_evt); |
283 | if (err) { |
284 | pr_err("twd: can't register interrupt %d (%d)\n" , twd_ppi, err); |
285 | goto out_free; |
286 | } |
287 | |
288 | cpuhp_setup_state_nocalls(state: CPUHP_AP_ARM_TWD_STARTING, |
289 | name: "arm/timer/twd:starting" , |
290 | startup: twd_timer_starting_cpu, teardown: twd_timer_dying_cpu); |
291 | |
292 | twd_get_clock(np); |
293 | if (!of_property_read_bool(np, propname: "always-on" )) |
294 | twd_features |= CLOCK_EVT_FEAT_C3STOP; |
295 | |
296 | /* |
297 | * Immediately configure the timer on the boot CPU, unless we need |
298 | * jiffies to be incrementing to calibrate the rate in which case |
299 | * setup the timer in late_time_init. |
300 | */ |
301 | if (twd_timer_rate) |
302 | twd_timer_setup(); |
303 | else |
304 | late_time_init = twd_timer_setup; |
305 | |
306 | return 0; |
307 | |
308 | out_free: |
309 | iounmap(addr: twd_base); |
310 | twd_base = NULL; |
311 | free_percpu(pdata: twd_evt); |
312 | |
313 | return err; |
314 | } |
315 | |
316 | static int __init twd_local_timer_of_register(struct device_node *np) |
317 | { |
318 | int err; |
319 | |
320 | twd_ppi = irq_of_parse_and_map(node: np, index: 0); |
321 | if (!twd_ppi) { |
322 | err = -EINVAL; |
323 | goto out; |
324 | } |
325 | |
326 | twd_base = of_iomap(node: np, index: 0); |
327 | if (!twd_base) { |
328 | err = -ENOMEM; |
329 | goto out; |
330 | } |
331 | |
332 | err = twd_local_timer_common_register(np); |
333 | |
334 | out: |
335 | WARN(err, "twd_local_timer_of_register failed (%d)\n" , err); |
336 | return err; |
337 | } |
338 | TIMER_OF_DECLARE(arm_twd_a9, "arm,cortex-a9-twd-timer" , twd_local_timer_of_register); |
339 | TIMER_OF_DECLARE(arm_twd_a5, "arm,cortex-a5-twd-timer" , twd_local_timer_of_register); |
340 | TIMER_OF_DECLARE(arm_twd_11mp, "arm,arm11mp-twd-timer" , twd_local_timer_of_register); |
341 | |