xref: /openbmc/linux/arch/arm/kernel/smp_twd.c (revision dcbc0edd)
1f32f4ce2SRussell King /*
2f32f4ce2SRussell King  *  linux/arch/arm/kernel/smp_twd.c
3f32f4ce2SRussell King  *
4f32f4ce2SRussell King  *  Copyright (C) 2002 ARM Ltd.
5f32f4ce2SRussell King  *  All Rights Reserved
6f32f4ce2SRussell King  *
7f32f4ce2SRussell King  * This program is free software; you can redistribute it and/or modify
8f32f4ce2SRussell King  * it under the terms of the GNU General Public License version 2 as
9f32f4ce2SRussell King  * published by the Free Software Foundation.
10f32f4ce2SRussell King  */
11f32f4ce2SRussell King #include <linux/init.h>
12f32f4ce2SRussell King #include <linux/kernel.h>
135def51b0SLinus Walleij #include <linux/clk.h>
14a894fcc2SStephen Boyd #include <linux/cpu.h>
15f32f4ce2SRussell King #include <linux/delay.h>
16f32f4ce2SRussell King #include <linux/device.h>
175def51b0SLinus Walleij #include <linux/err.h>
18f32f4ce2SRussell King #include <linux/smp.h>
19f32f4ce2SRussell King #include <linux/jiffies.h>
20f32f4ce2SRussell King #include <linux/clockchips.h>
2192485104SMarc Zyngier #include <linux/interrupt.h>
22f32f4ce2SRussell King #include <linux/io.h>
23d8e03643SMarc Zyngier #include <linux/of_irq.h>
24d8e03643SMarc Zyngier #include <linux/of_address.h>
25f32f4ce2SRussell King 
26f32f4ce2SRussell King #include <asm/smp_twd.h>
27f32f4ce2SRussell King 
28f32f4ce2SRussell King /* set up by the platform code */
2992485104SMarc Zyngier static void __iomem *twd_base;
30f32f4ce2SRussell King 
315def51b0SLinus Walleij static struct clk *twd_clk;
32f32f4ce2SRussell King static unsigned long twd_timer_rate;
33a68becd1SLinus Walleij static DEFINE_PER_CPU(bool, percpu_setup_called);
34f32f4ce2SRussell King 
35a894fcc2SStephen Boyd static struct clock_event_device __percpu *twd_evt;
36e1b8c05dSRussell King static unsigned int twd_features =
37e1b8c05dSRussell King 		CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
3881e46f7bSMarc Zyngier static int twd_ppi;
3928af690aSMarc Zyngier 
405e253571SViresh Kumar static int twd_shutdown(struct clock_event_device *clk)
41f32f4ce2SRussell King {
425e253571SViresh Kumar 	writel_relaxed(0, twd_base + TWD_TIMER_CONTROL);
435e253571SViresh Kumar 	return 0;
44f32f4ce2SRussell King }
45f32f4ce2SRussell King 
465e253571SViresh Kumar static int twd_set_oneshot(struct clock_event_device *clk)
475e253571SViresh Kumar {
485e253571SViresh Kumar 	/* period set, and timer enabled in 'next_event' hook */
495e253571SViresh Kumar 	writel_relaxed(TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT,
505e253571SViresh Kumar 		       twd_base + TWD_TIMER_CONTROL);
515e253571SViresh Kumar 	return 0;
525e253571SViresh Kumar }
535e253571SViresh Kumar 
545e253571SViresh Kumar static int twd_set_periodic(struct clock_event_device *clk)
555e253571SViresh Kumar {
565e253571SViresh Kumar 	unsigned long ctrl = TWD_TIMER_CONTROL_ENABLE |
575e253571SViresh Kumar 			     TWD_TIMER_CONTROL_IT_ENABLE |
585e253571SViresh Kumar 			     TWD_TIMER_CONTROL_PERIODIC;
595e253571SViresh Kumar 
605e253571SViresh Kumar 	writel_relaxed(DIV_ROUND_CLOSEST(twd_timer_rate, HZ),
615e253571SViresh Kumar 		       twd_base + TWD_TIMER_LOAD);
622e874ea3SBen Dooks 	writel_relaxed(ctrl, twd_base + TWD_TIMER_CONTROL);
635e253571SViresh Kumar 	return 0;
64f32f4ce2SRussell King }
65f32f4ce2SRussell King 
66f32f4ce2SRussell King static int twd_set_next_event(unsigned long evt,
67f32f4ce2SRussell King 			struct clock_event_device *unused)
68f32f4ce2SRussell King {
692e874ea3SBen Dooks 	unsigned long ctrl = readl_relaxed(twd_base + TWD_TIMER_CONTROL);
70f32f4ce2SRussell King 
714c5158d4SRussell King 	ctrl |= TWD_TIMER_CONTROL_ENABLE;
724c5158d4SRussell King 
732e874ea3SBen Dooks 	writel_relaxed(evt, twd_base + TWD_TIMER_COUNTER);
742e874ea3SBen Dooks 	writel_relaxed(ctrl, twd_base + TWD_TIMER_CONTROL);
75f32f4ce2SRussell King 
76f32f4ce2SRussell King 	return 0;
77f32f4ce2SRussell King }
78f32f4ce2SRussell King 
79f32f4ce2SRussell King /*
80f32f4ce2SRussell King  * local_timer_ack: checks for a local timer interrupt.
81f32f4ce2SRussell King  *
82f32f4ce2SRussell King  * If a local timer interrupt has occurred, acknowledge and return 1.
83f32f4ce2SRussell King  * Otherwise, return 0.
84f32f4ce2SRussell King  */
8592485104SMarc Zyngier static int twd_timer_ack(void)
86f32f4ce2SRussell King {
872e874ea3SBen Dooks 	if (readl_relaxed(twd_base + TWD_TIMER_INTSTAT)) {
882e874ea3SBen Dooks 		writel_relaxed(1, twd_base + TWD_TIMER_INTSTAT);
89f32f4ce2SRussell King 		return 1;
90f32f4ce2SRussell King 	}
91f32f4ce2SRussell King 
92f32f4ce2SRussell King 	return 0;
93f32f4ce2SRussell King }
94f32f4ce2SRussell King 
95a894fcc2SStephen Boyd static void twd_timer_stop(void)
9628af690aSMarc Zyngier {
9706b96c8bSChristoph Lameter 	struct clock_event_device *clk = raw_cpu_ptr(twd_evt);
98a894fcc2SStephen Boyd 
995e253571SViresh Kumar 	twd_shutdown(clk);
10028af690aSMarc Zyngier 	disable_percpu_irq(clk->irq);
10128af690aSMarc Zyngier }
10228af690aSMarc Zyngier 
1032b25d9f6SMike Turquette #ifdef CONFIG_COMMON_CLK
1042b25d9f6SMike Turquette 
1052b25d9f6SMike Turquette /*
1062b25d9f6SMike Turquette  * Updates clockevent frequency when the cpu frequency changes.
1072b25d9f6SMike Turquette  * Called on the cpu that is changing frequency with interrupts disabled.
1082b25d9f6SMike Turquette  */
1092b25d9f6SMike Turquette static void twd_update_frequency(void *new_rate)
1102b25d9f6SMike Turquette {
1112b25d9f6SMike Turquette 	twd_timer_rate = *((unsigned long *) new_rate);
1122b25d9f6SMike Turquette 
11306b96c8bSChristoph Lameter 	clockevents_update_freq(raw_cpu_ptr(twd_evt), twd_timer_rate);
1142b25d9f6SMike Turquette }
1152b25d9f6SMike Turquette 
1162b25d9f6SMike Turquette static int twd_rate_change(struct notifier_block *nb,
1172b25d9f6SMike Turquette 	unsigned long flags, void *data)
1182b25d9f6SMike Turquette {
1192b25d9f6SMike Turquette 	struct clk_notifier_data *cnd = data;
1202b25d9f6SMike Turquette 
1212b25d9f6SMike Turquette 	/*
1222b25d9f6SMike Turquette 	 * The twd clock events must be reprogrammed to account for the new
1232b25d9f6SMike Turquette 	 * frequency.  The timer is local to a cpu, so cross-call to the
1242b25d9f6SMike Turquette 	 * changing cpu.
1252b25d9f6SMike Turquette 	 */
1262b25d9f6SMike Turquette 	if (flags == POST_RATE_CHANGE)
127cbbe6f82SJason Liu 		on_each_cpu(twd_update_frequency,
1282b25d9f6SMike Turquette 				  (void *)&cnd->new_rate, 1);
1292b25d9f6SMike Turquette 
1302b25d9f6SMike Turquette 	return NOTIFY_OK;
1312b25d9f6SMike Turquette }
1322b25d9f6SMike Turquette 
1332b25d9f6SMike Turquette static struct notifier_block twd_clk_nb = {
1342b25d9f6SMike Turquette 	.notifier_call = twd_rate_change,
1352b25d9f6SMike Turquette };
1362b25d9f6SMike Turquette 
1372b25d9f6SMike Turquette static int twd_clk_init(void)
1382b25d9f6SMike Turquette {
13906b96c8bSChristoph Lameter 	if (twd_evt && raw_cpu_ptr(twd_evt) && !IS_ERR(twd_clk))
1402b25d9f6SMike Turquette 		return clk_notifier_register(twd_clk, &twd_clk_nb);
1412b25d9f6SMike Turquette 
1422b25d9f6SMike Turquette 	return 0;
1432b25d9f6SMike Turquette }
1442b25d9f6SMike Turquette core_initcall(twd_clk_init);
1452b25d9f6SMike Turquette 
1462b25d9f6SMike Turquette #elif defined (CONFIG_CPU_FREQ)
1472b25d9f6SMike Turquette 
1482b25d9f6SMike Turquette #include <linux/cpufreq.h>
1494fd7f9b1SLinus Walleij 
1504fd7f9b1SLinus Walleij /*
1514fd7f9b1SLinus Walleij  * Updates clockevent frequency when the cpu frequency changes.
1524fd7f9b1SLinus Walleij  * Called on the cpu that is changing frequency with interrupts disabled.
1534fd7f9b1SLinus Walleij  */
1544fd7f9b1SLinus Walleij static void twd_update_frequency(void *data)
1554fd7f9b1SLinus Walleij {
1564fd7f9b1SLinus Walleij 	twd_timer_rate = clk_get_rate(twd_clk);
1574fd7f9b1SLinus Walleij 
15806b96c8bSChristoph Lameter 	clockevents_update_freq(raw_cpu_ptr(twd_evt), twd_timer_rate);
1594fd7f9b1SLinus Walleij }
1604fd7f9b1SLinus Walleij 
1614fd7f9b1SLinus Walleij static int twd_cpufreq_transition(struct notifier_block *nb,
1624fd7f9b1SLinus Walleij 	unsigned long state, void *data)
1634fd7f9b1SLinus Walleij {
1644fd7f9b1SLinus Walleij 	struct cpufreq_freqs *freqs = data;
1654fd7f9b1SLinus Walleij 
1664fd7f9b1SLinus Walleij 	/*
1674fd7f9b1SLinus Walleij 	 * The twd clock events must be reprogrammed to account for the new
1684fd7f9b1SLinus Walleij 	 * frequency.  The timer is local to a cpu, so cross-call to the
1694fd7f9b1SLinus Walleij 	 * changing cpu.
1704fd7f9b1SLinus Walleij 	 */
1710b443eadSViresh Kumar 	if (state == CPUFREQ_POSTCHANGE)
1724fd7f9b1SLinus Walleij 		smp_call_function_single(freqs->cpu, twd_update_frequency,
1733cd88f99SRussell King 			NULL, 1);
1744fd7f9b1SLinus Walleij 
1754fd7f9b1SLinus Walleij 	return NOTIFY_OK;
1764fd7f9b1SLinus Walleij }
1774fd7f9b1SLinus Walleij 
1784fd7f9b1SLinus Walleij static struct notifier_block twd_cpufreq_nb = {
1794fd7f9b1SLinus Walleij 	.notifier_call = twd_cpufreq_transition,
1804fd7f9b1SLinus Walleij };
1814fd7f9b1SLinus Walleij 
1824fd7f9b1SLinus Walleij static int twd_cpufreq_init(void)
1834fd7f9b1SLinus Walleij {
18406b96c8bSChristoph Lameter 	if (twd_evt && raw_cpu_ptr(twd_evt) && !IS_ERR(twd_clk))
1854fd7f9b1SLinus Walleij 		return cpufreq_register_notifier(&twd_cpufreq_nb,
1864fd7f9b1SLinus Walleij 			CPUFREQ_TRANSITION_NOTIFIER);
1874fd7f9b1SLinus Walleij 
1884fd7f9b1SLinus Walleij 	return 0;
1894fd7f9b1SLinus Walleij }
1904fd7f9b1SLinus Walleij core_initcall(twd_cpufreq_init);
1914fd7f9b1SLinus Walleij 
1924fd7f9b1SLinus Walleij #endif
1934fd7f9b1SLinus Walleij 
1948bd26e3aSPaul Gortmaker static void twd_calibrate_rate(void)
195f32f4ce2SRussell King {
19603399c1cSRussell King 	unsigned long count;
197f32f4ce2SRussell King 	u64 waitjiffies;
198f32f4ce2SRussell King 
199f32f4ce2SRussell King 	/*
200f32f4ce2SRussell King 	 * If this is the first time round, we need to work out how fast
201f32f4ce2SRussell King 	 * the timer ticks
202f32f4ce2SRussell King 	 */
203f32f4ce2SRussell King 	if (twd_timer_rate == 0) {
2044ed89f22SRussell King 		pr_info("Calibrating local timer... ");
205f32f4ce2SRussell King 
206f32f4ce2SRussell King 		/* Wait for a tick to start */
207f32f4ce2SRussell King 		waitjiffies = get_jiffies_64() + 1;
208f32f4ce2SRussell King 
209f32f4ce2SRussell King 		while (get_jiffies_64() < waitjiffies)
210f32f4ce2SRussell King 			udelay(10);
211f32f4ce2SRussell King 
212f32f4ce2SRussell King 		/* OK, now the tick has started, let's get the timer going */
213f32f4ce2SRussell King 		waitjiffies += 5;
214f32f4ce2SRussell King 
215f32f4ce2SRussell King 				 /* enable, no interrupt or reload */
2162e874ea3SBen Dooks 		writel_relaxed(0x1, twd_base + TWD_TIMER_CONTROL);
217f32f4ce2SRussell King 
218f32f4ce2SRussell King 				 /* maximum value */
2192e874ea3SBen Dooks 		writel_relaxed(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
220f32f4ce2SRussell King 
221f32f4ce2SRussell King 		while (get_jiffies_64() < waitjiffies)
222f32f4ce2SRussell King 			udelay(10);
223f32f4ce2SRussell King 
2242e874ea3SBen Dooks 		count = readl_relaxed(twd_base + TWD_TIMER_COUNTER);
225f32f4ce2SRussell King 
226f32f4ce2SRussell King 		twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
227f32f4ce2SRussell King 
2284ed89f22SRussell King 		pr_cont("%lu.%02luMHz.\n", twd_timer_rate / 1000000,
22990c5ffe5SVitaly Kuzmichev 			(twd_timer_rate / 10000) % 100);
230f32f4ce2SRussell King 	}
231f32f4ce2SRussell King }
232f32f4ce2SRussell King 
23328af690aSMarc Zyngier static irqreturn_t twd_handler(int irq, void *dev_id)
23428af690aSMarc Zyngier {
235a894fcc2SStephen Boyd 	struct clock_event_device *evt = dev_id;
23628af690aSMarc Zyngier 
23728af690aSMarc Zyngier 	if (twd_timer_ack()) {
23828af690aSMarc Zyngier 		evt->event_handler(evt);
23928af690aSMarc Zyngier 		return IRQ_HANDLED;
24028af690aSMarc Zyngier 	}
24128af690aSMarc Zyngier 
24228af690aSMarc Zyngier 	return IRQ_NONE;
24328af690aSMarc Zyngier }
24428af690aSMarc Zyngier 
245bd603455SRob Herring static void twd_get_clock(struct device_node *np)
2465def51b0SLinus Walleij {
2475def51b0SLinus Walleij 	int err;
2485def51b0SLinus Walleij 
249bd603455SRob Herring 	if (np)
250bd603455SRob Herring 		twd_clk = of_clk_get(np, 0);
251bd603455SRob Herring 	else
252bd603455SRob Herring 		twd_clk = clk_get_sys("smp_twd", NULL);
253bd603455SRob Herring 
254bd603455SRob Herring 	if (IS_ERR(twd_clk)) {
255bd603455SRob Herring 		pr_err("smp_twd: clock not found %d\n", (int) PTR_ERR(twd_clk));
256bd603455SRob Herring 		return;
2575def51b0SLinus Walleij 	}
2585def51b0SLinus Walleij 
259bd603455SRob Herring 	err = clk_prepare_enable(twd_clk);
2605def51b0SLinus Walleij 	if (err) {
2612577cf24SLinus Walleij 		pr_err("smp_twd: clock failed to prepare+enable: %d\n", err);
262bd603455SRob Herring 		clk_put(twd_clk);
263bd603455SRob Herring 		return;
2645def51b0SLinus Walleij 	}
2655def51b0SLinus Walleij 
266bd603455SRob Herring 	twd_timer_rate = clk_get_rate(twd_clk);
2675def51b0SLinus Walleij }
2685def51b0SLinus Walleij 
269f32f4ce2SRussell King /*
270f32f4ce2SRussell King  * Setup the local clock events for a CPU.
271f32f4ce2SRussell King  */
27247dcd356SOlof Johansson static void twd_timer_setup(void)
273f32f4ce2SRussell King {
27406b96c8bSChristoph Lameter 	struct clock_event_device *clk = raw_cpu_ptr(twd_evt);
275a68becd1SLinus Walleij 	int cpu = smp_processor_id();
27628af690aSMarc Zyngier 
277a68becd1SLinus Walleij 	/*
278a68becd1SLinus Walleij 	 * If the basic setup for this CPU has been done before don't
279a68becd1SLinus Walleij 	 * bother with the below.
280a68becd1SLinus Walleij 	 */
281a68becd1SLinus Walleij 	if (per_cpu(percpu_setup_called, cpu)) {
2822e874ea3SBen Dooks 		writel_relaxed(0, twd_base + TWD_TIMER_CONTROL);
283a894fcc2SStephen Boyd 		clockevents_register_device(clk);
284a68becd1SLinus Walleij 		enable_percpu_irq(clk->irq, 0);
285a894fcc2SStephen Boyd 		return;
286a68becd1SLinus Walleij 	}
287a68becd1SLinus Walleij 	per_cpu(percpu_setup_called, cpu) = true;
288a68becd1SLinus Walleij 
289f32f4ce2SRussell King 	twd_calibrate_rate();
290f32f4ce2SRussell King 
291a68becd1SLinus Walleij 	/*
292a68becd1SLinus Walleij 	 * The following is done once per CPU the first time .setup() is
293a68becd1SLinus Walleij 	 * called.
294a68becd1SLinus Walleij 	 */
2952e874ea3SBen Dooks 	writel_relaxed(0, twd_base + TWD_TIMER_CONTROL);
296c214455fSMarc Zyngier 
297f32f4ce2SRussell King 	clk->name = "local_timer";
298e1b8c05dSRussell King 	clk->features = twd_features;
299f32f4ce2SRussell King 	clk->rating = 350;
3005e253571SViresh Kumar 	clk->set_state_shutdown = twd_shutdown;
3015e253571SViresh Kumar 	clk->set_state_periodic = twd_set_periodic;
3025e253571SViresh Kumar 	clk->set_state_oneshot = twd_set_oneshot;
3035e253571SViresh Kumar 	clk->tick_resume = twd_shutdown;
304f32f4ce2SRussell King 	clk->set_next_event = twd_set_next_event;
30581e46f7bSMarc Zyngier 	clk->irq = twd_ppi;
306a894fcc2SStephen Boyd 	clk->cpumask = cpumask_of(cpu);
30728af690aSMarc Zyngier 
30854d15b1dSLinus Walleij 	clockevents_config_and_register(clk, twd_timer_rate,
30954d15b1dSLinus Walleij 					0xf, 0xffffffff);
31028af690aSMarc Zyngier 	enable_percpu_irq(clk->irq, 0);
31181e46f7bSMarc Zyngier }
31281e46f7bSMarc Zyngier 
31347dcd356SOlof Johansson static int twd_timer_cpu_notify(struct notifier_block *self,
314a894fcc2SStephen Boyd 				unsigned long action, void *hcpu)
315a894fcc2SStephen Boyd {
316a894fcc2SStephen Boyd 	switch (action & ~CPU_TASKS_FROZEN) {
317a894fcc2SStephen Boyd 	case CPU_STARTING:
318a894fcc2SStephen Boyd 		twd_timer_setup();
319a894fcc2SStephen Boyd 		break;
320a894fcc2SStephen Boyd 	case CPU_DYING:
321a894fcc2SStephen Boyd 		twd_timer_stop();
322a894fcc2SStephen Boyd 		break;
323a894fcc2SStephen Boyd 	}
324a894fcc2SStephen Boyd 
325a894fcc2SStephen Boyd 	return NOTIFY_OK;
326a894fcc2SStephen Boyd }
327a894fcc2SStephen Boyd 
32847dcd356SOlof Johansson static struct notifier_block twd_timer_cpu_nb = {
329a894fcc2SStephen Boyd 	.notifier_call = twd_timer_cpu_notify,
33081e46f7bSMarc Zyngier };
33181e46f7bSMarc Zyngier 
332bd603455SRob Herring static int __init twd_local_timer_common_register(struct device_node *np)
33381e46f7bSMarc Zyngier {
33481e46f7bSMarc Zyngier 	int err;
33581e46f7bSMarc Zyngier 
336a894fcc2SStephen Boyd 	twd_evt = alloc_percpu(struct clock_event_device);
337d8e03643SMarc Zyngier 	if (!twd_evt) {
33881e46f7bSMarc Zyngier 		err = -ENOMEM;
339d8e03643SMarc Zyngier 		goto out_free;
34081e46f7bSMarc Zyngier 	}
34181e46f7bSMarc Zyngier 
34281e46f7bSMarc Zyngier 	err = request_percpu_irq(twd_ppi, twd_handler, "twd", twd_evt);
34381e46f7bSMarc Zyngier 	if (err) {
34481e46f7bSMarc Zyngier 		pr_err("twd: can't register interrupt %d (%d)\n", twd_ppi, err);
345d8e03643SMarc Zyngier 		goto out_free;
34681e46f7bSMarc Zyngier 	}
34781e46f7bSMarc Zyngier 
348a894fcc2SStephen Boyd 	err = register_cpu_notifier(&twd_timer_cpu_nb);
34981e46f7bSMarc Zyngier 	if (err)
350d8e03643SMarc Zyngier 		goto out_irq;
35181e46f7bSMarc Zyngier 
352bd603455SRob Herring 	twd_get_clock(np);
353194444c5SMarc Gonzalez 	if (!of_property_read_bool(np, "always-on"))
354e1b8c05dSRussell King 		twd_features |= CLOCK_EVT_FEAT_C3STOP;
355bd603455SRob Herring 
356a894fcc2SStephen Boyd 	/*
357a894fcc2SStephen Boyd 	 * Immediately configure the timer on the boot CPU, unless we need
358a894fcc2SStephen Boyd 	 * jiffies to be incrementing to calibrate the rate in which case
359a894fcc2SStephen Boyd 	 * setup the timer in late_time_init.
360a894fcc2SStephen Boyd 	 */
361a894fcc2SStephen Boyd 	if (twd_timer_rate)
362a894fcc2SStephen Boyd 		twd_timer_setup();
363a894fcc2SStephen Boyd 	else
364a894fcc2SStephen Boyd 		late_time_init = twd_timer_setup;
365a894fcc2SStephen Boyd 
36681e46f7bSMarc Zyngier 	return 0;
36781e46f7bSMarc Zyngier 
368d8e03643SMarc Zyngier out_irq:
369d8e03643SMarc Zyngier 	free_percpu_irq(twd_ppi, twd_evt);
370d8e03643SMarc Zyngier out_free:
37181e46f7bSMarc Zyngier 	iounmap(twd_base);
372d8e03643SMarc Zyngier 	twd_base = NULL;
37381e46f7bSMarc Zyngier 	free_percpu(twd_evt);
374d8e03643SMarc Zyngier 
37581e46f7bSMarc Zyngier 	return err;
376f32f4ce2SRussell King }
377d8e03643SMarc Zyngier 
378d8e03643SMarc Zyngier int __init twd_local_timer_register(struct twd_local_timer *tlt)
379d8e03643SMarc Zyngier {
380d8e03643SMarc Zyngier 	if (twd_base || twd_evt)
381d8e03643SMarc Zyngier 		return -EBUSY;
382d8e03643SMarc Zyngier 
383d8e03643SMarc Zyngier 	twd_ppi	= tlt->res[1].start;
384d8e03643SMarc Zyngier 
385d8e03643SMarc Zyngier 	twd_base = ioremap(tlt->res[0].start, resource_size(&tlt->res[0]));
386d8e03643SMarc Zyngier 	if (!twd_base)
387d8e03643SMarc Zyngier 		return -ENOMEM;
388d8e03643SMarc Zyngier 
389bd603455SRob Herring 	return twd_local_timer_common_register(NULL);
390d8e03643SMarc Zyngier }
391d8e03643SMarc Zyngier 
392d8e03643SMarc Zyngier #ifdef CONFIG_OF
393dcbc0eddSDaniel Lezcano static int __init twd_local_timer_of_register(struct device_node *np)
394d8e03643SMarc Zyngier {
395d8e03643SMarc Zyngier 	int err;
396d8e03643SMarc Zyngier 
397d8e03643SMarc Zyngier 	twd_ppi = irq_of_parse_and_map(np, 0);
398d8e03643SMarc Zyngier 	if (!twd_ppi) {
399d8e03643SMarc Zyngier 		err = -EINVAL;
400d8e03643SMarc Zyngier 		goto out;
401d8e03643SMarc Zyngier 	}
402d8e03643SMarc Zyngier 
403d8e03643SMarc Zyngier 	twd_base = of_iomap(np, 0);
404d8e03643SMarc Zyngier 	if (!twd_base) {
405d8e03643SMarc Zyngier 		err = -ENOMEM;
406d8e03643SMarc Zyngier 		goto out;
407d8e03643SMarc Zyngier 	}
408d8e03643SMarc Zyngier 
409bd603455SRob Herring 	err = twd_local_timer_common_register(np);
410d8e03643SMarc Zyngier 
411d8e03643SMarc Zyngier out:
412d8e03643SMarc Zyngier 	WARN(err, "twd_local_timer_of_register failed (%d)\n", err);
413dcbc0eddSDaniel Lezcano 	return err;
414d8e03643SMarc Zyngier }
415dcbc0eddSDaniel Lezcano CLOCKSOURCE_OF_DECLARE_RET(arm_twd_a9, "arm,cortex-a9-twd-timer", twd_local_timer_of_register);
416dcbc0eddSDaniel Lezcano CLOCKSOURCE_OF_DECLARE_RET(arm_twd_a5, "arm,cortex-a5-twd-timer", twd_local_timer_of_register);
417dcbc0eddSDaniel Lezcano CLOCKSOURCE_OF_DECLARE_RET(arm_twd_11mp, "arm,arm11mp-twd-timer", twd_local_timer_of_register);
418d8e03643SMarc Zyngier #endif
419