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/irq.h> 22 #include <linux/io.h> 23 24 #include <asm/smp_twd.h> 25 #include <asm/localtimer.h> 26 #include <asm/hardware/gic.h> 27 28 /* set up by the platform code */ 29 void __iomem *twd_base; 30 31 static struct clk *twd_clk; 32 static unsigned long twd_timer_rate; 33 34 static struct clock_event_device __percpu **twd_evt; 35 36 static void twd_set_mode(enum clock_event_mode mode, 37 struct clock_event_device *clk) 38 { 39 unsigned long ctrl; 40 41 switch (mode) { 42 case CLOCK_EVT_MODE_PERIODIC: 43 /* timer load already set up */ 44 ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE 45 | TWD_TIMER_CONTROL_PERIODIC; 46 __raw_writel(twd_timer_rate / HZ, twd_base + TWD_TIMER_LOAD); 47 break; 48 case CLOCK_EVT_MODE_ONESHOT: 49 /* period set, and timer enabled in 'next_event' hook */ 50 ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT; 51 break; 52 case CLOCK_EVT_MODE_UNUSED: 53 case CLOCK_EVT_MODE_SHUTDOWN: 54 default: 55 ctrl = 0; 56 } 57 58 __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL); 59 } 60 61 static int twd_set_next_event(unsigned long evt, 62 struct clock_event_device *unused) 63 { 64 unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL); 65 66 ctrl |= TWD_TIMER_CONTROL_ENABLE; 67 68 __raw_writel(evt, twd_base + TWD_TIMER_COUNTER); 69 __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL); 70 71 return 0; 72 } 73 74 /* 75 * local_timer_ack: checks for a local timer interrupt. 76 * 77 * If a local timer interrupt has occurred, acknowledge and return 1. 78 * Otherwise, return 0. 79 */ 80 int twd_timer_ack(void) 81 { 82 if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) { 83 __raw_writel(1, twd_base + TWD_TIMER_INTSTAT); 84 return 1; 85 } 86 87 return 0; 88 } 89 90 void twd_timer_stop(struct clock_event_device *clk) 91 { 92 twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk); 93 disable_percpu_irq(clk->irq); 94 } 95 96 #ifdef CONFIG_CPU_FREQ 97 98 /* 99 * Updates clockevent frequency when the cpu frequency changes. 100 * Called on the cpu that is changing frequency with interrupts disabled. 101 */ 102 static void twd_update_frequency(void *data) 103 { 104 twd_timer_rate = clk_get_rate(twd_clk); 105 106 clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate); 107 } 108 109 static int twd_cpufreq_transition(struct notifier_block *nb, 110 unsigned long state, void *data) 111 { 112 struct cpufreq_freqs *freqs = data; 113 114 /* 115 * The twd clock events must be reprogrammed to account for the new 116 * frequency. The timer is local to a cpu, so cross-call to the 117 * changing cpu. 118 */ 119 if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE) 120 smp_call_function_single(freqs->cpu, twd_update_frequency, 121 NULL, 1); 122 123 return NOTIFY_OK; 124 } 125 126 static struct notifier_block twd_cpufreq_nb = { 127 .notifier_call = twd_cpufreq_transition, 128 }; 129 130 static int twd_cpufreq_init(void) 131 { 132 if (twd_evt && *__this_cpu_ptr(twd_evt) && !IS_ERR(twd_clk)) 133 return cpufreq_register_notifier(&twd_cpufreq_nb, 134 CPUFREQ_TRANSITION_NOTIFIER); 135 136 return 0; 137 } 138 core_initcall(twd_cpufreq_init); 139 140 #endif 141 142 static void __cpuinit twd_calibrate_rate(void) 143 { 144 unsigned long count; 145 u64 waitjiffies; 146 147 /* 148 * If this is the first time round, we need to work out how fast 149 * the timer ticks 150 */ 151 if (twd_timer_rate == 0) { 152 printk(KERN_INFO "Calibrating local timer... "); 153 154 /* Wait for a tick to start */ 155 waitjiffies = get_jiffies_64() + 1; 156 157 while (get_jiffies_64() < waitjiffies) 158 udelay(10); 159 160 /* OK, now the tick has started, let's get the timer going */ 161 waitjiffies += 5; 162 163 /* enable, no interrupt or reload */ 164 __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL); 165 166 /* maximum value */ 167 __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER); 168 169 while (get_jiffies_64() < waitjiffies) 170 udelay(10); 171 172 count = __raw_readl(twd_base + TWD_TIMER_COUNTER); 173 174 twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5); 175 176 printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000, 177 (twd_timer_rate / 10000) % 100); 178 } 179 } 180 181 static irqreturn_t twd_handler(int irq, void *dev_id) 182 { 183 struct clock_event_device *evt = *(struct clock_event_device **)dev_id; 184 185 if (twd_timer_ack()) { 186 evt->event_handler(evt); 187 return IRQ_HANDLED; 188 } 189 190 return IRQ_NONE; 191 } 192 193 static struct clk *twd_get_clock(void) 194 { 195 struct clk *clk; 196 int err; 197 198 clk = clk_get_sys("smp_twd", NULL); 199 if (IS_ERR(clk)) { 200 pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); 201 return clk; 202 } 203 204 err = clk_prepare(clk); 205 if (err) { 206 pr_err("smp_twd: clock failed to prepare: %d\n", err); 207 clk_put(clk); 208 return ERR_PTR(err); 209 } 210 211 err = clk_enable(clk); 212 if (err) { 213 pr_err("smp_twd: clock failed to enable: %d\n", err); 214 clk_unprepare(clk); 215 clk_put(clk); 216 return ERR_PTR(err); 217 } 218 219 return clk; 220 } 221 222 /* 223 * Setup the local clock events for a CPU. 224 */ 225 void __cpuinit twd_timer_setup(struct clock_event_device *clk) 226 { 227 struct clock_event_device **this_cpu_clk; 228 229 if (!twd_evt) { 230 int err; 231 232 twd_evt = alloc_percpu(struct clock_event_device *); 233 if (!twd_evt) { 234 pr_err("twd: can't allocate memory\n"); 235 return; 236 } 237 238 err = request_percpu_irq(clk->irq, twd_handler, 239 "twd", twd_evt); 240 if (err) { 241 pr_err("twd: can't register interrupt %d (%d)\n", 242 clk->irq, err); 243 return; 244 } 245 } 246 247 if (!twd_clk) 248 twd_clk = twd_get_clock(); 249 250 if (!IS_ERR_OR_NULL(twd_clk)) 251 twd_timer_rate = clk_get_rate(twd_clk); 252 else 253 twd_calibrate_rate(); 254 255 __raw_writel(0, twd_base + TWD_TIMER_CONTROL); 256 257 clk->name = "local_timer"; 258 clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | 259 CLOCK_EVT_FEAT_C3STOP; 260 clk->rating = 350; 261 clk->set_mode = twd_set_mode; 262 clk->set_next_event = twd_set_next_event; 263 264 this_cpu_clk = __this_cpu_ptr(twd_evt); 265 *this_cpu_clk = clk; 266 267 clockevents_config_and_register(clk, twd_timer_rate, 268 0xf, 0xffffffff); 269 enable_percpu_irq(clk->irq, 0); 270 } 271