aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource
diff options
context:
space:
mode:
authorAndrea Bastoni <bastoni@cs.unc.edu>2010-05-30 19:16:45 -0400
committerAndrea Bastoni <bastoni@cs.unc.edu>2010-05-30 19:16:45 -0400
commitada47b5fe13d89735805b566185f4885f5a3f750 (patch)
tree644b88f8a71896307d71438e9b3af49126ffb22b /drivers/clocksource
parent43e98717ad40a4ae64545b5ba047c7b86aa44f4f (diff)
parent3280f21d43ee541f97f8cda5792150d2dbec20d5 (diff)
Merge branch 'wip-2.6.34' into old-private-masterarchived-private-master
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/cs5535-clockevt.c197
-rw-r--r--drivers/clocksource/sh_cmt.c68
-rw-r--r--drivers/clocksource/sh_mtu2.c7
-rw-r--r--drivers/clocksource/sh_tmu.c7
5 files changed, 233 insertions, 47 deletions
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index eef216f7f61d..be61ece6330b 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o
2obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o 2obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o
3obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o 3obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o
4obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o 4obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o
5obj-$(CONFIG_CS5535_CLOCK_EVENT_SRC) += cs5535-clockevt.o
5obj-$(CONFIG_SH_TIMER_CMT) += sh_cmt.o 6obj-$(CONFIG_SH_TIMER_CMT) += sh_cmt.o
6obj-$(CONFIG_SH_TIMER_MTU2) += sh_mtu2.o 7obj-$(CONFIG_SH_TIMER_MTU2) += sh_mtu2.o
7obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o 8obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o
diff --git a/drivers/clocksource/cs5535-clockevt.c b/drivers/clocksource/cs5535-clockevt.c
new file mode 100644
index 000000000000..b314a999aabe
--- /dev/null
+++ b/drivers/clocksource/cs5535-clockevt.c
@@ -0,0 +1,197 @@
1/*
2 * Clock event driver for the CS5535/CS5536
3 *
4 * Copyright (C) 2006, Advanced Micro Devices, Inc.
5 * Copyright (C) 2007 Andres Salomon <dilinger@debian.org>
6 * Copyright (C) 2009 Andres Salomon <dilinger@collabora.co.uk>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of version 2 of the GNU General Public License
10 * as published by the Free Software Foundation.
11 *
12 * The MFGPTs are documented in AMD Geode CS5536 Companion Device Data Book.
13 */
14
15#include <linux/kernel.h>
16#include <linux/irq.h>
17#include <linux/interrupt.h>
18#include <linux/module.h>
19#include <linux/cs5535.h>
20#include <linux/clockchips.h>
21
22#define DRV_NAME "cs5535-clockevt"
23
24static int timer_irq;
25module_param_named(irq, timer_irq, int, 0644);
26MODULE_PARM_DESC(irq, "Which IRQ to use for the clock source MFGPT ticks.");
27
28/*
29 * We are using the 32.768kHz input clock - it's the only one that has the
30 * ranges we find desirable. The following table lists the suitable
31 * divisors and the associated Hz, minimum interval and the maximum interval:
32 *
33 * Divisor Hz Min Delta (s) Max Delta (s)
34 * 1 32768 .00048828125 2.000
35 * 2 16384 .0009765625 4.000
36 * 4 8192 .001953125 8.000
37 * 8 4096 .00390625 16.000
38 * 16 2048 .0078125 32.000
39 * 32 1024 .015625 64.000
40 * 64 512 .03125 128.000
41 * 128 256 .0625 256.000
42 * 256 128 .125 512.000
43 */
44
45static unsigned int cs5535_tick_mode = CLOCK_EVT_MODE_SHUTDOWN;
46static struct cs5535_mfgpt_timer *cs5535_event_clock;
47
48/* Selected from the table above */
49
50#define MFGPT_DIVISOR 16
51#define MFGPT_SCALE 4 /* divisor = 2^(scale) */
52#define MFGPT_HZ (32768 / MFGPT_DIVISOR)
53#define MFGPT_PERIODIC (MFGPT_HZ / HZ)
54
55/*
56 * The MFPGT timers on the CS5536 provide us with suitable timers to use
57 * as clock event sources - not as good as a HPET or APIC, but certainly
58 * better than the PIT. This isn't a general purpose MFGPT driver, but
59 * a simplified one designed specifically to act as a clock event source.
60 * For full details about the MFGPT, please consult the CS5536 data sheet.
61 */
62
63static void disable_timer(struct cs5535_mfgpt_timer *timer)
64{
65 /* avoid races by clearing CMP1 and CMP2 unconditionally */
66 cs5535_mfgpt_write(timer, MFGPT_REG_SETUP,
67 (uint16_t) ~MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP1 |
68 MFGPT_SETUP_CMP2);
69}
70
71static void start_timer(struct cs5535_mfgpt_timer *timer, uint16_t delta)
72{
73 cs5535_mfgpt_write(timer, MFGPT_REG_CMP2, delta);
74 cs5535_mfgpt_write(timer, MFGPT_REG_COUNTER, 0);
75
76 cs5535_mfgpt_write(timer, MFGPT_REG_SETUP,
77 MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
78}
79
80static void mfgpt_set_mode(enum clock_event_mode mode,
81 struct clock_event_device *evt)
82{
83 disable_timer(cs5535_event_clock);
84
85 if (mode == CLOCK_EVT_MODE_PERIODIC)
86 start_timer(cs5535_event_clock, MFGPT_PERIODIC);
87
88 cs5535_tick_mode = mode;
89}
90
91static int mfgpt_next_event(unsigned long delta, struct clock_event_device *evt)
92{
93 start_timer(cs5535_event_clock, delta);
94 return 0;
95}
96
97static struct clock_event_device cs5535_clockevent = {
98 .name = DRV_NAME,
99 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
100 .set_mode = mfgpt_set_mode,
101 .set_next_event = mfgpt_next_event,
102 .rating = 250,
103 .cpumask = cpu_all_mask,
104 .shift = 32
105};
106
107static irqreturn_t mfgpt_tick(int irq, void *dev_id)
108{
109 uint16_t val = cs5535_mfgpt_read(cs5535_event_clock, MFGPT_REG_SETUP);
110
111 /* See if the interrupt was for us */
112 if (!(val & (MFGPT_SETUP_SETUP | MFGPT_SETUP_CMP2 | MFGPT_SETUP_CMP1)))
113 return IRQ_NONE;
114
115 /* Turn off the clock (and clear the event) */
116 disable_timer(cs5535_event_clock);
117
118 if (cs5535_tick_mode == CLOCK_EVT_MODE_SHUTDOWN)
119 return IRQ_HANDLED;
120
121 /* Clear the counter */
122 cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_COUNTER, 0);
123
124 /* Restart the clock in periodic mode */
125
126 if (cs5535_tick_mode == CLOCK_EVT_MODE_PERIODIC)
127 cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_SETUP,
128 MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
129
130 cs5535_clockevent.event_handler(&cs5535_clockevent);
131 return IRQ_HANDLED;
132}
133
134static struct irqaction mfgptirq = {
135 .handler = mfgpt_tick,
136 .flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_TIMER,
137 .name = DRV_NAME,
138};
139
140static int __init cs5535_mfgpt_init(void)
141{
142 struct cs5535_mfgpt_timer *timer;
143 int ret;
144 uint16_t val;
145
146 timer = cs5535_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING);
147 if (!timer) {
148 printk(KERN_ERR DRV_NAME ": Could not allocate MFPGT timer\n");
149 return -ENODEV;
150 }
151 cs5535_event_clock = timer;
152
153 /* Set up the IRQ on the MFGPT side */
154 if (cs5535_mfgpt_setup_irq(timer, MFGPT_CMP2, &timer_irq)) {
155 printk(KERN_ERR DRV_NAME ": Could not set up IRQ %d\n",
156 timer_irq);
157 return -EIO;
158 }
159
160 /* And register it with the kernel */
161 ret = setup_irq(timer_irq, &mfgptirq);
162 if (ret) {
163 printk(KERN_ERR DRV_NAME ": Unable to set up the interrupt.\n");
164 goto err;
165 }
166
167 /* Set the clock scale and enable the event mode for CMP2 */
168 val = MFGPT_SCALE | (3 << 8);
169
170 cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_SETUP, val);
171
172 /* Set up the clock event */
173 cs5535_clockevent.mult = div_sc(MFGPT_HZ, NSEC_PER_SEC,
174 cs5535_clockevent.shift);
175 cs5535_clockevent.min_delta_ns = clockevent_delta2ns(0xF,
176 &cs5535_clockevent);
177 cs5535_clockevent.max_delta_ns = clockevent_delta2ns(0xFFFE,
178 &cs5535_clockevent);
179
180 printk(KERN_INFO DRV_NAME
181 ": Registering MFGPT timer as a clock event, using IRQ %d\n",
182 timer_irq);
183 clockevents_register_device(&cs5535_clockevent);
184
185 return 0;
186
187err:
188 cs5535_mfgpt_release_irq(cs5535_event_clock, MFGPT_CMP2, &timer_irq);
189 printk(KERN_ERR DRV_NAME ": Unable to set up the MFGPT clock source\n");
190 return -EIO;
191}
192
193module_init(cs5535_mfgpt_init);
194
195MODULE_AUTHOR("Andres Salomon <dilinger@collabora.co.uk>");
196MODULE_DESCRIPTION("CS5535/CS5536 MFGPT clock event driver");
197MODULE_LICENSE("GPL");
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 6b3e0c2f33e2..744f748cc84b 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -29,6 +29,7 @@
29#include <linux/clocksource.h> 29#include <linux/clocksource.h>
30#include <linux/clockchips.h> 30#include <linux/clockchips.h>
31#include <linux/sh_timer.h> 31#include <linux/sh_timer.h>
32#include <linux/slab.h>
32 33
33struct sh_cmt_priv { 34struct sh_cmt_priv {
34 void __iomem *mapbase; 35 void __iomem *mapbase;
@@ -40,7 +41,6 @@ struct sh_cmt_priv {
40 struct platform_device *pdev; 41 struct platform_device *pdev;
41 42
42 unsigned long flags; 43 unsigned long flags;
43 unsigned long flags_suspend;
44 unsigned long match_value; 44 unsigned long match_value;
45 unsigned long next_match_value; 45 unsigned long next_match_value;
46 unsigned long max_match_value; 46 unsigned long max_match_value;
@@ -432,6 +432,11 @@ static void sh_cmt_clocksource_disable(struct clocksource *cs)
432 sh_cmt_stop(cs_to_sh_cmt(cs), FLAG_CLOCKSOURCE); 432 sh_cmt_stop(cs_to_sh_cmt(cs), FLAG_CLOCKSOURCE);
433} 433}
434 434
435static void sh_cmt_clocksource_resume(struct clocksource *cs)
436{
437 sh_cmt_start(cs_to_sh_cmt(cs), FLAG_CLOCKSOURCE);
438}
439
435static int sh_cmt_register_clocksource(struct sh_cmt_priv *p, 440static int sh_cmt_register_clocksource(struct sh_cmt_priv *p,
436 char *name, unsigned long rating) 441 char *name, unsigned long rating)
437{ 442{
@@ -442,6 +447,8 @@ static int sh_cmt_register_clocksource(struct sh_cmt_priv *p,
442 cs->read = sh_cmt_clocksource_read; 447 cs->read = sh_cmt_clocksource_read;
443 cs->enable = sh_cmt_clocksource_enable; 448 cs->enable = sh_cmt_clocksource_enable;
444 cs->disable = sh_cmt_clocksource_disable; 449 cs->disable = sh_cmt_clocksource_disable;
450 cs->suspend = sh_cmt_clocksource_disable;
451 cs->resume = sh_cmt_clocksource_resume;
445 cs->mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8); 452 cs->mask = CLOCKSOURCE_MASK(sizeof(unsigned long) * 8);
446 cs->flags = CLOCK_SOURCE_IS_CONTINUOUS; 453 cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
447 pr_info("sh_cmt: %s used as clock source\n", cs->name); 454 pr_info("sh_cmt: %s used as clock source\n", cs->name);
@@ -603,18 +610,13 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
603 p->irqaction.handler = sh_cmt_interrupt; 610 p->irqaction.handler = sh_cmt_interrupt;
604 p->irqaction.dev_id = p; 611 p->irqaction.dev_id = p;
605 p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL; 612 p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL;
606 ret = setup_irq(irq, &p->irqaction);
607 if (ret) {
608 pr_err("sh_cmt: failed to request irq %d\n", irq);
609 goto err1;
610 }
611 613
612 /* get hold of clock */ 614 /* get hold of clock */
613 p->clk = clk_get(&p->pdev->dev, cfg->clk); 615 p->clk = clk_get(&p->pdev->dev, cfg->clk);
614 if (IS_ERR(p->clk)) { 616 if (IS_ERR(p->clk)) {
615 pr_err("sh_cmt: cannot get clock \"%s\"\n", cfg->clk); 617 pr_err("sh_cmt: cannot get clock \"%s\"\n", cfg->clk);
616 ret = PTR_ERR(p->clk); 618 ret = PTR_ERR(p->clk);
617 goto err2; 619 goto err1;
618 } 620 }
619 621
620 if (resource_size(res) == 6) { 622 if (resource_size(res) == 6) {
@@ -627,14 +629,25 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
627 p->clear_bits = ~0xc000; 629 p->clear_bits = ~0xc000;
628 } 630 }
629 631
630 return sh_cmt_register(p, cfg->name, 632 ret = sh_cmt_register(p, cfg->name,
631 cfg->clockevent_rating, 633 cfg->clockevent_rating,
632 cfg->clocksource_rating); 634 cfg->clocksource_rating);
633 err2: 635 if (ret) {
634 remove_irq(irq, &p->irqaction); 636 pr_err("sh_cmt: registration failed\n");
635 err1: 637 goto err1;
638 }
639
640 ret = setup_irq(irq, &p->irqaction);
641 if (ret) {
642 pr_err("sh_cmt: failed to request irq %d\n", irq);
643 goto err1;
644 }
645
646 return 0;
647
648err1:
636 iounmap(p->mapbase); 649 iounmap(p->mapbase);
637 err0: 650err0:
638 return ret; 651 return ret;
639} 652}
640 653
@@ -668,38 +681,11 @@ static int __devexit sh_cmt_remove(struct platform_device *pdev)
668 return -EBUSY; /* cannot unregister clockevent and clocksource */ 681 return -EBUSY; /* cannot unregister clockevent and clocksource */
669} 682}
670 683
671static int sh_cmt_suspend(struct device *dev)
672{
673 struct platform_device *pdev = to_platform_device(dev);
674 struct sh_cmt_priv *p = platform_get_drvdata(pdev);
675
676 /* save flag state and stop CMT channel */
677 p->flags_suspend = p->flags;
678 sh_cmt_stop(p, p->flags);
679 return 0;
680}
681
682static int sh_cmt_resume(struct device *dev)
683{
684 struct platform_device *pdev = to_platform_device(dev);
685 struct sh_cmt_priv *p = platform_get_drvdata(pdev);
686
687 /* start CMT channel from saved state */
688 sh_cmt_start(p, p->flags_suspend);
689 return 0;
690}
691
692static struct dev_pm_ops sh_cmt_dev_pm_ops = {
693 .suspend = sh_cmt_suspend,
694 .resume = sh_cmt_resume,
695};
696
697static struct platform_driver sh_cmt_device_driver = { 684static struct platform_driver sh_cmt_device_driver = {
698 .probe = sh_cmt_probe, 685 .probe = sh_cmt_probe,
699 .remove = __devexit_p(sh_cmt_remove), 686 .remove = __devexit_p(sh_cmt_remove),
700 .driver = { 687 .driver = {
701 .name = "sh_cmt", 688 .name = "sh_cmt",
702 .pm = &sh_cmt_dev_pm_ops,
703 } 689 }
704}; 690};
705 691
diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c
index 973e714d6051..5fb78bfd73bb 100644
--- a/drivers/clocksource/sh_mtu2.c
+++ b/drivers/clocksource/sh_mtu2.c
@@ -29,6 +29,7 @@
29#include <linux/err.h> 29#include <linux/err.h>
30#include <linux/clockchips.h> 30#include <linux/clockchips.h>
31#include <linux/sh_timer.h> 31#include <linux/sh_timer.h>
32#include <linux/slab.h>
32 33
33struct sh_mtu2_priv { 34struct sh_mtu2_priv {
34 void __iomem *mapbase; 35 void __iomem *mapbase;
@@ -221,15 +222,15 @@ static void sh_mtu2_register_clockevent(struct sh_mtu2_priv *p,
221 ced->cpumask = cpumask_of(0); 222 ced->cpumask = cpumask_of(0);
222 ced->set_mode = sh_mtu2_clock_event_mode; 223 ced->set_mode = sh_mtu2_clock_event_mode;
223 224
225 pr_info("sh_mtu2: %s used for clock events\n", ced->name);
226 clockevents_register_device(ced);
227
224 ret = setup_irq(p->irqaction.irq, &p->irqaction); 228 ret = setup_irq(p->irqaction.irq, &p->irqaction);
225 if (ret) { 229 if (ret) {
226 pr_err("sh_mtu2: failed to request irq %d\n", 230 pr_err("sh_mtu2: failed to request irq %d\n",
227 p->irqaction.irq); 231 p->irqaction.irq);
228 return; 232 return;
229 } 233 }
230
231 pr_info("sh_mtu2: %s used for clock events\n", ced->name);
232 clockevents_register_device(ced);
233} 234}
234 235
235static int sh_mtu2_register(struct sh_mtu2_priv *p, char *name, 236static int sh_mtu2_register(struct sh_mtu2_priv *p, char *name,
diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c
index 93c2322feab7..fc9ff1e5b770 100644
--- a/drivers/clocksource/sh_tmu.c
+++ b/drivers/clocksource/sh_tmu.c
@@ -30,6 +30,7 @@
30#include <linux/clocksource.h> 30#include <linux/clocksource.h>
31#include <linux/clockchips.h> 31#include <linux/clockchips.h>
32#include <linux/sh_timer.h> 32#include <linux/sh_timer.h>
33#include <linux/slab.h>
33 34
34struct sh_tmu_priv { 35struct sh_tmu_priv {
35 void __iomem *mapbase; 36 void __iomem *mapbase;
@@ -323,15 +324,15 @@ static void sh_tmu_register_clockevent(struct sh_tmu_priv *p,
323 ced->set_next_event = sh_tmu_clock_event_next; 324 ced->set_next_event = sh_tmu_clock_event_next;
324 ced->set_mode = sh_tmu_clock_event_mode; 325 ced->set_mode = sh_tmu_clock_event_mode;
325 326
327 pr_info("sh_tmu: %s used for clock events\n", ced->name);
328 clockevents_register_device(ced);
329
326 ret = setup_irq(p->irqaction.irq, &p->irqaction); 330 ret = setup_irq(p->irqaction.irq, &p->irqaction);
327 if (ret) { 331 if (ret) {
328 pr_err("sh_tmu: failed to request irq %d\n", 332 pr_err("sh_tmu: failed to request irq %d\n",
329 p->irqaction.irq); 333 p->irqaction.irq);
330 return; 334 return;
331 } 335 }
332
333 pr_info("sh_tmu: %s used for clock events\n", ced->name);
334 clockevents_register_device(ced);
335} 336}
336 337
337static int sh_tmu_register(struct sh_tmu_priv *p, char *name, 338static int sh_tmu_register(struct sh_tmu_priv *p, char *name,