| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 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> | 
| Linus Walleij | 5def51b | 2011-12-13 12:47:31 +0100 | [diff] [blame] | 13 | #include <linux/clk.h> | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 14 | #include <linux/delay.h> | 
 | 15 | #include <linux/device.h> | 
| Linus Walleij | 5def51b | 2011-12-13 12:47:31 +0100 | [diff] [blame] | 16 | #include <linux/err.h> | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 17 | #include <linux/smp.h> | 
 | 18 | #include <linux/jiffies.h> | 
 | 19 | #include <linux/clockchips.h> | 
| Marc Zyngier | 9248510 | 2012-01-10 23:00:54 +0000 | [diff] [blame] | 20 | #include <linux/interrupt.h> | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 21 | #include <linux/io.h> | 
| Marc Zyngier | d8e0364 | 2012-01-10 22:15:45 +0000 | [diff] [blame] | 22 | #include <linux/of_irq.h> | 
 | 23 | #include <linux/of_address.h> | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 24 |  | 
 | 25 | #include <asm/smp_twd.h> | 
| Marc Zyngier | 28af690 | 2011-07-22 12:52:37 +0100 | [diff] [blame] | 26 | #include <asm/localtimer.h> | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 27 | #include <asm/hardware/gic.h> | 
 | 28 |  | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 29 | /* set up by the platform code */ | 
| Marc Zyngier | 9248510 | 2012-01-10 23:00:54 +0000 | [diff] [blame] | 30 | static void __iomem *twd_base; | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 31 |  | 
| Linus Walleij | 5def51b | 2011-12-13 12:47:31 +0100 | [diff] [blame] | 32 | static struct clk *twd_clk; | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 33 | static unsigned long twd_timer_rate; | 
| Linus Walleij | a68becd | 2012-10-23 08:29:48 +0100 | [diff] [blame] | 34 | static bool common_setup_called; | 
 | 35 | static DEFINE_PER_CPU(bool, percpu_setup_called); | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 36 |  | 
| Marc Zyngier | 28af690 | 2011-07-22 12:52:37 +0100 | [diff] [blame] | 37 | static struct clock_event_device __percpu **twd_evt; | 
| Marc Zyngier | 81e46f7 | 2012-01-10 19:39:26 +0000 | [diff] [blame] | 38 | static int twd_ppi; | 
| Marc Zyngier | 28af690 | 2011-07-22 12:52:37 +0100 | [diff] [blame] | 39 |  | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 40 | static void twd_set_mode(enum clock_event_mode mode, | 
 | 41 | 			struct clock_event_device *clk) | 
 | 42 | { | 
 | 43 | 	unsigned long ctrl; | 
 | 44 |  | 
| Russell King | 4c5158d | 2009-05-17 10:58:54 +0100 | [diff] [blame] | 45 | 	switch (mode) { | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 46 | 	case CLOCK_EVT_MODE_PERIODIC: | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 47 | 		ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE | 
 | 48 | 			| TWD_TIMER_CONTROL_PERIODIC; | 
| Linus Walleij | ad17a26 | 2012-10-22 10:17:24 +0100 | [diff] [blame] | 49 | 		__raw_writel(DIV_ROUND_CLOSEST(twd_timer_rate, HZ), | 
 | 50 | 			twd_base + TWD_TIMER_LOAD); | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 51 | 		break; | 
 | 52 | 	case CLOCK_EVT_MODE_ONESHOT: | 
 | 53 | 		/* period set, and timer enabled in 'next_event' hook */ | 
 | 54 | 		ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT; | 
 | 55 | 		break; | 
 | 56 | 	case CLOCK_EVT_MODE_UNUSED: | 
 | 57 | 	case CLOCK_EVT_MODE_SHUTDOWN: | 
 | 58 | 	default: | 
 | 59 | 		ctrl = 0; | 
 | 60 | 	} | 
 | 61 |  | 
 | 62 | 	__raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL); | 
 | 63 | } | 
 | 64 |  | 
 | 65 | static int twd_set_next_event(unsigned long evt, | 
 | 66 | 			struct clock_event_device *unused) | 
 | 67 | { | 
 | 68 | 	unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL); | 
 | 69 |  | 
