diff options
Diffstat (limited to 'drivers/clocksource/sh_tmu.c')
-rw-r--r-- | drivers/clocksource/sh_tmu.c | 127 |
1 files changed, 54 insertions, 73 deletions
diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 6bd17a8f3dd4..0f665b8f2461 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/ioport.h> | 24 | #include <linux/ioport.h> |
25 | #include <linux/irq.h> | 25 | #include <linux/irq.h> |
26 | #include <linux/module.h> | 26 | #include <linux/module.h> |
27 | #include <linux/of.h> | ||
27 | #include <linux/platform_device.h> | 28 | #include <linux/platform_device.h> |
28 | #include <linux/pm_domain.h> | 29 | #include <linux/pm_domain.h> |
29 | #include <linux/pm_runtime.h> | 30 | #include <linux/pm_runtime.h> |
@@ -32,7 +33,6 @@ | |||
32 | #include <linux/spinlock.h> | 33 | #include <linux/spinlock.h> |
33 | 34 | ||
34 | enum sh_tmu_model { | 35 | enum sh_tmu_model { |
35 | SH_TMU_LEGACY, | ||
36 | SH_TMU, | 36 | SH_TMU, |
37 | SH_TMU_SH3, | 37 | SH_TMU_SH3, |
38 | }; | 38 | }; |
@@ -62,6 +62,8 @@ struct sh_tmu_device { | |||
62 | 62 | ||
63 | enum sh_tmu_model model; | 63 | enum sh_tmu_model model; |
64 | 64 | ||
65 | raw_spinlock_t lock; /* Protect the shared start/stop register */ | ||
66 | |||
65 | struct sh_tmu_channel *channels; | 67 | struct sh_tmu_channel *channels; |
66 | unsigned int num_channels; | 68 | unsigned int num_channels; |
67 | 69 | ||
@@ -69,8 +71,6 @@ struct sh_tmu_device { | |||
69 | bool has_clocksource; | 71 | bool has_clocksource; |
70 | }; | 72 | }; |
71 | 73 | ||
72 | static DEFINE_RAW_SPINLOCK(sh_tmu_lock); | ||
73 | |||
74 | #define TSTR -1 /* shared register */ | 74 | #define TSTR -1 /* shared register */ |
75 | #define TCOR 0 /* channel register */ | 75 | #define TCOR 0 /* channel register */ |
76 | #define TCNT 1 /* channel register */ | 76 | #define TCNT 1 /* channel register */ |
@@ -91,8 +91,6 @@ static inline unsigned long sh_tmu_read(struct sh_tmu_channel *ch, int reg_nr) | |||
91 | 91 | ||
92 | if (reg_nr == TSTR) { | 92 | if (reg_nr == TSTR) { |
93 | switch (ch->tmu->model) { | 93 | switch (ch->tmu->model) { |
94 | case SH_TMU_LEGACY: | ||
95 | return ioread8(ch->tmu->mapbase); | ||
96 | case SH_TMU_SH3: | 94 | case SH_TMU_SH3: |
97 | return ioread8(ch->tmu->mapbase + 2); | 95 | return ioread8(ch->tmu->mapbase + 2); |
98 | case SH_TMU: | 96 | case SH_TMU: |
@@ -115,8 +113,6 @@ static inline void sh_tmu_write(struct sh_tmu_channel *ch, int reg_nr, | |||
115 | 113 | ||
116 | if (reg_nr == TSTR) { | 114 | if (reg_nr == TSTR) { |
117 | switch (ch->tmu->model) { | 115 | switch (ch->tmu->model) { |
118 | case SH_TMU_LEGACY: | ||
119 | return iowrite8(value, ch->tmu->mapbase); | ||
120 | case SH_TMU_SH3: | 116 | case SH_TMU_SH3: |
121 | return iowrite8(value, ch->tmu->mapbase + 2); | 117 | return iowrite8(value, ch->tmu->mapbase + 2); |
122 | case SH_TMU: | 118 | case SH_TMU: |
@@ -137,7 +133,7 @@ static void sh_tmu_start_stop_ch(struct sh_tmu_channel *ch, int start) | |||
137 | unsigned long flags, value; | 133 | unsigned long flags, value; |
138 | 134 | ||
139 | /* start stop register shared by multiple timer channels */ | 135 | /* start stop register shared by multiple timer channels */ |
140 | raw_spin_lock_irqsave(&sh_tmu_lock, flags); | 136 | raw_spin_lock_irqsave(&ch->tmu->lock, flags); |
141 | value = sh_tmu_read(ch, TSTR); | 137 | value = sh_tmu_read(ch, TSTR); |
142 | 138 | ||
143 | if (start) | 139 | if (start) |
@@ -146,7 +142,7 @@ static void sh_tmu_start_stop_ch(struct sh_tmu_channel *ch, int start) | |||
146 | value &= ~(1 << ch->index); | 142 | value &= ~(1 << ch->index); |
147 | 143 | ||
148 | sh_tmu_write(ch, TSTR, value); | 144 | sh_tmu_write(ch, TSTR, value); |
149 | raw_spin_unlock_irqrestore(&sh_tmu_lock, flags); | 145 | raw_spin_unlock_irqrestore(&ch->tmu->lock, flags); |
150 | } | 146 | } |
151 | 147 | ||
152 | static int __sh_tmu_enable(struct sh_tmu_channel *ch) | 148 | static int __sh_tmu_enable(struct sh_tmu_channel *ch) |
@@ -476,27 +472,12 @@ static int sh_tmu_channel_setup(struct sh_tmu_channel *ch, unsigned int index, | |||
476 | return 0; | 472 | return 0; |
477 | 473 | ||
478 | ch->tmu = tmu; | 474 | ch->tmu = tmu; |
475 | ch->index = index; | ||
479 | 476 | ||
480 | if (tmu->model == SH_TMU_LEGACY) { | 477 | if (tmu->model == SH_TMU_SH3) |
481 | struct sh_timer_config *cfg = tmu->pdev->dev.platform_data; | 478 | ch->base = tmu->mapbase + 4 + ch->index * 12; |
482 | 479 | else | |
483 | /* | 480 | ch->base = tmu->mapbase + 8 + ch->index * 12; |
484 | * The SH3 variant (SH770x, SH7705, SH7710 and SH7720) maps | ||
485 | * channel registers blocks at base + 2 + 12 * index, while all | ||
486 | * other variants map them at base + 4 + 12 * index. We can | ||
487 | * compute the index by just dividing by 12, the 2 bytes or 4 | ||
488 | * bytes offset being hidden by the integer division. | ||
489 | */ | ||
490 | ch->index = cfg->channel_offset / 12; | ||
491 | ch->base = tmu->mapbase + cfg->channel_offset; | ||
492 | } else { | ||
493 | ch->index = index; | ||
494 | |||
495 | if (tmu->model == SH_TMU_SH3) | ||
496 | ch->base = tmu->mapbase + 4 + ch->index * 12; | ||
497 | else | ||
498 | ch->base = tmu->mapbase + 8 + ch->index * 12; | ||
499 | } | ||
500 | 481 | ||
501 | ch->irq = platform_get_irq(tmu->pdev, index); | 482 | ch->irq = platform_get_irq(tmu->pdev, index); |
502 | if (ch->irq < 0) { | 483 | if (ch->irq < 0) { |
@@ -526,46 +507,53 @@ static int sh_tmu_map_memory(struct sh_tmu_device *tmu) | |||
526 | if (tmu->mapbase == NULL) | 507 | if (tmu->mapbase == NULL) |
527 | return -ENXIO; | 508 | return -ENXIO; |
528 | 509 | ||
529 | /* | ||
530 | * In legacy platform device configuration (with one device per channel) | ||
531 | * the resource points to the channel base address. | ||
532 | */ | ||
533 | if (tmu->model == SH_TMU_LEGACY) { | ||
534 | struct sh_timer_config *cfg = tmu->pdev->dev.platform_data; | ||
535 | tmu->mapbase -= cfg->channel_offset; | ||
536 | } | ||
537 | |||
538 | return 0; | 510 | return 0; |
539 | } | 511 | } |
540 | 512 | ||
541 | static void sh_tmu_unmap_memory(struct sh_tmu_device *tmu) | 513 | static int sh_tmu_parse_dt(struct sh_tmu_device *tmu) |
542 | { | 514 | { |
543 | if (tmu->model == SH_TMU_LEGACY) { | 515 | struct device_node *np = tmu->pdev->dev.of_node; |
544 | struct sh_timer_config *cfg = tmu->pdev->dev.platform_data; | 516 | |
545 | tmu->mapbase += cfg->channel_offset; | 517 | tmu->model = SH_TMU; |
518 | tmu->num_channels = 3; | ||
519 | |||
520 | of_property_read_u32(np, "#renesas,channels", &tmu->num_channels); | ||
521 | |||
522 | if (tmu->num_channels != 2 && tmu->num_channels != 3) { | ||
523 | dev_err(&tmu->pdev->dev, "invalid number of channels %u\n", | ||
524 | tmu->num_channels); | ||
525 | return -EINVAL; | ||
546 | } | 526 | } |
547 | 527 | ||
548 | iounmap(tmu->mapbase); | 528 | return 0; |
549 | } | 529 | } |
550 | 530 | ||
551 | static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) | 531 | static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) |
552 | { | 532 | { |
553 | struct sh_timer_config *cfg = pdev->dev.platform_data; | ||
554 | const struct platform_device_id *id = pdev->id_entry; | ||
555 | unsigned int i; | 533 | unsigned int i; |
556 | int ret; | 534 | int ret; |
557 | 535 | ||
558 | if (!cfg) { | 536 | tmu->pdev = pdev; |
537 | |||
538 | raw_spin_lock_init(&tmu->lock); | ||
539 | |||
540 | if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { | ||
541 | ret = sh_tmu_parse_dt(tmu); | ||
542 | if (ret < 0) | ||
543 | return ret; | ||
544 | } else if (pdev->dev.platform_data) { | ||
545 | const struct platform_device_id *id = pdev->id_entry; | ||
546 | struct sh_timer_config *cfg = pdev->dev.platform_data; | ||
547 | |||
548 | tmu->model = id->driver_data; | ||
549 | tmu->num_channels = hweight8(cfg->channels_mask); | ||
550 | } else { | ||
559 | dev_err(&tmu->pdev->dev, "missing platform data\n"); | 551 | dev_err(&tmu->pdev->dev, "missing platform data\n"); |
560 | return -ENXIO; | 552 | return -ENXIO; |
561 | } | 553 | } |
562 | 554 | ||
563 | tmu->pdev = pdev; | ||
564 | tmu->model = id->driver_data; | ||
565 | |||
566 | /* Get hold of clock. */ | 555 | /* Get hold of clock. */ |
567 | tmu->clk = clk_get(&tmu->pdev->dev, | 556 | tmu->clk = clk_get(&tmu->pdev->dev, "fck"); |
568 | tmu->model == SH_TMU_LEGACY ? "tmu_fck" : "fck"); | ||
569 | if (IS_ERR(tmu->clk)) { | 557 | if (IS_ERR(tmu->clk)) { |
570 | dev_err(&tmu->pdev->dev, "cannot get clock\n"); | 558 | dev_err(&tmu->pdev->dev, "cannot get clock\n"); |
571 | return PTR_ERR(tmu->clk); | 559 | return PTR_ERR(tmu->clk); |
@@ -583,11 +571,6 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) | |||
583 | } | 571 | } |
584 | 572 | ||
585 | /* Allocate and setup the channels. */ | 573 | /* Allocate and setup the channels. */ |
586 | if (tmu->model == SH_TMU_LEGACY) | ||
587 | tmu->num_channels = 1; | ||
588 | else | ||
589 | tmu->num_channels = hweight8(cfg->channels_mask); | ||
590 | |||
591 | tmu->channels = kzalloc(sizeof(*tmu->channels) * tmu->num_channels, | 574 | tmu->channels = kzalloc(sizeof(*tmu->channels) * tmu->num_channels, |
592 | GFP_KERNEL); | 575 | GFP_KERNEL); |
593 | if (tmu->channels == NULL) { | 576 | if (tmu->channels == NULL) { |
@@ -595,23 +578,15 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) | |||
595 | goto err_unmap; | 578 | goto err_unmap; |
596 | } | 579 | } |
597 | 580 | ||
598 | if (tmu->model == SH_TMU_LEGACY) { | 581 | /* |
599 | ret = sh_tmu_channel_setup(&tmu->channels[0], 0, | 582 | * Use the first channel as a clock event device and the second channel |
600 | cfg->clockevent_rating != 0, | 583 | * as a clock source. |
601 | cfg->clocksource_rating != 0, tmu); | 584 | */ |
585 | for (i = 0; i < tmu->num_channels; ++i) { | ||
586 | ret = sh_tmu_channel_setup(&tmu->channels[i], i, | ||
587 | i == 0, i == 1, tmu); | ||
602 | if (ret < 0) | 588 | if (ret < 0) |
603 | goto err_unmap; | 589 | goto err_unmap; |
604 | } else { | ||
605 | /* | ||
606 | * Use the first channel as a clock event device and the second | ||
607 | * channel as a clock source. | ||
608 | */ | ||
609 | for (i = 0; i < tmu->num_channels; ++i) { | ||
610 | ret = sh_tmu_channel_setup(&tmu->channels[i], i, | ||
611 | i == 0, i == 1, tmu); | ||
612 | if (ret < 0) | ||
613 | goto err_unmap; | ||
614 | } | ||
615 | } | 590 | } |
616 | 591 | ||
617 | platform_set_drvdata(pdev, tmu); | 592 | platform_set_drvdata(pdev, tmu); |
@@ -620,7 +595,7 @@ static int sh_tmu_setup(struct sh_tmu_device *tmu, struct platform_device *pdev) | |||
620 | 595 | ||
621 | err_unmap: | 596 | err_unmap: |
622 | kfree(tmu->channels); | 597 | kfree(tmu->channels); |
623 | sh_tmu_unmap_memory(tmu); | 598 | iounmap(tmu->mapbase); |
624 | err_clk_unprepare: | 599 | err_clk_unprepare: |
625 | clk_unprepare(tmu->clk); | 600 | clk_unprepare(tmu->clk); |
626 | err_clk_put: | 601 | err_clk_put: |
@@ -671,18 +646,24 @@ static int sh_tmu_remove(struct platform_device *pdev) | |||
671 | } | 646 | } |
672 | 647 | ||
673 | static const struct platform_device_id sh_tmu_id_table[] = { | 648 | static const struct platform_device_id sh_tmu_id_table[] = { |
674 | { "sh_tmu", SH_TMU_LEGACY }, | ||
675 | { "sh-tmu", SH_TMU }, | 649 | { "sh-tmu", SH_TMU }, |
676 | { "sh-tmu-sh3", SH_TMU_SH3 }, | 650 | { "sh-tmu-sh3", SH_TMU_SH3 }, |
677 | { } | 651 | { } |
678 | }; | 652 | }; |
679 | MODULE_DEVICE_TABLE(platform, sh_tmu_id_table); | 653 | MODULE_DEVICE_TABLE(platform, sh_tmu_id_table); |
680 | 654 | ||
655 | static const struct of_device_id sh_tmu_of_table[] __maybe_unused = { | ||
656 | { .compatible = "renesas,tmu" }, | ||
657 | { } | ||
658 | }; | ||
659 | MODULE_DEVICE_TABLE(of, sh_tmu_of_table); | ||
660 | |||
681 | static struct platform_driver sh_tmu_device_driver = { | 661 | static struct platform_driver sh_tmu_device_driver = { |
682 | .probe = sh_tmu_probe, | 662 | .probe = sh_tmu_probe, |
683 | .remove = sh_tmu_remove, | 663 | .remove = sh_tmu_remove, |
684 | .driver = { | 664 | .driver = { |
685 | .name = "sh_tmu", | 665 | .name = "sh_tmu", |
666 | .of_match_table = of_match_ptr(sh_tmu_of_table), | ||
686 | }, | 667 | }, |
687 | .id_table = sh_tmu_id_table, | 668 | .id_table = sh_tmu_id_table, |
688 | }; | 669 | }; |