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> 23f32f4ce2SRussell King 24f32f4ce2SRussell King #include <asm/smp_twd.h> 2528af690aSMarc Zyngier #include <asm/localtimer.h> 26f32f4ce2SRussell King #include <asm/hardware/gic.h> 27f32f4ce2SRussell King 28f32f4ce2SRussell King /* set up by the platform code */ 29f32f4ce2SRussell King void __iomem *twd_base; 30f32f4ce2SRussell King 315def51b0SLinus Walleij static struct clk *twd_clk; 32f32f4ce2SRussell King static unsigned long twd_timer_rate; 33f32f4ce2SRussell King 3428af690aSMarc Zyngier static struct clock_event_device __percpu **twd_evt; 3528af690aSMarc Zyngier 36f32f4ce2SRussell King static void twd_set_mode(enum clock_event_mode mode, 37f32f4ce2SRussell King struct clock_event_device *clk) 38f32f4ce2SRussell King { 39f32f4ce2SRussell King unsigned long ctrl; 40f32f4ce2SRussell King 41f32f4ce2SRussell King switch (mode) { 42f32f4ce2SRussell King case CLOCK_EVT_MODE_PERIODIC: 43f32f4ce2SRussell King /* timer load already set up */ 44f32f4ce2SRussell King ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE 45f32f4ce2SRussell King | TWD_TIMER_CONTROL_PERIODIC; 4603399c1cSRussell King __raw_writel(twd_timer_rate / HZ, twd_base + TWD_TIMER_LOAD); 47f32f4ce2SRussell King break; 48f32f4ce2SRussell King case CLOCK_EVT_MODE_ONESHOT: 49f32f4ce2SRussell King /* period set, and timer enabled in 'next_event' hook */ 50f32f4ce2SRussell King ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT; 51f32f4ce2SRussell King break; 52f32f4ce2SRussell King case CLOCK_EVT_MODE_UNUSED: 53f32f4ce2SRussell King case CLOCK_EVT_MODE_SHUTDOWN: 54f32f4ce2SRussell King default: 55f32f4ce2SRussell King ctrl = 0; 56f32f4ce2SRussell King } 57f32f4ce2SRussell King 58f32f4ce2SRussell King __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL); 59f32f4ce2SRussell King } 60f32f4ce2SRussell King 61f32f4ce2SRussell King static int twd_set_next_event(unsigned long evt, 62f32f4ce2SRussell King struct clock_event_device *unused) 63f32f4ce2SRussell King { 64f32f4ce2SRussell King unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL); 65f32f4ce2SRussell King 664c5158d4SRussell King ctrl |= TWD_TIMER_CONTROL_ENABLE; 674c5158d4SRussell King 68f32f4ce2SRussell King __raw_writel(evt, twd_base + TWD_TIMER_COUNTER); 694c5158d4SRussell King __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL); 70f32f4ce2SRussell King 71f32f4ce2SRussell King return 0; 72f32f4ce2SRussell King } 73f32f4ce2SRussell King 74f32f4ce2SRussell King /* 75f32f4ce2SRussell King * local_timer_ack: checks for a local timer interrupt. 76f32f4ce2SRussell King * 77f32f4ce2SRussell King * If a local timer interrupt has occurred, acknowledge and return 1. 78f32f4ce2SRussell King * Otherwise, return 0. 79f32f4ce2SRussell King */ 80f32f4ce2SRussell King int twd_timer_ack(void) 81f32f4ce2SRussell King { 82f32f4ce2SRussell King if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) { 83f32f4ce2SRussell King __raw_writel(1, twd_base + TWD_TIMER_INTSTAT); 84f32f4ce2SRussell King return 1; 85f32f4ce2SRussell King } 86f32f4ce2SRussell King 87f32f4ce2SRussell King return 0; 88f32f4ce2SRussell King } 89f32f4ce2SRussell King 9028af690aSMarc Zyngier void twd_timer_stop(struct clock_event_device *clk) 9128af690aSMarc Zyngier { 9228af690aSMarc Zyngier twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk); 9328af690aSMarc Zyngier disable_percpu_irq(clk->irq); 9428af690aSMarc Zyngier } 9528af690aSMarc Zyngier 964fd7f9b1SLinus Walleij #ifdef CONFIG_CPU_FREQ 974fd7f9b1SLinus Walleij 984fd7f9b1SLinus Walleij /* 994fd7f9b1SLinus Walleij * Updates clockevent frequency when the cpu frequency changes. 1004fd7f9b1SLinus Walleij * Called on the cpu that is changing frequency with interrupts disabled. 1014fd7f9b1SLinus Walleij */ 1024fd7f9b1SLinus Walleij static void twd_update_frequency(void *data) 1034fd7f9b1SLinus Walleij { 1044fd7f9b1SLinus Walleij twd_timer_rate = clk_get_rate(twd_clk); 1054fd7f9b1SLinus Walleij 1064fd7f9b1SLinus Walleij clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate); 1074fd7f9b1SLinus Walleij } 1084fd7f9b1SLinus Walleij 1094fd7f9b1SLinus Walleij static int twd_cpufreq_transition(struct notifier_block *nb, 1104fd7f9b1SLinus Walleij unsigned long state, void *data) 1114fd7f9b1SLinus Walleij { 1124fd7f9b1SLinus Walleij struct cpufreq_freqs *freqs = data; 1134fd7f9b1SLinus Walleij 1144fd7f9b1SLinus Walleij /* 1154fd7f9b1SLinus Walleij * The twd clock events must be reprogrammed to account for the new 1164fd7f9b1SLinus Walleij * frequency. The timer is local to a cpu, so cross-call to the 1174fd7f9b1SLinus Walleij * changing cpu. 1184fd7f9b1SLinus Walleij */ 1194fd7f9b1SLinus Walleij if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE) 1204fd7f9b1SLinus Walleij smp_call_function_single(freqs->cpu, twd_update_frequency, 1214fd7f9b1SLinus Walleij NULL, 1); 1224fd7f9b1SLinus Walleij 1234fd7f9b1SLinus Walleij return NOTIFY_OK; 1244fd7f9b1SLinus Walleij } 1254fd7f9b1SLinus Walleij 1264fd7f9b1SLinus Walleij static struct notifier_block twd_cpufreq_nb = { 1274fd7f9b1SLinus Walleij .notifier_call = twd_cpufreq_transition, 1284fd7f9b1SLinus Walleij }; 1294fd7f9b1SLinus Walleij 1304fd7f9b1SLinus Walleij static int twd_cpufreq_init(void) 1314fd7f9b1SLinus Walleij { 1324fd7f9b1SLinus Walleij if (!IS_ERR(twd_clk)) 1334fd7f9b1SLinus Walleij return cpufreq_register_notifier(&twd_cpufreq_nb, 1344fd7f9b1SLinus Walleij CPUFREQ_TRANSITION_NOTIFIER); 1354fd7f9b1SLinus Walleij 1364fd7f9b1SLinus Walleij return 0; 1374fd7f9b1SLinus Walleij } 1384fd7f9b1SLinus Walleij core_initcall(twd_cpufreq_init); 1394fd7f9b1SLinus Walleij 1404fd7f9b1SLinus Walleij #endif 1414fd7f9b1SLinus Walleij 142f32f4ce2SRussell King static void __cpuinit twd_calibrate_rate(void) 143f32f4ce2SRussell King { 14403399c1cSRussell King unsigned long count; 145f32f4ce2SRussell King u64 waitjiffies; 146f32f4ce2SRussell King 147f32f4ce2SRussell King /* 148f32f4ce2SRussell King * If this is the first time round, we need to work out how fast 149f32f4ce2SRussell King * the timer ticks 150f32f4ce2SRussell King */ 151f32f4ce2SRussell King if (twd_timer_rate == 0) { 1524c5158d4SRussell King printk(KERN_INFO "Calibrating local timer... "); 153f32f4ce2SRussell King 154f32f4ce2SRussell King /* Wait for a tick to start */ 155f32f4ce2SRussell King waitjiffies = get_jiffies_64() + 1; 156f32f4ce2SRussell King 157f32f4ce2SRussell King while (get_jiffies_64() < waitjiffies) 158f32f4ce2SRussell King udelay(10); 159f32f4ce2SRussell King 160f32f4ce2SRussell King /* OK, now the tick has started, let's get the timer going */ 161f32f4ce2SRussell King waitjiffies += 5; 162f32f4ce2SRussell King 163f32f4ce2SRussell King /* enable, no interrupt or reload */ 164f32f4ce2SRussell King __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL); 165f32f4ce2SRussell King 166f32f4ce2SRussell King /* maximum value */ 167f32f4ce2SRussell King __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER); 168f32f4ce2SRussell King 169f32f4ce2SRussell King while (get_jiffies_64() < waitjiffies) 170f32f4ce2SRussell King udelay(10); 171f32f4ce2SRussell King 172f32f4ce2SRussell King count = __raw_readl(twd_base + TWD_TIMER_COUNTER); 173f32f4ce2SRussell King 174f32f4ce2SRussell King twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5); 175f32f4ce2SRussell King 176f32f4ce2SRussell King printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000, 17790c5ffe5SVitaly Kuzmichev (twd_timer_rate / 10000) % 100); 178f32f4ce2SRussell King } 179f32f4ce2SRussell King } 180f32f4ce2SRussell King 18128af690aSMarc Zyngier static irqreturn_t twd_handler(int irq, void *dev_id) 18228af690aSMarc Zyngier { 18328af690aSMarc Zyngier struct clock_event_device *evt = *(struct clock_event_device **)dev_id; 18428af690aSMarc Zyngier 18528af690aSMarc Zyngier if (twd_timer_ack()) { 18628af690aSMarc Zyngier evt->event_handler(evt); 18728af690aSMarc Zyngier return IRQ_HANDLED; 18828af690aSMarc Zyngier } 18928af690aSMarc Zyngier 19028af690aSMarc Zyngier return IRQ_NONE; 19128af690aSMarc Zyngier } 19228af690aSMarc Zyngier 1935def51b0SLinus Walleij static struct clk *twd_get_clock(void) 1945def51b0SLinus Walleij { 1955def51b0SLinus Walleij struct clk *clk; 1965def51b0SLinus Walleij int err; 1975def51b0SLinus Walleij 1985def51b0SLinus Walleij clk = clk_get_sys("smp_twd", NULL); 1995def51b0SLinus Walleij if (IS_ERR(clk)) { 2005def51b0SLinus Walleij pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); 2015def51b0SLinus Walleij return clk; 2025def51b0SLinus Walleij } 2035def51b0SLinus Walleij 2045def51b0SLinus Walleij err = clk_prepare(clk); 2055def51b0SLinus Walleij if (err) { 2065def51b0SLinus Walleij pr_err("smp_twd: clock failed to prepare: %d\n", err); 2075def51b0SLinus Walleij clk_put(clk); 2085def51b0SLinus Walleij return ERR_PTR(err); 2095def51b0SLinus Walleij } 2105def51b0SLinus Walleij 2115def51b0SLinus Walleij err = clk_enable(clk); 2125def51b0SLinus Walleij if (err) { 2135def51b0SLinus Walleij pr_err("smp_twd: clock failed to enable: %d\n", err); 2145def51b0SLinus Walleij clk_unprepare(clk); 2155def51b0SLinus Walleij clk_put(clk); 2165def51b0SLinus Walleij return ERR_PTR(err); 2175def51b0SLinus Walleij } 2185def51b0SLinus Walleij 2195def51b0SLinus Walleij return clk; 2205def51b0SLinus Walleij } 2215def51b0SLinus Walleij 222f32f4ce2SRussell King /* 223f32f4ce2SRussell King * Setup the local clock events for a CPU. 224f32f4ce2SRussell King */ 225f32f4ce2SRussell King void __cpuinit twd_timer_setup(struct clock_event_device *clk) 226f32f4ce2SRussell King { 22728af690aSMarc Zyngier struct clock_event_device **this_cpu_clk; 22828af690aSMarc Zyngier 22928af690aSMarc Zyngier if (!twd_evt) { 23028af690aSMarc Zyngier int err; 23128af690aSMarc Zyngier 23228af690aSMarc Zyngier twd_evt = alloc_percpu(struct clock_event_device *); 23328af690aSMarc Zyngier if (!twd_evt) { 23428af690aSMarc Zyngier pr_err("twd: can't allocate memory\n"); 23528af690aSMarc Zyngier return; 23628af690aSMarc Zyngier } 23728af690aSMarc Zyngier 23828af690aSMarc Zyngier err = request_percpu_irq(clk->irq, twd_handler, 23928af690aSMarc Zyngier "twd", twd_evt); 24028af690aSMarc Zyngier if (err) { 24128af690aSMarc Zyngier pr_err("twd: can't register interrupt %d (%d)\n", 24228af690aSMarc Zyngier clk->irq, err); 24328af690aSMarc Zyngier return; 24428af690aSMarc Zyngier } 24528af690aSMarc Zyngier } 24628af690aSMarc Zyngier 2475def51b0SLinus Walleij if (!twd_clk) 2485def51b0SLinus Walleij twd_clk = twd_get_clock(); 2495def51b0SLinus Walleij 2505def51b0SLinus Walleij if (!IS_ERR_OR_NULL(twd_clk)) 2515def51b0SLinus Walleij twd_timer_rate = clk_get_rate(twd_clk); 2525def51b0SLinus Walleij else 253f32f4ce2SRussell King twd_calibrate_rate(); 254f32f4ce2SRussell King 255f32f4ce2SRussell King clk->name = "local_timer"; 2565388a6b2SRussell King clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | 2575388a6b2SRussell King CLOCK_EVT_FEAT_C3STOP; 258f32f4ce2SRussell King clk->rating = 350; 259f32f4ce2SRussell King clk->set_mode = twd_set_mode; 260f32f4ce2SRussell King clk->set_next_event = twd_set_next_event; 261f32f4ce2SRussell King 26228af690aSMarc Zyngier this_cpu_clk = __this_cpu_ptr(twd_evt); 26328af690aSMarc Zyngier *this_cpu_clk = clk; 26428af690aSMarc Zyngier 26554d15b1dSLinus Walleij clockevents_config_and_register(clk, twd_timer_rate, 26654d15b1dSLinus Walleij 0xf, 0xffffffff); 26728af690aSMarc Zyngier enable_percpu_irq(clk->irq, 0); 268f32f4ce2SRussell King } 269