15a0015d6SChris Zankel /*
25a0015d6SChris Zankel * arch/xtensa/kernel/time.c
35a0015d6SChris Zankel *
45a0015d6SChris Zankel * Timer and clock support.
55a0015d6SChris Zankel *
65a0015d6SChris Zankel * This file is subject to the terms and conditions of the GNU General Public
75a0015d6SChris Zankel * License. See the file "COPYING" in the main directory of this archive
85a0015d6SChris Zankel * for more details.
95a0015d6SChris Zankel *
105a0015d6SChris Zankel * Copyright (C) 2005 Tensilica Inc.
115a0015d6SChris Zankel *
125a0015d6SChris Zankel * Chris Zankel <chris@zankel.net>
135a0015d6SChris Zankel */
145a0015d6SChris Zankel
15205ad548SMax Filippov #include <linux/clk.h>
16047b0420SGeert Uytterhoeven #include <linux/of_clk.h>
175a0015d6SChris Zankel #include <linux/errno.h>
18d43c36dcSAlexey Dobriyan #include <linux/sched.h>
195a0015d6SChris Zankel #include <linux/time.h>
20fcc8f0f8SJohannes Weiner #include <linux/clocksource.h>
21925f5532SBaruch Siach #include <linux/clockchips.h>
225a0015d6SChris Zankel #include <linux/interrupt.h>
235a0015d6SChris Zankel #include <linux/module.h>
245a0015d6SChris Zankel #include <linux/init.h>
255a0015d6SChris Zankel #include <linux/irq.h>
265a0015d6SChris Zankel #include <linux/profile.h>
275a0015d6SChris Zankel #include <linux/delay.h>
282206d5ddSMax Filippov #include <linux/irqdomain.h>
29e3f43291SBaruch Siach #include <linux/sched_clock.h>
305a0015d6SChris Zankel
315a0015d6SChris Zankel #include <asm/timex.h>
325a0015d6SChris Zankel #include <asm/platform.h>
335a0015d6SChris Zankel
34e504c4b6SBaruch Siach unsigned long ccount_freq; /* ccount Hz */
3545ec8860SMax Filippov EXPORT_SYMBOL(ccount_freq);
365a0015d6SChris Zankel
ccount_read(struct clocksource * cs)37a5a1d1c2SThomas Gleixner static u64 ccount_read(struct clocksource *cs)
38fcc8f0f8SJohannes Weiner {
39a5a1d1c2SThomas Gleixner return (u64)get_ccount();
40fcc8f0f8SJohannes Weiner }
41fcc8f0f8SJohannes Weiner
ccount_sched_clock_read(void)423ade4f81SStephen Boyd static u64 notrace ccount_sched_clock_read(void)
43e3f43291SBaruch Siach {
44e3f43291SBaruch Siach return get_ccount();
45e3f43291SBaruch Siach }
46e3f43291SBaruch Siach
47fcc8f0f8SJohannes Weiner static struct clocksource ccount_clocksource = {
48fcc8f0f8SJohannes Weiner .name = "ccount",
49fcc8f0f8SJohannes Weiner .rating = 200,
50fcc8f0f8SJohannes Weiner .read = ccount_read,
51fcc8f0f8SJohannes Weiner .mask = CLOCKSOURCE_MASK(32),
520fb4040eSBaruch Siach .flags = CLOCK_SOURCE_IS_CONTINUOUS,
53fcc8f0f8SJohannes Weiner };
54fcc8f0f8SJohannes Weiner
5562351531SMax Filippov struct ccount_timer {
56925f5532SBaruch Siach struct clock_event_device evt;
57925f5532SBaruch Siach int irq_enabled;
5862351531SMax Filippov char name[24];
59925f5532SBaruch Siach };
60925f5532SBaruch Siach
ccount_timer_set_next_event(unsigned long delta,struct clock_event_device * dev)61925f5532SBaruch Siach static int ccount_timer_set_next_event(unsigned long delta,
62925f5532SBaruch Siach struct clock_event_device *dev)
63925f5532SBaruch Siach {
64925f5532SBaruch Siach unsigned long flags, next;
65925f5532SBaruch Siach int ret = 0;
66925f5532SBaruch Siach
67925f5532SBaruch Siach local_irq_save(flags);
68925f5532SBaruch Siach next = get_ccount() + delta;
69925f5532SBaruch Siach set_linux_timer(next);
70925f5532SBaruch Siach if (next - get_ccount() > delta)
71925f5532SBaruch Siach ret = -ETIME;
72925f5532SBaruch Siach local_irq_restore(flags);
73925f5532SBaruch Siach
74925f5532SBaruch Siach return ret;
75925f5532SBaruch Siach }
76925f5532SBaruch Siach
77925f5532SBaruch Siach /*
78925f5532SBaruch Siach * There is no way to disable the timer interrupt at the device level,
79925f5532SBaruch Siach * only at the intenable register itself. Since enable_irq/disable_irq
80925f5532SBaruch Siach * calls are nested, we need to make sure that these calls are
81925f5532SBaruch Siach * balanced.
82925f5532SBaruch Siach */
ccount_timer_shutdown(struct clock_event_device * evt)838e40fc4bSViresh Kumar static int ccount_timer_shutdown(struct clock_event_device *evt)
848e40fc4bSViresh Kumar {
858e40fc4bSViresh Kumar struct ccount_timer *timer =
868e40fc4bSViresh Kumar container_of(evt, struct ccount_timer, evt);
878e40fc4bSViresh Kumar
88925f5532SBaruch Siach if (timer->irq_enabled) {
894fe8713bSMax Filippov disable_irq_nosync(evt->irq);
90925f5532SBaruch Siach timer->irq_enabled = 0;
91925f5532SBaruch Siach }
928e40fc4bSViresh Kumar return 0;
938e40fc4bSViresh Kumar }
948e40fc4bSViresh Kumar
ccount_timer_set_oneshot(struct clock_event_device * evt)958e40fc4bSViresh Kumar static int ccount_timer_set_oneshot(struct clock_event_device *evt)
968e40fc4bSViresh Kumar {
978e40fc4bSViresh Kumar struct ccount_timer *timer =
988e40fc4bSViresh Kumar container_of(evt, struct ccount_timer, evt);
998e40fc4bSViresh Kumar
100925f5532SBaruch Siach if (!timer->irq_enabled) {
101925f5532SBaruch Siach enable_irq(evt->irq);
102925f5532SBaruch Siach timer->irq_enabled = 1;
103925f5532SBaruch Siach }
1048e40fc4bSViresh Kumar return 0;
105925f5532SBaruch Siach }
106925f5532SBaruch Siach
10774d69eaaSMax Filippov static DEFINE_PER_CPU(struct ccount_timer, ccount_timer) = {
10874d69eaaSMax Filippov .evt = {
10974d69eaaSMax Filippov .features = CLOCK_EVT_FEAT_ONESHOT,
11074d69eaaSMax Filippov .rating = 300,
11174d69eaaSMax Filippov .set_next_event = ccount_timer_set_next_event,
11274d69eaaSMax Filippov .set_state_shutdown = ccount_timer_shutdown,
11374d69eaaSMax Filippov .set_state_oneshot = ccount_timer_set_oneshot,
11474d69eaaSMax Filippov .tick_resume = ccount_timer_set_oneshot,
11574d69eaaSMax Filippov },
11674d69eaaSMax Filippov };
11774d69eaaSMax Filippov
timer_interrupt(int irq,void * dev_id)11874d69eaaSMax Filippov static irqreturn_t timer_interrupt(int irq, void *dev_id)
11974d69eaaSMax Filippov {
12074d69eaaSMax Filippov struct clock_event_device *evt = &this_cpu_ptr(&ccount_timer)->evt;
12174d69eaaSMax Filippov
12274d69eaaSMax Filippov set_linux_timer(get_linux_timer());
12374d69eaaSMax Filippov evt->event_handler(evt);
12474d69eaaSMax Filippov return IRQ_HANDLED;
12574d69eaaSMax Filippov }
12674d69eaaSMax Filippov
local_timer_setup(unsigned cpu)12762351531SMax Filippov void local_timer_setup(unsigned cpu)
12862351531SMax Filippov {
12962351531SMax Filippov struct ccount_timer *timer = &per_cpu(ccount_timer, cpu);
13062351531SMax Filippov struct clock_event_device *clockevent = &timer->evt;
13162351531SMax Filippov
13262351531SMax Filippov timer->irq_enabled = 1;
13362351531SMax Filippov snprintf(timer->name, sizeof(timer->name), "ccount_clockevent_%u", cpu);
13474d69eaaSMax Filippov clockevent->name = timer->name;
13562351531SMax Filippov clockevent->cpumask = cpumask_of(cpu);
13662351531SMax Filippov clockevent->irq = irq_create_mapping(NULL, LINUX_TIMER_INT);
13762351531SMax Filippov if (WARN(!clockevent->irq, "error: can't map timer irq"))
13862351531SMax Filippov return;
13962351531SMax Filippov clockevents_config_and_register(clockevent, ccount_freq,
14062351531SMax Filippov 0xf, 0xffffffff);
14162351531SMax Filippov }
14262351531SMax Filippov
143205ad548SMax Filippov #ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
144205ad548SMax Filippov #ifdef CONFIG_OF
calibrate_ccount(void)145205ad548SMax Filippov static void __init calibrate_ccount(void)
146205ad548SMax Filippov {
147205ad548SMax Filippov struct device_node *cpu;
148205ad548SMax Filippov struct clk *clk;
149205ad548SMax Filippov
150205ad548SMax Filippov cpu = of_find_compatible_node(NULL, NULL, "cdns,xtensa-cpu");
151205ad548SMax Filippov if (cpu) {
152205ad548SMax Filippov clk = of_clk_get(cpu, 0);
153*a0117dc9SLiang He of_node_put(cpu);
154205ad548SMax Filippov if (!IS_ERR(clk)) {
155205ad548SMax Filippov ccount_freq = clk_get_rate(clk);
156205ad548SMax Filippov return;
157205ad548SMax Filippov } else {
158205ad548SMax Filippov pr_warn("%s: CPU input clock not found\n",
159205ad548SMax Filippov __func__);
160205ad548SMax Filippov }
161205ad548SMax Filippov } else {
162205ad548SMax Filippov pr_warn("%s: CPU node not found in the device tree\n",
163205ad548SMax Filippov __func__);
164205ad548SMax Filippov }
165205ad548SMax Filippov
166205ad548SMax Filippov platform_calibrate_ccount();
167205ad548SMax Filippov }
168205ad548SMax Filippov #else
calibrate_ccount(void)169205ad548SMax Filippov static inline void calibrate_ccount(void)
170205ad548SMax Filippov {
171205ad548SMax Filippov platform_calibrate_ccount();
172205ad548SMax Filippov }
173205ad548SMax Filippov #endif
174205ad548SMax Filippov #endif
175205ad548SMax Filippov
time_init(void)1765a0015d6SChris Zankel void __init time_init(void)
1775a0015d6SChris Zankel {
17898060484Safzal mohammed int irq;
17998060484Safzal mohammed
180205ad548SMax Filippov of_clk_init(NULL);
181288a60cfSChris Zankel #ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
182d4eccafcSMax Filippov pr_info("Calibrating CPU frequency ");
183205ad548SMax Filippov calibrate_ccount();
184d4eccafcSMax Filippov pr_cont("%d.%02d MHz\n",
185d4eccafcSMax Filippov (int)ccount_freq / 1000000,
186e504c4b6SBaruch Siach (int)(ccount_freq / 10000) % 100);
187fedc21dcSBaruch Siach #else
188fedc21dcSBaruch Siach ccount_freq = CONFIG_XTENSA_CPU_CLOCK*1000000UL;
1895a0015d6SChris Zankel #endif
190205ad548SMax Filippov WARN(!ccount_freq,
191205ad548SMax Filippov "%s: CPU clock frequency is not set up correctly\n",
192205ad548SMax Filippov __func__);
1938d5e1d8eSBaruch Siach clocksource_register_hz(&ccount_clocksource, ccount_freq);
19462351531SMax Filippov local_timer_setup(0);
19598060484Safzal mohammed irq = this_cpu_ptr(&ccount_timer)->evt.irq;
19698060484Safzal mohammed if (request_irq(irq, timer_interrupt, IRQF_TIMER, "timer", NULL))
19798060484Safzal mohammed pr_err("Failed to request irq %d (timer)\n", irq);
1983ade4f81SStephen Boyd sched_clock_register(ccount_sched_clock_read, 32, ccount_freq);
199ba5d08c0SDaniel Lezcano timer_probe();
2005a0015d6SChris Zankel }
2015a0015d6SChris Zankel
2025a0015d6SChris Zankel #ifndef CONFIG_GENERIC_CALIBRATE_DELAY
calibrate_delay(void)2036cb4c159SPaul Gortmaker void calibrate_delay(void)
2045a0015d6SChris Zankel {
2058d5e1d8eSBaruch Siach loops_per_jiffy = ccount_freq / HZ;
206d4eccafcSMax Filippov pr_info("Calibrating delay loop (skipped)... %lu.%02lu BogoMIPS preset\n",
2075a0015d6SChris Zankel loops_per_jiffy / (1000000 / HZ),
2085a0015d6SChris Zankel (loops_per_jiffy / (10000 / HZ)) % 100);
2095a0015d6SChris Zankel }
2105a0015d6SChris Zankel #endif
211