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