time-armada-370-xp.c revision faef31b482549640e2d0095afdf3dedb992cfa80
1/* 2 * Marvell Armada 370/XP SoC timer handling. 3 * 4 * Copyright (C) 2012 Marvell 5 * 6 * Lior Amsalem <alior@marvell.com> 7 * Gregory CLEMENT <gregory.clement@free-electrons.com> 8 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 9 * 10 * This file is licensed under the terms of the GNU General Public 11 * License version 2. This program is licensed "as is" without any 12 * warranty of any kind, whether express or implied. 13 * 14 * Timer 0 is used as free-running clocksource, while timer 1 is 15 * used as clock_event_device. 16 */ 17 18#include <linux/init.h> 19#include <linux/platform_device.h> 20#include <linux/kernel.h> 21#include <linux/clk.h> 22#include <linux/timer.h> 23#include <linux/clockchips.h> 24#include <linux/interrupt.h> 25#include <linux/of.h> 26#include <linux/of_irq.h> 27#include <linux/of_address.h> 28#include <linux/irq.h> 29#include <linux/module.h> 30#include <linux/sched_clock.h> 31#include <linux/percpu.h> 32#include <linux/time-armada-370-xp.h> 33 34#include <asm/localtimer.h> 35/* 36 * Timer block registers. 37 */ 38#define TIMER_CTRL_OFF 0x0000 39#define TIMER0_EN 0x0001 40#define TIMER0_RELOAD_EN 0x0002 41#define TIMER0_25MHZ 0x0800 42#define TIMER0_DIV(div) ((div) << 19) 43#define TIMER1_EN 0x0004 44#define TIMER1_RELOAD_EN 0x0008 45#define TIMER1_25MHZ 0x1000 46#define TIMER1_DIV(div) ((div) << 22) 47#define TIMER_EVENTS_STATUS 0x0004 48#define TIMER0_CLR_MASK (~0x1) 49#define TIMER1_CLR_MASK (~0x100) 50#define TIMER0_RELOAD_OFF 0x0010 51#define TIMER0_VAL_OFF 0x0014 52#define TIMER1_RELOAD_OFF 0x0018 53#define TIMER1_VAL_OFF 0x001c 54 55#define LCL_TIMER_EVENTS_STATUS 0x0028 56/* Global timers are connected to the coherency fabric clock, and the 57 below divider reduces their incrementing frequency. */ 58#define TIMER_DIVIDER_SHIFT 5 59#define TIMER_DIVIDER (1 << TIMER_DIVIDER_SHIFT) 60 61/* 62 * SoC-specific data. 63 */ 64static void __iomem *timer_base, *local_base; 65static unsigned int timer_clk; 66static bool timer25Mhz = true; 67 68/* 69 * Number of timer ticks per jiffy. 70 */ 71static u32 ticks_per_jiffy; 72 73static struct clock_event_device __percpu **percpu_armada_370_xp_evt; 74 75static u32 notrace armada_370_xp_read_sched_clock(void) 76{ 77 return ~readl(timer_base + TIMER0_VAL_OFF); 78} 79 80/* 81 * Clockevent handling. 82 */ 83static int 84armada_370_xp_clkevt_next_event(unsigned long delta, 85 struct clock_event_device *dev) 86{ 87 u32 u; 88 /* 89 * Clear clockevent timer interrupt. 90 */ 91 writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS); 92 93 /* 94 * Setup new clockevent timer value. 95 */ 96 writel(delta, local_base + TIMER0_VAL_OFF); 97 98 /* 99 * Enable the timer. 100 */ 101 u = readl(local_base + TIMER_CTRL_OFF); 102 u = ((u & ~TIMER0_RELOAD_EN) | TIMER0_EN | 103 TIMER0_DIV(TIMER_DIVIDER_SHIFT)); 104 writel(u, local_base + TIMER_CTRL_OFF); 105 106 return 0; 107} 108 109static void 110armada_370_xp_clkevt_mode(enum clock_event_mode mode, 111 struct clock_event_device *dev) 112{ 113 u32 u; 114 115 if (mode == CLOCK_EVT_MODE_PERIODIC) { 116 117 /* 118 * Setup timer to fire at 1/HZ intervals. 119 */ 120 writel(ticks_per_jiffy - 1, local_base + TIMER0_RELOAD_OFF); 121 writel(ticks_per_jiffy - 1, local_base + TIMER0_VAL_OFF); 122 123 /* 124 * Enable timer. 125 */ 126 127 u = readl(local_base + TIMER_CTRL_OFF); 128 129 writel((u | TIMER0_EN | TIMER0_RELOAD_EN | 130 TIMER0_DIV(TIMER_DIVIDER_SHIFT)), 131 local_base + TIMER_CTRL_OFF); 132 } else { 133 /* 134 * Disable timer. 135 */ 136 u = readl(local_base + TIMER_CTRL_OFF); 137 writel(u & ~TIMER0_EN, local_base + TIMER_CTRL_OFF); 138 139 /* 140 * ACK pending timer interrupt. 141 */ 142 writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS); 143 } 144} 145 146static struct clock_event_device armada_370_xp_clkevt = { 147 .name = "armada_370_xp_per_cpu_tick", 148 .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, 149 .shift = 32, 150 .rating = 300, 151 .set_next_event = armada_370_xp_clkevt_next_event, 152 .set_mode = armada_370_xp_clkevt_mode, 153}; 154 155static irqreturn_t armada_370_xp_timer_interrupt(int irq, void *dev_id) 156{ 157 /* 158 * ACK timer interrupt and call event handler. 159 */ 160 struct clock_event_device *evt = *(struct clock_event_device **)dev_id; 161 162 writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS); 163 evt->event_handler(evt); 164 165 return IRQ_HANDLED; 166} 167 168/* 169 * Setup the local clock events for a CPU. 170 */ 171static int __cpuinit armada_370_xp_timer_setup(struct clock_event_device *evt) 172{ 173 u32 u; 174 int cpu = smp_processor_id(); 175 176 /* Use existing clock_event for cpu 0 */ 177 if (!smp_processor_id()) 178 return 0; 179 180 u = readl(local_base + TIMER_CTRL_OFF); 181 if (timer25Mhz) 182 writel(u | TIMER0_25MHZ, local_base + TIMER_CTRL_OFF); 183 else 184 writel(u & ~TIMER0_25MHZ, local_base + TIMER_CTRL_OFF); 185 186 evt->name = armada_370_xp_clkevt.name; 187 evt->irq = armada_370_xp_clkevt.irq; 188 evt->features = armada_370_xp_clkevt.features; 189 evt->shift = armada_370_xp_clkevt.shift; 190 evt->rating = armada_370_xp_clkevt.rating, 191 evt->set_next_event = armada_370_xp_clkevt_next_event, 192 evt->set_mode = armada_370_xp_clkevt_mode, 193 evt->cpumask = cpumask_of(cpu); 194 195 *__this_cpu_ptr(percpu_armada_370_xp_evt) = evt; 196 197 clockevents_config_and_register(evt, timer_clk, 1, 0xfffffffe); 198 enable_percpu_irq(evt->irq, 0); 199 200 return 0; 201} 202 203static void armada_370_xp_timer_stop(struct clock_event_device *evt) 204{ 205 evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt); 206 disable_percpu_irq(evt->irq); 207} 208 209static struct local_timer_ops armada_370_xp_local_timer_ops __cpuinitdata = { 210 .setup = armada_370_xp_timer_setup, 211 .stop = armada_370_xp_timer_stop, 212}; 213 214void __init armada_370_xp_timer_init(void) 215{ 216 u32 u; 217 struct device_node *np; 218 int res; 219 220 np = of_find_compatible_node(NULL, NULL, "marvell,armada-370-xp-timer"); 221 timer_base = of_iomap(np, 0); 222 WARN_ON(!timer_base); 223 local_base = of_iomap(np, 1); 224 225 if (of_find_property(np, "marvell,timer-25Mhz", NULL)) { 226 /* The fixed 25MHz timer is available so let's use it */ 227 u = readl(local_base + TIMER_CTRL_OFF); 228 writel(u | TIMER0_25MHZ, 229 local_base + TIMER_CTRL_OFF); 230 u = readl(timer_base + TIMER_CTRL_OFF); 231 writel(u | TIMER0_25MHZ, 232 timer_base + TIMER_CTRL_OFF); 233 timer_clk = 25000000; 234 } else { 235 unsigned long rate = 0; 236 struct clk *clk = of_clk_get(np, 0); 237 WARN_ON(IS_ERR(clk)); 238 rate = clk_get_rate(clk); 239 u = readl(local_base + TIMER_CTRL_OFF); 240 writel(u & ~(TIMER0_25MHZ), 241 local_base + TIMER_CTRL_OFF); 242 243 u = readl(timer_base + TIMER_CTRL_OFF); 244 writel(u & ~(TIMER0_25MHZ), 245 timer_base + TIMER_CTRL_OFF); 246 247 timer_clk = rate / TIMER_DIVIDER; 248 timer25Mhz = false; 249 } 250 251 /* 252 * We use timer 0 as clocksource, and private(local) timer 0 253 * for clockevents 254 */ 255 armada_370_xp_clkevt.irq = irq_of_parse_and_map(np, 4); 256 257 ticks_per_jiffy = (timer_clk + HZ / 2) / HZ; 258 259 /* 260 * Set scale and timer for sched_clock. 261 */ 262 setup_sched_clock(armada_370_xp_read_sched_clock, 32, timer_clk); 263 264 /* 265 * Setup free-running clocksource timer (interrupts 266 * disabled). 267 */ 268 writel(0xffffffff, timer_base + TIMER0_VAL_OFF); 269 writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF); 270 271 u = readl(timer_base + TIMER_CTRL_OFF); 272 273 writel((u | TIMER0_EN | TIMER0_RELOAD_EN | 274 TIMER0_DIV(TIMER_DIVIDER_SHIFT)), timer_base + TIMER_CTRL_OFF); 275 276 clocksource_mmio_init(timer_base + TIMER0_VAL_OFF, 277 "armada_370_xp_clocksource", 278 timer_clk, 300, 32, clocksource_mmio_readl_down); 279 280 /* Register the clockevent on the private timer of CPU 0 */ 281 armada_370_xp_clkevt.cpumask = cpumask_of(0); 282 clockevents_config_and_register(&armada_370_xp_clkevt, 283 timer_clk, 1, 0xfffffffe); 284 285 percpu_armada_370_xp_evt = alloc_percpu(struct clock_event_device *); 286 287 288 /* 289 * Setup clockevent timer (interrupt-driven). 290 */ 291 *__this_cpu_ptr(percpu_armada_370_xp_evt) = &armada_370_xp_clkevt; 292 res = request_percpu_irq(armada_370_xp_clkevt.irq, 293 armada_370_xp_timer_interrupt, 294 armada_370_xp_clkevt.name, 295 percpu_armada_370_xp_evt); 296 if (!res) { 297 enable_percpu_irq(armada_370_xp_clkevt.irq, 0); 298#ifdef CONFIG_LOCAL_TIMERS 299 local_timer_register(&armada_370_xp_local_timer_ops); 300#endif 301 } 302} 303