blob: 982908350b46c5eae27dbab634789842e5feec07 [file] [log] [blame]
Binghua Duan02c981c2011-07-08 17:40:12 +08001/*
2 * System timer for CSR SiRFprimaII
3 *
4 * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
5 *
6 * Licensed under GPLv2 or later.
7 */
8
9#include <linux/kernel.h>
10#include <linux/interrupt.h>
11#include <linux/clockchips.h>
12#include <linux/clocksource.h>
13#include <linux/bitops.h>
14#include <linux/irq.h>
15#include <linux/clk.h>
16#include <linux/err.h>
17#include <linux/slab.h>
18#include <linux/of.h>
Arnd Bergmann67d71342013-03-19 15:31:08 +010019#include <linux/of_irq.h>
Binghua Duan02c981c2011-07-08 17:40:12 +080020#include <linux/of_address.h>
21#include <mach/map.h>
Marc Zyngierbc8d8492012-01-16 11:44:12 +000022#include <asm/sched_clock.h>
Binghua Duan02c981c2011-07-08 17:40:12 +080023#include <asm/mach/time.h>
24
Binghua Duan198678b2012-08-20 06:42:36 +000025#include "common.h"
26
Binghua Duan02c981c2011-07-08 17:40:12 +080027#define SIRFSOC_TIMER_COUNTER_LO 0x0000
28#define SIRFSOC_TIMER_COUNTER_HI 0x0004
29#define SIRFSOC_TIMER_MATCH_0 0x0008
30#define SIRFSOC_TIMER_MATCH_1 0x000C
31#define SIRFSOC_TIMER_MATCH_2 0x0010
32#define SIRFSOC_TIMER_MATCH_3 0x0014
33#define SIRFSOC_TIMER_MATCH_4 0x0018
34#define SIRFSOC_TIMER_MATCH_5 0x001C
35#define SIRFSOC_TIMER_STATUS 0x0020
36#define SIRFSOC_TIMER_INT_EN 0x0024
37#define SIRFSOC_TIMER_WATCHDOG_EN 0x0028
38#define SIRFSOC_TIMER_DIV 0x002C
39#define SIRFSOC_TIMER_LATCH 0x0030
40#define SIRFSOC_TIMER_LATCHED_LO 0x0034
41#define SIRFSOC_TIMER_LATCHED_HI 0x0038
42
43#define SIRFSOC_TIMER_WDT_INDEX 5
44
45#define SIRFSOC_TIMER_LATCH_BIT BIT(0)
46
Barry Songe5598a82011-09-21 20:56:33 +080047#define SIRFSOC_TIMER_REG_CNT 11
48
49static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = {
50 SIRFSOC_TIMER_MATCH_0, SIRFSOC_TIMER_MATCH_1, SIRFSOC_TIMER_MATCH_2,
51 SIRFSOC_TIMER_MATCH_3, SIRFSOC_TIMER_MATCH_4, SIRFSOC_TIMER_MATCH_5,
52 SIRFSOC_TIMER_INT_EN, SIRFSOC_TIMER_WATCHDOG_EN, SIRFSOC_TIMER_DIV,
53 SIRFSOC_TIMER_LATCHED_LO, SIRFSOC_TIMER_LATCHED_HI,
54};
55
56static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT];
57
Binghua Duan02c981c2011-07-08 17:40:12 +080058static void __iomem *sirfsoc_timer_base;
59static void __init sirfsoc_of_timer_map(void);
60
61/* timer0 interrupt handler */
62static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id)
63{
64 struct clock_event_device *ce = dev_id;
65
66 WARN_ON(!(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_STATUS) & BIT(0)));
67
68 /* clear timer0 interrupt */
69 writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
70
71 ce->event_handler(ce);
72
73 return IRQ_HANDLED;
74}
75
76/* read 64-bit timer counter */
77static cycle_t sirfsoc_timer_read(struct clocksource *cs)
78{
79 u64 cycles;
80
81 /* latch the 64-bit timer counter */
82 writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
83 cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_HI);
84 cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
85
86 return cycles;
87}
88
89static int sirfsoc_timer_set_next_event(unsigned long delta,
90 struct clock_event_device *ce)
91{
92 unsigned long now, next;
93
94 writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
95 now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
96 next = now + delta;
97 writel_relaxed(next, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0);
98 writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
99 now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
100
101 return next - now > delta ? -ETIME : 0;
102}
103
104static void sirfsoc_timer_set_mode(enum clock_event_mode mode,
105 struct clock_event_device *ce)
106{
107 u32 val = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
108 switch (mode) {
109 case CLOCK_EVT_MODE_PERIODIC:
110 WARN_ON(1);
111 break;
112 case CLOCK_EVT_MODE_ONESHOT:
113 writel_relaxed(val | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
114 break;
115 case CLOCK_EVT_MODE_SHUTDOWN:
116 writel_relaxed(val & ~BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
117 break;
118 case CLOCK_EVT_MODE_UNUSED:
119 case CLOCK_EVT_MODE_RESUME:
120 break;
121 }
122}
123
Barry Songe5598a82011-09-21 20:56:33 +0800124static void sirfsoc_clocksource_suspend(struct clocksource *cs)
125{
126 int i;
127
128 writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
129
130 for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++)
131 sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
132}
133
134static void sirfsoc_clocksource_resume(struct clocksource *cs)
135{
136 int i;
137
Barry Songdebeaf62012-07-30 13:29:30 +0800138 for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++)
Barry Songe5598a82011-09-21 20:56:33 +0800139 writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
140
Barry Songdebeaf62012-07-30 13:29:30 +0800141 writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
142 writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
Barry Songe5598a82011-09-21 20:56:33 +0800143}
144
Binghua Duan02c981c2011-07-08 17:40:12 +0800145static struct clock_event_device sirfsoc_clockevent = {
146 .name = "sirfsoc_clockevent",
147 .rating = 200,
148 .features = CLOCK_EVT_FEAT_ONESHOT,
149 .set_mode = sirfsoc_timer_set_mode,
150 .set_next_event = sirfsoc_timer_set_next_event,
151};
152
153static struct clocksource sirfsoc_clocksource = {
154 .name = "sirfsoc_clocksource",
155 .rating = 200,
156 .mask = CLOCKSOURCE_MASK(64),
157 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
158 .read = sirfsoc_timer_read,
Barry Songe5598a82011-09-21 20:56:33 +0800159 .suspend = sirfsoc_clocksource_suspend,
160 .resume = sirfsoc_clocksource_resume,
Binghua Duan02c981c2011-07-08 17:40:12 +0800161};
162
163static struct irqaction sirfsoc_timer_irq = {
164 .name = "sirfsoc_timer0",
165 .flags = IRQF_TIMER,
166 .irq = 0,
167 .handler = sirfsoc_timer_interrupt,
168 .dev_id = &sirfsoc_clockevent,
169};
170
171/* Overwrite weak default sched_clock with more precise one */
Marc Zyngierbc8d8492012-01-16 11:44:12 +0000172static u32 notrace sirfsoc_read_sched_clock(void)
Binghua Duan02c981c2011-07-08 17:40:12 +0800173{
Marc Zyngierbc8d8492012-01-16 11:44:12 +0000174 return (u32)(sirfsoc_timer_read(NULL) & 0xffffffff);
Binghua Duan02c981c2011-07-08 17:40:12 +0800175}
176
177static void __init sirfsoc_clockevent_init(void)
178{
Binghua Duan02c981c2011-07-08 17:40:12 +0800179 sirfsoc_clockevent.cpumask = cpumask_of(0);
Shawn Guo838a2ae2013-01-12 11:50:05 +0000180 clockevents_config_and_register(&sirfsoc_clockevent, CLOCK_TICK_RATE,
181 2, -2);
Binghua Duan02c981c2011-07-08 17:40:12 +0800182}
183
184/* initialize the kernel jiffy timer source */
Barry Song0d5983a2012-12-20 17:33:49 +0800185void __init sirfsoc_prima2_timer_init(void)
Binghua Duan02c981c2011-07-08 17:40:12 +0800186{
187 unsigned long rate;
Binghua Duan198678b2012-08-20 06:42:36 +0000188 struct clk *clk;
189
190 /* initialize clocking early, we want to set the OS timer */
191 sirfsoc_of_clk_init();
Binghua Duan02c981c2011-07-08 17:40:12 +0800192
193 /* timer's input clock is io clock */
Binghua Duan198678b2012-08-20 06:42:36 +0000194 clk = clk_get_sys("io", NULL);
Binghua Duan02c981c2011-07-08 17:40:12 +0800195
196 BUG_ON(IS_ERR(clk));
197
198 rate = clk_get_rate(clk);
199
200 BUG_ON(rate < CLOCK_TICK_RATE);
201 BUG_ON(rate % CLOCK_TICK_RATE);
202
Marc Zyngierbc8d8492012-01-16 11:44:12 +0000203 sirfsoc_of_timer_map();
204
Binghua Duan02c981c2011-07-08 17:40:12 +0800205 writel_relaxed(rate / CLOCK_TICK_RATE / 2 - 1, sirfsoc_timer_base + SIRFSOC_TIMER_DIV);
206 writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
207 writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
208 writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
209
210 BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE));
211
Marc Zyngierbc8d8492012-01-16 11:44:12 +0000212 setup_sched_clock(sirfsoc_read_sched_clock, 32, CLOCK_TICK_RATE);
213
Binghua Duan02c981c2011-07-08 17:40:12 +0800214 BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq));
215
216 sirfsoc_clockevent_init();
217}
218
219static struct of_device_id timer_ids[] = {
220 { .compatible = "sirf,prima2-tick" },
Jamie Iles6a537472011-08-01 21:09:36 +0100221 {},
Binghua Duan02c981c2011-07-08 17:40:12 +0800222};
223
Barry Song0d5983a2012-12-20 17:33:49 +0800224static void __init sirfsoc_of_timer_map(void)
Binghua Duan02c981c2011-07-08 17:40:12 +0800225{
226 struct device_node *np;
Binghua Duan02c981c2011-07-08 17:40:12 +0800227
228 np = of_find_matching_node(NULL, timer_ids);
229 if (!np)
Barry Song0d5983a2012-12-20 17:33:49 +0800230 return;
Binghua Duan02c981c2011-07-08 17:40:12 +0800231 sirfsoc_timer_base = of_iomap(np, 0);
232 if (!sirfsoc_timer_base)
233 panic("unable to map timer cpu registers\n");
234
235 /* Get the interrupts property */
Arnd Bergmann67d71342013-03-19 15:31:08 +0100236 sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0);
Binghua Duan02c981c2011-07-08 17:40:12 +0800237
238 of_node_put(np);
239}