diff options
Diffstat (limited to 'drivers/clocksource/sh_mtu2.c')
-rw-r--r-- | drivers/clocksource/sh_mtu2.c | 146 |
1 files changed, 44 insertions, 102 deletions
diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 188d4e092efc..3d88698cf2b8 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/ioport.h> | 23 | #include <linux/ioport.h> |
24 | #include <linux/irq.h> | 24 | #include <linux/irq.h> |
25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
26 | #include <linux/of.h> | ||
26 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
27 | #include <linux/pm_domain.h> | 28 | #include <linux/pm_domain.h> |
28 | #include <linux/pm_runtime.h> | 29 | #include <linux/pm_runtime.h> |
@@ -37,7 +38,6 @@ struct sh_mtu2_channel { | |||
37 | unsigned int index; | 38 | unsigned int index; |
38 | 39 | ||
39 | void __iomem *base; | 40 | void __iomem *base; |
40 | int irq; | ||
41 | 41 | ||
42 | struct clock_event_device ced; | 42 | struct clock_event_device ced; |
43 | }; | 43 | }; |
@@ -48,15 +48,14 @@ struct sh_mtu2_device { | |||
48 | void __iomem *mapbase; | 48 | void __iomem *mapbase; |
49 | struct clk *clk; | 49 | struct clk *clk; |
50 | 50 | ||
51 | raw_spinlock_t lock; /* Protect the shared registers */ | ||
52 | |||
51 | struct sh_mtu2_channel *channels; | 53 | struct sh_mtu2_channel *channels; |
52 | unsigned int num_channels; | 54 | unsigned int num_channels; |
53 | 55 | ||
54 | bool legacy; | ||
55 | bool has_clockevent; | 56 | bool has_clockevent; |
56 | }; | 57 | }; |
57 | 58 | ||
58 | static DEFINE_RAW_SPINLOCK(sh_mtu2_lock); | ||
59 | |||
60 | #define TSTR -1 /* shared register */ | 59 | #define TSTR -1 /* shared register */ |
61 | #define TCR 0 /* channel register */ | 60 | #define TCR 0 /* channel register */ |
62 | #define TMDR 1 /* channel register */ | 61 | #define TMDR 1 /* channel register */ |
@@ -162,12 +161,8 @@ static inline unsigned long sh_mtu2_read(struct sh_mtu2_channel *ch, int reg_nr) | |||
162 | { | 161 | { |
163 | unsigned long offs; | 162 | unsigned long offs; |
164 | 163 | ||
165 | if (reg_nr == TSTR) { | 164 | if (reg_nr == TSTR) |
166 | if (ch->mtu->legacy) | 165 | return ioread8(ch->mtu->mapbase + 0x280); |
167 | return ioread8(ch->mtu->mapbase); | ||
168 | else | ||
169 | return ioread8(ch->mtu->mapbase + 0x280); | ||
170 | } | ||
171 | 166 | ||
172 | offs = mtu2_reg_offs[reg_nr]; | 167 | offs = mtu2_reg_offs[reg_nr]; |
173 | 168 | ||
@@ -182,12 +177,8 @@ static inline void sh_mtu2_write(struct sh_mtu2_channel *ch, int reg_nr, | |||
182 | { | 177 | { |
183 | unsigned long offs; | 178 | unsigned long offs; |
184 | 179 | ||
185 | if (reg_nr == TSTR) { | 180 | if (reg_nr == TSTR) |
186 | if (ch->mtu->legacy) | 181 | return iowrite8(value, ch->mtu->mapbase + 0x280); |
187 | return iowrite8(value, ch->mtu->mapbase); | ||
188 | else | ||
189 | return iowrite8(value, ch->mtu->mapbase + 0x280); | ||
190 | } | ||
191 | 182 | ||
192 | offs = mtu2_reg_offs[reg_nr]; | 183 | offs = mtu2_reg_offs[reg_nr]; |
193 | 184 | ||
@@ -202,7 +193,7 @@ static void sh_mtu2_start_stop_ch(struct sh_mtu2_channel *ch, int start) | |||
202 | unsigned long flags, value; | 193 | unsigned long flags, value; |
203 | 194 | ||
204 | /* start stop register shared by multiple timer channels */ | 195 | /* start stop register shared by multiple timer channels */ |
205 | raw_spin_lock_irqsave(&sh_mtu2_lock, flags); | 196 | raw_spin_lock_irqsave(&ch->mtu->lock, flags); |
206 | value = sh_mtu2_read(ch, TSTR); | 197 | value = sh_mtu2_read(ch, TSTR); |
207 | 198 | ||
208 | if (start) | 199 | if (start) |
@@ -211,7 +202,7 @@ static void sh_mtu2_start_stop_ch(struct sh_mtu2_channel *ch, int start) | |||
211 | value &= ~(1 << ch->index); | 202 | value &= ~(1 << ch->index); |
212 | 203 | ||
213 | sh_mtu2_write(ch, TSTR, value); | 204 | sh_mtu2_write(ch, TSTR, value); |
214 | raw_spin_unlock_irqrestore(&sh_mtu2_lock, flags); | 205 | raw_spin_unlock_irqrestore(&ch->mtu->lock, flags); |
215 | } | 206 | } |
216 | 207 | ||
217 | static int sh_mtu2_enable(struct sh_mtu2_channel *ch) | 208 | static int sh_mtu2_enable(struct sh_mtu2_channel *ch) |
@@ -331,7 +322,6 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch, | |||
331 | const char *name) | 322 | const char *name) |
332 | { | 323 | { |
333 | struct clock_event_device *ced = &ch->ced; | 324 | struct clock_event_device *ced = &ch->ced; |
334 | int ret; | ||
335 | 325 | ||
336 | ced->name = name; | 326 | ced->name = name; |
337 | ced->features = CLOCK_EVT_FEAT_PERIODIC; | 327 | ced->features = CLOCK_EVT_FEAT_PERIODIC; |
@@ -344,24 +334,12 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_channel *ch, | |||
344 | dev_info(&ch->mtu->pdev->dev, "ch%u: used for clock events\n", | 334 | dev_info(&ch->mtu->pdev->dev, "ch%u: used for clock events\n", |
345 | ch->index); | 335 | ch->index); |
346 | clockevents_register_device(ced); | 336 | clockevents_register_device(ced); |
347 | |||
348 | ret = request_irq(ch->irq, sh_mtu2_interrupt, | ||
349 | IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, | ||
350 | dev_name(&ch->mtu->pdev->dev), ch); | ||
351 | if (ret) { | ||
352 | dev_err(&ch->mtu->pdev->dev, "ch%u: failed to request irq %d\n", | ||
353 | ch->index, ch->irq); | ||
354 | return; | ||
355 | } | ||
356 | } | 337 | } |
357 | 338 | ||
358 | static int sh_mtu2_register(struct sh_mtu2_channel *ch, const char *name, | 339 | static int sh_mtu2_register(struct sh_mtu2_channel *ch, const char *name) |
359 | bool clockevent) | ||
360 | { | 340 | { |
361 | if (clockevent) { | 341 | ch->mtu->has_clockevent = true; |
362 | ch->mtu->has_clockevent = true; | 342 | sh_mtu2_register_clockevent(ch, name); |
363 | sh_mtu2_register_clockevent(ch, name); | ||
364 | } | ||
365 | 343 | ||
366 | return 0; | 344 | return 0; |
367 | } | 345 | } |
@@ -372,40 +350,32 @@ static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch, unsigned int index, | |||
372 | static const unsigned int channel_offsets[] = { | 350 | static const unsigned int channel_offsets[] = { |
373 | 0x300, 0x380, 0x000, | 351 | 0x300, 0x380, 0x000, |
374 | }; | 352 | }; |
375 | bool clockevent; | 353 | char name[6]; |
354 | int irq; | ||
355 | int ret; | ||
376 | 356 | ||
377 | ch->mtu = mtu; | 357 | ch->mtu = mtu; |
378 | 358 | ||
379 | if (mtu->legacy) { | 359 | sprintf(name, "tgi%ua", index); |
380 | struct sh_timer_config *cfg = mtu->pdev->dev.platform_data; | 360 | irq = platform_get_irq_byname(mtu->pdev, name); |
381 | 361 | if (irq < 0) { | |
382 | clockevent = cfg->clockevent_rating != 0; | ||
383 | |||
384 | ch->irq = platform_get_irq(mtu->pdev, 0); | ||
385 | ch->base = mtu->mapbase - cfg->channel_offset; | ||
386 | ch->index = cfg->timer_bit; | ||
387 | } else { | ||
388 | char name[6]; | ||
389 | |||
390 | clockevent = true; | ||
391 | |||
392 | sprintf(name, "tgi%ua", index); | ||
393 | ch->irq = platform_get_irq_byname(mtu->pdev, name); | ||
394 | ch->base = mtu->mapbase + channel_offsets[index]; | ||
395 | ch->index = index; | ||
396 | } | ||
397 | |||
398 | if (ch->irq < 0) { | ||
399 | /* Skip channels with no declared interrupt. */ | 362 | /* Skip channels with no declared interrupt. */ |
400 | if (!mtu->legacy) | 363 | return 0; |
401 | return 0; | 364 | } |
402 | 365 | ||
403 | dev_err(&mtu->pdev->dev, "ch%u: failed to get irq\n", | 366 | ret = request_irq(irq, sh_mtu2_interrupt, |
404 | ch->index); | 367 | IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, |
405 | return ch->irq; | 368 | dev_name(&ch->mtu->pdev->dev), ch); |
369 | if (ret) { | ||
370 | dev_err(&ch->mtu->pdev->dev, "ch%u: failed to request irq %d\n", | ||
371 | index, irq); | ||
372 | return ret; | ||
406 | } | 373 | } |
407 | 374 | ||
408 | return sh_mtu2_register(ch, dev_name(&mtu->pdev->dev), clockevent); | 375 | ch->base = mtu->mapbase + channel_offsets[index]; |
376 | ch->index = index; | ||
377 | |||
378 | return sh_mtu2_register(ch, dev_name(&mtu->pdev->dev)); | ||
409 | } | 379 | } |
410 | 380 | ||
411 | static int sh_mtu2_map_memory(struct sh_mtu2_device *mtu) | 381 | static int sh_mtu2_map_memory(struct sh_mtu2_device *mtu) |
@@ -422,46 +392,21 @@ static int sh_mtu2_map_memory(struct sh_mtu2_device *mtu) | |||
422 | if (mtu->mapbase == NULL) | 392 | if (mtu->mapbase == NULL) |
423 | return -ENXIO; | 393 | return -ENXIO; |
424 | 394 | ||
425 | /* | ||
426 | * In legacy platform device configuration (with one device per channel) | ||
427 | * the resource points to the channel base address. | ||
428 | */ | ||
429 | if (mtu->legacy) { | ||
430 | struct sh_timer_config *cfg = mtu->pdev->dev.platform_data; | ||
431 | mtu->mapbase += cfg->channel_offset; | ||
432 | } | ||
433 | |||
434 | return 0; | 395 | return 0; |
435 | } | 396 | } |
436 | 397 | ||
437 | static void sh_mtu2_unmap_memory(struct sh_mtu2_device *mtu) | ||
438 | { | ||
439 | if (mtu->legacy) { | ||
440 | struct sh_timer_config *cfg = mtu->pdev->dev.platform_data; | ||
441 | mtu->mapbase -= cfg->channel_offset; | ||
442 | } | ||
443 | |||
444 | iounmap(mtu->mapbase); | ||
445 | } | ||
446 | |||
447 | static int sh_mtu2_setup(struct sh_mtu2_device *mtu, | 398 | static int sh_mtu2_setup(struct sh_mtu2_device *mtu, |
448 | struct platform_device *pdev) | 399 | struct platform_device *pdev) |
449 | { | 400 | { |
450 | struct sh_timer_config *cfg = pdev->dev.platform_data; | ||
451 | const struct platform_device_id *id = pdev->id_entry; | ||
452 | unsigned int i; | 401 | unsigned int i; |
453 | int ret; | 402 | int ret; |
454 | 403 | ||
455 | mtu->pdev = pdev; | 404 | mtu->pdev = pdev; |
456 | mtu->legacy = id->driver_data; | ||
457 | 405 | ||
458 | if (mtu->legacy && !cfg) { | 406 | raw_spin_lock_init(&mtu->lock); |
459 | dev_err(&mtu->pdev->dev, "missing platform data\n"); | ||
460 | return -ENXIO; | ||
461 | } | ||
462 | 407 | ||
463 | /* Get hold of clock. */ | 408 | /* Get hold of clock. */ |
464 | mtu->clk = clk_get(&mtu->pdev->dev, mtu->legacy ? "mtu2_fck" : "fck"); | 409 | mtu->clk = clk_get(&mtu->pdev->dev, "fck"); |
465 | if (IS_ERR(mtu->clk)) { | 410 | if (IS_ERR(mtu->clk)) { |
466 | dev_err(&mtu->pdev->dev, "cannot get clock\n"); | 411 | dev_err(&mtu->pdev->dev, "cannot get clock\n"); |
467 | return PTR_ERR(mtu->clk); | 412 | return PTR_ERR(mtu->clk); |
@@ -479,10 +424,7 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu, | |||
479 | } | 424 | } |
480 | 425 | ||
481 | /* Allocate and setup the channels. */ | 426 | /* Allocate and setup the channels. */ |
482 | if (mtu->legacy) | 427 | mtu->num_channels = 3; |
483 | mtu->num_channels = 1; | ||
484 | else | ||
485 | mtu->num_channels = 3; | ||
486 | 428 | ||
487 | mtu->channels = kzalloc(sizeof(*mtu->channels) * mtu->num_channels, | 429 | mtu->channels = kzalloc(sizeof(*mtu->channels) * mtu->num_channels, |
488 | GFP_KERNEL); | 430 | GFP_KERNEL); |
@@ -491,16 +433,10 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu, | |||
491 | goto err_unmap; | 433 | goto err_unmap; |
492 | } | 434 | } |
493 | 435 | ||
494 | if (mtu->legacy) { | 436 | for (i = 0; i < mtu->num_channels; ++i) { |
495 | ret = sh_mtu2_setup_channel(&mtu->channels[0], 0, mtu); | 437 | ret = sh_mtu2_setup_channel(&mtu->channels[i], i, mtu); |
496 | if (ret < 0) | 438 | if (ret < 0) |
497 | goto err_unmap; | 439 | goto err_unmap; |
498 | } else { | ||
499 | for (i = 0; i < mtu->num_channels; ++i) { | ||
500 | ret = sh_mtu2_setup_channel(&mtu->channels[i], i, mtu); | ||
501 | if (ret < 0) | ||
502 | goto err_unmap; | ||
503 | } | ||
504 | } | 440 | } |
505 | 441 | ||
506 | platform_set_drvdata(pdev, mtu); | 442 | platform_set_drvdata(pdev, mtu); |
@@ -509,7 +445,7 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu, | |||
509 | 445 | ||
510 | err_unmap: | 446 | err_unmap: |
511 | kfree(mtu->channels); | 447 | kfree(mtu->channels); |
512 | sh_mtu2_unmap_memory(mtu); | 448 | iounmap(mtu->mapbase); |
513 | err_clk_unprepare: | 449 | err_clk_unprepare: |
514 | clk_unprepare(mtu->clk); | 450 | clk_unprepare(mtu->clk); |
515 | err_clk_put: | 451 | err_clk_put: |
@@ -560,17 +496,23 @@ static int sh_mtu2_remove(struct platform_device *pdev) | |||
560 | } | 496 | } |
561 | 497 | ||
562 | static const struct platform_device_id sh_mtu2_id_table[] = { | 498 | static const struct platform_device_id sh_mtu2_id_table[] = { |
563 | { "sh_mtu2", 1 }, | ||
564 | { "sh-mtu2", 0 }, | 499 | { "sh-mtu2", 0 }, |
565 | { }, | 500 | { }, |
566 | }; | 501 | }; |
567 | MODULE_DEVICE_TABLE(platform, sh_mtu2_id_table); | 502 | MODULE_DEVICE_TABLE(platform, sh_mtu2_id_table); |
568 | 503 | ||
504 | static const struct of_device_id sh_mtu2_of_table[] __maybe_unused = { | ||
505 | { .compatible = "renesas,mtu2" }, | ||
506 | { } | ||
507 | }; | ||
508 | MODULE_DEVICE_TABLE(of, sh_mtu2_of_table); | ||
509 | |||
569 | static struct platform_driver sh_mtu2_device_driver = { | 510 | static struct platform_driver sh_mtu2_device_driver = { |
570 | .probe = sh_mtu2_probe, | 511 | .probe = sh_mtu2_probe, |
571 | .remove = sh_mtu2_remove, | 512 | .remove = sh_mtu2_remove, |
572 | .driver = { | 513 | .driver = { |
573 | .name = "sh_mtu2", | 514 | .name = "sh_mtu2", |
515 | .of_match_table = of_match_ptr(sh_mtu2_of_table), | ||
574 | }, | 516 | }, |
575 | .id_table = sh_mtu2_id_table, | 517 | .id_table = sh_mtu2_id_table, |
576 | }; | 518 | }; |