xref: /linux/drivers/clocksource/timer-keystone.c (revision f8f5fe86f7e10ecc784b53537edbe3fcf0c00884)
1e6b7f580SIvan Khoronzhuk /*
2e6b7f580SIvan Khoronzhuk  * Keystone broadcast clock-event
3e6b7f580SIvan Khoronzhuk  *
4e6b7f580SIvan Khoronzhuk  * Copyright 2013 Texas Instruments, Inc.
5e6b7f580SIvan Khoronzhuk  *
6e6b7f580SIvan Khoronzhuk  * Author: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
7e6b7f580SIvan Khoronzhuk  *
8e6b7f580SIvan Khoronzhuk  * This program is free software; you can redistribute it and/or modify
9e6b7f580SIvan Khoronzhuk  * it under the terms of the GNU General Public License version 2 as
10e6b7f580SIvan Khoronzhuk  * published by the Free Software Foundation.
11e6b7f580SIvan Khoronzhuk  *
12e6b7f580SIvan Khoronzhuk  */
13e6b7f580SIvan Khoronzhuk 
14e6b7f580SIvan Khoronzhuk #include <linux/clk.h>
15e6b7f580SIvan Khoronzhuk #include <linux/clockchips.h>
16e6b7f580SIvan Khoronzhuk #include <linux/clocksource.h>
17e6b7f580SIvan Khoronzhuk #include <linux/interrupt.h>
18e6b7f580SIvan Khoronzhuk #include <linux/of_address.h>
19e6b7f580SIvan Khoronzhuk #include <linux/of_irq.h>
20e6b7f580SIvan Khoronzhuk 
21e6b7f580SIvan Khoronzhuk #define TIMER_NAME			"timer-keystone"
22e6b7f580SIvan Khoronzhuk 
23e6b7f580SIvan Khoronzhuk /* Timer register offsets */
24e6b7f580SIvan Khoronzhuk #define TIM12				0x10
25e6b7f580SIvan Khoronzhuk #define TIM34				0x14
26e6b7f580SIvan Khoronzhuk #define PRD12				0x18
27e6b7f580SIvan Khoronzhuk #define PRD34				0x1c
28e6b7f580SIvan Khoronzhuk #define TCR				0x20
29e6b7f580SIvan Khoronzhuk #define TGCR				0x24
30e6b7f580SIvan Khoronzhuk #define INTCTLSTAT			0x44
31e6b7f580SIvan Khoronzhuk 
32e6b7f580SIvan Khoronzhuk /* Timer register bitfields */
33e6b7f580SIvan Khoronzhuk #define TCR_ENAMODE_MASK		0xC0
34e6b7f580SIvan Khoronzhuk #define TCR_ENAMODE_ONESHOT_MASK	0x40
35e6b7f580SIvan Khoronzhuk #define TCR_ENAMODE_PERIODIC_MASK	0x80
36e6b7f580SIvan Khoronzhuk 
37e6b7f580SIvan Khoronzhuk #define TGCR_TIM_UNRESET_MASK		0x03
38e6b7f580SIvan Khoronzhuk #define INTCTLSTAT_ENINT_MASK		0x01
39e6b7f580SIvan Khoronzhuk 
40e6b7f580SIvan Khoronzhuk /**
41e6b7f580SIvan Khoronzhuk  * struct keystone_timer: holds timer's data
42e6b7f580SIvan Khoronzhuk  * @base: timer memory base address
43e6b7f580SIvan Khoronzhuk  * @hz_period: cycles per HZ period
44e6b7f580SIvan Khoronzhuk  * @event_dev: event device based on timer
45e6b7f580SIvan Khoronzhuk  */
46e6b7f580SIvan Khoronzhuk static struct keystone_timer {
47e6b7f580SIvan Khoronzhuk 	void __iomem *base;
48e6b7f580SIvan Khoronzhuk 	unsigned long hz_period;
49e6b7f580SIvan Khoronzhuk 	struct clock_event_device event_dev;
50e6b7f580SIvan Khoronzhuk } timer;
51e6b7f580SIvan Khoronzhuk 
52e6b7f580SIvan Khoronzhuk static inline u32 keystone_timer_readl(unsigned long rg)
53e6b7f580SIvan Khoronzhuk {
54e6b7f580SIvan Khoronzhuk 	return readl_relaxed(timer.base + rg);
55e6b7f580SIvan Khoronzhuk }
56e6b7f580SIvan Khoronzhuk 
57e6b7f580SIvan Khoronzhuk static inline void keystone_timer_writel(u32 val, unsigned long rg)
58e6b7f580SIvan Khoronzhuk {
59e6b7f580SIvan Khoronzhuk 	writel_relaxed(val, timer.base + rg);
60e6b7f580SIvan Khoronzhuk }
61e6b7f580SIvan Khoronzhuk 
62e6b7f580SIvan Khoronzhuk /**
63e6b7f580SIvan Khoronzhuk  * keystone_timer_barrier: write memory barrier
64e6b7f580SIvan Khoronzhuk  * use explicit barrier to avoid using readl/writel non relaxed function
65e6b7f580SIvan Khoronzhuk  * variants, because in our case non relaxed variants hide the true places
66e6b7f580SIvan Khoronzhuk  * where barrier is needed.
67e6b7f580SIvan Khoronzhuk  */
68e6b7f580SIvan Khoronzhuk static inline void keystone_timer_barrier(void)
69e6b7f580SIvan Khoronzhuk {
70e6b7f580SIvan Khoronzhuk 	__iowmb();
71e6b7f580SIvan Khoronzhuk }
72e6b7f580SIvan Khoronzhuk 
73e6b7f580SIvan Khoronzhuk /**
74e6b7f580SIvan Khoronzhuk  * keystone_timer_config: configures timer to work in oneshot/periodic modes.
75634eb0ecSViresh Kumar  * @ mask: mask of the mode to configure
76e6b7f580SIvan Khoronzhuk  * @ period: cycles number to configure for
77e6b7f580SIvan Khoronzhuk  */
78634eb0ecSViresh Kumar static int keystone_timer_config(u64 period, int mask)
79e6b7f580SIvan Khoronzhuk {
80e6b7f580SIvan Khoronzhuk 	u32 tcr;
81e6b7f580SIvan Khoronzhuk 	u32 off;
82e6b7f580SIvan Khoronzhuk 
83e6b7f580SIvan Khoronzhuk 	tcr = keystone_timer_readl(TCR);
84e6b7f580SIvan Khoronzhuk 	off = tcr & ~(TCR_ENAMODE_MASK);
85e6b7f580SIvan Khoronzhuk 
86e6b7f580SIvan Khoronzhuk 	/* set enable mode */
87634eb0ecSViresh Kumar 	tcr |= mask;
88e6b7f580SIvan Khoronzhuk 
89e6b7f580SIvan Khoronzhuk 	/* disable timer */
90e6b7f580SIvan Khoronzhuk 	keystone_timer_writel(off, TCR);
91e6b7f580SIvan Khoronzhuk 	/* here we have to be sure the timer has been disabled */
92e6b7f580SIvan Khoronzhuk 	keystone_timer_barrier();
93e6b7f580SIvan Khoronzhuk 
94e6b7f580SIvan Khoronzhuk 	/* reset counter to zero, set new period */
95e6b7f580SIvan Khoronzhuk 	keystone_timer_writel(0, TIM12);
96e6b7f580SIvan Khoronzhuk 	keystone_timer_writel(0, TIM34);
97e6b7f580SIvan Khoronzhuk 	keystone_timer_writel(period & 0xffffffff, PRD12);
98e6b7f580SIvan Khoronzhuk 	keystone_timer_writel(period >> 32, PRD34);
99e6b7f580SIvan Khoronzhuk 
100e6b7f580SIvan Khoronzhuk 	/*
101e6b7f580SIvan Khoronzhuk 	 * enable timer
102e6b7f580SIvan Khoronzhuk 	 * here we have to be sure that CNTLO, CNTHI, PRDLO, PRDHI registers
103e6b7f580SIvan Khoronzhuk 	 * have been written.
104e6b7f580SIvan Khoronzhuk 	 */
105e6b7f580SIvan Khoronzhuk 	keystone_timer_barrier();
106e6b7f580SIvan Khoronzhuk 	keystone_timer_writel(tcr, TCR);
107e6b7f580SIvan Khoronzhuk 	return 0;
108e6b7f580SIvan Khoronzhuk }
109e6b7f580SIvan Khoronzhuk 
110e6b7f580SIvan Khoronzhuk static void keystone_timer_disable(void)
111e6b7f580SIvan Khoronzhuk {
112e6b7f580SIvan Khoronzhuk 	u32 tcr;
113e6b7f580SIvan Khoronzhuk 
114e6b7f580SIvan Khoronzhuk 	tcr = keystone_timer_readl(TCR);
115e6b7f580SIvan Khoronzhuk 
116e6b7f580SIvan Khoronzhuk 	/* disable timer */
117e6b7f580SIvan Khoronzhuk 	tcr &= ~(TCR_ENAMODE_MASK);
118e6b7f580SIvan Khoronzhuk 	keystone_timer_writel(tcr, TCR);
119e6b7f580SIvan Khoronzhuk }
120e6b7f580SIvan Khoronzhuk 
121e6b7f580SIvan Khoronzhuk static irqreturn_t keystone_timer_interrupt(int irq, void *dev_id)
122e6b7f580SIvan Khoronzhuk {
123e6b7f580SIvan Khoronzhuk 	struct clock_event_device *evt = dev_id;
124e6b7f580SIvan Khoronzhuk 
125e6b7f580SIvan Khoronzhuk 	evt->event_handler(evt);
126e6b7f580SIvan Khoronzhuk 	return IRQ_HANDLED;
127e6b7f580SIvan Khoronzhuk }
128e6b7f580SIvan Khoronzhuk 
129e6b7f580SIvan Khoronzhuk static int keystone_set_next_event(unsigned long cycles,
130e6b7f580SIvan Khoronzhuk 				  struct clock_event_device *evt)
131e6b7f580SIvan Khoronzhuk {
132634eb0ecSViresh Kumar 	return keystone_timer_config(cycles, TCR_ENAMODE_ONESHOT_MASK);
133e6b7f580SIvan Khoronzhuk }
134e6b7f580SIvan Khoronzhuk 
135634eb0ecSViresh Kumar static int keystone_shutdown(struct clock_event_device *evt)
136e6b7f580SIvan Khoronzhuk {
137e6b7f580SIvan Khoronzhuk 	keystone_timer_disable();
138634eb0ecSViresh Kumar 	return 0;
139e6b7f580SIvan Khoronzhuk }
140634eb0ecSViresh Kumar 
141634eb0ecSViresh Kumar static int keystone_set_periodic(struct clock_event_device *evt)
142634eb0ecSViresh Kumar {
143634eb0ecSViresh Kumar 	keystone_timer_config(timer.hz_period, TCR_ENAMODE_PERIODIC_MASK);
144634eb0ecSViresh Kumar 	return 0;
145e6b7f580SIvan Khoronzhuk }
146e6b7f580SIvan Khoronzhuk 
14753186505SDaniel Lezcano static int __init keystone_timer_init(struct device_node *np)
148e6b7f580SIvan Khoronzhuk {
149e6b7f580SIvan Khoronzhuk 	struct clock_event_device *event_dev = &timer.event_dev;
150e6b7f580SIvan Khoronzhuk 	unsigned long rate;
151e6b7f580SIvan Khoronzhuk 	struct clk *clk;
152e6b7f580SIvan Khoronzhuk 	int irq, error;
153e6b7f580SIvan Khoronzhuk 
154e6b7f580SIvan Khoronzhuk 	irq  = irq_of_parse_and_map(np, 0);
155bdf7344eSDaniel Lezcano 	if (!irq) {
156e6b7f580SIvan Khoronzhuk 		pr_err("%s: failed to map interrupts\n", __func__);
15753186505SDaniel Lezcano 		return -EINVAL;
158e6b7f580SIvan Khoronzhuk 	}
159e6b7f580SIvan Khoronzhuk 
160e6b7f580SIvan Khoronzhuk 	timer.base = of_iomap(np, 0);
161e6b7f580SIvan Khoronzhuk 	if (!timer.base) {
162e6b7f580SIvan Khoronzhuk 		pr_err("%s: failed to map registers\n", __func__);
16353186505SDaniel Lezcano 		return -ENXIO;
164e6b7f580SIvan Khoronzhuk 	}
165e6b7f580SIvan Khoronzhuk 
166e6b7f580SIvan Khoronzhuk 	clk = of_clk_get(np, 0);
167e6b7f580SIvan Khoronzhuk 	if (IS_ERR(clk)) {
168e6b7f580SIvan Khoronzhuk 		pr_err("%s: failed to get clock\n", __func__);
169e6b7f580SIvan Khoronzhuk 		iounmap(timer.base);
17053186505SDaniel Lezcano 		return PTR_ERR(clk);
171e6b7f580SIvan Khoronzhuk 	}
172e6b7f580SIvan Khoronzhuk 
173e6b7f580SIvan Khoronzhuk 	error = clk_prepare_enable(clk);
174e6b7f580SIvan Khoronzhuk 	if (error) {
175e6b7f580SIvan Khoronzhuk 		pr_err("%s: failed to enable clock\n", __func__);
176e6b7f580SIvan Khoronzhuk 		goto err;
177e6b7f580SIvan Khoronzhuk 	}
178e6b7f580SIvan Khoronzhuk 
179e6b7f580SIvan Khoronzhuk 	rate = clk_get_rate(clk);
180e6b7f580SIvan Khoronzhuk 
181e6b7f580SIvan Khoronzhuk 	/* disable, use internal clock source */
182e6b7f580SIvan Khoronzhuk 	keystone_timer_writel(0, TCR);
183e6b7f580SIvan Khoronzhuk 	/* here we have to be sure the timer has been disabled */
184e6b7f580SIvan Khoronzhuk 	keystone_timer_barrier();
185e6b7f580SIvan Khoronzhuk 
186e6b7f580SIvan Khoronzhuk 	/* reset timer as 64-bit, no pre-scaler, plus features are disabled */
187e6b7f580SIvan Khoronzhuk 	keystone_timer_writel(0, TGCR);
188e6b7f580SIvan Khoronzhuk 
189e6b7f580SIvan Khoronzhuk 	/* unreset timer */
190e099d01eSMatthias Brugger 	keystone_timer_writel(TGCR_TIM_UNRESET_MASK, TGCR);
191e6b7f580SIvan Khoronzhuk 
192e6b7f580SIvan Khoronzhuk 	/* init counter to zero */
193e6b7f580SIvan Khoronzhuk 	keystone_timer_writel(0, TIM12);
194e6b7f580SIvan Khoronzhuk 	keystone_timer_writel(0, TIM34);
195e6b7f580SIvan Khoronzhuk 
196e6b7f580SIvan Khoronzhuk 	timer.hz_period = DIV_ROUND_UP(rate, HZ);
197e6b7f580SIvan Khoronzhuk 
198e6b7f580SIvan Khoronzhuk 	/* enable timer interrupts */
199e6b7f580SIvan Khoronzhuk 	keystone_timer_writel(INTCTLSTAT_ENINT_MASK, INTCTLSTAT);
200e6b7f580SIvan Khoronzhuk 
201e6b7f580SIvan Khoronzhuk 	error = request_irq(irq, keystone_timer_interrupt, IRQF_TIMER,
202e6b7f580SIvan Khoronzhuk 			    TIMER_NAME, event_dev);
203e6b7f580SIvan Khoronzhuk 	if (error) {
204e6b7f580SIvan Khoronzhuk 		pr_err("%s: failed to setup irq\n", __func__);
205e6b7f580SIvan Khoronzhuk 		goto err;
206e6b7f580SIvan Khoronzhuk 	}
207e6b7f580SIvan Khoronzhuk 
208e6b7f580SIvan Khoronzhuk 	/* setup clockevent */
209e6b7f580SIvan Khoronzhuk 	event_dev->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
210e6b7f580SIvan Khoronzhuk 	event_dev->set_next_event = keystone_set_next_event;
211634eb0ecSViresh Kumar 	event_dev->set_state_shutdown = keystone_shutdown;
212634eb0ecSViresh Kumar 	event_dev->set_state_periodic = keystone_set_periodic;
213634eb0ecSViresh Kumar 	event_dev->set_state_oneshot = keystone_shutdown;
214*f8f5fe86SSudeep Holla 	event_dev->cpumask = cpu_possible_mask;
215e6b7f580SIvan Khoronzhuk 	event_dev->owner = THIS_MODULE;
216e6b7f580SIvan Khoronzhuk 	event_dev->name = TIMER_NAME;
217e6b7f580SIvan Khoronzhuk 	event_dev->irq = irq;
218e6b7f580SIvan Khoronzhuk 
219e6b7f580SIvan Khoronzhuk 	clockevents_config_and_register(event_dev, rate, 1, ULONG_MAX);
220e6b7f580SIvan Khoronzhuk 
221e6b7f580SIvan Khoronzhuk 	pr_info("keystone timer clock @%lu Hz\n", rate);
22253186505SDaniel Lezcano 	return 0;
223e6b7f580SIvan Khoronzhuk err:
224e6b7f580SIvan Khoronzhuk 	clk_put(clk);
225e6b7f580SIvan Khoronzhuk 	iounmap(timer.base);
22653186505SDaniel Lezcano 	return error;
227e6b7f580SIvan Khoronzhuk }
228e6b7f580SIvan Khoronzhuk 
22917273395SDaniel Lezcano TIMER_OF_DECLARE(keystone_timer, "ti,keystone-timer",
230e6b7f580SIvan Khoronzhuk 			   keystone_timer_init);
231