time-armada-370-xp.c revision 5ddb6d21c30d10ae4a740a788bb9101bd384fea5
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/cpu.h> 23#include <linux/timer.h> 24#include <linux/clockchips.h> 25#include <linux/interrupt.h> 26#include <linux/of.h> 27#include <linux/of_irq.h> 28#include <linux/of_address.h> 29#include <linux/irq.h> 30#include <linux/module.h> 31#include <linux/sched_clock.h> 32#include <linux/percpu.h> 33#include <linux/time-armada-370-xp.h> 34 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 *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 int armada_370_xp_clkevt_irq; 147 148static irqreturn_t armada_370_xp_timer_interrupt(int irq, void *dev_id) 149{ 150 /* 151 * ACK timer interrupt and call event handler. 152 */ 153 struct clock_event_device *evt = dev_id; 154 155 writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS); 156 evt->event_handler(evt); 157 158 return IRQ_HANDLED; 159} 160 161/* 162 * Setup the local clock events for a CPU. 163 */ 164static int __cpuinit armada_370_xp_timer_setup(struct clock_event_device *evt) 165{ 166 u32 u; 167 int cpu = smp_processor_id(); 168 169 u = readl(local_base + TIMER_CTRL_OFF); 170 if (timer25Mhz) 171 writel(u | TIMER0_25MHZ, local_base + TIMER_CTRL_OFF); 172 else 173 writel(u & ~TIMER0_25MHZ, local_base + TIMER_CTRL_OFF); 174 175 evt->name = "armada_370_xp_per_cpu_tick", 176 evt->features = CLOCK_EVT_FEAT_ONESHOT | 177 CLOCK_EVT_FEAT_PERIODIC; 178 evt->shift = 32, 179 evt->rating = 300, 180 evt->set_next_event = armada_370_xp_clkevt_next_event, 181 evt->set_mode = armada_370_xp_clkevt_mode, 182 evt->irq = armada_370_xp_clkevt_irq; 183 evt->cpumask = cpumask_of(cpu); 184 185 clockevents_config_and_register(evt, timer_clk, 1, 0xfffffffe); 186 enable_percpu_irq(evt->irq, 0); 187 188 return 0; 189} 190 191static void __cpuinit armada_370_xp_timer_stop(struct clock_event_device *evt) 192{ 193 evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt); 194 disable_percpu_irq(evt->irq); 195} 196 197static int __cpuinit armada_370_xp_timer_cpu_notify(struct notifier_block *self, 198 unsigned long action, void *hcpu) 199{ 200 /* 201 * Grab cpu pointer in each case to avoid spurious 202 * preemptible warnings 203 */ 204 switch (action & ~CPU_TASKS_FROZEN) { 205 case CPU_STARTING: 206 armada_370_xp_timer_setup(this_cpu_ptr(armada_370_xp_evt)); 207 break; 208 case CPU_DYING: 209 armada_370_xp_timer_stop(this_cpu_ptr(armada_370_xp_evt)); 210 break; 211 } 212 213 return NOTIFY_OK; 214} 215 216static struct notifier_block armada_370_xp_timer_cpu_nb __cpuinitdata = { 217 .notifier_call = armada_370_xp_timer_cpu_notify, 218}; 219 220void __init armada_370_xp_timer_init(void) 221{ 222 u32 u; 223 struct device_node *np; 224 int res; 225 226 np = of_find_compatible_node(NULL, NULL, "marvell,armada-370-xp-timer"); 227 timer_base = of_iomap(np, 0); 228 WARN_ON(!timer_base); 229 local_base = of_iomap(np, 1); 230 231 if (of_find_property(np, "marvell,timer-25Mhz", NULL)) { 232 /* The fixed 25MHz timer is available so let's use it */ 233 u = readl(timer_base + TIMER_CTRL_OFF); 234 writel(u | TIMER0_25MHZ, 235 timer_base + TIMER_CTRL_OFF); 236 timer_clk = 25000000; 237 } else { 238 unsigned long rate = 0; 239 struct clk *clk = of_clk_get(np, 0); 240 WARN_ON(IS_ERR(clk)); 241 rate = clk_get_rate(clk); 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_cpu_notifier(&armada_370_xp_timer_cpu_nb); 281 282 armada_370_xp_evt = alloc_percpu(struct clock_event_device); 283 284 285 /* 286 * Setup clockevent timer (interrupt-driven). 287 */ 288 res = request_percpu_irq(armada_370_xp_clkevt_irq, 289 armada_370_xp_timer_interrupt, 290 "armada_370_xp_per_cpu_tick", 291 armada_370_xp_evt); 292 /* Immediately configure the timer on the boot CPU */ 293 if (!res) 294 armada_370_xp_timer_setup(this_cpu_ptr(armada_370_xp_evt)); 295} 296