#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/globaldata.h>
#include <machine/md_var.h>
#include <machine/cpufunc.h>
#include <machine/cpufreq.h>
#include <machine/cputypes.h>
#include <machine/specialreg.h>
#include "acpi.h"
#include "acpi_cpu_cstate.h"
#define ACPI_GAS_INTEL_VENDOR 1
#define ACPI_GAS_INTEL_CLASS_C1_IO_HALT 1
#define ACPI_GAS_INTEL_CLASS_CX_NATIVE 2
#define ACPI_GAS_INTEL_ARG1_HWCOORD 0x1
#define ACPI_GAS_INTEL_ARG1_BM_STS 0x2
#define ACPI_GAS_INTEL_ARG0_MWAIT_HINTMASK 0xffffffff
static int acpi_cst_cx_mwait_setup(struct acpi_cst_cx *);
static void acpi_cst_cx_mwait_enter(const struct acpi_cst_cx *);
int
acpi_cst_md_cx_setup(struct acpi_cst_cx *cx)
{
int error;
if (cpu_vendor_id != CPU_VENDOR_INTEL) {
if (cx->type == ACPI_STATE_C1 &&
cx->gas.SpaceId == ACPI_ADR_SPACE_FIXED_HARDWARE)
return 0;
if (cx->gas.SpaceId != ACPI_ADR_SPACE_SYSTEM_IO &&
cx->gas.SpaceId != ACPI_ADR_SPACE_SYSTEM_MEMORY) {
kprintf("C%d: invalid SpaceId %d\n", cx->type,
cx->gas.SpaceId);
return EINVAL;
}
return 0;
}
switch (cx->gas.SpaceId) {
case ACPI_ADR_SPACE_SYSTEM_IO:
case ACPI_ADR_SPACE_SYSTEM_MEMORY:
break;
case ACPI_ADR_SPACE_FIXED_HARDWARE:
error = acpi_cst_cx_mwait_setup(cx);
if (error)
return error;
break;
default:
kprintf("C%d: invalid SpaceId %d\n", cx->type, cx->gas.SpaceId);
return EINVAL;
}
if (cx->type >= ACPI_STATE_C3) {
if ((CPUID_TO_FAMILY(cpu_id) > 0xf ||
(CPUID_TO_FAMILY(cpu_id) == 0x6 &&
CPUID_TO_MODEL(cpu_id) >= 0xf)) &&
!acpi_cst_force_bmarb) {
cx->preamble = ACPI_CST_CX_PREAMBLE_NONE;
} else if ((acpi_cst_quirks & ACPI_CST_QUIRK_NO_BM) == 0) {
cx->preamble = ACPI_CST_CX_PREAMBLE_BM_ARB;
}
}
return 0;
}
static int
acpi_cst_cx_mwait_setup(struct acpi_cst_cx *cx)
{
uint32_t eax_hint;
int error;
if (bootverbose) {
kprintf("C%d: BitWidth(vendor) %d, BitOffset(class) %d, "
"Address(arg0) 0x%jx, AccessWidth(arg1) 0x%x\n", cx->type,
cx->gas.BitWidth, cx->gas.BitOffset,
(uintmax_t)cx->gas.Address, cx->gas.AccessWidth);
}
if (cx->type == ACPI_STATE_C1) {
return 0;
}
if (cx->gas.BitOffset != ACPI_GAS_INTEL_CLASS_CX_NATIVE)
return EINVAL;
if ((cpu_feature2 & CPUID2_MON) == 0)
return EOPNOTSUPP;
if ((cpu_mwait_feature & (CPUID_MWAIT_EXT | CPUID_MWAIT_INTBRK)) !=
(CPUID_MWAIT_EXT | CPUID_MWAIT_INTBRK))
return EOPNOTSUPP;
eax_hint = cx->gas.Address & ACPI_GAS_INTEL_ARG0_MWAIT_HINTMASK;
if (bootverbose) {
kprintf("C%d -> cpu specific C%d sub state %d\n", cx->type,
MWAIT_EAX_TO_CX(eax_hint), MWAIT_EAX_TO_CX_SUB(eax_hint));
}
if (!cpu_mwait_hint_valid(eax_hint)) {
kprintf("C%d: invalid mwait hint 0x%08x\n", cx->type, eax_hint);
error = EINVAL;
goto done;
}
cx->md_arg0 = eax_hint;
cx->enter = acpi_cst_cx_mwait_enter;
error = 0;
done:
if ((cx->gas.AccessWidth & ACPI_GAS_INTEL_ARG1_BM_STS) == 0 &&
!acpi_cst_force_bmsts) {
cpu_mwait_cx_no_bmsts();
if (cx->type >= ACPI_STATE_C3)
cx->flags &= ~ACPI_CST_CX_FLAG_BM_STS;
}
if (cx->type < ACPI_STATE_C3 && MWAIT_EAX_TO_CX(eax_hint) >= 3) {
if (!acpi_cst_force_bmsts)
cpu_mwait_cx_no_bmsts();
if (!acpi_cst_force_bmarb)
cpu_mwait_cx_no_bmarb();
}
return error;
}
static void
acpi_cst_cx_mwait_enter(const struct acpi_cst_cx *cx)
{
struct globaldata *gd = mycpu;
int reqflags;
reqflags = gd->gd_reqflags;
if ((reqflags & RQF_IDLECHECK_WK_MASK) == 0) {
cpu_mmw_pause_int(&gd->gd_reqflags, reqflags, cx->md_arg0,
MWAIT_ECX_INTBRK);
}
}