0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/clk.h>
0011 #include <linux/clockchips.h>
0012 #include <linux/interrupt.h>
0013 #include <linux/of_address.h>
0014 #include <linux/of_irq.h>
0015 #include <linux/sched_clock.h>
0016 #include <linux/slab.h>
0017
0018 #define MCHP_PIT64B_CR 0x00
0019 #define MCHP_PIT64B_CR_START BIT(0)
0020 #define MCHP_PIT64B_CR_SWRST BIT(8)
0021
0022 #define MCHP_PIT64B_MR 0x04
0023 #define MCHP_PIT64B_MR_CONT BIT(0)
0024 #define MCHP_PIT64B_MR_ONE_SHOT (0)
0025 #define MCHP_PIT64B_MR_SGCLK BIT(3)
0026 #define MCHP_PIT64B_MR_PRES GENMASK(11, 8)
0027
0028 #define MCHP_PIT64B_LSB_PR 0x08
0029
0030 #define MCHP_PIT64B_MSB_PR 0x0C
0031
0032 #define MCHP_PIT64B_IER 0x10
0033 #define MCHP_PIT64B_IER_PERIOD BIT(0)
0034
0035 #define MCHP_PIT64B_ISR 0x1C
0036
0037 #define MCHP_PIT64B_TLSBR 0x20
0038
0039 #define MCHP_PIT64B_TMSBR 0x24
0040
0041 #define MCHP_PIT64B_PRES_MAX 0x10
0042 #define MCHP_PIT64B_LSBMASK GENMASK_ULL(31, 0)
0043 #define MCHP_PIT64B_PRES_TO_MODE(p) (MCHP_PIT64B_MR_PRES & ((p) << 8))
0044 #define MCHP_PIT64B_MODE_TO_PRES(m) ((MCHP_PIT64B_MR_PRES & (m)) >> 8)
0045 #define MCHP_PIT64B_DEF_FREQ 5000000UL
0046
0047 #define MCHP_PIT64B_NAME "pit64b"
0048
0049
0050
0051
0052
0053
0054
0055
0056 struct mchp_pit64b_timer {
0057 void __iomem *base;
0058 struct clk *pclk;
0059 struct clk *gclk;
0060 u32 mode;
0061 };
0062
0063
0064
0065
0066
0067
0068 struct mchp_pit64b_clkevt {
0069 struct mchp_pit64b_timer timer;
0070 struct clock_event_device clkevt;
0071 };
0072
0073 #define clkevt_to_mchp_pit64b_timer(x) \
0074 ((struct mchp_pit64b_timer *)container_of(x,\
0075 struct mchp_pit64b_clkevt, clkevt))
0076
0077
0078
0079
0080
0081
0082 struct mchp_pit64b_clksrc {
0083 struct mchp_pit64b_timer timer;
0084 struct clocksource clksrc;
0085 };
0086
0087 #define clksrc_to_mchp_pit64b_timer(x) \
0088 ((struct mchp_pit64b_timer *)container_of(x,\
0089 struct mchp_pit64b_clksrc, clksrc))
0090
0091
0092 static void __iomem *mchp_pit64b_cs_base;
0093
0094 static u64 mchp_pit64b_ce_cycles;
0095
0096 static inline u64 mchp_pit64b_cnt_read(void __iomem *base)
0097 {
0098 unsigned long flags;
0099 u32 low, high;
0100
0101 raw_local_irq_save(flags);
0102
0103
0104
0105
0106
0107
0108 low = readl_relaxed(base + MCHP_PIT64B_TLSBR);
0109 high = readl_relaxed(base + MCHP_PIT64B_TMSBR);
0110
0111 raw_local_irq_restore(flags);
0112
0113 return (((u64)high << 32) | low);
0114 }
0115
0116 static inline void mchp_pit64b_reset(struct mchp_pit64b_timer *timer,
0117 u64 cycles, u32 mode, u32 irqs)
0118 {
0119 u32 low, high;
0120
0121 low = cycles & MCHP_PIT64B_LSBMASK;
0122 high = cycles >> 32;
0123
0124 writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR);
0125 writel_relaxed(mode | timer->mode, timer->base + MCHP_PIT64B_MR);
0126 writel_relaxed(high, timer->base + MCHP_PIT64B_MSB_PR);
0127 writel_relaxed(low, timer->base + MCHP_PIT64B_LSB_PR);
0128 writel_relaxed(irqs, timer->base + MCHP_PIT64B_IER);
0129 writel_relaxed(MCHP_PIT64B_CR_START, timer->base + MCHP_PIT64B_CR);
0130 }
0131
0132 static void mchp_pit64b_suspend(struct mchp_pit64b_timer *timer)
0133 {
0134 writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR);
0135 if (timer->mode & MCHP_PIT64B_MR_SGCLK)
0136 clk_disable_unprepare(timer->gclk);
0137 clk_disable_unprepare(timer->pclk);
0138 }
0139
0140 static void mchp_pit64b_resume(struct mchp_pit64b_timer *timer)
0141 {
0142 clk_prepare_enable(timer->pclk);
0143 if (timer->mode & MCHP_PIT64B_MR_SGCLK)
0144 clk_prepare_enable(timer->gclk);
0145 }
0146
0147 static void mchp_pit64b_clksrc_suspend(struct clocksource *cs)
0148 {
0149 struct mchp_pit64b_timer *timer = clksrc_to_mchp_pit64b_timer(cs);
0150
0151 mchp_pit64b_suspend(timer);
0152 }
0153
0154 static void mchp_pit64b_clksrc_resume(struct clocksource *cs)
0155 {
0156 struct mchp_pit64b_timer *timer = clksrc_to_mchp_pit64b_timer(cs);
0157
0158 mchp_pit64b_resume(timer);
0159 mchp_pit64b_reset(timer, ULLONG_MAX, MCHP_PIT64B_MR_CONT, 0);
0160 }
0161
0162 static u64 mchp_pit64b_clksrc_read(struct clocksource *cs)
0163 {
0164 return mchp_pit64b_cnt_read(mchp_pit64b_cs_base);
0165 }
0166
0167 static u64 notrace mchp_pit64b_sched_read_clk(void)
0168 {
0169 return mchp_pit64b_cnt_read(mchp_pit64b_cs_base);
0170 }
0171
0172 static int mchp_pit64b_clkevt_shutdown(struct clock_event_device *cedev)
0173 {
0174 struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
0175
0176 if (!clockevent_state_detached(cedev))
0177 mchp_pit64b_suspend(timer);
0178
0179 return 0;
0180 }
0181
0182 static int mchp_pit64b_clkevt_set_periodic(struct clock_event_device *cedev)
0183 {
0184 struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
0185
0186 if (clockevent_state_shutdown(cedev))
0187 mchp_pit64b_resume(timer);
0188
0189 mchp_pit64b_reset(timer, mchp_pit64b_ce_cycles, MCHP_PIT64B_MR_CONT,
0190 MCHP_PIT64B_IER_PERIOD);
0191
0192 return 0;
0193 }
0194
0195 static int mchp_pit64b_clkevt_set_oneshot(struct clock_event_device *cedev)
0196 {
0197 struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
0198
0199 if (clockevent_state_shutdown(cedev))
0200 mchp_pit64b_resume(timer);
0201
0202 mchp_pit64b_reset(timer, mchp_pit64b_ce_cycles, MCHP_PIT64B_MR_ONE_SHOT,
0203 MCHP_PIT64B_IER_PERIOD);
0204
0205 return 0;
0206 }
0207
0208 static int mchp_pit64b_clkevt_set_next_event(unsigned long evt,
0209 struct clock_event_device *cedev)
0210 {
0211 struct mchp_pit64b_timer *timer = clkevt_to_mchp_pit64b_timer(cedev);
0212
0213 mchp_pit64b_reset(timer, evt, MCHP_PIT64B_MR_ONE_SHOT,
0214 MCHP_PIT64B_IER_PERIOD);
0215
0216 return 0;
0217 }
0218
0219 static irqreturn_t mchp_pit64b_interrupt(int irq, void *dev_id)
0220 {
0221 struct mchp_pit64b_clkevt *irq_data = dev_id;
0222
0223
0224 readl_relaxed(irq_data->timer.base + MCHP_PIT64B_ISR);
0225
0226 irq_data->clkevt.event_handler(&irq_data->clkevt);
0227
0228 return IRQ_HANDLED;
0229 }
0230
0231 static void __init mchp_pit64b_pres_compute(u32 *pres, u32 clk_rate,
0232 u32 max_rate)
0233 {
0234 u32 tmp;
0235
0236 for (*pres = 0; *pres < MCHP_PIT64B_PRES_MAX; (*pres)++) {
0237 tmp = clk_rate / (*pres + 1);
0238 if (tmp <= max_rate)
0239 break;
0240 }
0241
0242
0243 if (*pres == MCHP_PIT64B_PRES_MAX)
0244 *pres = MCHP_PIT64B_PRES_MAX - 1;
0245 }
0246
0247
0248
0249
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264
0265
0266
0267
0268
0269
0270
0271
0272
0273
0274
0275
0276
0277
0278
0279
0280
0281
0282
0283 static int __init mchp_pit64b_init_mode(struct mchp_pit64b_timer *timer,
0284 unsigned long max_rate)
0285 {
0286 unsigned long pclk_rate, diff = 0, best_diff = ULONG_MAX;
0287 long gclk_round = 0;
0288 u32 pres, best_pres = 0;
0289
0290 pclk_rate = clk_get_rate(timer->pclk);
0291 if (!pclk_rate)
0292 return -EINVAL;
0293
0294 timer->mode = 0;
0295
0296
0297 gclk_round = clk_round_rate(timer->gclk, max_rate);
0298 if (gclk_round < 0)
0299 goto pclk;
0300
0301 if (pclk_rate / gclk_round < 3)
0302 goto pclk;
0303
0304 mchp_pit64b_pres_compute(&pres, gclk_round, max_rate);
0305 best_diff = abs(gclk_round / (pres + 1) - max_rate);
0306 best_pres = pres;
0307
0308 if (!best_diff) {
0309 timer->mode |= MCHP_PIT64B_MR_SGCLK;
0310 clk_set_rate(timer->gclk, gclk_round);
0311 goto done;
0312 }
0313
0314 pclk:
0315
0316 mchp_pit64b_pres_compute(&pres, pclk_rate, max_rate);
0317 diff = abs(pclk_rate / (pres + 1) - max_rate);
0318
0319 if (best_diff > diff) {
0320
0321 best_pres = pres;
0322 } else {
0323
0324 timer->mode |= MCHP_PIT64B_MR_SGCLK;
0325 clk_set_rate(timer->gclk, gclk_round);
0326 }
0327
0328 done:
0329 timer->mode |= MCHP_PIT64B_PRES_TO_MODE(best_pres);
0330
0331 pr_info("PIT64B: using clk=%s with prescaler %u, freq=%lu [Hz]\n",
0332 timer->mode & MCHP_PIT64B_MR_SGCLK ? "gclk" : "pclk", best_pres,
0333 timer->mode & MCHP_PIT64B_MR_SGCLK ?
0334 gclk_round / (best_pres + 1) : pclk_rate / (best_pres + 1));
0335
0336 return 0;
0337 }
0338
0339 static int __init mchp_pit64b_init_clksrc(struct mchp_pit64b_timer *timer,
0340 u32 clk_rate)
0341 {
0342 struct mchp_pit64b_clksrc *cs;
0343 int ret;
0344
0345 cs = kzalloc(sizeof(*cs), GFP_KERNEL);
0346 if (!cs)
0347 return -ENOMEM;
0348
0349 mchp_pit64b_resume(timer);
0350 mchp_pit64b_reset(timer, ULLONG_MAX, MCHP_PIT64B_MR_CONT, 0);
0351
0352 mchp_pit64b_cs_base = timer->base;
0353
0354 cs->timer.base = timer->base;
0355 cs->timer.pclk = timer->pclk;
0356 cs->timer.gclk = timer->gclk;
0357 cs->timer.mode = timer->mode;
0358 cs->clksrc.name = MCHP_PIT64B_NAME;
0359 cs->clksrc.mask = CLOCKSOURCE_MASK(64);
0360 cs->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
0361 cs->clksrc.rating = 210;
0362 cs->clksrc.read = mchp_pit64b_clksrc_read;
0363 cs->clksrc.suspend = mchp_pit64b_clksrc_suspend;
0364 cs->clksrc.resume = mchp_pit64b_clksrc_resume;
0365
0366 ret = clocksource_register_hz(&cs->clksrc, clk_rate);
0367 if (ret) {
0368 pr_debug("clksrc: Failed to register PIT64B clocksource!\n");
0369
0370
0371 mchp_pit64b_suspend(timer);
0372 kfree(cs);
0373
0374 return ret;
0375 }
0376
0377 sched_clock_register(mchp_pit64b_sched_read_clk, 64, clk_rate);
0378
0379 return 0;
0380 }
0381
0382 static int __init mchp_pit64b_init_clkevt(struct mchp_pit64b_timer *timer,
0383 u32 clk_rate, u32 irq)
0384 {
0385 struct mchp_pit64b_clkevt *ce;
0386 int ret;
0387
0388 ce = kzalloc(sizeof(*ce), GFP_KERNEL);
0389 if (!ce)
0390 return -ENOMEM;
0391
0392 mchp_pit64b_ce_cycles = DIV_ROUND_CLOSEST(clk_rate, HZ);
0393
0394 ce->timer.base = timer->base;
0395 ce->timer.pclk = timer->pclk;
0396 ce->timer.gclk = timer->gclk;
0397 ce->timer.mode = timer->mode;
0398 ce->clkevt.name = MCHP_PIT64B_NAME;
0399 ce->clkevt.features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC;
0400 ce->clkevt.rating = 150;
0401 ce->clkevt.set_state_shutdown = mchp_pit64b_clkevt_shutdown;
0402 ce->clkevt.set_state_periodic = mchp_pit64b_clkevt_set_periodic;
0403 ce->clkevt.set_state_oneshot = mchp_pit64b_clkevt_set_oneshot;
0404 ce->clkevt.set_next_event = mchp_pit64b_clkevt_set_next_event;
0405 ce->clkevt.cpumask = cpumask_of(0);
0406 ce->clkevt.irq = irq;
0407
0408 ret = request_irq(irq, mchp_pit64b_interrupt, IRQF_TIMER,
0409 "pit64b_tick", ce);
0410 if (ret) {
0411 pr_debug("clkevt: Failed to setup PIT64B IRQ\n");
0412 kfree(ce);
0413 return ret;
0414 }
0415
0416 clockevents_config_and_register(&ce->clkevt, clk_rate, 1, ULONG_MAX);
0417
0418 return 0;
0419 }
0420
0421 static int __init mchp_pit64b_dt_init_timer(struct device_node *node,
0422 bool clkevt)
0423 {
0424 struct mchp_pit64b_timer timer;
0425 unsigned long clk_rate;
0426 u32 irq = 0;
0427 int ret;
0428
0429
0430 timer.pclk = of_clk_get_by_name(node, "pclk");
0431 if (IS_ERR(timer.pclk))
0432 return PTR_ERR(timer.pclk);
0433
0434 timer.gclk = of_clk_get_by_name(node, "gclk");
0435 if (IS_ERR(timer.gclk))
0436 return PTR_ERR(timer.gclk);
0437
0438 timer.base = of_iomap(node, 0);
0439 if (!timer.base)
0440 return -ENXIO;
0441
0442 if (clkevt) {
0443 irq = irq_of_parse_and_map(node, 0);
0444 if (!irq) {
0445 ret = -ENODEV;
0446 goto io_unmap;
0447 }
0448 }
0449
0450
0451 ret = mchp_pit64b_init_mode(&timer, MCHP_PIT64B_DEF_FREQ);
0452 if (ret)
0453 goto irq_unmap;
0454
0455 if (timer.mode & MCHP_PIT64B_MR_SGCLK)
0456 clk_rate = clk_get_rate(timer.gclk);
0457 else
0458 clk_rate = clk_get_rate(timer.pclk);
0459 clk_rate = clk_rate / (MCHP_PIT64B_MODE_TO_PRES(timer.mode) + 1);
0460
0461 if (clkevt)
0462 ret = mchp_pit64b_init_clkevt(&timer, clk_rate, irq);
0463 else
0464 ret = mchp_pit64b_init_clksrc(&timer, clk_rate);
0465
0466 if (ret)
0467 goto irq_unmap;
0468
0469 return 0;
0470
0471 irq_unmap:
0472 irq_dispose_mapping(irq);
0473 io_unmap:
0474 iounmap(timer.base);
0475
0476 return ret;
0477 }
0478
0479 static int __init mchp_pit64b_dt_init(struct device_node *node)
0480 {
0481 static int inits;
0482
0483 switch (inits++) {
0484 case 0:
0485
0486 return mchp_pit64b_dt_init_timer(node, true);
0487 case 1:
0488
0489 return mchp_pit64b_dt_init_timer(node, false);
0490 }
0491
0492
0493 return -EINVAL;
0494 }
0495
0496 TIMER_OF_DECLARE(mchp_pit64b, "microchip,sam9x60-pit64b", mchp_pit64b_dt_init);