| Russell King | 4c5158d | 2009-05-17 10:58:54 +0100 | [diff] [blame] | 70 | 	ctrl |= TWD_TIMER_CONTROL_ENABLE; | 
 | 71 |  | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 72 | 	__raw_writel(evt, twd_base + TWD_TIMER_COUNTER); | 
| Russell King | 4c5158d | 2009-05-17 10:58:54 +0100 | [diff] [blame] | 73 | 	__raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL); | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 74 |  | 
 | 75 | 	return 0; | 
 | 76 | } | 
 | 77 |  | 
 | 78 | /* | 
 | 79 |  * local_timer_ack: checks for a local timer interrupt. | 
 | 80 |  * | 
 | 81 |  * If a local timer interrupt has occurred, acknowledge and return 1. | 
 | 82 |  * Otherwise, return 0. | 
 | 83 |  */ | 
| Marc Zyngier | 9248510 | 2012-01-10 23:00:54 +0000 | [diff] [blame] | 84 | static int twd_timer_ack(void) | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 85 | { | 
 | 86 | 	if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) { | 
 | 87 | 		__raw_writel(1, twd_base + TWD_TIMER_INTSTAT); | 
 | 88 | 		return 1; | 
 | 89 | 	} | 
 | 90 |  | 
 | 91 | 	return 0; | 
 | 92 | } | 
 | 93 |  | 
| Marc Zyngier | abde710 | 2012-01-10 19:07:28 +0000 | [diff] [blame] | 94 | static void twd_timer_stop(struct clock_event_device *clk) | 
| Marc Zyngier | 28af690 | 2011-07-22 12:52:37 +0100 | [diff] [blame] | 95 | { | 
 | 96 | 	twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk); | 
 | 97 | 	disable_percpu_irq(clk->irq); | 
 | 98 | } | 
 | 99 |  | 
| Mike Turquette | 2b25d9f | 2012-09-13 22:04:18 +0100 | [diff] [blame] | 100 | #ifdef CONFIG_COMMON_CLK | 
 | 101 |  | 
 | 102 | /* | 
 | 103 |  * Updates clockevent frequency when the cpu frequency changes. | 
 | 104 |  * Called on the cpu that is changing frequency with interrupts disabled. | 
 | 105 |  */ | 
 | 106 | static void twd_update_frequency(void *new_rate) | 
 | 107 | { | 
 | 108 | 	twd_timer_rate = *((unsigned long *) new_rate); | 
 | 109 |  | 
 | 110 | 	clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate); | 
 | 111 | } | 
 | 112 |  | 
 | 113 | static int twd_rate_change(struct notifier_block *nb, | 
 | 114 | 	unsigned long flags, void *data) | 
 | 115 | { | 
 | 116 | 	struct clk_notifier_data *cnd = data; | 
 | 117 |  | 
 | 118 | 	/* | 
 | 119 | 	 * The twd clock events must be reprogrammed to account for the new | 
 | 120 | 	 * frequency.  The timer is local to a cpu, so cross-call to the | 
 | 121 | 	 * changing cpu. | 
 | 122 | 	 */ | 
 | 123 | 	if (flags == POST_RATE_CHANGE) | 
 | 124 | 		smp_call_function(twd_update_frequency, | 
 | 125 | 				  (void *)&cnd->new_rate, 1); | 
 | 126 |  | 
 | 127 | 	return NOTIFY_OK; | 
 | 128 | } | 
 | 129 |  | 
 | 130 | static struct notifier_block twd_clk_nb = { | 
 | 131 | 	.notifier_call = twd_rate_change, | 
 | 132 | }; | 
 | 133 |  | 
 | 134 | static int twd_clk_init(void) | 
 | 135 | { | 
 | 136 | 	if (twd_evt && *__this_cpu_ptr(twd_evt) && !IS_ERR(twd_clk)) | 
 | 137 | 		return clk_notifier_register(twd_clk, &twd_clk_nb); | 
 | 138 |  | 
 | 139 | 	return 0; | 
 | 140 | } | 
 | 141 | core_initcall(twd_clk_init); | 
 | 142 |  | 
 | 143 | #elif defined (CONFIG_CPU_FREQ) | 
 | 144 |  | 
 | 145 | #include <linux/cpufreq.h> | 
