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