1
2
3
4
5
6
7
8
9
10#include <linux/clk.h>
11#include <linux/clockchips.h>
12#include <linux/interrupt.h>
13#include <linux/of_address.h>
14#include <linux/of_irq.h>
15#include <linux/sched_clock.h>
16#include <linux/slab.h>
17
18#define MCHP_PIT64B_CR 0x00
19#define MCHP_PIT64B_CR_START BIT(0)
20#define MCHP_PIT64B_CR_SWRST BIT(8)
21
22#define MCHP_PIT64B_MR 0x04
23#define MCHP_PIT64B_MR_CONT BIT(0)
24#define MCHP_PIT64B_MR_ONE_SHOT (0)
25#define MCHP_PIT64B_MR_SGCLK BIT(3)
26#define MCHP_PIT64B_MR_PRES GENMASK(11, 8)
27
28#define MCHP_PIT64B_LSB_PR 0x08
29
30#define MCHP_PIT64B_MSB_PR 0x0C
31
32#define MCHP_PIT64B_IER 0x10
33#define MCHP_PIT64B_IER_PERIOD BIT(0)
34
35#define MCHP_PIT64B_ISR 0x1C
36
37#define MCHP_PIT64B_TLSBR 0x20
38
39#define MCHP_PIT64B_TMSBR 0x24
40
41#define MCHP_PIT64B_PRES_MAX 0x10
42#define MCHP_PIT64B_LSBMASK GENMASK_ULL(31, 0)
43#define MCHP_PIT64B_PRES_TO_MODE(p) (MCHP_PIT64B_MR_PRES & ((p) << 8))
44#define MCHP_PIT64B_MODE_TO_PRES(m) ((MCHP_PIT64B_MR_PRES & (m)) >> 8)
45#define MCHP_PIT64B_DEF_CS_FREQ 5000000UL
46#define MCHP_PIT64B_DEF_CE_FREQ 32768
47
48#define MCHP_PIT64B_NAME "pit64b"
49
50
51
52
53
54
55
56
57struct mchp_pit64b_timer {
58 void __iomem *base;
59 struct clk *pclk;
60 struct clk *gclk;
61 u32 mode;
62};
63
64
65
66
67
68
69struct mchp_pit64b_clkevt {
70 struct mchp_pit64b_timer timer;
71 struct clock_event_device clkevt;
72};
73
74#define clkevt_to_mchp_pit64b_timer(x) \
75 ((struct mchp_pit64b_timer *)container_of(x,\
76 struct mchp_pit64b_clkevt, clkevt))
77
78
79
80
81
82
83struct mchp_pit64b_clksrc {
84 struct mchp_pit64b_timer timer;
85 struct clocksource clksrc;
86};
87
88#define clksrc_to_mchp_pit64b_timer(x) \
89 ((struct mchp_pit64b_timer *)container_of(x,\
90 struct mchp_pit64b_clksrc, clksrc))
91
92
93static void __iomem *mchp_pit64b_cs_base;
94
95static u64 mchp_pit64b_ce_cycles;
96
97static inline u64 mchp_pit64b_cnt_read(void __iomem *base)
98{
99 unsigned long flags;
100 u32 low, high;
101
102 raw_local_irq_save(flags);
103
104
105
106
107
108
109 low = readl_relaxed(base + MCHP_PIT64B_TLSBR);
110 high = readl_relaxed(base + MCHP_PIT64B_TMSBR);
111
112 raw_local_irq_restore(flags);
113
114 return (((u64)high << 32) | low);
115}
116
117static inline void mchp_pit64b_reset(struct mchp_pit64b_timer *timer,
118 u64 cycles, u32 mode, u32 irqs)
119{
120 u32 low, high;
121
122 low = cycles & MCHP_PIT64B_LSBMASK;
123 high = cycles >> 32;
124
125 writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR);
126 writel_relaxed(mode | timer->mode, timer->base + MCHP_PIT64B_MR);
127 writel_relaxed(high, timer->base + MCHP_PIT64B_MSB_PR);
128 writel_relaxed(low, timer->base + MCHP_PIT64B_LSB_PR);
129 writel_relaxed(irqs, timer->base + MCHP_PIT64B_IER);
130 writel_relaxed(MCHP_PIT64B_CR_START, timer->base + MCHP_PIT64B_CR);
131}
132
133static void mchp_pit64b_suspend(struct mchp_pit64b_timer *timer)
134{
135 writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR);
136 if (timer->mode & MCHP_PIT64B_MR_SGCLK)
137 clk_disable_unprepare(timer->gclk);
138 clk_disable_unprepare(timer->pclk);
139}
140
141static void mchp_pit64b_resume(struct mchp_pit64b_timer *timer)
142{
143 clk_prepare_enable(timer->pclk);
144 if (timer->mode & MCHP_PIT64B_MR_SGCLK)
145 clk_prepare_enable(timer->gclk);
146}
147
148static void mchp_pit64b_clksrc_suspend(struct clocksource *cs)
149{
150 struct mchp_pit64b_timer *timer = clksrc_to_mchp_pit64b_timer(cs);
151
152 mchp_pit64b_suspend(timer);
153}
154
155static void mchp_pit64b_clksrc_resume(struct clocksource *cs)
156{
157 struct mchp_pit64b_timer *timer = clksrc_to_mchp_pit64b_timer(cs);
158
159 mchp_pit64b_resume(timer);
160 mchp_pit64b_reset(timer, ULLONG_MAX, MCHP_PIT64B_MR_CONT, 0);
161}
162
163static u64 mchp_pit64b_clksrc_read(struct clocksource *cs)
164{
165 return mchp_pit64b_cnt_read(mchp_pit64b_cs_base);
166}
167
168static u64 mchp_pit64b_sched_read_clk(void)
169{
170 return mchp_pit64b_cnt_read(mchp_pit64b_cs_base);
171}
172
173static int mchp_pit64b_clkevt_shutdown(struct clock_event_device *cedev)
174{
175 struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
176
177 writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR);
178
179 return 0;
180}
181
182static int mchp_pit64b_clkevt_set_periodic(struct clock_event_device *cedev)
183{
184 struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
185
186 mchp_pit64b_reset(timer, mchp_pit64b_ce_cycles, MCHP_PIT64B_MR_CONT,
187 MCHP_PIT64B_IER_PERIOD);
188
189 return 0;
190}
191
192static int mchp_pit64b_clkevt_set_next_event(unsigned long evt,
193 struct clock_event_device *cedev)
194{
195 struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
196
197 mchp_pit64b_reset(timer, evt, MCHP_PIT64B_MR_ONE_SHOT,
198 MCHP_PIT64B_IER_PERIOD);
199
200 return 0;
201}
202
203static void mchp_pit64b_clkevt_suspend(struct clock_event_device *cedev)
204{
205 struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
206
207 mchp_pit64b_suspend(timer);
208}
209
210static void mchp_pit64b_clkevt_resume(struct clock_event_device *cedev)
211{
212 struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
213
214 mchp_pit64b_resume(timer);
215}
216
217static irqreturn_t mchp_pit64b_interrupt(int irq, void *dev_id)
218{
219 struct mchp_pit64b_clkevt *irq_data = dev_id;
220
221
222 readl_relaxed(irq_data->timer.base + MCHP_PIT64B_ISR);
223
224 irq_data->clkevt.event_handler(&irq_data->clkevt);
225
226 return IRQ_HANDLED;
227}
228
229static void __init mchp_pit64b_pres_compute(u32 *pres, u32 clk_rate,
230 u32 max_rate)
231{
232 u32 tmp;
233
234 for (*pres = 0; *pres < MCHP_PIT64B_PRES_MAX; (*pres)++) {
235 tmp = clk_rate / (*pres + 1);
236 if (tmp <= max_rate)
237 break;
238 }
239
240
241 if (*pres == MCHP_PIT64B_PRES_MAX)
242 *pres = MCHP_PIT64B_PRES_MAX - 1;
243}
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279static int __init mchp_pit64b_init_mode(struct mchp_pit64b_timer *timer,
280 unsigned long max_rate)
281{
282 unsigned long pclk_rate, diff = 0, best_diff = ULONG_MAX;
283 long gclk_round = 0;
284 u32 pres, best_pres = 0;
285
286 pclk_rate = clk_get_rate(timer->pclk);
287 if (!pclk_rate)
288 return -EINVAL;
289
290 timer->mode = 0;
291
292
293 gclk_round = clk_round_rate(timer->gclk, max_rate);
294 if (gclk_round < 0)
295 goto pclk;
296
297 if (pclk_rate / gclk_round < 3)
298 goto pclk;
299
300 mchp_pit64b_pres_compute(&pres, gclk_round, max_rate);
301 best_diff = abs(gclk_round / (pres + 1) - max_rate);
302 best_pres = pres;
303
304 if (!best_diff) {
305 timer->mode |= MCHP_PIT64B_MR_SGCLK;
306 clk_set_rate(timer->gclk, gclk_round);
307 goto done;
308 }
309
310pclk:
311
312 mchp_pit64b_pres_compute(&pres, pclk_rate, max_rate);
313 diff = abs(pclk_rate / (pres + 1) - max_rate);
314
315 if (best_diff > diff) {
316
317 best_pres = pres;
318 } else {
319
320 timer->mode |= MCHP_PIT64B_MR_SGCLK;
321 clk_set_rate(timer->gclk, gclk_round);
322 }
323
324done:
325 timer->mode |= MCHP_PIT64B_PRES_TO_MODE(best_pres);
326
327 pr_info("PIT64B: using clk=%s with prescaler %u, freq=%lu [Hz]\n",
328 timer->mode & MCHP_PIT64B_MR_SGCLK ? "gclk" : "pclk", best_pres,
329 timer->mode & MCHP_PIT64B_MR_SGCLK ?
330 gclk_round / (best_pres + 1) : pclk_rate / (best_pres + 1));
331
332 return 0;
333}
334
335static int __init mchp_pit64b_init_clksrc(struct mchp_pit64b_timer *timer,
336 u32 clk_rate)
337{
338 struct mchp_pit64b_clksrc *cs;
339 int ret;
340
341 cs = kzalloc(sizeof(*cs), GFP_KERNEL);
342 if (!cs)
343 return -ENOMEM;
344
345 mchp_pit64b_reset(timer, ULLONG_MAX, MCHP_PIT64B_MR_CONT, 0);
346
347 mchp_pit64b_cs_base = timer->base;
348
349 cs->timer.base = timer->base;
350 cs->timer.pclk = timer->pclk;
351 cs->timer.gclk = timer->gclk;
352 cs->timer.mode = timer->mode;
353 cs->clksrc.name = MCHP_PIT64B_NAME;
354 cs->clksrc.mask = CLOCKSOURCE_MASK(64);
355 cs->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
356 cs->clksrc.rating = 210;
357 cs->clksrc.read = mchp_pit64b_clksrc_read;
358 cs->clksrc.suspend = mchp_pit64b_clksrc_suspend;
359 cs->clksrc.resume = mchp_pit64b_clksrc_resume;
360
361 ret = clocksource_register_hz(&cs->clksrc, clk_rate);
362 if (ret) {
363 pr_debug("clksrc: Failed to register PIT64B clocksource!\n");
364
365
366 writel_relaxed(MCHP_PIT64B_CR_SWRST,
367 timer->base + MCHP_PIT64B_CR);
368 kfree(cs);
369
370 return ret;
371 }
372
373 sched_clock_register(mchp_pit64b_sched_read_clk, 64, clk_rate);
374
375 return 0;
376}
377
378static int __init mchp_pit64b_init_clkevt(struct mchp_pit64b_timer *timer,
379 u32 clk_rate, u32 irq)
380{
381 struct mchp_pit64b_clkevt *ce;
382 int ret;
383
384 ce = kzalloc(sizeof(*ce), GFP_KERNEL);
385 if (!ce)
386 return -ENOMEM;
387
388 mchp_pit64b_ce_cycles = DIV_ROUND_CLOSEST(clk_rate, HZ);
389
390 ce->timer.base = timer->base;
391 ce->timer.pclk = timer->pclk;
392 ce->timer.gclk = timer->gclk;
393 ce->timer.mode = timer->mode;
394 ce->clkevt.name = MCHP_PIT64B_NAME;
395 ce->clkevt.features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC;
396 ce->clkevt.rating = 150;
397 ce->clkevt.set_state_shutdown = mchp_pit64b_clkevt_shutdown;
398 ce->clkevt.set_state_periodic = mchp_pit64b_clkevt_set_periodic;
399 ce->clkevt.set_next_event = mchp_pit64b_clkevt_set_next_event;
400 ce->clkevt.suspend = mchp_pit64b_clkevt_suspend;
401 ce->clkevt.resume = mchp_pit64b_clkevt_resume;
402 ce->clkevt.cpumask = cpumask_of(0);
403 ce->clkevt.irq = irq;
404
405 ret = request_irq(irq, mchp_pit64b_interrupt, IRQF_TIMER,
406 "pit64b_tick", ce);
407 if (ret) {
408 pr_debug("clkevt: Failed to setup PIT64B IRQ\n");
409 kfree(ce);
410 return ret;
411 }
412
413 clockevents_config_and_register(&ce->clkevt, clk_rate, 1, ULONG_MAX);
414
415 return 0;
416}
417
418static int __init mchp_pit64b_dt_init_timer(struct device_node *node,
419 bool clkevt)
420{
421 u32 freq = clkevt ? MCHP_PIT64B_DEF_CE_FREQ : MCHP_PIT64B_DEF_CS_FREQ;
422 struct mchp_pit64b_timer timer;
423 unsigned long clk_rate;
424 u32 irq = 0;
425 int ret;
426
427
428 timer.pclk = of_clk_get_by_name(node, "pclk");
429 if (IS_ERR(timer.pclk))
430 return PTR_ERR(timer.pclk);
431
432 timer.gclk = of_clk_get_by_name(node, "gclk");
433 if (IS_ERR(timer.gclk))
434 return PTR_ERR(timer.gclk);
435
436 timer.base = of_iomap(node, 0);
437 if (!timer.base)
438 return -ENXIO;
439
440 if (clkevt) {
441 irq = irq_of_parse_and_map(node, 0);
442 if (!irq) {
443 ret = -ENODEV;
444 goto io_unmap;
445 }
446 }
447
448
449 ret = mchp_pit64b_init_mode(&timer, freq);
450 if (ret)
451 goto irq_unmap;
452
453 ret = clk_prepare_enable(timer.pclk);
454 if (ret)
455 goto irq_unmap;
456
457 if (timer.mode & MCHP_PIT64B_MR_SGCLK) {
458 ret = clk_prepare_enable(timer.gclk);
459 if (ret)
460 goto pclk_unprepare;
461
462 clk_rate = clk_get_rate(timer.gclk);
463 } else {
464 clk_rate = clk_get_rate(timer.pclk);
465 }
466 clk_rate = clk_rate / (MCHP_PIT64B_MODE_TO_PRES(timer.mode) + 1);
467
468 if (clkevt)
469 ret = mchp_pit64b_init_clkevt(&timer, clk_rate, irq);
470 else
471 ret = mchp_pit64b_init_clksrc(&timer, clk_rate);
472
473 if (ret)
474 goto gclk_unprepare;
475
476 return 0;
477
478gclk_unprepare:
479 if (timer.mode & MCHP_PIT64B_MR_SGCLK)
480 clk_disable_unprepare(timer.gclk);
481pclk_unprepare:
482 clk_disable_unprepare(timer.pclk);
483irq_unmap:
484 irq_dispose_mapping(irq);
485io_unmap:
486 iounmap(timer.base);
487
488 return ret;
489}
490
491static int __init mchp_pit64b_dt_init(struct device_node *node)
492{
493 static int inits;
494
495 switch (inits++) {
496 case 0:
497
498 return mchp_pit64b_dt_init_timer(node, true);
499 case 1:
500
501 return mchp_pit64b_dt_init_timer(node, false);
502 }
503
504
505 return -EINVAL;
506}
507
508TIMER_OF_DECLARE(mchp_pit64b, "microchip,sam9x60-pit64b", mchp_pit64b_dt_init);
509