diff options
author | Marco Peereboom <marco@cvs.openbsd.org> | 2009-02-19 21:02:06 +0000 |
---|---|---|
committer | Marco Peereboom <marco@cvs.openbsd.org> | 2009-02-19 21:02:06 +0000 |
commit | 748c385494ee5d641b7e648df28568538e41467c (patch) | |
tree | 9b3648d5d080c29915d0f71b7a8d9c490046223d /sys/dev/acpi/acpi.c | |
parent | 6fb68817a3957ab44390ea949e41e4b949d653a7 (diff) |
suspend/resume bits so that we can develop this in tree. This is disabled.
code from mlarkin and me
help from art,toby,jordan and several others
ok jordan, go for it deraadt
Diffstat (limited to 'sys/dev/acpi/acpi.c')
-rw-r--r-- | sys/dev/acpi/acpi.c | 214 |
1 files changed, 158 insertions, 56 deletions
diff --git a/sys/dev/acpi/acpi.c b/sys/dev/acpi/acpi.c index 42aab210cab..b34d35e1a18 100644 --- a/sys/dev/acpi/acpi.c +++ b/sys/dev/acpi/acpi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpi.c,v 1.129 2009/02/10 02:13:19 jordan Exp $ */ +/* $OpenBSD: acpi.c,v 1.130 2009/02/19 21:02:05 marco Exp $ */ /* * Copyright (c) 2005 Thorsten Lockert <tholo@sigmasoft.com> * Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org> @@ -26,6 +26,7 @@ #include <sys/signalvar.h> #include <sys/proc.h> #include <sys/kthread.h> +#include <sys/workq.h> #include <machine/conf.h> #include <machine/cpufunc.h> @@ -81,6 +82,10 @@ void acpi_init_states(struct acpi_softc *); void acpi_init_gpes(struct acpi_softc *); void acpi_init_pm(struct acpi_softc *); +#ifdef ACPI_SLEEP_ENABLED +void acpi_sleep_walk(struct acpi_softc *, int); +#endif /* ACPI_SLEEP_ENABLED */ + #ifndef SMALL_KERNEL int acpi_add_device(struct aml_node *node, void *arg); #endif /* SMALL_KERNEL */ @@ -841,6 +846,13 @@ acpiioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) ACPI_LOCK(sc); /* fake APM */ switch (cmd) { +#ifdef ACPI_SLEEP_ENABLED + case APM_IOC_SUSPEND: + case APM_IOC_STANDBY: + workq_add_task(NULL, 0, (workq_fn)acpi_sleep_state, + acpi_softc, (void *)ACPI_STATE_S3); + break; +#endif /* ACPI_SLEEP_ENABLED */ case APM_IOC_GETPOWER: /* A/C */ pi->ac_state = APM_AC_UNKNOWN; @@ -1651,65 +1663,78 @@ acpi_init_pm(struct acpi_softc *sc) sc->sc_gts = aml_searchname(&aml_root, "_GTS"); } +#ifndef SMALL_KERNEL void -acpi_enter_sleep_state(struct acpi_softc *sc, int state) +acpi_sleep_walk(struct acpi_softc *sc, int state) { - struct aml_value env; - u_int16_t rega, regb; - int retries; + struct acpi_wakeq *wentry; + int idx; - if (sc == NULL || state == ACPI_STATE_S0) - return; - if (sc->sc_sleeptype[state].slp_typa == -1 || - sc->sc_sleeptype[state].slp_typb == -1) { - printf("%s: state S%d unavailable\n", - sc->sc_dev.dv_xname, state); - return; + /* Clear GPE status */ + for (idx = 0; idx < sc->sc_lastgpe; idx += 8) { + acpi_write_pmreg(sc, ACPIREG_GPE_EN, idx>>3, 0); + acpi_write_pmreg(sc, ACPIREG_GPE_STS, idx>>3, -1); } - memset(&env, 0, sizeof(env)); - env.type = AML_OBJTYPE_INTEGER; - env.v_integer = state; - /* _TTS(state) */ - if (sc->sc_tts) { - if (aml_evalnode(sc, sc->sc_tts, 1, &env, NULL) != 0) { - dnprintf(10, "%s evaluating method _TTS failed.\n", - DEVNAME(sc)); - return; - } + SIMPLEQ_FOREACH(wentry, &sc->sc_wakedevs, q_next) { + dnprintf(10, "%.4s(S%d) gpe %.2x\n", wentry->q_node->name, + wentry->q_state, + wentry->q_gpe); + + if (state <= wentry->q_state) + acpi_enable_onegpe(sc, wentry->q_gpe, 1); } +} +#endif /* ! SMALL_KERNEL */ + +int +acpi_sleep_state(struct acpi_softc *sc, int state) +{ + int ret; + switch (state) { + case ACPI_STATE_S0: + return (0); + case ACPI_STATE_S4: + return (EOPNOTSUPP); + case ACPI_STATE_S5: + break; case ACPI_STATE_S1: case ACPI_STATE_S2: - resettodr(); - dopowerhooks(PWR_SUSPEND); - break; case ACPI_STATE_S3: - resettodr(); - dopowerhooks(PWR_STANDBY); - break; - } - /* _PTS(state) */ - if (sc->sc_pts) { - if (aml_evalnode(sc, sc->sc_pts, 1, &env, NULL) != 0) { - dnprintf(10, "%s evaluating method _PTS failed.\n", - DEVNAME(sc)); - return; - } - } - sc->sc_state = state; - /* _GTS(state) */ - if (sc->sc_gts) { - if (aml_evalnode(sc, sc->sc_gts, 1, &env, NULL) != 0) { - dnprintf(10, "%s evaluating method _GTS failed.\n", - DEVNAME(sc)); - return; - } + if (sc->sc_sleeptype[state].slp_typa == -1 || + sc->sc_sleeptype[state].slp_typb == -1) + return (EOPNOTSUPP); } - disable_intr(); + + acpi_sleep_walk(sc, state); + + if ((ret = acpi_prepare_sleep_state(sc, state)) != 0) + return (ret); + + if (state != ACPI_STATE_S1) + ret = acpi_sleep_machdep(sc, state); + else + ret = acpi_enter_sleep_state(sc, state); + +#ifndef SMALL_KERNEL + acpi_resume(sc); + Debugger(); +#endif /* ! SMALL_KERNEL */ + return (ret); +} + +int +acpi_enter_sleep_state(struct acpi_softc *sc, int state) +{ + uint16_t rega, regb; + int retries; /* Clear WAK_STS bit */ - acpi_write_pmreg(sc, ACPIREG_PM1_STS, 0, ACPI_PM1_WAK_STS); + acpi_write_pmreg(sc, ACPIREG_PM1_STS, 1, ACPI_PM1_WAK_STS); + + /* Disable BM arbitration */ + acpi_write_pmreg(sc, ACPIREG_PM2_CNT, 1, ACPI_PM2_ARB_DIS); /* Write SLP_TYPx values */ rega = acpi_read_pmreg(sc, ACPIREG_PM1A_CNT, 0); @@ -1724,9 +1749,15 @@ acpi_enter_sleep_state(struct acpi_softc *sc, int state) /* Set SLP_EN bit */ rega |= ACPI_PM1_SLP_EN; regb |= ACPI_PM1_SLP_EN; + + /* + * Let the machdep code flush caches and do any other necessary + * tasks before going away. + */ + acpi_cpu_flush(sc, state); + acpi_write_pmreg(sc, ACPIREG_PM1A_CNT, 0, rega); acpi_write_pmreg(sc, ACPIREG_PM1B_CNT, 0, regb); - /* Loop on WAK_STS */ for (retries = 1000; retries > 0; retries--) { rega = acpi_read_pmreg(sc, ACPIREG_PM1A_STS, 0); @@ -1737,10 +1768,10 @@ acpi_enter_sleep_state(struct acpi_softc *sc, int state) DELAY(10); } - enable_intr(); + return (-1); } -#if 0 +#ifndef SMALL_KERNEL void acpi_resume(struct acpi_softc *sc) { @@ -1750,20 +1781,21 @@ acpi_resume(struct acpi_softc *sc) env.type = AML_OBJTYPE_INTEGER; env.v_integer = sc->sc_state; - if (sc->sc_bfs) { + if (sc->sc_bfs) if (aml_evalnode(sc, sc->sc_pts, 1, &env, NULL) != 0) { dnprintf(10, "%s evaluating method _BFS failed.\n", DEVNAME(sc)); } - } + dopowerhooks(PWR_RESUME); inittodr(0); - if (sc->sc_wak) { + + if (sc->sc_wak) if (aml_evalnode(sc, sc->sc_wak, 1, &env, NULL) != 0) { dnprintf(10, "%s evaluating method _WAK failed.\n", DEVNAME(sc)); } - } + sc->sc_state = ACPI_STATE_S0; if (sc->sc_tts) { env.v_integer = sc->sc_state; @@ -1773,14 +1805,84 @@ acpi_resume(struct acpi_softc *sc) } } } -#endif +#endif /* ! SMALL_KERNEL */ + +int +acpi_prepare_sleep_state(struct acpi_softc *sc, int state) +{ + struct aml_value env; + + if (sc == NULL || state == ACPI_STATE_S0) + return(0); + + if (sc->sc_sleeptype[state].slp_typa == -1 || + sc->sc_sleeptype[state].slp_typb == -1) { + printf("%s: state S%d unavailable\n", + sc->sc_dev.dv_xname, state); + return (ENXIO); + } + + memset(&env, 0, sizeof(env)); + env.type = AML_OBJTYPE_INTEGER; + env.v_integer = state; + /* _TTS(state) */ + if (sc->sc_tts) + if (aml_evalnode(sc, sc->sc_tts, 1, &env, NULL) != 0) { + dnprintf(10, "%s evaluating method _TTS failed.\n", + DEVNAME(sc)); + return (ENXIO); + } + + switch (state) { + case ACPI_STATE_S1: + case ACPI_STATE_S2: + resettodr(); + dopowerhooks(PWR_SUSPEND); + break; + case ACPI_STATE_S3: + resettodr(); + dopowerhooks(PWR_STANDBY); + break; + } + + /* _PTS(state) */ + if (sc->sc_pts) + if (aml_evalnode(sc, sc->sc_pts, 1, &env, NULL) != 0) { + dnprintf(10, "%s evaluating method _PTS failed.\n", + DEVNAME(sc)); + return (ENXIO); + } + + sc->sc_state = state; + /* _GTS(state) */ + if (sc->sc_gts) + if (aml_evalnode(sc, sc->sc_gts, 1, &env, NULL) != 0) { + dnprintf(10, "%s evaluating method _GTS failed.\n", + DEVNAME(sc)); + return (ENXIO); + } + + disable_intr(); + aml_evalname(sc, &aml_root, "\\_SST", 1, &env, NULL); + sc->sc_state = state; + + return (0); +} + + void acpi_powerdown(void) { - acpi_enter_sleep_state(acpi_softc, ACPI_STATE_S5); + /* + * In case acpi_prepare_sleep fails, we shouldn't try to enter + * the sleep state. It might cost us the battery. + */ + if (acpi_prepare_sleep_state(acpi_softc, ACPI_STATE_S5) == 0) + acpi_enter_sleep_state(acpi_softc, ACPI_STATE_S5); } + extern int aml_busy; void |