Linux Audio

Check our new training course

Loading...
v6.8
  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
 22struct 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
 30static struct ftm_clock_device *priv;
 31
 32static 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
 40static 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
 48static 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
 59static 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
 69static 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
 78static 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
 87static 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
 96static 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
106static u64 notrace ftm_read_sched_clock(void)
107{
108	return ftm_readl(priv->clksrc_base + FTM_CNT);
109}
110
111static 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 disabled.
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
141static int ftm_set_oneshot(struct clock_event_device *evt)
142{
143	ftm_counter_disable(priv->clkevt_base);
144	return 0;
145}
146
147static int ftm_set_periodic(struct clock_event_device *evt)
148{
149	ftm_set_next_event(priv->periodic_cyc, evt);
150	return 0;
151}
152
153static 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
169static 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
179static int __init ftm_clockevent_init(unsigned long freq, int irq)
180{
181	int err;
182
183	ftm_writel(0x00, priv->clkevt_base + FTM_CNTIN);
184	ftm_writel(~0u, priv->clkevt_base + FTM_MOD);
185
186	ftm_reset_counter(priv->clkevt_base);
187
188	err = request_irq(irq, ftm_evt_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
189			  "Freescale ftm timer", &ftm_clockevent);
190	if (err) {
191		pr_err("ftm: setup irq failed: %d\n", err);
192		return err;
193	}
194
195	ftm_clockevent.cpumask = cpumask_of(0);
196	ftm_clockevent.irq = irq;
197
198	clockevents_config_and_register(&ftm_clockevent,
199					freq / (1 << priv->ps),
200					1, 0xffff);
201
202	ftm_counter_enable(priv->clkevt_base);
203
204	return 0;
205}
206
207static int __init ftm_clocksource_init(unsigned long freq)
208{
209	int err;
210
211	ftm_writel(0x00, priv->clksrc_base + FTM_CNTIN);
212	ftm_writel(~0u, priv->clksrc_base + FTM_MOD);
213
214	ftm_reset_counter(priv->clksrc_base);
215
216	sched_clock_register(ftm_read_sched_clock, 16, freq / (1 << priv->ps));
217	err = clocksource_mmio_init(priv->clksrc_base + FTM_CNT, "fsl-ftm",
218				    freq / (1 << priv->ps), 300, 16,
219				    clocksource_mmio_readl_up);
220	if (err) {
221		pr_err("ftm: init clock source mmio failed: %d\n", err);
222		return err;
223	}
224
225	ftm_counter_enable(priv->clksrc_base);
226
227	return 0;
228}
229
230static int __init __ftm_clk_init(struct device_node *np, char *cnt_name,
231				 char *ftm_name)
232{
233	struct clk *clk;
234	int err;
235
236	clk = of_clk_get_by_name(np, cnt_name);
237	if (IS_ERR(clk)) {
238		pr_err("ftm: Cannot get \"%s\": %ld\n", cnt_name, PTR_ERR(clk));
239		return PTR_ERR(clk);
240	}
241	err = clk_prepare_enable(clk);
242	if (err) {
243		pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n",
244			cnt_name, err);
245		return err;
246	}
247
248	clk = of_clk_get_by_name(np, ftm_name);
249	if (IS_ERR(clk)) {
250		pr_err("ftm: Cannot get \"%s\": %ld\n", ftm_name, PTR_ERR(clk));
251		return PTR_ERR(clk);
252	}
253	err = clk_prepare_enable(clk);
254	if (err)
255		pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n",
256			ftm_name, err);
257
258	return clk_get_rate(clk);
259}
260
261static unsigned long __init ftm_clk_init(struct device_node *np)
262{
263	long freq;
264
265	freq = __ftm_clk_init(np, "ftm-evt-counter-en", "ftm-evt");
266	if (freq <= 0)
267		return 0;
268
269	freq = __ftm_clk_init(np, "ftm-src-counter-en", "ftm-src");
270	if (freq <= 0)
271		return 0;
272
273	return freq;
274}
275
276static int __init ftm_calc_closest_round_cyc(unsigned long freq)
277{
278	priv->ps = 0;
279
280	/* The counter register is only using the lower 16 bits, and
281	 * if the 'freq' value is to big here, then the periodic_cyc
282	 * may exceed 0xFFFF.
283	 */
284	do {
285		priv->periodic_cyc = DIV_ROUND_CLOSEST(freq,
286						HZ * (1 << priv->ps++));
287	} while (priv->periodic_cyc > 0xFFFF);
288
289	if (priv->ps > FTM_PS_MAX) {
290		pr_err("ftm: the prescaler is %lu > %d\n",
291				priv->ps, FTM_PS_MAX);
292		return -EINVAL;
293	}
294
295	return 0;
296}
297
298static int __init ftm_timer_init(struct device_node *np)
299{
300	unsigned long freq;
301	int ret, irq;
302
303	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
304	if (!priv)
305		return -ENOMEM;
306
307	ret = -ENXIO;
308	priv->clkevt_base = of_iomap(np, 0);
309	if (!priv->clkevt_base) {
310		pr_err("ftm: unable to map event timer registers\n");
311		goto err_clkevt;
312	}
313
314	priv->clksrc_base = of_iomap(np, 1);
315	if (!priv->clksrc_base) {
316		pr_err("ftm: unable to map source timer registers\n");
317		goto err_clksrc;
318	}
319
320	ret = -EINVAL;
321	irq = irq_of_parse_and_map(np, 0);
322	if (irq <= 0) {
323		pr_err("ftm: unable to get IRQ from DT, %d\n", irq);
324		goto err;
325	}
326
327	priv->big_endian = of_property_read_bool(np, "big-endian");
328
329	freq = ftm_clk_init(np);
330	if (!freq)
331		goto err;
332
333	ret = ftm_calc_closest_round_cyc(freq);
334	if (ret)
335		goto err;
336
337	ret = ftm_clocksource_init(freq);
338	if (ret)
339		goto err;
340
341	ret = ftm_clockevent_init(freq, irq);
342	if (ret)
343		goto err;
344
345	return 0;
346
347err:
348	iounmap(priv->clksrc_base);
349err_clksrc:
350	iounmap(priv->clkevt_base);
351err_clkevt:
352	kfree(priv);
353	return ret;
354}
355TIMER_OF_DECLARE(flextimer, "fsl,ftm-timer", ftm_timer_init);
v6.13.7
  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
 22struct 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
 30static struct ftm_clock_device *priv;
 31
 32static 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
 40static 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
 48static 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
 59static 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
 69static 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
 78static 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
 87static 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
 96static 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
