aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-05-02 12:38:16 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-05-02 12:38:16 -0400
commit99c6bcf46d2233d33e441834e958ed0bc22b190a (patch)
tree25abf5e856bc0f08d75e623715eb5acc4d4de2b2 /drivers/clocksource
parent97b1007a2924aaa9126398623f6755a8c3c6a616 (diff)
parent2fdfe1c26fb9f24cfdf124384abb35396ca2cd3f (diff)
Merge tag 'multiplatform-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC multiplatform updates from Olof Johansson: "More multiplatform enablement for ARM platforms. The ones converted in this branch are: - bcm2835 - cns3xxx - sirf - nomadik - msx - spear - tegra - ux500 We're getting close to having most of them converted! One of the larger platforms remaining is Samsung Exynos, and there are a bunch of supporting patches in this merge window for it. There was a patch in this branch to a early version of multiplatform conversion, but it ended up being reverted due to need of more bake time. The revert commit is part of the branch since it would have required rebasing multiple dependent branches and they were stable by then" * tag 'multiplatform-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (70 commits) mmc: sdhci-s3c: Fix operation on non-single image Samsung platforms clocksource: nomadik-mtu: fix up clocksource/timer Revert "ARM: exynos: enable multiplatform support" ARM: SPEAr13xx: Fix typo "ARCH_HAVE_CPUFREQ" ARM: exynos: enable multiplatform support rtc: s3c: make header file local mtd: onenand/samsung: make regs-onenand.h file local thermal/exynos: remove unnecessary header inclusions mmc: sdhci-s3c: remove platform dependencies ARM: samsung: move mfc device definition to s5p-dev-mfc.c ARM: exynos: move debug-macro.S to include/debug/ ARM: exynos: prepare for sparse IRQ ARM: exynos: introduce EXYNOS_ATAGS symbol ARM: tegra: build assembly files with -march=armv7-a ARM: Push selects for TWD/SCU into machine entries ARM: ux500: build hotplug.o for ARMv7-a ARM: ux500: move to multiplatform ARM: ux500: make remaining headers local ARM: ux500: make irqs.h local to platform ARM: ux500: get rid of <mach/[hardware|db8500-regs].h> ...
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/Makefile2
-rw-r--r--drivers/clocksource/clksrc-dbx500-prcmu.c3
-rw-r--r--drivers/clocksource/nomadik-mtu.c4
-rw-r--r--drivers/clocksource/timer-marco.c299
-rw-r--r--drivers/clocksource/timer-prima2.c215
5 files changed, 518 insertions, 5 deletions
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 4897f243a000..682d48d08164 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -16,7 +16,9 @@ obj-$(CONFIG_CLKSRC_NOMADIK_MTU) += nomadik-mtu.o
16obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o 16obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o
17obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o 17obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o
18obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o 18obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o
19obj-$(CONFIG_ARCH_MARCO) += timer-marco.o
19obj-$(CONFIG_ARCH_MXS) += mxs_timer.o 20obj-$(CONFIG_ARCH_MXS) += mxs_timer.o
21obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o
20obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o 22obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o
21obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o 23obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o
22obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o 24obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o
diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c
index c26c369eb9e6..54f3d119d99c 100644
--- a/drivers/clocksource/clksrc-dbx500-prcmu.c
+++ b/drivers/clocksource/clksrc-dbx500-prcmu.c
@@ -17,9 +17,6 @@
17 17
18#include <asm/sched_clock.h> 18#include <asm/sched_clock.h>
19 19
20#include <mach/setup.h>
21#include <mach/hardware.h>
22
23#define RATE_32K 32768 20#define RATE_32K 32768
24 21
25#define TIMER_MODE_CONTINOUS 0x1 22#define TIMER_MODE_CONTINOUS 0x1
diff --git a/drivers/clocksource/nomadik-mtu.c b/drivers/clocksource/nomadik-mtu.c
index 071f6eadfea2..e405531e1cc5 100644
--- a/drivers/clocksource/nomadik-mtu.c
+++ b/drivers/clocksource/nomadik-mtu.c
@@ -67,7 +67,7 @@ static u32 clk_prescale;
67static u32 nmdk_cycle; /* write-once */ 67static u32 nmdk_cycle; /* write-once */
68static struct delay_timer mtu_delay_timer; 68static struct delay_timer mtu_delay_timer;
69 69
70#ifdef CONFIG_NOMADIK_MTU_SCHED_CLOCK 70#ifdef CONFIG_CLKSRC_NOMADIK_MTU_SCHED_CLOCK
71/* 71/*
72 * Override the global weak sched_clock symbol with this 72 * Override the global weak sched_clock symbol with this
73 * local implementation which uses the clocksource to get some 73 * local implementation which uses the clocksource to get some
@@ -233,7 +233,7 @@ void __init nmdk_timer_init(void __iomem *base, int irq)
233 pr_err("timer: failed to initialize clock source %s\n", 233 pr_err("timer: failed to initialize clock source %s\n",
234 "mtu_0"); 234 "mtu_0");
235 235
236#ifdef CONFIG_NOMADIK_MTU_SCHED_CLOCK 236#ifdef CONFIG_CLKSRC_NOMADIK_MTU_SCHED_CLOCK
237 setup_sched_clock(nomadik_read_sched_clock, 32, rate); 237 setup_sched_clock(nomadik_read_sched_clock, 32, rate);
238#endif 238#endif
239 239
diff --git a/drivers/clocksource/timer-marco.c b/drivers/clocksource/timer-marco.c
new file mode 100644
index 000000000000..97738dbf3e3b
--- /dev/null
+++ b/drivers/clocksource/timer-marco.c
@@ -0,0 +1,299 @@
1/*
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/slab.h>
17#include <linux/of.h>
18#include <linux/of_irq.h>
19#include <linux/of_address.h>
20#include <asm/sched_clock.h>
21#include <asm/localtimer.h>
22#include <asm/mach/time.h>
23
24#define SIRFSOC_TIMER_32COUNTER_0_CTRL 0x0000
25#define SIRFSOC_TIMER_32COUNTER_1_CTRL 0x0004
26#define SIRFSOC_TIMER_MATCH_0 0x0018
27#define SIRFSOC_TIMER_MATCH_1 0x001c
28#define SIRFSOC_TIMER_COUNTER_0 0x0048
29#define SIRFSOC_TIMER_COUNTER_1 0x004c
30#define SIRFSOC_TIMER_INTR_STATUS 0x0060
31#define SIRFSOC_TIMER_WATCHDOG_EN 0x0064
32#define SIRFSOC_TIMER_64COUNTER_CTRL 0x0068
33#define SIRFSOC_TIMER_64COUNTER_LO 0x006c
34#define SIRFSOC_TIMER_64COUNTER_HI 0x0070
35#define SIRFSOC_TIMER_64COUNTER_LOAD_LO 0x0074
36#define SIRFSOC_TIMER_64COUNTER_LOAD_HI 0x0078
37#define SIRFSOC_TIMER_64COUNTER_RLATCHED_LO 0x007c
38#define SIRFSOC_TIMER_64COUNTER_RLATCHED_HI 0x0080
39
40#define SIRFSOC_TIMER_REG_CNT 6
41
42static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = {
43 SIRFSOC_TIMER_WATCHDOG_EN,
44 SIRFSOC_TIMER_32COUNTER_0_CTRL,
45 SIRFSOC_TIMER_32COUNTER_1_CTRL,
46 SIRFSOC_TIMER_64COUNTER_CTRL,
47 SIRFSOC_TIMER_64COUNTER_RLATCHED_LO,
48 SIRFSOC_TIMER_64COUNTER_RLATCHED_HI,
49};
50
51static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT];
52
53static void __iomem *sirfsoc_timer_base;
54
55/* disable count and interrupt */
56static inline void sirfsoc_timer_count_disable(int idx)
57{
58 writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) & ~0x7,
59 sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx);
60}
61
62/* enable count and interrupt */
63static inline void sirfsoc_timer_count_enable(int idx)
64{
65 writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) | 0x7,
66 sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx);
67}
68
69/* timer interrupt handler */
70static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id)
71{
72 struct clock_event_device *ce = dev_id;
73 int cpu = smp_processor_id();
74
75 /* clear timer interrupt */
76 writel_relaxed(BIT(cpu), sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
77
78 if (ce->mode == CLOCK_EVT_MODE_ONESHOT)
79 sirfsoc_timer_count_disable(cpu);
80
81 ce->event_handler(ce);
82
83 return IRQ_HANDLED;
84}
85
86/* read 64-bit timer counter */
87static cycle_t sirfsoc_timer_read(struct clocksource *cs)
88{
89 u64 cycles;
90
91 writel_relaxed((readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
92 BIT(0)) & ~BIT(1), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
93
94 cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_HI);
95 cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_LO);
96
97 return cycles;
98}
99
100static int sirfsoc_timer_set_next_event(unsigned long delta,
101 struct clock_event_device *ce)
102{
103 int cpu = smp_processor_id();
104
105 writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0 +
106 4 * cpu);
107 writel_relaxed(delta, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0 +
108 4 * cpu);
109
110 /* enable the tick */
111 sirfsoc_timer_count_enable(cpu);
112
113 return 0;
114}
115
116static void sirfsoc_timer_set_mode(enum clock_event_mode mode,
117 struct clock_event_device *ce)
118{
119 switch (mode) {
120 case CLOCK_EVT_MODE_ONESHOT:
121 /* enable in set_next_event */
122 break;
123 default:
124 break;
125 }
126
127 sirfsoc_timer_count_disable(smp_processor_id());
128}
129
130static void sirfsoc_clocksource_suspend(struct clocksource *cs)
131{
132 int i;
133
134 for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++)
135 sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
136}
137
138static void sirfsoc_clocksource_resume(struct clocksource *cs)
139{
140 int i;
141
142 for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++)
143 writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
144
145 writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2],
146 sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO);
147 writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1],
148 sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI);
149
150 writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
151 BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
152}
153
154static struct clock_event_device sirfsoc_clockevent = {
155 .name = "sirfsoc_clockevent",
156 .rating = 200,
157 .features = CLOCK_EVT_FEAT_ONESHOT,
158 .set_mode = sirfsoc_timer_set_mode,
159 .set_next_event = sirfsoc_timer_set_next_event,
160};
161
162static struct clocksource sirfsoc_clocksource = {
163 .name = "sirfsoc_clocksource",
164 .rating = 200,
165 .mask = CLOCKSOURCE_MASK(64),
166 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
167 .read = sirfsoc_timer_read,
168 .suspend = sirfsoc_clocksource_suspend,
169 .resume = sirfsoc_clocksource_resume,
170};
171
172static struct irqaction sirfsoc_timer_irq = {
173 .name = "sirfsoc_timer0",
174 .flags = IRQF_TIMER | IRQF_NOBALANCING,
175 .handler = sirfsoc_timer_interrupt,
176 .dev_id = &sirfsoc_clockevent,
177};
178
179#ifdef CONFIG_LOCAL_TIMERS
180
181static struct irqaction sirfsoc_timer1_irq = {
182 .name = "sirfsoc_timer1",
183 .flags = IRQF_TIMER | IRQF_NOBALANCING,
184 .handler = sirfsoc_timer_interrupt,
185};
186
187static int __cpuinit sirfsoc_local_timer_setup(struct clock_event_device *ce)
188{
189 /* Use existing clock_event for cpu 0 */
190 if (!smp_processor_id())
191 return 0;
192
193 ce->irq = sirfsoc_timer1_irq.irq;
194 ce->name = "local_timer";
195 ce->features = sirfsoc_clockevent.features;
196 ce->rating = sirfsoc_clockevent.rating;
197 ce->set_mode = sirfsoc_timer_set_mode;
198 ce->set_next_event = sirfsoc_timer_set_next_event;
199 ce->shift = sirfsoc_clockevent.shift;
200 ce->mult = sirfsoc_clockevent.mult;
201 ce->max_delta_ns = sirfsoc_clockevent.max_delta_ns;
202 ce->min_delta_ns = sirfsoc_clockevent.min_delta_ns;
203
204 sirfsoc_timer1_irq.dev_id = ce;
205 BUG_ON(setup_irq(ce->irq, &sirfsoc_timer1_irq));
206 irq_set_affinity(sirfsoc_timer1_irq.irq, cpumask_of(1));
207
208 clockevents_register_device(ce);
209 return 0;
210}
211
212static void sirfsoc_local_timer_stop(struct clock_event_device *ce)
213{
214 sirfsoc_timer_count_disable(1);
215
216 remove_irq(sirfsoc_timer1_irq.irq, &sirfsoc_timer1_irq);
217}
218
219static struct local_timer_ops sirfsoc_local_timer_ops __cpuinitdata = {
220 .setup = sirfsoc_local_timer_setup,
221 .stop = sirfsoc_local_timer_stop,
222};
223#endif /* CONFIG_LOCAL_TIMERS */
224
225static void __init sirfsoc_clockevent_init(void)
226{
227 clockevents_calc_mult_shift(&sirfsoc_clockevent, CLOCK_TICK_RATE, 60);
228
229 sirfsoc_clockevent.max_delta_ns =
230 clockevent_delta2ns(-2, &sirfsoc_clockevent);
231 sirfsoc_clockevent.min_delta_ns =
232 clockevent_delta2ns(2, &sirfsoc_clockevent);
233
234 sirfsoc_clockevent.cpumask = cpumask_of(0);
235 clockevents_register_device(&sirfsoc_clockevent);
236#ifdef CONFIG_LOCAL_TIMERS
237 local_timer_register(&sirfsoc_local_timer_ops);
238#endif
239}
240
241/* initialize the kernel jiffy timer source */
242static void __init sirfsoc_marco_timer_init(void)
243{
244 unsigned long rate;
245 u32 timer_div;
246 struct clk *clk;
247
248 /* timer's input clock is io clock */
249 clk = clk_get_sys("io", NULL);
250
251 BUG_ON(IS_ERR(clk));
252 rate = clk_get_rate(clk);
253
254 BUG_ON(rate < CLOCK_TICK_RATE);
255 BUG_ON(rate % CLOCK_TICK_RATE);
256
257 /* Initialize the timer dividers */
258 timer_div = rate / CLOCK_TICK_RATE - 1;
259 writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
260 writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL);
261 writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_1_CTRL);
262
263 /* Initialize timer counters to 0 */
264 writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO);
265 writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI);
266 writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) |
267 BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL);
268 writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0);
269 writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_1);
270
271 /* Clear all interrupts */
272 writel_relaxed(0xFFFF, sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS);
273
274 BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE));
275
276 BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq));
277
278 sirfsoc_clockevent_init();
279}
280
281static void __init sirfsoc_of_timer_init(struct device_node *np)
282{
283 sirfsoc_timer_base = of_iomap(np, 0);
284 if (!sirfsoc_timer_base)
285 panic("unable to map timer cpu registers\n");
286
287 sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0);
288 if (!sirfsoc_timer_irq.irq)
289 panic("No irq passed for timer0 via DT\n");
290
291#ifdef CONFIG_LOCAL_TIMERS
292 sirfsoc_timer1_irq.irq = irq_of_parse_and_map(np, 1);
293 if (!sirfsoc_timer1_irq.irq)
294 panic("No irq passed for timer1 via DT\n");
295#endif
296
297 sirfsoc_marco_timer_init();
298}
299CLOCKSOURCE_OF_DECLARE(sirfsoc_marco_timer, "sirf,marco-tick", sirfsoc_of_timer_init );
diff --git a/drivers/clocksource/timer-prima2.c b/drivers/clocksource/timer-prima2.c
new file mode 100644
index 000000000000..760882665d7a
--- /dev/null
+++ b/drivers/clocksource/timer-prima2.c
@@ -0,0 +1,215 @@
1/*
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>
19#include <linux/of_irq.h>
20#include <linux/of_address.h>
21#include <asm/sched_clock.h>
22#include <asm/mach/time.h>
23
24#define SIRFSOC_TIMER_COUNTER_LO 0x0000
25#define SIRFSOC_TIMER_COUNTER_HI 0x0004
26#define SIRFSOC_TIMER_MATCH_0 0x0008
27#define SIRFSOC_TIMER_MATCH_1 0x000C
28#define SIRFSOC_TIMER_MATCH_2 0x0010
29#define SIRFSOC_TIMER_MATCH_3 0x0014
30#define SIRFSOC_TIMER_MATCH_4 0x0018
31#define SIRFSOC_TIMER_MATCH_5 0x001C
32#define SIRFSOC_TIMER_STATUS 0x0020
33#define SIRFSOC_TIMER_INT_EN 0x0024
34#define SIRFSOC_TIMER_WATCHDOG_EN 0x0028
35#define SIRFSOC_TIMER_DIV 0x002C
36#define SIRFSOC_TIMER_LATCH 0x0030
37#define SIRFSOC_TIMER_LATCHED_LO 0x0034
38#define SIRFSOC_TIMER_LATCHED_HI 0x0038
39
40#define SIRFSOC_TIMER_WDT_INDEX 5
41
42#define SIRFSOC_TIMER_LATCH_BIT BIT(0)
43
44#define SIRFSOC_TIMER_REG_CNT 11
45
46static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = {
47 SIRFSOC_TIMER_MATCH_0, SIRFSOC_TIMER_MATCH_1, SIRFSOC_TIMER_MATCH_2,
48 SIRFSOC_TIMER_MATCH_3, SIRFSOC_TIMER_MATCH_4, SIRFSOC_TIMER_MATCH_5,
49 SIRFSOC_TIMER_INT_EN, SIRFSOC_TIMER_WATCHDOG_EN, SIRFSOC_TIMER_DIV,
50 SIRFSOC_TIMER_LATCHED_LO, SIRFSOC_TIMER_LATCHED_HI,
51};
52
53static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT];
54
55static void __iomem *sirfsoc_timer_base;
56
57/* timer0 interrupt handler */
58static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id)
59{
60 struct clock_event_device *ce = dev_id;
61
62 WARN_ON(!(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_STATUS) & BIT(0)));
63
64 /* clear timer0 interrupt */
65 writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
66
67 ce->event_handler(ce);
68
69 return IRQ_HANDLED;
70}
71
72/* read 64-bit timer counter */
73static cycle_t sirfsoc_timer_read(struct clocksource *cs)
74{
75 u64 cycles;
76
77 /* latch the 64-bit timer counter */
78 writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
79 cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_HI);
80 cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
81
82 return cycles;
83}
84
85static int sirfsoc_timer_set_next_event(unsigned long delta,
86 struct clock_event_device *ce)
87{
88 unsigned long now, next;
89
90 writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
91 now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
92 next = now + delta;
93 writel_relaxed(next, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0);
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
97 return next - now > delta ? -ETIME : 0;
98}
99
100static void sirfsoc_timer_set_mode(enum clock_event_mode mode,
101 struct clock_event_device *ce)
102{
103 u32 val = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
104 switch (mode) {
105 case CLOCK_EVT_MODE_PERIODIC:
106 WARN_ON(1);
107 break;
108 case CLOCK_EVT_MODE_ONESHOT:
109 writel_relaxed(val | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
110 break;
111 case CLOCK_EVT_MODE_SHUTDOWN:
112 writel_relaxed(val & ~BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
113 break;
114 case CLOCK_EVT_MODE_UNUSED:
115 case CLOCK_EVT_MODE_RESUME:
116 break;
117 }
118}
119
120static void sirfsoc_clocksource_suspend(struct clocksource *cs)
121{
122 int i;
123
124 writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
125
126 for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++)
127 sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
128}
129
130static void sirfsoc_clocksource_resume(struct clocksource *cs)
131{
132 int i;
133
134 for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++)
135 writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
136
137 writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
138 writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
139}
140
141static struct clock_event_device sirfsoc_clockevent = {
142 .name = "sirfsoc_clockevent",
143 .rating = 200,
144 .features = CLOCK_EVT_FEAT_ONESHOT,
145 .set_mode = sirfsoc_timer_set_mode,
146 .set_next_event = sirfsoc_timer_set_next_event,
147};
148
149static struct clocksource sirfsoc_clocksource = {
150 .name = "sirfsoc_clocksource",
151 .rating = 200,
152 .mask = CLOCKSOURCE_MASK(64),
153 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
154 .read = sirfsoc_timer_read,
155 .suspend = sirfsoc_clocksource_suspend,
156 .resume = sirfsoc_clocksource_resume,
157};
158
159static struct irqaction sirfsoc_timer_irq = {
160 .name = "sirfsoc_timer0",
161 .flags = IRQF_TIMER,
162 .irq = 0,
163 .handler = sirfsoc_timer_interrupt,
164 .dev_id = &sirfsoc_clockevent,
165};
166
167/* Overwrite weak default sched_clock with more precise one */
168static u32 notrace sirfsoc_read_sched_clock(void)
169{
170 return (u32)(sirfsoc_timer_read(NULL) & 0xffffffff);
171}
172
173static void __init sirfsoc_clockevent_init(void)
174{
175 sirfsoc_clockevent.cpumask = cpumask_of(0);
176 clockevents_config_and_register(&sirfsoc_clockevent, CLOCK_TICK_RATE,
177 2, -2);
178}
179
180/* initialize the kernel jiffy timer source */
181static void __init sirfsoc_prima2_timer_init(struct device_node *np)
182{
183 unsigned long rate;
184 struct clk *clk;
185
186 /* timer's input clock is io clock */
187 clk = clk_get_sys("io", NULL);
188
189 BUG_ON(IS_ERR(clk));
190
191 rate = clk_get_rate(clk);
192
193 BUG_ON(rate < CLOCK_TICK_RATE);
194 BUG_ON(rate % CLOCK_TICK_RATE);
195
196 sirfsoc_timer_base = of_iomap(np, 0);
197 if (!sirfsoc_timer_base)
198 panic("unable to map timer cpu registers\n");
199
200 sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0);
201
202 writel_relaxed(rate / CLOCK_TICK_RATE / 2 - 1, sirfsoc_timer_base + SIRFSOC_TIMER_DIV);
203 writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
204 writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
205 writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
206
207 BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE));
208
209 setup_sched_clock(sirfsoc_read_sched_clock, 32, CLOCK_TICK_RATE);
210
211 BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq));
212
213 sirfsoc_clockevent_init();
214}
215CLOCKSOURCE_OF_DECLARE(sirfsoc_prima2_timer, "sirf,prima2-tick", sirfsoc_prima2_timer_init);