| Linus Walleij | 4fd7f9b | 2011-12-13 12:48:18 +0100 | [diff] [blame] | 146 |  | 
 | 147 | /* | 
 | 148 |  * Updates clockevent frequency when the cpu frequency changes. | 
 | 149 |  * Called on the cpu that is changing frequency with interrupts disabled. | 
 | 150 |  */ | 
 | 151 | static void twd_update_frequency(void *data) | 
 | 152 | { | 
 | 153 | 	twd_timer_rate = clk_get_rate(twd_clk); | 
 | 154 |  | 
 | 155 | 	clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate); | 
 | 156 | } | 
 | 157 |  | 
 | 158 | static int twd_cpufreq_transition(struct notifier_block *nb, | 
 | 159 | 	unsigned long state, void *data) | 
 | 160 | { | 
 | 161 | 	struct cpufreq_freqs *freqs = data; | 
 | 162 |  | 
 | 163 | 	/* | 
 | 164 | 	 * The twd clock events must be reprogrammed to account for the new | 
 | 165 | 	 * frequency.  The timer is local to a cpu, so cross-call to the | 
 | 166 | 	 * changing cpu. | 
 | 167 | 	 */ | 
 | 168 | 	if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE) | 
 | 169 | 		smp_call_function_single(freqs->cpu, twd_update_frequency, | 
| Russell King | 3cd88f9 | 2012-04-19 19:35:10 +0100 | [diff] [blame] | 170 | 			NULL, 1); | 
| Linus Walleij | 4fd7f9b | 2011-12-13 12:48:18 +0100 | [diff] [blame] | 171 |  | 
 | 172 | 	return NOTIFY_OK; | 
 | 173 | } | 
 | 174 |  | 
 | 175 | static struct notifier_block twd_cpufreq_nb = { | 
 | 176 | 	.notifier_call = twd_cpufreq_transition, | 
 | 177 | }; | 
 | 178 |  | 
 | 179 | static int twd_cpufreq_init(void) | 
 | 180 | { | 
| Santosh Shilimkar | 910ba59 | 2012-02-21 10:24:22 +0100 | [diff] [blame] | 181 | 	if (twd_evt && *__this_cpu_ptr(twd_evt) && !IS_ERR(twd_clk)) | 
| Linus Walleij | 4fd7f9b | 2011-12-13 12:48:18 +0100 | [diff] [blame] | 182 | 		return cpufreq_register_notifier(&twd_cpufreq_nb, | 
 | 183 | 			CPUFREQ_TRANSITION_NOTIFIER); | 
 | 184 |  | 
 | 185 | 	return 0; | 
 | 186 | } | 
 | 187 | core_initcall(twd_cpufreq_init); | 
 | 188 |  | 
 | 189 | #endif | 
 | 190 |  | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 191 | static void __cpuinit twd_calibrate_rate(void) | 
 | 192 | { | 
| Russell King | 03399c1 | 2011-01-25 10:35:36 +0000 | [diff] [blame] | 193 | 	unsigned long count; | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 194 | 	u64 waitjiffies; | 
 | 195 |  | 
 | 196 | 	/* | 
 | 197 | 	 * If this is the first time round, we need to work out how fast | 
 | 198 | 	 * the timer ticks | 
 | 199 | 	 */ | 
 | 200 | 	if (twd_timer_rate == 0) { | 
| Russell King | 4c5158d | 2009-05-17 10:58:54 +0100 | [diff] [blame] | 201 | 		printk(KERN_INFO "Calibrating local timer... "); | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 202 |  | 
 | 203 | 		/* Wait for a tick to start */ | 
 | 204 | 		waitjiffies = get_jiffies_64() + 1; | 
 | 205 |  | 
 | 206 | 		while (get_jiffies_64() < waitjiffies) | 
 | 207 | 			udelay(10); | 
 | 208 |  | 
 | 209 | 		/* OK, now the tick has started, let's get the timer going */ | 
 | 210 | 		waitjiffies += 5; | 
 | 211 |  | 
 | 212 | 				 /* enable, no interrupt or reload */ | 
 | 213 | 		__raw_writel(0x1, twd_base + TWD_TIMER_CONTROL); | 
 | 214 |  | 
 | 215 | 				 /* maximum value */ | 
 | 216 | 		__raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER); | 
 | 217 |  | 
 | 218 | 		while (get_jiffies_64() < waitjiffies) | 
 | 219 | 			udelay(10); | 
 | 220 |  | 
 | 221 | 		count = __raw_readl(twd_base + TWD_TIMER_COUNTER); | 
 | 222 |  | 
 | 223 | 		twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5); | 
 | 224 |  | 
 | 225 | 		printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000, | 
