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