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