1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f32f4ce2SRussell King /*
3f32f4ce2SRussell King * linux/arch/arm/kernel/smp_twd.c
4f32f4ce2SRussell King *
5f32f4ce2SRussell King * Copyright (C) 2002 ARM Ltd.
6f32f4ce2SRussell King * All Rights Reserved
7f32f4ce2SRussell King */
8f32f4ce2SRussell King #include <linux/init.h>
9f32f4ce2SRussell King #include <linux/kernel.h>
105def51b0SLinus Walleij #include <linux/clk.h>
11a894fcc2SStephen Boyd #include <linux/cpu.h>
12f32f4ce2SRussell King #include <linux/delay.h>
13f32f4ce2SRussell King #include <linux/device.h>
145def51b0SLinus Walleij #include <linux/err.h>
15f32f4ce2SRussell King #include <linux/smp.h>
16f32f4ce2SRussell King #include <linux/jiffies.h>
17f32f4ce2SRussell King #include <linux/clockchips.h>
1892485104SMarc Zyngier #include <linux/interrupt.h>
19f32f4ce2SRussell King #include <linux/io.h>
20d8e03643SMarc Zyngier #include <linux/of_irq.h>
21d8e03643SMarc Zyngier #include <linux/of_address.h>
22f32f4ce2SRussell King
23f32f4ce2SRussell King #include <asm/smp_twd.h>
24f32f4ce2SRussell King
25f32f4ce2SRussell King /* set up by the platform code */
2692485104SMarc Zyngier static void __iomem *twd_base;
27f32f4ce2SRussell King
285def51b0SLinus Walleij static struct clk *twd_clk;
29f32f4ce2SRussell King static unsigned long twd_timer_rate;
30a68becd1SLinus Walleij static DEFINE_PER_CPU(bool, percpu_setup_called);
31f32f4ce2SRussell King
32a894fcc2SStephen Boyd static struct clock_event_device __percpu *twd_evt;
33e1b8c05dSRussell King static unsigned int twd_features =
34e1b8c05dSRussell King CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
3581e46f7bSMarc Zyngier static int twd_ppi;
3628af690aSMarc Zyngier
twd_shutdown(struct clock_event_device * clk)375e253571SViresh Kumar static int twd_shutdown(struct clock_event_device *clk)
38f32f4ce2SRussell King {
395e253571SViresh Kumar writel_relaxed(0, twd_base + TWD_TIMER_CONTROL);
405e253571SViresh Kumar return 0;
41f32f4ce2SRussell King }
42f32f4ce2SRussell King
twd_set_oneshot(struct clock_event_device * clk)435e253571SViresh Kumar static int twd_set_oneshot(struct clock_event_device *clk)
445e253571SViresh Kumar {
455e253571SViresh Kumar /* period set, and timer enabled in 'next_event' hook */
465e253571SViresh Kumar writel_relaxed(TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT,
475e253571SViresh Kumar twd_base + TWD_TIMER_CONTROL);
485e253571SViresh Kumar return 0;
495e253571SViresh Kumar }
505e253571SViresh Kumar
twd_set_periodic(struct clock_event_device * clk)515e253571SViresh Kumar static int twd_set_periodic(struct clock_event_device *clk)
525e253571SViresh Kumar {
535e253571SViresh Kumar unsigned long ctrl = TWD_TIMER_CONTROL_ENABLE |
545e253571SViresh Kumar TWD_TIMER_CONTROL_IT_ENABLE |
555e253571SViresh Kumar TWD_TIMER_CONTROL_PERIODIC;
565e253571SViresh Kumar
575e253571SViresh Kumar writel_relaxed(DIV_ROUND_CLOSEST(twd_timer_rate, HZ),
585e253571SViresh Kumar twd_base + TWD_TIMER_LOAD);
592e874ea3SBen Dooks writel_relaxed(ctrl, twd_base + TWD_TIMER_CONTROL);
605e253571SViresh Kumar return 0;
61f32f4ce2SRussell King }
62f32f4ce2SRussell King
twd_set_next_event(unsigned long evt,struct clock_event_device * unused)63f32f4ce2SRussell King static int twd_set_next_event(unsigned long evt,
64f32f4ce2SRussell King struct clock_event_device *unused)
65f32f4ce2SRussell King {
662e874ea3SBen Dooks unsigned long ctrl = readl_relaxed(twd_base + TWD_TIMER_CONTROL);
67f32f4ce2SRussell King
684c5158d4SRussell King ctrl |= TWD_TIMER_CONTROL_ENABLE;
694c5158d4SRussell King
702e874ea3SBen Dooks writel_relaxed(evt, twd_base + TWD_TIMER_COUNTER);
712e874ea3SBen Dooks writel_relaxed(ctrl, twd_base + TWD_TIMER_CONTROL);
72f32f4ce2SRussell King
73f32f4ce2SRussell King return 0;
74f32f4ce2SRussell King }
75f32f4ce2SRussell King
76f32f4ce2SRussell King /*
77f32f4ce2SRussell King * local_timer_ack: checks for a local timer interrupt.
78f32f4ce2SRussell King *
79f32f4ce2SRussell King * If a local timer interrupt has occurred, acknowledge and return 1.
80f32f4ce2SRussell King * Otherwise, return 0.
81f32f4ce2SRussell King */
twd_timer_ack(void)8292485104SMarc Zyngier static int twd_timer_ack(void)
83f32f4ce2SRussell King {
842e874ea3SBen Dooks if (readl_relaxed(twd_base + TWD_TIMER_INTSTAT)) {
852e874ea3SBen Dooks writel_relaxed(1, twd_base + TWD_TIMER_INTSTAT);
86f32f4ce2SRussell King return 1;
87f32f4ce2SRussell King }
88f32f4ce2SRussell King
89f32f4ce2SRussell King return 0;
90f32f4ce2SRussell King }
91f32f4ce2SRussell King
twd_timer_stop(void)92a894fcc2SStephen Boyd static void twd_timer_stop(void)
9328af690aSMarc Zyngier {
9406b96c8bSChristoph Lameter struct clock_event_device *clk = raw_cpu_ptr(twd_evt);
95a894fcc2SStephen Boyd
965e253571SViresh Kumar twd_shutdown(clk);
9728af690aSMarc Zyngier disable_percpu_irq(clk->irq);
9828af690aSMarc Zyngier }
9928af690aSMarc Zyngier
1002b25d9f6SMike Turquette /*
1012b25d9f6SMike Turquette * Updates clockevent frequency when the cpu frequency changes.
1022b25d9f6SMike Turquette * Called on the cpu that is changing frequency with interrupts disabled.
1032b25d9f6SMike Turquette */
twd_update_frequency(void * new_rate)1042b25d9f6SMike Turquette static void twd_update_frequency(void *new_rate)
1052b25d9f6SMike Turquette {
1062b25d9f6SMike Turquette twd_timer_rate = *((unsigned long *) new_rate);
1072b25d9f6SMike Turquette
10806b96c8bSChristoph Lameter clockevents_update_freq(raw_cpu_ptr(twd_evt), twd_timer_rate);
1092b25d9f6SMike Turquette }
1102b25d9f6SMike Turquette
twd_rate_change(struct notifier_block * nb,unsigned long flags,void * data)1112b25d9f6SMike Turquette static int twd_rate_change(struct notifier_block *nb,
1122b25d9f6SMike Turquette unsigned long flags, void *data)
1132b25d9f6SMike Turquette {
1142b25d9f6SMike Turquette struct clk_notifier_data *cnd = data;
1152b25d9f6SMike Turquette
1162b25d9f6SMike Turquette /*
1172b25d9f6SMike Turquette * The twd clock events must be reprogrammed to account for the new
1182b25d9f6SMike Turquette * frequency. The timer is local to a cpu, so cross-call to the
1192b25d9f6SMike Turquette * changing cpu.
1202b25d9f6SMike Turquette */
1212b25d9f6SMike Turquette if (flags == POST_RATE_CHANGE)
122cbbe6f82SJason Liu on_each_cpu(twd_update_frequency,
1232b25d9f6SMike Turquette (void *)&cnd->new_rate, 1);
1242b25d9f6SMike Turquette
1252b25d9f6SMike Turquette return NOTIFY_OK;
1262b25d9f6SMike Turquette }
1272b25d9f6SMike Turquette
1282b25d9f6SMike Turquette static struct notifier_block twd_clk_nb = {
1292b25d9f6SMike Turquette .notifier_call = twd_rate_change,
1302b25d9f6SMike Turquette };
1312b25d9f6SMike Turquette
twd_clk_init(void)1322b25d9f6SMike Turquette static int twd_clk_init(void)
1332b25d9f6SMike Turquette {
13406b96c8bSChristoph Lameter if (twd_evt && raw_cpu_ptr(twd_evt) && !IS_ERR(twd_clk))
1352b25d9f6SMike Turquette return clk_notifier_register(twd_clk, &twd_clk_nb);
1362b25d9f6SMike Turquette
1372b25d9f6SMike Turquette return 0;
1382b25d9f6SMike Turquette }
1392b25d9f6SMike Turquette core_initcall(twd_clk_init);
1402b25d9f6SMike Turquette
twd_calibrate_rate(void)1418bd26e3aSPaul Gortmaker static void twd_calibrate_rate(void)
142f32f4ce2SRussell King {
14303399c1cSRussell King unsigned long count;
144f32f4ce2SRussell King u64 waitjiffies;
145f32f4ce2SRussell King
146f32f4ce2SRussell King /*
147f32f4ce2SRussell King * If this is the first time round, we need to work out how fast
148f32f4ce2SRussell King * the timer ticks
149f32f4ce2SRussell King */
150f32f4ce2SRussell King if (twd_timer_rate == 0) {
1514ed89f22SRussell King pr_info("Calibrating local timer... ");
152f32f4ce2SRussell King
153f32f4ce2SRussell King /* Wait for a tick to start */
154f32f4ce2SRussell King waitjiffies = get_jiffies_64() + 1;
155f32f4ce2SRussell King
156f32f4ce2SRussell King while (get_jiffies_64() < waitjiffies)
157f32f4ce2SRussell King udelay(10);
158f32f4ce2SRussell King
159f32f4ce2SRussell King /* OK, now the tick has started, let's get the timer going */
160f32f4ce2SRussell King waitjiffies += 5;
161f32f4ce2SRussell King
162f32f4ce2SRussell King /* enable, no interrupt or reload */
1632e874ea3SBen Dooks writel_relaxed(0x1, twd_base + TWD_TIMER_CONTROL);
164f32f4ce2SRussell King
165f32f4ce2SRussell King /* maximum value */
1662e874ea3SBen Dooks writel_relaxed(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
167f32f4ce2SRussell King
168f32f4ce2SRussell King while (get_jiffies_64() < waitjiffies)
169f32f4ce2SRussell King udelay(10);
170f32f4ce2SRussell King
1712e874ea3SBen Dooks count = readl_relaxed(twd_base + TWD_TIMER_COUNTER);
172f32f4ce2SRussell King
173f32f4ce2SRussell King twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
174f32f4ce2SRussell King
1754ed89f22SRussell King pr_cont("%lu.%02luMHz.\n", twd_timer_rate / 1000000,
17690c5ffe5SVitaly Kuzmichev (twd_timer_rate / 10000) % 100);
177f32f4ce2SRussell King }
178f32f4ce2SRussell King }
179f32f4ce2SRussell King
twd_handler(int irq,void * dev_id)18028af690aSMarc Zyngier static irqreturn_t twd_handler(int irq, void *dev_id)
18128af690aSMarc Zyngier {
182a894fcc2SStephen Boyd struct clock_event_device *evt = dev_id;
18328af690aSMarc Zyngier
18428af690aSMarc Zyngier if (twd_timer_ack()) {
18528af690aSMarc Zyngier evt->event_handler(evt);
18628af690aSMarc Zyngier return IRQ_HANDLED;
18728af690aSMarc Zyngier }
18828af690aSMarc Zyngier
18928af690aSMarc Zyngier return IRQ_NONE;
19028af690aSMarc Zyngier }
19128af690aSMarc Zyngier
twd_get_clock(struct device_node * np)192bd603455SRob Herring static void twd_get_clock(struct device_node *np)
1935def51b0SLinus Walleij {
1945def51b0SLinus Walleij int err;
1955def51b0SLinus Walleij
196bd603455SRob Herring if (np)
197bd603455SRob Herring twd_clk = of_clk_get(np, 0);
198bd603455SRob Herring else
199bd603455SRob Herring twd_clk = clk_get_sys("smp_twd", NULL);
200bd603455SRob Herring
201bd603455SRob Herring if (IS_ERR(twd_clk)) {
202bd603455SRob Herring pr_err("smp_twd: clock not found %d\n", (int) PTR_ERR(twd_clk));
203bd603455SRob Herring return;
2045def51b0SLinus Walleij }
2055def51b0SLinus Walleij
206bd603455SRob Herring err = clk_prepare_enable(twd_clk);
2075def51b0SLinus Walleij if (err) {
2082577cf24SLinus Walleij pr_err("smp_twd: clock failed to prepare+enable: %d\n", err);
209bd603455SRob Herring clk_put(twd_clk);
210bd603455SRob Herring return;
2115def51b0SLinus Walleij }
2125def51b0SLinus Walleij
213bd603455SRob Herring twd_timer_rate = clk_get_rate(twd_clk);
2145def51b0SLinus Walleij }
2155def51b0SLinus Walleij
216f32f4ce2SRussell King /*
217f32f4ce2SRussell King * Setup the local clock events for a CPU.
218f32f4ce2SRussell King */
twd_timer_setup(void)21947dcd356SOlof Johansson static void twd_timer_setup(void)
220f32f4ce2SRussell King {
22106b96c8bSChristoph Lameter struct clock_event_device *clk = raw_cpu_ptr(twd_evt);
222a68becd1SLinus Walleij int cpu = smp_processor_id();
22328af690aSMarc Zyngier
224a68becd1SLinus Walleij /*
225a68becd1SLinus Walleij * If the basic setup for this CPU has been done before don't
226a68becd1SLinus Walleij * bother with the below.
227a68becd1SLinus Walleij */
228a68becd1SLinus Walleij if (per_cpu(percpu_setup_called, cpu)) {
2292e874ea3SBen Dooks writel_relaxed(0, twd_base + TWD_TIMER_CONTROL);
230a894fcc2SStephen Boyd clockevents_register_device(clk);
231a68becd1SLinus Walleij enable_percpu_irq(clk->irq, 0);
232a894fcc2SStephen Boyd return;
233a68becd1SLinus Walleij }
234a68becd1SLinus Walleij per_cpu(percpu_setup_called, cpu) = true;
235a68becd1SLinus Walleij
236f32f4ce2SRussell King twd_calibrate_rate();
237f32f4ce2SRussell King
238a68becd1SLinus Walleij /*
239a68becd1SLinus Walleij * The following is done once per CPU the first time .setup() is
240a68becd1SLinus Walleij * called.
241a68becd1SLinus Walleij */
2422e874ea3SBen Dooks writel_relaxed(0, twd_base + TWD_TIMER_CONTROL);
243c214455fSMarc Zyngier
244f32f4ce2SRussell King clk->name = "local_timer";
245e1b8c05dSRussell King clk->features = twd_features;
246f32f4ce2SRussell King clk->rating = 350;
2475e253571SViresh Kumar clk->set_state_shutdown = twd_shutdown;
2485e253571SViresh Kumar clk->set_state_periodic = twd_set_periodic;
2495e253571SViresh Kumar clk->set_state_oneshot = twd_set_oneshot;
2505e253571SViresh Kumar clk->tick_resume = twd_shutdown;
251f32f4ce2SRussell King clk->set_next_event = twd_set_next_event;
25281e46f7bSMarc Zyngier clk->irq = twd_ppi;
253a894fcc2SStephen Boyd clk->cpumask = cpumask_of(cpu);
25428af690aSMarc Zyngier
25554d15b1dSLinus Walleij clockevents_config_and_register(clk, twd_timer_rate,
25654d15b1dSLinus Walleij 0xf, 0xffffffff);
25728af690aSMarc Zyngier enable_percpu_irq(clk->irq, 0);
25881e46f7bSMarc Zyngier }
25981e46f7bSMarc Zyngier
twd_timer_starting_cpu(unsigned int cpu)26026b87688SRichard Cochran static int twd_timer_starting_cpu(unsigned int cpu)
261a894fcc2SStephen Boyd {
262a894fcc2SStephen Boyd twd_timer_setup();
26326b87688SRichard Cochran return 0;
26426b87688SRichard Cochran }
26526b87688SRichard Cochran
twd_timer_dying_cpu(unsigned int cpu)26626b87688SRichard Cochran static int twd_timer_dying_cpu(unsigned int cpu)
26726b87688SRichard Cochran {
268a894fcc2SStephen Boyd twd_timer_stop();
26926b87688SRichard Cochran return 0;
270a894fcc2SStephen Boyd }
271a894fcc2SStephen Boyd
twd_local_timer_common_register(struct device_node * np)272bd603455SRob Herring static int __init twd_local_timer_common_register(struct device_node *np)
27381e46f7bSMarc Zyngier {
27481e46f7bSMarc Zyngier int err;
27581e46f7bSMarc Zyngier
276a894fcc2SStephen Boyd twd_evt = alloc_percpu(struct clock_event_device);
277d8e03643SMarc Zyngier if (!twd_evt) {
27881e46f7bSMarc Zyngier err = -ENOMEM;
279d8e03643SMarc Zyngier goto out_free;
28081e46f7bSMarc Zyngier }
28181e46f7bSMarc Zyngier
28281e46f7bSMarc Zyngier err = request_percpu_irq(twd_ppi, twd_handler, "twd", twd_evt);
28381e46f7bSMarc Zyngier if (err) {
28481e46f7bSMarc Zyngier pr_err("twd: can't register interrupt %d (%d)\n", twd_ppi, err);
285d8e03643SMarc Zyngier goto out_free;
28681e46f7bSMarc Zyngier }
28781e46f7bSMarc Zyngier
28826b87688SRichard Cochran cpuhp_setup_state_nocalls(CPUHP_AP_ARM_TWD_STARTING,
28973c1b41eSThomas Gleixner "arm/timer/twd:starting",
29026b87688SRichard Cochran twd_timer_starting_cpu, twd_timer_dying_cpu);
29181e46f7bSMarc Zyngier
292bd603455SRob Herring twd_get_clock(np);
293194444c5SMarc Gonzalez if (!of_property_read_bool(np, "always-on"))
294e1b8c05dSRussell King twd_features |= CLOCK_EVT_FEAT_C3STOP;
295bd603455SRob Herring
296a894fcc2SStephen Boyd /*
297a894fcc2SStephen Boyd * Immediately configure the timer on the boot CPU, unless we need
298a894fcc2SStephen Boyd * jiffies to be incrementing to calibrate the rate in which case
299a894fcc2SStephen Boyd * setup the timer in late_time_init.
300a894fcc2SStephen Boyd */
301a894fcc2SStephen Boyd if (twd_timer_rate)
302a894fcc2SStephen Boyd twd_timer_setup();
303a894fcc2SStephen Boyd else
304a894fcc2SStephen Boyd late_time_init = twd_timer_setup;
305a894fcc2SStephen Boyd
30681e46f7bSMarc Zyngier return 0;
30781e46f7bSMarc Zyngier
308d8e03643SMarc Zyngier out_free:
30981e46f7bSMarc Zyngier iounmap(twd_base);
310d8e03643SMarc Zyngier twd_base = NULL;
31181e46f7bSMarc Zyngier free_percpu(twd_evt);
312d8e03643SMarc Zyngier
31381e46f7bSMarc Zyngier return err;
314f32f4ce2SRussell King }
315d8e03643SMarc Zyngier
twd_local_timer_of_register(struct device_node * np)316dcbc0eddSDaniel Lezcano static int __init twd_local_timer_of_register(struct device_node *np)
317d8e03643SMarc Zyngier {
318d8e03643SMarc Zyngier int err;
319d8e03643SMarc Zyngier
320d8e03643SMarc Zyngier twd_ppi = irq_of_parse_and_map(np, 0);
321d8e03643SMarc Zyngier if (!twd_ppi) {
322d8e03643SMarc Zyngier err = -EINVAL;
323d8e03643SMarc Zyngier goto out;
324d8e03643SMarc Zyngier }
325d8e03643SMarc Zyngier
326d8e03643SMarc Zyngier twd_base = of_iomap(np, 0);
327d8e03643SMarc Zyngier if (!twd_base) {
328d8e03643SMarc Zyngier err = -ENOMEM;
329d8e03643SMarc Zyngier goto out;
330d8e03643SMarc Zyngier }
331d8e03643SMarc Zyngier
332bd603455SRob Herring err = twd_local_timer_common_register(np);
333d8e03643SMarc Zyngier
334d8e03643SMarc Zyngier out:
335d8e03643SMarc Zyngier WARN(err, "twd_local_timer_of_register failed (%d)\n", err);
336dcbc0eddSDaniel Lezcano return err;
337d8e03643SMarc Zyngier }
33817273395SDaniel Lezcano TIMER_OF_DECLARE(arm_twd_a9, "arm,cortex-a9-twd-timer", twd_local_timer_of_register);
33917273395SDaniel Lezcano TIMER_OF_DECLARE(arm_twd_a5, "arm,cortex-a5-twd-timer", twd_local_timer_of_register);
34017273395SDaniel Lezcano TIMER_OF_DECLARE(arm_twd_11mp, "arm,arm11mp-twd-timer", twd_local_timer_of_register);
341