| Vitaly Kuzmichev | 90c5ffe | 2011-07-07 14:56:05 +0100 | [diff] [blame] | 226 | 			(twd_timer_rate / 10000) % 100); | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 227 | 	} | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 228 | } | 
 | 229 |  | 
| Marc Zyngier | 28af690 | 2011-07-22 12:52:37 +0100 | [diff] [blame] | 230 | static irqreturn_t twd_handler(int irq, void *dev_id) | 
 | 231 | { | 
 | 232 | 	struct clock_event_device *evt = *(struct clock_event_device **)dev_id; | 
 | 233 |  | 
 | 234 | 	if (twd_timer_ack()) { | 
 | 235 | 		evt->event_handler(evt); | 
 | 236 | 		return IRQ_HANDLED; | 
 | 237 | 	} | 
 | 238 |  | 
 | 239 | 	return IRQ_NONE; | 
 | 240 | } | 
 | 241 |  | 
| Linus Walleij | 5def51b | 2011-12-13 12:47:31 +0100 | [diff] [blame] | 242 | static struct clk *twd_get_clock(void) | 
 | 243 | { | 
 | 244 | 	struct clk *clk; | 
 | 245 | 	int err; | 
 | 246 |  | 
 | 247 | 	clk = clk_get_sys("smp_twd", NULL); | 
 | 248 | 	if (IS_ERR(clk)) { | 
 | 249 | 		pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); | 
 | 250 | 		return clk; | 
 | 251 | 	} | 
 | 252 |  | 
| Linus Walleij | 2577cf2 | 2012-10-22 10:18:06 +0100 | [diff] [blame] | 253 | 	err = clk_prepare_enable(clk); | 
| Linus Walleij | 5def51b | 2011-12-13 12:47:31 +0100 | [diff] [blame] | 254 | 	if (err) { | 
| Linus Walleij | 2577cf2 | 2012-10-22 10:18:06 +0100 | [diff] [blame] | 255 | 		pr_err("smp_twd: clock failed to prepare+enable: %d\n", err); | 
| Linus Walleij | 5def51b | 2011-12-13 12:47:31 +0100 | [diff] [blame] | 256 | 		clk_put(clk); | 
 | 257 | 		return ERR_PTR(err); | 
 | 258 | 	} | 
 | 259 |  | 
 | 260 | 	return clk; | 
 | 261 | } | 
 | 262 |  | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 263 | /* | 
 | 264 |  * Setup the local clock events for a CPU. | 
 | 265 |  */ | 
