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> 144fd7f9b1SLinus Walleij #include <linux/cpufreq.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> 2728af690aSMarc Zyngier #include <asm/localtimer.h> 28f32f4ce2SRussell King #include <asm/hardware/gic.h> 29f32f4ce2SRussell King 30f32f4ce2SRussell King /* set up by the platform code */ 3192485104SMarc Zyngier static void __iomem *twd_base; 32f32f4ce2SRussell King 335def51b0SLinus Walleij static struct clk *twd_clk; 34f32f4ce2SRussell King static unsigned long twd_timer_rate; 35f32f4ce2SRussell King 3628af690aSMarc Zyngier static struct clock_event_device __percpu **twd_evt; 3781e46f7bSMarc Zyngier static int twd_ppi; 3828af690aSMarc Zyngier 39f32f4ce2SRussell King static void twd_set_mode(enum clock_event_mode mode, 40f32f4ce2SRussell King struct clock_event_device *clk) 41f32f4ce2SRussell King { 42f32f4ce2SRussell King unsigned long ctrl; 43f32f4ce2SRussell King 44f32f4ce2SRussell King switch (mode) { 45f32f4ce2SRussell King case CLOCK_EVT_MODE_PERIODIC: 46f32f4ce2SRussell King /* timer load already set up */ 47f32f4ce2SRussell King ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE 48f32f4ce2SRussell King | TWD_TIMER_CONTROL_PERIODIC; 4903399c1cSRussell King __raw_writel(twd_timer_rate / HZ, twd_base + TWD_TIMER_LOAD); 50f32f4ce2SRussell King break; 51f32f4ce2SRussell King case CLOCK_EVT_MODE_ONESHOT: 52f32f4ce2SRussell King /* period set, and timer enabled in 'next_event' hook */ 53f32f4ce2SRussell King ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT; 54f32f4ce2SRussell King break; 55f32f4ce2SRussell King case CLOCK_EVT_MODE_UNUSED: 56f32f4ce2SRussell King case CLOCK_EVT_MODE_SHUTDOWN: 57f32f4ce2SRussell King default: 58f32f4ce2SRussell King ctrl = 0; 59f32f4ce2SRussell King } 60f32f4ce2SRussell King 61f32f4ce2SRussell King __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL); 62f32f4ce2SRussell King } 63f32f4ce2SRussell King 64f32f4ce2SRussell King static int twd_set_next_event(unsigned long evt, 65f32f4ce2SRussell King struct clock_event_device *unused) 66f32f4ce2SRussell King { 67f32f4ce2SRussell King unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL); 68f32f4ce2SRussell King 694c5158d4SRussell King ctrl |= TWD_TIMER_CONTROL_ENABLE; 704c5158d4SRussell King 71f32f4ce2SRussell King __raw_writel(evt, twd_base + TWD_TIMER_COUNTER); 724c5158d4SRussell King __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL); 73f32f4ce2SRussell King 74f32f4ce2SRussell King return 0; 75f32f4ce2SRussell King } 76f32f4ce2SRussell King 77f32f4ce2SRussell King /* 78f32f4ce2SRussell King * local_timer_ack: checks for a local timer interrupt. 79f32f4ce2SRussell King * 80f32f4ce2SRussell King * If a local timer interrupt has occurred, acknowledge and return 1. 81f32f4ce2SRussell King * Otherwise, return 0. 82f32f4ce2SRussell King */ 8392485104SMarc Zyngier static int twd_timer_ack(void) 84f32f4ce2SRussell King { 85f32f4ce2SRussell King if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) { 86f32f4ce2SRussell King __raw_writel(1, twd_base + TWD_TIMER_INTSTAT); 87f32f4ce2SRussell King return 1; 88f32f4ce2SRussell King } 89f32f4ce2SRussell King 90f32f4ce2SRussell King return 0; 91f32f4ce2SRussell King } 92f32f4ce2SRussell King 93abde710cSMarc Zyngier static void twd_timer_stop(struct clock_event_device *clk) 9428af690aSMarc Zyngier { 9528af690aSMarc Zyngier twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk); 9628af690aSMarc Zyngier disable_percpu_irq(clk->irq); 9728af690aSMarc Zyngier } 9828af690aSMarc Zyngier 994fd7f9b1SLinus Walleij #ifdef CONFIG_CPU_FREQ 1004fd7f9b1SLinus Walleij 1014fd7f9b1SLinus Walleij /* 1024fd7f9b1SLinus Walleij * Updates clockevent frequency when the cpu frequency changes. 1034fd7f9b1SLinus Walleij * Called on the cpu that is changing frequency with interrupts disabled. 1044fd7f9b1SLinus Walleij */ 1054fd7f9b1SLinus Walleij static void twd_update_frequency(void *data) 1064fd7f9b1SLinus Walleij { 1074fd7f9b1SLinus Walleij twd_timer_rate = clk_get_rate(twd_clk); 1084fd7f9b1SLinus Walleij 1094fd7f9b1SLinus Walleij clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate); 1104fd7f9b1SLinus Walleij } 1114fd7f9b1SLinus Walleij 1124fd7f9b1SLinus Walleij static int twd_cpufreq_transition(struct notifier_block *nb, 1134fd7f9b1SLinus Walleij unsigned long state, void *data) 1144fd7f9b1SLinus Walleij { 1154fd7f9b1SLinus Walleij struct cpufreq_freqs *freqs = data; 1164fd7f9b1SLinus Walleij 1174fd7f9b1SLinus Walleij /* 1184fd7f9b1SLinus Walleij * The twd clock events must be reprogrammed to account for the new 1194fd7f9b1SLinus Walleij * frequency. The timer is local to a cpu, so cross-call to the 1204fd7f9b1SLinus Walleij * changing cpu. 1214fd7f9b1SLinus Walleij */ 1224fd7f9b1SLinus Walleij if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE) 1234fd7f9b1SLinus Walleij smp_call_function_single(freqs->cpu, twd_update_frequency, 1243cd88f99SRussell King NULL, 1); 1254fd7f9b1SLinus Walleij 1264fd7f9b1SLinus Walleij return NOTIFY_OK; 1274fd7f9b1SLinus Walleij } 1284fd7f9b1SLinus Walleij 1294fd7f9b1SLinus Walleij static struct notifier_block twd_cpufreq_nb = { 1304fd7f9b1SLinus Walleij .notifier_call = twd_cpufreq_transition, 1314fd7f9b1SLinus Walleij }; 1324fd7f9b1SLinus Walleij 1334fd7f9b1SLinus Walleij static int twd_cpufreq_init(void) 1344fd7f9b1SLinus Walleij { 135910ba598SSantosh Shilimkar if (twd_evt && *__this_cpu_ptr(twd_evt) && !IS_ERR(twd_clk)) 1364fd7f9b1SLinus Walleij return cpufreq_register_notifier(&twd_cpufreq_nb, 1374fd7f9b1SLinus Walleij CPUFREQ_TRANSITION_NOTIFIER); 1384fd7f9b1SLinus Walleij 1394fd7f9b1SLinus Walleij return 0; 1404fd7f9b1SLinus Walleij } 1414fd7f9b1SLinus Walleij core_initcall(twd_cpufreq_init); 1424fd7f9b1SLinus Walleij 1434fd7f9b1SLinus Walleij #endif 1444fd7f9b1SLinus Walleij 145f32f4ce2SRussell King static void __cpuinit twd_calibrate_rate(void) 146f32f4ce2SRussell King { 14703399c1cSRussell King unsigned long count; 148f32f4ce2SRussell King u64 waitjiffies; 149f32f4ce2SRussell King 150f32f4ce2SRussell King /* 151f32f4ce2SRussell King * If this is the first time round, we need to work out how fast 152f32f4ce2SRussell King * the timer ticks 153f32f4ce2SRussell King */ 154f32f4ce2SRussell King if (twd_timer_rate == 0) { 1554c5158d4SRussell King printk(KERN_INFO "Calibrating local timer... "); 156f32f4ce2SRussell King 157f32f4ce2SRussell King /* Wait for a tick to start */ 158f32f4ce2SRussell King waitjiffies = get_jiffies_64() + 1; 159f32f4ce2SRussell King 160f32f4ce2SRussell King while (get_jiffies_64() < waitjiffies) 161f32f4ce2SRussell King udelay(10); 162f32f4ce2SRussell King 163f32f4ce2SRussell King /* OK, now the tick has started, let's get the timer going */ 164f32f4ce2SRussell King waitjiffies += 5; 165f32f4ce2SRussell King 166f32f4ce2SRussell King /* enable, no interrupt or reload */ 167f32f4ce2SRussell King __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL); 168f32f4ce2SRussell King 169f32f4ce2SRussell King /* maximum value */ 170f32f4ce2SRussell King __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER); 171f32f4ce2SRussell King 172f32f4ce2SRussell King while (get_jiffies_64() < waitjiffies) 173f32f4ce2SRussell King udelay(10); 174f32f4ce2SRussell King 175f32f4ce2SRussell King count = __raw_readl(twd_base + TWD_TIMER_COUNTER); 176f32f4ce2SRussell King 177f32f4ce2SRussell King twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5); 178f32f4ce2SRussell King 179f32f4ce2SRussell King printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000, 18090c5ffe5SVitaly Kuzmichev (twd_timer_rate / 10000) % 100); 181f32f4ce2SRussell King } 182f32f4ce2SRussell King } 183f32f4ce2SRussell King 18428af690aSMarc Zyngier static irqreturn_t twd_handler(int irq, void *dev_id) 18528af690aSMarc Zyngier { 18628af690aSMarc Zyngier struct clock_event_device *evt = *(struct clock_event_device **)dev_id; 18728af690aSMarc Zyngier 18828af690aSMarc Zyngier if (twd_timer_ack()) { 18928af690aSMarc Zyngier evt->event_handler(evt); 19028af690aSMarc Zyngier return IRQ_HANDLED; 19128af690aSMarc Zyngier } 19228af690aSMarc Zyngier 19328af690aSMarc Zyngier return IRQ_NONE; 19428af690aSMarc Zyngier } 19528af690aSMarc Zyngier 1965def51b0SLinus Walleij static struct clk *twd_get_clock(void) 1975def51b0SLinus Walleij { 1985def51b0SLinus Walleij struct clk *clk; 1995def51b0SLinus Walleij int err; 2005def51b0SLinus Walleij 2015def51b0SLinus Walleij clk = clk_get_sys("smp_twd", NULL); 2025def51b0SLinus Walleij if (IS_ERR(clk)) { 2035def51b0SLinus Walleij pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); 2045def51b0SLinus Walleij return clk; 2055def51b0SLinus Walleij } 2065def51b0SLinus Walleij 2075def51b0SLinus Walleij err = clk_prepare(clk); 2085def51b0SLinus Walleij if (err) { 2095def51b0SLinus Walleij pr_err("smp_twd: clock failed to prepare: %d\n", err); 2105def51b0SLinus Walleij clk_put(clk); 2115def51b0SLinus Walleij return ERR_PTR(err); 2125def51b0SLinus Walleij } 2135def51b0SLinus Walleij 2145def51b0SLinus Walleij err = clk_enable(clk); 2155def51b0SLinus Walleij if (err) { 2165def51b0SLinus Walleij pr_err("smp_twd: clock failed to enable: %d\n", err); 2175def51b0SLinus Walleij clk_unprepare(clk); 2185def51b0SLinus Walleij clk_put(clk); 2195def51b0SLinus Walleij return ERR_PTR(err); 2205def51b0SLinus Walleij } 2215def51b0SLinus Walleij 2225def51b0SLinus Walleij return clk; 2235def51b0SLinus Walleij } 2245def51b0SLinus Walleij 225f32f4ce2SRussell King /* 226f32f4ce2SRussell King * Setup the local clock events for a CPU. 227f32f4ce2SRussell King */ 22892485104SMarc Zyngier static int __cpuinit twd_timer_setup(struct clock_event_device *clk) 229f32f4ce2SRussell King { 23028af690aSMarc Zyngier struct clock_event_device **this_cpu_clk; 23128af690aSMarc Zyngier 2325def51b0SLinus Walleij if (!twd_clk) 2335def51b0SLinus Walleij twd_clk = twd_get_clock(); 2345def51b0SLinus Walleij 2355def51b0SLinus Walleij if (!IS_ERR_OR_NULL(twd_clk)) 2365def51b0SLinus Walleij twd_timer_rate = clk_get_rate(twd_clk); 2375def51b0SLinus Walleij else 238f32f4ce2SRussell King twd_calibrate_rate(); 239f32f4ce2SRussell King 240c214455fSMarc Zyngier __raw_writel(0, twd_base + TWD_TIMER_CONTROL); 241c214455fSMarc Zyngier 242f32f4ce2SRussell King clk->name = "local_timer"; 2435388a6b2SRussell King clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | 2445388a6b2SRussell King CLOCK_EVT_FEAT_C3STOP; 245f32f4ce2SRussell King clk->rating = 350; 246f32f4ce2SRussell King clk->set_mode = twd_set_mode; 247f32f4ce2SRussell King clk->set_next_event = twd_set_next_event; 24881e46f7bSMarc Zyngier clk->irq = twd_ppi; 249f32f4ce2SRussell King 25028af690aSMarc Zyngier this_cpu_clk = __this_cpu_ptr(twd_evt); 25128af690aSMarc Zyngier *this_cpu_clk = clk; 25228af690aSMarc Zyngier 25354d15b1dSLinus Walleij clockevents_config_and_register(clk, twd_timer_rate, 25454d15b1dSLinus Walleij 0xf, 0xffffffff); 25528af690aSMarc Zyngier enable_percpu_irq(clk->irq, 0); 25681e46f7bSMarc Zyngier 25781e46f7bSMarc Zyngier return 0; 25881e46f7bSMarc Zyngier } 25981e46f7bSMarc Zyngier 26081e46f7bSMarc Zyngier static struct local_timer_ops twd_lt_ops __cpuinitdata = { 26181e46f7bSMarc Zyngier .setup = twd_timer_setup, 26281e46f7bSMarc Zyngier .stop = twd_timer_stop, 26381e46f7bSMarc Zyngier }; 26481e46f7bSMarc Zyngier 265d8e03643SMarc Zyngier static int __init twd_local_timer_common_register(void) 26681e46f7bSMarc Zyngier { 26781e46f7bSMarc Zyngier int err; 26881e46f7bSMarc Zyngier 26981e46f7bSMarc Zyngier twd_evt = alloc_percpu(struct clock_event_device *); 270d8e03643SMarc Zyngier if (!twd_evt) { 27181e46f7bSMarc Zyngier err = -ENOMEM; 272d8e03643SMarc Zyngier goto out_free; 27381e46f7bSMarc Zyngier } 27481e46f7bSMarc Zyngier 27581e46f7bSMarc Zyngier err = request_percpu_irq(twd_ppi, twd_handler, "twd", twd_evt); 27681e46f7bSMarc Zyngier if (err) { 27781e46f7bSMarc Zyngier pr_err("twd: can't register interrupt %d (%d)\n", twd_ppi, err); 278d8e03643SMarc Zyngier goto out_free; 27981e46f7bSMarc Zyngier } 28081e46f7bSMarc Zyngier 28181e46f7bSMarc Zyngier err = local_timer_register(&twd_lt_ops); 28281e46f7bSMarc Zyngier if (err) 283d8e03643SMarc Zyngier goto out_irq; 28481e46f7bSMarc Zyngier 28581e46f7bSMarc Zyngier return 0; 28681e46f7bSMarc Zyngier 287d8e03643SMarc Zyngier out_irq: 288d8e03643SMarc Zyngier free_percpu_irq(twd_ppi, twd_evt); 289d8e03643SMarc Zyngier out_free: 29081e46f7bSMarc Zyngier iounmap(twd_base); 291d8e03643SMarc Zyngier twd_base = NULL; 29281e46f7bSMarc Zyngier free_percpu(twd_evt); 293d8e03643SMarc Zyngier 29481e46f7bSMarc Zyngier return err; 295f32f4ce2SRussell King } 296d8e03643SMarc Zyngier 297d8e03643SMarc Zyngier int __init twd_local_timer_register(struct twd_local_timer *tlt) 298d8e03643SMarc Zyngier { 299d8e03643SMarc Zyngier if (twd_base || twd_evt) 300d8e03643SMarc Zyngier return -EBUSY; 301d8e03643SMarc Zyngier 302d8e03643SMarc Zyngier twd_ppi = tlt->res[1].start; 303d8e03643SMarc Zyngier 304d8e03643SMarc Zyngier twd_base = ioremap(tlt->res[0].start, resource_size(&tlt->res[0])); 305d8e03643SMarc Zyngier if (!twd_base) 306d8e03643SMarc Zyngier return -ENOMEM; 307d8e03643SMarc Zyngier 308d8e03643SMarc Zyngier return twd_local_timer_common_register(); 309d8e03643SMarc Zyngier } 310d8e03643SMarc Zyngier 311d8e03643SMarc Zyngier #ifdef CONFIG_OF 312d8e03643SMarc Zyngier const static struct of_device_id twd_of_match[] __initconst = { 313d8e03643SMarc Zyngier { .compatible = "arm,cortex-a9-twd-timer", }, 314d8e03643SMarc Zyngier { .compatible = "arm,cortex-a5-twd-timer", }, 315d8e03643SMarc Zyngier { .compatible = "arm,arm11mp-twd-timer", }, 316d8e03643SMarc Zyngier { }, 317d8e03643SMarc Zyngier }; 318d8e03643SMarc Zyngier 319d8e03643SMarc Zyngier void __init twd_local_timer_of_register(void) 320d8e03643SMarc Zyngier { 321d8e03643SMarc Zyngier struct device_node *np; 322d8e03643SMarc Zyngier int err; 323d8e03643SMarc Zyngier 324d8e03643SMarc Zyngier np = of_find_matching_node(NULL, twd_of_match); 325d8e03643SMarc Zyngier if (!np) { 326d8e03643SMarc Zyngier err = -ENODEV; 327d8e03643SMarc Zyngier goto out; 328d8e03643SMarc Zyngier } 329d8e03643SMarc Zyngier 330d8e03643SMarc Zyngier twd_ppi = irq_of_parse_and_map(np, 0); 331d8e03643SMarc Zyngier if (!twd_ppi) { 332d8e03643SMarc Zyngier err = -EINVAL; 333d8e03643SMarc Zyngier goto out; 334d8e03643SMarc Zyngier } 335d8e03643SMarc Zyngier 336d8e03643SMarc Zyngier twd_base = of_iomap(np, 0); 337d8e03643SMarc Zyngier if (!twd_base) { 338d8e03643SMarc Zyngier err = -ENOMEM; 339d8e03643SMarc Zyngier goto out; 340d8e03643SMarc Zyngier } 341d8e03643SMarc Zyngier 342d8e03643SMarc Zyngier err = twd_local_timer_common_register(); 343d8e03643SMarc Zyngier 344d8e03643SMarc Zyngier out: 345d8e03643SMarc Zyngier WARN(err, "twd_local_timer_of_register failed (%d)\n", err); 346d8e03643SMarc Zyngier } 347d8e03643SMarc Zyngier #endif 348