1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Freescale FlexTimer Module (FTM) timer driver.
4  *
5  * Copyright 2014 Freescale Semiconductor, Inc.
6  */
7 
8 #include <linux/clk.h>
9 #include <linux/clockchips.h>
10 #include <linux/clocksource.h>
11 #include <linux/err.h>
12 #include <linux/interrupt.h>
13 #include <linux/io.h>
14 #include <linux/of_address.h>
15 #include <linux/of_irq.h>
16 #include <linux/sched_clock.h>
17 #include <linux/slab.h>
18 #include <linux/fsl/ftm.h>
19 
20 #define FTM_SC_CLK(c)	((c) << FTM_SC_CLK_MASK_SHIFT)
21 
22 struct ftm_clock_device {
23 	void __iomem *clksrc_base;
24 	void __iomem *clkevt_base;
25 	unsigned long periodic_cyc;
26 	unsigned long ps;
27 	bool big_endian;
28 };
29 
30 static struct ftm_clock_device *priv;
31 
32 static inline u32 ftm_readl(void __iomem *addr)
33 {
34 	if (priv->big_endian)
35 		return ioread32be(addr);
36 	else
37 		return ioread32(addr);
38 }
39 
40 static inline void ftm_writel(u32 val, void __iomem *addr)
41 {
42 	if (priv->big_endian)
43 		iowrite32be(val, addr);
44 	else
45 		iowrite32(val, addr);
46 }
47 
48 static inline void ftm_counter_enable(void __iomem *base)
49 {
50 	u32 val;
51 
52 	/* select and enable counter clock source */
53 	val = ftm_readl(base + FTM_SC);
54 	val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
55 	val |= priv->ps | FTM_SC_CLK(1);
56 	ftm_writel(val, base + FTM_SC);
57 }
58 
59 static inline void ftm_counter_disable(void __iomem *base)
60 {
61 	u32 val;
62 
63 	/* disable counter clock source */
64 	val = ftm_readl(base + FTM_SC);
65 	val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK);
66 	ftm_writel(val, base + FTM_SC);
67 }
68 
69 static inline void ftm_irq_acknowledge(void __iomem *base)
70 {
71 	u32 val;
72 
73 	val = ftm_readl(base + FTM_SC);
74 	val &= ~FTM_SC_TOF;
75 	ftm_writel(val, base + FTM_SC);
76 }
77 
78 static inline void ftm_irq_enable(void __iomem *base)
79 {
80 	u32 val;
81 
82 	val = ftm_readl(base + FTM_SC);
83 	val |= FTM_SC_TOIE;
84 	ftm_writel(val, base + FTM_SC);
85 }
86 
87 static inline void ftm_irq_disable(void __iomem *base)
88 {
89 	u32 val;
90 
91 	val = ftm_readl(base + FTM_SC);
92 	val &= ~FTM_SC_TOIE;
93 	ftm_writel(val, base + FTM_SC);
94 }
95 
96 static inline void ftm_reset_counter(void __iomem *base)
97 {
98 	/*
99 	 * The CNT register contains the FTM counter value.
100 	 * Reset clears the CNT register. Writing any value to COUNT
101 	 * updates the counter with its initial value, CNTIN.
102 	 */
103 	ftm_writel(0x00, base + FTM_CNT);
104 }
105 
106 static u64 notrace ftm_read_sched_clock(void)
107 {
108 	return ftm_readl(priv->clksrc_base + FTM_CNT);
109 }
110 
111 static int ftm_set_next_event(unsigned long delta,
112 				struct clock_event_device *unused)
113 {
114 	/*
115 	 * The CNNIN and MOD are all double buffer registers, writing
116 	 * to the MOD register latches the value into a buffer. The MOD
117 	 * register is updated with the value of its write buffer with
118 	 * the following scenario:
119 	 * a, the counter source clock is diabled.
120 	 */
121 	ftm_counter_disable(priv->clkevt_base);
122 
123 	/* Force the value of CNTIN to be loaded into the FTM counter */
124 	ftm_reset_counter(priv->clkevt_base);
125 
126 	/*
127 	 * The counter increments until the value of MOD is reached,
128 	 * at which point the counter is reloaded with the value of CNTIN.
129 	 * The TOF (the overflow flag) bit is set when the FTM counter
130 	 * changes from MOD to CNTIN. So we should using the delta - 1.
131 	 */
132 	ftm_writel(delta - 1, priv->clkevt_base + FTM_MOD);
133 
134 	ftm_counter_enable(priv->clkevt_base);
135 
136 	ftm_irq_enable(priv->clkevt_base);
137 
138 	return 0;
139 }
140 
141 static int ftm_set_oneshot(struct clock_event_device *evt)
142 {
143 	ftm_counter_disable(priv->clkevt_base);
144 	return 0;
145 }
146 
147 static int ftm_set_periodic(struct clock_event_device *evt)
148 {
149 	ftm_set_next_event(priv->periodic_cyc, evt);
150 	return 0;
151 }
152 
153 static irqreturn_t ftm_evt_interrupt(int irq, void *dev_id)
154 {
155 	struct clock_event_device *evt = dev_id;
156 
157 	ftm_irq_acknowledge(priv->clkevt_base);
158 
159 	if (likely(clockevent_state_oneshot(evt))) {
160 		ftm_irq_disable(priv->clkevt_base);
161 		ftm_counter_disable(priv->clkevt_base);
162 	}
163 
164 	evt->event_handler(evt);
165 
166 	return IRQ_HANDLED;
167 }
168 
169 static struct clock_event_device ftm_clockevent = {
170 	.name			= "Freescale ftm timer",
171 	.features		= CLOCK_EVT_FEAT_PERIODIC |
172 				  CLOCK_EVT_FEAT_ONESHOT,
173 	.set_state_periodic	= ftm_set_periodic,
174 	.set_state_oneshot	= ftm_set_oneshot,
175 	.set_next_event		= ftm_set_next_event,
176 	.rating			= 300,
177 };
178 
179 static struct irqaction ftm_timer_irq = {
180 	.name		= "Freescale ftm timer",
181 	.flags		= IRQF_TIMER | IRQF_IRQPOLL,
182 	.handler	= ftm_evt_interrupt,
183 	.dev_id		= &ftm_clockevent,
184 };
185 
186 static int __init ftm_clockevent_init(unsigned long freq, int irq)
187 {
188 	int err;
189 
190 	ftm_writel(0x00, priv->clkevt_base + FTM_CNTIN);
191 	ftm_writel(~0u, priv->clkevt_base + FTM_MOD);
192 
193 	ftm_reset_counter(priv->clkevt_base);
194 
195 	err = setup_irq(irq, &ftm_timer_irq);
196 	if (err) {
197 		pr_err("ftm: setup irq failed: %d\n", err);
198 		return err;
199 	}
200 
201 	ftm_clockevent.cpumask = cpumask_of(0);
202 	ftm_clockevent.irq = irq;
203 
204 	clockevents_config_and_register(&ftm_clockevent,
205 					freq / (1 << priv->ps),
206 					1, 0xffff);
207 
208 	ftm_counter_enable(priv->clkevt_base);
209 
210 	return 0;
211 }
212 
213 static int __init ftm_clocksource_init(unsigned long freq)
214 {
215 	int err;
216 
217 	ftm_writel(0x00, priv->clksrc_base + FTM_CNTIN);
218 	ftm_writel(~0u, priv->clksrc_base + FTM_MOD);
219 
220 	ftm_reset_counter(priv->clksrc_base);
221 
222 	sched_clock_register(ftm_read_sched_clock, 16, freq / (1 << priv->ps));
223 	err = clocksource_mmio_init(priv->clksrc_base + FTM_CNT, "fsl-ftm",
224 				    freq / (1 << priv->ps), 300, 16,
225 				    clocksource_mmio_readl_up);
226 	if (err) {
227 		pr_err("ftm: init clock source mmio failed: %d\n", err);
228 		return err;
229 	}
230 
231 	ftm_counter_enable(priv->clksrc_base);
232 
233 	return 0;
234 }
235 
236 static int __init __ftm_clk_init(struct device_node *np, char *cnt_name,
237 				 char *ftm_name)
238 {
239 	struct clk *clk;
240 	int err;
241 
242 	clk = of_clk_get_by_name(np, cnt_name);
243 	if (IS_ERR(clk)) {
244 		pr_err("ftm: Cannot get \"%s\": %ld\n", cnt_name, PTR_ERR(clk));
245 		return PTR_ERR(clk);
246 	}
247 	err = clk_prepare_enable(clk);
248 	if (err) {
249 		pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n",
250 			cnt_name, err);
251 		return err;
252 	}
253 
254 	clk = of_clk_get_by_name(np, ftm_name);
255 	if (IS_ERR(clk)) {
256 		pr_err("ftm: Cannot get \"%s\": %ld\n", ftm_name, PTR_ERR(clk));
257 		return PTR_ERR(clk);
258 	}
259 	err = clk_prepare_enable(clk);
260 	if (err)
261 		pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n",
262 			ftm_name, err);
263 
264 	return clk_get_rate(clk);
265 }
266 
267 static unsigned long __init ftm_clk_init(struct device_node *np)
268 {
269 	long freq;
270 
271 	freq = __ftm_clk_init(np, "ftm-evt-counter-en", "ftm-evt");
272 	if (freq <= 0)
273 		return 0;
274 
275 	freq = __ftm_clk_init(np, "ftm-src-counter-en", "ftm-src");
276 	if (freq <= 0)
277 		return 0;
278 
279 	return freq;
280 }
281 
282 static int __init ftm_calc_closest_round_cyc(unsigned long freq)
283 {
284 	priv->ps = 0;
285 
286 	/* The counter register is only using the lower 16 bits, and
287 	 * if the 'freq' value is to big here, then the periodic_cyc
288 	 * may exceed 0xFFFF.
289 	 */
290 	do {
291 		priv->periodic_cyc = DIV_ROUND_CLOSEST(freq,
292 						HZ * (1 << priv->ps++));
293 	} while (priv->periodic_cyc > 0xFFFF);
294 
295 	if (priv->ps > FTM_PS_MAX) {
296 		pr_err("ftm: the prescaler is %lu > %d\n",
297 				priv->ps, FTM_PS_MAX);
298 		return -EINVAL;
299 	}
300 
301 	return 0;
302 }
303 
304 static int __init ftm_timer_init(struct device_node *np)
305 {
306 	unsigned long freq;
307 	int ret, irq;
308 
309 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
310 	if (!priv)
311 		return -ENOMEM;
312 
313 	ret = -ENXIO;
314 	priv->clkevt_base = of_iomap(np, 0);
315 	if (!priv->clkevt_base) {
316 		pr_err("ftm: unable to map event timer registers\n");
317 		goto err_clkevt;
318 	}
319 
320 	priv->clksrc_base = of_iomap(np, 1);
321 	if (!priv->clksrc_base) {
322 		pr_err("ftm: unable to map source timer registers\n");
323 		goto err_clksrc;
324 	}
325 
326 	ret = -EINVAL;
327 	irq = irq_of_parse_and_map(np, 0);
328 	if (irq <= 0) {
329 		pr_err("ftm: unable to get IRQ from DT, %d\n", irq);
330 		goto err;
331 	}
332 
333 	priv->big_endian = of_property_read_bool(np, "big-endian");
334 
335 	freq = ftm_clk_init(np);
336 	if (!freq)
337 		goto err;
338 
339 	ret = ftm_calc_closest_round_cyc(freq);
340 	if (ret)
341 		goto err;
342 
343 	ret = ftm_clocksource_init(freq);
344 	if (ret)
345 		goto err;
346 
347 	ret = ftm_clockevent_init(freq, irq);
348 	if (ret)
349 		goto err;
350 
351 	return 0;
352 
353 err:
354 	iounmap(priv->clksrc_base);
355 err_clksrc:
356 	iounmap(priv->clkevt_base);
357 err_clkevt:
358 	kfree(priv);
359 	return ret;
360 }
361 TIMER_OF_DECLARE(flextimer, "fsl,ftm-timer", ftm_timer_init);
362