| Marc Zyngier | 9248510 | 2012-01-10 23:00:54 +0000 | [diff] [blame] | 266 | static int __cpuinit twd_timer_setup(struct clock_event_device *clk) | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 267 | { | 
| Marc Zyngier | 28af690 | 2011-07-22 12:52:37 +0100 | [diff] [blame] | 268 | 	struct clock_event_device **this_cpu_clk; | 
| Linus Walleij | a68becd | 2012-10-23 08:29:48 +0100 | [diff] [blame] | 269 | 	int cpu = smp_processor_id(); | 
| Marc Zyngier | 28af690 | 2011-07-22 12:52:37 +0100 | [diff] [blame] | 270 |  | 
| Linus Walleij | a68becd | 2012-10-23 08:29:48 +0100 | [diff] [blame] | 271 | 	/* | 
 | 272 | 	 * If the basic setup for this CPU has been done before don't | 
 | 273 | 	 * bother with the below. | 
 | 274 | 	 */ | 
 | 275 | 	if (per_cpu(percpu_setup_called, cpu)) { | 
 | 276 | 		__raw_writel(0, twd_base + TWD_TIMER_CONTROL); | 
 | 277 | 		clockevents_register_device(*__this_cpu_ptr(twd_evt)); | 
 | 278 | 		enable_percpu_irq(clk->irq, 0); | 
 | 279 | 		return 0; | 
 | 280 | 	} | 
 | 281 | 	per_cpu(percpu_setup_called, cpu) = true; | 
 | 282 |  | 
 | 283 | 	/* | 
 | 284 | 	 * This stuff only need to be done once for the entire TWD cluster | 
 | 285 | 	 * during the runtime of the system. | 
 | 286 | 	 */ | 
 | 287 | 	if (!common_setup_called) { | 
| Linus Walleij | 5def51b | 2011-12-13 12:47:31 +0100 | [diff] [blame] | 288 | 		twd_clk = twd_get_clock(); | 
 | 289 |  | 
| Linus Walleij | a68becd | 2012-10-23 08:29:48 +0100 | [diff] [blame] | 290 | 		/* | 
 | 291 | 		 * We use IS_ERR_OR_NULL() here, because if the clock stubs | 
 | 292 | 		 * are active we will get a valid clk reference which is | 
 | 293 | 		 * however NULL and will return the rate 0. In that case we | 
 | 294 | 		 * need to calibrate the rate instead. | 
 | 295 | 		 */ | 
 | 296 | 		if (!IS_ERR_OR_NULL(twd_clk)) | 
 | 297 | 			twd_timer_rate = clk_get_rate(twd_clk); | 
 | 298 | 		else | 
 | 299 | 			twd_calibrate_rate(); | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 300 |  | 
| Linus Walleij | a68becd | 2012-10-23 08:29:48 +0100 | [diff] [blame] | 301 | 		common_setup_called = true; | 
 | 302 | 	} | 
 | 303 |  | 
 | 304 | 	/* | 
 | 305 | 	 * The following is done once per CPU the first time .setup() is | 
 | 306 | 	 * called. | 
 | 307 | 	 */ | 
| Marc Zyngier | c214455 | 2012-01-20 12:24:47 +0100 | [diff] [blame] | 308 | 	__raw_writel(0, twd_base + TWD_TIMER_CONTROL); | 
 | 309 |  | 
| Russell King | 4c5158d | 2009-05-17 10:58:54 +0100 | [diff] [blame] | 310 | 	clk->name = "local_timer"; | 
| Russell King | 5388a6b | 2010-07-26 13:19:43 +0100 | [diff] [blame] | 311 | 	clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | | 
 | 312 | 			CLOCK_EVT_FEAT_C3STOP; | 
| Russell King | 4c5158d | 2009-05-17 10:58:54 +0100 | [diff] [blame] | 313 | 	clk->rating = 350; | 
 | 314 | 	clk->set_mode = twd_set_mode; | 
 | 315 | 	clk->set_next_event = twd_set_next_event; | 
| Marc Zyngier | 9248510 | 2012-01-10 23:00:54 +0000 | [diff] [blame] | 316 | 	clk->irq = twd_ppi; | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 317 |  | 
| Marc Zyngier | 28af690 | 2011-07-22 12:52:37 +0100 | [diff] [blame] | 318 | 	this_cpu_clk = __this_cpu_ptr(twd_evt); | 
 | 319 | 	*this_cpu_clk = clk; | 
 | 320 |  | 
| Linus Walleij | 54d15b1 | 2011-12-13 12:46:43 +0100 | [diff] [blame] | 321 | 	clockevents_config_and_register(clk, twd_timer_rate, | 
 | 322 | 					0xf, 0xffffffff); | 
| Marc Zyngier | 28af690 | 2011-07-22 12:52:37 +0100 | [diff] [blame] | 323 | 	enable_percpu_irq(clk->irq, 0); | 
| Marc Zyngier | 81e46f7 | 2012-01-10 19:39:26 +0000 | [diff] [blame] | 324 |  | 
 | 325 | 	return 0; | 
 | 326 | } | 
 | 327 |  | 
 | 328 | static struct local_timer_ops twd_lt_ops __cpuinitdata = { | 
 | 329 | 	.setup	= twd_timer_setup, | 
 | 330 | 	.stop	= twd_timer_stop, | 
 | 331 | }; | 
 | 332 |  | 
