diff options
| -rw-r--r-- | arch/x86/kernel/mfgpt_32.c | 52 | ||||
| -rw-r--r-- | include/asm-x86/geode.h | 3 |
2 files changed, 39 insertions, 16 deletions
diff --git a/arch/x86/kernel/mfgpt_32.c b/arch/x86/kernel/mfgpt_32.c index 07c0f828f488..3b599518c322 100644 --- a/arch/x86/kernel/mfgpt_32.c +++ b/arch/x86/kernel/mfgpt_32.c | |||
| @@ -33,6 +33,8 @@ | |||
| 33 | #include <linux/module.h> | 33 | #include <linux/module.h> |
| 34 | #include <asm/geode.h> | 34 | #include <asm/geode.h> |
| 35 | 35 | ||
| 36 | #define MFGPT_DEFAULT_IRQ 7 | ||
| 37 | |||
| 36 | static struct mfgpt_timer_t { | 38 | static struct mfgpt_timer_t { |
| 37 | unsigned int avail:1; | 39 | unsigned int avail:1; |
| 38 | } mfgpt_timers[MFGPT_MAX_TIMERS]; | 40 | } mfgpt_timers[MFGPT_MAX_TIMERS]; |
| @@ -157,29 +159,48 @@ int geode_mfgpt_toggle_event(int timer, int cmp, int event, int enable) | |||
| 157 | } | 159 | } |
| 158 | EXPORT_SYMBOL_GPL(geode_mfgpt_toggle_event); | 160 | EXPORT_SYMBOL_GPL(geode_mfgpt_toggle_event); |
| 159 | 161 | ||
| 160 | int geode_mfgpt_set_irq(int timer, int cmp, int irq, int enable) | 162 | int geode_mfgpt_set_irq(int timer, int cmp, int *irq, int enable) |
| 161 | { | 163 | { |
| 162 | u32 val, dummy; | 164 | u32 zsel, lpc, dummy; |
| 163 | int offset; | 165 | int shift; |
| 164 | 166 | ||
| 165 | if (timer < 0 || timer >= MFGPT_MAX_TIMERS) | 167 | if (timer < 0 || timer >= MFGPT_MAX_TIMERS) |
| 166 | return -EIO; | 168 | return -EIO; |
| 167 | 169 | ||
| 168 | if (geode_mfgpt_toggle_event(timer, cmp, MFGPT_EVENT_IRQ, enable)) | 170 | /* |
| 171 | * Unfortunately, MFGPTs come in pairs sharing their IRQ lines. If VSA | ||
| 172 | * is using the same CMP of the timer's Siamese twin, the IRQ is set to | ||
| 173 | * 2, and we mustn't use nor change it. | ||
| 174 | * XXX: Likewise, 2 Linux drivers might clash if the 2nd overwrites the | ||
| 175 | * IRQ of the 1st. This can only happen if forcing an IRQ, calling this | ||
| 176 | * with *irq==0 is safe. Currently there _are_ no 2 drivers. | ||
| 177 | */ | ||
| 178 | rdmsr(MSR_PIC_ZSEL_LOW, zsel, dummy); | ||
| 179 | shift = ((cmp == MFGPT_CMP1 ? 0 : 4) + timer % 4) * 4; | ||
| 180 | if (((zsel >> shift) & 0xF) == 2) | ||
| 169 | return -EIO; | 181 | return -EIO; |
| 170 | 182 | ||
| 171 | rdmsr(MSR_PIC_ZSEL_LOW, val, dummy); | 183 | /* Choose IRQ: if none supplied, keep IRQ already set or use default */ |
| 184 | if (!*irq) | ||
| 185 | *irq = (zsel >> shift) & 0xF; | ||
| 186 | if (!*irq) | ||
| 187 | *irq = MFGPT_DEFAULT_IRQ; | ||
| 172 | 188 | ||
| 173 | offset = (timer % 4) * 4; | 189 | /* Can't use IRQ if it's 0 (=disabled), 2, or routed to LPC */ |
| 174 | 190 | if (*irq < 1 || *irq == 2 || *irq > 15) | |
| 175 | val &= ~((0xF << offset) | (0xF << (offset + 16))); | 191 | return -EIO; |
| 192 | rdmsr(MSR_PIC_IRQM_LPC, lpc, dummy); | ||
| 193 | if (lpc & (1 << *irq)) | ||
| 194 | return -EIO; | ||
| 176 | 195 | ||
| 196 | /* All chosen and checked - go for it */ | ||
| 197 | if (geode_mfgpt_toggle_event(timer, cmp, MFGPT_EVENT_IRQ, enable)) | ||
| 198 | return -EIO; | ||
| 177 | if (enable) { | 199 | if (enable) { |
| 178 | val |= (irq & 0x0F) << (offset); | 200 | zsel = (zsel & ~(0xF << shift)) | (*irq << shift); |
| 179 | val |= (irq & 0x0F) << (offset + 16); | 201 | wrmsr(MSR_PIC_ZSEL_LOW, zsel, dummy); |
| 180 | } | 202 | } |
| 181 | 203 | ||
| 182 | wrmsr(MSR_PIC_ZSEL_LOW, val, dummy); | ||
| 183 | return 0; | 204 | return 0; |
| 184 | } | 205 | } |
| 185 | 206 | ||
| @@ -242,7 +263,7 @@ EXPORT_SYMBOL_GPL(geode_mfgpt_alloc_timer); | |||
| 242 | static unsigned int mfgpt_tick_mode = CLOCK_EVT_MODE_SHUTDOWN; | 263 | static unsigned int mfgpt_tick_mode = CLOCK_EVT_MODE_SHUTDOWN; |
| 243 | static u16 mfgpt_event_clock; | 264 | static u16 mfgpt_event_clock; |
| 244 | 265 | ||
| 245 | static int irq = 7; | 266 | static int irq; |
| 246 | static int __init mfgpt_setup(char *str) | 267 | static int __init mfgpt_setup(char *str) |
| 247 | { | 268 | { |
| 248 | get_option(&str, &irq); | 269 | get_option(&str, &irq); |
| @@ -346,7 +367,7 @@ int __init mfgpt_timer_setup(void) | |||
| 346 | mfgpt_event_clock = timer; | 367 | mfgpt_event_clock = timer; |
| 347 | 368 | ||
| 348 | /* Set up the IRQ on the MFGPT side */ | 369 | /* Set up the IRQ on the MFGPT side */ |
| 349 | if (geode_mfgpt_setup_irq(mfgpt_event_clock, MFGPT_CMP2, irq)) { | 370 | if (geode_mfgpt_setup_irq(mfgpt_event_clock, MFGPT_CMP2, &irq)) { |
| 350 | printk(KERN_ERR "mfgpt-timer: Could not set up IRQ %d\n", irq); | 371 | printk(KERN_ERR "mfgpt-timer: Could not set up IRQ %d\n", irq); |
| 351 | return -EIO; | 372 | return -EIO; |
| 352 | } | 373 | } |
| @@ -374,13 +395,14 @@ int __init mfgpt_timer_setup(void) | |||
| 374 | &mfgpt_clockevent); | 395 | &mfgpt_clockevent); |
| 375 | 396 | ||
| 376 | printk(KERN_INFO | 397 | printk(KERN_INFO |
| 377 | "mfgpt-timer: registering the MFGPT timer as a clock event.\n"); | 398 | "mfgpt-timer: Registering MFGPT timer %d as a clock event, using IRQ %d\n", |
| 399 | timer, irq); | ||
| 378 | clockevents_register_device(&mfgpt_clockevent); | 400 | clockevents_register_device(&mfgpt_clockevent); |
| 379 | 401 | ||
| 380 | return 0; | 402 | return 0; |
| 381 | 403 | ||
| 382 | err: | 404 | err: |
| 383 | geode_mfgpt_release_irq(mfgpt_event_clock, MFGPT_CMP2, irq); | 405 | geode_mfgpt_release_irq(mfgpt_event_clock, MFGPT_CMP2, &irq); |
| 384 | printk(KERN_ERR | 406 | printk(KERN_ERR |
| 385 | "mfgpt-timer: Unable to set up the MFGPT clock source\n"); | 407 | "mfgpt-timer: Unable to set up the MFGPT clock source\n"); |
| 386 | return -EIO; | 408 | return -EIO; |
diff --git a/include/asm-x86/geode.h b/include/asm-x86/geode.h index bb06027fc83e..2c1cda0b8a86 100644 --- a/include/asm-x86/geode.h +++ b/include/asm-x86/geode.h | |||
| @@ -50,6 +50,7 @@ extern int geode_get_dev_base(unsigned int dev); | |||
| 50 | #define MSR_PIC_YSEL_HIGH 0x51400021 | 50 | #define MSR_PIC_YSEL_HIGH 0x51400021 |
| 51 | #define MSR_PIC_ZSEL_LOW 0x51400022 | 51 | #define MSR_PIC_ZSEL_LOW 0x51400022 |
| 52 | #define MSR_PIC_ZSEL_HIGH 0x51400023 | 52 | #define MSR_PIC_ZSEL_HIGH 0x51400023 |
| 53 | #define MSR_PIC_IRQM_LPC 0x51400025 | ||
| 53 | 54 | ||
| 54 | #define MSR_MFGPT_IRQ 0x51400028 | 55 | #define MSR_MFGPT_IRQ 0x51400028 |
| 55 | #define MSR_MFGPT_NR 0x51400029 | 56 | #define MSR_MFGPT_NR 0x51400029 |
| @@ -237,7 +238,7 @@ static inline u16 geode_mfgpt_read(int timer, u16 reg) | |||
| 237 | } | 238 | } |
| 238 | 239 | ||
| 239 | extern int geode_mfgpt_toggle_event(int timer, int cmp, int event, int enable); | 240 | extern int geode_mfgpt_toggle_event(int timer, int cmp, int event, int enable); |
| 240 | extern int geode_mfgpt_set_irq(int timer, int cmp, int irq, int enable); | 241 | extern int geode_mfgpt_set_irq(int timer, int cmp, int *irq, int enable); |
| 241 | extern int geode_mfgpt_alloc_timer(int timer, int domain); | 242 | extern int geode_mfgpt_alloc_timer(int timer, int domain); |
| 242 | 243 | ||
| 243 | #define geode_mfgpt_setup_irq(t, c, i) geode_mfgpt_set_irq((t), (c), (i), 1) | 244 | #define geode_mfgpt_setup_irq(t, c, i) geode_mfgpt_set_irq((t), (c), (i), 1) |
