time-armada-370-xp.c revision 573145f08c2b92c45498468afbbba909f6ce6135
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 void timer_ctrl_clrset(u32 clr, u32 set)
75{
76	writel((readl(timer_base + TIMER_CTRL_OFF) & ~clr) | set,
77		timer_base + TIMER_CTRL_OFF);
78}
79
80static void local_timer_ctrl_clrset(u32 clr, u32 set)
81{
82	writel((readl(local_base + TIMER_CTRL_OFF) & ~clr) | set,
83		local_base + TIMER_CTRL_OFF);
84}
85
86static u32 notrace armada_370_xp_read_sched_clock(void)
87{
88	return ~readl(timer_base + TIMER0_VAL_OFF);
89}
90
91/*
92 * Clockevent handling.
93 */
94static int
95armada_370_xp_clkevt_next_event(unsigned long delta,
96				struct clock_event_device *dev)
97{
98	/*
99	 * Clear clockevent timer interrupt.
100	 */
101	writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS);
102
103	/*
104	 * Setup new clockevent timer value.
105	 */
106	writel(delta, local_base + TIMER0_VAL_OFF);
107
108	/*
109	 * Enable the timer.
110	 */
111	local_timer_ctrl_clrset(TIMER0_RELOAD_EN,
112				TIMER0_EN | TIMER0_DIV(TIMER_DIVIDER_SHIFT));
113	return 0;
114}
115
116static void
117armada_370_xp_clkevt_mode(enum clock_event_mode mode,
118			  struct clock_event_device *dev)
119{
120	if (mode == CLOCK_EVT_MODE_PERIODIC) {
121
122		/*
123		 * Setup timer to fire at 1/HZ intervals.
124		 */
125		writel(ticks_per_jiffy - 1, local_base + TIMER0_RELOAD_OFF);
126		writel(ticks_per_jiffy - 1, local_base + TIMER0_VAL_OFF);
127
128		/*
129		 * Enable timer.
130		 */
131		local_timer_ctrl_clrset(0, TIMER0_RELOAD_EN |
132					   TIMER0_EN |
133					   TIMER0_DIV(TIMER_DIVIDER_SHIFT));
134	} else {
135		/*
136		 * Disable timer.
137		 */
138		local_timer_ctrl_clrset(TIMER0_EN, 0);
139
140		/*
141		 * ACK pending timer interrupt.
142		 */
143		writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS);
144	}
145}
146
147static struct clock_event_device armada_370_xp_clkevt = {
148	.name		= "armada_370_xp_per_cpu_tick",
149	.features	= CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
150	.shift		= 32,
151	.rating		= 300,
152	.set_next_event	= armada_370_xp_clkevt_next_event,
153	.set_mode	= armada_370_xp_clkevt_mode,
154};
155
156static irqreturn_t armada_370_xp_timer_interrupt(int irq, void *dev_id)
157{
158	/*
159	 * ACK timer interrupt and call event handler.
160	 */
161	struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
162
163	writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS);
164	evt->event_handler(evt);
165
166	return IRQ_HANDLED;
167}
168
169/*
170 * Setup the local clock events for a CPU.
171 */
172static int armada_370_xp_timer_setup(struct clock_event_device *evt)
173{
174	u32 clr = 0, set = 0;
175	int cpu = smp_processor_id();
176
177	/* Use existing clock_event for cpu 0 */
178	if (!smp_processor_id())
179		return 0;
180
181	if (timer25Mhz)
182		set = TIMER0_25MHZ;
183	else
184		clr = TIMER0_25MHZ;
185	local_timer_ctrl_clrset(clr, set);
186
187	evt->name		= armada_370_xp_clkevt.name;
188	evt->irq		= armada_370_xp_clkevt.irq;
189	evt->features		= armada_370_xp_clkevt.features;
190	evt->shift		= armada_370_xp_clkevt.shift;
191	evt->rating		= armada_370_xp_clkevt.rating,
192	evt->set_next_event	= armada_370_xp_clkevt_next_event,
193	evt->set_mode		= armada_370_xp_clkevt_mode,
194	evt->cpumask		= cpumask_of(cpu);
195
196	*__this_cpu_ptr(percpu_armada_370_xp_evt) = evt;
197
198	clockevents_config_and_register(evt, timer_clk, 1, 0xfffffffe);
199	enable_percpu_irq(evt->irq, 0);
200
201	return 0;
202}
203
204static void  armada_370_xp_timer_stop(struct clock_event_device *evt)
205{
206	evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
207	disable_percpu_irq(evt->irq);
208}
209
210static struct local_timer_ops armada_370_xp_local_timer_ops = {
211	.setup	= armada_370_xp_timer_setup,
212	.stop	=  armada_370_xp_timer_stop,
213};
214
215static void __init armada_370_xp_timer_init(struct device_node *np)
216{
217	u32 clr = 0, set = 0;
218	int res;
219
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		set = TIMER0_25MHZ;
227		timer_clk = 25000000;
228	} else {
229		unsigned long rate = 0;
230		struct clk *clk = of_clk_get(np, 0);
231		WARN_ON(IS_ERR(clk));
232		rate =  clk_get_rate(clk);
233		timer_clk = rate / TIMER_DIVIDER;
234
235		clr = TIMER0_25MHZ;
236		timer25Mhz = false;
237	}
238	timer_ctrl_clrset(clr, set);
239	local_timer_ctrl_clrset(clr, set);
240
241	/*
242	 * We use timer 0 as clocksource, and private(local) timer 0
243	 * for clockevents
244	 */
245	armada_370_xp_clkevt.irq = irq_of_parse_and_map(np, 4);
246
247	ticks_per_jiffy = (timer_clk + HZ / 2) / HZ;
248
249	/*
250	 * Set scale and timer for sched_clock.
251	 */
252	setup_sched_clock(armada_370_xp_read_sched_clock, 32, timer_clk);
253
254	/*
255	 * Setup free-running clocksource timer (interrupts
256	 * disabled).
257	 */
258	writel(0xffffffff, timer_base + TIMER0_VAL_OFF);
259	writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF);
260
261	timer_ctrl_clrset(0, TIMER0_EN | TIMER0_RELOAD_EN |
262			     TIMER0_DIV(TIMER_DIVIDER_SHIFT));
263
264	clocksource_mmio_init(timer_base + TIMER0_VAL_OFF,
265			      "armada_370_xp_clocksource",
266			      timer_clk, 300, 32, clocksource_mmio_readl_down);
267
268	/* Register the clockevent on the private timer of CPU 0 */
269	armada_370_xp_clkevt.cpumask = cpumask_of(0);
270	clockevents_config_and_register(&armada_370_xp_clkevt,
271					timer_clk, 1, 0xfffffffe);
272
273	percpu_armada_370_xp_evt = alloc_percpu(struct clock_event_device *);
274
275
276	/*
277	 * Setup clockevent timer (interrupt-driven).
278	 */
279	*__this_cpu_ptr(percpu_armada_370_xp_evt) = &armada_370_xp_clkevt;
280	res = request_percpu_irq(armada_370_xp_clkevt.irq,
281				armada_370_xp_timer_interrupt,
282				armada_370_xp_clkevt.name,
283				percpu_armada_370_xp_evt);
284	if (!res) {
285		enable_percpu_irq(armada_370_xp_clkevt.irq, 0);
286#ifdef CONFIG_LOCAL_TIMERS
287		local_timer_register(&armada_370_xp_local_timer_ops);
288#endif
289	}
290}
291CLOCKSOURCE_OF_DECLARE(armada_370_xp, "marvell,armada-370-xp-timer",
292		       armada_370_xp_timer_init);
293