106static u64 notrace ftm_read_sched_clock(void)
107{
108	return ftm_readl(priv->clksrc_base + FTM_CNT);
109}
110
111static 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 disabled.
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
141static int ftm_set_oneshot(struct clock_event_device *evt)
142{
143	ftm_counter_disable(priv->clkevt_base);
144	return 0;
145}
146
147static int ftm_set_periodic(struct clock_event_device *evt)
148{
149	ftm_set_next_event(priv->periodic_cyc, evt);
150	return 0;
151}
152
153static 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
169static 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
179static int __init ftm_clockevent_init(unsigned long freq, int irq)
180{
181	int err;
182
183	ftm_writel(0x00, priv->clkevt_base + FTM_CNTIN);
184	ftm_writel(~0u, priv->clkevt_base + FTM_MOD);
185
186	ftm_reset_counter(priv->clkevt_base);
187
188	err = request_irq(irq, ftm_evt_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
189			  "Freescale ftm timer", &ftm_clockevent);
190	if (err) {
191		pr_err("ftm: setup irq failed: %d\n", err);
192		return err;
193	}
194
195	ftm_clockevent.cpumask = cpumask_of(0);
196	ftm_clockevent.irq = irq;
197
198	clockevents_config_and_register(&ftm_clockevent,
199					freq / (1 << priv->ps),
200					1, 0xffff);
201
202	ftm_counter_enable(priv->clkevt_base);
203
204	return 0;
205}
206
207static int __init ftm_clocksource_init(unsigned long freq)
208{
209	int err;
210
211	ftm_writel(0x00, priv->clksrc_base + FTM_CNTIN);
212	ftm_writel(~0u, priv->clksrc_base + FTM_MOD);
213
214	ftm_reset_counter(priv->clksrc_base);
215
216	sched_clock_register(ftm_read_sched_clock, 16, freq / (1 << priv->ps));
217	err = clocksource_mmio_init(priv->clksrc_base + FTM_CNT, "fsl-ftm",
218				    freq / (1 << priv->ps), 300, 16,
219				    clocksource_mmio_readl_up);
220	if (err) {
221		pr_err("ftm: init clock source mmio failed: %d\n", err);
222		return err;
223	}
224
225	ftm_counter_enable(priv->clksrc_base);
226
227	return 0;
228}
229
230static int __init __ftm_clk_init(struct device_node *np, char *cnt_name,
231				 char *ftm_name)
232{
233	struct clk *clk;
234	int err;
235
236	clk = of_clk_get_by_name(np, cnt_name);
237	if (IS_ERR(clk)) {
238		pr_err("ftm: Cannot get \"%s\": %ld\n", cnt_name, PTR_ERR(clk));
239		return PTR_ERR(clk);
240	}
241	err = clk_prepare_enable(clk);
242	if (err) {
243		pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n",
244			cnt_name, err);
245		return err;
246	}
247
248	clk = of_clk_get_by_name(np, ftm_name);
249	if (IS_ERR(clk)) {
250		pr_err("ftm: Cannot get \"%s\": %ld\n", ftm_name, PTR_ERR(clk));
251		return PTR_ERR(clk);
252	}
253	err = clk_prepare_enable(clk);
254	if (err)
255		pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n",
256			ftm_name, err);
257
258	return clk_get_rate(clk);
259}
260
261static unsigned long __init ftm_clk_init(struct device_node *np)
262{
263	long freq;
264
265	freq = __ftm_clk_init(np, "ftm-evt-counter-en", "ftm-evt");
266	if (freq <= 0)
267		return 0;
268
269	freq = __ftm_clk_init(np, "ftm-src-counter-en", "ftm-src");
270	if (freq <= 0)
271		return 0;
272
273	return freq;
274}
275
276static int __init ftm_calc_closest_round_cyc(unsigned long freq)
277{
278	priv->ps = 0;
279
280	/* The counter register is only using the lower 16 bits, and
281	 * if the 'freq' value is to big here, then the periodic_cyc
282	 * may exceed 0xFFFF.
283	 */
284	do {
285		priv->periodic_cyc = DIV_ROUND_CLOSEST(freq,
286						HZ * (1 << priv->ps++));
287	} while (priv->periodic_cyc > 0xFFFF);
288
289	if (priv->ps > FTM_PS_MAX) {
290		pr_err("ftm: the prescaler is %lu > %d\n",
291				priv->ps, FTM_PS_MAX);
292		return -EINVAL;
293	}
294
295	return 0;
296}
297
298static int __init ftm_timer_init(struct device_node *np)
299{
300	unsigned long freq;
301	int ret, irq;
302
303	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
304	if (!priv)
305		return -ENOMEM;
306
307	ret = -ENXIO;
308	priv->clkevt_base = of_iomap(np, 0);
309	if (!priv->clkevt_base) {
310		pr_err("ftm: unable to map event timer registers\n");
311		goto err_clkevt;
312	}
313
314	priv->clksrc_base = of_iomap(np, 1);
315	if (!priv->clksrc_base) {
316		pr_err("ftm: unable to map source timer registers\n");
317		goto err_clksrc;
318	}
319
320	ret = -EINVAL;
321	irq = irq_of_parse_and_map(np, 0);
322	if (irq <= 0) {
323		pr_err("ftm: unable to get IRQ from DT, %d\n", irq);
324		goto err;
325	}
326
327	priv->big_endian = of_property_read_bool(np, "big-endian");
328
329	freq = ftm_clk_init(np);
330	if (!freq)
331		goto err;
332
333	ret = ftm_calc_closest_round_cyc(freq);
334	if (ret)
335		goto err;
336
337	ret = ftm_clocksource_init(freq);
338	if (ret)
339		goto err;
340
341	ret = ftm_clockevent_init(freq, irq);
342	if (ret)
343		goto err;
344
345	return 0;
346
347err:
348	iounmap(priv->clksrc_base);
349err_clksrc:
350	iounmap(priv->clkevt_base);
351err_clkevt:
352	kfree(priv);
353	return ret;
354}
355TIMER_OF_DECLARE(flextimer, "fsl,ftm-timer", ftm_timer_init);