diff options
author | Jonas Aaberg <jonas.aberg@stericsson.com> | 2011-09-14 04:32:51 -0400 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2011-09-22 09:44:10 -0400 |
commit | 2f73a06843e357190a7a3924c7afb0534cf6adef (patch) | |
tree | 098cc733897c0d182eadcbb445d04d3f129e733e /arch/arm/plat-nomadik | |
parent | 5f5663a436a4c54ede441b4f8ab3f947e78db345 (diff) |
ARM: plat-nomadik: timer: Add support for periodic timers
This adds support for a periodic mode in the MTU (Nomadik)
timer clockevent driver. It also include changes needed for
deeper powerstates where MTU block gets powered off.
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'arch/arm/plat-nomadik')
-rw-r--r-- | arch/arm/plat-nomadik/timer.c | 88 |
1 files changed, 60 insertions, 28 deletions
diff --git a/arch/arm/plat-nomadik/timer.c b/arch/arm/plat-nomadik/timer.c index bd638c552f04..a04b5215b6d8 100644 --- a/arch/arm/plat-nomadik/timer.c +++ b/arch/arm/plat-nomadik/timer.c | |||
@@ -23,7 +23,12 @@ | |||
23 | 23 | ||
24 | #include <plat/mtu.h> | 24 | #include <plat/mtu.h> |
25 | 25 | ||
26 | static bool clkevt_periodic; | ||
27 | static u32 clk_prescale; | ||
28 | static u32 nmdk_cycle; /* write-once */ | ||
29 | |||
26 | void __iomem *mtu_base; /* Assigned by machine code */ | 30 | void __iomem *mtu_base; /* Assigned by machine code */ |
31 | |||
27 | #ifdef CONFIG_NOMADIK_MTU_SCHED_CLOCK | 32 | #ifdef CONFIG_NOMADIK_MTU_SCHED_CLOCK |
28 | /* | 33 | /* |
29 | * Override the global weak sched_clock symbol with this | 34 | * Override the global weak sched_clock symbol with this |
@@ -49,31 +54,55 @@ static void notrace nomadik_update_sched_clock(void) | |||
49 | update_sched_clock(&cd, cyc, (u32)~0); | 54 | update_sched_clock(&cd, cyc, (u32)~0); |
50 | } | 55 | } |
51 | #endif | 56 | #endif |
57 | |||
52 | /* Clockevent device: use one-shot mode */ | 58 | /* Clockevent device: use one-shot mode */ |
59 | static int nmdk_clkevt_next(unsigned long evt, struct clock_event_device *ev) | ||
60 | { | ||
61 | writel(1 << 1, mtu_base + MTU_IMSC); | ||
62 | writel(evt, mtu_base + MTU_LR(1)); | ||
63 | /* Load highest value, enable device, enable interrupts */ | ||
64 | writel(MTU_CRn_ONESHOT | clk_prescale | | ||
65 | MTU_CRn_32BITS | MTU_CRn_ENA, | ||
66 | mtu_base + MTU_CR(1)); | ||
67 | |||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | static void nmdk_clkevt_reset(void) | ||
72 | { | ||
73 | if (clkevt_periodic) { | ||
74 | |||
75 | /* Timer: configure load and background-load, and fire it up */ | ||
76 | writel(nmdk_cycle, mtu_base + MTU_LR(1)); | ||
77 | writel(nmdk_cycle, mtu_base + MTU_BGLR(1)); | ||
78 | |||
79 | writel(MTU_CRn_PERIODIC | clk_prescale | | ||
80 | MTU_CRn_32BITS | MTU_CRn_ENA, | ||
81 | mtu_base + MTU_CR(1)); | ||
82 | writel(1 << 1, mtu_base + MTU_IMSC); | ||
83 | } else { | ||
84 | /* Generate an interrupt to start the clockevent again */ | ||
85 | (void) nmdk_clkevt_next(nmdk_cycle, NULL); | ||
86 | } | ||
87 | } | ||
88 | |||
53 | static void nmdk_clkevt_mode(enum clock_event_mode mode, | 89 | static void nmdk_clkevt_mode(enum clock_event_mode mode, |
54 | struct clock_event_device *dev) | 90 | struct clock_event_device *dev) |
55 | { | 91 | { |
56 | u32 cr; | ||
57 | 92 | ||
58 | switch (mode) { | 93 | switch (mode) { |
59 | case CLOCK_EVT_MODE_PERIODIC: | 94 | case CLOCK_EVT_MODE_PERIODIC: |
60 | pr_err("%s: periodic mode not supported\n", __func__); | 95 | clkevt_periodic = true; |
96 | nmdk_clkevt_reset(); | ||
61 | break; | 97 | break; |
62 | case CLOCK_EVT_MODE_ONESHOT: | 98 | case CLOCK_EVT_MODE_ONESHOT: |
63 | /* Load highest value, enable device, enable interrupts */ | 99 | clkevt_periodic = false; |
64 | cr = readl(mtu_base + MTU_CR(1)); | ||
65 | writel(0, mtu_base + MTU_LR(1)); | ||
66 | writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(1)); | ||
67 | writel(1 << 1, mtu_base + MTU_IMSC); | ||
68 | break; | 100 | break; |
69 | case CLOCK_EVT_MODE_SHUTDOWN: | 101 | case CLOCK_EVT_MODE_SHUTDOWN: |
70 | case CLOCK_EVT_MODE_UNUSED: | 102 | case CLOCK_EVT_MODE_UNUSED: |
71 | /* disable irq */ | ||
72 | writel(0, mtu_base + MTU_IMSC); | 103 | writel(0, mtu_base + MTU_IMSC); |
73 | /* disable timer */ | 104 | /* disable timer */ |
74 | cr = readl(mtu_base + MTU_CR(1)); | 105 | writel(0, mtu_base + MTU_CR(1)); |
75 | cr &= ~MTU_CRn_ENA; | ||
76 | writel(cr, mtu_base + MTU_CR(1)); | ||
77 | /* load some high default value */ | 106 | /* load some high default value */ |
78 | writel(0xffffffff, mtu_base + MTU_LR(1)); | 107 | writel(0xffffffff, mtu_base + MTU_LR(1)); |
79 | break; | 108 | break; |
@@ -82,16 +111,9 @@ static void nmdk_clkevt_mode(enum clock_event_mode mode, | |||
82 | } | 111 | } |
83 | } | 112 | } |
84 | 113 | ||
85 | static int nmdk_clkevt_next(unsigned long evt, struct clock_event_device *ev) | ||
86 | { | ||
87 | /* writing the value has immediate effect */ | ||
88 | writel(evt, mtu_base + MTU_LR(1)); | ||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static struct clock_event_device nmdk_clkevt = { | 114 | static struct clock_event_device nmdk_clkevt = { |
93 | .name = "mtu_1", | 115 | .name = "mtu_1", |
94 | .features = CLOCK_EVT_FEAT_ONESHOT, | 116 | .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, |
95 | .rating = 200, | 117 | .rating = 200, |
96 | .set_mode = nmdk_clkevt_mode, | 118 | .set_mode = nmdk_clkevt_mode, |
97 | .set_next_event = nmdk_clkevt_next, | 119 | .set_next_event = nmdk_clkevt_next, |
@@ -116,11 +138,23 @@ static struct irqaction nmdk_timer_irq = { | |||
116 | .dev_id = &nmdk_clkevt, | 138 | .dev_id = &nmdk_clkevt, |
117 | }; | 139 | }; |
118 | 140 | ||
141 | static void nmdk_clksrc_reset(void) | ||
142 | { | ||
143 | /* Disable */ | ||
144 | writel(0, mtu_base + MTU_CR(0)); | ||
145 | |||
146 | /* ClockSource: configure load and background-load, and fire it up */ | ||
147 | writel(nmdk_cycle, mtu_base + MTU_LR(0)); | ||
148 | writel(nmdk_cycle, mtu_base + MTU_BGLR(0)); | ||
149 | |||
150 | writel(clk_prescale | MTU_CRn_32BITS | MTU_CRn_ENA, | ||
151 | mtu_base + MTU_CR(0)); | ||
152 | } | ||
153 | |||
119 | void __init nmdk_timer_init(void) | 154 | void __init nmdk_timer_init(void) |
120 | { | 155 | { |
121 | unsigned long rate; | 156 | unsigned long rate; |
122 | struct clk *clk0; | 157 | struct clk *clk0; |
123 | u32 cr = MTU_CRn_32BITS; | ||
124 | 158 | ||
125 | clk0 = clk_get_sys("mtu0", NULL); | 159 | clk0 = clk_get_sys("mtu0", NULL); |
126 | BUG_ON(IS_ERR(clk0)); | 160 | BUG_ON(IS_ERR(clk0)); |
@@ -138,16 +172,16 @@ void __init nmdk_timer_init(void) | |||
138 | rate = clk_get_rate(clk0); | 172 | rate = clk_get_rate(clk0); |
139 | if (rate > 32000000) { | 173 | if (rate > 32000000) { |
140 | rate /= 16; | 174 | rate /= 16; |
141 | cr |= MTU_CRn_PRESCALE_16; | 175 | clk_prescale = MTU_CRn_PRESCALE_16; |
142 | } else { | 176 | } else { |
143 | cr |= MTU_CRn_PRESCALE_1; | 177 | clk_prescale = MTU_CRn_PRESCALE_1; |
144 | } | 178 | } |
145 | 179 | ||
180 | nmdk_cycle = (rate + HZ/2) / HZ; | ||
181 | |||
182 | |||
146 | /* Timer 0 is the free running clocksource */ | 183 | /* Timer 0 is the free running clocksource */ |
147 | writel(cr, mtu_base + MTU_CR(0)); | 184 | nmdk_clksrc_reset(); |
148 | writel(0, mtu_base + MTU_LR(0)); | ||
149 | writel(0, mtu_base + MTU_BGLR(0)); | ||
150 | writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(0)); | ||
151 | 185 | ||
152 | if (clocksource_mmio_init(mtu_base + MTU_VAL(0), "mtu_0", | 186 | if (clocksource_mmio_init(mtu_base + MTU_VAL(0), "mtu_0", |
153 | rate, 200, 32, clocksource_mmio_readl_down)) | 187 | rate, 200, 32, clocksource_mmio_readl_down)) |
@@ -160,8 +194,6 @@ void __init nmdk_timer_init(void) | |||
160 | 194 | ||
161 | clockevents_calc_mult_shift(&nmdk_clkevt, rate, MTU_MIN_RANGE); | 195 | clockevents_calc_mult_shift(&nmdk_clkevt, rate, MTU_MIN_RANGE); |
162 | 196 | ||
163 | writel(cr | MTU_CRn_ONESHOT, mtu_base + MTU_CR(1)); /* off, currently */ | ||
164 | |||
165 | nmdk_clkevt.max_delta_ns = | 197 | nmdk_clkevt.max_delta_ns = |
166 | clockevent_delta2ns(0xffffffff, &nmdk_clkevt); | 198 | clockevent_delta2ns(0xffffffff, &nmdk_clkevt); |
167 | nmdk_clkevt.min_delta_ns = | 199 | nmdk_clkevt.min_delta_ns = |