xref: /OK3568_Linux_fs/kernel/drivers/clocksource/timer-microchip-pit64b.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * 64-bit Periodic Interval Timer driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2019 Microchip Technology Inc. and its subsidiaries
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/clk.h>
11*4882a593Smuzhiyun #include <linux/clockchips.h>
12*4882a593Smuzhiyun #include <linux/interrupt.h>
13*4882a593Smuzhiyun #include <linux/of_address.h>
14*4882a593Smuzhiyun #include <linux/of_irq.h>
15*4882a593Smuzhiyun #include <linux/sched_clock.h>
16*4882a593Smuzhiyun #include <linux/slab.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #define MCHP_PIT64B_CR			0x00	/* Control Register */
19*4882a593Smuzhiyun #define MCHP_PIT64B_CR_START		BIT(0)
20*4882a593Smuzhiyun #define MCHP_PIT64B_CR_SWRST		BIT(8)
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun #define MCHP_PIT64B_MR			0x04	/* Mode Register */
23*4882a593Smuzhiyun #define MCHP_PIT64B_MR_CONT		BIT(0)
24*4882a593Smuzhiyun #define MCHP_PIT64B_MR_ONE_SHOT		(0)
25*4882a593Smuzhiyun #define MCHP_PIT64B_MR_SGCLK		BIT(3)
26*4882a593Smuzhiyun #define MCHP_PIT64B_MR_PRES		GENMASK(11, 8)
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun #define MCHP_PIT64B_LSB_PR		0x08	/* LSB Period Register */
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun #define MCHP_PIT64B_MSB_PR		0x0C	/* MSB Period Register */
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun #define MCHP_PIT64B_IER			0x10	/* Interrupt Enable Register */
33*4882a593Smuzhiyun #define MCHP_PIT64B_IER_PERIOD		BIT(0)
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun #define MCHP_PIT64B_ISR			0x1C	/* Interrupt Status Register */
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun #define MCHP_PIT64B_TLSBR		0x20	/* Timer LSB Register */
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun #define MCHP_PIT64B_TMSBR		0x24	/* Timer MSB Register */
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun #define MCHP_PIT64B_PRES_MAX		0x10
42*4882a593Smuzhiyun #define MCHP_PIT64B_LSBMASK		GENMASK_ULL(31, 0)
43*4882a593Smuzhiyun #define MCHP_PIT64B_PRES_TO_MODE(p)	(MCHP_PIT64B_MR_PRES & ((p) << 8))
44*4882a593Smuzhiyun #define MCHP_PIT64B_MODE_TO_PRES(m)	((MCHP_PIT64B_MR_PRES & (m)) >> 8)
45*4882a593Smuzhiyun #define MCHP_PIT64B_DEF_CS_FREQ		5000000UL	/* 5 MHz */
46*4882a593Smuzhiyun #define MCHP_PIT64B_DEF_CE_FREQ		32768		/* 32 KHz */
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun #define MCHP_PIT64B_NAME		"pit64b"
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun /**
51*4882a593Smuzhiyun  * struct mchp_pit64b_timer - PIT64B timer data structure
52*4882a593Smuzhiyun  * @base: base address of PIT64B hardware block
53*4882a593Smuzhiyun  * @pclk: PIT64B's peripheral clock
54*4882a593Smuzhiyun  * @gclk: PIT64B's generic clock
55*4882a593Smuzhiyun  * @mode: precomputed value for mode register
56*4882a593Smuzhiyun  */
57*4882a593Smuzhiyun struct mchp_pit64b_timer {
58*4882a593Smuzhiyun 	void __iomem	*base;
59*4882a593Smuzhiyun 	struct clk	*pclk;
60*4882a593Smuzhiyun 	struct clk	*gclk;
61*4882a593Smuzhiyun 	u32		mode;
62*4882a593Smuzhiyun };
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun /**
65*4882a593Smuzhiyun  * mchp_pit64b_clkevt - PIT64B clockevent data structure
66*4882a593Smuzhiyun  * @timer: PIT64B timer
67*4882a593Smuzhiyun  * @clkevt: clockevent
68*4882a593Smuzhiyun  */
69*4882a593Smuzhiyun struct mchp_pit64b_clkevt {
70*4882a593Smuzhiyun 	struct mchp_pit64b_timer	timer;
71*4882a593Smuzhiyun 	struct clock_event_device	clkevt;
72*4882a593Smuzhiyun };
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun #define to_mchp_pit64b_timer(x) \
75*4882a593Smuzhiyun 	((struct mchp_pit64b_timer *)container_of(x,\
76*4882a593Smuzhiyun 		struct mchp_pit64b_clkevt, clkevt))
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun /* Base address for clocksource timer. */
79*4882a593Smuzhiyun static void __iomem *mchp_pit64b_cs_base;
80*4882a593Smuzhiyun /* Default cycles for clockevent timer. */
81*4882a593Smuzhiyun static u64 mchp_pit64b_ce_cycles;
82*4882a593Smuzhiyun 
mchp_pit64b_cnt_read(void __iomem * base)83*4882a593Smuzhiyun static inline u64 mchp_pit64b_cnt_read(void __iomem *base)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun 	unsigned long	flags;
86*4882a593Smuzhiyun 	u32		low, high;
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	raw_local_irq_save(flags);
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	/*
91*4882a593Smuzhiyun 	 * When using a 64 bit period TLSB must be read first, followed by the
92*4882a593Smuzhiyun 	 * read of TMSB. This sequence generates an atomic read of the 64 bit
93*4882a593Smuzhiyun 	 * timer value whatever the lapse of time between the accesses.
94*4882a593Smuzhiyun 	 */
95*4882a593Smuzhiyun 	low = readl_relaxed(base + MCHP_PIT64B_TLSBR);
96*4882a593Smuzhiyun 	high = readl_relaxed(base + MCHP_PIT64B_TMSBR);
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	raw_local_irq_restore(flags);
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	return (((u64)high << 32) | low);
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun 
mchp_pit64b_reset(struct mchp_pit64b_timer * timer,u64 cycles,u32 mode,u32 irqs)103*4882a593Smuzhiyun static inline void mchp_pit64b_reset(struct mchp_pit64b_timer *timer,
104*4882a593Smuzhiyun 				     u64 cycles, u32 mode, u32 irqs)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun 	u32 low, high;
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	low = cycles & MCHP_PIT64B_LSBMASK;
109*4882a593Smuzhiyun 	high = cycles >> 32;
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR);
112*4882a593Smuzhiyun 	writel_relaxed(mode | timer->mode, timer->base + MCHP_PIT64B_MR);
113*4882a593Smuzhiyun 	writel_relaxed(high, timer->base + MCHP_PIT64B_MSB_PR);
114*4882a593Smuzhiyun 	writel_relaxed(low, timer->base + MCHP_PIT64B_LSB_PR);
115*4882a593Smuzhiyun 	writel_relaxed(irqs, timer->base + MCHP_PIT64B_IER);
116*4882a593Smuzhiyun 	writel_relaxed(MCHP_PIT64B_CR_START, timer->base + MCHP_PIT64B_CR);
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun 
mchp_pit64b_clksrc_read(struct clocksource * cs)119*4882a593Smuzhiyun static u64 mchp_pit64b_clksrc_read(struct clocksource *cs)
120*4882a593Smuzhiyun {
121*4882a593Smuzhiyun 	return mchp_pit64b_cnt_read(mchp_pit64b_cs_base);
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun 
mchp_pit64b_sched_read_clk(void)124*4882a593Smuzhiyun static u64 notrace mchp_pit64b_sched_read_clk(void)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun 	return mchp_pit64b_cnt_read(mchp_pit64b_cs_base);
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun 
mchp_pit64b_clkevt_shutdown(struct clock_event_device * cedev)129*4882a593Smuzhiyun static int mchp_pit64b_clkevt_shutdown(struct clock_event_device *cedev)
130*4882a593Smuzhiyun {
131*4882a593Smuzhiyun 	struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev);
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR);
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	return 0;
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun 
mchp_pit64b_clkevt_set_periodic(struct clock_event_device * cedev)138*4882a593Smuzhiyun static int mchp_pit64b_clkevt_set_periodic(struct clock_event_device *cedev)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun 	struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev);
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	mchp_pit64b_reset(timer, mchp_pit64b_ce_cycles, MCHP_PIT64B_MR_CONT,
143*4882a593Smuzhiyun 			  MCHP_PIT64B_IER_PERIOD);
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	return 0;
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun 
mchp_pit64b_clkevt_set_next_event(unsigned long evt,struct clock_event_device * cedev)148*4882a593Smuzhiyun static int mchp_pit64b_clkevt_set_next_event(unsigned long evt,
149*4882a593Smuzhiyun 					     struct clock_event_device *cedev)
150*4882a593Smuzhiyun {
151*4882a593Smuzhiyun 	struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev);
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	mchp_pit64b_reset(timer, evt, MCHP_PIT64B_MR_ONE_SHOT,
154*4882a593Smuzhiyun 			  MCHP_PIT64B_IER_PERIOD);
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	return 0;
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun 
mchp_pit64b_clkevt_suspend(struct clock_event_device * cedev)159*4882a593Smuzhiyun static void mchp_pit64b_clkevt_suspend(struct clock_event_device *cedev)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun 	struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev);
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR);
164*4882a593Smuzhiyun 	if (timer->mode & MCHP_PIT64B_MR_SGCLK)
165*4882a593Smuzhiyun 		clk_disable_unprepare(timer->gclk);
166*4882a593Smuzhiyun 	clk_disable_unprepare(timer->pclk);
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun 
mchp_pit64b_clkevt_resume(struct clock_event_device * cedev)169*4882a593Smuzhiyun static void mchp_pit64b_clkevt_resume(struct clock_event_device *cedev)
170*4882a593Smuzhiyun {
171*4882a593Smuzhiyun 	struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev);
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	clk_prepare_enable(timer->pclk);
174*4882a593Smuzhiyun 	if (timer->mode & MCHP_PIT64B_MR_SGCLK)
175*4882a593Smuzhiyun 		clk_prepare_enable(timer->gclk);
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun 
mchp_pit64b_interrupt(int irq,void * dev_id)178*4882a593Smuzhiyun static irqreturn_t mchp_pit64b_interrupt(int irq, void *dev_id)
179*4882a593Smuzhiyun {
180*4882a593Smuzhiyun 	struct mchp_pit64b_clkevt *irq_data = dev_id;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	/* Need to clear the interrupt. */
183*4882a593Smuzhiyun 	readl_relaxed(irq_data->timer.base + MCHP_PIT64B_ISR);
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	irq_data->clkevt.event_handler(&irq_data->clkevt);
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	return IRQ_HANDLED;
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun 
mchp_pit64b_pres_compute(u32 * pres,u32 clk_rate,u32 max_rate)190*4882a593Smuzhiyun static void __init mchp_pit64b_pres_compute(u32 *pres, u32 clk_rate,
191*4882a593Smuzhiyun 					    u32 max_rate)
192*4882a593Smuzhiyun {
193*4882a593Smuzhiyun 	u32 tmp;
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	for (*pres = 0; *pres < MCHP_PIT64B_PRES_MAX; (*pres)++) {
196*4882a593Smuzhiyun 		tmp = clk_rate / (*pres + 1);
197*4882a593Smuzhiyun 		if (tmp <= max_rate)
198*4882a593Smuzhiyun 			break;
199*4882a593Smuzhiyun 	}
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	/* Use the bigest prescaler if we didn't match one. */
202*4882a593Smuzhiyun 	if (*pres == MCHP_PIT64B_PRES_MAX)
203*4882a593Smuzhiyun 		*pres = MCHP_PIT64B_PRES_MAX - 1;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun /**
207*4882a593Smuzhiyun  * mchp_pit64b_init_mode - prepare PIT64B mode register value to be used at
208*4882a593Smuzhiyun  *			   runtime; this includes prescaler and SGCLK bit
209*4882a593Smuzhiyun  *
210*4882a593Smuzhiyun  * PIT64B timer may be fed by gclk or pclk. When gclk is used its rate has to
211*4882a593Smuzhiyun  * be at least 3 times lower that pclk's rate. pclk rate is fixed, gclk rate
212*4882a593Smuzhiyun  * could be changed via clock APIs. The chosen clock (pclk or gclk) could be
213*4882a593Smuzhiyun  * divided by the internal PIT64B's divider.
214*4882a593Smuzhiyun  *
215*4882a593Smuzhiyun  * This function, first tries to use GCLK by requesting the desired rate from
216*4882a593Smuzhiyun  * PMC and then using the internal PIT64B prescaler, if any, to reach the
217*4882a593Smuzhiyun  * requested rate. If PCLK/GCLK < 3 (condition requested by PIT64B hardware)
218*4882a593Smuzhiyun  * then the function falls back on using PCLK as clock source for PIT64B timer
219*4882a593Smuzhiyun  * choosing the highest prescaler in case it doesn't locate one to match the
220*4882a593Smuzhiyun  * requested frequency.
221*4882a593Smuzhiyun  *
222*4882a593Smuzhiyun  * Below is presented the PIT64B block in relation with PMC:
223*4882a593Smuzhiyun  *
224*4882a593Smuzhiyun  *                                PIT64B
225*4882a593Smuzhiyun  *  PMC             +------------------------------------+
226*4882a593Smuzhiyun  * +----+           |   +-----+                          |
227*4882a593Smuzhiyun  * |    |-->gclk -->|-->|     |    +---------+  +-----+  |
228*4882a593Smuzhiyun  * |    |           |   | MUX |--->| Divider |->|timer|  |
229*4882a593Smuzhiyun  * |    |-->pclk -->|-->|     |    +---------+  +-----+  |
230*4882a593Smuzhiyun  * +----+           |   +-----+                          |
231*4882a593Smuzhiyun  *                  |      ^                             |
232*4882a593Smuzhiyun  *                  |     sel                            |
233*4882a593Smuzhiyun  *                  +------------------------------------+
234*4882a593Smuzhiyun  *
235*4882a593Smuzhiyun  * Where:
236*4882a593Smuzhiyun  *	- gclk rate <= pclk rate/3
237*4882a593Smuzhiyun  *	- gclk rate could be requested from PMC
238*4882a593Smuzhiyun  *	- pclk rate is fixed (cannot be requested from PMC)
239*4882a593Smuzhiyun  */
mchp_pit64b_init_mode(struct mchp_pit64b_timer * timer,unsigned long max_rate)240*4882a593Smuzhiyun static int __init mchp_pit64b_init_mode(struct mchp_pit64b_timer *timer,
241*4882a593Smuzhiyun 					unsigned long max_rate)
242*4882a593Smuzhiyun {
243*4882a593Smuzhiyun 	unsigned long pclk_rate, diff = 0, best_diff = ULONG_MAX;
244*4882a593Smuzhiyun 	long gclk_round = 0;
245*4882a593Smuzhiyun 	u32 pres, best_pres = 0;
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	pclk_rate = clk_get_rate(timer->pclk);
248*4882a593Smuzhiyun 	if (!pclk_rate)
249*4882a593Smuzhiyun 		return -EINVAL;
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	timer->mode = 0;
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 	/* Try using GCLK. */
254*4882a593Smuzhiyun 	gclk_round = clk_round_rate(timer->gclk, max_rate);
255*4882a593Smuzhiyun 	if (gclk_round < 0)
256*4882a593Smuzhiyun 		goto pclk;
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	if (pclk_rate / gclk_round < 3)
259*4882a593Smuzhiyun 		goto pclk;
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	mchp_pit64b_pres_compute(&pres, gclk_round, max_rate);
262*4882a593Smuzhiyun 	best_diff = abs(gclk_round / (pres + 1) - max_rate);
263*4882a593Smuzhiyun 	best_pres = pres;
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	if (!best_diff) {
266*4882a593Smuzhiyun 		timer->mode |= MCHP_PIT64B_MR_SGCLK;
267*4882a593Smuzhiyun 		clk_set_rate(timer->gclk, gclk_round);
268*4882a593Smuzhiyun 		goto done;
269*4882a593Smuzhiyun 	}
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun pclk:
272*4882a593Smuzhiyun 	/* Check if requested rate could be obtained using PCLK. */
273*4882a593Smuzhiyun 	mchp_pit64b_pres_compute(&pres, pclk_rate, max_rate);
274*4882a593Smuzhiyun 	diff = abs(pclk_rate / (pres + 1) - max_rate);
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun 	if (best_diff > diff) {
277*4882a593Smuzhiyun 		/* Use PCLK. */
278*4882a593Smuzhiyun 		best_pres = pres;
279*4882a593Smuzhiyun 	} else {
280*4882a593Smuzhiyun 		/* Use GCLK. */
281*4882a593Smuzhiyun 		timer->mode |= MCHP_PIT64B_MR_SGCLK;
282*4882a593Smuzhiyun 		clk_set_rate(timer->gclk, gclk_round);
283*4882a593Smuzhiyun 	}
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun done:
286*4882a593Smuzhiyun 	timer->mode |= MCHP_PIT64B_PRES_TO_MODE(best_pres);
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 	pr_info("PIT64B: using clk=%s with prescaler %u, freq=%lu [Hz]\n",
289*4882a593Smuzhiyun 		timer->mode & MCHP_PIT64B_MR_SGCLK ? "gclk" : "pclk", best_pres,
290*4882a593Smuzhiyun 		timer->mode & MCHP_PIT64B_MR_SGCLK ?
291*4882a593Smuzhiyun 		gclk_round / (best_pres + 1) : pclk_rate / (best_pres + 1));
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	return 0;
294*4882a593Smuzhiyun }
295*4882a593Smuzhiyun 
mchp_pit64b_init_clksrc(struct mchp_pit64b_timer * timer,u32 clk_rate)296*4882a593Smuzhiyun static int __init mchp_pit64b_init_clksrc(struct mchp_pit64b_timer *timer,
297*4882a593Smuzhiyun 					  u32 clk_rate)
298*4882a593Smuzhiyun {
299*4882a593Smuzhiyun 	int ret;
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	mchp_pit64b_reset(timer, ULLONG_MAX, MCHP_PIT64B_MR_CONT, 0);
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun 	mchp_pit64b_cs_base = timer->base;
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	ret = clocksource_mmio_init(timer->base, MCHP_PIT64B_NAME, clk_rate,
306*4882a593Smuzhiyun 				    210, 64, mchp_pit64b_clksrc_read);
307*4882a593Smuzhiyun 	if (ret) {
308*4882a593Smuzhiyun 		pr_debug("clksrc: Failed to register PIT64B clocksource!\n");
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 		/* Stop timer. */
311*4882a593Smuzhiyun 		writel_relaxed(MCHP_PIT64B_CR_SWRST,
312*4882a593Smuzhiyun 			       timer->base + MCHP_PIT64B_CR);
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 		return ret;
315*4882a593Smuzhiyun 	}
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 	sched_clock_register(mchp_pit64b_sched_read_clk, 64, clk_rate);
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	return 0;
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun 
mchp_pit64b_init_clkevt(struct mchp_pit64b_timer * timer,u32 clk_rate,u32 irq)322*4882a593Smuzhiyun static int __init mchp_pit64b_init_clkevt(struct mchp_pit64b_timer *timer,
323*4882a593Smuzhiyun 					  u32 clk_rate, u32 irq)
324*4882a593Smuzhiyun {
325*4882a593Smuzhiyun 	struct mchp_pit64b_clkevt *ce;
326*4882a593Smuzhiyun 	int ret;
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun 	ce = kzalloc(sizeof(*ce), GFP_KERNEL);
329*4882a593Smuzhiyun 	if (!ce)
330*4882a593Smuzhiyun 		return -ENOMEM;
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 	mchp_pit64b_ce_cycles = DIV_ROUND_CLOSEST(clk_rate, HZ);
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 	ce->timer.base = timer->base;
335*4882a593Smuzhiyun 	ce->timer.pclk = timer->pclk;
336*4882a593Smuzhiyun 	ce->timer.gclk = timer->gclk;
337*4882a593Smuzhiyun 	ce->timer.mode = timer->mode;
338*4882a593Smuzhiyun 	ce->clkevt.name = MCHP_PIT64B_NAME;
339*4882a593Smuzhiyun 	ce->clkevt.features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC;
340*4882a593Smuzhiyun 	ce->clkevt.rating = 150;
341*4882a593Smuzhiyun 	ce->clkevt.set_state_shutdown = mchp_pit64b_clkevt_shutdown;
342*4882a593Smuzhiyun 	ce->clkevt.set_state_periodic = mchp_pit64b_clkevt_set_periodic;
343*4882a593Smuzhiyun 	ce->clkevt.set_next_event = mchp_pit64b_clkevt_set_next_event;
344*4882a593Smuzhiyun 	ce->clkevt.suspend = mchp_pit64b_clkevt_suspend;
345*4882a593Smuzhiyun 	ce->clkevt.resume = mchp_pit64b_clkevt_resume;
346*4882a593Smuzhiyun 	ce->clkevt.cpumask = cpumask_of(0);
347*4882a593Smuzhiyun 	ce->clkevt.irq = irq;
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	ret = request_irq(irq, mchp_pit64b_interrupt, IRQF_TIMER,
350*4882a593Smuzhiyun 			  "pit64b_tick", ce);
351*4882a593Smuzhiyun 	if (ret) {
352*4882a593Smuzhiyun 		pr_debug("clkevt: Failed to setup PIT64B IRQ\n");
353*4882a593Smuzhiyun 		kfree(ce);
354*4882a593Smuzhiyun 		return ret;
355*4882a593Smuzhiyun 	}
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun 	clockevents_config_and_register(&ce->clkevt, clk_rate, 1, ULONG_MAX);
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	return 0;
360*4882a593Smuzhiyun }
361*4882a593Smuzhiyun 
mchp_pit64b_dt_init_timer(struct device_node * node,bool clkevt)362*4882a593Smuzhiyun static int __init mchp_pit64b_dt_init_timer(struct device_node *node,
363*4882a593Smuzhiyun 					    bool clkevt)
364*4882a593Smuzhiyun {
365*4882a593Smuzhiyun 	u32 freq = clkevt ? MCHP_PIT64B_DEF_CE_FREQ : MCHP_PIT64B_DEF_CS_FREQ;
366*4882a593Smuzhiyun 	struct mchp_pit64b_timer timer;
367*4882a593Smuzhiyun 	unsigned long clk_rate;
368*4882a593Smuzhiyun 	u32 irq = 0;
369*4882a593Smuzhiyun 	int ret;
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	/* Parse DT node. */
372*4882a593Smuzhiyun 	timer.pclk = of_clk_get_by_name(node, "pclk");
373*4882a593Smuzhiyun 	if (IS_ERR(timer.pclk))
374*4882a593Smuzhiyun 		return PTR_ERR(timer.pclk);
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	timer.gclk = of_clk_get_by_name(node, "gclk");
377*4882a593Smuzhiyun 	if (IS_ERR(timer.gclk))
378*4882a593Smuzhiyun 		return PTR_ERR(timer.gclk);
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun 	timer.base = of_iomap(node, 0);
381*4882a593Smuzhiyun 	if (!timer.base)
382*4882a593Smuzhiyun 		return -ENXIO;
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 	if (clkevt) {
385*4882a593Smuzhiyun 		irq = irq_of_parse_and_map(node, 0);
386*4882a593Smuzhiyun 		if (!irq) {
387*4882a593Smuzhiyun 			ret = -ENODEV;
388*4882a593Smuzhiyun 			goto io_unmap;
389*4882a593Smuzhiyun 		}
390*4882a593Smuzhiyun 	}
391*4882a593Smuzhiyun 
392*4882a593Smuzhiyun 	/* Initialize mode (prescaler + SGCK bit). To be used at runtime. */
393*4882a593Smuzhiyun 	ret = mchp_pit64b_init_mode(&timer, freq);
394*4882a593Smuzhiyun 	if (ret)
395*4882a593Smuzhiyun 		goto irq_unmap;
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	ret = clk_prepare_enable(timer.pclk);
398*4882a593Smuzhiyun 	if (ret)
399*4882a593Smuzhiyun 		goto irq_unmap;
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	if (timer.mode & MCHP_PIT64B_MR_SGCLK) {
402*4882a593Smuzhiyun 		ret = clk_prepare_enable(timer.gclk);
403*4882a593Smuzhiyun 		if (ret)
404*4882a593Smuzhiyun 			goto pclk_unprepare;
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 		clk_rate = clk_get_rate(timer.gclk);
407*4882a593Smuzhiyun 	} else {
408*4882a593Smuzhiyun 		clk_rate = clk_get_rate(timer.pclk);
409*4882a593Smuzhiyun 	}
410*4882a593Smuzhiyun 	clk_rate = clk_rate / (MCHP_PIT64B_MODE_TO_PRES(timer.mode) + 1);
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	if (clkevt)
413*4882a593Smuzhiyun 		ret = mchp_pit64b_init_clkevt(&timer, clk_rate, irq);
414*4882a593Smuzhiyun 	else
415*4882a593Smuzhiyun 		ret = mchp_pit64b_init_clksrc(&timer, clk_rate);
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 	if (ret)
418*4882a593Smuzhiyun 		goto gclk_unprepare;
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	return 0;
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun gclk_unprepare:
423*4882a593Smuzhiyun 	if (timer.mode & MCHP_PIT64B_MR_SGCLK)
424*4882a593Smuzhiyun 		clk_disable_unprepare(timer.gclk);
425*4882a593Smuzhiyun pclk_unprepare:
426*4882a593Smuzhiyun 	clk_disable_unprepare(timer.pclk);
427*4882a593Smuzhiyun irq_unmap:
428*4882a593Smuzhiyun 	irq_dispose_mapping(irq);
429*4882a593Smuzhiyun io_unmap:
430*4882a593Smuzhiyun 	iounmap(timer.base);
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	return ret;
433*4882a593Smuzhiyun }
434*4882a593Smuzhiyun 
mchp_pit64b_dt_init(struct device_node * node)435*4882a593Smuzhiyun static int __init mchp_pit64b_dt_init(struct device_node *node)
436*4882a593Smuzhiyun {
437*4882a593Smuzhiyun 	static int inits;
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 	switch (inits++) {
440*4882a593Smuzhiyun 	case 0:
441*4882a593Smuzhiyun 		/* 1st request, register clockevent. */
442*4882a593Smuzhiyun 		return mchp_pit64b_dt_init_timer(node, true);
443*4882a593Smuzhiyun 	case 1:
444*4882a593Smuzhiyun 		/* 2nd request, register clocksource. */
445*4882a593Smuzhiyun 		return mchp_pit64b_dt_init_timer(node, false);
446*4882a593Smuzhiyun 	}
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 	/* The rest, don't care. */
449*4882a593Smuzhiyun 	return -EINVAL;
450*4882a593Smuzhiyun }
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun TIMER_OF_DECLARE(mchp_pit64b, "microchip,sam9x60-pit64b", mchp_pit64b_dt_init);
453