blob: a21020c57df683d5fb00a403bfc6abed5a218d5c [file] [log] [blame]
Linus Walleij47505352017-01-22 13:17:17 +01001/*
Linus Walleijf5bf0ee2017-03-24 22:32:34 +01002 * Faraday Technology FTTMR010 timer driver
Linus Walleij47505352017-01-22 13:17:17 +01003 * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
4 *
5 * Based on a rewrite of arch/arm/mach-gemini/timer.c:
6 * Copyright (C) 2001-2006 Storlink, Corp.
7 * Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
8 */
9#include <linux/interrupt.h>
10#include <linux/io.h>
11#include <linux/of.h>
12#include <linux/of_address.h>
13#include <linux/of_irq.h>
Linus Walleij47505352017-01-22 13:17:17 +010014#include <linux/clockchips.h>
15#include <linux/clocksource.h>
16#include <linux/sched_clock.h>
Linus Walleij28e71e22017-03-24 22:32:35 +010017#include <linux/clk.h>
Linus Walleije7bad212017-05-18 22:17:01 +020018#include <linux/slab.h>
Linus Walleijd0d76d52017-05-18 22:17:02 +020019#include <linux/bitops.h>
Linus Walleij47505352017-01-22 13:17:17 +010020
21/*
22 * Register definitions for the timers
23 */
24#define TIMER1_COUNT (0x00)
25#define TIMER1_LOAD (0x04)
26#define TIMER1_MATCH1 (0x08)
27#define TIMER1_MATCH2 (0x0c)
28#define TIMER2_COUNT (0x10)
29#define TIMER2_LOAD (0x14)
30#define TIMER2_MATCH1 (0x18)
31#define TIMER2_MATCH2 (0x1c)
32#define TIMER3_COUNT (0x20)
33#define TIMER3_LOAD (0x24)
34#define TIMER3_MATCH1 (0x28)
35#define TIMER3_MATCH2 (0x2c)
36#define TIMER_CR (0x30)
37#define TIMER_INTR_STATE (0x34)
38#define TIMER_INTR_MASK (0x38)
39
Linus Walleijd0d76d52017-05-18 22:17:02 +020040#define TIMER_1_CR_ENABLE BIT(0)
41#define TIMER_1_CR_CLOCK BIT(1)
42#define TIMER_1_CR_INT BIT(2)
43#define TIMER_2_CR_ENABLE BIT(3)
44#define TIMER_2_CR_CLOCK BIT(4)
45#define TIMER_2_CR_INT BIT(5)
46#define TIMER_3_CR_ENABLE BIT(6)
47#define TIMER_3_CR_CLOCK BIT(7)
48#define TIMER_3_CR_INT BIT(8)
49#define TIMER_1_CR_UPDOWN BIT(9)
50#define TIMER_2_CR_UPDOWN BIT(10)
51#define TIMER_3_CR_UPDOWN BIT(11)
Linus Walleij47505352017-01-22 13:17:17 +010052
Linus Walleijec14ba12017-05-18 22:17:04 +020053/*
54 * The Aspeed AST2400 moves bits around in the control register
55 * and lacks bits for setting the timer to count upwards.
56 */
57#define TIMER_1_CR_ASPEED_ENABLE BIT(0)
58#define TIMER_1_CR_ASPEED_CLOCK BIT(1)
59#define TIMER_1_CR_ASPEED_INT BIT(2)
60#define TIMER_2_CR_ASPEED_ENABLE BIT(4)
61#define TIMER_2_CR_ASPEED_CLOCK BIT(5)
62#define TIMER_2_CR_ASPEED_INT BIT(6)
63#define TIMER_3_CR_ASPEED_ENABLE BIT(8)
64#define TIMER_3_CR_ASPEED_CLOCK BIT(9)
65#define TIMER_3_CR_ASPEED_INT BIT(10)
66
Linus Walleijd0d76d52017-05-18 22:17:02 +020067#define TIMER_1_INT_MATCH1 BIT(0)
68#define TIMER_1_INT_MATCH2 BIT(1)
69#define TIMER_1_INT_OVERFLOW BIT(2)
70#define TIMER_2_INT_MATCH1 BIT(3)
71#define TIMER_2_INT_MATCH2 BIT(4)
72#define TIMER_2_INT_OVERFLOW BIT(5)
73#define TIMER_3_INT_MATCH1 BIT(6)
74#define TIMER_3_INT_MATCH2 BIT(7)
75#define TIMER_3_INT_OVERFLOW BIT(8)
Linus Walleij47505352017-01-22 13:17:17 +010076#define TIMER_INT_ALL_MASK 0x1ff
77
Linus Walleije7bad212017-05-18 22:17:01 +020078struct fttmr010 {
79 void __iomem *base;
80 unsigned int tick_rate;
Linus Walleijec14ba12017-05-18 22:17:04 +020081 bool count_down;
82 u32 t1_enable_val;
Linus Walleije7bad212017-05-18 22:17:01 +020083 struct clock_event_device clkevt;
84};
85
86/* A local singleton used by sched_clock, which is stateless */
87static struct fttmr010 *local_fttmr;
88
89static inline struct fttmr010 *to_fttmr010(struct clock_event_device *evt)
90{
91 return container_of(evt, struct fttmr010, clkevt);
92}
Linus Walleij47505352017-01-22 13:17:17 +010093
Linus Walleijf5bf0ee2017-03-24 22:32:34 +010094static u64 notrace fttmr010_read_sched_clock(void)
Linus Walleij47505352017-01-22 13:17:17 +010095{
Linus Walleijec14ba12017-05-18 22:17:04 +020096 if (local_fttmr->count_down)
97 return ~readl(local_fttmr->base + TIMER2_COUNT);
Linus Walleijb589da82017-05-18 22:17:03 +020098 return readl(local_fttmr->base + TIMER2_COUNT);
Linus Walleij47505352017-01-22 13:17:17 +010099}
100
Linus Walleijf5bf0ee2017-03-24 22:32:34 +0100101static int fttmr010_timer_set_next_event(unsigned long cycles,
Linus Walleij47505352017-01-22 13:17:17 +0100102 struct clock_event_device *evt)
103{
Linus Walleije7bad212017-05-18 22:17:01 +0200104 struct fttmr010 *fttmr010 = to_fttmr010(evt);
Linus Walleij47505352017-01-22 13:17:17 +0100105 u32 cr;
106
Linus Walleijec14ba12017-05-18 22:17:04 +0200107 /* Stop */
108 cr = readl(fttmr010->base + TIMER_CR);
109 cr &= ~fttmr010->t1_enable_val;
110 writel(cr, fttmr010->base + TIMER_CR);
111
112 /* Setup the match register forward/backward in time */
Linus Walleije7bad212017-05-18 22:17:01 +0200113 cr = readl(fttmr010->base + TIMER1_COUNT);
Linus Walleijec14ba12017-05-18 22:17:04 +0200114 if (fttmr010->count_down)
115 cr -= cycles;
116 else
117 cr += cycles;
118 writel(cr, fttmr010->base + TIMER1_MATCH1);
119
120 /* Start */
121 cr = readl(fttmr010->base + TIMER_CR);
122 cr |= fttmr010->t1_enable_val;
123 writel(cr, fttmr010->base + TIMER_CR);
Linus Walleij47505352017-01-22 13:17:17 +0100124
125 return 0;
126}
127
Linus Walleijf5bf0ee2017-03-24 22:32:34 +0100128static int fttmr010_timer_shutdown(struct clock_event_device *evt)
Linus Walleij47505352017-01-22 13:17:17 +0100129{
Linus Walleije7bad212017-05-18 22:17:01 +0200130 struct fttmr010 *fttmr010 = to_fttmr010(evt);
Linus Walleij47505352017-01-22 13:17:17 +0100131 u32 cr;
132
Linus Walleijec14ba12017-05-18 22:17:04 +0200133 /* Stop */
Linus Walleije7bad212017-05-18 22:17:01 +0200134 cr = readl(fttmr010->base + TIMER_CR);
Linus Walleijec14ba12017-05-18 22:17:04 +0200135 cr &= ~fttmr010->t1_enable_val;
Linus Walleije7bad212017-05-18 22:17:01 +0200136 writel(cr, fttmr010->base + TIMER_CR);
137
138 return 0;
139}
140
141static int fttmr010_timer_set_oneshot(struct clock_event_device *evt)
142{
143 struct fttmr010 *fttmr010 = to_fttmr010(evt);
144 u32 cr;
145
Linus Walleijec14ba12017-05-18 22:17:04 +0200146 /* Stop */
Linus Walleije7bad212017-05-18 22:17:01 +0200147 cr = readl(fttmr010->base + TIMER_CR);
Linus Walleijec14ba12017-05-18 22:17:04 +0200148 cr &= ~fttmr010->t1_enable_val;
Linus Walleije7bad212017-05-18 22:17:01 +0200149 writel(cr, fttmr010->base + TIMER_CR);
Linus Walleij47505352017-01-22 13:17:17 +0100150
Linus Walleijec14ba12017-05-18 22:17:04 +0200151 /* Setup counter start from 0 or ~0 */
Linus Walleije7bad212017-05-18 22:17:01 +0200152 writel(0, fttmr010->base + TIMER1_COUNT);
Linus Walleijec14ba12017-05-18 22:17:04 +0200153 if (fttmr010->count_down)
154 writel(~0, fttmr010->base + TIMER1_LOAD);
155 else
156 writel(0, fttmr010->base + TIMER1_LOAD);
Linus Walleij47505352017-01-22 13:17:17 +0100157
Linus Walleije7bad212017-05-18 22:17:01 +0200158 /* Enable interrupt */
159 cr = readl(fttmr010->base + TIMER_INTR_MASK);
Linus Walleij47505352017-01-22 13:17:17 +0100160 cr &= ~(TIMER_1_INT_OVERFLOW | TIMER_1_INT_MATCH2);
161 cr |= TIMER_1_INT_MATCH1;
Linus Walleije7bad212017-05-18 22:17:01 +0200162 writel(cr, fttmr010->base + TIMER_INTR_MASK);
Linus Walleij47505352017-01-22 13:17:17 +0100163
Linus Walleij47505352017-01-22 13:17:17 +0100164 return 0;
165}
166
Linus Walleijf5bf0ee2017-03-24 22:32:34 +0100167static int fttmr010_timer_set_periodic(struct clock_event_device *evt)
Linus Walleij47505352017-01-22 13:17:17 +0100168{
Linus Walleije7bad212017-05-18 22:17:01 +0200169 struct fttmr010 *fttmr010 = to_fttmr010(evt);
170 u32 period = DIV_ROUND_CLOSEST(fttmr010->tick_rate, HZ);
Linus Walleij47505352017-01-22 13:17:17 +0100171 u32 cr;
172
Linus Walleijec14ba12017-05-18 22:17:04 +0200173 /* Stop */
Linus Walleije7bad212017-05-18 22:17:01 +0200174 cr = readl(fttmr010->base + TIMER_CR);
Linus Walleijec14ba12017-05-18 22:17:04 +0200175 cr &= ~fttmr010->t1_enable_val;
Linus Walleije7bad212017-05-18 22:17:01 +0200176 writel(cr, fttmr010->base + TIMER_CR);
Linus Walleij47505352017-01-22 13:17:17 +0100177
Linus Walleijec14ba12017-05-18 22:17:04 +0200178 /* Setup timer to fire at 1/HZ intervals. */
179 if (fttmr010->count_down) {
180 writel(period, fttmr010->base + TIMER1_LOAD);
181 writel(0, fttmr010->base + TIMER1_MATCH1);
182 } else {
183 cr = 0xffffffff - (period - 1);
184 writel(cr, fttmr010->base + TIMER1_COUNT);
185 writel(cr, fttmr010->base + TIMER1_LOAD);
Linus Walleij47505352017-01-22 13:17:17 +0100186
Linus Walleijec14ba12017-05-18 22:17:04 +0200187 /* Enable interrupt on overflow */
188 cr = readl(fttmr010->base + TIMER_INTR_MASK);
189 cr &= ~(TIMER_1_INT_MATCH1 | TIMER_1_INT_MATCH2);
190 cr |= TIMER_1_INT_OVERFLOW;
191 writel(cr, fttmr010->base + TIMER_INTR_MASK);
192 }
Linus Walleij47505352017-01-22 13:17:17 +0100193
194 /* Start the timer */
Linus Walleije7bad212017-05-18 22:17:01 +0200195 cr = readl(fttmr010->base + TIMER_CR);
Linus Walleijec14ba12017-05-18 22:17:04 +0200196 cr |= fttmr010->t1_enable_val;
Linus Walleije7bad212017-05-18 22:17:01 +0200197 writel(cr, fttmr010->base + TIMER_CR);
Linus Walleij47505352017-01-22 13:17:17 +0100198
199 return 0;
200}
201
Linus Walleij47505352017-01-22 13:17:17 +0100202/*
203 * IRQ handler for the timer
204 */
Linus Walleijf5bf0ee2017-03-24 22:32:34 +0100205static irqreturn_t fttmr010_timer_interrupt(int irq, void *dev_id)
Linus Walleij47505352017-01-22 13:17:17 +0100206{
Linus Walleije7bad212017-05-18 22:17:01 +0200207 struct clock_event_device *evt = dev_id;
Linus Walleij47505352017-01-22 13:17:17 +0100208
209 evt->event_handler(evt);
210 return IRQ_HANDLED;
211}
212
Daniel Lezcanoef897182017-05-26 10:38:07 +0200213static int __init fttmr010_common_init(struct device_node *np, bool is_aspeed)
Linus Walleij47505352017-01-22 13:17:17 +0100214{
Linus Walleije7bad212017-05-18 22:17:01 +0200215 struct fttmr010 *fttmr010;
Linus Walleij47505352017-01-22 13:17:17 +0100216 int irq;
Linus Walleijdd984422017-05-18 22:17:00 +0200217 struct clk *clk;
218 int ret;
Linus Walleijec14ba12017-05-18 22:17:04 +0200219 u32 val;
Linus Walleijdd984422017-05-18 22:17:00 +0200220
221 /*
222 * These implementations require a clock reference.
223 * FIXME: we currently only support clocking using PCLK
224 * and using EXTCLK is not supported in the driver.
225 */
226 clk = of_clk_get_by_name(np, "PCLK");
227 if (IS_ERR(clk)) {
228 pr_err("could not get PCLK\n");
229 return PTR_ERR(clk);
230 }
231 ret = clk_prepare_enable(clk);
232 if (ret) {
233 pr_err("failed to enable PCLK\n");
234 return ret;
235 }
Linus Walleij47505352017-01-22 13:17:17 +0100236
Linus Walleije7bad212017-05-18 22:17:01 +0200237 fttmr010 = kzalloc(sizeof(*fttmr010), GFP_KERNEL);
238 if (!fttmr010) {
239 ret = -ENOMEM;
240 goto out_disable_clock;
241 }
242 fttmr010->tick_rate = clk_get_rate(clk);
243
244 fttmr010->base = of_iomap(np, 0);
245 if (!fttmr010->base) {
Linus Walleij47505352017-01-22 13:17:17 +0100246 pr_err("Can't remap registers");
Linus Walleije7bad212017-05-18 22:17:01 +0200247 ret = -ENXIO;
248 goto out_free;
Linus Walleij47505352017-01-22 13:17:17 +0100249 }
250 /* IRQ for timer 1 */
251 irq = irq_of_parse_and_map(np, 0);
252 if (irq <= 0) {
253 pr_err("Can't parse IRQ");
Linus Walleije7bad212017-05-18 22:17:01 +0200254 ret = -EINVAL;
255 goto out_unmap;
Linus Walleij47505352017-01-22 13:17:17 +0100256 }
257
Linus Walleij47505352017-01-22 13:17:17 +0100258 /*
Linus Walleijec14ba12017-05-18 22:17:04 +0200259 * The Aspeed AST2400 moves bits around in the control register,
260 * otherwise it works the same.
261 */
Daniel Lezcanoef897182017-05-26 10:38:07 +0200262 if (is_aspeed) {
Linus Walleijec14ba12017-05-18 22:17:04 +0200263 fttmr010->t1_enable_val = TIMER_1_CR_ASPEED_ENABLE |
264 TIMER_1_CR_ASPEED_INT;
265 /* Downward not available */
266 fttmr010->count_down = true;
267 } else {
268 fttmr010->t1_enable_val = TIMER_1_CR_ENABLE | TIMER_1_CR_INT;
269 }
270
271 /*
Linus Walleij47505352017-01-22 13:17:17 +0100272 * Reset the interrupt mask and status
273 */
Linus Walleije7bad212017-05-18 22:17:01 +0200274 writel(TIMER_INT_ALL_MASK, fttmr010->base + TIMER_INTR_MASK);
275 writel(0, fttmr010->base + TIMER_INTR_STATE);
Linus Walleijec14ba12017-05-18 22:17:04 +0200276
277 /*
278 * Enable timer 1 count up, timer 2 count up, except on Aspeed,
279 * where everything just counts down.
280 */
Daniel Lezcanoef897182017-05-26 10:38:07 +0200281 if (is_aspeed)
Linus Walleijec14ba12017-05-18 22:17:04 +0200282 val = TIMER_2_CR_ASPEED_ENABLE;
283 else {
284 val = TIMER_2_CR_ENABLE;
285 if (!fttmr010->count_down)
286 val |= TIMER_1_CR_UPDOWN | TIMER_2_CR_UPDOWN;
287 }
288 writel(val, fttmr010->base + TIMER_CR);
Linus Walleij47505352017-01-22 13:17:17 +0100289
290 /*
291 * Setup free-running clocksource timer (interrupts
292 * disabled.)
293 */
Linus Walleije7bad212017-05-18 22:17:01 +0200294 local_fttmr = fttmr010;
Linus Walleijb589da82017-05-18 22:17:03 +0200295 writel(0, fttmr010->base + TIMER2_COUNT);
Linus Walleijb589da82017-05-18 22:17:03 +0200296 writel(0, fttmr010->base + TIMER2_MATCH1);
297 writel(0, fttmr010->base + TIMER2_MATCH2);
Linus Walleijec14ba12017-05-18 22:17:04 +0200298
299 if (fttmr010->count_down) {
300 writel(~0, fttmr010->base + TIMER2_LOAD);
301 clocksource_mmio_init(fttmr010->base + TIMER2_COUNT,
302 "FTTMR010-TIMER2",
303 fttmr010->tick_rate,
304 300, 32, clocksource_mmio_readl_down);
305 } else {
306 writel(0, fttmr010->base + TIMER2_LOAD);
307 clocksource_mmio_init(fttmr010->base + TIMER2_COUNT,
308 "FTTMR010-TIMER2",
309 fttmr010->tick_rate,
310 300, 32, clocksource_mmio_readl_up);
311 }
Linus Walleije7bad212017-05-18 22:17:01 +0200312 sched_clock_register(fttmr010_read_sched_clock, 32,
313 fttmr010->tick_rate);
Linus Walleij47505352017-01-22 13:17:17 +0100314
315 /*
Linus Walleije7bad212017-05-18 22:17:01 +0200316 * Setup clockevent timer (interrupt-driven) on timer 1.
Linus Walleij47505352017-01-22 13:17:17 +0100317 */
Linus Walleije7bad212017-05-18 22:17:01 +0200318 writel(0, fttmr010->base + TIMER1_COUNT);
319 writel(0, fttmr010->base + TIMER1_LOAD);
320 writel(0, fttmr010->base + TIMER1_MATCH1);
321 writel(0, fttmr010->base + TIMER1_MATCH2);
322 ret = request_irq(irq, fttmr010_timer_interrupt, IRQF_TIMER,
323 "FTTMR010-TIMER1", &fttmr010->clkevt);
324 if (ret) {
325 pr_err("FTTMR010-TIMER1 no IRQ\n");
326 goto out_unmap;
327 }
328
329 fttmr010->clkevt.name = "FTTMR010-TIMER1";
330 /* Reasonably fast and accurate clock event */
331 fttmr010->clkevt.rating = 300;
332 fttmr010->clkevt.features = CLOCK_EVT_FEAT_PERIODIC |
333 CLOCK_EVT_FEAT_ONESHOT;
334 fttmr010->clkevt.set_next_event = fttmr010_timer_set_next_event;
335 fttmr010->clkevt.set_state_shutdown = fttmr010_timer_shutdown;
336 fttmr010->clkevt.set_state_periodic = fttmr010_timer_set_periodic;
337 fttmr010->clkevt.set_state_oneshot = fttmr010_timer_set_oneshot;
338 fttmr010->clkevt.tick_resume = fttmr010_timer_shutdown;
339 fttmr010->clkevt.cpumask = cpumask_of(0);
340 fttmr010->clkevt.irq = irq;
341 clockevents_config_and_register(&fttmr010->clkevt,
342 fttmr010->tick_rate,
Linus Walleij47505352017-01-22 13:17:17 +0100343 1, 0xffffffff);
344
345 return 0;
Linus Walleije7bad212017-05-18 22:17:01 +0200346
347out_unmap:
348 iounmap(fttmr010->base);
349out_free:
350 kfree(fttmr010);
351out_disable_clock:
352 clk_disable_unprepare(clk);
353
354 return ret;
Linus Walleij47505352017-01-22 13:17:17 +0100355}
Daniel Lezcanoef897182017-05-26 10:38:07 +0200356
357static __init int aspeed_timer_init(struct device_node *np)
358{
359 return fttmr010_common_init(np, true);
360}
361
362static __init int fttmr010_timer_init(struct device_node *np)
363{
364 return fttmr010_common_init(np, false);
365}
366
Daniel Lezcano17273392017-05-26 16:56:11 +0200367TIMER_OF_DECLARE(fttmr010, "faraday,fttmr010", fttmr010_timer_init);
368TIMER_OF_DECLARE(gemini, "cortina,gemini-timer", fttmr010_timer_init);
369TIMER_OF_DECLARE(moxart, "moxa,moxart-timer", fttmr010_timer_init);
370TIMER_OF_DECLARE(ast2400, "aspeed,ast2400-timer", aspeed_timer_init);
371TIMER_OF_DECLARE(ast2500, "aspeed,ast2500-timer", aspeed_timer_init);