aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJacob Pan <jacob.jun.pan@linux.intel.com>2010-05-19 15:01:25 -0400
committerH. Peter Anvin <hpa@linux.intel.com>2010-05-19 16:45:39 -0400
commita875c01944f0d750eeb1ef3133feceb13f13c4b3 (patch)
treecdd39431457cdeed92d67420a89296cbfb23f620
parenta0c173bd8a3fd0541be8e4ef962170e48d8811c7 (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.h1
-rw-r--r--arch/x86/include/asm/mrst.h1
-rw-r--r--arch/x86/kernel/apb_timer.c37
-rw-r--r--arch/x86/kernel/mrst.c88
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);
55extern int arch_setup_apbt_irqs(int irq, int trigger, int mask, int cpu); 55extern int arch_setup_apbt_irqs(int irq, int trigger, int mask, int cpu);
56extern void apbt_setup_secondary_clock(void); 56extern void apbt_setup_secondary_clock(void);
57extern unsigned int boot_cpu_id; 57extern unsigned int boot_cpu_id;
58extern int disable_apbt_percpu;
59 58
60extern struct sfi_timer_table_entry *sfi_get_mtmr(int hint); 59extern struct sfi_timer_table_entry *sfi_get_mtmr(int hint);
61extern void sfi_free_mtmr(struct sfi_timer_table_entry *mtmr); 60extern 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
13extern int pci_mrst_init(void); 13extern int pci_mrst_init(void);
14extern int mrst_identify_cpu(void); 14extern int mrst_identify_cpu(void);
15extern int mrst_timer_options __cpuinitdata;
15int __init sfi_parse_mrtc(struct sfi_table_header *table); 16int __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
86int disable_apbt_percpu __cpuinitdata;
87
88static DEFINE_PER_CPU(struct apbt_dev, cpu_apbt_dev); 87static 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 */
201static 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
430static __init int apbt_late_init(void) 406static __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
49int mrst_timer_options __cpuinitdata;
50
28static u32 sfi_mtimer_usage[SFI_MTMR_MAX_NUM]; 51static u32 sfi_mtimer_usage[SFI_MTMR_MAX_NUM];
29static struct sfi_timer_table_entry sfi_mtimer_array[SFI_MTMR_MAX_NUM]; 52static struct sfi_timer_table_entry sfi_mtimer_array[SFI_MTMR_MAX_NUM];
30static int mrst_cpu_chip; 53static 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 */
176static 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
184static unsigned long __init mrst_calibrate_tsc(void) 195static 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
198void __init mrst_time_init(void) 209void __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 */
214static 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
221int mrst_identify_cpu(void) 236int 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 */
292static 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);