xref: /openbmc/linux/arch/xtensa/kernel/time.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
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