time-armada-370-xp.c revision ad48bd618f3761922c53f08e05fe00f3c85ca275
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 32#include <asm/localtimer.h> 33#include <linux/percpu.h> 34/* 35 * Timer block registers. 36 */ 37#define TIMER_CTRL_OFF 0x0000 38#define TIMER0_EN BIT(0) 39#define TIMER0_RELOAD_EN BIT(1) 40#define TIMER0_25MHZ BIT(11) 41#define TIMER0_DIV(div) ((div) << 19) 42#define TIMER1_EN BIT(2) 43#define TIMER1_RELOAD_EN BIT(3) 44#define TIMER1_25MHZ BIT(12) 45#define TIMER1_DIV(div) ((div) << 22) 46#define TIMER_EVENTS_STATUS 0x0004 47#define TIMER0_CLR_MASK (~0x1) 48#define TIMER1_CLR_MASK (~0x100) 49#define TIMER0_RELOAD_OFF 0x0010 50#define TIMER0_VAL_OFF 0x0014 51#define TIMER1_RELOAD_OFF 0x0018 52#define TIMER1_VAL_OFF 0x001c 53 54#define LCL_TIMER_EVENTS_STATUS 0x0028 55/* Global timers are connected to the coherency fabric clock, and the 56 below divider reduces their incrementing frequency. */ 57#define TIMER_DIVIDER_SHIFT 5 58#define TIMER_DIVIDER (1 << TIMER_DIVIDER_SHIFT) 59 60/* 61 * SoC-specific data. 62 */ 63static void __iomem *timer_base, *local_base; 64static unsigned int timer_clk; 65static bool timer25Mhz = true; 66 67/* 68 * Number of timer ticks per jiffy. 69 */ 70static u32 ticks_per_jiffy; 71 72static struct clock_event_device __percpu **percpu_armada_370_xp_evt; 73 74static u32 notrace armada_370_xp_read_sched_clock(void) 75{ 76 return ~readl(timer_base + TIMER0_VAL_OFF); 77} 78 79/* 80 * Clockevent handling. 81 */ 82static int 83armada_370_xp_clkevt_next_event(unsigned long delta, 84 struct clock_event_device *dev) 85{ 86 u32 u; 87 /* 88 * Clear clockevent timer interrupt. 89 */ 90 writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS); 91 92 /* 93 * Setup new clockevent timer value. 94 */ 95 writel(delta, local_base + TIMER0_VAL_OFF); 96 97 /* 98 * Enable the timer. 99 */ 100 u = readl(local_base + TIMER_CTRL_OFF); 101 u = ((u & ~TIMER0_RELOAD_EN) | TIMER0_EN | 102 TIMER0_DIV(TIMER_DIVIDER_SHIFT)); 103 writel(u, local_base + TIMER_CTRL_OFF); 104 105 return 0; 106} 107 108static void 109armada_370_xp_clkevt_mode(enum clock_event_mode mode, 110 struct clock_event_device *dev) 111{ 112 u32 u; 113 114 if (mode == CLOCK_EVT_MODE_PERIODIC) { 115 116 /* 117 * Setup timer to fire at 1/HZ intervals. 118 */ 119 writel(ticks_per_jiffy - 1, local_base + TIMER0_RELOAD_OFF); 120 writel(ticks_per_jiffy - 1, local_base + TIMER0_VAL_OFF); 121 122 /* 123 * Enable timer. 124 */ 125 126 u = readl(local_base + TIMER_CTRL_OFF); 127 128 writel((u | TIMER0_EN | TIMER0_RELOAD_EN | 129 TIMER0_DIV(TIMER_DIVIDER_SHIFT)), 130 local_base + TIMER_CTRL_OFF); 131 } else { 132 /* 133 * Disable timer. 134 */ 135 u = readl(local_base + TIMER_CTRL_OFF); 136 writel(u & ~TIMER0_EN, local_base + TIMER_CTRL_OFF); 137 138 /* 139 * ACK pending timer interrupt. 140 */ 141 writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS); 142 } 143} 144 145static struct clock_event_device armada_370_xp_clkevt = { 146 .name = "armada_370_xp_per_cpu_tick", 147 .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, 148 .shift = 32, 149 .rating = 300, 150 .set_next_event = armada_370_xp_clkevt_next_event, 151 .set_mode = armada_370_xp_clkevt_mode, 152}; 153 154static irqreturn_t armada_370_xp_timer_interrupt(int irq, void *dev_id) 155{ 156 /* 157 * ACK timer interrupt and call event handler. 158 */ 159 struct clock_event_device *evt = *(struct clock_event_device **)dev_id; 160 161 writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS); 162 evt->event_handler(evt); 163 164 return IRQ_HANDLED; 165} 166 167/* 168 * Setup the local clock events for a CPU. 169 */ 170static int 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 = { 209 .setup = armada_370_xp_timer_setup, 210 .stop = armada_370_xp_timer_stop, 211}; 212 213void __init armada_370_xp_timer_init(void) 214{ 215 u32 u; 216 struct device_node *np; 217 int res; 218 219 np = of_find_compatible_node(NULL, NULL, "marvell,armada-370-xp-timer"); 220 timer_base = of_iomap(np, 0); 221 WARN_ON(!timer_base); 222 local_base = of_iomap(np, 1); 223 224 if (of_find_property(np, "marvell,timer-25Mhz", NULL)) { 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); 229 u = readl(timer_base + TIMER_CTRL_OFF); 230 writel(u | TIMER0_25MHZ, 231 timer_base + TIMER_CTRL_OFF); 232 timer_clk = 25000000; 233 } else { 234 unsigned long rate = 0; 235 struct clk *clk = of_clk_get(np, 0); 236 WARN_ON(IS_ERR(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 242 u = readl(timer_base + TIMER_CTRL_OFF); 243 writel(u & ~(TIMER0_25MHZ), 244 timer_base + TIMER_CTRL_OFF); 245 246 timer_clk = rate / TIMER_DIVIDER; 247 timer25Mhz = false; 248 } 249 250 /* 251 * We use timer 0 as clocksource, and private(local) timer 0 252 * for clockevents 253 */ 254 armada_370_xp_clkevt.irq = irq_of_parse_and_map(np, 4); 255 256 ticks_per_jiffy = (timer_clk + HZ / 2) / HZ; 257 258 /* 259 * Set scale and timer for sched_clock. 260 */ 261 setup_sched_clock(armada_370_xp_read_sched_clock, 32, timer_clk); 262 263 /* 264 * Setup free-running clocksource timer (interrupts 265 * disabled). 266 */ 267 writel(0xffffffff, timer_base + TIMER0_VAL_OFF); 268 writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF); 269 270 u = readl(timer_base + TIMER_CTRL_OFF); 271 272 writel((u | TIMER0_EN | TIMER0_RELOAD_EN | 273 TIMER0_DIV(TIMER_DIVIDER_SHIFT)), timer_base + TIMER_CTRL_OFF); 274 275 clocksource_mmio_init(timer_base + TIMER0_VAL_OFF, 276 "armada_370_xp_clocksource", 277 timer_clk, 300, 32, clocksource_mmio_readl_down); 278 279 /* Register the clockevent on the private timer of CPU 0 */ 280 armada_370_xp_clkevt.cpumask = cpumask_of(0); 281 clockevents_config_and_register(&armada_370_xp_clkevt, 282 timer_clk, 1, 0xfffffffe); 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} 302