source: src/linux/universal/linux-4.9/arch/arm/kernel/smp_twd.c @ 31574

Last change on this file since 31574 was 31574, checked in by brainslayer, 4 months ago

kernel 4.9 update

File size: 9.5 KB
Line 
1/*
2 *  linux/arch/arm/kernel/smp_twd.c
3 *
4 *  Copyright (C) 2002 ARM Ltd.
5 *  All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11#include <linux/init.h>
12#include <linux/kernel.h>
13#include <linux/clk.h>
14#include <linux/cpu.h>
15#include <linux/delay.h>
16#include <linux/device.h>
17#include <linux/err.h>
18#include <linux/smp.h>
19#include <linux/jiffies.h>
20#include <linux/clockchips.h>
21#include <linux/interrupt.h>
22#include <linux/io.h>
23#include <linux/of_irq.h>
24#include <linux/of_address.h>
25#include <linux/module.h>
26#include <linux/moduleparam.h>
27
28#include <asm/smp_twd.h>
29
30/* set up by the platform code */
31static void __iomem *twd_base;
32
33static struct clk *twd_clk;
34static unsigned long twd_timer_rate;
35static DEFINE_PER_CPU(bool, percpu_setup_called);
36
37static struct clock_event_device __percpu *twd_evt;
38static unsigned int twd_features =
39                CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
40static int twd_ppi;
41
42static int twd_shutdown(struct clock_event_device *clk)
43{
44        writel_relaxed(0, twd_base + TWD_TIMER_CONTROL);
45        return 0;
46}
47
48static int twd_set_oneshot(struct clock_event_device *clk)
49{
50        /* period set, and timer enabled in 'next_event' hook */
51        writel_relaxed(TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT,
52                       twd_base + TWD_TIMER_CONTROL);
53        return 0;
54}
55
56static int twd_set_periodic(struct clock_event_device *clk)
57{
58        unsigned long ctrl = TWD_TIMER_CONTROL_ENABLE |
59                             TWD_TIMER_CONTROL_IT_ENABLE |
60                             TWD_TIMER_CONTROL_PERIODIC;
61
62        writel_relaxed(DIV_ROUND_CLOSEST(twd_timer_rate, HZ),
63                       twd_base + TWD_TIMER_LOAD);
64        writel_relaxed(ctrl, twd_base + TWD_TIMER_CONTROL);
65        return 0;
66}
67
68static int twd_set_next_event(unsigned long evt,
69                        struct clock_event_device *unused)
70{
71        unsigned long ctrl = readl_relaxed(twd_base + TWD_TIMER_CONTROL);
72
73        ctrl |= TWD_TIMER_CONTROL_ENABLE;
74
75        writel_relaxed(evt, twd_base + TWD_TIMER_COUNTER);
76        writel_relaxed(ctrl, twd_base + TWD_TIMER_CONTROL);
77
78        return 0;
79}
80
81/*
82 * local_timer_ack: checks for a local timer interrupt.
83 *
84 * If a local timer interrupt has occurred, acknowledge and return 1.
85 * Otherwise, return 0.
86 */
87static int twd_timer_ack(void)
88{
89        if (readl_relaxed(twd_base + TWD_TIMER_INTSTAT)) {
90                writel_relaxed(1, twd_base + TWD_TIMER_INTSTAT);
91                return 1;
92        }
93
94        return 0;
95}
96
97static void twd_timer_stop(void)
98{
99        struct clock_event_device *clk = raw_cpu_ptr(twd_evt);
100
101        twd_shutdown(clk);
102        disable_percpu_irq(clk->irq);
103}
104
105#ifdef CONFIG_COMMON_CLK
106
107/*
108 * Updates clockevent frequency when the cpu frequency changes.
109 * Called on the cpu that is changing frequency with interrupts disabled.
110 */
111static void twd_update_frequency(void *new_rate)
112{
113        twd_timer_rate = *((unsigned long *) new_rate);
114
115        clockevents_update_freq(raw_cpu_ptr(twd_evt), twd_timer_rate);
116}
117
118static int twd_rate_change(struct notifier_block *nb,
119        unsigned long flags, void *data)
120{
121        struct clk_notifier_data *cnd = data;
122
123        /*
124         * The twd clock events must be reprogrammed to account for the new
125         * frequency.  The timer is local to a cpu, so cross-call to the
126         * changing cpu.
127         */
128        if (flags == POST_RATE_CHANGE)
129                on_each_cpu(twd_update_frequency,
130                                  (void *)&cnd->new_rate, 1);
131
132        return NOTIFY_OK;
133}
134
135static struct notifier_block twd_clk_nb = {
136        .notifier_call = twd_rate_change,
137};
138
139static int twd_clk_init(void)
140{
141        if (twd_evt && raw_cpu_ptr(twd_evt) && !IS_ERR(twd_clk))
142                return clk_notifier_register(twd_clk, &twd_clk_nb);
143
144        return 0;
145}
146core_initcall(twd_clk_init);
147
148#elif defined (CONFIG_CPU_FREQ)
149
150#include <linux/cpufreq.h>
151
152/*
153 * Updates clockevent frequency when the cpu frequency changes.
154 * Called on the cpu that is changing frequency with interrupts disabled.
155 */
156static void twd_update_frequency(void *data)
157{
158        twd_timer_rate = clk_get_rate(twd_clk);
159
160        clockevents_update_freq(raw_cpu_ptr(twd_evt), twd_timer_rate);
161}
162
163static int twd_cpufreq_transition(struct notifier_block *nb,
164        unsigned long state, void *data)
165{
166        struct cpufreq_freqs *freqs = data;
167
168        /*
169         * The twd clock events must be reprogrammed to account for the new
170         * frequency.  The timer is local to a cpu, so cross-call to the
171         * changing cpu.
172         */
173        if (state == CPUFREQ_POSTCHANGE)
174                smp_call_function_single(freqs->cpu, twd_update_frequency,
175                        NULL, 1);
176
177        return NOTIFY_OK;
178}
179
180static struct notifier_block twd_cpufreq_nb = {
181        .notifier_call = twd_cpufreq_transition,
182};
183
184static int twd_cpufreq_init(void)
185{
186        if (twd_evt && raw_cpu_ptr(twd_evt) && !IS_ERR(twd_clk))
187                return cpufreq_register_notifier(&twd_cpufreq_nb,
188                        CPUFREQ_TRANSITION_NOTIFIER);
189
190        return 0;
191}
192core_initcall(twd_cpufreq_init);
193
194#endif
195
196static void twd_calibrate_rate(void)
197{
198        unsigned long count;
199        u64 waitjiffies;
200
201        /*
202         * If this is the first time round, we need to work out how fast
203         * the timer ticks
204         */
205        if (twd_timer_rate == 0) {
206                pr_info("Calibrating local timer... ");
207
208                /* Wait for a tick to start */
209                waitjiffies = get_jiffies_64() + 1;
210
211                while (get_jiffies_64() < waitjiffies)
212                        udelay(10);
213
214                /* OK, now the tick has started, let's get the timer going */
215                waitjiffies += 5;
216
217                                 /* enable, no interrupt or reload */
218                writel_relaxed(0x1, twd_base + TWD_TIMER_CONTROL);
219
220                                 /* maximum value */
221                writel_relaxed(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
222
223                while (get_jiffies_64() < waitjiffies)
224                        udelay(10);
225
226                count = readl_relaxed(twd_base + TWD_TIMER_COUNTER);
227
228                twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
229
230                pr_cont("%lu.%02luMHz.\n", twd_timer_rate / 1000000,
231                        (twd_timer_rate / 10000) % 100);
232        }
233}
234
235static irqreturn_t twd_handler(int irq, void *dev_id)
236{
237        struct clock_event_device *evt = dev_id;
238
239        if (twd_timer_ack()) {
240                evt->event_handler(evt);
241                return IRQ_HANDLED;
242        }
243
244        return IRQ_NONE;
245}
246#ifndef CONFIG_ARCH_CNS3XXX
247
248static void twd_get_clock(struct device_node *np)
249{
250        int err;
251
252        if (np)
253                twd_clk = of_clk_get(np, 0);
254        else
255                twd_clk = clk_get_sys("smp_twd", NULL);
256
257        if (IS_ERR(twd_clk)) {
258                pr_err("smp_twd: clock not found %d\n", (int) PTR_ERR(twd_clk));
259                return;
260        }
261
262        err = clk_prepare_enable(twd_clk);
263        if (err) {
264                pr_err("smp_twd: clock failed to prepare+enable: %d\n", err);
265                clk_put(twd_clk);
266                return;
267        }
268
269        twd_timer_rate = clk_get_rate(twd_clk);
270}
271#endif
272/*
273 * Setup the local clock events for a CPU.
274 */
275static void twd_timer_setup(void)
276{
277        struct clock_event_device *clk = raw_cpu_ptr(twd_evt);
278
279        int cpu = smp_processor_id();
280
281        /*
282         * If the basic setup for this CPU has been done before don't
283         * bother with the below.
284         */
285        if (per_cpu(percpu_setup_called, cpu)) {
286                writel_relaxed(0, twd_base + TWD_TIMER_CONTROL);
287                clockevents_register_device(clk);
288                enable_percpu_irq(clk->irq, 0);
289                return;
290        }
291        per_cpu(percpu_setup_called, cpu) = true;
292
293
294        twd_calibrate_rate();
295
296        /*
297         * The following is done once per CPU the first time .setup() is
298         * called.
299         */
300
301        writel_relaxed(0, twd_base + TWD_TIMER_CONTROL);
302
303        clk->name = "local_timer";
304        clk->features = twd_features;
305        clk->rating = 350;
306        clk->set_state_shutdown = twd_shutdown;
307        clk->set_state_periodic = twd_set_periodic;
308        clk->set_state_oneshot = twd_set_oneshot;
309        clk->tick_resume = twd_shutdown;
310        clk->set_next_event = twd_set_next_event;
311        clk->irq = twd_ppi;
312        clk->cpumask = cpumask_of(cpu);
313
314        clockevents_config_and_register(clk, twd_timer_rate,
315                                        0xf, 0xffffffff);
316        enable_percpu_irq(clk->irq, 0);
317}
318
319static int twd_timer_starting_cpu(unsigned int cpu)
320{
321        twd_timer_setup();
322        return 0;
323}
324
325static int twd_timer_dying_cpu(unsigned int cpu)
326{
327        twd_timer_stop();
328        return 0;
329}
330
331static int __init twd_local_timer_common_register(struct device_node *np)
332{
333        int err;
334
335        twd_evt = alloc_percpu(struct clock_event_device);
336        if (!twd_evt) {
337                err = -ENOMEM;
338                goto out_free;
339        }
340
341        err = request_percpu_irq(twd_ppi, twd_handler, "twd", twd_evt);
342        if (err) {
343                pr_err("twd: can't register interrupt %d (%d)\n", twd_ppi, err);
344                goto out_free;
345        }
346
347        cpuhp_setup_state_nocalls(CPUHP_AP_ARM_TWD_STARTING,
348                                  "AP_ARM_TWD_STARTING",
349                                  twd_timer_starting_cpu, twd_timer_dying_cpu);
350
351#ifndef CONFIG_ARCH_CNS3XXX
352        twd_get_clock(np);
353#endif
354        if (!of_property_read_bool(np, "always-on"))
355                twd_features |= CLOCK_EVT_FEAT_C3STOP;
356
357        /*
358         * Immediately configure the timer on the boot CPU, unless we need
359         * jiffies to be incrementing to calibrate the rate in which case
360         * setup the timer in late_time_init.
361         */
362        if (twd_timer_rate)
363                twd_timer_setup();
364        else
365                late_time_init = twd_timer_setup;
366
367        return 0;
368
369out_free:
370        iounmap(twd_base);
371        twd_base = NULL;
372        free_percpu(twd_evt);
373
374        return err;
375}
376
377int __init twd_local_timer_register(struct twd_local_timer *tlt)
378{
379        if (twd_base || twd_evt)
380                return -EBUSY;
381
382        twd_ppi = tlt->res[1].start;
383
384        twd_base = ioremap(tlt->res[0].start, resource_size(&tlt->res[0]));
385        if (!twd_base)
386                return -ENOMEM;
387
388        return twd_local_timer_common_register(NULL);
389}
390
391#ifdef CONFIG_OF
392static int __init twd_local_timer_of_register(struct device_node *np)
393{
394        int err;
395
396        twd_ppi = irq_of_parse_and_map(np, 0);
397        if (!twd_ppi) {
398                err = -EINVAL;
399                goto out;
400        }
401
402        twd_base = of_iomap(np, 0);
403        if (!twd_base) {
404                err = -ENOMEM;
405                goto out;
406        }
407
408        err = twd_local_timer_common_register(np);
409
410out:
411        WARN(err, "twd_local_timer_of_register failed (%d)\n", err);
412        return err;
413}
414CLOCKSOURCE_OF_DECLARE(arm_twd_a9, "arm,cortex-a9-twd-timer", twd_local_timer_of_register);
415CLOCKSOURCE_OF_DECLARE(arm_twd_a5, "arm,cortex-a5-twd-timer", twd_local_timer_of_register);
416CLOCKSOURCE_OF_DECLARE(arm_twd_11mp, "arm,arm11mp-twd-timer", twd_local_timer_of_register);
417#endif
418
419/* Needed by mpcore_wdt */
420unsigned long twd_timer_get_rate(void)
421{
422        return twd_timer_rate;
423}
424EXPORT_SYMBOL_GPL(twd_timer_get_rate);
Note: See TracBrowser for help on using the repository browser.