aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2014-01-28 06:36:48 -0500
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2014-04-16 06:03:23 -0400
commit8c7f21e6739ad836f30561d641393a8417abdad3 (patch)
tree51058ea9037d14242cf815fca6800146293e6053 /drivers/clocksource
parentf1010ed1a13ea38f495ebfa2fdb1f38b7f87301f (diff)
clocksource: sh_tmu: Add support for multiple channels per device
TMU hardware devices can support multiple channels, with global registers and per-channel registers. The sh_tmu driver currently models the hardware with one Linux device per channel. This model makes it difficult to handle global registers in a clean way. Add support for a new model that uses one Linux device per timer with multiple channels per device. This requires changes to platform data, add new channel configuration fields. Support for the legacy model is kept and will be removed after all platforms switch to the new model. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/sh_tmu.c213
1 files changed, 152 insertions, 61 deletions
diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c
index fec9bedb8f45..0306d31e9f1d 100644
--- a/drivers/clocksource/sh_tmu.c
+++ b/drivers/clocksource/sh_tmu.c
@@ -35,6 +35,12 @@
35#include <linux/pm_domain.h> 35#include <linux/pm_domain.h>
36#include <linux/pm_runtime.h> 36#include <linux/pm_runtime.h>
37 37
38enum sh_tmu_model {
39 SH_TMU_LEGACY,
40 SH_TMU,
41 SH_TMU_SH3,
42};
43
38struct sh_tmu_device; 44struct sh_tmu_device;
39 45
40struct sh_tmu_channel { 46struct sh_tmu_channel {
@@ -58,8 +64,13 @@ struct sh_tmu_device {
58 void __iomem *mapbase; 64 void __iomem *mapbase;
59 struct clk *clk; 65 struct clk *clk;
60 66
67 enum sh_tmu_model model;
68
61 struct sh_tmu_channel *channels; 69 struct sh_tmu_channel *channels;
62 unsigned int num_channels; 70 unsigned int num_channels;
71
72 bool has_clockevent;
73 bool has_clocksource;
63}; 74};
64 75
65static DEFINE_RAW_SPINLOCK(sh_tmu_lock); 76static DEFINE_RAW_SPINLOCK(sh_tmu_lock);
@@ -82,8 +93,16 @@ static inline unsigned long sh_tmu_read(struct sh_tmu_channel *ch, int reg_nr)
82{ 93{
83 unsigned long offs; 94 unsigned long offs;
84 95
85 if (reg_nr == TSTR) 96 if (reg_nr == TSTR) {
86 return ioread8(ch->tmu->mapbase); 97 switch (ch->tmu->model) {
98 case SH_TMU_LEGACY:
99 return ioread8(ch->tmu->mapbase);
100 case SH_TMU_SH3:
101 return ioread8(ch->tmu->mapbase + 2);
102 case SH_TMU:
103 return ioread8(ch->tmu->mapbase + 4);
104 }
105 }
87 106
88 offs = reg_nr << 2; 107 offs = reg_nr << 2;
89 108
@@ -99,8 +118,14 @@ static inline void sh_tmu_write(struct sh_tmu_channel *ch, int reg_nr,
99 unsigned long offs; 118 unsigned long offs;
100 119
101 if (reg_nr == TSTR) { 120 if (reg_nr == TSTR) {
102 iowrite8(value, ch->tmu->mapbase); 121 switch (ch->tmu->model) {
103 return; 122 case SH_TMU_LEGACY:
123 return iowrite8(value, ch->tmu->mapbase);
124 case SH_TMU_SH3:
125 return iowrite8(value, ch->tmu->mapbase + 2);
126 case SH_TMU:
127 return iowrite8(value, ch->tmu->mapbase + 4);
128 }
104 } 129 }
105 130
106 offs = reg_nr << 2; 131 offs = reg_nr << 2;
@@ -435,31 +460,49 @@ static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch,
435static int sh_tmu_register(struct sh_tmu_channel *ch, const char *name, 460static int sh_tmu_register(struct sh_tmu_channel *ch, const char *name,
436 bool clockevent, bool clocksource) 461 bool clockevent, bool clocksource)
437{ 462{
438 if (clockevent) 463 if (clockevent) {
464 ch->tmu->has_clockevent = true;
439 sh_tmu_register_clockevent(ch, name); 465 sh_tmu_register_clockevent(ch, name);
440 else if (clocksource) 466 } else if (clocksource) {
467 ch->tmu->has_clocksource = true;
441 sh_tmu_register_clocksource(ch, name); 468 sh_tmu_register_clocksource(ch, name);
469 }
442 470
443 return 0; 471 return 0;
444} 472}
445 473
446static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, 474static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, unsigned int index,
475 bool clockevent, bool clocksource,
447 struct sh_tmu_device *tmu) 476 struct sh_tmu_device *tmu)
448{ 477{
449 struct sh_timer_config *cfg = tmu->pdev->dev.platform_data; 478 /* Skip unused channels. */
479 if (!clockevent && !clocksource)
480 return 0;
450 481
451 ch->tmu = tmu; 482 ch->tmu = tmu;
452 483
453 /* 484 if (tmu->model == SH_TMU_LEGACY) {
454 * The SH3 variant (SH770x, SH7705, SH7710 and SH7720) maps channel 485 struct sh_timer_config *cfg = tmu->pdev->dev.platform_data;
455 * registers blocks at base + 2 + 12 * index, while all other variants 486
456 * map them at base + 4 + 12 * index. We can compute the index by just 487 /*
457 * dividing by 12, the 2 bytes or 4 bytes offset being hidden by the 488 * The SH3 variant (SH770x, SH7705, SH7710 and SH7720) maps
458 * integer division. 489 * channel registers blocks at base + 2 + 12 * index, while all
459 */ 490 * other variants map them at base + 4 + 12 * index. We can
460 ch->index = cfg->channel_offset / 12; 491 * compute the index by just dividing by 12, the 2 bytes or 4
492 * bytes offset being hidden by the integer division.
493 */
494 ch->index = cfg->channel_offset / 12;
495 ch->base = tmu->mapbase + cfg->channel_offset;
496 } else {
497 ch->index = index;
498
499 if (tmu->model == SH_TMU_SH3)
500 ch->base = tmu->mapbase + 4 + ch->index * 12;
501 else
502 ch->base = tmu->mapbase + 8 + ch->index * 12;
503 }
461 504
462 ch->irq = platform_get_irq(tmu->pdev, 0); 505 ch->irq = platform_get_irq(tmu->pdev, ch->index);
463 if (ch->irq < 0) { 506 if (ch->irq < 0) {
464 dev_err(&tmu->pdev->dev, "ch%u: failed to get irq\n", 507 dev_err(&tmu->pdev->dev, "ch%u: failed to get irq\n",
465 ch->index); 508 ch->index);
@@ -470,88 +513,127 @@ static int sh_tmu_channel_setup(struct sh_tmu_channel *ch,
470 ch->enable_count = 0; 513 ch->enable_count = 0;
471 514
472 return sh_tmu_register(ch, dev_name(&tmu->pdev->dev), 515 return sh_tmu_register(ch, dev_name(&tmu->pdev->dev),
473 cfg->clockevent_rating != 0, 516 clockevent, clocksource);
474 cfg->clocksource_rating != 0);
475} 517}
476 518
477static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) 519static int sh_tmu_map_memory(struct sh_tmu_device *tmu)
478{ 520{
479 struct sh_timer_config *cfg = pdev->dev.platform_data;
480 struct resource *res; 521 struct resource *res;
481 void __iomem *base;
482 int ret;
483 ret = -ENXIO;
484
485 tmu->pdev = pdev;
486
487 if (!cfg) {
488 dev_err(&tmu->pdev->dev, "missing platform data\n");
489 goto err0;
490 }
491
492 platform_set_drvdata(pdev, tmu);
493 522
494 res = platform_get_resource(tmu->pdev, IORESOURCE_MEM, 0); 523 res = platform_get_resource(tmu->pdev, IORESOURCE_MEM, 0);
495 if (!res) { 524 if (!res) {
496 dev_err(&tmu->pdev->dev, "failed to get I/O memory\n"); 525 dev_err(&tmu->pdev->dev, "failed to get I/O memory\n");
497 goto err0; 526 return -ENXIO;
498 } 527 }
499 528
529 tmu->mapbase = ioremap_nocache(res->start, resource_size(res));
530 if (tmu->mapbase == NULL)
531 return -ENXIO;
532
500 /* 533 /*
501 * Map memory, let base point to our channel and mapbase to the 534 * In legacy platform device configuration (with one device per channel)
502 * start/stop shared register. 535 * the resource points to the channel base address.
503 */ 536 */
504 base = ioremap_nocache(res->start, resource_size(res)); 537 if (tmu->model == SH_TMU_LEGACY) {
505 if (base == NULL) { 538 struct sh_timer_config *cfg = tmu->pdev->dev.platform_data;
506 dev_err(&tmu->pdev->dev, "failed to remap I/O memory\n"); 539 tmu->mapbase -= cfg->channel_offset;
507 goto err0;
508 } 540 }
509 541
510 tmu->mapbase = base - cfg->channel_offset; 542 return 0;
543}
511 544
512 /* get hold of clock */ 545static void sh_tmu_unmap_memory(struct sh_tmu_device *tmu)
546{
547 if (tmu->model == SH_TMU_LEGACY) {
548 struct sh_timer_config *cfg = tmu->pdev->dev.platform_data;
549 tmu->mapbase += cfg->channel_offset;
550 }
551
552 iounmap(tmu->mapbase);
553}
554
555static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev)
556{
557 struct sh_timer_config *cfg = pdev->dev.platform_data;
558 const struct platform_device_id *id = pdev->id_entry;
559 unsigned int i;
560 int ret;
561
562 if (!cfg) {
563 dev_err(&tmu->pdev->dev, "missing platform data\n");
564 return -ENXIO;
565 }
566
567 tmu->pdev = pdev;
568 tmu->model = id->driver_data;
569
570 /* Get hold of clock. */
513 tmu->clk = clk_get(&tmu->pdev->dev, "tmu_fck"); 571 tmu->clk = clk_get(&tmu->pdev->dev, "tmu_fck");
514 if (IS_ERR(tmu->clk)) { 572 if (IS_ERR(tmu->clk)) {
515 dev_err(&tmu->pdev->dev, "cannot get clock\n"); 573 dev_err(&tmu->pdev->dev, "cannot get clock\n");
516 ret = PTR_ERR(tmu->clk); 574 return PTR_ERR(tmu->clk);
517 goto err1;
518 } 575 }
519 576
520 ret = clk_prepare(tmu->clk); 577 ret = clk_prepare(tmu->clk);
521 if (ret < 0) 578 if (ret < 0)
522 goto err2; 579 goto err_clk_put;
580
581 /* Map the memory resource. */
582 ret = sh_tmu_map_memory(tmu);
583 if (ret < 0) {
584 dev_err(&tmu->pdev->dev, "failed to remap I/O memory\n");
585 goto err_clk_unprepare;
586 }
523 587
524 tmu->channels = kzalloc(sizeof(*tmu->channels), GFP_KERNEL); 588 /* Allocate and setup the channels. */
589 if (tmu->model == SH_TMU_LEGACY)
590 tmu->num_channels = 1;
591 else
592 tmu->num_channels = hweight8(cfg->channels_mask);
593
594 tmu->channels = kzalloc(sizeof(*tmu->channels) * tmu->num_channels,
595 GFP_KERNEL);
525 if (tmu->channels == NULL) { 596 if (tmu->channels == NULL) {
526 ret = -ENOMEM; 597 ret = -ENOMEM;
527 goto err3; 598 goto err_unmap;
528 } 599 }
529 600
530 tmu->num_channels = 1; 601 if (tmu->model == SH_TMU_LEGACY) {
531 602 ret = sh_tmu_channel_setup(&tmu->channels[0], 0,
532 tmu->channels[0].base = base; 603 cfg->clockevent_rating != 0,
604 cfg->clocksource_rating != 0, tmu);
605 if (ret < 0)
606 goto err_unmap;
607 } else {
608 /*
609 * Use the first channel as a clock event device and the second
610 * channel as a clock source.
611 */
612 for (i = 0; i < tmu->num_channels; ++i) {
613 ret = sh_tmu_channel_setup(&tmu->channels[i], i,
614 i == 0, i == 1, tmu);
615 if (ret < 0)
616 goto err_unmap;
617 }
618 }
533 619
534 ret = sh_tmu_channel_setup(&tmu->channels[0], tmu); 620 platform_set_drvdata(pdev, tmu);
535 if (ret < 0)
536 goto err3;
537 621
538 return 0; 622 return 0;
539 623
540 err3: 624err_unmap:
541 kfree(tmu->channels); 625 kfree(tmu->channels);
626 sh_tmu_unmap_memory(tmu);
627err_clk_unprepare:
542 clk_unprepare(tmu->clk); 628 clk_unprepare(tmu->clk);
543 err2: 629err_clk_put:
544 clk_put(tmu->clk); 630 clk_put(tmu->clk);
545 err1:
546 iounmap(base);
547 err0:
548 return ret; 631 return ret;
549} 632}
550 633
551static int sh_tmu_probe(struct platform_device *pdev) 634static int sh_tmu_probe(struct platform_device *pdev)
552{ 635{
553 struct sh_tmu_device *tmu = platform_get_drvdata(pdev); 636 struct sh_tmu_device *tmu = platform_get_drvdata(pdev);
554 struct sh_timer_config *cfg = pdev->dev.platform_data;
555 int ret; 637 int ret;
556 638
557 if (!is_early_platform_device(pdev)) { 639 if (!is_early_platform_device(pdev)) {
@@ -580,7 +662,7 @@ static int sh_tmu_probe(struct platform_device *pdev)
580 return 0; 662 return 0;
581 663
582 out: 664 out:
583 if (cfg->clockevent_rating || cfg->clocksource_rating) 665 if (tmu->has_clockevent || tmu->has_clocksource)
584 pm_runtime_irq_safe(&pdev->dev); 666 pm_runtime_irq_safe(&pdev->dev);
585 else 667 else
586 pm_runtime_idle(&pdev->dev); 668 pm_runtime_idle(&pdev->dev);
@@ -593,12 +675,21 @@ static int sh_tmu_remove(struct platform_device *pdev)
593 return -EBUSY; /* cannot unregister clockevent and clocksource */ 675 return -EBUSY; /* cannot unregister clockevent and clocksource */
594} 676}
595 677
678static const struct platform_device_id sh_tmu_id_table[] = {
679 { "sh_tmu", SH_TMU_LEGACY },
680 { "sh-tmu", SH_TMU },
681 { "sh-tmu-sh3", SH_TMU_SH3 },
682 { }
683};
684MODULE_DEVICE_TABLE(platform, sh_tmu_id_table);
685
596static struct platform_driver sh_tmu_device_driver = { 686static struct platform_driver sh_tmu_device_driver = {
597 .probe = sh_tmu_probe, 687 .probe = sh_tmu_probe,
598 .remove = sh_tmu_remove, 688 .remove = sh_tmu_remove,
599 .driver = { 689 .driver = {
600 .name = "sh_tmu", 690 .name = "sh_tmu",
601 } 691 },
692 .id_table = sh_tmu_id_table,
602}; 693};
603 694
604static int __init sh_tmu_init(void) 695static int __init sh_tmu_init(void)