| Marc Zyngier | d8e0364 | 2012-01-10 22:15:45 +0000 | [diff] [blame] | 333 | static int __init twd_local_timer_common_register(void) | 
| Marc Zyngier | 81e46f7 | 2012-01-10 19:39:26 +0000 | [diff] [blame] | 334 | { | 
 | 335 | 	int err; | 
 | 336 |  | 
| Marc Zyngier | 81e46f7 | 2012-01-10 19:39:26 +0000 | [diff] [blame] | 337 | 	twd_evt = alloc_percpu(struct clock_event_device *); | 
| Marc Zyngier | d8e0364 | 2012-01-10 22:15:45 +0000 | [diff] [blame] | 338 | 	if (!twd_evt) { | 
| Marc Zyngier | 81e46f7 | 2012-01-10 19:39:26 +0000 | [diff] [blame] | 339 | 		err = -ENOMEM; | 
| Marc Zyngier | d8e0364 | 2012-01-10 22:15:45 +0000 | [diff] [blame] | 340 | 		goto out_free; | 
| Marc Zyngier | 81e46f7 | 2012-01-10 19:39:26 +0000 | [diff] [blame] | 341 | 	} | 
 | 342 |  | 
 | 343 | 	err = request_percpu_irq(twd_ppi, twd_handler, "twd", twd_evt); | 
 | 344 | 	if (err) { | 
 | 345 | 		pr_err("twd: can't register interrupt %d (%d)\n", twd_ppi, err); | 
| Marc Zyngier | d8e0364 | 2012-01-10 22:15:45 +0000 | [diff] [blame] | 346 | 		goto out_free; | 
| Marc Zyngier | 81e46f7 | 2012-01-10 19:39:26 +0000 | [diff] [blame] | 347 | 	} | 
 | 348 |  | 
 | 349 | 	err = local_timer_register(&twd_lt_ops); | 
 | 350 | 	if (err) | 
| Marc Zyngier | d8e0364 | 2012-01-10 22:15:45 +0000 | [diff] [blame] | 351 | 		goto out_irq; | 
| Marc Zyngier | 81e46f7 | 2012-01-10 19:39:26 +0000 | [diff] [blame] | 352 |  | 
 | 353 | 	return 0; | 
 | 354 |  | 
| Marc Zyngier | d8e0364 | 2012-01-10 22:15:45 +0000 | [diff] [blame] | 355 | out_irq: | 
 | 356 | 	free_percpu_irq(twd_ppi, twd_evt); | 
 | 357 | out_free: | 
| Marc Zyngier | 81e46f7 | 2012-01-10 19:39:26 +0000 | [diff] [blame] | 358 | 	iounmap(twd_base); | 
| Marc Zyngier | d8e0364 | 2012-01-10 22:15:45 +0000 | [diff] [blame] | 359 | 	twd_base = NULL; | 
| Marc Zyngier | 81e46f7 | 2012-01-10 19:39:26 +0000 | [diff] [blame] | 360 | 	free_percpu(twd_evt); | 
| Marc Zyngier | d8e0364 | 2012-01-10 22:15:45 +0000 | [diff] [blame] | 361 |  | 
| Marc Zyngier | 81e46f7 | 2012-01-10 19:39:26 +0000 | [diff] [blame] | 362 | 	return err; | 
| Russell King | f32f4ce | 2009-05-16 12:14:21 +0100 | [diff] [blame] | 363 | } | 
| Marc Zyngier | d8e0364 | 2012-01-10 22:15:45 +0000 | [diff] [blame] | 364 |  | 
 | 365 | int __init twd_local_timer_register(struct twd_local_timer *tlt) | 
 | 366 | { | 
 | 367 | 	if (twd_base || twd_evt) | 
 | 368 | 		return -EBUSY; | 
 | 369 |  | 
 | 370 | 	twd_ppi	= tlt->res[1].start; | 
 | 371 |  | 
 | 372 | 	twd_base = ioremap(tlt->res[0].start, resource_size(&tlt->res[0])); | 
 | 373 | 	if (!twd_base) | 
 | 374 | 		return -ENOMEM; | 
 | 375 |  | 
 | 376 | 	return twd_local_timer_common_register(); | 
 | 377 | } | 
 | 378 |  | 
 | 379 | #ifdef CONFIG_OF | 
 | 380 | const static struct of_device_id twd_of_match[] __initconst = { | 
 | 381 | 	{ .compatible = "arm,cortex-a9-twd-timer",	}, | 
 | 382 | 	{ .compatible = "arm,cortex-a5-twd-timer",	}, | 
 | 383 | 	{ .compatible = "arm,arm11mp-twd-timer",	}, | 
 | 384 | 	{ }, | 
 | 385 | }; | 
 | 386 |  | 
 | 387 | void __init twd_local_timer_of_register(void) | 
 | 388 | { | 
 | 389 | 	struct device_node *np; | 
 | 390 | 	int err; | 
 | 391 |  | 
 | 392 | 	np = of_find_matching_node(NULL, twd_of_match); | 
| Rob Herring | 0336517 | 2012-10-25 13:53:09 -0500 | [diff] [blame] | 393 | 	if (!np) | 
| Arnd Bergmann | 5bd09fb | 2012-11-06 23:02:27 +0100 | [diff] [blame] | 394 | 		return; | 
| Marc Zyngier | d8e0364 | 2012-01-10 22:15:45 +0000 | [diff] [blame] | 395 |  | 
 | 396 | 	twd_ppi = irq_of_parse_and_map(np, 0); | 
 | 397 | 	if (!twd_ppi) { | 
 | 398 | 		err = -EINVAL; | 
 | 399 | 		goto out; | 
 | 400 | 	} | 
 | 401 |  | 
 | 402 | 	twd_base = of_iomap(np, 0); | 
 | 403 | 	if (!twd_base) { | 
 | 404 | 		err = -ENOMEM; | 
 | 405 | 		goto out; | 
 | 406 | 	} | 
 | 407 |  | 
 | 408 | 	err = twd_local_timer_common_register(); | 
 | 409 |  | 
 | 410 | out: | 
 | 411 | 	WARN(err, "twd_local_timer_of_register failed (%d)\n", err); | 
 | 412 | } | 
 | 413 | #endif |