]> err.no Git - linux-2.6/blobdiff - drivers/acpi/processor_idle.c
x86: don't disable TSC in any C states on AMD Fam10h
[linux-2.6] / drivers / acpi / processor_idle.c
index 7b6c20eeeaffd738a673ac294c15b4d3f4a629ec..eb1f82f79153c0d3c6f9136b8a88faf829c886d1 100644 (file)
@@ -76,7 +76,11 @@ static void (*pm_idle_save) (void) __read_mostly;
 #define PM_TIMER_TICKS_TO_US(p)                (((p) * 1000)/(PM_TIMER_FREQUENCY/1000))
 
 static unsigned int max_cstate __read_mostly = ACPI_PROCESSOR_MAX_POWER;
+#ifdef CONFIG_CPU_IDLE
 module_param(max_cstate, uint, 0000);
+#else
+module_param(max_cstate, uint, 0644);
+#endif
 static unsigned int nocst __read_mostly;
 module_param(nocst, uint, 0000);
 
@@ -353,6 +357,26 @@ int acpi_processor_resume(struct acpi_device * device)
        return 0;
 }
 
+#if defined (CONFIG_GENERIC_TIME) && defined (CONFIG_X86_TSC)
+static int tsc_halts_in_c(int state)
+{
+       switch (boot_cpu_data.x86_vendor) {
+       case X86_VENDOR_AMD:
+               /*
+                * AMD Fam10h TSC will tick in all
+                * C/P/S0/S1 states when this bit is set.
+                */
+               if (boot_cpu_has(X86_FEATURE_CONSTANT_TSC))
+                       return 0;
+               /*FALL THROUGH*/
+       case X86_VENDOR_INTEL:
+               /* Several cases known where TSC halts in C2 too */
+       default:
+               return state > ACPI_STATE_C1;
+       }
+}
+#endif
+
 #ifndef CONFIG_CPU_IDLE
 static void acpi_processor_idle(void)
 {
@@ -512,7 +536,8 @@ static void acpi_processor_idle(void)
 
 #if defined (CONFIG_GENERIC_TIME) && defined (CONFIG_X86_TSC)
                /* TSC halts in C2, so notify users */
-               mark_tsc_unstable("possible TSC halt in C2");
+               if (tsc_halts_in_c(ACPI_STATE_C2))
+                       mark_tsc_unstable("possible TSC halt in C2");
 #endif
                /* Compute time (ticks) that we were actually asleep */
                sleep_ticks = ticks_elapsed(t1, t2);
@@ -530,6 +555,12 @@ static void acpi_processor_idle(void)
                break;
 
        case ACPI_STATE_C3:
+               acpi_unlazy_tlb(smp_processor_id());
+               /*
+                * Must be done before busmaster disable as we might
+                * need to access HPET !
+                */
+               acpi_state_timer_broadcast(pr, cx, 1);
                /*
                 * disable bus master
                 * bm_check implies we need ARB_DIS
@@ -557,7 +588,6 @@ static void acpi_processor_idle(void)
                /* Get start time (ticks) */
                t1 = inl(acpi_gbl_FADT.xpm_timer_block.address);
                /* Invoke C3 */
-               acpi_state_timer_broadcast(pr, cx, 1);
                /* Tell the scheduler that we are going deep-idle: */
                sched_clock_idle_sleep_event();
                acpi_cstate_enter(cx);
@@ -571,7 +601,8 @@ static void acpi_processor_idle(void)
 
 #if defined (CONFIG_GENERIC_TIME) && defined (CONFIG_X86_TSC)
                /* TSC halts in C3, so notify users */
-               mark_tsc_unstable("TSC halts in C3");
+               if (tsc_halts_in_c(ACPI_STATE_C3))
+                       mark_tsc_unstable("TSC halts in C3");
 #endif
                /* Compute time (ticks) that we were actually asleep */
                sleep_ticks = ticks_elapsed(t1, t2);
@@ -1401,9 +1432,6 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
        if (acpi_idle_suspend)
                return(acpi_idle_enter_c1(dev, state));
 
-       if (pr->flags.bm_check)
-               acpi_idle_update_bm_rld(pr, cx);
-
        local_irq_disable();
        current_thread_info()->status &= ~TS_POLLING;
        /*
@@ -1418,19 +1446,29 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
                return 0;
        }
 
+       acpi_unlazy_tlb(smp_processor_id());
+       /*
+        * Must be done before busmaster disable as we might need to
+        * access HPET !
+        */
+       acpi_state_timer_broadcast(pr, cx, 1);
+
+       if (pr->flags.bm_check)
+               acpi_idle_update_bm_rld(pr, cx);
+
        if (cx->type == ACPI_STATE_C3)
                ACPI_FLUSH_CPU_CACHE();
 
        t1 = inl(acpi_gbl_FADT.xpm_timer_block.address);
        /* Tell the scheduler that we are going deep-idle: */
        sched_clock_idle_sleep_event();
-       acpi_state_timer_broadcast(pr, cx, 1);
        acpi_idle_do_entry(cx);
        t2 = inl(acpi_gbl_FADT.xpm_timer_block.address);
 
 #if defined (CONFIG_GENERIC_TIME) && defined (CONFIG_X86_TSC)
        /* TSC could halt in idle, so notify users */
-       mark_tsc_unstable("TSC halts in idle");;
+       if (tsc_halts_in_c(cx->type))
+               mark_tsc_unstable("TSC halts in idle");;
 #endif
        sleep_ticks = ticks_elapsed(t1, t2);
 
@@ -1541,7 +1579,8 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
 
 #if defined (CONFIG_GENERIC_TIME) && defined (CONFIG_X86_TSC)
        /* TSC could halt in idle, so notify users */
-       mark_tsc_unstable("TSC halts in idle");
+       if (tsc_halts_in_c(ACPI_STATE_C3))
+               mark_tsc_unstable("TSC halts in idle");
 #endif
        sleep_ticks = ticks_elapsed(t1, t2);
        /* Tell the scheduler how much we idled: */
@@ -1673,6 +1712,7 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr,
 
        if (!first_run) {
                dmi_check_system(processor_power_dmi_table);
+               max_cstate = acpi_processor_cstate_check(max_cstate);
                if (max_cstate < ACPI_C_STATES_MAX)
                        printk(KERN_NOTICE
                               "ACPI: processor limited to max C-state %d\n",