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