diff options
author | Jacob Pan <jacob.jun.pan@linux.intel.com> | 2010-05-19 15:01:25 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2010-05-19 16:45:39 -0400 |
commit | a875c01944f0d750eeb1ef3133feceb13f13c4b3 (patch) | |
tree | cdd39431457cdeed92d67420a89296cbfb23f620 | |
parent | a0c173bd8a3fd0541be8e4ef962170e48d8811c7 (diff) |
x86, mrst: add more timer config options
Always-on local APIC timer (ARAT) has been introduced to Medfield, along
with the platform APB timers we have more timer configuration options
between Moorestown and Medfield.
This patch adds run-time detection of avaiable timer features so that
we can treat Medfield as a variant of Moorestown and set up the optimal
timer options for each platform. i.e.
Medfield: per cpu always-on local APIC timer
Moorestown: per cpu APB timer
Manual override is possible via cmdline option x86_mrst_timer.
Signed-off-by: Jacob Pan <jacob.jun.pan@linux.intel.com>
LKML-Reference: <1274295685-6774-4-git-send-email-jacob.jun.pan@linux.intel.com>
Acked-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r-- | arch/x86/include/asm/apb_timer.h | 1 | ||||
-rw-r--r-- | arch/x86/include/asm/mrst.h | 1 | ||||
-rw-r--r-- | arch/x86/kernel/apb_timer.c | 37 | ||||
-rw-r--r-- | arch/x86/kernel/mrst.c | 88 |
4 files changed, 72 insertions, 55 deletions
diff --git a/arch/x86/include/asm/apb_timer.h b/arch/x86/include/asm/apb_timer.h index c74a2eebe570..a69b1ac9eaf8 100644 --- a/arch/x86/include/asm/apb_timer.h +++ b/arch/x86/include/asm/apb_timer.h | |||
@@ -55,7 +55,6 @@ extern unsigned long apbt_quick_calibrate(void); | |||
55 | extern int arch_setup_apbt_irqs(int irq, int trigger, int mask, int cpu); | 55 | extern int arch_setup_apbt_irqs(int irq, int trigger, int mask, int cpu); |
56 | extern void apbt_setup_secondary_clock(void); | 56 | extern void apbt_setup_secondary_clock(void); |
57 | extern unsigned int boot_cpu_id; | 57 | extern unsigned int boot_cpu_id; |
58 | extern int disable_apbt_percpu; | ||
59 | 58 | ||
60 | extern struct sfi_timer_table_entry *sfi_get_mtmr(int hint); | 59 | extern struct sfi_timer_table_entry *sfi_get_mtmr(int hint); |
61 | extern void sfi_free_mtmr(struct sfi_timer_table_entry *mtmr); | 60 | extern void sfi_free_mtmr(struct sfi_timer_table_entry *mtmr); |
diff --git a/arch/x86/include/asm/mrst.h b/arch/x86/include/asm/mrst.h index dc5c8500bfc7..67ad31545778 100644 --- a/arch/x86/include/asm/mrst.h +++ b/arch/x86/include/asm/mrst.h | |||
@@ -12,6 +12,7 @@ | |||
12 | #define _ASM_X86_MRST_H | 12 | #define _ASM_X86_MRST_H |
13 | extern int pci_mrst_init(void); | 13 | extern int pci_mrst_init(void); |
14 | extern int mrst_identify_cpu(void); | 14 | extern int mrst_identify_cpu(void); |
15 | extern int mrst_timer_options __cpuinitdata; | ||
15 | int __init sfi_parse_mrtc(struct sfi_table_header *table); | 16 | int __init sfi_parse_mrtc(struct sfi_table_header *table); |
16 | 17 | ||
17 | /* | 18 | /* |
diff --git a/arch/x86/kernel/apb_timer.c b/arch/x86/kernel/apb_timer.c index a35347501d36..8dd77800ff5d 100644 --- a/arch/x86/kernel/apb_timer.c +++ b/arch/x86/kernel/apb_timer.c | |||
@@ -43,10 +43,11 @@ | |||
43 | 43 | ||
44 | #include <asm/fixmap.h> | 44 | #include <asm/fixmap.h> |
45 | #include <asm/apb_timer.h> | 45 | #include <asm/apb_timer.h> |
46 | #include <asm/mrst.h> | ||
46 | 47 | ||
47 | #define APBT_MASK CLOCKSOURCE_MASK(32) | 48 | #define APBT_MASK CLOCKSOURCE_MASK(32) |
48 | #define APBT_SHIFT 22 | 49 | #define APBT_SHIFT 22 |
49 | #define APBT_CLOCKEVENT_RATING 150 | 50 | #define APBT_CLOCKEVENT_RATING 110 |
50 | #define APBT_CLOCKSOURCE_RATING 250 | 51 | #define APBT_CLOCKSOURCE_RATING 250 |
51 | #define APBT_MIN_DELTA_USEC 200 | 52 | #define APBT_MIN_DELTA_USEC 200 |
52 | 53 | ||
@@ -83,8 +84,6 @@ struct apbt_dev { | |||
83 | char name[10]; | 84 | char name[10]; |
84 | }; | 85 | }; |
85 | 86 | ||
86 | int disable_apbt_percpu __cpuinitdata; | ||
87 | |||
88 | static DEFINE_PER_CPU(struct apbt_dev, cpu_apbt_dev); | 87 | static DEFINE_PER_CPU(struct apbt_dev, cpu_apbt_dev); |
89 | 88 | ||
90 | #ifdef CONFIG_SMP | 89 | #ifdef CONFIG_SMP |
@@ -195,29 +194,6 @@ static struct clock_event_device apbt_clockevent = { | |||
195 | }; | 194 | }; |
196 | 195 | ||
197 | /* | 196 | /* |
198 | * if user does not want to use per CPU apb timer, just give it a lower rating | ||
199 | * than local apic timer and skip the late per cpu timer init. | ||
200 | */ | ||
201 | static inline int __init setup_x86_mrst_timer(char *arg) | ||
202 | { | ||
203 | if (!arg) | ||
204 | return -EINVAL; | ||
205 | |||
206 | if (strcmp("apbt_only", arg) == 0) | ||
207 | disable_apbt_percpu = 0; | ||
208 | else if (strcmp("lapic_and_apbt", arg) == 0) | ||
209 | disable_apbt_percpu = 1; | ||
210 | else { | ||
211 | pr_warning("X86 MRST timer option %s not recognised" | ||
212 | " use x86_mrst_timer=apbt_only or lapic_and_apbt\n", | ||
213 | arg); | ||
214 | return -EINVAL; | ||
215 | } | ||
216 | return 0; | ||
217 | } | ||
218 | __setup("x86_mrst_timer=", setup_x86_mrst_timer); | ||
219 | |||
220 | /* | ||
221 | * start count down from 0xffff_ffff. this is done by toggling the enable bit | 197 | * start count down from 0xffff_ffff. this is done by toggling the enable bit |
222 | * then load initial load count to ~0. | 198 | * then load initial load count to ~0. |
223 | */ | 199 | */ |
@@ -335,7 +311,7 @@ static int __init apbt_clockevent_register(void) | |||
335 | adev->num = smp_processor_id(); | 311 | adev->num = smp_processor_id(); |
336 | memcpy(&adev->evt, &apbt_clockevent, sizeof(struct clock_event_device)); | 312 | memcpy(&adev->evt, &apbt_clockevent, sizeof(struct clock_event_device)); |
337 | 313 | ||
338 | if (disable_apbt_percpu) { | 314 | if (mrst_timer_options == MRST_TIMER_LAPIC_APBT) { |
339 | apbt_clockevent.rating = APBT_CLOCKEVENT_RATING - 100; | 315 | apbt_clockevent.rating = APBT_CLOCKEVENT_RATING - 100; |
340 | global_clock_event = &adev->evt; | 316 | global_clock_event = &adev->evt; |
341 | printk(KERN_DEBUG "%s clockevent registered as global\n", | 317 | printk(KERN_DEBUG "%s clockevent registered as global\n", |
@@ -429,7 +405,8 @@ static int apbt_cpuhp_notify(struct notifier_block *n, | |||
429 | 405 | ||
430 | static __init int apbt_late_init(void) | 406 | static __init int apbt_late_init(void) |
431 | { | 407 | { |
432 | if (disable_apbt_percpu || !apb_timer_block_enabled) | 408 | if (mrst_timer_options == MRST_TIMER_LAPIC_APBT || |
409 | !apb_timer_block_enabled) | ||
433 | return 0; | 410 | return 0; |
434 | /* This notifier should be called after workqueue is ready */ | 411 | /* This notifier should be called after workqueue is ready */ |
435 | hotcpu_notifier(apbt_cpuhp_notify, -20); | 412 | hotcpu_notifier(apbt_cpuhp_notify, -20); |
@@ -450,6 +427,8 @@ static void apbt_set_mode(enum clock_event_mode mode, | |||
450 | int timer_num; | 427 | int timer_num; |
451 | struct apbt_dev *adev = EVT_TO_APBT_DEV(evt); | 428 | struct apbt_dev *adev = EVT_TO_APBT_DEV(evt); |
452 | 429 | ||
430 | BUG_ON(!apbt_virt_address); | ||
431 | |||
453 | timer_num = adev->num; | 432 | timer_num = adev->num; |
454 | pr_debug("%s CPU %d timer %d mode=%d\n", | 433 | pr_debug("%s CPU %d timer %d mode=%d\n", |
455 | __func__, first_cpu(*evt->cpumask), timer_num, mode); | 434 | __func__, first_cpu(*evt->cpumask), timer_num, mode); |
@@ -676,7 +655,7 @@ void __init apbt_time_init(void) | |||
676 | } | 655 | } |
677 | #ifdef CONFIG_SMP | 656 | #ifdef CONFIG_SMP |
678 | /* kernel cmdline disable apb timer, so we will use lapic timers */ | 657 | /* kernel cmdline disable apb timer, so we will use lapic timers */ |
679 | if (disable_apbt_percpu) { | 658 | if (mrst_timer_options == MRST_TIMER_LAPIC_APBT) { |
680 | printk(KERN_INFO "apbt: disabled per cpu timer\n"); | 659 | printk(KERN_INFO "apbt: disabled per cpu timer\n"); |
681 | return; | 660 | return; |
682 | } | 661 | } |
diff --git a/arch/x86/kernel/mrst.c b/arch/x86/kernel/mrst.c index ceaebeb5866f..636b53bd4198 100644 --- a/arch/x86/kernel/mrst.c +++ b/arch/x86/kernel/mrst.c | |||
@@ -25,6 +25,29 @@ | |||
25 | #include <asm/i8259.h> | 25 | #include <asm/i8259.h> |
26 | #include <asm/apb_timer.h> | 26 | #include <asm/apb_timer.h> |
27 | 27 | ||
28 | /* | ||
29 | * the clockevent devices on Moorestown/Medfield can be APBT or LAPIC clock, | ||
30 | * cmdline option x86_mrst_timer can be used to override the configuration | ||
31 | * to prefer one or the other. | ||
32 | * at runtime, there are basically three timer configurations: | ||
33 | * 1. per cpu apbt clock only | ||
34 | * 2. per cpu always-on lapic clocks only, this is Penwell/Medfield only | ||
35 | * 3. per cpu lapic clock (C3STOP) and one apbt clock, with broadcast. | ||
36 | * | ||
37 | * by default (without cmdline option), platform code first detects cpu type | ||
38 | * to see if we are on lincroft or penwell, then set up both lapic or apbt | ||
39 | * clocks accordingly. | ||
40 | * i.e. by default, medfield uses configuration #2, moorestown uses #1. | ||
41 | * config #3 is supported but not recommended on medfield. | ||
42 | * | ||
43 | * rating and feature summary: | ||
44 | * lapic (with C3STOP) --------- 100 | ||
45 | * apbt (always-on) ------------ 110 | ||
46 | * lapic (always-on,ARAT) ------ 150 | ||
47 | */ | ||
48 | |||
49 | int mrst_timer_options __cpuinitdata; | ||
50 | |||
28 | static u32 sfi_mtimer_usage[SFI_MTMR_MAX_NUM]; | 51 | static u32 sfi_mtimer_usage[SFI_MTMR_MAX_NUM]; |
29 | static struct sfi_timer_table_entry sfi_mtimer_array[SFI_MTMR_MAX_NUM]; | 52 | static struct sfi_timer_table_entry sfi_mtimer_array[SFI_MTMR_MAX_NUM]; |
30 | static int mrst_cpu_chip; | 53 | static int mrst_cpu_chip; |
@@ -169,18 +192,6 @@ int __init sfi_parse_mrtc(struct sfi_table_header *table) | |||
169 | return 0; | 192 | return 0; |
170 | } | 193 | } |
171 | 194 | ||
172 | /* | ||
173 | * the secondary clock in Moorestown can be APBT or LAPIC clock, default to | ||
174 | * APBT but cmdline option can also override it. | ||
175 | */ | ||
176 | static void __cpuinit mrst_setup_secondary_clock(void) | ||
177 | { | ||
178 | /* restore default lapic clock if disabled by cmdline */ | ||
179 | if (disable_apbt_percpu) | ||
180 | return setup_secondary_APIC_clock(); | ||
181 | apbt_setup_secondary_clock(); | ||
182 | } | ||
183 | |||
184 | static unsigned long __init mrst_calibrate_tsc(void) | 195 | static unsigned long __init mrst_calibrate_tsc(void) |
185 | { | 196 | { |
186 | unsigned long flags, fast_calibrate; | 197 | unsigned long flags, fast_calibrate; |
@@ -197,6 +208,21 @@ static unsigned long __init mrst_calibrate_tsc(void) | |||
197 | 208 | ||
198 | void __init mrst_time_init(void) | 209 | void __init mrst_time_init(void) |
199 | { | 210 | { |
211 | switch (mrst_timer_options) { | ||
212 | case MRST_TIMER_APBT_ONLY: | ||
213 | break; | ||
214 | case MRST_TIMER_LAPIC_APBT: | ||
215 | x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock; | ||
216 | x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock; | ||
217 | break; | ||
218 | default: | ||
219 | if (!boot_cpu_has(X86_FEATURE_ARAT)) | ||
220 | break; | ||
221 | x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock; | ||
222 | x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock; | ||
223 | return; | ||
224 | } | ||
225 | /* we need at least one APB timer */ | ||
200 | sfi_table_parse(SFI_SIG_MTMR, NULL, NULL, sfi_parse_mtmr); | 226 | sfi_table_parse(SFI_SIG_MTMR, NULL, NULL, sfi_parse_mtmr); |
201 | pre_init_apic_IRQ0(); | 227 | pre_init_apic_IRQ0(); |
202 | apbt_time_init(); | 228 | apbt_time_init(); |
@@ -207,17 +233,6 @@ void __init mrst_rtc_init(void) | |||
207 | sfi_table_parse(SFI_SIG_MRTC, NULL, NULL, sfi_parse_mrtc); | 233 | sfi_table_parse(SFI_SIG_MRTC, NULL, NULL, sfi_parse_mrtc); |
208 | } | 234 | } |
209 | 235 | ||
210 | /* | ||
211 | * if we use per cpu apb timer, the bootclock already setup. if we use lapic | ||
212 | * timer and one apbt timer for broadcast, we need to set up lapic boot clock. | ||
213 | */ | ||
214 | static void __init mrst_setup_boot_clock(void) | ||
215 | { | ||
216 | pr_info("%s: per cpu apbt flag %d \n", __func__, disable_apbt_percpu); | ||
217 | if (disable_apbt_percpu) | ||
218 | setup_boot_APIC_clock(); | ||
219 | }; | ||
220 | |||
221 | int mrst_identify_cpu(void) | 236 | int mrst_identify_cpu(void) |
222 | { | 237 | { |
223 | return mrst_cpu_chip; | 238 | return mrst_cpu_chip; |
@@ -250,13 +265,13 @@ void __init x86_mrst_early_setup(void) | |||
250 | x86_init.resources.reserve_resources = x86_init_noop; | 265 | x86_init.resources.reserve_resources = x86_init_noop; |
251 | 266 | ||
252 | x86_init.timers.timer_init = mrst_time_init; | 267 | x86_init.timers.timer_init = mrst_time_init; |
253 | x86_init.timers.setup_percpu_clockev = mrst_setup_boot_clock; | 268 | x86_init.timers.setup_percpu_clockev = x86_init_noop; |
254 | 269 | ||
255 | x86_init.irqs.pre_vector_init = x86_init_noop; | 270 | x86_init.irqs.pre_vector_init = x86_init_noop; |
256 | 271 | ||
257 | x86_init.oem.arch_setup = mrst_arch_setup; | 272 | x86_init.oem.arch_setup = mrst_arch_setup; |
258 | 273 | ||
259 | x86_cpuinit.setup_percpu_clockev = mrst_setup_secondary_clock; | 274 | x86_cpuinit.setup_percpu_clockev = apbt_setup_secondary_clock; |
260 | 275 | ||
261 | x86_platform.calibrate_tsc = mrst_calibrate_tsc; | 276 | x86_platform.calibrate_tsc = mrst_calibrate_tsc; |
262 | x86_init.pci.init = pci_mrst_init; | 277 | x86_init.pci.init = pci_mrst_init; |
@@ -269,3 +284,26 @@ void __init x86_mrst_early_setup(void) | |||
269 | x86_init.mpparse.get_smp_config = x86_init_uint_noop; | 284 | x86_init.mpparse.get_smp_config = x86_init_uint_noop; |
270 | 285 | ||
271 | } | 286 | } |
287 | |||
288 | /* | ||
289 | * if user does not want to use per CPU apb timer, just give it a lower rating | ||
290 | * than local apic timer and skip the late per cpu timer init. | ||
291 | */ | ||
292 | static inline int __init setup_x86_mrst_timer(char *arg) | ||
293 | { | ||
294 | if (!arg) | ||
295 | return -EINVAL; | ||
296 | |||
297 | if (strcmp("apbt_only", arg) == 0) | ||
298 | mrst_timer_options = MRST_TIMER_APBT_ONLY; | ||
299 | else if (strcmp("lapic_and_apbt", arg) == 0) | ||
300 | mrst_timer_options = MRST_TIMER_LAPIC_APBT; | ||
301 | else { | ||
302 | pr_warning("X86 MRST timer option %s not recognised" | ||
303 | " use x86_mrst_timer=apbt_only or lapic_and_apbt\n", | ||
304 | arg); | ||
305 | return -EINVAL; | ||
306 | } | ||
307 | return 0; | ||
308 | } | ||
309 | __setup("x86_mrst_timer=", setup_x86_mrst_timer); | ||