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> 21f32f4ce2SRussell King #include <linux/irq.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 */ 31f32f4ce2SRussell King 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 */ 83f32f4ce2SRussell King 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 99abde710cSMarc Zyngier /* Temporary hack to be removed when all TWD users are converted to 100abde710cSMarc Zyngier the new registration interface */ 101abde710cSMarc Zyngier void local_timer_stop(struct clock_event_device *clk) 102abde710cSMarc Zyngier __attribute__ ((alias ("twd_timer_stop"))); 103abde710cSMarc Zyngier 1044fd7f9b1SLinus Walleij #ifdef CONFIG_CPU_FREQ 1054fd7f9b1SLinus Walleij 1064fd7f9b1SLinus Walleij /* 1074fd7f9b1SLinus Walleij * Updates clockevent frequency when the cpu frequency changes. 1084fd7f9b1SLinus Walleij * Called on the cpu that is changing frequency with interrupts disabled. 1094fd7f9b1SLinus Walleij */ 1104fd7f9b1SLinus Walleij static void twd_update_frequency(void *data) 1114fd7f9b1SLinus Walleij { 1124fd7f9b1SLinus Walleij twd_timer_rate = clk_get_rate(twd_clk); 1134fd7f9b1SLinus Walleij 1144fd7f9b1SLinus Walleij clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate); 1154fd7f9b1SLinus Walleij } 1164fd7f9b1SLinus Walleij 1174fd7f9b1SLinus Walleij static int twd_cpufreq_transition(struct notifier_block *nb, 1184fd7f9b1SLinus Walleij unsigned long state, void *data) 1194fd7f9b1SLinus Walleij { 1204fd7f9b1SLinus Walleij struct cpufreq_freqs *freqs = data; 1214fd7f9b1SLinus Walleij 1224fd7f9b1SLinus Walleij /* 1234fd7f9b1SLinus Walleij * The twd clock events must be reprogrammed to account for the new 1244fd7f9b1SLinus Walleij * frequency. The timer is local to a cpu, so cross-call to the 1254fd7f9b1SLinus Walleij * changing cpu. 1264fd7f9b1SLinus Walleij */ 1274fd7f9b1SLinus Walleij if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE) 1284fd7f9b1SLinus Walleij smp_call_function_single(freqs->cpu, twd_update_frequency, 1294fd7f9b1SLinus Walleij NULL, 1); 1304fd7f9b1SLinus Walleij 1314fd7f9b1SLinus Walleij return NOTIFY_OK; 1324fd7f9b1SLinus Walleij } 1334fd7f9b1SLinus Walleij 1344fd7f9b1SLinus Walleij static struct notifier_block twd_cpufreq_nb = { 1354fd7f9b1SLinus Walleij .notifier_call = twd_cpufreq_transition, 1364fd7f9b1SLinus Walleij }; 1374fd7f9b1SLinus Walleij 1384fd7f9b1SLinus Walleij static int twd_cpufreq_init(void) 1394fd7f9b1SLinus Walleij { 1404fd7f9b1SLinus Walleij if (!IS_ERR(twd_clk)) 1414fd7f9b1SLinus Walleij return cpufreq_register_notifier(&twd_cpufreq_nb, 1424fd7f9b1SLinus Walleij CPUFREQ_TRANSITION_NOTIFIER); 1434fd7f9b1SLinus Walleij 1444fd7f9b1SLinus Walleij return 0; 1454fd7f9b1SLinus Walleij } 1464fd7f9b1SLinus Walleij core_initcall(twd_cpufreq_init); 1474fd7f9b1SLinus Walleij 1484fd7f9b1SLinus Walleij #endif 1494fd7f9b1SLinus Walleij 150f32f4ce2SRussell King static void __cpuinit twd_calibrate_rate(void) 151f32f4ce2SRussell King { 15203399c1cSRussell King unsigned long count; 153f32f4ce2SRussell King u64 waitjiffies; 154f32f4ce2SRussell King 155f32f4ce2SRussell King /* 156f32f4ce2SRussell King * If this is the first time round, we need to work out how fast 157f32f4ce2SRussell King * the timer ticks 158f32f4ce2SRussell King */ 159f32f4ce2SRussell King if (twd_timer_rate == 0) { 1604c5158d4SRussell King printk(KERN_INFO "Calibrating local timer... "); 161f32f4ce2SRussell King 162f32f4ce2SRussell King /* Wait for a tick to start */ 163f32f4ce2SRussell King waitjiffies = get_jiffies_64() + 1; 164f32f4ce2SRussell King 165f32f4ce2SRussell King while (get_jiffies_64() < waitjiffies) 166f32f4ce2SRussell King udelay(10); 167f32f4ce2SRussell King 168f32f4ce2SRussell King /* OK, now the tick has started, let's get the timer going */ 169f32f4ce2SRussell King waitjiffies += 5; 170f32f4ce2SRussell King 171f32f4ce2SRussell King /* enable, no interrupt or reload */ 172f32f4ce2SRussell King __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL); 173f32f4ce2SRussell King 174f32f4ce2SRussell King /* maximum value */ 175f32f4ce2SRussell King __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER); 176f32f4ce2SRussell King 177f32f4ce2SRussell King while (get_jiffies_64() < waitjiffies) 178f32f4ce2SRussell King udelay(10); 179f32f4ce2SRussell King 180f32f4ce2SRussell King count = __raw_readl(twd_base + TWD_TIMER_COUNTER); 181f32f4ce2SRussell King 182f32f4ce2SRussell King twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5); 183f32f4ce2SRussell King 184f32f4ce2SRussell King printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000, 18590c5ffe5SVitaly Kuzmichev (twd_timer_rate / 10000) % 100); 186f32f4ce2SRussell King } 187f32f4ce2SRussell King } 188f32f4ce2SRussell King 18928af690aSMarc Zyngier static irqreturn_t twd_handler(int irq, void *dev_id) 19028af690aSMarc Zyngier { 19128af690aSMarc Zyngier struct clock_event_device *evt = *(struct clock_event_device **)dev_id; 19228af690aSMarc Zyngier 19328af690aSMarc Zyngier if (twd_timer_ack()) { 19428af690aSMarc Zyngier evt->event_handler(evt); 19528af690aSMarc Zyngier return IRQ_HANDLED; 19628af690aSMarc Zyngier } 19728af690aSMarc Zyngier 19828af690aSMarc Zyngier return IRQ_NONE; 19928af690aSMarc Zyngier } 20028af690aSMarc Zyngier 2015def51b0SLinus Walleij static struct clk *twd_get_clock(void) 2025def51b0SLinus Walleij { 2035def51b0SLinus Walleij struct clk *clk; 2045def51b0SLinus Walleij int err; 2055def51b0SLinus Walleij 2065def51b0SLinus Walleij clk = clk_get_sys("smp_twd", NULL); 2075def51b0SLinus Walleij if (IS_ERR(clk)) { 2085def51b0SLinus Walleij pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); 2095def51b0SLinus Walleij return clk; 2105def51b0SLinus Walleij } 2115def51b0SLinus Walleij 2125def51b0SLinus Walleij err = clk_prepare(clk); 2135def51b0SLinus Walleij if (err) { 2145def51b0SLinus Walleij pr_err("smp_twd: clock failed to prepare: %d\n", err); 2155def51b0SLinus Walleij clk_put(clk); 2165def51b0SLinus Walleij return ERR_PTR(err); 2175def51b0SLinus Walleij } 2185def51b0SLinus Walleij 2195def51b0SLinus Walleij err = clk_enable(clk); 2205def51b0SLinus Walleij if (err) { 2215def51b0SLinus Walleij pr_err("smp_twd: clock failed to enable: %d\n", err); 2225def51b0SLinus Walleij clk_unprepare(clk); 2235def51b0SLinus Walleij clk_put(clk); 2245def51b0SLinus Walleij return ERR_PTR(err); 2255def51b0SLinus Walleij } 2265def51b0SLinus Walleij 2275def51b0SLinus Walleij return clk; 2285def51b0SLinus Walleij } 2295def51b0SLinus Walleij 230f32f4ce2SRussell King /* 231f32f4ce2SRussell King * Setup the local clock events for a CPU. 232f32f4ce2SRussell King */ 23381e46f7bSMarc Zyngier int __cpuinit twd_timer_setup(struct clock_event_device *clk) 234f32f4ce2SRussell King { 23528af690aSMarc Zyngier struct clock_event_device **this_cpu_clk; 23628af690aSMarc Zyngier 23728af690aSMarc Zyngier if (!twd_evt) { 23828af690aSMarc Zyngier int err; 23928af690aSMarc Zyngier 24028af690aSMarc Zyngier twd_evt = alloc_percpu(struct clock_event_device *); 24128af690aSMarc Zyngier if (!twd_evt) { 24228af690aSMarc Zyngier pr_err("twd: can't allocate memory\n"); 24381e46f7bSMarc Zyngier return -ENOMEM; 24428af690aSMarc Zyngier } 24528af690aSMarc Zyngier 24628af690aSMarc Zyngier err = request_percpu_irq(clk->irq, twd_handler, 24728af690aSMarc Zyngier "twd", twd_evt); 24828af690aSMarc Zyngier if (err) { 24928af690aSMarc Zyngier pr_err("twd: can't register interrupt %d (%d)\n", 25028af690aSMarc Zyngier clk->irq, err); 25181e46f7bSMarc Zyngier return err; 25228af690aSMarc Zyngier } 25328af690aSMarc Zyngier } 25428af690aSMarc Zyngier 2555def51b0SLinus Walleij if (!twd_clk) 2565def51b0SLinus Walleij twd_clk = twd_get_clock(); 2575def51b0SLinus Walleij 2585def51b0SLinus Walleij if (!IS_ERR_OR_NULL(twd_clk)) 2595def51b0SLinus Walleij twd_timer_rate = clk_get_rate(twd_clk); 2605def51b0SLinus Walleij else 261f32f4ce2SRussell King twd_calibrate_rate(); 262f32f4ce2SRussell King 263c214455fSMarc Zyngier __raw_writel(0, twd_base + TWD_TIMER_CONTROL); 264c214455fSMarc Zyngier 265f32f4ce2SRussell King clk->name = "local_timer"; 2665388a6b2SRussell King clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | 2675388a6b2SRussell King CLOCK_EVT_FEAT_C3STOP; 268f32f4ce2SRussell King clk->rating = 350; 269f32f4ce2SRussell King clk->set_mode = twd_set_mode; 270f32f4ce2SRussell King clk->set_next_event = twd_set_next_event; 27181e46f7bSMarc Zyngier if (!clk->irq) 27281e46f7bSMarc Zyngier clk->irq = twd_ppi; 273f32f4ce2SRussell King 27428af690aSMarc Zyngier this_cpu_clk = __this_cpu_ptr(twd_evt); 27528af690aSMarc Zyngier *this_cpu_clk = clk; 27628af690aSMarc Zyngier 27754d15b1dSLinus Walleij clockevents_config_and_register(clk, twd_timer_rate, 27854d15b1dSLinus Walleij 0xf, 0xffffffff); 27928af690aSMarc Zyngier enable_percpu_irq(clk->irq, 0); 28081e46f7bSMarc Zyngier 28181e46f7bSMarc Zyngier return 0; 28281e46f7bSMarc Zyngier } 28381e46f7bSMarc Zyngier 28481e46f7bSMarc Zyngier static struct local_timer_ops twd_lt_ops __cpuinitdata = { 28581e46f7bSMarc Zyngier .setup = twd_timer_setup, 28681e46f7bSMarc Zyngier .stop = twd_timer_stop, 28781e46f7bSMarc Zyngier }; 28881e46f7bSMarc Zyngier 289d8e03643SMarc Zyngier static int __init twd_local_timer_common_register(void) 29081e46f7bSMarc Zyngier { 29181e46f7bSMarc Zyngier int err; 29281e46f7bSMarc Zyngier 29381e46f7bSMarc Zyngier twd_evt = alloc_percpu(struct clock_event_device *); 294d8e03643SMarc Zyngier if (!twd_evt) { 29581e46f7bSMarc Zyngier err = -ENOMEM; 296d8e03643SMarc Zyngier goto out_free; 29781e46f7bSMarc Zyngier } 29881e46f7bSMarc Zyngier 29981e46f7bSMarc Zyngier err = request_percpu_irq(twd_ppi, twd_handler, "twd", twd_evt); 30081e46f7bSMarc Zyngier if (err) { 30181e46f7bSMarc Zyngier pr_err("twd: can't register interrupt %d (%d)\n", twd_ppi, err); 302d8e03643SMarc Zyngier goto out_free; 30381e46f7bSMarc Zyngier } 30481e46f7bSMarc Zyngier 30581e46f7bSMarc Zyngier err = local_timer_register(&twd_lt_ops); 30681e46f7bSMarc Zyngier if (err) 307d8e03643SMarc Zyngier goto out_irq; 30881e46f7bSMarc Zyngier 30981e46f7bSMarc Zyngier return 0; 31081e46f7bSMarc Zyngier 311d8e03643SMarc Zyngier out_irq: 312d8e03643SMarc Zyngier free_percpu_irq(twd_ppi, twd_evt); 313d8e03643SMarc Zyngier out_free: 31481e46f7bSMarc Zyngier iounmap(twd_base); 315d8e03643SMarc Zyngier twd_base = NULL; 31681e46f7bSMarc Zyngier free_percpu(twd_evt); 317d8e03643SMarc Zyngier 31881e46f7bSMarc Zyngier return err; 319f32f4ce2SRussell King } 320d8e03643SMarc Zyngier 321d8e03643SMarc Zyngier int __init twd_local_timer_register(struct twd_local_timer *tlt) 322d8e03643SMarc Zyngier { 323d8e03643SMarc Zyngier if (twd_base || twd_evt) 324d8e03643SMarc Zyngier return -EBUSY; 325d8e03643SMarc Zyngier 326d8e03643SMarc Zyngier twd_ppi = tlt->res[1].start; 327d8e03643SMarc Zyngier 328d8e03643SMarc Zyngier twd_base = ioremap(tlt->res[0].start, resource_size(&tlt->res[0])); 329d8e03643SMarc Zyngier if (!twd_base) 330d8e03643SMarc Zyngier return -ENOMEM; 331d8e03643SMarc Zyngier 332d8e03643SMarc Zyngier return twd_local_timer_common_register(); 333d8e03643SMarc Zyngier } 334d8e03643SMarc Zyngier 335d8e03643SMarc Zyngier #ifdef CONFIG_OF 336d8e03643SMarc Zyngier const static struct of_device_id twd_of_match[] __initconst = { 337d8e03643SMarc Zyngier { .compatible = "arm,cortex-a9-twd-timer", }, 338d8e03643SMarc Zyngier { .compatible = "arm,cortex-a5-twd-timer", }, 339d8e03643SMarc Zyngier { .compatible = "arm,arm11mp-twd-timer", }, 340d8e03643SMarc Zyngier { }, 341d8e03643SMarc Zyngier }; 342d8e03643SMarc Zyngier 343d8e03643SMarc Zyngier void __init twd_local_timer_of_register(void) 344d8e03643SMarc Zyngier { 345d8e03643SMarc Zyngier struct device_node *np; 346d8e03643SMarc Zyngier int err; 347d8e03643SMarc Zyngier 348d8e03643SMarc Zyngier np = of_find_matching_node(NULL, twd_of_match); 349d8e03643SMarc Zyngier if (!np) { 350d8e03643SMarc Zyngier err = -ENODEV; 351d8e03643SMarc Zyngier goto out; 352d8e03643SMarc Zyngier } 353d8e03643SMarc Zyngier 354d8e03643SMarc Zyngier twd_ppi = irq_of_parse_and_map(np, 0); 355d8e03643SMarc Zyngier if (!twd_ppi) { 356d8e03643SMarc Zyngier err = -EINVAL; 357d8e03643SMarc Zyngier goto out; 358d8e03643SMarc Zyngier } 359d8e03643SMarc Zyngier 360d8e03643SMarc Zyngier twd_base = of_iomap(np, 0); 361d8e03643SMarc Zyngier if (!twd_base) { 362d8e03643SMarc Zyngier err = -ENOMEM; 363d8e03643SMarc Zyngier goto out; 364d8e03643SMarc Zyngier } 365d8e03643SMarc Zyngier 366d8e03643SMarc Zyngier err = twd_local_timer_common_register(); 367d8e03643SMarc Zyngier 368d8e03643SMarc Zyngier out: 369d8e03643SMarc Zyngier WARN(err, "twd_local_timer_of_register failed (%d)\n", err); 370d8e03643SMarc Zyngier } 371d8e03643SMarc Zyngier #endif 372