1 /* 2 * linux/arch/arm/kernel/smp_twd.c 3 * 4 * Copyright (C) 2002 ARM Ltd. 5 * All Rights Reserved 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 #include <linux/init.h> 12 #include <linux/kernel.h> 13 #include <linux/clk.h> 14 #include <linux/cpufreq.h> 15 #include <linux/delay.h> 16 #include <linux/device.h> 17 #include <linux/err.h> 18 #include <linux/smp.h> 19 #include <linux/jiffies.h> 20 #include <linux/clockchips.h> 21 #include <linux/interrupt.h> 22 #include <linux/io.h> 23 #include <linux/of_irq.h> 24 #include <linux/of_address.h> 25 26 #include <asm/smp_twd.h> 27 #include <asm/localtimer.h> 28 #include <asm/hardware/gic.h> 29 30 /* set up by the platform code */ 31 static void __iomem *twd_base; 32 33 static struct clk *twd_clk; 34 static unsigned long twd_timer_rate; 35 36 static struct clock_event_device __percpu **twd_evt; 37 static int twd_ppi; 38 39 static void twd_set_mode(enum clock_event_mode mode, 40 struct clock_event_device *clk) 41 { 42 unsigned long ctrl; 43 44 switch (mode) { 45 case CLOCK_EVT_MODE_PERIODIC: 46 /* timer load already set up */ 47 ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE 48 | TWD_TIMER_CONTROL_PERIODIC; 49 __raw_writel(twd_timer_rate / HZ, twd_base + TWD_TIMER_LOAD); 50 break; 51 case CLOCK_EVT_MODE_ONESHOT: 52 /* period set, and timer enabled in 'next_event' hook */ 53 ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT; 54 break; 55 case CLOCK_EVT_MODE_UNUSED: 56 case CLOCK_EVT_MODE_SHUTDOWN: 57 default: 58 ctrl = 0; 59 } 60 61 __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL); 62 } 63 64 static int twd_set_next_event(unsigned long evt, 65 struct clock_event_device *unused) 66 { 67 unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL); 68 69 ctrl |= TWD_TIMER_CONTROL_ENABLE; 70 71 __raw_writel(evt, twd_base + TWD_TIMER_COUNTER); 72 __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL); 73 74 return 0; 75 } 76 77 /* 78 * local_timer_ack: checks for a local timer interrupt. 79 * 80 * If a local timer interrupt has occurred, acknowledge and return 1. 81 * Otherwise, return 0. 82 */ 83 static int twd_timer_ack(void) 84 { 85 if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) { 86 __raw_writel(1, twd_base + TWD_TIMER_INTSTAT); 87 return 1; 88 } 89 90 return 0; 91 } 92 93 static void twd_timer_stop(struct clock_event_device *clk) 94 { 95 twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk); 96 disable_percpu_irq(clk->irq); 97 } 98 99 #ifdef CONFIG_CPU_FREQ 100 101 /* 102 * Updates clockevent frequency when the cpu frequency changes. 103 * Called on the cpu that is changing frequency with interrupts disabled. 104 */ 105 static void twd_update_frequency(void *data) 106 { 107 twd_timer_rate = clk_get_rate(twd_clk); 108 109 clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate); 110 } 111 112 static int twd_cpufreq_transition(struct notifier_block *nb, 113 unsigned long state, void *data) 114 { 115 struct cpufreq_freqs *freqs = data; 116 117 /* 118 * The twd clock events must be reprogrammed to account for the new 119 * frequency. The timer is local to a cpu, so cross-call to the 120 * changing cpu. 121 */ 122 if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE) 123 smp_call_function_single(freqs->cpu, twd_update_frequency, 124 NULL, 1); 125 126 return NOTIFY_OK; 127 } 128 129 static struct notifier_block twd_cpufreq_nb = { 130 .notifier_call = twd_cpufreq_transition, 131 }; 132 133 static int twd_cpufreq_init(void) 134 { 135 if (twd_evt && *__this_cpu_ptr(twd_evt) && !IS_ERR(twd_clk)) 136 return cpufreq_register_notifier(&twd_cpufreq_nb, 137 CPUFREQ_TRANSITION_NOTIFIER); 138 139 return 0; 140 } 141 core_initcall(twd_cpufreq_init); 142 143 #endif 144 145 static void __cpuinit twd_calibrate_rate(void) 146 { 147 unsigned long count; 148 u64 waitjiffies; 149 150 /* 151 * If this is the first time round, we need to work out how fast 152 * the timer ticks 153 */ 154 if (twd_timer_rate == 0) { 155 printk(KERN_INFO "Calibrating local timer... "); 156 157 /* Wait for a tick to start */ 158 waitjiffies = get_jiffies_64() + 1; 159 160 while (get_jiffies_64() < waitjiffies) 161 udelay(10); 162 163 /* OK, now the tick has started, let's get the timer going */ 164 waitjiffies += 5; 165 166 /* enable, no interrupt or reload */ 167 __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL); 168 169 /* maximum value */ 170 __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER); 171 172 while (get_jiffies_64() < waitjiffies) 173 udelay(10); 174 175 count = __raw_readl(twd_base + TWD_TIMER_COUNTER); 176 177 twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5); 178 179 printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000, 180 (twd_timer_rate / 10000) % 100); 181 } 182 } 183 184 static irqreturn_t twd_handler(int irq, void *dev_id) 185 { 186 struct clock_event_device *evt = *(struct clock_event_device **)dev_id; 187 188 if (twd_timer_ack()) { 189 evt->event_handler(evt); 190 return IRQ_HANDLED; 191 } 192 193 return IRQ_NONE; 194 } 195 196 static struct clk *twd_get_clock(void) 197 { 198 struct clk *clk; 199 int err; 200 201 clk = clk_get_sys("smp_twd", NULL); 202 if (IS_ERR(clk)) { 203 pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); 204 return clk; 205 } 206 207 err = clk_prepare(clk); 208 if (err) { 209 pr_err("smp_twd: clock failed to prepare: %d\n", err); 210 clk_put(clk); 211 return ERR_PTR(err); 212 } 213 214 err = clk_enable(clk); 215 if (err) { 216 pr_err("smp_twd: clock failed to enable: %d\n", err); 217 clk_unprepare(clk); 218 clk_put(clk); 219 return ERR_PTR(err); 220 } 221 222 return clk; 223 } 224 225 /* 226 * Setup the local clock events for a CPU. 227 */ 228 static int __cpuinit twd_timer_setup(struct clock_event_device *clk) 229 { 230 struct clock_event_device **this_cpu_clk; 231 232 if (!twd_clk) 233 twd_clk = twd_get_clock(); 234 235 if (!IS_ERR_OR_NULL(twd_clk)) 236 twd_timer_rate = clk_get_rate(twd_clk); 237 else 238 twd_calibrate_rate(); 239 240 __raw_writel(0, twd_base + TWD_TIMER_CONTROL); 241 242 clk->name = "local_timer"; 243 clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | 244 CLOCK_EVT_FEAT_C3STOP; 245 clk->rating = 350; 246 clk->set_mode = twd_set_mode; 247 clk->set_next_event = twd_set_next_event; 248 clk->irq = twd_ppi; 249 250 this_cpu_clk = __this_cpu_ptr(twd_evt); 251 *this_cpu_clk = clk; 252 253 clockevents_config_and_register(clk, twd_timer_rate, 254 0xf, 0xffffffff); 255 enable_percpu_irq(clk->irq, 0); 256 257 return 0; 258 } 259 260 static struct local_timer_ops twd_lt_ops __cpuinitdata = { 261 .setup = twd_timer_setup, 262 .stop = twd_timer_stop, 263 }; 264 265 static int __init twd_local_timer_common_register(void) 266 { 267 int err; 268 269 twd_evt = alloc_percpu(struct clock_event_device *); 270 if (!twd_evt) { 271 err = -ENOMEM; 272 goto out_free; 273 } 274 275 err = request_percpu_irq(twd_ppi, twd_handler, "twd", twd_evt); 276 if (err) { 277 pr_err("twd: can't register interrupt %d (%d)\n", twd_ppi, err); 278 goto out_free; 279 } 280 281 err = local_timer_register(&twd_lt_ops); 282 if (err) 283 goto out_irq; 284 285 return 0; 286 287 out_irq: 288 free_percpu_irq(twd_ppi, twd_evt); 289 out_free: 290 iounmap(twd_base); 291 twd_base = NULL; 292 free_percpu(twd_evt); 293 294 return err; 295 } 296 297 int __init twd_local_timer_register(struct twd_local_timer *tlt) 298 { 299 if (twd_base || twd_evt) 300 return -EBUSY; 301 302 twd_ppi = tlt->res[1].start; 303 304 twd_base = ioremap(tlt->res[0].start, resource_size(&tlt->res[0])); 305 if (!twd_base) 306 return -ENOMEM; 307 308 return twd_local_timer_common_register(); 309 } 310 311 #ifdef CONFIG_OF 312 const static struct of_device_id twd_of_match[] __initconst = { 313 { .compatible = "arm,cortex-a9-twd-timer", }, 314 { .compatible = "arm,cortex-a5-twd-timer", }, 315 { .compatible = "arm,arm11mp-twd-timer", }, 316 { }, 317 }; 318 319 void __init twd_local_timer_of_register(void) 320 { 321 struct device_node *np; 322 int err; 323 324 np = of_find_matching_node(NULL, twd_of_match); 325 if (!np) { 326 err = -ENODEV; 327 goto out; 328 } 329 330 twd_ppi = irq_of_parse_and_map(np, 0); 331 if (!twd_ppi) { 332 err = -EINVAL; 333 goto out; 334 } 335 336 twd_base = of_iomap(np, 0); 337 if (!twd_base) { 338 err = -ENOMEM; 339 goto out; 340 } 341 342 err = twd_local_timer_common_register(); 343 344 out: 345 WARN(err, "twd_local_timer_of_register failed (%d)\n", err); 346 } 347 #endif 348