xref: /linux/drivers/clocksource/timer-nxp-pit.c (revision fc346a155fe910a1cf4639b00b131f9a10284bdd)
1*fc346a15SDaniel Lezcano // SPDX-License-Identifier: GPL-2.0-or-later
2*fc346a15SDaniel Lezcano /*
3*fc346a15SDaniel Lezcano  * Copyright 2012-2013 Freescale Semiconductor, Inc.
4*fc346a15SDaniel Lezcano  */
5*fc346a15SDaniel Lezcano 
6*fc346a15SDaniel Lezcano #include <linux/interrupt.h>
7*fc346a15SDaniel Lezcano #include <linux/clockchips.h>
8*fc346a15SDaniel Lezcano #include <linux/clk.h>
9*fc346a15SDaniel Lezcano #include <linux/of_address.h>
10*fc346a15SDaniel Lezcano #include <linux/of_irq.h>
11*fc346a15SDaniel Lezcano #include <linux/sched_clock.h>
12*fc346a15SDaniel Lezcano 
13*fc346a15SDaniel Lezcano /*
14*fc346a15SDaniel Lezcano  * Each pit takes 0x10 Bytes register space
15*fc346a15SDaniel Lezcano  */
16*fc346a15SDaniel Lezcano #define PIT0_OFFSET	0x100
17*fc346a15SDaniel Lezcano #define PIT_CH(n)       (PIT0_OFFSET + 0x10 * (n))
18*fc346a15SDaniel Lezcano 
19*fc346a15SDaniel Lezcano #define PITMCR(__base)	(__base)
20*fc346a15SDaniel Lezcano 
21*fc346a15SDaniel Lezcano #define PITMCR_FRZ	BIT(0)
22*fc346a15SDaniel Lezcano #define PITMCR_MDIS	BIT(1)
23*fc346a15SDaniel Lezcano 
24*fc346a15SDaniel Lezcano #define PITLDVAL(__base)	(__base)
25*fc346a15SDaniel Lezcano #define PITTCTRL(__base)	((__base) + 0x08)
26*fc346a15SDaniel Lezcano 
27*fc346a15SDaniel Lezcano #define PITCVAL_OFFSET	0x04
28*fc346a15SDaniel Lezcano #define PITCVAL(__base)	((__base) + 0x04)
29*fc346a15SDaniel Lezcano 
30*fc346a15SDaniel Lezcano #define PITTCTRL_TEN			BIT(0)
31*fc346a15SDaniel Lezcano #define PITTCTRL_TIE			BIT(1)
32*fc346a15SDaniel Lezcano 
33*fc346a15SDaniel Lezcano #define PITTFLG(__base)	((__base) + 0x0c)
34*fc346a15SDaniel Lezcano 
35*fc346a15SDaniel Lezcano #define PITTFLG_TIF			BIT(0)
36*fc346a15SDaniel Lezcano 
37*fc346a15SDaniel Lezcano struct pit_timer {
38*fc346a15SDaniel Lezcano 	void __iomem *clksrc_base;
39*fc346a15SDaniel Lezcano 	void __iomem *clkevt_base;
40*fc346a15SDaniel Lezcano 	unsigned long cycle_per_jiffy;
41*fc346a15SDaniel Lezcano 	struct clock_event_device ced;
42*fc346a15SDaniel Lezcano 	struct clocksource cs;
43*fc346a15SDaniel Lezcano };
44*fc346a15SDaniel Lezcano 
45*fc346a15SDaniel Lezcano static void __iomem *sched_clock_base;
46*fc346a15SDaniel Lezcano 
47*fc346a15SDaniel Lezcano static inline struct pit_timer *ced_to_pit(struct clock_event_device *ced)
48*fc346a15SDaniel Lezcano {
49*fc346a15SDaniel Lezcano 	return container_of(ced, struct pit_timer, ced);
50*fc346a15SDaniel Lezcano }
51*fc346a15SDaniel Lezcano 
52*fc346a15SDaniel Lezcano static inline struct pit_timer *cs_to_pit(struct clocksource *cs)
53*fc346a15SDaniel Lezcano {
54*fc346a15SDaniel Lezcano 	return container_of(cs, struct pit_timer, cs);
55*fc346a15SDaniel Lezcano }
56*fc346a15SDaniel Lezcano 
57*fc346a15SDaniel Lezcano static inline void pit_module_enable(void __iomem *base)
58*fc346a15SDaniel Lezcano {
59*fc346a15SDaniel Lezcano 	writel(0, PITMCR(base));
60*fc346a15SDaniel Lezcano }
61*fc346a15SDaniel Lezcano 
62*fc346a15SDaniel Lezcano static inline void pit_module_disable(void __iomem *base)
63*fc346a15SDaniel Lezcano {
64*fc346a15SDaniel Lezcano 	writel(PITMCR_MDIS, PITMCR(base));
65*fc346a15SDaniel Lezcano }
66*fc346a15SDaniel Lezcano 
67*fc346a15SDaniel Lezcano static inline void pit_timer_enable(void __iomem *base, bool tie)
68*fc346a15SDaniel Lezcano {
69*fc346a15SDaniel Lezcano 	u32 val = PITTCTRL_TEN | (tie ? PITTCTRL_TIE : 0);
70*fc346a15SDaniel Lezcano 
71*fc346a15SDaniel Lezcano 	writel(val, PITTCTRL(base));
72*fc346a15SDaniel Lezcano }
73*fc346a15SDaniel Lezcano 
74*fc346a15SDaniel Lezcano static inline void pit_timer_disable(void __iomem *base)
75*fc346a15SDaniel Lezcano {
76*fc346a15SDaniel Lezcano 	writel(0, PITTCTRL(base));
77*fc346a15SDaniel Lezcano }
78*fc346a15SDaniel Lezcano 
79*fc346a15SDaniel Lezcano static inline void pit_timer_set_counter(void __iomem *base, unsigned int cnt)
80*fc346a15SDaniel Lezcano {
81*fc346a15SDaniel Lezcano 	writel(cnt, PITLDVAL(base));
82*fc346a15SDaniel Lezcano }
83*fc346a15SDaniel Lezcano 
84*fc346a15SDaniel Lezcano static inline void pit_timer_irqack(struct pit_timer *pit)
85*fc346a15SDaniel Lezcano {
86*fc346a15SDaniel Lezcano 	writel(PITTFLG_TIF, PITTFLG(pit->clkevt_base));
87*fc346a15SDaniel Lezcano }
88*fc346a15SDaniel Lezcano 
89*fc346a15SDaniel Lezcano static u64 notrace pit_read_sched_clock(void)
90*fc346a15SDaniel Lezcano {
91*fc346a15SDaniel Lezcano 	return ~readl(sched_clock_base);
92*fc346a15SDaniel Lezcano }
93*fc346a15SDaniel Lezcano 
94*fc346a15SDaniel Lezcano static u64 pit_timer_clocksource_read(struct clocksource *cs)
95*fc346a15SDaniel Lezcano {
96*fc346a15SDaniel Lezcano 	struct pit_timer *pit = cs_to_pit(cs);
97*fc346a15SDaniel Lezcano 
98*fc346a15SDaniel Lezcano 	return (u64)~readl(PITCVAL(pit->clksrc_base));
99*fc346a15SDaniel Lezcano }
100*fc346a15SDaniel Lezcano 
101*fc346a15SDaniel Lezcano static int __init pit_clocksource_init(struct pit_timer *pit, const char *name,
102*fc346a15SDaniel Lezcano 				       void __iomem *base, unsigned long rate)
103*fc346a15SDaniel Lezcano {
104*fc346a15SDaniel Lezcano 	/*
105*fc346a15SDaniel Lezcano 	 * The channels 0 and 1 can be chained to build a 64-bit
106*fc346a15SDaniel Lezcano 	 * timer. Let's use the channel 2 as a clocksource and leave
107*fc346a15SDaniel Lezcano 	 * the channels 0 and 1 unused for anyone else who needs them
108*fc346a15SDaniel Lezcano 	 */
109*fc346a15SDaniel Lezcano 	pit->clksrc_base = base + PIT_CH(2);
110*fc346a15SDaniel Lezcano 	pit->cs.name = name;
111*fc346a15SDaniel Lezcano 	pit->cs.rating = 300;
112*fc346a15SDaniel Lezcano 	pit->cs.read = pit_timer_clocksource_read;
113*fc346a15SDaniel Lezcano 	pit->cs.mask = CLOCKSOURCE_MASK(32);
114*fc346a15SDaniel Lezcano 	pit->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
115*fc346a15SDaniel Lezcano 
116*fc346a15SDaniel Lezcano 	/* set the max load value and start the clock source counter */
117*fc346a15SDaniel Lezcano 	pit_timer_disable(pit->clksrc_base);
118*fc346a15SDaniel Lezcano 	pit_timer_set_counter(pit->clksrc_base, ~0);
119*fc346a15SDaniel Lezcano 	pit_timer_enable(pit->clksrc_base, 0);
120*fc346a15SDaniel Lezcano 
121*fc346a15SDaniel Lezcano 	sched_clock_base = pit->clksrc_base + PITCVAL_OFFSET;
122*fc346a15SDaniel Lezcano 	sched_clock_register(pit_read_sched_clock, 32, rate);
123*fc346a15SDaniel Lezcano 
124*fc346a15SDaniel Lezcano 	return clocksource_register_hz(&pit->cs, rate);
125*fc346a15SDaniel Lezcano }
126*fc346a15SDaniel Lezcano 
127*fc346a15SDaniel Lezcano static int pit_set_next_event(unsigned long delta, struct clock_event_device *ced)
128*fc346a15SDaniel Lezcano {
129*fc346a15SDaniel Lezcano 	struct pit_timer *pit = ced_to_pit(ced);
130*fc346a15SDaniel Lezcano 
131*fc346a15SDaniel Lezcano 	/*
132*fc346a15SDaniel Lezcano 	 * set a new value to PITLDVAL register will not restart the timer,
133*fc346a15SDaniel Lezcano 	 * to abort the current cycle and start a timer period with the new
134*fc346a15SDaniel Lezcano 	 * value, the timer must be disabled and enabled again.
135*fc346a15SDaniel Lezcano 	 * and the PITLAVAL should be set to delta minus one according to pit
136*fc346a15SDaniel Lezcano 	 * hardware requirement.
137*fc346a15SDaniel Lezcano 	 */
138*fc346a15SDaniel Lezcano 	pit_timer_disable(pit->clkevt_base);
139*fc346a15SDaniel Lezcano 	pit_timer_set_counter(pit->clkevt_base, delta - 1);
140*fc346a15SDaniel Lezcano 	pit_timer_enable(pit->clkevt_base, true);
141*fc346a15SDaniel Lezcano 
142*fc346a15SDaniel Lezcano 	return 0;
143*fc346a15SDaniel Lezcano }
144*fc346a15SDaniel Lezcano 
145*fc346a15SDaniel Lezcano static int pit_shutdown(struct clock_event_device *ced)
146*fc346a15SDaniel Lezcano {
147*fc346a15SDaniel Lezcano 	struct pit_timer *pit = ced_to_pit(ced);
148*fc346a15SDaniel Lezcano 
149*fc346a15SDaniel Lezcano 	pit_timer_disable(pit->clkevt_base);
150*fc346a15SDaniel Lezcano 
151*fc346a15SDaniel Lezcano 	return 0;
152*fc346a15SDaniel Lezcano }
153*fc346a15SDaniel Lezcano 
154*fc346a15SDaniel Lezcano static int pit_set_periodic(struct clock_event_device *ced)
155*fc346a15SDaniel Lezcano {
156*fc346a15SDaniel Lezcano 	struct pit_timer *pit = ced_to_pit(ced);
157*fc346a15SDaniel Lezcano 
158*fc346a15SDaniel Lezcano 	pit_set_next_event(pit->cycle_per_jiffy, ced);
159*fc346a15SDaniel Lezcano 
160*fc346a15SDaniel Lezcano 	return 0;
161*fc346a15SDaniel Lezcano }
162*fc346a15SDaniel Lezcano 
163*fc346a15SDaniel Lezcano static irqreturn_t pit_timer_interrupt(int irq, void *dev_id)
164*fc346a15SDaniel Lezcano {
165*fc346a15SDaniel Lezcano 	struct clock_event_device *ced = dev_id;
166*fc346a15SDaniel Lezcano 	struct pit_timer *pit = ced_to_pit(ced);
167*fc346a15SDaniel Lezcano 
168*fc346a15SDaniel Lezcano 	pit_timer_irqack(pit);
169*fc346a15SDaniel Lezcano 
170*fc346a15SDaniel Lezcano 	/*
171*fc346a15SDaniel Lezcano 	 * pit hardware doesn't support oneshot, it will generate an interrupt
172*fc346a15SDaniel Lezcano 	 * and reload the counter value from PITLDVAL when PITCVAL reach zero,
173*fc346a15SDaniel Lezcano 	 * and start the counter again. So software need to disable the timer
174*fc346a15SDaniel Lezcano 	 * to stop the counter loop in ONESHOT mode.
175*fc346a15SDaniel Lezcano 	 */
176*fc346a15SDaniel Lezcano 	if (likely(clockevent_state_oneshot(ced)))
177*fc346a15SDaniel Lezcano 		pit_timer_disable(pit->clkevt_base);
178*fc346a15SDaniel Lezcano 
179*fc346a15SDaniel Lezcano 	ced->event_handler(ced);
180*fc346a15SDaniel Lezcano 
181*fc346a15SDaniel Lezcano 	return IRQ_HANDLED;
182*fc346a15SDaniel Lezcano }
183*fc346a15SDaniel Lezcano 
184*fc346a15SDaniel Lezcano static int __init pit_clockevent_init(struct pit_timer *pit, const char *name,
185*fc346a15SDaniel Lezcano 				      void __iomem *base, unsigned long rate,
186*fc346a15SDaniel Lezcano 				      int irq, unsigned int cpu)
187*fc346a15SDaniel Lezcano {
188*fc346a15SDaniel Lezcano 	/*
189*fc346a15SDaniel Lezcano 	 * The channels 0 and 1 can be chained to build a 64-bit
190*fc346a15SDaniel Lezcano 	 * timer. Let's use the channel 3 as a clockevent and leave
191*fc346a15SDaniel Lezcano 	 * the channels 0 and 1 unused for anyone else who needs them
192*fc346a15SDaniel Lezcano 	 */
193*fc346a15SDaniel Lezcano 	pit->clkevt_base = base + PIT_CH(3);
194*fc346a15SDaniel Lezcano 	pit->cycle_per_jiffy = rate / (HZ);
195*fc346a15SDaniel Lezcano 
196*fc346a15SDaniel Lezcano 	pit_timer_disable(pit->clkevt_base);
197*fc346a15SDaniel Lezcano 
198*fc346a15SDaniel Lezcano 	pit_timer_irqack(pit);
199*fc346a15SDaniel Lezcano 
200*fc346a15SDaniel Lezcano 	BUG_ON(request_irq(irq, pit_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
201*fc346a15SDaniel Lezcano 			   name, &pit->ced));
202*fc346a15SDaniel Lezcano 
203*fc346a15SDaniel Lezcano 	pit->ced.cpumask = cpumask_of(cpu);
204*fc346a15SDaniel Lezcano 	pit->ced.irq = irq;
205*fc346a15SDaniel Lezcano 
206*fc346a15SDaniel Lezcano 	pit->ced.name = name;
207*fc346a15SDaniel Lezcano 	pit->ced.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
208*fc346a15SDaniel Lezcano 	pit->ced.set_state_shutdown = pit_shutdown;
209*fc346a15SDaniel Lezcano 	pit->ced.set_state_periodic = pit_set_periodic;
210*fc346a15SDaniel Lezcano 	pit->ced.set_next_event	= pit_set_next_event;
211*fc346a15SDaniel Lezcano 	pit->ced.rating	= 300;
212*fc346a15SDaniel Lezcano 
213*fc346a15SDaniel Lezcano 	/*
214*fc346a15SDaniel Lezcano 	 * The value for the LDVAL register trigger is calculated as:
215*fc346a15SDaniel Lezcano 	 * LDVAL trigger = (period / clock period) - 1
216*fc346a15SDaniel Lezcano 	 * The pit is a 32-bit down count timer, when the counter value
217*fc346a15SDaniel Lezcano 	 * reaches 0, it will generate an interrupt, thus the minimal
218*fc346a15SDaniel Lezcano 	 * LDVAL trigger value is 1. And then the min_delta is
219*fc346a15SDaniel Lezcano 	 * minimal LDVAL trigger value + 1, and the max_delta is full 32-bit.
220*fc346a15SDaniel Lezcano 	 */
221*fc346a15SDaniel Lezcano 	clockevents_config_and_register(&pit->ced, rate, 2, 0xffffffff);
222*fc346a15SDaniel Lezcano 
223*fc346a15SDaniel Lezcano 	return 0;
224*fc346a15SDaniel Lezcano }
225*fc346a15SDaniel Lezcano 
226*fc346a15SDaniel Lezcano static int __init pit_timer_init(struct device_node *np)
227*fc346a15SDaniel Lezcano {
228*fc346a15SDaniel Lezcano 	struct pit_timer *pit;
229*fc346a15SDaniel Lezcano 	struct clk *pit_clk;
230*fc346a15SDaniel Lezcano 	void __iomem *timer_base;
231*fc346a15SDaniel Lezcano 	const char *name = of_node_full_name(np);
232*fc346a15SDaniel Lezcano 	unsigned long clk_rate;
233*fc346a15SDaniel Lezcano 	int irq, ret;
234*fc346a15SDaniel Lezcano 
235*fc346a15SDaniel Lezcano 	pit = kzalloc(sizeof(*pit), GFP_KERNEL);
236*fc346a15SDaniel Lezcano 	if (!pit)
237*fc346a15SDaniel Lezcano 		return -ENOMEM;
238*fc346a15SDaniel Lezcano 
239*fc346a15SDaniel Lezcano 	ret = -ENXIO;
240*fc346a15SDaniel Lezcano 	timer_base = of_iomap(np, 0);
241*fc346a15SDaniel Lezcano 	if (!timer_base) {
242*fc346a15SDaniel Lezcano 		pr_err("Failed to iomap\n");
243*fc346a15SDaniel Lezcano 		goto out_kfree;
244*fc346a15SDaniel Lezcano 	}
245*fc346a15SDaniel Lezcano 
246*fc346a15SDaniel Lezcano 	ret = -EINVAL;
247*fc346a15SDaniel Lezcano 	irq = irq_of_parse_and_map(np, 0);
248*fc346a15SDaniel Lezcano 	if (irq <= 0) {
249*fc346a15SDaniel Lezcano 		pr_err("Failed to irq_of_parse_and_map\n");
250*fc346a15SDaniel Lezcano 		goto out_iounmap;
251*fc346a15SDaniel Lezcano 	}
252*fc346a15SDaniel Lezcano 
253*fc346a15SDaniel Lezcano 	pit_clk = of_clk_get(np, 0);
254*fc346a15SDaniel Lezcano 	if (IS_ERR(pit_clk)) {
255*fc346a15SDaniel Lezcano 		ret = PTR_ERR(pit_clk);
256*fc346a15SDaniel Lezcano 		goto out_iounmap;
257*fc346a15SDaniel Lezcano 	}
258*fc346a15SDaniel Lezcano 
259*fc346a15SDaniel Lezcano 	ret = clk_prepare_enable(pit_clk);
260*fc346a15SDaniel Lezcano 	if (ret)
261*fc346a15SDaniel Lezcano 		goto out_clk_put;
262*fc346a15SDaniel Lezcano 
263*fc346a15SDaniel Lezcano 	clk_rate = clk_get_rate(pit_clk);
264*fc346a15SDaniel Lezcano 
265*fc346a15SDaniel Lezcano 	/* enable the pit module */
266*fc346a15SDaniel Lezcano 	pit_module_enable(timer_base);
267*fc346a15SDaniel Lezcano 
268*fc346a15SDaniel Lezcano 	ret = pit_clocksource_init(pit, name, timer_base, clk_rate);
269*fc346a15SDaniel Lezcano 	if (ret)
270*fc346a15SDaniel Lezcano 		goto out_pit_module_disable;
271*fc346a15SDaniel Lezcano 
272*fc346a15SDaniel Lezcano 	ret = pit_clockevent_init(pit, name, timer_base, clk_rate, irq, 0);
273*fc346a15SDaniel Lezcano 	if (ret)
274*fc346a15SDaniel Lezcano 		goto out_pit_clocksource_unregister;
275*fc346a15SDaniel Lezcano 
276*fc346a15SDaniel Lezcano 	return 0;
277*fc346a15SDaniel Lezcano 
278*fc346a15SDaniel Lezcano out_pit_clocksource_unregister:
279*fc346a15SDaniel Lezcano 	clocksource_unregister(&pit->cs);
280*fc346a15SDaniel Lezcano out_pit_module_disable:
281*fc346a15SDaniel Lezcano 	pit_module_disable(timer_base);
282*fc346a15SDaniel Lezcano 	clk_disable_unprepare(pit_clk);
283*fc346a15SDaniel Lezcano out_clk_put:
284*fc346a15SDaniel Lezcano 	clk_put(pit_clk);
285*fc346a15SDaniel Lezcano out_iounmap:
286*fc346a15SDaniel Lezcano 	iounmap(timer_base);
287*fc346a15SDaniel Lezcano out_kfree:
288*fc346a15SDaniel Lezcano 	kfree(pit);
289*fc346a15SDaniel Lezcano 
290*fc346a15SDaniel Lezcano 	return ret;
291*fc346a15SDaniel Lezcano }
292*fc346a15SDaniel Lezcano TIMER_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init);
293