{
struct acpi_processor_power *power;
struct acpi_processor_cx *cx;
+ void *mwait_ptr;
if ( (power = processor_powers[smp_processor_id()]) == NULL )
goto default_halt;
if ( (cx = &power->states[power->count-1]) == NULL )
goto default_halt;
- /*
- * cache must be flashed as the last ops before cpu going into dead,
- * otherwise, cpu may dead with dirty data breaking cache coherency,
- * leading to strange errors.
- */
- wbinvd();
- for ( ; ; )
+ mwait_ptr = (void *)&mwait_wakeup(smp_processor_id());
+
+ if ( cx->entry_method == ACPI_CSTATE_EM_FFH )
{
- switch ( cx->entry_method )
+ /*
+ * cache must be flashed as the last ops before cpu going into dead,
+ * otherwise, cpu may dead with dirty data breaking cache coherency,
+ * leading to strange errors.
+ */
+ wbinvd();
+
+ while ( 1 )
{
- case ACPI_CSTATE_EM_FFH:
- /* Not treat interrupt as break event */
- __monitor((void *)&mwait_wakeup(smp_processor_id()), 0, 0);
- __mwait(cx->address, 0);
- break;
- default:
- goto default_halt;
+ /*
+ * 1. The CLFLUSH is a workaround for erratum AAI65 for
+ * the Xeon 7400 series.
+ * 2. The WBINVD is insufficient due to the spurious-wakeup
+ * case where we return around the loop.
+ * 3. Unlike wbinvd, clflush is a light weight but not serializing
+ * instruction, hence memory fence is necessary to make sure all
+ * load/store visible before flush cache line.
+ */
+ mb();
+ clflush(mwait_ptr);
+ __monitor(mwait_ptr, 0, 0);
+ mb();
+ __mwait(cx->address, 0);
}
}