aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource/time-armada-370-xp.c
diff options
context:
space:
mode:
authorGregory CLEMENT <gregory.clement@free-electrons.com>2013-01-25 12:32:42 -0500
committerArnd Bergmann <arnd@arndb.de>2013-02-28 12:57:07 -0500
commitddd3f69f9f01063edabeb8ca5b1551936f98dfb1 (patch)
treeabb1164e485e1fa5e321c83b2c7bc1bee820b4c6 /drivers/clocksource/time-armada-370-xp.c
parent3a6f08a3708f6faf6aba4abbdeaa9da29393f696 (diff)
clocksource: time-armada-370-xp: add local timer support
On the SOCs Armada 370 and Armada XP, each CPU comes with two private timers. This patch use the timer 0 of each CPU as local timer for the clockevent if CONFIG_LOCAL_TIMER is selected. In the other case, use only the private Timer 0 of CPU 0. Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com> Signed-off-by: Jason Cooper <jason@lakedaemon.net>
Diffstat (limited to 'drivers/clocksource/time-armada-370-xp.c')
-rw-r--r--drivers/clocksource/time-armada-370-xp.c150
1 files changed, 112 insertions, 38 deletions
diff --git a/drivers/clocksource/time-armada-370-xp.c b/drivers/clocksource/time-armada-370-xp.c
index a4605fd7e303..47a673070d70 100644
--- a/drivers/clocksource/time-armada-370-xp.c
+++ b/drivers/clocksource/time-armada-370-xp.c
@@ -27,8 +27,10 @@
27#include <linux/of_address.h> 27#include <linux/of_address.h>
28#include <linux/irq.h> 28#include <linux/irq.h>
29#include <linux/module.h> 29#include <linux/module.h>
30#include <asm/sched_clock.h>
31 30
31#include <asm/sched_clock.h>
32#include <asm/localtimer.h>
33#include <linux/percpu.h>
32/* 34/*
33 * Timer block registers. 35 * Timer block registers.
34 */ 36 */
@@ -49,6 +51,7 @@
49#define TIMER1_RELOAD_OFF 0x0018 51#define TIMER1_RELOAD_OFF 0x0018
50#define TIMER1_VAL_OFF 0x001c 52#define TIMER1_VAL_OFF 0x001c
51 53
54#define LCL_TIMER_EVENTS_STATUS 0x0028
52/* Global timers are connected to the coherency fabric clock, and the 55/* Global timers are connected to the coherency fabric clock, and the
53 below divider reduces their incrementing frequency. */ 56 below divider reduces their incrementing frequency. */
54#define TIMER_DIVIDER_SHIFT 5 57#define TIMER_DIVIDER_SHIFT 5
@@ -57,14 +60,17 @@
57/* 60/*
58 * SoC-specific data. 61 * SoC-specific data.
59 */ 62 */
60static void __iomem *timer_base; 63static void __iomem *timer_base, *local_base;
61static int timer_irq; 64static unsigned int timer_clk;
65static bool timer25Mhz = true;
62 66
63/* 67/*
64 * Number of timer ticks per jiffy. 68 * Number of timer ticks per jiffy.
65 */ 69 */
66static u32 ticks_per_jiffy; 70static u32 ticks_per_jiffy;
67 71
72static struct clock_event_device __percpu **percpu_armada_370_xp_evt;
73
68static u32 notrace armada_370_xp_read_sched_clock(void) 74static u32 notrace armada_370_xp_read_sched_clock(void)
69{ 75{
70 return ~readl(timer_base + TIMER0_VAL_OFF); 76 return ~readl(timer_base + TIMER0_VAL_OFF);
@@ -78,24 +84,23 @@ armada_370_xp_clkevt_next_event(unsigned long delta,
78 struct clock_event_device *dev) 84 struct clock_event_device *dev)
79{ 85{
80 u32 u; 86 u32 u;
81
82 /* 87 /*
83 * Clear clockevent timer interrupt. 88 * Clear clockevent timer interrupt.
84 */ 89 */
85 writel(TIMER1_CLR_MASK, timer_base + TIMER_EVENTS_STATUS); 90 writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS);
86 91
87 /* 92 /*
88 * Setup new clockevent timer value. 93 * Setup new clockevent timer value.
89 */ 94 */
90 writel(delta, timer_base + TIMER1_VAL_OFF); 95 writel(delta, local_base + TIMER0_VAL_OFF);
91 96
92 /* 97 /*
93 * Enable the timer. 98 * Enable the timer.
94 */ 99 */
95 u = readl(timer_base + TIMER_CTRL_OFF); 100 u = readl(local_base + TIMER_CTRL_OFF);
96 u = ((u & ~TIMER1_RELOAD_EN) | TIMER1_EN | 101 u = ((u & ~TIMER0_RELOAD_EN) | TIMER0_EN |
97 TIMER1_DIV(TIMER_DIVIDER_SHIFT)); 102 TIMER0_DIV(TIMER_DIVIDER_SHIFT));
98 writel(u, timer_base + TIMER_CTRL_OFF); 103 writel(u, local_base + TIMER_CTRL_OFF);
99 104
100 return 0; 105 return 0;
101} 106}
@@ -107,37 +112,38 @@ armada_370_xp_clkevt_mode(enum clock_event_mode mode,
107 u32 u; 112 u32 u;
108 113
109 if (mode == CLOCK_EVT_MODE_PERIODIC) { 114 if (mode == CLOCK_EVT_MODE_PERIODIC) {
115
110 /* 116 /*
111 * Setup timer to fire at 1/HZ intervals. 117 * Setup timer to fire at 1/HZ intervals.
112 */ 118 */
113 writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD_OFF); 119 writel(ticks_per_jiffy - 1, local_base + TIMER0_RELOAD_OFF);
114 writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL_OFF); 120 writel(ticks_per_jiffy - 1, local_base + TIMER0_VAL_OFF);
115 121
116 /* 122 /*
117 * Enable timer. 123 * Enable timer.
118 */ 124 */
119 u = readl(timer_base + TIMER_CTRL_OFF);
120 125
121 writel((u | TIMER1_EN | TIMER1_RELOAD_EN | 126 u = readl(local_base + TIMER_CTRL_OFF);
122 TIMER1_DIV(TIMER_DIVIDER_SHIFT)), 127
123 timer_base + TIMER_CTRL_OFF); 128 writel((u | TIMER0_EN | TIMER0_RELOAD_EN |
129 TIMER0_DIV(TIMER_DIVIDER_SHIFT)),
130 local_base + TIMER_CTRL_OFF);
124 } else { 131 } else {
125 /* 132 /*
126 * Disable timer. 133 * Disable timer.
127 */ 134 */
128 u = readl(timer_base + TIMER_CTRL_OFF); 135 u = readl(local_base + TIMER_CTRL_OFF);
129 writel(u & ~TIMER1_EN, timer_base + TIMER_CTRL_OFF); 136 writel(u & ~TIMER0_EN, local_base + TIMER_CTRL_OFF);
130 137
131 /* 138 /*
132 * ACK pending timer interrupt. 139 * ACK pending timer interrupt.
133 */ 140 */
134 writel(TIMER1_CLR_MASK, timer_base + TIMER_EVENTS_STATUS); 141 writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS);
135
136 } 142 }
137} 143}
138 144
139static struct clock_event_device armada_370_xp_clkevt = { 145static struct clock_event_device armada_370_xp_clkevt = {
140 .name = "armada_370_xp_tick", 146 .name = "armada_370_xp_per_cpu_tick",
141 .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, 147 .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
142 .shift = 32, 148 .shift = 32,
143 .rating = 300, 149 .rating = 300,
@@ -150,32 +156,78 @@ static irqreturn_t armada_370_xp_timer_interrupt(int irq, void *dev_id)
150 /* 156 /*
151 * ACK timer interrupt and call event handler. 157 * ACK timer interrupt and call event handler.
152 */ 158 */
159 struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
153 160
154 writel(TIMER1_CLR_MASK, timer_base + TIMER_EVENTS_STATUS); 161 writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS);
155 armada_370_xp_clkevt.event_handler(&armada_370_xp_clkevt); 162 evt->event_handler(evt);
156 163
157 return IRQ_HANDLED; 164 return IRQ_HANDLED;
158} 165}
159 166
160static struct irqaction armada_370_xp_timer_irq = { 167/*
161 .name = "armada_370_xp_tick", 168 * Setup the local clock events for a CPU.
162 .flags = IRQF_DISABLED | IRQF_TIMER, 169 */
163 .handler = armada_370_xp_timer_interrupt 170static int __cpuinit armada_370_xp_timer_setup(struct clock_event_device *evt)
171{
172 u32 u;
173 int cpu = smp_processor_id();
174
175 /* Use existing clock_event for cpu 0 */
176 if (!smp_processor_id())
177 return 0;
178
179 u = readl(local_base + TIMER_CTRL_OFF);
180 if (timer25Mhz)
181 writel(u | TIMER0_25MHZ, local_base + TIMER_CTRL_OFF);
182 else
183 writel(u & ~TIMER0_25MHZ, local_base + TIMER_CTRL_OFF);
184
185 evt->name = armada_370_xp_clkevt.name;
186 evt->irq = armada_370_xp_clkevt.irq;
187 evt->features = armada_370_xp_clkevt.features;
188 evt->shift = armada_370_xp_clkevt.shift;
189 evt->rating = armada_370_xp_clkevt.rating,
190 evt->set_next_event = armada_370_xp_clkevt_next_event,
191 evt->set_mode = armada_370_xp_clkevt_mode,
192 evt->cpumask = cpumask_of(cpu);
193
194 *__this_cpu_ptr(percpu_armada_370_xp_evt) = evt;
195
196 clockevents_config_and_register(evt, timer_clk, 1, 0xfffffffe);
197 enable_percpu_irq(evt->irq, 0);
198
199 return 0;
200}
201
202static void armada_370_xp_timer_stop(struct clock_event_device *evt)
203{
204 evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
205 disable_percpu_irq(evt->irq);
206}
207
208static struct local_timer_ops armada_370_xp_local_timer_ops __cpuinitdata = {
209 .setup = armada_370_xp_timer_setup,
210 .stop = armada_370_xp_timer_stop,
164}; 211};
165 212
166void __init armada_370_xp_timer_init(void) 213void __init armada_370_xp_timer_init(void)
167{ 214{
168 u32 u; 215 u32 u;
169 struct device_node *np; 216 struct device_node *np;
170 unsigned int timer_clk; 217 int res;
218
171 np = of_find_compatible_node(NULL, NULL, "marvell,armada-370-xp-timer"); 219 np = of_find_compatible_node(NULL, NULL, "marvell,armada-370-xp-timer");
172 timer_base = of_iomap(np, 0); 220 timer_base = of_iomap(np, 0);
173 WARN_ON(!timer_base); 221 WARN_ON(!timer_base);
222 local_base = of_iomap(np, 1);
174 223
175 if (of_find_property(np, "marvell,timer-25Mhz", NULL)) { 224 if (of_find_property(np, "marvell,timer-25Mhz", NULL)) {
176 /* The fixed 25MHz timer is available so let's use it */ 225 /* The fixed 25MHz timer is available so let's use it */
226 u = readl(local_base + TIMER_CTRL_OFF);
227 writel(u | TIMER0_25MHZ,
228 local_base + TIMER_CTRL_OFF);
177 u = readl(timer_base + TIMER_CTRL_OFF); 229 u = readl(timer_base + TIMER_CTRL_OFF);
178 writel(u | TIMER0_25MHZ | TIMER1_25MHZ, 230 writel(u | TIMER0_25MHZ,
179 timer_base + TIMER_CTRL_OFF); 231 timer_base + TIMER_CTRL_OFF);
180 timer_clk = 25000000; 232 timer_clk = 25000000;
181 } else { 233 } else {
@@ -183,15 +235,23 @@ void __init armada_370_xp_timer_init(void)
183 struct clk *clk = of_clk_get(np, 0); 235 struct clk *clk = of_clk_get(np, 0);
184 WARN_ON(IS_ERR(clk)); 236 WARN_ON(IS_ERR(clk));
185 rate = clk_get_rate(clk); 237 rate = clk_get_rate(clk);
238 u = readl(local_base + TIMER_CTRL_OFF);
239 writel(u & ~(TIMER0_25MHZ),
240 local_base + TIMER_CTRL_OFF);
241
186 u = readl(timer_base + TIMER_CTRL_OFF); 242 u = readl(timer_base + TIMER_CTRL_OFF);
187 writel(u & ~(TIMER0_25MHZ | TIMER1_25MHZ), 243 writel(u & ~(TIMER0_25MHZ),
188 timer_base + TIMER_CTRL_OFF); 244 timer_base + TIMER_CTRL_OFF);
245
189 timer_clk = rate / TIMER_DIVIDER; 246 timer_clk = rate / TIMER_DIVIDER;
247 timer25Mhz = false;
190 } 248 }
191 249
192 /* We use timer 0 as clocksource, and timer 1 for 250 /*
193 clockevents */ 251 * We use timer 0 as clocksource, and private(local) timer 0
194 timer_irq = irq_of_parse_and_map(np, 1); 252 * for clockevents
253 */
254 armada_370_xp_clkevt.irq = irq_of_parse_and_map(np, 4);
195 255
196 ticks_per_jiffy = (timer_clk + HZ / 2) / HZ; 256 ticks_per_jiffy = (timer_clk + HZ / 2) / HZ;
197 257
@@ -216,12 +276,26 @@ void __init armada_370_xp_timer_init(void)
216 "armada_370_xp_clocksource", 276 "armada_370_xp_clocksource",
217 timer_clk, 300, 32, clocksource_mmio_readl_down); 277 timer_clk, 300, 32, clocksource_mmio_readl_down);
218 278
219 /* 279 /* Register the clockevent on the private timer of CPU 0 */
220 * Setup clockevent timer (interrupt-driven).
221 */
222 setup_irq(timer_irq, &armada_370_xp_timer_irq);
223 armada_370_xp_clkevt.cpumask = cpumask_of(0); 280 armada_370_xp_clkevt.cpumask = cpumask_of(0);
224 clockevents_config_and_register(&armada_370_xp_clkevt, 281 clockevents_config_and_register(&armada_370_xp_clkevt,
225 timer_clk, 1, 0xfffffffe); 282 timer_clk, 1, 0xfffffffe);
226}
227 283
284 percpu_armada_370_xp_evt = alloc_percpu(struct clock_event_device *);
285
286
287 /*
288 * Setup clockevent timer (interrupt-driven).
289 */
290 *__this_cpu_ptr(percpu_armada_370_xp_evt) = &armada_370_xp_clkevt;
291 res = request_percpu_irq(armada_370_xp_clkevt.irq,
292 armada_370_xp_timer_interrupt,
293 armada_370_xp_clkevt.name,
294 percpu_armada_370_xp_evt);
295 if (!res) {
296 enable_percpu_irq(armada_370_xp_clkevt.irq, 0);
297#ifdef CONFIG_LOCAL_TIMERS
298 local_timer_register(&armada_370_xp_local_timer_ops);
299#endif
300 }
301}