diff options
Diffstat (limited to 'arch/arm/common/timer-sp.c')
-rw-r--r-- | arch/arm/common/timer-sp.c | 82 |
1 files changed, 51 insertions, 31 deletions
diff --git a/arch/arm/common/timer-sp.c b/arch/arm/common/timer-sp.c index 6ef3342153b9..41df47875122 100644 --- a/arch/arm/common/timer-sp.c +++ b/arch/arm/common/timer-sp.c | |||
@@ -18,53 +18,67 @@ | |||
18 | * along with this program; if not, write to the Free Software | 18 | * along with this program; if not, write to the Free Software |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 | */ | 20 | */ |
21 | #include <linux/clk.h> | ||
21 | #include <linux/clocksource.h> | 22 | #include <linux/clocksource.h> |
22 | #include <linux/clockchips.h> | 23 | #include <linux/clockchips.h> |
24 | #include <linux/err.h> | ||
23 | #include <linux/interrupt.h> | 25 | #include <linux/interrupt.h> |
24 | #include <linux/irq.h> | 26 | #include <linux/irq.h> |
25 | #include <linux/io.h> | 27 | #include <linux/io.h> |
26 | 28 | ||
27 | #include <asm/hardware/arm_timer.h> | 29 | #include <asm/hardware/arm_timer.h> |
28 | 30 | ||
29 | /* | 31 | static long __init sp804_get_clock_rate(const char *name) |
30 | * These timers are currently always setup to be clocked at 1MHz. | 32 | { |
31 | */ | 33 | struct clk *clk; |
32 | #define TIMER_FREQ_KHZ (1000) | 34 | long rate; |
33 | #define TIMER_RELOAD (TIMER_FREQ_KHZ * 1000 / HZ) | 35 | int err; |
36 | |||
37 | clk = clk_get_sys("sp804", name); | ||
38 | if (IS_ERR(clk)) { | ||
39 | pr_err("sp804: %s clock not found: %d\n", name, | ||
40 | (int)PTR_ERR(clk)); | ||
41 | return PTR_ERR(clk); | ||
42 | } | ||
34 | 43 | ||
35 | static void __iomem *clksrc_base; | 44 | err = clk_enable(clk); |
45 | if (err) { | ||
46 | pr_err("sp804: %s clock failed to enable: %d\n", name, err); | ||
47 | clk_put(clk); | ||
48 | return err; | ||
49 | } | ||
36 | 50 | ||
37 | static cycle_t sp804_read(struct clocksource *cs) | 51 | rate = clk_get_rate(clk); |
38 | { | 52 | if (rate < 0) { |
39 | return ~readl(clksrc_base + TIMER_VALUE); | 53 | pr_err("sp804: %s clock failed to get rate: %ld\n", name, rate); |
40 | } | 54 | clk_disable(clk); |
55 | clk_put(clk); | ||
56 | } | ||
41 | 57 | ||
42 | static struct clocksource clocksource_sp804 = { | 58 | return rate; |
43 | .name = "timer3", | 59 | } |
44 | .rating = 200, | ||
45 | .read = sp804_read, | ||
46 | .mask = CLOCKSOURCE_MASK(32), | ||
47 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
48 | }; | ||
49 | 60 | ||
50 | void __init sp804_clocksource_init(void __iomem *base) | 61 | void __init sp804_clocksource_init(void __iomem *base, const char *name) |
51 | { | 62 | { |
52 | struct clocksource *cs = &clocksource_sp804; | 63 | long rate = sp804_get_clock_rate(name); |
53 | 64 | ||
54 | clksrc_base = base; | 65 | if (rate < 0) |
66 | return; | ||
55 | 67 | ||
56 | /* setup timer 0 as free-running clocksource */ | 68 | /* setup timer 0 as free-running clocksource */ |
57 | writel(0, clksrc_base + TIMER_CTRL); | 69 | writel(0, base + TIMER_CTRL); |
58 | writel(0xffffffff, clksrc_base + TIMER_LOAD); | 70 | writel(0xffffffff, base + TIMER_LOAD); |
59 | writel(0xffffffff, clksrc_base + TIMER_VALUE); | 71 | writel(0xffffffff, base + TIMER_VALUE); |
60 | writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC, | 72 | writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC, |
61 | clksrc_base + TIMER_CTRL); | 73 | base + TIMER_CTRL); |
62 | 74 | ||
63 | clocksource_register_khz(cs, TIMER_FREQ_KHZ); | 75 | clocksource_mmio_init(base + TIMER_VALUE, name, |
76 | rate, 200, 32, clocksource_mmio_readl_down); | ||
64 | } | 77 | } |
65 | 78 | ||
66 | 79 | ||
67 | static void __iomem *clkevt_base; | 80 | static void __iomem *clkevt_base; |
81 | static unsigned long clkevt_reload; | ||
68 | 82 | ||
69 | /* | 83 | /* |
70 | * IRQ handler for the timer | 84 | * IRQ handler for the timer |
@@ -90,7 +104,7 @@ static void sp804_set_mode(enum clock_event_mode mode, | |||
90 | 104 | ||
91 | switch (mode) { | 105 | switch (mode) { |
92 | case CLOCK_EVT_MODE_PERIODIC: | 106 | case CLOCK_EVT_MODE_PERIODIC: |
93 | writel(TIMER_RELOAD, clkevt_base + TIMER_LOAD); | 107 | writel(clkevt_reload, clkevt_base + TIMER_LOAD); |
94 | ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE; | 108 | ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE; |
95 | break; | 109 | break; |
96 | 110 | ||
@@ -120,7 +134,6 @@ static int sp804_set_next_event(unsigned long next, | |||
120 | } | 134 | } |
121 | 135 | ||
122 | static struct clock_event_device sp804_clockevent = { | 136 | static struct clock_event_device sp804_clockevent = { |
123 | .name = "timer0", | ||
124 | .shift = 32, | 137 | .shift = 32, |
125 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | 138 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, |
126 | .set_mode = sp804_set_mode, | 139 | .set_mode = sp804_set_mode, |
@@ -136,17 +149,24 @@ static struct irqaction sp804_timer_irq = { | |||
136 | .dev_id = &sp804_clockevent, | 149 | .dev_id = &sp804_clockevent, |
137 | }; | 150 | }; |
138 | 151 | ||
139 | void __init sp804_clockevents_init(void __iomem *base, unsigned int timer_irq) | 152 | void __init sp804_clockevents_init(void __iomem *base, unsigned int irq, |
153 | const char *name) | ||
140 | { | 154 | { |
141 | struct clock_event_device *evt = &sp804_clockevent; | 155 | struct clock_event_device *evt = &sp804_clockevent; |
156 | long rate = sp804_get_clock_rate(name); | ||
157 | |||
158 | if (rate < 0) | ||
159 | return; | ||
142 | 160 | ||
143 | clkevt_base = base; | 161 | clkevt_base = base; |
162 | clkevt_reload = DIV_ROUND_CLOSEST(rate, HZ); | ||
144 | 163 | ||
145 | evt->irq = timer_irq; | 164 | evt->name = name; |
146 | evt->mult = div_sc(TIMER_FREQ_KHZ, NSEC_PER_MSEC, evt->shift); | 165 | evt->irq = irq; |
166 | evt->mult = div_sc(rate, NSEC_PER_SEC, evt->shift); | ||
147 | evt->max_delta_ns = clockevent_delta2ns(0xffffffff, evt); | 167 | evt->max_delta_ns = clockevent_delta2ns(0xffffffff, evt); |
148 | evt->min_delta_ns = clockevent_delta2ns(0xf, evt); | 168 | evt->min_delta_ns = clockevent_delta2ns(0xf, evt); |
149 | 169 | ||
150 | setup_irq(timer_irq, &sp804_timer_irq); | 170 | setup_irq(irq, &sp804_timer_irq); |
151 | clockevents_register_device(evt); | 171 | clockevents_register_device(evt); |
152 | } | 172 | } |