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 | ||