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