+ /* Don't trace irqs off for idle */
+ stop_critical_timings();
+ if (cx->entry_method == ACPI_CSTATE_FFH) {
+ /* Call into architectural FFH based C-state */
+ acpi_processor_ffh_cstate_enter(cx);
+ } else if (cx->entry_method == ACPI_CSTATE_HALT) {
+ acpi_safe_halt();
+ } else {
+ int unused;
+ /* IO port based C-state */
+ inb(cx->address);
+ /* Dummy wait op - must do something useless after P_LVL2 read
+ because chipsets cannot guarantee that STPCLK# signal
+ gets asserted in time to freeze execution properly. */
+ unused = inl(acpi_gbl_FADT.xpm_timer_block.address);
+ }
+ start_critical_timings();
+}
+
+/**
+ * acpi_idle_enter_c1 - enters an ACPI C1 state-type
+ * @dev: the target CPU
+ * @state: the state data
+ *
+ * This is equivalent to the HALT instruction.
+ */
+static int acpi_idle_enter_c1(struct cpuidle_device *dev,
+ struct cpuidle_state *state)
+{
+ u32 t1, t2;
+ struct acpi_processor *pr;
+ struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
+
+ pr = __get_cpu_var(processors);
+
+ if (unlikely(!pr))
+ return 0;
+
+ local_irq_disable();
+
+ /* Do not access any ACPI IO ports in suspend path */
+ if (acpi_idle_suspend) {
+ acpi_safe_halt();
+ local_irq_enable();
+ return 0;
+ }
+
+ t1 = inl(acpi_gbl_FADT.xpm_timer_block.address);
+ acpi_idle_do_entry(cx);
+ t2 = inl(acpi_gbl_FADT.xpm_timer_block.address);
+
+ local_irq_enable();
+ cx->usage++;
+
+ return ticks_elapsed_in_us(t1, t2);
+}
+
+/**
+ * acpi_idle_enter_simple - enters an ACPI state without BM handling
+ * @dev: the target CPU
+ * @state: the state data
+ */
+static int acpi_idle_enter_simple(struct cpuidle_device *dev,
+ struct cpuidle_state *state)
+{
+ struct acpi_processor *pr;
+ struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
+ u32 t1, t2;
+ int sleep_ticks = 0;
+
+ pr = __get_cpu_var(processors);
+
+ if (unlikely(!pr))
+ return 0;
+
+ if (acpi_idle_suspend)
+ return(acpi_idle_enter_c1(dev, state));
+
+ local_irq_disable();
+ current_thread_info()->status &= ~TS_POLLING;
+ /*
+ * TS_POLLING-cleared state must be visible before we test
+ * NEED_RESCHED:
+ */
+ smp_mb();
+
+ if (unlikely(need_resched())) {
+ current_thread_info()->status |= TS_POLLING;
+ local_irq_enable();
+ return 0;
+ }
+
+ /*
+ * Must be done before busmaster disable as we might need to
+ * access HPET !
+ */
+ acpi_state_timer_broadcast(pr, cx, 1);
+
+ 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_idle_do_entry(cx);
+ t2 = inl(acpi_gbl_FADT.xpm_timer_block.address);
+
+#if defined (CONFIG_GENERIC_TIME) && defined (CONFIG_X86)
+ /* TSC could halt in idle, so notify users */
+ if (tsc_halts_in_c(cx->type))
+ mark_tsc_unstable("TSC halts in idle");;
+#endif
+ sleep_ticks = ticks_elapsed(t1, t2);
+
+ /* Tell the scheduler how much we idled: */
+ sched_clock_idle_wakeup_event(sleep_ticks*PM_TIMER_TICK_NS);
+
+ local_irq_enable();
+ current_thread_info()->status |= TS_POLLING;
+
+ cx->usage++;
+
+ acpi_state_timer_broadcast(pr, cx, 0);
+ cx->time += sleep_ticks;
+ return ticks_elapsed_in_us(t1, t2);
+}
+
+static int c3_cpu_count;
+static DEFINE_SPINLOCK(c3_lock);
+
+/**
+ * acpi_idle_enter_bm - enters C3 with proper BM handling
+ * @dev: the target CPU
+ * @state: the state data
+ *
+ * If BM is detected, the deepest non-C3 idle state is entered instead.
+ */
+static int acpi_idle_enter_bm(struct cpuidle_device *dev,
+ struct cpuidle_state *state)
+{
+ struct acpi_processor *pr;
+ struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
+ u32 t1, t2;
+ int sleep_ticks = 0;
+
+ pr = __get_cpu_var(processors);
+
+ if (unlikely(!pr))
+ return 0;
+
+ if (acpi_idle_suspend)
+ return(acpi_idle_enter_c1(dev, state));
+
+ if (acpi_idle_bm_check()) {
+ if (dev->safe_state) {
+ dev->last_state = dev->safe_state;
+ return dev->safe_state->enter(dev, dev->safe_state);
+ } else {
+ local_irq_disable();
+ acpi_safe_halt();
+ local_irq_enable();
+ return 0;
+ }
+ }
+
+ local_irq_disable();
+ current_thread_info()->status &= ~TS_POLLING;
+ /*
+ * TS_POLLING-cleared state must be visible before we test
+ * NEED_RESCHED:
+ */
+ smp_mb();
+
+ if (unlikely(need_resched())) {
+ current_thread_info()->status |= TS_POLLING;
+ local_irq_enable();
+ return 0;
+ }
+
+ acpi_unlazy_tlb(smp_processor_id());
+
+ /* Tell the scheduler that we are going deep-idle: */
+ sched_clock_idle_sleep_event();
+ /*
+ * 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
+ * !bm_check implies we need cache flush
+ * bm_control implies whether we can do ARB_DIS
+ *
+ * That leaves a case where bm_check is set and bm_control is
+ * not set. In that case we cannot do much, we enter C3
+ * without doing anything.
+ */
+ if (pr->flags.bm_check && pr->flags.bm_control) {
+ spin_lock(&c3_lock);
+ c3_cpu_count++;
+ /* Disable bus master arbitration when all CPUs are in C3 */
+ if (c3_cpu_count == num_online_cpus())
+ acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1);
+ spin_unlock(&c3_lock);
+ } else if (!pr->flags.bm_check) {
+ ACPI_FLUSH_CPU_CACHE();
+ }
+
+ t1 = inl(acpi_gbl_FADT.xpm_timer_block.address);
+ acpi_idle_do_entry(cx);
+ t2 = inl(acpi_gbl_FADT.xpm_timer_block.address);
+
+ /* Re-enable bus master arbitration */
+ if (pr->flags.bm_check && pr->flags.bm_control) {
+ spin_lock(&c3_lock);
+ acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0);
+ c3_cpu_count--;
+ spin_unlock(&c3_lock);
+ }
+
+#if defined (CONFIG_GENERIC_TIME) && defined (CONFIG_X86)
+ /* TSC could halt in idle, so notify users */
+ 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: */
+ sched_clock_idle_wakeup_event(sleep_ticks*PM_TIMER_TICK_NS);
+
+ local_irq_enable();
+ current_thread_info()->status |= TS_POLLING;
+
+ cx->usage++;
+
+ acpi_state_timer_broadcast(pr, cx, 0);
+ cx->time += sleep_ticks;
+ return ticks_elapsed_in_us(t1, t2);