xref: /linux/drivers/clocksource/timer-microchip-pit64b.c (revision 0585244523f0f4de7e4480375e871617a79cab98)
1625022a5SClaudiu Beznea // SPDX-License-Identifier: GPL-2.0
2625022a5SClaudiu Beznea /*
3625022a5SClaudiu Beznea  * 64-bit Periodic Interval Timer driver
4625022a5SClaudiu Beznea  *
5625022a5SClaudiu Beznea  * Copyright (C) 2019 Microchip Technology Inc. and its subsidiaries
6625022a5SClaudiu Beznea  *
7625022a5SClaudiu Beznea  * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
8625022a5SClaudiu Beznea  */
9625022a5SClaudiu Beznea 
10625022a5SClaudiu Beznea #include <linux/clk.h>
11625022a5SClaudiu Beznea #include <linux/clockchips.h>
12625022a5SClaudiu Beznea #include <linux/interrupt.h>
13625022a5SClaudiu Beznea #include <linux/of_address.h>
14625022a5SClaudiu Beznea #include <linux/of_irq.h>
15625022a5SClaudiu Beznea #include <linux/sched_clock.h>
16625022a5SClaudiu Beznea #include <linux/slab.h>
17625022a5SClaudiu Beznea 
18625022a5SClaudiu Beznea #define MCHP_PIT64B_CR			0x00	/* Control Register */
19625022a5SClaudiu Beznea #define MCHP_PIT64B_CR_START		BIT(0)
20625022a5SClaudiu Beznea #define MCHP_PIT64B_CR_SWRST		BIT(8)
21625022a5SClaudiu Beznea 
22625022a5SClaudiu Beznea #define MCHP_PIT64B_MR			0x04	/* Mode Register */
23625022a5SClaudiu Beznea #define MCHP_PIT64B_MR_CONT		BIT(0)
24625022a5SClaudiu Beznea #define MCHP_PIT64B_MR_ONE_SHOT		(0)
25625022a5SClaudiu Beznea #define MCHP_PIT64B_MR_SGCLK		BIT(3)
26625022a5SClaudiu Beznea #define MCHP_PIT64B_MR_PRES		GENMASK(11, 8)
27625022a5SClaudiu Beznea 
28625022a5SClaudiu Beznea #define MCHP_PIT64B_LSB_PR		0x08	/* LSB Period Register */
29625022a5SClaudiu Beznea 
30625022a5SClaudiu Beznea #define MCHP_PIT64B_MSB_PR		0x0C	/* MSB Period Register */
31625022a5SClaudiu Beznea 
32625022a5SClaudiu Beznea #define MCHP_PIT64B_IER			0x10	/* Interrupt Enable Register */
33625022a5SClaudiu Beznea #define MCHP_PIT64B_IER_PERIOD		BIT(0)
34625022a5SClaudiu Beznea 
35625022a5SClaudiu Beznea #define MCHP_PIT64B_ISR			0x1C	/* Interrupt Status Register */
36625022a5SClaudiu Beznea 
37625022a5SClaudiu Beznea #define MCHP_PIT64B_TLSBR		0x20	/* Timer LSB Register */
38625022a5SClaudiu Beznea 
39625022a5SClaudiu Beznea #define MCHP_PIT64B_TMSBR		0x24	/* Timer MSB Register */
40625022a5SClaudiu Beznea 
41625022a5SClaudiu Beznea #define MCHP_PIT64B_PRES_MAX		0x10
42625022a5SClaudiu Beznea #define MCHP_PIT64B_LSBMASK		GENMASK_ULL(31, 0)
43625022a5SClaudiu Beznea #define MCHP_PIT64B_PRES_TO_MODE(p)	(MCHP_PIT64B_MR_PRES & ((p) << 8))
44625022a5SClaudiu Beznea #define MCHP_PIT64B_MODE_TO_PRES(m)	((MCHP_PIT64B_MR_PRES & (m)) >> 8)
45625022a5SClaudiu Beznea #define MCHP_PIT64B_DEF_CS_FREQ		5000000UL	/* 5 MHz */
46625022a5SClaudiu Beznea #define MCHP_PIT64B_DEF_CE_FREQ		32768		/* 32 KHz */
47625022a5SClaudiu Beznea 
48625022a5SClaudiu Beznea #define MCHP_PIT64B_NAME		"pit64b"
49625022a5SClaudiu Beznea 
50625022a5SClaudiu Beznea /**
51625022a5SClaudiu Beznea  * struct mchp_pit64b_timer - PIT64B timer data structure
52625022a5SClaudiu Beznea  * @base: base address of PIT64B hardware block
53625022a5SClaudiu Beznea  * @pclk: PIT64B's peripheral clock
54625022a5SClaudiu Beznea  * @gclk: PIT64B's generic clock
55625022a5SClaudiu Beznea  * @mode: precomputed value for mode register
56625022a5SClaudiu Beznea  */
57625022a5SClaudiu Beznea struct mchp_pit64b_timer {
58625022a5SClaudiu Beznea 	void __iomem	*base;
59625022a5SClaudiu Beznea 	struct clk	*pclk;
60625022a5SClaudiu Beznea 	struct clk	*gclk;
61625022a5SClaudiu Beznea 	u32		mode;
62625022a5SClaudiu Beznea };
63625022a5SClaudiu Beznea 
64625022a5SClaudiu Beznea /**
65625022a5SClaudiu Beznea  * mchp_pit64b_clkevt - PIT64B clockevent data structure
66625022a5SClaudiu Beznea  * @timer: PIT64B timer
67625022a5SClaudiu Beznea  * @clkevt: clockevent
68625022a5SClaudiu Beznea  */
69625022a5SClaudiu Beznea struct mchp_pit64b_clkevt {
70625022a5SClaudiu Beznea 	struct mchp_pit64b_timer	timer;
71625022a5SClaudiu Beznea 	struct clock_event_device	clkevt;
72625022a5SClaudiu Beznea };
73625022a5SClaudiu Beznea 
74625022a5SClaudiu Beznea #define to_mchp_pit64b_timer(x) \
75625022a5SClaudiu Beznea 	((struct mchp_pit64b_timer *)container_of(x,\
76625022a5SClaudiu Beznea 		struct mchp_pit64b_clkevt, clkevt))
77625022a5SClaudiu Beznea 
78625022a5SClaudiu Beznea /* Base address for clocksource timer. */
79625022a5SClaudiu Beznea static void __iomem *mchp_pit64b_cs_base;
80625022a5SClaudiu Beznea /* Default cycles for clockevent timer. */
81625022a5SClaudiu Beznea static u64 mchp_pit64b_ce_cycles;
82625022a5SClaudiu Beznea 
83625022a5SClaudiu Beznea static inline u64 mchp_pit64b_cnt_read(void __iomem *base)
84625022a5SClaudiu Beznea {
85625022a5SClaudiu Beznea 	unsigned long	flags;
86625022a5SClaudiu Beznea 	u32		low, high;
87625022a5SClaudiu Beznea 
88625022a5SClaudiu Beznea 	raw_local_irq_save(flags);
89625022a5SClaudiu Beznea 
90625022a5SClaudiu Beznea 	/*
91625022a5SClaudiu Beznea 	 * When using a 64 bit period TLSB must be read first, followed by the
92625022a5SClaudiu Beznea 	 * read of TMSB. This sequence generates an atomic read of the 64 bit
93625022a5SClaudiu Beznea 	 * timer value whatever the lapse of time between the accesses.
94625022a5SClaudiu Beznea 	 */
95625022a5SClaudiu Beznea 	low = readl_relaxed(base + MCHP_PIT64B_TLSBR);
96625022a5SClaudiu Beznea 	high = readl_relaxed(base + MCHP_PIT64B_TMSBR);
97625022a5SClaudiu Beznea 
98625022a5SClaudiu Beznea 	raw_local_irq_restore(flags);
99625022a5SClaudiu Beznea 
100625022a5SClaudiu Beznea 	return (((u64)high << 32) | low);
101625022a5SClaudiu Beznea }
102625022a5SClaudiu Beznea 
103625022a5SClaudiu Beznea static inline void mchp_pit64b_reset(struct mchp_pit64b_timer *timer,
104625022a5SClaudiu Beznea 				     u64 cycles, u32 mode, u32 irqs)
105625022a5SClaudiu Beznea {
106625022a5SClaudiu Beznea 	u32 low, high;
107625022a5SClaudiu Beznea 
108625022a5SClaudiu Beznea 	low = cycles & MCHP_PIT64B_LSBMASK;
109625022a5SClaudiu Beznea 	high = cycles >> 32;
110625022a5SClaudiu Beznea 
111625022a5SClaudiu Beznea 	writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR);
112625022a5SClaudiu Beznea 	writel_relaxed(mode | timer->mode, timer->base + MCHP_PIT64B_MR);
113625022a5SClaudiu Beznea 	writel_relaxed(high, timer->base + MCHP_PIT64B_MSB_PR);
114625022a5SClaudiu Beznea 	writel_relaxed(low, timer->base + MCHP_PIT64B_LSB_PR);
115625022a5SClaudiu Beznea 	writel_relaxed(irqs, timer->base + MCHP_PIT64B_IER);
116625022a5SClaudiu Beznea 	writel_relaxed(MCHP_PIT64B_CR_START, timer->base + MCHP_PIT64B_CR);
117625022a5SClaudiu Beznea }
118625022a5SClaudiu Beznea 
119625022a5SClaudiu Beznea static u64 mchp_pit64b_clksrc_read(struct clocksource *cs)
120625022a5SClaudiu Beznea {
121625022a5SClaudiu Beznea 	return mchp_pit64b_cnt_read(mchp_pit64b_cs_base);
122625022a5SClaudiu Beznea }
123625022a5SClaudiu Beznea 
124625022a5SClaudiu Beznea static u64 mchp_pit64b_sched_read_clk(void)
125625022a5SClaudiu Beznea {
126625022a5SClaudiu Beznea 	return mchp_pit64b_cnt_read(mchp_pit64b_cs_base);
127625022a5SClaudiu Beznea }
128625022a5SClaudiu Beznea 
129625022a5SClaudiu Beznea static int mchp_pit64b_clkevt_shutdown(struct clock_event_device *cedev)
130625022a5SClaudiu Beznea {
131625022a5SClaudiu Beznea 	struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev);
132625022a5SClaudiu Beznea 
133625022a5SClaudiu Beznea 	writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR);
134625022a5SClaudiu Beznea 
135625022a5SClaudiu Beznea 	return 0;
136625022a5SClaudiu Beznea }
137625022a5SClaudiu Beznea 
138625022a5SClaudiu Beznea static int mchp_pit64b_clkevt_set_periodic(struct clock_event_device *cedev)
139625022a5SClaudiu Beznea {
140625022a5SClaudiu Beznea 	struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev);
141625022a5SClaudiu Beznea 
142625022a5SClaudiu Beznea 	mchp_pit64b_reset(timer, mchp_pit64b_ce_cycles, MCHP_PIT64B_MR_CONT,
143625022a5SClaudiu Beznea 			  MCHP_PIT64B_IER_PERIOD);
144625022a5SClaudiu Beznea 
145625022a5SClaudiu Beznea 	return 0;
146625022a5SClaudiu Beznea }
147625022a5SClaudiu Beznea 
148625022a5SClaudiu Beznea static int mchp_pit64b_clkevt_set_next_event(unsigned long evt,
149625022a5SClaudiu Beznea 					     struct clock_event_device *cedev)
150625022a5SClaudiu Beznea {
151625022a5SClaudiu Beznea 	struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev);
152625022a5SClaudiu Beznea 
153625022a5SClaudiu Beznea 	mchp_pit64b_reset(timer, evt, MCHP_PIT64B_MR_ONE_SHOT,
154625022a5SClaudiu Beznea 			  MCHP_PIT64B_IER_PERIOD);
155625022a5SClaudiu Beznea 
156625022a5SClaudiu Beznea 	return 0;
157625022a5SClaudiu Beznea }
158625022a5SClaudiu Beznea 
159625022a5SClaudiu Beznea static void mchp_pit64b_clkevt_suspend(struct clock_event_device *cedev)
160625022a5SClaudiu Beznea {
161625022a5SClaudiu Beznea 	struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev);
162625022a5SClaudiu Beznea 
163625022a5SClaudiu Beznea 	writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR);
164625022a5SClaudiu Beznea 	if (timer->mode & MCHP_PIT64B_MR_SGCLK)
165625022a5SClaudiu Beznea 		clk_disable_unprepare(timer->gclk);
166625022a5SClaudiu Beznea 	clk_disable_unprepare(timer->pclk);
167625022a5SClaudiu Beznea }
168625022a5SClaudiu Beznea 
169625022a5SClaudiu Beznea static void mchp_pit64b_clkevt_resume(struct clock_event_device *cedev)
170625022a5SClaudiu Beznea {
171625022a5SClaudiu Beznea 	struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev);
172625022a5SClaudiu Beznea 
173625022a5SClaudiu Beznea 	clk_prepare_enable(timer->pclk);
174625022a5SClaudiu Beznea 	if (timer->mode & MCHP_PIT64B_MR_SGCLK)
175625022a5SClaudiu Beznea 		clk_prepare_enable(timer->gclk);
176625022a5SClaudiu Beznea }
177625022a5SClaudiu Beznea 
178625022a5SClaudiu Beznea static irqreturn_t mchp_pit64b_interrupt(int irq, void *dev_id)
179625022a5SClaudiu Beznea {
180625022a5SClaudiu Beznea 	struct mchp_pit64b_clkevt *irq_data = dev_id;
181625022a5SClaudiu Beznea 
182625022a5SClaudiu Beznea 	/* Need to clear the interrupt. */
183625022a5SClaudiu Beznea 	readl_relaxed(irq_data->timer.base + MCHP_PIT64B_ISR);
184625022a5SClaudiu Beznea 
185625022a5SClaudiu Beznea 	irq_data->clkevt.event_handler(&irq_data->clkevt);
186625022a5SClaudiu Beznea 
187625022a5SClaudiu Beznea 	return IRQ_HANDLED;
188625022a5SClaudiu Beznea }
189625022a5SClaudiu Beznea 
190625022a5SClaudiu Beznea static void __init mchp_pit64b_pres_compute(u32 *pres, u32 clk_rate,
191625022a5SClaudiu Beznea 					    u32 max_rate)
192625022a5SClaudiu Beznea {
193625022a5SClaudiu Beznea 	u32 tmp;
194625022a5SClaudiu Beznea 
195625022a5SClaudiu Beznea 	for (*pres = 0; *pres < MCHP_PIT64B_PRES_MAX; (*pres)++) {
196625022a5SClaudiu Beznea 		tmp = clk_rate / (*pres + 1);
197625022a5SClaudiu Beznea 		if (tmp <= max_rate)
198625022a5SClaudiu Beznea 			break;
199625022a5SClaudiu Beznea 	}
200625022a5SClaudiu Beznea 
201625022a5SClaudiu Beznea 	/* Use the bigest prescaler if we didn't match one. */
202625022a5SClaudiu Beznea 	if (*pres == MCHP_PIT64B_PRES_MAX)
203625022a5SClaudiu Beznea 		*pres = MCHP_PIT64B_PRES_MAX - 1;
204625022a5SClaudiu Beznea }
205625022a5SClaudiu Beznea 
206625022a5SClaudiu Beznea /**
207625022a5SClaudiu Beznea  * mchp_pit64b_init_mode - prepare PIT64B mode register value to be used at
208625022a5SClaudiu Beznea  *			   runtime; this includes prescaler and SGCLK bit
209625022a5SClaudiu Beznea  *
210625022a5SClaudiu Beznea  * PIT64B timer may be fed by gclk or pclk. When gclk is used its rate has to
211625022a5SClaudiu Beznea  * be at least 3 times lower that pclk's rate. pclk rate is fixed, gclk rate
212625022a5SClaudiu Beznea  * could be changed via clock APIs. The chosen clock (pclk or gclk) could be
213625022a5SClaudiu Beznea  * divided by the internal PIT64B's divider.
214625022a5SClaudiu Beznea  *
215625022a5SClaudiu Beznea  * This function, first tries to use GCLK by requesting the desired rate from
216625022a5SClaudiu Beznea  * PMC and then using the internal PIT64B prescaler, if any, to reach the
217625022a5SClaudiu Beznea  * requested rate. If PCLK/GCLK < 3 (condition requested by PIT64B hardware)
218625022a5SClaudiu Beznea  * then the function falls back on using PCLK as clock source for PIT64B timer
219625022a5SClaudiu Beznea  * choosing the highest prescaler in case it doesn't locate one to match the
220625022a5SClaudiu Beznea  * requested frequency.
221625022a5SClaudiu Beznea  *
222625022a5SClaudiu Beznea  * Below is presented the PIT64B block in relation with PMC:
223625022a5SClaudiu Beznea  *
224625022a5SClaudiu Beznea  *                                PIT64B
225625022a5SClaudiu Beznea  *  PMC             +------------------------------------+
226625022a5SClaudiu Beznea  * +----+           |   +-----+                          |
227625022a5SClaudiu Beznea  * |    |-->gclk -->|-->|     |    +---------+  +-----+  |
228625022a5SClaudiu Beznea  * |    |           |   | MUX |--->| Divider |->|timer|  |
229625022a5SClaudiu Beznea  * |    |-->pclk -->|-->|     |    +---------+  +-----+  |
230625022a5SClaudiu Beznea  * +----+           |   +-----+                          |
231625022a5SClaudiu Beznea  *                  |      ^                             |
232625022a5SClaudiu Beznea  *                  |     sel                            |
233625022a5SClaudiu Beznea  *                  +------------------------------------+
234625022a5SClaudiu Beznea  *
235625022a5SClaudiu Beznea  * Where:
236625022a5SClaudiu Beznea  *	- gclk rate <= pclk rate/3
237625022a5SClaudiu Beznea  *	- gclk rate could be requested from PMC
238625022a5SClaudiu Beznea  *	- pclk rate is fixed (cannot be requested from PMC)
239625022a5SClaudiu Beznea  */
240625022a5SClaudiu Beznea static int __init mchp_pit64b_init_mode(struct mchp_pit64b_timer *timer,
241625022a5SClaudiu Beznea 					unsigned long max_rate)
242625022a5SClaudiu Beznea {
243625022a5SClaudiu Beznea 	unsigned long pclk_rate, diff = 0, best_diff = ULONG_MAX;
244625022a5SClaudiu Beznea 	long gclk_round = 0;
245625022a5SClaudiu Beznea 	u32 pres, best_pres = 0;
246625022a5SClaudiu Beznea 
247625022a5SClaudiu Beznea 	pclk_rate = clk_get_rate(timer->pclk);
248625022a5SClaudiu Beznea 	if (!pclk_rate)
249625022a5SClaudiu Beznea 		return -EINVAL;
250625022a5SClaudiu Beznea 
251b9c60a74SClaudiu Beznea 	timer->mode = 0;
252b9c60a74SClaudiu Beznea 
253625022a5SClaudiu Beznea 	/* Try using GCLK. */
254625022a5SClaudiu Beznea 	gclk_round = clk_round_rate(timer->gclk, max_rate);
255625022a5SClaudiu Beznea 	if (gclk_round < 0)
256625022a5SClaudiu Beznea 		goto pclk;
257625022a5SClaudiu Beznea 
258625022a5SClaudiu Beznea 	if (pclk_rate / gclk_round < 3)
259625022a5SClaudiu Beznea 		goto pclk;
260625022a5SClaudiu Beznea 
261625022a5SClaudiu Beznea 	mchp_pit64b_pres_compute(&pres, gclk_round, max_rate);
262625022a5SClaudiu Beznea 	best_diff = abs(gclk_round / (pres + 1) - max_rate);
263625022a5SClaudiu Beznea 	best_pres = pres;
264625022a5SClaudiu Beznea 
265625022a5SClaudiu Beznea 	if (!best_diff) {
266625022a5SClaudiu Beznea 		timer->mode |= MCHP_PIT64B_MR_SGCLK;
267*05852445SClaudiu Beznea 		clk_set_rate(timer->gclk, gclk_round);
268625022a5SClaudiu Beznea 		goto done;
269625022a5SClaudiu Beznea 	}
270625022a5SClaudiu Beznea 
271625022a5SClaudiu Beznea pclk:
272625022a5SClaudiu Beznea 	/* Check if requested rate could be obtained using PCLK. */
273625022a5SClaudiu Beznea 	mchp_pit64b_pres_compute(&pres, pclk_rate, max_rate);
274625022a5SClaudiu Beznea 	diff = abs(pclk_rate / (pres + 1) - max_rate);
275625022a5SClaudiu Beznea 
276625022a5SClaudiu Beznea 	if (best_diff > diff) {
277625022a5SClaudiu Beznea 		/* Use PCLK. */
278625022a5SClaudiu Beznea 		best_pres = pres;
279625022a5SClaudiu Beznea 	} else {
280625022a5SClaudiu Beznea 		/* Use GCLK. */
281625022a5SClaudiu Beznea 		timer->mode |= MCHP_PIT64B_MR_SGCLK;
282625022a5SClaudiu Beznea 		clk_set_rate(timer->gclk, gclk_round);
283625022a5SClaudiu Beznea 	}
284625022a5SClaudiu Beznea 
285625022a5SClaudiu Beznea done:
286625022a5SClaudiu Beznea 	timer->mode |= MCHP_PIT64B_PRES_TO_MODE(best_pres);
287625022a5SClaudiu Beznea 
288625022a5SClaudiu Beznea 	pr_info("PIT64B: using clk=%s with prescaler %u, freq=%lu [Hz]\n",
289625022a5SClaudiu Beznea 		timer->mode & MCHP_PIT64B_MR_SGCLK ? "gclk" : "pclk", best_pres,
290625022a5SClaudiu Beznea 		timer->mode & MCHP_PIT64B_MR_SGCLK ?
291625022a5SClaudiu Beznea 		gclk_round / (best_pres + 1) : pclk_rate / (best_pres + 1));
292625022a5SClaudiu Beznea 
293625022a5SClaudiu Beznea 	return 0;
294625022a5SClaudiu Beznea }
295625022a5SClaudiu Beznea 
296625022a5SClaudiu Beznea static int __init mchp_pit64b_init_clksrc(struct mchp_pit64b_timer *timer,
297625022a5SClaudiu Beznea 					  u32 clk_rate)
298625022a5SClaudiu Beznea {
299625022a5SClaudiu Beznea 	int ret;
300625022a5SClaudiu Beznea 
301625022a5SClaudiu Beznea 	mchp_pit64b_reset(timer, ULLONG_MAX, MCHP_PIT64B_MR_CONT, 0);
302625022a5SClaudiu Beznea 
303625022a5SClaudiu Beznea 	mchp_pit64b_cs_base = timer->base;
304625022a5SClaudiu Beznea 
305625022a5SClaudiu Beznea 	ret = clocksource_mmio_init(timer->base, MCHP_PIT64B_NAME, clk_rate,
306625022a5SClaudiu Beznea 				    210, 64, mchp_pit64b_clksrc_read);
307625022a5SClaudiu Beznea 	if (ret) {
308625022a5SClaudiu Beznea 		pr_debug("clksrc: Failed to register PIT64B clocksource!\n");
309625022a5SClaudiu Beznea 
310625022a5SClaudiu Beznea 		/* Stop timer. */
311625022a5SClaudiu Beznea 		writel_relaxed(MCHP_PIT64B_CR_SWRST,
312625022a5SClaudiu Beznea 			       timer->base + MCHP_PIT64B_CR);
313625022a5SClaudiu Beznea 
314625022a5SClaudiu Beznea 		return ret;
315625022a5SClaudiu Beznea 	}
316625022a5SClaudiu Beznea 
317625022a5SClaudiu Beznea 	sched_clock_register(mchp_pit64b_sched_read_clk, 64, clk_rate);
318625022a5SClaudiu Beznea 
319625022a5SClaudiu Beznea 	return 0;
320625022a5SClaudiu Beznea }
321625022a5SClaudiu Beznea 
322625022a5SClaudiu Beznea static int __init mchp_pit64b_init_clkevt(struct mchp_pit64b_timer *timer,
323625022a5SClaudiu Beznea 					  u32 clk_rate, u32 irq)
324625022a5SClaudiu Beznea {
325625022a5SClaudiu Beznea 	struct mchp_pit64b_clkevt *ce;
326625022a5SClaudiu Beznea 	int ret;
327625022a5SClaudiu Beznea 
328625022a5SClaudiu Beznea 	ce = kzalloc(sizeof(*ce), GFP_KERNEL);
329625022a5SClaudiu Beznea 	if (!ce)
330625022a5SClaudiu Beznea 		return -ENOMEM;
331625022a5SClaudiu Beznea 
332625022a5SClaudiu Beznea 	mchp_pit64b_ce_cycles = DIV_ROUND_CLOSEST(clk_rate, HZ);
333625022a5SClaudiu Beznea 
334625022a5SClaudiu Beznea 	ce->timer.base = timer->base;
335625022a5SClaudiu Beznea 	ce->timer.pclk = timer->pclk;
336625022a5SClaudiu Beznea 	ce->timer.gclk = timer->gclk;
337625022a5SClaudiu Beznea 	ce->timer.mode = timer->mode;
338625022a5SClaudiu Beznea 	ce->clkevt.name = MCHP_PIT64B_NAME;
339625022a5SClaudiu Beznea 	ce->clkevt.features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC;
340625022a5SClaudiu Beznea 	ce->clkevt.rating = 150;
341625022a5SClaudiu Beznea 	ce->clkevt.set_state_shutdown = mchp_pit64b_clkevt_shutdown;
342625022a5SClaudiu Beznea 	ce->clkevt.set_state_periodic = mchp_pit64b_clkevt_set_periodic;
343625022a5SClaudiu Beznea 	ce->clkevt.set_next_event = mchp_pit64b_clkevt_set_next_event;
344625022a5SClaudiu Beznea 	ce->clkevt.suspend = mchp_pit64b_clkevt_suspend;
345625022a5SClaudiu Beznea 	ce->clkevt.resume = mchp_pit64b_clkevt_resume;
346625022a5SClaudiu Beznea 	ce->clkevt.cpumask = cpumask_of(0);
347625022a5SClaudiu Beznea 	ce->clkevt.irq = irq;
348625022a5SClaudiu Beznea 
349625022a5SClaudiu Beznea 	ret = request_irq(irq, mchp_pit64b_interrupt, IRQF_TIMER,
350625022a5SClaudiu Beznea 			  "pit64b_tick", ce);
351625022a5SClaudiu Beznea 	if (ret) {
352625022a5SClaudiu Beznea 		pr_debug("clkevt: Failed to setup PIT64B IRQ\n");
353625022a5SClaudiu Beznea 		kfree(ce);
354625022a5SClaudiu Beznea 		return ret;
355625022a5SClaudiu Beznea 	}
356625022a5SClaudiu Beznea 
357625022a5SClaudiu Beznea 	clockevents_config_and_register(&ce->clkevt, clk_rate, 1, ULONG_MAX);
358625022a5SClaudiu Beznea 
359625022a5SClaudiu Beznea 	return 0;
360625022a5SClaudiu Beznea }
361625022a5SClaudiu Beznea 
362625022a5SClaudiu Beznea static int __init mchp_pit64b_dt_init_timer(struct device_node *node,
363625022a5SClaudiu Beznea 					    bool clkevt)
364625022a5SClaudiu Beznea {
365625022a5SClaudiu Beznea 	u32 freq = clkevt ? MCHP_PIT64B_DEF_CE_FREQ : MCHP_PIT64B_DEF_CS_FREQ;
366b9c60a74SClaudiu Beznea 	struct mchp_pit64b_timer timer;
367625022a5SClaudiu Beznea 	unsigned long clk_rate;
368625022a5SClaudiu Beznea 	u32 irq = 0;
369625022a5SClaudiu Beznea 	int ret;
370625022a5SClaudiu Beznea 
371625022a5SClaudiu Beznea 	/* Parse DT node. */
372625022a5SClaudiu Beznea 	timer.pclk = of_clk_get_by_name(node, "pclk");
373625022a5SClaudiu Beznea 	if (IS_ERR(timer.pclk))
374625022a5SClaudiu Beznea 		return PTR_ERR(timer.pclk);
375625022a5SClaudiu Beznea 
376625022a5SClaudiu Beznea 	timer.gclk = of_clk_get_by_name(node, "gclk");
377625022a5SClaudiu Beznea 	if (IS_ERR(timer.gclk))
378625022a5SClaudiu Beznea 		return PTR_ERR(timer.gclk);
379625022a5SClaudiu Beznea 
380625022a5SClaudiu Beznea 	timer.base = of_iomap(node, 0);
381625022a5SClaudiu Beznea 	if (!timer.base)
382625022a5SClaudiu Beznea 		return -ENXIO;
383625022a5SClaudiu Beznea 
384625022a5SClaudiu Beznea 	if (clkevt) {
385625022a5SClaudiu Beznea 		irq = irq_of_parse_and_map(node, 0);
386625022a5SClaudiu Beznea 		if (!irq) {
387625022a5SClaudiu Beznea 			ret = -ENODEV;
388625022a5SClaudiu Beznea 			goto io_unmap;
389625022a5SClaudiu Beznea 		}
390625022a5SClaudiu Beznea 	}
391625022a5SClaudiu Beznea 
392625022a5SClaudiu Beznea 	/* Initialize mode (prescaler + SGCK bit). To be used at runtime. */
393625022a5SClaudiu Beznea 	ret = mchp_pit64b_init_mode(&timer, freq);
394625022a5SClaudiu Beznea 	if (ret)
395625022a5SClaudiu Beznea 		goto irq_unmap;
396625022a5SClaudiu Beznea 
397625022a5SClaudiu Beznea 	ret = clk_prepare_enable(timer.pclk);
398625022a5SClaudiu Beznea 	if (ret)
399625022a5SClaudiu Beznea 		goto irq_unmap;
400625022a5SClaudiu Beznea 
401625022a5SClaudiu Beznea 	if (timer.mode & MCHP_PIT64B_MR_SGCLK) {
402625022a5SClaudiu Beznea 		ret = clk_prepare_enable(timer.gclk);
403625022a5SClaudiu Beznea 		if (ret)
404625022a5SClaudiu Beznea 			goto pclk_unprepare;
405625022a5SClaudiu Beznea 
406625022a5SClaudiu Beznea 		clk_rate = clk_get_rate(timer.gclk);
407625022a5SClaudiu Beznea 	} else {
408625022a5SClaudiu Beznea 		clk_rate = clk_get_rate(timer.pclk);
409625022a5SClaudiu Beznea 	}
410625022a5SClaudiu Beznea 	clk_rate = clk_rate / (MCHP_PIT64B_MODE_TO_PRES(timer.mode) + 1);
411625022a5SClaudiu Beznea 
412625022a5SClaudiu Beznea 	if (clkevt)
413625022a5SClaudiu Beznea 		ret = mchp_pit64b_init_clkevt(&timer, clk_rate, irq);
414625022a5SClaudiu Beznea 	else
415625022a5SClaudiu Beznea 		ret = mchp_pit64b_init_clksrc(&timer, clk_rate);
416625022a5SClaudiu Beznea 
417625022a5SClaudiu Beznea 	if (ret)
418625022a5SClaudiu Beznea 		goto gclk_unprepare;
419625022a5SClaudiu Beznea 
420625022a5SClaudiu Beznea 	return 0;
421625022a5SClaudiu Beznea 
422625022a5SClaudiu Beznea gclk_unprepare:
423625022a5SClaudiu Beznea 	if (timer.mode & MCHP_PIT64B_MR_SGCLK)
424625022a5SClaudiu Beznea 		clk_disable_unprepare(timer.gclk);
425625022a5SClaudiu Beznea pclk_unprepare:
426625022a5SClaudiu Beznea 	clk_disable_unprepare(timer.pclk);
427625022a5SClaudiu Beznea irq_unmap:
428625022a5SClaudiu Beznea 	irq_dispose_mapping(irq);
429625022a5SClaudiu Beznea io_unmap:
430625022a5SClaudiu Beznea 	iounmap(timer.base);
431625022a5SClaudiu Beznea 
432625022a5SClaudiu Beznea 	return ret;
433625022a5SClaudiu Beznea }
434625022a5SClaudiu Beznea 
435625022a5SClaudiu Beznea static int __init mchp_pit64b_dt_init(struct device_node *node)
436625022a5SClaudiu Beznea {
437625022a5SClaudiu Beznea 	static int inits;
438625022a5SClaudiu Beznea 
439625022a5SClaudiu Beznea 	switch (inits++) {
440625022a5SClaudiu Beznea 	case 0:
441625022a5SClaudiu Beznea 		/* 1st request, register clockevent. */
442625022a5SClaudiu Beznea 		return mchp_pit64b_dt_init_timer(node, true);
443625022a5SClaudiu Beznea 	case 1:
444625022a5SClaudiu Beznea 		/* 2nd request, register clocksource. */
445625022a5SClaudiu Beznea 		return mchp_pit64b_dt_init_timer(node, false);
446625022a5SClaudiu Beznea 	}
447625022a5SClaudiu Beznea 
448625022a5SClaudiu Beznea 	/* The rest, don't care. */
449625022a5SClaudiu Beznea 	return -EINVAL;
450625022a5SClaudiu Beznea }
451625022a5SClaudiu Beznea 
452625022a5SClaudiu Beznea TIMER_OF_DECLARE(mchp_pit64b, "microchip,sam9x60-pit64b", mchp_pit64b_dt_init);
453