#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <dev/acpi/acpireg.h>
#include <dev/acpi/acpivar.h>
#include <dev/acpi/acpidev.h>
#include <dev/acpi/dsdt.h>
#include <machine/apmvar.h>
#include <ddb/db_var.h>
int
sleep_showstate(void *v, int sleepmode)
{
struct acpi_softc *sc = v;
int fallback_state = -1;
extern int lid_action;
switch (sleepmode) {
case SLEEP_SUSPEND:
sc->sc_state = ACPI_STATE_S3;
#ifdef __amd64__
if (lid_action == -1)
sc->sc_state = ACPI_STATE_S0;
fallback_state = ACPI_STATE_S0;
#endif
#ifdef DDB
if (db_suspend)
sc->sc_state = ACPI_STATE_S0;
#endif
break;
case SLEEP_HIBERNATE:
sc->sc_state = ACPI_STATE_S4;
fallback_state = ACPI_STATE_S5;
break;
default:
return (EOPNOTSUPP);
}
if (sc->sc_sleeptype[sc->sc_state].slp_typa == -1 ||
sc->sc_sleeptype[sc->sc_state].slp_typb == -1) {
if (fallback_state != -1) {
printf("%s: S%d unavailable, using S%d\n",
sc->sc_dev.dv_xname, sc->sc_state, fallback_state);
sc->sc_state = fallback_state;
} else {
printf("%s: state S%d unavailable\n",
sc->sc_dev.dv_xname, sc->sc_state);
return (EOPNOTSUPP);
}
}
if (sc->sc_state != ACPI_STATE_S0) {
if (aml_node_setval(sc, sc->sc_tts, sc->sc_state) != 0)
return (EINVAL);
}
acpi_indicator(sc, ACPI_SST_WAKING);
return 0;
}
int
sleep_setstate(void *v)
{
struct acpi_softc *sc = v;
if (sc->sc_state != ACPI_STATE_S0) {
if (aml_node_setval(sc, sc->sc_pts, sc->sc_state) != 0)
return (EINVAL);
}
return 0;
}
int
gosleep(void *v)
{
extern int cpu_wakeups;
struct acpi_softc *sc = v;
int ret;
acpibtn_enable_psw();
acpi_indicator(sc, ACPI_SST_SLEEPING);
if (sc->sc_state != ACPI_STATE_S0)
aml_node_setval(sc, sc->sc_gts, sc->sc_state);
acpi_write_pmreg(sc, ACPIREG_PM1_STS, 0, ACPI_PM1_ALL_STS);
acpi_disable_allgpes(sc);
acpi_enable_wakegpes(sc, sc->sc_state);
if (sc->sc_pmc_suspend)
sc->sc_pmc_suspend(sc->sc_pmc_cookie);
cpu_wakeups = 0;
sc->sc_wakeup = 0;
sc->sc_wakeups = 0;
while (!sc->sc_wakeup) {
sc->sc_wakegpe = WAKEGPE_NONE;
ret = acpi_sleep_cpu(sc, sc->sc_state);
acpi_resume_cpu(sc, sc->sc_state);
sc->sc_wakeups++;
if (sc->sc_ec && sc->sc_wakegpe == sc->sc_ec->sc_gpe) {
sc->sc_wakeup = 0;
acpiec_gpehandler(sc, sc->sc_wakegpe, sc->sc_ec);
} else
sc->sc_wakeup = 1;
}
if (sc->sc_pmc_resume)
sc->sc_pmc_resume(sc->sc_pmc_cookie);
acpi_indicator(sc, ACPI_SST_WAKING);
if (sc->sc_state != ACPI_STATE_S0)
aml_node_setval(sc, sc->sc_wak, sc->sc_state);
return ret;
}
int
sleep_resume(void *v)
{
struct acpi_softc *sc = v;
acpibtn_disable_psw();
if (sc->sc_state != ACPI_STATE_S0) {
if (aml_node_setval(sc, sc->sc_tts, ACPI_STATE_S0) != 0)
return (EINVAL);
}
return 0;
}
static int
checklids(struct acpi_softc *sc)
{
extern int lid_action;
int lids;
lids = acpibtn_numopenlids();
if (lids == 0 && lid_action != 0)
return 1;
return 0;
}
int
suspend_finish(void *v)
{
extern int cpu_wakeups;
struct acpi_softc *sc = v;
int sleepmode = SLEEP_RESUME;
printf("wakeups: %d %d\n", cpu_wakeups, sc->sc_wakeups);
printf("wakeup event: ");
switch (sc->sc_wakegpe) {
case 0:
printf("unknown\n");
break;
case WAKEGPE_PWRBTN:
printf("PWRBTN\n");
break;
case WAKEGPE_SLPBTN:
printf("SLPBTN\n");
break;
case WAKEGPE_RTC:
printf("RTC\n");
sleepmode = SLEEP_HIBERNATE;
break;
case WAKEGPE_GPIO:
printf("GPIO 0x%x\n", sc->sc_wakegpio);
break;
default:
printf("GPE 0x%x\n", sc->sc_wakegpe);
break;
}
acpi_record_event(sc, APM_NORMAL_RESUME);
acpi_indicator(sc, ACPI_SST_WORKING);
sc->sc_state = ACPI_STATE_S0;
if (sleepmode == SLEEP_RESUME && checklids(sc))
sleepmode = SLEEP_SUSPEND;
return sleepmode;
}