diff options
Diffstat (limited to 'drivers/clocksource/sh_cmt.c')
| -rw-r--r-- | drivers/clocksource/sh_cmt.c | 116 |
1 files changed, 97 insertions, 19 deletions
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 1c92c39a53aa..cf56a2af5fe1 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c | |||
| @@ -18,7 +18,6 @@ | |||
| 18 | */ | 18 | */ |
| 19 | 19 | ||
| 20 | #include <linux/init.h> | 20 | #include <linux/init.h> |
| 21 | #include <linux/bootmem.h> | ||
| 22 | #include <linux/platform_device.h> | 21 | #include <linux/platform_device.h> |
| 23 | #include <linux/spinlock.h> | 22 | #include <linux/spinlock.h> |
| 24 | #include <linux/interrupt.h> | 23 | #include <linux/interrupt.h> |
| @@ -29,7 +28,7 @@ | |||
| 29 | #include <linux/err.h> | 28 | #include <linux/err.h> |
| 30 | #include <linux/clocksource.h> | 29 | #include <linux/clocksource.h> |
| 31 | #include <linux/clockchips.h> | 30 | #include <linux/clockchips.h> |
| 32 | #include <linux/sh_cmt.h> | 31 | #include <linux/sh_timer.h> |
| 33 | 32 | ||
| 34 | struct sh_cmt_priv { | 33 | struct sh_cmt_priv { |
| 35 | void __iomem *mapbase; | 34 | void __iomem *mapbase; |
| @@ -47,6 +46,7 @@ struct sh_cmt_priv { | |||
| 47 | unsigned long rate; | 46 | unsigned long rate; |
| 48 | spinlock_t lock; | 47 | spinlock_t lock; |
| 49 | struct clock_event_device ced; | 48 | struct clock_event_device ced; |
| 49 | struct clocksource cs; | ||
| 50 | unsigned long total_cycles; | 50 | unsigned long total_cycles; |
| 51 | }; | 51 | }; |
| 52 | 52 | ||
| @@ -59,7 +59,7 @@ static DEFINE_SPINLOCK(sh_cmt_lock); | |||
| 59 | 59 | ||
| 60 | static inline unsigned long sh_cmt_read(struct sh_cmt_priv *p, int reg_nr) | 60 | static inline unsigned long sh_cmt_read(struct sh_cmt_priv *p, int reg_nr) |
| 61 | { | 61 | { |
| 62 | struct sh_cmt_config *cfg = p->pdev->dev.platform_data; | 62 | struct sh_timer_config *cfg = p->pdev->dev.platform_data; |
| 63 | void __iomem *base = p->mapbase; | 63 | void __iomem *base = p->mapbase; |
| 64 | unsigned long offs; | 64 | unsigned long offs; |
| 65 | 65 | ||
| @@ -83,7 +83,7 @@ static inline unsigned long sh_cmt_read(struct sh_cmt_priv *p, int reg_nr) | |||
| 83 | static inline void sh_cmt_write(struct sh_cmt_priv *p, int reg_nr, | 83 | static inline void sh_cmt_write(struct sh_cmt_priv *p, int reg_nr, |
| 84 | unsigned long value) | 84 | unsigned long value) |
| 85 | { | 85 | { |
| 86 | struct sh_cmt_config *cfg = p->pdev->dev.platform_data; | 86 | struct sh_timer_config *cfg = p->pdev->dev.platform_data; |
| 87 | void __iomem *base = p->mapbase; | 87 | void __iomem *base = p->mapbase; |
| 88 | unsigned long offs; | 88 | unsigned long offs; |
| 89 | 89 | ||
| @@ -110,23 +110,28 @@ static unsigned long sh_cmt_get_counter(struct sh_cmt_priv *p, | |||
| 110 | int *has_wrapped) | 110 | int *has_wrapped) |
| 111 | { | 111 | { |
| 112 | unsigned long v1, v2, v3; | 112 | unsigned long v1, v2, v3; |
| 113 | int o1, o2; | ||
| 114 | |||
| 115 | o1 = sh_cmt_read(p, CMCSR) & p->overflow_bit; | ||
| 113 | 116 | ||
| 114 | /* Make sure the timer value is stable. Stolen from acpi_pm.c */ | 117 | /* Make sure the timer value is stable. Stolen from acpi_pm.c */ |
| 115 | do { | 118 | do { |
| 119 | o2 = o1; | ||
| 116 | v1 = sh_cmt_read(p, CMCNT); | 120 | v1 = sh_cmt_read(p, CMCNT); |
| 117 | v2 = sh_cmt_read(p, CMCNT); | 121 | v2 = sh_cmt_read(p, CMCNT); |
| 118 | v3 = sh_cmt_read(p, CMCNT); | 122 | v3 = sh_cmt_read(p, CMCNT); |
| 119 | } while (unlikely((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1) | 123 | o1 = sh_cmt_read(p, CMCSR) & p->overflow_bit; |
| 120 | || (v3 > v1 && v3 < v2))); | 124 | } while (unlikely((o1 != o2) || (v1 > v2 && v1 < v3) |
| 125 | || (v2 > v3 && v2 < v1) || (v3 > v1 && v3 < v2))); | ||
| 121 | 126 | ||
| 122 | *has_wrapped = sh_cmt_read(p, CMCSR) & p->overflow_bit; | 127 | *has_wrapped = o1; |
| 123 | return v2; | 128 | return v2; |
| 124 | } | 129 | } |
| 125 | 130 | ||
| 126 | 131 | ||
| 127 | static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start) | 132 | static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start) |
| 128 | { | 133 | { |
| 129 | struct sh_cmt_config *cfg = p->pdev->dev.platform_data; | 134 | struct sh_timer_config *cfg = p->pdev->dev.platform_data; |
| 130 | unsigned long flags, value; | 135 | unsigned long flags, value; |
| 131 | 136 | ||
| 132 | /* start stop register shared by multiple timer channels */ | 137 | /* start stop register shared by multiple timer channels */ |
| @@ -144,7 +149,7 @@ static void sh_cmt_start_stop_ch(struct sh_cmt_priv *p, int start) | |||
| 144 | 149 | ||
| 145 | static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate) | 150 | static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate) |
| 146 | { | 151 | { |
| 147 | struct sh_cmt_config *cfg = p->pdev->dev.platform_data; | 152 | struct sh_timer_config *cfg = p->pdev->dev.platform_data; |
| 148 | int ret; | 153 | int ret; |
| 149 | 154 | ||
| 150 | /* enable clock */ | 155 | /* enable clock */ |
| @@ -153,16 +158,18 @@ static int sh_cmt_enable(struct sh_cmt_priv *p, unsigned long *rate) | |||
| 153 | pr_err("sh_cmt: cannot enable clock \"%s\"\n", cfg->clk); | 158 | pr_err("sh_cmt: cannot enable clock \"%s\"\n", cfg->clk); |
| 154 | return ret; | 159 | return ret; |
| 155 | } | 160 | } |
| 156 | *rate = clk_get_rate(p->clk) / 8; | ||
| 157 | 161 | ||
| 158 | /* make sure channel is disabled */ | 162 | /* make sure channel is disabled */ |
| 159 | sh_cmt_start_stop_ch(p, 0); | 163 | sh_cmt_start_stop_ch(p, 0); |
| 160 | 164 | ||
| 161 | /* configure channel, periodic mode and maximum timeout */ | 165 | /* configure channel, periodic mode and maximum timeout */ |
| 162 | if (p->width == 16) | 166 | if (p->width == 16) { |
| 163 | sh_cmt_write(p, CMCSR, 0); | 167 | *rate = clk_get_rate(p->clk) / 512; |
| 164 | else | 168 | sh_cmt_write(p, CMCSR, 0x43); |
| 169 | } else { | ||
| 170 | *rate = clk_get_rate(p->clk) / 8; | ||
| 165 | sh_cmt_write(p, CMCSR, 0x01a4); | 171 | sh_cmt_write(p, CMCSR, 0x01a4); |
| 172 | } | ||
| 166 | 173 | ||
| 167 | sh_cmt_write(p, CMCOR, 0xffffffff); | 174 | sh_cmt_write(p, CMCOR, 0xffffffff); |
| 168 | sh_cmt_write(p, CMCNT, 0); | 175 | sh_cmt_write(p, CMCNT, 0); |
| @@ -376,6 +383,68 @@ static void sh_cmt_stop(struct sh_cmt_priv *p, unsigned long flag) | |||
| 376 | spin_unlock_irqrestore(&p->lock, flags); | 383 | spin_unlock_irqrestore(&p->lock, flags); |
| 377 | } | 384 | } |
| 378 | 385 | ||
| 386 | static struct sh_cmt_priv *cs_to_sh_cmt(struct clocksource *cs) | ||
| 387 | { | ||
| 388 | return container_of(cs, struct sh_cmt_priv, cs); | ||
| 389 | } | ||
| 390 | |||
| 391 | static cycle_t sh_cmt_clocksource_read(struct clocksource *cs) | ||
| 392 | { | ||
| 393 | struct sh_cmt_priv *p = cs_to_sh_cmt(cs); | ||
| 394 | unsigned long flags, raw; | ||
| 395 | unsigned long value; | ||
| 396 | int has_wrapped; | ||
| 397 | |||
| 398 | spin_lock_irqsave(&p->lock, flags); | ||
| 399 | value = p->total_cycles; | ||
| 400 | raw = sh_cmt_get_counter(p, &has_wrapped); | ||
| 401 | |||
| 402 | if (unlikely(has_wrapped)) | ||
| 403 | raw += p->match_value; | ||
| 404 | spin_unlock_irqrestore(&p->lock, flags); | ||
| 405 | |||
| 406 | return value + raw; | ||
| 407 | } | ||
| 408 | |||
| 409 | static int sh_cmt_clocksource_enable(struct clocksource *cs) | ||
| 410 | { | ||
| 411 | struct sh_cmt_priv *p = cs_to_sh_cmt(cs); | ||
| 412 | int ret; | ||
| 413 | |||
| 414 | p->total_cycles = 0; | ||
| 415 | |||
| 416 | ret = sh_cmt_start(p, FLAG_CLOCKSOURCE); | ||
| 417 | if (ret) | ||
| 418 | return ret; | ||
| 419 | |||
| 420 | /* TODO: calculate good shift from rate and counter bit width */ | ||
| 421 | cs->shift = 0; | ||
| 422 | cs->mult = clocksource_hz2mult(p->rate, cs->shift); | ||
| 423 | return 0; | ||
| 424 | } | ||
| 425 | |||
| 426 | static void sh_cmt_clocksource_disable(struct clocksource *cs) | ||
| 427 | { | ||
| 428 | sh_cmt_stop(cs_to_sh_cmt(cs), FLAG_CLOCKSOURCE); | ||
| 429 | } | ||
| 430 | |||
| 431 | static int sh_cmt_register_clocksource(struct sh_cmt_priv *p, | ||
| 432 | char *name, unsigned long rating) | ||
| 433 | { | ||
| 434 | struct clocksource *cs = &p->cs; | ||
| 435 | |||
| 436 | cs->name = name; | ||
| 437 | cs->rating = rating; | ||
| 438 | cs->read = sh_cmt_clocksource_read; | ||
| 439 | cs->enable = sh_cmt_clocksource_enable; | ||
| 440 | cs->disable = sh_cmt_clocksource_disable; | ||
| 441 | cs->mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8); | ||
| 442 | cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; | ||
| 443 | pr_info("sh_cmt: %s used as clock source\n", cs->name); | ||
| 444 | clocksource_register(cs); | ||
| 445 | return 0; | ||
| 446 | } | ||
| 447 | |||
| 379 | static struct sh_cmt_priv *ced_to_sh_cmt(struct clock_event_device *ced) | 448 | static struct sh_cmt_priv *ced_to_sh_cmt(struct clock_event_device *ced) |
| 380 | { | 449 | { |
| 381 | return container_of(ced, struct sh_cmt_priv, ced); | 450 | return container_of(ced, struct sh_cmt_priv, ced); |
| @@ -468,9 +537,9 @@ static void sh_cmt_register_clockevent(struct sh_cmt_priv *p, | |||
| 468 | clockevents_register_device(ced); | 537 | clockevents_register_device(ced); |
| 469 | } | 538 | } |
| 470 | 539 | ||
| 471 | int sh_cmt_register(struct sh_cmt_priv *p, char *name, | 540 | static int sh_cmt_register(struct sh_cmt_priv *p, char *name, |
| 472 | unsigned long clockevent_rating, | 541 | unsigned long clockevent_rating, |
| 473 | unsigned long clocksource_rating) | 542 | unsigned long clocksource_rating) |
| 474 | { | 543 | { |
| 475 | if (p->width == (sizeof(p->max_match_value) * 8)) | 544 | if (p->width == (sizeof(p->max_match_value) * 8)) |
| 476 | p->max_match_value = ~0; | 545 | p->max_match_value = ~0; |
| @@ -483,12 +552,15 @@ int sh_cmt_register(struct sh_cmt_priv *p, char *name, | |||
| 483 | if (clockevent_rating) | 552 | if (clockevent_rating) |
| 484 | sh_cmt_register_clockevent(p, name, clockevent_rating); | 553 | sh_cmt_register_clockevent(p, name, clockevent_rating); |
| 485 | 554 | ||
| 555 | if (clocksource_rating) | ||
| 556 | sh_cmt_register_clocksource(p, name, clocksource_rating); | ||
| 557 | |||
| 486 | return 0; | 558 | return 0; |
| 487 | } | 559 | } |
| 488 | 560 | ||
| 489 | static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) | 561 | static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) |
| 490 | { | 562 | { |
| 491 | struct sh_cmt_config *cfg = pdev->dev.platform_data; | 563 | struct sh_timer_config *cfg = pdev->dev.platform_data; |
| 492 | struct resource *res; | 564 | struct resource *res; |
| 493 | int irq, ret; | 565 | int irq, ret; |
| 494 | ret = -ENXIO; | 566 | ret = -ENXIO; |
| @@ -545,7 +617,7 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) | |||
| 545 | if (resource_size(res) == 6) { | 617 | if (resource_size(res) == 6) { |
| 546 | p->width = 16; | 618 | p->width = 16; |
| 547 | p->overflow_bit = 0x80; | 619 | p->overflow_bit = 0x80; |
| 548 | p->clear_bits = ~0xc0; | 620 | p->clear_bits = ~0x80; |
| 549 | } else { | 621 | } else { |
| 550 | p->width = 32; | 622 | p->width = 32; |
| 551 | p->overflow_bit = 0x8000; | 623 | p->overflow_bit = 0x8000; |
| @@ -566,8 +638,14 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev) | |||
| 566 | static int __devinit sh_cmt_probe(struct platform_device *pdev) | 638 | static int __devinit sh_cmt_probe(struct platform_device *pdev) |
| 567 | { | 639 | { |
| 568 | struct sh_cmt_priv *p = platform_get_drvdata(pdev); | 640 | struct sh_cmt_priv *p = platform_get_drvdata(pdev); |
| 641 | struct sh_timer_config *cfg = pdev->dev.platform_data; | ||
| 569 | int ret; | 642 | int ret; |
| 570 | 643 | ||
| 644 | if (p) { | ||
| 645 | pr_info("sh_cmt: %s kept as earlytimer\n", cfg->name); | ||
| 646 | return 0; | ||
| 647 | } | ||
| 648 | |||
| 571 | p = kmalloc(sizeof(*p), GFP_KERNEL); | 649 | p = kmalloc(sizeof(*p), GFP_KERNEL); |
| 572 | if (p == NULL) { | 650 | if (p == NULL) { |
| 573 | dev_err(&pdev->dev, "failed to allocate driver data\n"); | 651 | dev_err(&pdev->dev, "failed to allocate driver data\n"); |
| @@ -577,7 +655,6 @@ static int __devinit sh_cmt_probe(struct platform_device *pdev) | |||
| 577 | ret = sh_cmt_setup(p, pdev); | 655 | ret = sh_cmt_setup(p, pdev); |
| 578 | if (ret) { | 656 | if (ret) { |
| 579 | kfree(p); | 657 | kfree(p); |
| 580 | |||
| 581 | platform_set_drvdata(pdev, NULL); | 658 | platform_set_drvdata(pdev, NULL); |
| 582 | } | 659 | } |
| 583 | return ret; | 660 | return ret; |
| @@ -606,6 +683,7 @@ static void __exit sh_cmt_exit(void) | |||
| 606 | platform_driver_unregister(&sh_cmt_device_driver); | 683 | platform_driver_unregister(&sh_cmt_device_driver); |
| 607 | } | 684 | } |
| 608 | 685 | ||
| 686 | early_platform_init("earlytimer", &sh_cmt_device_driver); | ||
| 609 | module_init(sh_cmt_init); | 687 | module_init(sh_cmt_init); |
| 610 | module_exit(sh_cmt_exit); | 688 | module_exit(sh_cmt_exit); |
| 611 | 689 | ||
