ARM: smp: Remove local timer API
[cascardo/linux.git] / drivers / clocksource / time-armada-370-xp.c
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  */
64 static void __iomem *timer_base, *local_base;
65 static unsigned int timer_clk;
66 static bool timer25Mhz = true;
67
68 /*
69  * Number of timer ticks per jiffy.
70  */
71 static u32 ticks_per_jiffy;
72
73 static struct clock_event_device __percpu *armada_370_xp_evt;
74
75 static 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  */
83 static int
84 armada_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
109 static void
110 armada_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
146 static int armada_370_xp_clkevt_irq;
147
148 static 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  */
164 static 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
191 static 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
197 static 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
216 static struct notifier_block armada_370_xp_timer_cpu_nb __cpuinitdata = {
217         .notifier_call = armada_370_xp_timer_cpu_notify,
218 };
219
220 void __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 }