diff options
Diffstat (limited to 'arch/x86/kernel/mfgpt_32.c')
-rw-r--r-- | arch/x86/kernel/mfgpt_32.c | 123 |
1 files changed, 65 insertions, 58 deletions
diff --git a/arch/x86/kernel/mfgpt_32.c b/arch/x86/kernel/mfgpt_32.c index 219f86eb6123..027fc067b399 100644 --- a/arch/x86/kernel/mfgpt_32.c +++ b/arch/x86/kernel/mfgpt_32.c | |||
@@ -12,48 +12,37 @@ | |||
12 | */ | 12 | */ |
13 | 13 | ||
14 | /* | 14 | /* |
15 | * We are using the 32Khz input clock - its the only one that has the | 15 | * We are using the 32.768kHz input clock - it's the only one that has the |
16 | * ranges we find desirable. The following table lists the suitable | 16 | * ranges we find desirable. The following table lists the suitable |
17 | * divisors and the associated hz, minimum interval | 17 | * divisors and the associated Hz, minimum interval and the maximum interval: |
18 | * and the maximum interval: | ||
19 | * | 18 | * |
20 | * Divisor Hz Min Delta (S) Max Delta (S) | 19 | * Divisor Hz Min Delta (s) Max Delta (s) |
21 | * 1 32000 .0005 2.048 | 20 | * 1 32768 .00048828125 2.000 |
22 | * 2 16000 .001 4.096 | 21 | * 2 16384 .0009765625 4.000 |
23 | * 4 8000 .002 8.192 | 22 | * 4 8192 .001953125 8.000 |
24 | * 8 4000 .004 16.384 | 23 | * 8 4096 .00390625 16.000 |
25 | * 16 2000 .008 32.768 | 24 | * 16 2048 .0078125 32.000 |
26 | * 32 1000 .016 65.536 | 25 | * 32 1024 .015625 64.000 |
27 | * 64 500 .032 131.072 | 26 | * 64 512 .03125 128.000 |
28 | * 128 250 .064 262.144 | 27 | * 128 256 .0625 256.000 |
29 | * 256 125 .128 524.288 | 28 | * 256 128 .125 512.000 |
30 | */ | 29 | */ |
31 | 30 | ||
32 | #include <linux/kernel.h> | 31 | #include <linux/kernel.h> |
33 | #include <linux/interrupt.h> | 32 | #include <linux/interrupt.h> |
34 | #include <linux/module.h> | ||
35 | #include <asm/geode.h> | 33 | #include <asm/geode.h> |
36 | 34 | ||
37 | #define F_AVAIL 0x01 | ||
38 | |||
39 | static struct mfgpt_timer_t { | 35 | static struct mfgpt_timer_t { |
40 | int flags; | 36 | unsigned int avail:1; |
41 | struct module *owner; | ||
42 | } mfgpt_timers[MFGPT_MAX_TIMERS]; | 37 | } mfgpt_timers[MFGPT_MAX_TIMERS]; |
43 | 38 | ||
44 | /* Selected from the table above */ | 39 | /* Selected from the table above */ |
45 | 40 | ||
46 | #define MFGPT_DIVISOR 16 | 41 | #define MFGPT_DIVISOR 16 |
47 | #define MFGPT_SCALE 4 /* divisor = 2^(scale) */ | 42 | #define MFGPT_SCALE 4 /* divisor = 2^(scale) */ |
48 | #define MFGPT_HZ (32000 / MFGPT_DIVISOR) | 43 | #define MFGPT_HZ (32768 / MFGPT_DIVISOR) |
49 | #define MFGPT_PERIODIC (MFGPT_HZ / HZ) | 44 | #define MFGPT_PERIODIC (MFGPT_HZ / HZ) |
50 | 45 | ||
51 | #ifdef CONFIG_GEODE_MFGPT_TIMER | ||
52 | static int __init mfgpt_timer_setup(void); | ||
53 | #else | ||
54 | #define mfgpt_timer_setup() (0) | ||
55 | #endif | ||
56 | |||
57 | /* Allow for disabling of MFGPTs */ | 46 | /* Allow for disabling of MFGPTs */ |
58 | static int disable; | 47 | static int disable; |
59 | static int __init mfgpt_disable(char *s) | 48 | static int __init mfgpt_disable(char *s) |
@@ -85,28 +74,37 @@ __setup("mfgptfix", mfgpt_fix); | |||
85 | * In other cases (such as with VSAless OpenFirmware), the system firmware | 74 | * In other cases (such as with VSAless OpenFirmware), the system firmware |
86 | * leaves timers available for us to use. | 75 | * leaves timers available for us to use. |
87 | */ | 76 | */ |
88 | int __init geode_mfgpt_detect(void) | 77 | |
78 | |||
79 | static int timers = -1; | ||
80 | |||
81 | static void geode_mfgpt_detect(void) | ||
89 | { | 82 | { |
90 | int count = 0, i; | 83 | int i; |
91 | u16 val; | 84 | u16 val; |
92 | 85 | ||
86 | timers = 0; | ||
87 | |||
93 | if (disable) { | 88 | if (disable) { |
94 | printk(KERN_INFO "geode-mfgpt: Skipping MFGPT setup\n"); | 89 | printk(KERN_INFO "geode-mfgpt: MFGPT support is disabled\n"); |
95 | return 0; | 90 | goto done; |
91 | } | ||
92 | |||
93 | if (!geode_get_dev_base(GEODE_DEV_MFGPT)) { | ||
94 | printk(KERN_INFO "geode-mfgpt: MFGPT LBAR is not set up\n"); | ||
95 | goto done; | ||
96 | } | 96 | } |
97 | 97 | ||
98 | for (i = 0; i < MFGPT_MAX_TIMERS; i++) { | 98 | for (i = 0; i < MFGPT_MAX_TIMERS; i++) { |
99 | val = geode_mfgpt_read(i, MFGPT_REG_SETUP); | 99 | val = geode_mfgpt_read(i, MFGPT_REG_SETUP); |
100 | if (!(val & MFGPT_SETUP_SETUP)) { | 100 | if (!(val & MFGPT_SETUP_SETUP)) { |
101 | mfgpt_timers[i].flags = F_AVAIL; | 101 | mfgpt_timers[i].avail = 1; |
102 | count++; | 102 | timers++; |
103 | } | 103 | } |
104 | } | 104 | } |
105 | 105 | ||
106 | /* set up clock event device, if desired */ | 106 | done: |
107 | i = mfgpt_timer_setup(); | 107 | printk(KERN_INFO "geode-mfgpt: %d MFGPT timers available.\n", timers); |
108 | |||
109 | return count; | ||
110 | } | 108 | } |
111 | 109 | ||
112 | int geode_mfgpt_toggle_event(int timer, int cmp, int event, int enable) | 110 | int geode_mfgpt_toggle_event(int timer, int cmp, int event, int enable) |
@@ -183,36 +181,41 @@ int geode_mfgpt_set_irq(int timer, int cmp, int irq, int enable) | |||
183 | return 0; | 181 | return 0; |
184 | } | 182 | } |
185 | 183 | ||
186 | static int mfgpt_get(int timer, struct module *owner) | 184 | static int mfgpt_get(int timer) |
187 | { | 185 | { |
188 | mfgpt_timers[timer].flags &= ~F_AVAIL; | 186 | mfgpt_timers[timer].avail = 0; |
189 | mfgpt_timers[timer].owner = owner; | ||
190 | printk(KERN_INFO "geode-mfgpt: Registered timer %d\n", timer); | 187 | printk(KERN_INFO "geode-mfgpt: Registered timer %d\n", timer); |
191 | return timer; | 188 | return timer; |
192 | } | 189 | } |
193 | 190 | ||
194 | int geode_mfgpt_alloc_timer(int timer, int domain, struct module *owner) | 191 | int geode_mfgpt_alloc_timer(int timer, int domain) |
195 | { | 192 | { |
196 | int i; | 193 | int i; |
197 | 194 | ||
198 | if (!geode_get_dev_base(GEODE_DEV_MFGPT)) | 195 | if (timers == -1) { |
199 | return -ENODEV; | 196 | /* timers haven't been detected yet */ |
197 | geode_mfgpt_detect(); | ||
198 | } | ||
199 | |||
200 | if (!timers) | ||
201 | return -1; | ||
202 | |||
200 | if (timer >= MFGPT_MAX_TIMERS) | 203 | if (timer >= MFGPT_MAX_TIMERS) |
201 | return -EIO; | 204 | return -1; |
202 | 205 | ||
203 | if (timer < 0) { | 206 | if (timer < 0) { |
204 | /* Try to find an available timer */ | 207 | /* Try to find an available timer */ |
205 | for (i = 0; i < MFGPT_MAX_TIMERS; i++) { | 208 | for (i = 0; i < MFGPT_MAX_TIMERS; i++) { |
206 | if (mfgpt_timers[i].flags & F_AVAIL) | 209 | if (mfgpt_timers[i].avail) |
207 | return mfgpt_get(i, owner); | 210 | return mfgpt_get(i); |
208 | 211 | ||
209 | if (i == 5 && domain == MFGPT_DOMAIN_WORKING) | 212 | if (i == 5 && domain == MFGPT_DOMAIN_WORKING) |
210 | break; | 213 | break; |
211 | } | 214 | } |
212 | } else { | 215 | } else { |
213 | /* If they requested a specific timer, try to honor that */ | 216 | /* If they requested a specific timer, try to honor that */ |
214 | if (mfgpt_timers[timer].flags & F_AVAIL) | 217 | if (mfgpt_timers[timer].avail) |
215 | return mfgpt_get(timer, owner); | 218 | return mfgpt_get(timer); |
216 | } | 219 | } |
217 | 220 | ||
218 | /* No timers available - too bad */ | 221 | /* No timers available - too bad */ |
@@ -244,10 +247,11 @@ static int __init mfgpt_setup(char *str) | |||
244 | } | 247 | } |
245 | __setup("mfgpt_irq=", mfgpt_setup); | 248 | __setup("mfgpt_irq=", mfgpt_setup); |
246 | 249 | ||
247 | static inline void mfgpt_disable_timer(u16 clock) | 250 | static void mfgpt_disable_timer(u16 clock) |
248 | { | 251 | { |
249 | u16 val = geode_mfgpt_read(clock, MFGPT_REG_SETUP); | 252 | /* avoid races by clearing CMP1 and CMP2 unconditionally */ |
250 | geode_mfgpt_write(clock, MFGPT_REG_SETUP, val & ~MFGPT_SETUP_CNTEN); | 253 | geode_mfgpt_write(clock, MFGPT_REG_SETUP, (u16) ~MFGPT_SETUP_CNTEN | |
254 | MFGPT_SETUP_CMP1 | MFGPT_SETUP_CMP2); | ||
251 | } | 255 | } |
252 | 256 | ||
253 | static int mfgpt_next_event(unsigned long, struct clock_event_device *); | 257 | static int mfgpt_next_event(unsigned long, struct clock_event_device *); |
@@ -263,7 +267,7 @@ static struct clock_event_device mfgpt_clockevent = { | |||
263 | .shift = 32 | 267 | .shift = 32 |
264 | }; | 268 | }; |
265 | 269 | ||
266 | static inline void mfgpt_start_timer(u16 clock, u16 delta) | 270 | static void mfgpt_start_timer(u16 delta) |
267 | { | 271 | { |
268 | geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_CMP2, (u16) delta); | 272 | geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_CMP2, (u16) delta); |
269 | geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_COUNTER, 0); | 273 | geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_COUNTER, 0); |
@@ -278,21 +282,25 @@ static void mfgpt_set_mode(enum clock_event_mode mode, | |||
278 | mfgpt_disable_timer(mfgpt_event_clock); | 282 | mfgpt_disable_timer(mfgpt_event_clock); |
279 | 283 | ||
280 | if (mode == CLOCK_EVT_MODE_PERIODIC) | 284 | if (mode == CLOCK_EVT_MODE_PERIODIC) |
281 | mfgpt_start_timer(mfgpt_event_clock, MFGPT_PERIODIC); | 285 | mfgpt_start_timer(MFGPT_PERIODIC); |
282 | 286 | ||
283 | mfgpt_tick_mode = mode; | 287 | mfgpt_tick_mode = mode; |
284 | } | 288 | } |
285 | 289 | ||
286 | static int mfgpt_next_event(unsigned long delta, struct clock_event_device *evt) | 290 | static int mfgpt_next_event(unsigned long delta, struct clock_event_device *evt) |
287 | { | 291 | { |
288 | mfgpt_start_timer(mfgpt_event_clock, delta); | 292 | mfgpt_start_timer(delta); |
289 | return 0; | 293 | return 0; |
290 | } | 294 | } |
291 | 295 | ||
292 | /* Assume (foolishly?), that this interrupt was due to our tick */ | ||
293 | |||
294 | static irqreturn_t mfgpt_tick(int irq, void *dev_id) | 296 | static irqreturn_t mfgpt_tick(int irq, void *dev_id) |
295 | { | 297 | { |
298 | u16 val = geode_mfgpt_read(mfgpt_event_clock, MFGPT_REG_SETUP); | ||
299 | |||
300 | /* See if the interrupt was for us */ | ||
301 | if (!(val & (MFGPT_SETUP_SETUP | MFGPT_SETUP_CMP2 | MFGPT_SETUP_CMP1))) | ||
302 | return IRQ_NONE; | ||
303 | |||
296 | /* Turn off the clock (and clear the event) */ | 304 | /* Turn off the clock (and clear the event) */ |
297 | mfgpt_disable_timer(mfgpt_event_clock); | 305 | mfgpt_disable_timer(mfgpt_event_clock); |
298 | 306 | ||
@@ -320,13 +328,12 @@ static struct irqaction mfgptirq = { | |||
320 | .name = "mfgpt-timer" | 328 | .name = "mfgpt-timer" |
321 | }; | 329 | }; |
322 | 330 | ||
323 | static int __init mfgpt_timer_setup(void) | 331 | int __init mfgpt_timer_setup(void) |
324 | { | 332 | { |
325 | int timer, ret; | 333 | int timer, ret; |
326 | u16 val; | 334 | u16 val; |
327 | 335 | ||
328 | timer = geode_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING, | 336 | timer = geode_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING); |
329 | THIS_MODULE); | ||
330 | if (timer < 0) { | 337 | if (timer < 0) { |
331 | printk(KERN_ERR | 338 | printk(KERN_ERR |
332 | "mfgpt-timer: Could not allocate a MFPGT timer\n"); | 339 | "mfgpt-timer: Could not allocate a MFPGT timer\n"); |
@@ -363,7 +370,7 @@ static int __init mfgpt_timer_setup(void) | |||
363 | &mfgpt_clockevent); | 370 | &mfgpt_clockevent); |
364 | 371 | ||
365 | printk(KERN_INFO | 372 | printk(KERN_INFO |
366 | "mfgpt-timer: registering the MFGT timer as a clock event.\n"); | 373 | "mfgpt-timer: registering the MFGPT timer as a clock event.\n"); |
367 | clockevents_register_device(&mfgpt_clockevent); | 374 | clockevents_register_device(&mfgpt_clockevent); |
368 | 375 | ||
369 | return 0; | 376 | return 0; |