Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/acpica/acpi.c
| Show First 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
| #include <sys/sbuf.h> | #include <sys/sbuf.h> | ||||
| #include <sys/sched.h> | #include <sys/sched.h> | ||||
| #include <sys/smp.h> | #include <sys/smp.h> | ||||
| #include <sys/timetc.h> | #include <sys/timetc.h> | ||||
| #include <sys/uuid.h> | #include <sys/uuid.h> | ||||
| #if defined(__i386__) || defined(__amd64__) | #if defined(__i386__) || defined(__amd64__) | ||||
| #include <machine/clock.h> | #include <machine/clock.h> | ||||
| #include <machine/intr_machdep.h> | |||||
| #include <machine/pci_cfgreg.h> | #include <machine/pci_cfgreg.h> | ||||
| #include <x86/cputypes.h> | #include <x86/cputypes.h> | ||||
| #include <x86/x86_var.h> | #include <x86/x86_var.h> | ||||
| #endif | #endif | ||||
| #include <machine/resource.h> | #include <machine/resource.h> | ||||
| #include <machine/bus.h> | #include <machine/bus.h> | ||||
| #include <sys/rman.h> | #include <sys/rman.h> | ||||
| #include <isa/isavar.h> | #include <isa/isavar.h> | ||||
| Show All 28 Lines | static struct cdevsw acpi_cdevsw = { | ||||
| .d_name = "acpi", | .d_name = "acpi", | ||||
| }; | }; | ||||
| struct acpi_interface { | struct acpi_interface { | ||||
| ACPI_STRING *data; | ACPI_STRING *data; | ||||
| int num; | int num; | ||||
| }; | }; | ||||
| enum s2idle_enable { | |||||
| POWER_BUTTON_S2I, | |||||
| SLEEP_BUTTON_S2I, | |||||
| LID_SWITCH_S2I, | |||||
| STANDBY_S2I, | |||||
| SUSPEND_S2I | |||||
| }; | |||||
| static char *sysres_ids[] = { "PNP0C01", "PNP0C02", NULL }; | static char *sysres_ids[] = { "PNP0C01", "PNP0C02", NULL }; | ||||
| /* Global mutex for locking access to the ACPI subsystem. */ | /* Global mutex for locking access to the ACPI subsystem. */ | ||||
| struct mtx acpi_mutex; | struct mtx acpi_mutex; | ||||
| struct callout acpi_sleep_timer; | struct callout acpi_sleep_timer; | ||||
| /* Bitmap of device quirks. */ | /* Bitmap of device quirks. */ | ||||
| int acpi_quirks; | int acpi_quirks; | ||||
| /* Supported sleep states. */ | /* Supported sleep states. */ | ||||
| static BOOLEAN acpi_sleep_states[ACPI_S_STATE_COUNT]; | static BOOLEAN acpi_sleep_states[ACPI_S_STATE_COUNT]; | ||||
| static void acpi_lookup(void *arg, const char *name, device_t *dev); | static void acpi_lookup(void *arg, const char *name, device_t *dev); | ||||
| static int acpi_modevent(struct module *mod, int event, void *junk); | static int acpi_modevent(struct module *mod, int event, void *junk); | ||||
| static int acpi_parse_pxm(device_t dev); | |||||
| static device_probe_t acpi_probe; | static device_probe_t acpi_probe; | ||||
| static device_attach_t acpi_attach; | static device_attach_t acpi_attach; | ||||
| static device_suspend_t acpi_suspend; | static device_suspend_t acpi_suspend; | ||||
| static device_resume_t acpi_resume; | static device_resume_t acpi_resume; | ||||
| static device_shutdown_t acpi_shutdown; | static device_shutdown_t acpi_shutdown; | ||||
| static bus_add_child_t acpi_add_child; | static bus_add_child_t acpi_add_child; | ||||
| Show All 14 Lines | |||||
| static bus_deactivate_resource_t acpi_deactivate_resource; | static bus_deactivate_resource_t acpi_deactivate_resource; | ||||
| static bus_map_resource_t acpi_map_resource; | static bus_map_resource_t acpi_map_resource; | ||||
| static bus_unmap_resource_t acpi_unmap_resource; | static bus_unmap_resource_t acpi_unmap_resource; | ||||
| static bus_child_pnpinfo_t acpi_child_pnpinfo_method; | static bus_child_pnpinfo_t acpi_child_pnpinfo_method; | ||||
| static bus_child_location_t acpi_child_location_method; | static bus_child_location_t acpi_child_location_method; | ||||
| static bus_hint_device_unit_t acpi_hint_device_unit; | static bus_hint_device_unit_t acpi_hint_device_unit; | ||||
| static bus_get_property_t acpi_bus_get_prop; | static bus_get_property_t acpi_bus_get_prop; | ||||
| static bus_get_device_path_t acpi_get_device_path; | static bus_get_device_path_t acpi_get_device_path; | ||||
| static bus_get_domain_t acpibus_get_domain_method; | static bus_get_domain_t acpi_get_domain_method; | ||||
| static acpi_id_probe_t acpi_device_id_probe; | static acpi_id_probe_t acpi_device_id_probe; | ||||
| static acpi_evaluate_object_t acpi_device_eval_obj; | static acpi_evaluate_object_t acpi_device_eval_obj; | ||||
| static acpi_get_property_t acpi_device_get_prop; | static acpi_get_property_t acpi_device_get_prop; | ||||
| static acpi_scan_children_t acpi_device_scan_children; | static acpi_scan_children_t acpi_device_scan_children; | ||||
| static isa_pnp_probe_t acpi_isa_pnp_probe; | static isa_pnp_probe_t acpi_isa_pnp_probe; | ||||
| static void acpi_reserve_resources(device_t dev); | static void acpi_reserve_resources(device_t dev); | ||||
| static int acpi_sysres_alloc(device_t dev); | static int acpi_sysres_alloc(device_t dev); | ||||
| static uint32_t acpi_isa_get_logicalid(device_t dev); | static uint32_t acpi_isa_get_logicalid(device_t dev); | ||||
| static int acpi_isa_get_compatid(device_t dev, uint32_t *cids, int count); | static int acpi_isa_get_compatid(device_t dev, uint32_t *cids, int count); | ||||
| static ACPI_STATUS acpi_device_scan_cb(ACPI_HANDLE h, UINT32 level, | static ACPI_STATUS acpi_device_scan_cb(ACPI_HANDLE h, UINT32 level, | ||||
| void *context, void **retval); | void *context, void **retval); | ||||
| static ACPI_STATUS acpi_find_dsd(struct acpi_device *ad); | static ACPI_STATUS acpi_find_dsd(struct acpi_device *ad); | ||||
| static void acpi_platform_osc(device_t dev); | static void acpi_platform_osc(device_t dev); | ||||
| static void acpi_probe_children(device_t bus); | static void acpi_probe_children(device_t bus); | ||||
| static void acpi_probe_order(ACPI_HANDLE handle, int *order); | static void acpi_probe_order(ACPI_HANDLE handle, int *order); | ||||
| static ACPI_STATUS acpi_probe_child(ACPI_HANDLE handle, UINT32 level, | static ACPI_STATUS acpi_probe_child(ACPI_HANDLE handle, UINT32 level, | ||||
| void *context, void **status); | void *context, void **status); | ||||
| static void acpi_sleep_enable(void *arg); | static void acpi_sleep_enable(void *arg); | ||||
| static ACPI_STATUS acpi_sleep_disable(struct acpi_softc *sc); | static ACPI_STATUS acpi_sleep_disable(struct acpi_softc *sc); | ||||
| static ACPI_STATUS acpi_EnterSleepState(struct acpi_softc *sc, enum sleep_type stype); | static ACPI_STATUS acpi_EnterSleepState(struct acpi_softc *sc, int state); | ||||
| static void acpi_shutdown_final(void *arg, int howto); | static void acpi_shutdown_final(void *arg, int howto); | ||||
| static void acpi_enable_fixed_events(struct acpi_softc *sc); | static void acpi_enable_fixed_events(struct acpi_softc *sc); | ||||
| static void acpi_resync_clock(struct acpi_softc *sc); | static void acpi_resync_clock(struct acpi_softc *sc); | ||||
| static int acpi_wake_sleep_prep(ACPI_HANDLE handle, int sstate); | static int acpi_wake_sleep_prep(ACPI_HANDLE handle, int sstate); | ||||
| static int acpi_wake_run_prep(ACPI_HANDLE handle, int sstate); | static int acpi_wake_run_prep(ACPI_HANDLE handle, int sstate); | ||||
| static int acpi_wake_prep_walk(int sstate); | static int acpi_wake_prep_walk(int sstate); | ||||
| static int acpi_wake_sysctl_walk(device_t dev); | static int acpi_wake_sysctl_walk(device_t dev); | ||||
| static int acpi_wake_set_sysctl(SYSCTL_HANDLER_ARGS); | static int acpi_wake_set_sysctl(SYSCTL_HANDLER_ARGS); | ||||
| static void acpi_system_eventhandler_sleep(void *arg, enum sleep_type type); | static void acpi_system_eventhandler_sleep(void *arg, int state); | ||||
| static void acpi_system_eventhandler_wakeup(void *arg, enum sleep_type type); | static void acpi_system_eventhandler_wakeup(void *arg, int state); | ||||
| static int acpi_stype2sstate(enum sleep_type type); | |||||
| static int acpi_sname2sstate(const char *sname); | static int acpi_sname2sstate(const char *sname); | ||||
| static const char *acpi_sstate2sname(int sstate); | static const char *acpi_sstate2sname(int sstate); | ||||
| static int acpi_supported_sleep_state_sysctl(SYSCTL_HANDLER_ARGS); | static int acpi_supported_sleep_state_sysctl(SYSCTL_HANDLER_ARGS); | ||||
| static int acpi_sleep_state_sysctl(SYSCTL_HANDLER_ARGS); | static int acpi_sleep_state_sysctl(SYSCTL_HANDLER_ARGS); | ||||
| static int acpi_debug_objects_sysctl(SYSCTL_HANDLER_ARGS); | static int acpi_debug_objects_sysctl(SYSCTL_HANDLER_ARGS); | ||||
| static int acpi_pm_func(u_long cmd, void *arg, ...); | static int acpi_pm_func(u_long cmd, void *arg, ...); | ||||
| static void acpi_enable_pcie(void); | static void acpi_enable_pcie(void); | ||||
| static void acpi_reset_interfaces(device_t dev); | static void acpi_reset_interfaces(device_t dev); | ||||
| Show All 28 Lines | static device_method_t acpi_methods[] = { | ||||
| DEVMETHOD(bus_map_resource, acpi_map_resource), | DEVMETHOD(bus_map_resource, acpi_map_resource), | ||||
| DEVMETHOD(bus_unmap_resource, acpi_unmap_resource), | DEVMETHOD(bus_unmap_resource, acpi_unmap_resource), | ||||
| DEVMETHOD(bus_child_pnpinfo, acpi_child_pnpinfo_method), | DEVMETHOD(bus_child_pnpinfo, acpi_child_pnpinfo_method), | ||||
| DEVMETHOD(bus_child_location, acpi_child_location_method), | DEVMETHOD(bus_child_location, acpi_child_location_method), | ||||
| DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), | DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), | ||||
| DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), | DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), | ||||
| DEVMETHOD(bus_hint_device_unit, acpi_hint_device_unit), | DEVMETHOD(bus_hint_device_unit, acpi_hint_device_unit), | ||||
| DEVMETHOD(bus_get_cpus, acpi_get_cpus), | DEVMETHOD(bus_get_cpus, acpi_get_cpus), | ||||
| DEVMETHOD(bus_get_domain, acpibus_get_domain_method), | DEVMETHOD(bus_get_domain, acpi_get_domain_method), | ||||
| DEVMETHOD(bus_get_property, acpi_bus_get_prop), | DEVMETHOD(bus_get_property, acpi_bus_get_prop), | ||||
| DEVMETHOD(bus_get_device_path, acpi_get_device_path), | DEVMETHOD(bus_get_device_path, acpi_get_device_path), | ||||
| /* ACPI bus */ | /* ACPI bus */ | ||||
| DEVMETHOD(acpi_id_probe, acpi_device_id_probe), | DEVMETHOD(acpi_id_probe, acpi_device_id_probe), | ||||
| DEVMETHOD(acpi_evaluate_object, acpi_device_eval_obj), | DEVMETHOD(acpi_evaluate_object, acpi_device_eval_obj), | ||||
| DEVMETHOD(acpi_get_property, acpi_device_get_prop), | DEVMETHOD(acpi_get_property, acpi_device_get_prop), | ||||
| DEVMETHOD(acpi_pwr_for_sleep, acpi_device_pwr_for_sleep), | DEVMETHOD(acpi_pwr_for_sleep, acpi_device_pwr_for_sleep), | ||||
| ▲ Show 20 Lines • Show All 347 Lines • ▼ Show 20 Lines | CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); | ||||
| SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | ||||
| OID_AUTO, "supported_sleep_state", | OID_AUTO, "supported_sleep_state", | ||||
| CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, | CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, | ||||
| 0, 0, acpi_supported_sleep_state_sysctl, "A", | 0, 0, acpi_supported_sleep_state_sysctl, "A", | ||||
| "List supported ACPI sleep states."); | "List supported ACPI sleep states."); | ||||
| SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | ||||
| OID_AUTO, "power_button_state", | OID_AUTO, "power_button_state", | ||||
| CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, | CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, | ||||
| &sc->acpi_power_button.sx, POWER_BUTTON_S2I, acpi_sleep_state_sysctl, | &sc->acpi_power_button_sx, 0, acpi_sleep_state_sysctl, "A", | ||||
| "A", "Power button ACPI sleep state."); | "Power button ACPI sleep state."); | ||||
| SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | ||||
| OID_AUTO, "sleep_button_state", | OID_AUTO, "sleep_button_state", | ||||
| CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, | CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, | ||||
| &sc->acpi_sleep_button.sx, POWER_BUTTON_S2I, acpi_sleep_state_sysctl, | &sc->acpi_sleep_button_sx, 0, acpi_sleep_state_sysctl, "A", | ||||
| "A", "Sleep button ACPI sleep state."); | "Sleep button ACPI sleep state."); | ||||
| SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | ||||
| OID_AUTO, "lid_switch_state", | OID_AUTO, "lid_switch_state", | ||||
| CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, | CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, | ||||
| &sc->acpi_lid_switch.sx, POWER_BUTTON_S2I, acpi_sleep_state_sysctl, "A", | &sc->acpi_lid_switch_sx, 0, acpi_sleep_state_sysctl, "A", | ||||
| "Lid ACPI sleep state. Set to S3 if you want to suspend your laptop when close the Lid."); | "Lid ACPI sleep state. Set to S3 if you want to suspend your laptop when close the Lid."); | ||||
| SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | ||||
| OID_AUTO, "standby_state", | OID_AUTO, "standby_state", | ||||
| CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, | CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, | ||||
| &sc->acpi_standby.sx, POWER_BUTTON_S2I, acpi_sleep_state_sysctl, "A", | &sc->acpi_standby_sx, 0, acpi_sleep_state_sysctl, "A", ""); | ||||
| ""); | |||||
| SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | SYSCTL_ADD_PROC(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | ||||
| OID_AUTO, "suspend_state", | OID_AUTO, "suspend_state", | ||||
| CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, | CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, | ||||
| &sc->acpi_suspend.sx, POWER_BUTTON_S2I, acpi_sleep_state_sysctl, "A", | &sc->acpi_suspend_sx, 0, acpi_sleep_state_sysctl, "A", ""); | ||||
| ""); | |||||
| SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | ||||
| OID_AUTO, "sleep_delay", CTLFLAG_RW, &sc->acpi_sleep_delay, 0, | OID_AUTO, "sleep_delay", CTLFLAG_RW, &sc->acpi_sleep_delay, 0, | ||||
| "sleep delay in seconds"); | "sleep delay in seconds"); | ||||
| SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | ||||
| OID_AUTO, "s4bios", CTLFLAG_RW, &sc->acpi_s4bios, 0, "S4BIOS mode"); | OID_AUTO, "s4bios", CTLFLAG_RW, &sc->acpi_s4bios, 0, "S4BIOS mode"); | ||||
| SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | ||||
| OID_AUTO, "verbose", CTLFLAG_RW, &sc->acpi_verbose, 0, "verbose mode"); | OID_AUTO, "verbose", CTLFLAG_RW, &sc->acpi_verbose, 0, "verbose mode"); | ||||
| SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | SYSCTL_ADD_INT(&sc->acpi_sysctl_ctx, SYSCTL_CHILDREN(sc->acpi_sysctl_tree), | ||||
| ▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | if (ACPI_SUCCESS(AcpiEvaluateObject(ACPI_ROOT_OBJECT, | ||||
| __DECONST(char *, AcpiGbl_SleepStateNames[state]), NULL, NULL)) && | __DECONST(char *, AcpiGbl_SleepStateNames[state]), NULL, NULL)) && | ||||
| ACPI_SUCCESS(AcpiGetSleepTypeData(state, &TypeA, &TypeB))) | ACPI_SUCCESS(AcpiGetSleepTypeData(state, &TypeA, &TypeB))) | ||||
| acpi_sleep_states[state] = TRUE; | acpi_sleep_states[state] = TRUE; | ||||
| /* | /* | ||||
| * Dispatch the default sleep state to devices. The lid switch is set | * Dispatch the default sleep state to devices. The lid switch is set | ||||
| * to UNKNOWN by default to avoid surprising users. | * to UNKNOWN by default to avoid surprising users. | ||||
| */ | */ | ||||
| sc->acpi_power_button.sx = acpi_sleep_states[ACPI_STATE_S5] ? | sc->acpi_power_button_sx = acpi_sleep_states[ACPI_STATE_S5] ? | ||||
| ACPI_STATE_S5 : ACPI_STATE_UNKNOWN; | ACPI_STATE_S5 : ACPI_STATE_UNKNOWN; | ||||
| sc->acpi_lid_switch.sx = ACPI_STATE_UNKNOWN; | sc->acpi_lid_switch_sx = ACPI_STATE_UNKNOWN; | ||||
| sc->acpi_standby.sx = acpi_sleep_states[ACPI_STATE_S1] ? | sc->acpi_standby_sx = acpi_sleep_states[ACPI_STATE_S1] ? | ||||
| ACPI_STATE_S1 : ACPI_STATE_UNKNOWN; | ACPI_STATE_S1 : ACPI_STATE_UNKNOWN; | ||||
| sc->acpi_suspend.sx = acpi_sleep_states[ACPI_STATE_S3] ? | sc->acpi_suspend_sx = acpi_sleep_states[ACPI_STATE_S3] ? | ||||
| ACPI_STATE_S3 : ACPI_STATE_UNKNOWN; | ACPI_STATE_S3 : ACPI_STATE_UNKNOWN; | ||||
| /* Pick the first valid sleep state for the sleep button default. */ | /* Pick the first valid sleep state for the sleep button default. */ | ||||
| sc->acpi_sleep_button.sx = ACPI_STATE_UNKNOWN; | sc->acpi_sleep_button_sx = ACPI_STATE_UNKNOWN; | ||||
| for (state = ACPI_STATE_S1; state <= ACPI_STATE_S4; state++) | for (state = ACPI_STATE_S1; state <= ACPI_STATE_S4; state++) | ||||
| if (acpi_sleep_states[state]) { | if (acpi_sleep_states[state]) { | ||||
| sc->acpi_sleep_button.sx = state; | sc->acpi_sleep_button_sx = state; | ||||
| break; | break; | ||||
| } | } | ||||
| acpi_enable_fixed_events(sc); | acpi_enable_fixed_events(sc); | ||||
| /* | /* | ||||
| * Scan the namespace and attach/initialise children. | * Scan the namespace and attach/initialise children. | ||||
| */ | */ | ||||
| /* Register our shutdown handler. */ | /* Register our shutdown handler. */ | ||||
| EVENTHANDLER_REGISTER(shutdown_final, acpi_shutdown_final, sc, | EVENTHANDLER_REGISTER(shutdown_final, acpi_shutdown_final, sc, | ||||
| SHUTDOWN_PRI_LAST + 150); | SHUTDOWN_PRI_LAST + 150); | ||||
| /* | /* | ||||
| * Register our acpi event handlers. | * Register our acpi event handlers. | ||||
| * XXX should be configurable eg. via userland policy manager. | * XXX should be configurable eg. via userland policy manager. | ||||
| */ | */ | ||||
| EVENTHANDLER_REGISTER(acpi_sleep_event, acpi_system_eventhandler_sleep, | EVENTHANDLER_REGISTER(acpi_sleep_event, acpi_system_eventhandler_sleep, | ||||
| sc, ACPI_EVENT_PRI_LAST); | sc, ACPI_EVENT_PRI_LAST); | ||||
| EVENTHANDLER_REGISTER(acpi_wakeup_event, acpi_system_eventhandler_wakeup, | EVENTHANDLER_REGISTER(acpi_wakeup_event, acpi_system_eventhandler_wakeup, | ||||
| sc, ACPI_EVENT_PRI_LAST); | sc, ACPI_EVENT_PRI_LAST); | ||||
| /* Flag our initial states. */ | /* Flag our initial states. */ | ||||
| sc->acpi_enabled = TRUE; | sc->acpi_enabled = TRUE; | ||||
| sc->acpi_sstate = AWAKE; | sc->acpi_sstate = ACPI_STATE_S0; | ||||
| sc->acpi_repressed_states.flags |= 1 << ACPI_SLEEP_DISABLED; | sc->acpi_sleep_disabled = TRUE; | ||||
| /* Create the control device */ | /* Create the control device */ | ||||
| sc->acpi_dev_t = make_dev(&acpi_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0664, | sc->acpi_dev_t = make_dev(&acpi_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0664, | ||||
| "acpi"); | "acpi"); | ||||
| sc->acpi_dev_t->si_drv1 = sc; | sc->acpi_dev_t->si_drv1 = sc; | ||||
| if ((error = acpi_machdep_init(dev))) | if ((error = acpi_machdep_init(dev))) | ||||
| goto out; | goto out; | ||||
| ▲ Show 20 Lines • Show All 584 Lines • ▼ Show 20 Lines | if (setsize != sizeof(cpuset_t)) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| CPU_AND(cpuset, cpuset, &cpuset_domain[d]); | CPU_AND(cpuset, cpuset, &cpuset_domain[d]); | ||||
| return (0); | return (0); | ||||
| default: | default: | ||||
| return (bus_generic_get_cpus(dev, child, op, setsize, cpuset)); | return (bus_generic_get_cpus(dev, child, op, setsize, cpuset)); | ||||
| } | } | ||||
| } | } | ||||
| /* | static int | ||||
| * Fetch the NUMA domain for the given device 'dev'. | |||||
| * | |||||
| * If a device has a _PXM method, map that to a NUMA domain. | |||||
| * Otherwise, pass the request up to the parent. | |||||
| * If there's no matching domain or the domain cannot be | |||||
| * determined, return ENOENT. | |||||
| */ | |||||
| int | |||||
| acpi_get_domain_method(device_t dev, device_t child, int *domain) | acpi_get_domain_method(device_t dev, device_t child, int *domain) | ||||
| { | { | ||||
| int d; | int error; | ||||
| d = acpi_pxm_parse(child); | |||||
| if (d >= 0) { | |||||
| *domain = d; | |||||
| return (0); | |||||
| } | |||||
| if (d == -1) | |||||
| return (ENOENT); | |||||
| /* No _PXM node; go up a level */ | |||||
| return (bus_generic_get_domain(dev, child, domain)); | |||||
| } | |||||
| static int | |||||
| acpibus_get_domain_method(device_t dev, device_t child, int *domain) | |||||
| { | |||||
| int error, d; | |||||
| error = acpi_read_ivar(dev, child, ACPI_IVAR_DOMAIN, | error = acpi_read_ivar(dev, child, ACPI_IVAR_DOMAIN, | ||||
| (uintptr_t *)domain); | (uintptr_t *)domain); | ||||
| if (error == 0 && *domain != ACPI_DEV_DOMAIN_UNKNOWN) | if (error == 0 && *domain != ACPI_DEV_DOMAIN_UNKNOWN) | ||||
| return (0); | return (0); | ||||
| d = acpi_parse_pxm(child); | |||||
| if (d >= 0) { | |||||
| *domain = d; | |||||
| acpi_write_ivar(dev, child, ACPI_IVAR_DOMAIN, d); | |||||
| return (0); | |||||
| } | |||||
| return (ENOENT); | return (ENOENT); | ||||
| } | } | ||||
| static struct rman * | static struct rman * | ||||
| acpi_get_rman(device_t bus, int type, u_int flags) | acpi_get_rman(device_t bus, int type, u_int flags) | ||||
| { | { | ||||
| /* Only memory and IO resources are managed. */ | /* Only memory and IO resources are managed. */ | ||||
| switch (type) { | switch (type) { | ||||
| ▲ Show 20 Lines • Show All 706 Lines • ▼ Show 20 Lines | acpi_device_pwr_for_sleep(device_t bus, device_t dev, int *dstate) | ||||
| acpi_MatchHid(handle, "PNP0511")) | acpi_MatchHid(handle, "PNP0511")) | ||||
| return (ENXIO); | return (ENXIO); | ||||
| /* | /* | ||||
| * Override next state with the value from _SxD, if present. | * Override next state with the value from _SxD, if present. | ||||
| * Note illegal _S0D is evaluated because some systems expect this. | * Note illegal _S0D is evaluated because some systems expect this. | ||||
| */ | */ | ||||
| sc = device_get_softc(bus); | sc = device_get_softc(bus); | ||||
| snprintf(sxd, sizeof(sxd), "_S%dD", sc->acpi_sstate); | |||||
| snprintf(sxd, sizeof(sxd), "_S%dD", | |||||
| sc->acpi_sstate != SUSPEND_TO_IDLE ?: SUSPEND); | |||||
| MPASS(sxd[2] < '4'); /* only up to S3 supported */ | |||||
| status = acpi_GetInteger(handle, sxd, dstate); | status = acpi_GetInteger(handle, sxd, dstate); | ||||
| if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { | if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { | ||||
| device_printf(dev, "failed to get %s on %s: %s\n", sxd, | device_printf(dev, "failed to get %s on %s: %s\n", sxd, | ||||
| acpi_name(handle), AcpiFormatException(status)); | acpi_name(handle), AcpiFormatException(status)); | ||||
| return (ENXIO); | return (ENXIO); | ||||
| } | } | ||||
| return (0); | return (0); | ||||
| ▲ Show 20 Lines • Show All 290 Lines • ▼ Show 20 Lines | |||||
| { | { | ||||
| ACPI_DEVICE_INFO *devinfo; | ACPI_DEVICE_INFO *devinfo; | ||||
| struct acpi_device *ad; | struct acpi_device *ad; | ||||
| struct acpi_prw_data prw; | struct acpi_prw_data prw; | ||||
| ACPI_OBJECT_TYPE type; | ACPI_OBJECT_TYPE type; | ||||
| ACPI_HANDLE h; | ACPI_HANDLE h; | ||||
| device_t bus, child; | device_t bus, child; | ||||
| char *handle_str; | char *handle_str; | ||||
| int order; | int d, order; | ||||
| ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | ||||
| if (acpi_disabled("children")) | if (acpi_disabled("children")) | ||||
| return_ACPI_STATUS (AE_OK); | return_ACPI_STATUS (AE_OK); | ||||
| /* Skip this device if we think we'll have trouble with it. */ | /* Skip this device if we think we'll have trouble with it. */ | ||||
| if (acpi_avoid(handle)) | if (acpi_avoid(handle)) | ||||
| ▲ Show 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | break; | ||||
| if (ACPI_SUCCESS(AcpiGetObjectInfo(handle, &devinfo))) { | if (ACPI_SUCCESS(AcpiGetObjectInfo(handle, &devinfo))) { | ||||
| if ((devinfo->Valid & ACPI_VALID_CLS) != 0 && | if ((devinfo->Valid & ACPI_VALID_CLS) != 0 && | ||||
| devinfo->ClassCode.Length >= ACPI_PCICLS_STRING_SIZE) { | devinfo->ClassCode.Length >= ACPI_PCICLS_STRING_SIZE) { | ||||
| ad->ad_cls_class = strtoul(devinfo->ClassCode.String, | ad->ad_cls_class = strtoul(devinfo->ClassCode.String, | ||||
| NULL, 16); | NULL, 16); | ||||
| } | } | ||||
| AcpiOsFree(devinfo); | AcpiOsFree(devinfo); | ||||
| } | } | ||||
| d = acpi_pxm_parse(child); | |||||
| if (d >= 0) | |||||
| ad->ad_domain = d; | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| return_ACPI_STATUS (AE_OK); | return_ACPI_STATUS (AE_OK); | ||||
| } | } | ||||
| /* | /* | ||||
| ▲ Show 20 Lines • Show All 653 Lines • ▼ Show 20 Lines | |||||
| { | { | ||||
| static int once; | static int once; | ||||
| if (!once) { | if (!once) { | ||||
| device_printf(sc->acpi_dev, | device_printf(sc->acpi_dev, | ||||
| "warning: acpi_SetSleepState() deprecated, need to update your software\n"); | "warning: acpi_SetSleepState() deprecated, need to update your software\n"); | ||||
| once = 1; | once = 1; | ||||
| } | } | ||||
| MPASS(state < ACPI_S_STATES_MAX); | return (acpi_EnterSleepState(sc, state)); | ||||
| return (acpi_EnterSleepState(sc, (enum sleep_type) state)); | |||||
| } | } | ||||
| #if defined(__amd64__) || defined(__i386__) | #if defined(__amd64__) || defined(__i386__) | ||||
| static void | static void | ||||
| acpi_sleep_force_task(void *context) | acpi_sleep_force_task(void *context) | ||||
| { | { | ||||
| struct acpi_softc *sc = (struct acpi_softc *)context; | struct acpi_softc *sc = (struct acpi_softc *)context; | ||||
| Show All 12 Lines | "suspend request timed out, forcing sleep now\n"); | ||||
| /* | /* | ||||
| * XXX Suspending from callout causes freezes in DEVICE_SUSPEND(). | * XXX Suspending from callout causes freezes in DEVICE_SUSPEND(). | ||||
| * Suspend from acpi_task thread instead. | * Suspend from acpi_task thread instead. | ||||
| */ | */ | ||||
| if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER, | if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER, | ||||
| acpi_sleep_force_task, sc))) | acpi_sleep_force_task, sc))) | ||||
| device_printf(sc->acpi_dev, "AcpiOsExecute() for sleeping failed\n"); | device_printf(sc->acpi_dev, "AcpiOsExecute() for sleeping failed\n"); | ||||
| } | } | ||||
| static enum sleep_type | |||||
| select_sleep_type(struct acpi_softc *sc, int state) | |||||
| { | |||||
| MPASS(state == ACPI_STATE_S3 || state == ACPI_STATE_S1); | |||||
| /* The user explicitly requested */ | |||||
| if (state == ACPI_STATE_S3 && | |||||
| sc->acpi_suspend.sx == SUSPEND_TO_IDLE) | |||||
| return SUSPEND_TO_IDLE; | |||||
| if (state == ACPI_STATE_S1 && | |||||
| sc->acpi_standby.sx == SUSPEND_TO_IDLE) | |||||
| return SUSPEND_TO_IDLE; | |||||
| /* The system supports the HW state, and that's probably best */ | |||||
| if (acpi_sleep_states[state]) | |||||
| return (enum sleep_type)state; | |||||
| /* idle is the best we can do. */ | |||||
| device_printf(sc->acpi_dev, | |||||
| "Requested S%d, but using S0IDLE instead\n", state); | |||||
| return SUSPEND_TO_IDLE; | |||||
| } | |||||
| #else | |||||
| static enum sleep_type | |||||
| select_sleep_type(struct acpi_softc *sc) | |||||
| { | |||||
| return AWAKE; | |||||
| } | |||||
| #endif | #endif | ||||
| /* Given a sleep type and a state, figures out if an error is warranted */ | |||||
| static int | |||||
| sanitize_sstate(struct acpi_softc *sc, enum sleep_type stype, int state, | |||||
| bool acpi_err) | |||||
| { | |||||
| if (stype == SUSPEND_TO_IDLE) | |||||
| goto out; | |||||
| if (state < ACPI_STATE_S1 || state > ACPI_S_STATES_MAX) | |||||
| return acpi_err ? (AE_BAD_PARAMETER) : (EINVAL); | |||||
| if (!acpi_sleep_states[state]) { | |||||
| device_printf(sc->acpi_dev, | |||||
| "Sleep state S%d not supported by BIOS\n", state); | |||||
| return acpi_err ? (AE_SUPPORT) : (EOPNOTSUPP); | |||||
| } | |||||
| out: | |||||
| return acpi_err ? (AE_OK) : (0); | |||||
| } | |||||
| /* | /* | ||||
| * Request that the system enter the given suspend state. All /dev/apm | * Request that the system enter the given suspend state. All /dev/apm | ||||
| * devices and devd(8) will be notified. Userland then has a chance to | * devices and devd(8) will be notified. Userland then has a chance to | ||||
| * save state and acknowledge the request. The system sleeps once all | * save state and acknowledge the request. The system sleeps once all | ||||
| * acks are in. | * acks are in. | ||||
| */ | */ | ||||
| int | int | ||||
| acpi_ReqSleepState(struct acpi_softc *sc, enum sleep_type stype) | acpi_ReqSleepState(struct acpi_softc *sc, int state) | ||||
| { | { | ||||
| #if defined(__amd64__) || defined(__i386__) | #if defined(__amd64__) || defined(__i386__) | ||||
| struct apm_clone_data *clone; | struct apm_clone_data *clone; | ||||
| ACPI_STATUS status; | ACPI_STATUS status; | ||||
| int sstate, err; | |||||
| sstate = acpi_stype2sstate(stype); | if (state < ACPI_STATE_S1 || state > ACPI_S_STATES_MAX) | ||||
| return (EINVAL); | |||||
| if (!acpi_sleep_states[state]) | |||||
| return (EOPNOTSUPP); | |||||
| err = sanitize_sstate(sc, stype, sstate, false); | |||||
| if (err) | |||||
| return err; | |||||
| /* | /* | ||||
| * If a reboot/shutdown/suspend request is already in progress or | * If a reboot/shutdown/suspend request is already in progress or | ||||
| * suspend is blocked due to an upcoming shutdown, just return. | * suspend is blocked due to an upcoming shutdown, just return. | ||||
| */ | */ | ||||
| if (rebooting || sc->acpi_next_sstate != AWAKE || suspend_blocked) { | if (rebooting || sc->acpi_next_sstate != 0 || suspend_blocked) { | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| /* Wait until sleep is enabled. */ | /* Wait until sleep is enabled. */ | ||||
| while (sc->acpi_repressed_states.flags & (1 <<ACPI_SLEEP_DISABLED)) { | while (sc->acpi_sleep_disabled) { | ||||
| AcpiOsSleep(1000); | AcpiOsSleep(1000); | ||||
| } | } | ||||
| ACPI_LOCK(acpi); | ACPI_LOCK(acpi); | ||||
| sc->acpi_next_sstate = stype; | sc->acpi_next_sstate = state; | ||||
| /* S5 (soft-off) should be entered directly with no waiting. */ | /* S5 (soft-off) should be entered directly with no waiting. */ | ||||
| if (stype == POWEROFF) { | if (state == ACPI_STATE_S5) { | ||||
| ACPI_UNLOCK(acpi); | ACPI_UNLOCK(acpi); | ||||
| status = acpi_EnterSleepState(sc, stype); | status = acpi_EnterSleepState(sc, state); | ||||
| return (ACPI_SUCCESS(status) ? 0 : ENXIO); | return (ACPI_SUCCESS(status) ? 0 : ENXIO); | ||||
| } | } | ||||
| /* Record the pending state and notify all apm devices. */ | /* Record the pending state and notify all apm devices. */ | ||||
| STAILQ_FOREACH(clone, &sc->apm_cdevs, entries) { | STAILQ_FOREACH(clone, &sc->apm_cdevs, entries) { | ||||
| clone->notify_status = APM_EV_NONE; | clone->notify_status = APM_EV_NONE; | ||||
| if ((clone->flags & ACPI_EVF_DEVD) == 0) { | if ((clone->flags & ACPI_EVF_DEVD) == 0) { | ||||
| selwakeuppri(&clone->sel_read, PZERO); | selwakeuppri(&clone->sel_read, PZERO); | ||||
| KNOTE_LOCKED(&clone->sel_read.si_note, 0); | KNOTE_LOCKED(&clone->sel_read.si_note, 0); | ||||
| } | } | ||||
| } | } | ||||
| /* If devd(8) is not running, immediately enter the sleep state. */ | /* If devd(8) is not running, immediately enter the sleep state. */ | ||||
| if (!devctl_process_running()) { | if (!devctl_process_running()) { | ||||
| ACPI_UNLOCK(acpi); | ACPI_UNLOCK(acpi); | ||||
| status = acpi_EnterSleepState(sc, stype); | status = acpi_EnterSleepState(sc, state); | ||||
| return (ACPI_SUCCESS(status) ? 0 : ENXIO); | return (ACPI_SUCCESS(status) ? 0 : ENXIO); | ||||
| } | } | ||||
| /* | /* | ||||
| * Set a timeout to fire if userland doesn't ack the suspend request | * Set a timeout to fire if userland doesn't ack the suspend request | ||||
| * in time. This way we still eventually go to sleep if we were | * in time. This way we still eventually go to sleep if we were | ||||
| * overheating or running low on battery, even if userland is hung. | * overheating or running low on battery, even if userland is hung. | ||||
| * We cancel this timeout once all userland acks are in or the | * We cancel this timeout once all userland acks are in or the | ||||
| * suspend request is aborted. | * suspend request is aborted. | ||||
| */ | */ | ||||
| callout_reset(&sc->susp_force_to, 10 * hz, acpi_sleep_force, sc); | callout_reset(&sc->susp_force_to, 10 * hz, acpi_sleep_force, sc); | ||||
| ACPI_UNLOCK(acpi); | ACPI_UNLOCK(acpi); | ||||
| /* Now notify devd(8) also. */ | /* Now notify devd(8) also. */ | ||||
| acpi_UserNotify("Suspend", ACPI_ROOT_OBJECT, stype); | acpi_UserNotify("Suspend", ACPI_ROOT_OBJECT, state); | ||||
| return (0); | return (0); | ||||
| #else | #else | ||||
| /* | /* This platform does not support acpi suspend/resume. */ | ||||
| * This platform does not support acpi suspend/resume. | |||||
| * TODO: Support s2idle here since it's platform agnostic. | |||||
| */ | |||||
| return (EOPNOTSUPP); | return (EOPNOTSUPP); | ||||
| #endif | #endif | ||||
| } | } | ||||
| /* | /* | ||||
| * Acknowledge (or reject) a pending sleep state. The caller has | * Acknowledge (or reject) a pending sleep state. The caller has | ||||
| * prepared for suspend and is now ready for it to proceed. If the | * prepared for suspend and is now ready for it to proceed. If the | ||||
| * error argument is non-zero, it indicates suspend should be cancelled | * error argument is non-zero, it indicates suspend should be cancelled | ||||
| * and gives an errno value describing why. Once all votes are in, | * and gives an errno value describing why. Once all votes are in, | ||||
| * we suspend the system. | * we suspend the system. | ||||
| */ | */ | ||||
| int | int | ||||
| acpi_AckSleepState(struct apm_clone_data *clone, int error) | acpi_AckSleepState(struct apm_clone_data *clone, int error) | ||||
| { | { | ||||
| #if defined(__amd64__) || defined(__i386__) | #if defined(__amd64__) || defined(__i386__) | ||||
| struct acpi_softc *sc; | struct acpi_softc *sc; | ||||
| int ret, sleeping; | int ret, sleeping; | ||||
| /* If no pending sleep state, return an error. */ | /* If no pending sleep state, return an error. */ | ||||
| ACPI_LOCK(acpi); | ACPI_LOCK(acpi); | ||||
| sc = clone->acpi_sc; | sc = clone->acpi_sc; | ||||
| if (sc->acpi_next_sstate == AWAKE) { | if (sc->acpi_next_sstate == 0) { | ||||
| ACPI_UNLOCK(acpi); | ACPI_UNLOCK(acpi); | ||||
| return (ENXIO); | return (ENXIO); | ||||
| } | } | ||||
| /* Caller wants to abort suspend process. */ | /* Caller wants to abort suspend process. */ | ||||
| if (error) { | if (error) { | ||||
| sc->acpi_next_sstate = AWAKE; | sc->acpi_next_sstate = 0; | ||||
| callout_stop(&sc->susp_force_to); | callout_stop(&sc->susp_force_to); | ||||
| device_printf(sc->acpi_dev, | device_printf(sc->acpi_dev, | ||||
| "listener on %s cancelled the pending suspend\n", | "listener on %s cancelled the pending suspend\n", | ||||
| devtoname(clone->cdev)); | devtoname(clone->cdev)); | ||||
| ACPI_UNLOCK(acpi); | ACPI_UNLOCK(acpi); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| Show All 18 Lines | callout_stop(&sc->susp_force_to); | ||||
| ACPI_UNLOCK(acpi); | ACPI_UNLOCK(acpi); | ||||
| ret = 0; | ret = 0; | ||||
| if (sleeping) { | if (sleeping) { | ||||
| if (ACPI_FAILURE(acpi_EnterSleepState(sc, sc->acpi_next_sstate))) | if (ACPI_FAILURE(acpi_EnterSleepState(sc, sc->acpi_next_sstate))) | ||||
| ret = ENODEV; | ret = ENODEV; | ||||
| } | } | ||||
| return (ret); | return (ret); | ||||
| #else | #else | ||||
| /* | /* This platform does not support acpi suspend/resume. */ | ||||
| * This platform does not support acpi suspend/resume. | |||||
| * TODO: Support s2idle here since it's platform agnostic. | |||||
| */ | |||||
| return (EOPNOTSUPP); | return (EOPNOTSUPP); | ||||
| #endif | #endif | ||||
| } | } | ||||
| static void | static void | ||||
| acpi_sleep_enable(void *arg) | acpi_sleep_enable(void *arg) | ||||
| { | { | ||||
| struct acpi_softc *sc = (struct acpi_softc *)arg; | struct acpi_softc *sc = (struct acpi_softc *)arg; | ||||
| ACPI_LOCK_ASSERT(acpi); | ACPI_LOCK_ASSERT(acpi); | ||||
| /* Reschedule if the system is not fully up and running. */ | /* Reschedule if the system is not fully up and running. */ | ||||
| if (!AcpiGbl_SystemAwakeAndRunning) { | if (!AcpiGbl_SystemAwakeAndRunning) { | ||||
| callout_schedule(&acpi_sleep_timer, hz * ACPI_MINIMUM_AWAKETIME); | callout_schedule(&acpi_sleep_timer, hz * ACPI_MINIMUM_AWAKETIME); | ||||
| return; | return; | ||||
| } | } | ||||
| atomic_clear_int(&sc->acpi_repressed_states.flags, 1 << ACPI_SLEEP_DISABLED); | sc->acpi_sleep_disabled = FALSE; | ||||
| } | } | ||||
| static ACPI_STATUS | static ACPI_STATUS | ||||
| acpi_sleep_disable(struct acpi_softc *sc) | acpi_sleep_disable(struct acpi_softc *sc) | ||||
| { | { | ||||
| ACPI_STATUS status; | |||||
| /* Fail if the system is not fully up and running. */ | /* Fail if the system is not fully up and running. */ | ||||
| if (!AcpiGbl_SystemAwakeAndRunning) | if (!AcpiGbl_SystemAwakeAndRunning) | ||||
| return (AE_ERROR); | return (AE_ERROR); | ||||
| if (atomic_testandset_int(&sc->acpi_repressed_states.flags, ACPI_SLEEP_DISABLED)) | ACPI_LOCK(acpi); | ||||
| return AE_ERROR; | status = sc->acpi_sleep_disabled ? AE_ERROR : AE_OK; | ||||
| sc->acpi_sleep_disabled = TRUE; | |||||
| ACPI_UNLOCK(acpi); | |||||
| return AE_OK; | return (status); | ||||
| } | } | ||||
| enum acpi_sleep_state { | enum acpi_sleep_state { | ||||
| ACPI_SS_NONE, | ACPI_SS_NONE, | ||||
| ACPI_SS_GPE_SET, | ACPI_SS_GPE_SET, | ||||
| ACPI_SS_DEV_SUSPEND, | ACPI_SS_DEV_SUSPEND, | ||||
| ACPI_SS_SLP_PREP, | ACPI_SS_SLP_PREP, | ||||
| ACPI_SS_SLEPT, | ACPI_SS_SLEPT, | ||||
| }; | }; | ||||
| static void | |||||
| acpi_state_transition_disable(struct acpi_softc *sc) | |||||
| { | |||||
| int tmp; | |||||
| tmp = atomic_swap_int(&sc->acpi_repressed_states.flags, ~0); | |||||
| /* Disallow re-entrancy. Disabling sleep with this is allowed though. */ | |||||
| if (tmp & ~(1 << ACPI_SLEEP_DISABLED)) { | |||||
| device_printf(sc->acpi_dev, "Invalid saved power flags"); | |||||
| tmp &= (1 << ACPI_SLEEP_DISABLED); | |||||
| } | |||||
| sc->acpi_repressed_states.saved_flags = tmp; | |||||
| } | |||||
| static void | |||||
| acpi_state_transition_enable(struct acpi_softc *sc) | |||||
| { | |||||
| #ifdef INVARIANTS | |||||
| int tmp; | |||||
| tmp = atomic_swap_int(&sc->acpi_repressed_states.flags, sc->acpi_repressed_states.saved_flags); | |||||
| MPASS(tmp == ~0); | |||||
| #else | |||||
| atomic_store_int(&sc->acpi_repressed_states.flags, sc->sc->acpi_repressed_states.saved_flags); | |||||
| #endif | |||||
| sc->acpi_repressed_states.saved_flags = 0; | |||||
| } | |||||
| BOOLEAN | |||||
| acpi_PowerTransitionIsEnabled(void) | |||||
| { | |||||
| struct acpi_softc *sc = devclass_get_softc(devclass_find("acpi"), 0); | |||||
| MPASS(sc != NULL); | |||||
| return (sc->acpi_repressed_states.flags & (1 << ACPI_POWER_DISABLED)) == 0; | |||||
| } | |||||
| static void | |||||
| __do_idle(struct acpi_softc *sc) | |||||
| { | |||||
| register_t intr; | |||||
| intr = intr_disable(); | |||||
| intr_suspend(); | |||||
| /* XXX: Is this actually required? */ | |||||
| intr_enable_src(acpi_GetSciInterrupt()); | |||||
| acpi_state_transition_disable(sc); | |||||
| cpu_idle(0); | |||||
| acpi_state_transition_enable(sc); | |||||
| intr_resume(false); | |||||
| intr_restore(intr); | |||||
| } | |||||
| static void | |||||
| __do_sleep(struct acpi_softc *sc, int state, enum acpi_sleep_state *pass) | |||||
| { | |||||
| int sleep_result; | |||||
| register_t intr; | |||||
| MPASS(state == ACPI_STATE_S3 || state == ACPI_STATE_S4); | |||||
| intr = intr_disable(); | |||||
| sleep_result = acpi_sleep_machdep(sc, state); | |||||
| acpi_wakeup_machdep(sc, state, sleep_result, 0); | |||||
| if (sleep_result == 1 && state == ACPI_STATE_S3) { | |||||
| /* | /* | ||||
| * XXX According to ACPI specification SCI_EN bit should be restored by | |||||
| * ACPI platform (BIOS, firmware) to its pre-sleep state. Unfortunately | |||||
| * some BIOSes fail to do that and that leads to unexpected and serious | |||||
| * consequences during wake up like a system getting stuck in SMI | |||||
| * handlers. This hack is picked up from Linux, which claims that it | |||||
| * follows Windows behavior. | |||||
| */ | |||||
| AcpiWriteBitRegister(ACPI_BITREG_SCI_ENABLE, ACPI_ENABLE_EVENT); | |||||
| } | |||||
| intr_restore(intr); | |||||
| /* call acpi_wakeup_machdep() again with interrupt enabled */ | |||||
| acpi_wakeup_machdep(sc, state, sleep_result, 1); | |||||
| AcpiLeaveSleepStatePrep(state); | |||||
| if (sleep_result == -1) | |||||
| return; | |||||
| /* Re-enable ACPI hardware on wakeup from sleep state 4. */ | |||||
| if (state == ACPI_STATE_S4) | |||||
| AcpiEnable(); | |||||
| *pass = ACPI_SS_SLEPT; | |||||
| } | |||||
| /* | |||||
| * Enter the desired system sleep state. | * Enter the desired system sleep state. | ||||
| * | * | ||||
| * Currently we support S1-S5 but S4 is only S4BIOS | * Currently we support S1-S5 but S4 is only S4BIOS | ||||
| */ | */ | ||||
| static ACPI_STATUS | static ACPI_STATUS | ||||
| acpi_EnterSleepState(struct acpi_softc *sc, enum sleep_type stype) | acpi_EnterSleepState(struct acpi_softc *sc, int state) | ||||
| { | { | ||||
| register_t intr; | |||||
| ACPI_STATUS status; | ACPI_STATUS status; | ||||
| ACPI_EVENT_STATUS power_button_status; | |||||
| enum acpi_sleep_state slp_state; | enum acpi_sleep_state slp_state; | ||||
| int state, err; | int sleep_result; | ||||
| ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, stype); | ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, state); | ||||
| state = acpi_stype2sstate(stype); | if (state < ACPI_STATE_S1 || state > ACPI_S_STATES_MAX) | ||||
| return_ACPI_STATUS (AE_BAD_PARAMETER); | |||||
| if (!acpi_sleep_states[state]) { | |||||
| device_printf(sc->acpi_dev, "Sleep state S%d not supported by BIOS\n", | |||||
| state); | |||||
| return (AE_SUPPORT); | |||||
| } | |||||
| err = sanitize_sstate(sc, stype, state, true); | |||||
| if (err) | |||||
| return_ACPI_STATUS (err); | |||||
| /* Re-entry once we're suspending is not allowed. */ | /* Re-entry once we're suspending is not allowed. */ | ||||
| status = acpi_sleep_disable(sc); | status = acpi_sleep_disable(sc); | ||||
| if (ACPI_FAILURE(status)) { | if (ACPI_FAILURE(status)) { | ||||
| device_printf(sc->acpi_dev, | device_printf(sc->acpi_dev, | ||||
| "suspend request ignored (not ready yet)\n"); | "suspend request ignored (not ready yet)\n"); | ||||
| return (status); | return (status); | ||||
| } | } | ||||
| if (stype == POWEROFF) { | if (state == ACPI_STATE_S5) { | ||||
| /* | /* | ||||
| * Shut down cleanly and power off. This will call us back through the | * Shut down cleanly and power off. This will call us back through the | ||||
| * shutdown handlers. | * shutdown handlers. | ||||
| */ | */ | ||||
| shutdown_nice(RB_POWEROFF); | shutdown_nice(RB_POWEROFF); | ||||
| return_ACPI_STATUS (AE_OK); | return_ACPI_STATUS (AE_OK); | ||||
| } | } | ||||
| EVENTHANDLER_INVOKE(power_suspend_early); | EVENTHANDLER_INVOKE(power_suspend_early); | ||||
| stop_all_proc(); | stop_all_proc(); | ||||
| suspend_all_fs(); | suspend_all_fs(); | ||||
| EVENTHANDLER_INVOKE(power_suspend); | EVENTHANDLER_INVOKE(power_suspend); | ||||
| // XXX s2idle | #ifdef EARLY_AP_STARTUP | ||||
| MPASS(mp_ncpus == 1 || smp_started); | MPASS(mp_ncpus == 1 || smp_started); | ||||
| thread_lock(curthread); | thread_lock(curthread); | ||||
| sched_bind(curthread, 0); | sched_bind(curthread, 0); | ||||
| thread_unlock(curthread); | thread_unlock(curthread); | ||||
| #else | |||||
| if (smp_started) { | |||||
| thread_lock(curthread); | |||||
| sched_bind(curthread, 0); | |||||
| thread_unlock(curthread); | |||||
| } | |||||
| #endif | |||||
| /* | /* | ||||
| * Be sure to hold Giant across DEVICE_SUSPEND/RESUME | * Be sure to hold Giant across DEVICE_SUSPEND/RESUME | ||||
| */ | */ | ||||
| bus_topo_lock(); | bus_topo_lock(); | ||||
| slp_state = ACPI_SS_NONE; | slp_state = ACPI_SS_NONE; | ||||
| sc->acpi_sstate = stype; | sc->acpi_sstate = state; | ||||
| /* Enable any GPEs as appropriate and requested by the user. */ | /* Enable any GPEs as appropriate and requested by the user. */ | ||||
| acpi_wake_prep_walk(state); | acpi_wake_prep_walk(state); | ||||
| slp_state = ACPI_SS_GPE_SET; | slp_state = ACPI_SS_GPE_SET; | ||||
| /* | /* | ||||
| * Inform all devices that we are going to sleep. If at least one | * Inform all devices that we are going to sleep. If at least one | ||||
| * device fails, DEVICE_SUSPEND() automatically resumes the tree. | * device fails, DEVICE_SUSPEND() automatically resumes the tree. | ||||
| * | * | ||||
| * XXX Note that a better two-pass approach with a 'veto' pass | * XXX Note that a better two-pass approach with a 'veto' pass | ||||
| * followed by a "real thing" pass would be better, but the current | * followed by a "real thing" pass would be better, but the current | ||||
| * bus interface does not provide for this. | * bus interface does not provide for this. | ||||
| */ | */ | ||||
| if (DEVICE_SUSPEND(root_bus) != 0) { | if (DEVICE_SUSPEND(root_bus) != 0) { | ||||
| device_printf(sc->acpi_dev, "device_suspend failed\n"); | device_printf(sc->acpi_dev, "device_suspend failed\n"); | ||||
| goto backout; | goto backout; | ||||
| } | } | ||||
| slp_state = ACPI_SS_DEV_SUSPEND; | slp_state = ACPI_SS_DEV_SUSPEND; | ||||
| if (stype != SUSPEND_TO_IDLE) { | |||||
| status = AcpiEnterSleepStatePrep(state); | status = AcpiEnterSleepStatePrep(state); | ||||
| if (ACPI_FAILURE(status)) { | if (ACPI_FAILURE(status)) { | ||||
| device_printf(sc->acpi_dev, "AcpiEnterSleepStatePrep failed - %s\n", | device_printf(sc->acpi_dev, "AcpiEnterSleepStatePrep failed - %s\n", | ||||
| AcpiFormatException(status)); | AcpiFormatException(status)); | ||||
| goto backout; | goto backout; | ||||
| } | } | ||||
| } | |||||
| slp_state = ACPI_SS_SLP_PREP; | slp_state = ACPI_SS_SLP_PREP; | ||||
| if (sc->acpi_sleep_delay > 0) | if (sc->acpi_sleep_delay > 0) | ||||
| DELAY(sc->acpi_sleep_delay * 1000000); | DELAY(sc->acpi_sleep_delay * 1000000); | ||||
| suspendclock(); | suspendclock(); | ||||
| switch (stype) { | intr = intr_disable(); | ||||
| case STANDBY: | if (state != ACPI_STATE_S1) { | ||||
| { | sleep_result = acpi_sleep_machdep(sc, state); | ||||
| register_t intr = intr_disable(); | acpi_wakeup_machdep(sc, state, sleep_result, 0); | ||||
| /* | |||||
| * XXX According to ACPI specification SCI_EN bit should be restored | |||||
| * by ACPI platform (BIOS, firmware) to its pre-sleep state. | |||||
| * Unfortunately some BIOSes fail to do that and that leads to | |||||
| * unexpected and serious consequences during wake up like a system | |||||
| * getting stuck in SMI handlers. | |||||
| * This hack is picked up from Linux, which claims that it follows | |||||
| * Windows behavior. | |||||
| */ | |||||
| if (sleep_result == 1 && state != ACPI_STATE_S4) | |||||
| AcpiWriteBitRegister(ACPI_BITREG_SCI_ENABLE, ACPI_ENABLE_EVENT); | |||||
| if (sleep_result == 1 && state == ACPI_STATE_S3) { | |||||
| /* | |||||
| * Prevent mis-interpretation of the wakeup by power button | |||||
| * as a request for power off. | |||||
| * Ideally we should post an appropriate wakeup event, | |||||
| * perhaps using acpi_event_power_button_wake or alike. | |||||
| * | |||||
| * Clearing of power button status after wakeup is mandated | |||||
| * by ACPI specification in section "Fixed Power Button". | |||||
| * | |||||
| * XXX As of ACPICA 20121114 AcpiGetEventStatus provides | |||||
| * status as 0/1 corressponding to inactive/active despite | |||||
| * its type being ACPI_EVENT_STATUS. In other words, | |||||
| * we should not test for ACPI_EVENT_FLAG_SET for time being. | |||||
| */ | |||||
| if (ACPI_SUCCESS(AcpiGetEventStatus(ACPI_EVENT_POWER_BUTTON, | |||||
| &power_button_status)) && power_button_status != 0) { | |||||
| AcpiClearEvent(ACPI_EVENT_POWER_BUTTON); | |||||
| device_printf(sc->acpi_dev, | |||||
| "cleared fixed power button status\n"); | |||||
| } | |||||
| } | |||||
| intr_restore(intr); | |||||
| /* call acpi_wakeup_machdep() again with interrupt enabled */ | |||||
| acpi_wakeup_machdep(sc, state, sleep_result, 1); | |||||
| AcpiLeaveSleepStatePrep(state); | |||||
| if (sleep_result == -1) | |||||
| goto backout; | |||||
| /* Re-enable ACPI hardware on wakeup from sleep state 4. */ | |||||
| if (state == ACPI_STATE_S4) | |||||
| AcpiEnable(); | |||||
| } else { | |||||
| status = AcpiEnterSleepState(state); | status = AcpiEnterSleepState(state); | ||||
| intr_restore(intr); | intr_restore(intr); | ||||
| AcpiLeaveSleepStatePrep(state); | AcpiLeaveSleepStatePrep(state); | ||||
| if (ACPI_FAILURE(status)) | if (ACPI_FAILURE(status)) { | ||||
| device_printf(sc->acpi_dev, "AcpiEnterSleepState failed - %s\n", | device_printf(sc->acpi_dev, "AcpiEnterSleepState failed - %s\n", | ||||
| AcpiFormatException(status)); | AcpiFormatException(status)); | ||||
| slp_state = ACPI_SS_SLEPT; | goto backout; | ||||
| } | } | ||||
| break; | |||||
| case SUSPEND: | |||||
| case HIBERNATE: | |||||
| __do_sleep(sc, state, &slp_state); | |||||
| break; | |||||
| case SUSPEND_TO_IDLE: | |||||
| __do_idle(sc); | |||||
| break; | |||||
| case AWAKE: | |||||
| case POWEROFF: | |||||
| default: | |||||
| __unreachable(); | |||||
| } | } | ||||
| slp_state = ACPI_SS_SLEPT; | |||||
| /* | /* | ||||
| * Back out state according to how far along we got in the suspend | * Back out state according to how far along we got in the suspend | ||||
| * process. This handles both the error and success cases. | * process. This handles both the error and success cases. | ||||
| */ | */ | ||||
| backout: | backout: | ||||
| if (slp_state >= ACPI_SS_SLP_PREP) | if (slp_state >= ACPI_SS_SLP_PREP) | ||||
| resumeclock(); | resumeclock(); | ||||
| if (slp_state >= ACPI_SS_GPE_SET) { | if (slp_state >= ACPI_SS_GPE_SET) { | ||||
| acpi_wake_prep_walk(state); | acpi_wake_prep_walk(state); | ||||
| sc->acpi_sstate = AWAKE; | sc->acpi_sstate = ACPI_STATE_S0; | ||||
| } | } | ||||
| if (slp_state >= ACPI_SS_DEV_SUSPEND) | if (slp_state >= ACPI_SS_DEV_SUSPEND) | ||||
| DEVICE_RESUME(root_bus); | DEVICE_RESUME(root_bus); | ||||
| if (slp_state >= ACPI_SS_SLP_PREP) | |||||
| if (stype != SUSPEND_TO_IDLE && (slp_state >= ACPI_SS_SLP_PREP)) | |||||
| AcpiLeaveSleepState(state); | AcpiLeaveSleepState(state); | ||||
| if (slp_state >= ACPI_SS_SLEPT) { | if (slp_state >= ACPI_SS_SLEPT) { | ||||
| #if defined(__i386__) || defined(__amd64__) | #if defined(__i386__) || defined(__amd64__) | ||||
| /* NB: we are still using ACPI timecounter at this point. */ | /* NB: we are still using ACPI timecounter at this point. */ | ||||
| resume_TSC(); | resume_TSC(); | ||||
| #endif | #endif | ||||
| acpi_resync_clock(sc); | acpi_resync_clock(sc); | ||||
| acpi_enable_fixed_events(sc); | acpi_enable_fixed_events(sc); | ||||
| } | } | ||||
| sc->acpi_next_sstate = AWAKE; | sc->acpi_next_sstate = 0; | ||||
| bus_topo_unlock(); | bus_topo_unlock(); | ||||
| // XXX s2idle | #ifdef EARLY_AP_STARTUP | ||||
| thread_lock(curthread); | thread_lock(curthread); | ||||
| sched_unbind(curthread); | sched_unbind(curthread); | ||||
| thread_unlock(curthread); | thread_unlock(curthread); | ||||
| #else | |||||
| if (smp_started) { | |||||
| thread_lock(curthread); | |||||
| sched_unbind(curthread); | |||||
| thread_unlock(curthread); | |||||
| } | |||||
| #endif | |||||
| resume_all_fs(); | resume_all_fs(); | ||||
| resume_all_proc(); | resume_all_proc(); | ||||
| EVENTHANDLER_INVOKE(power_resume); | EVENTHANDLER_INVOKE(power_resume); | ||||
| /* Allow another sleep request after a while. */ | /* Allow another sleep request after a while. */ | ||||
| callout_schedule(&acpi_sleep_timer, hz * ACPI_MINIMUM_AWAKETIME); | callout_schedule(&acpi_sleep_timer, hz * ACPI_MINIMUM_AWAKETIME); | ||||
| ▲ Show 20 Lines • Show All 296 Lines • ▼ Show 20 Lines | |||||
| /* | /* | ||||
| * ACPI Event Handlers | * ACPI Event Handlers | ||||
| */ | */ | ||||
| /* System Event Handlers (registered by EVENTHANDLER_REGISTER) */ | /* System Event Handlers (registered by EVENTHANDLER_REGISTER) */ | ||||
| static void | static void | ||||
| acpi_system_eventhandler_sleep(void *arg, enum sleep_type type) | acpi_system_eventhandler_sleep(void *arg, int state) | ||||
| { | { | ||||
| struct acpi_softc *sc = (struct acpi_softc *)arg; | struct acpi_softc *sc = (struct acpi_softc *)arg; | ||||
| int ret; | int ret; | ||||
| ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, type); | ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, state); | ||||
| /* Check if button action is disabled or unknown. */ | /* Check if button action is disabled or unknown. */ | ||||
| if (type > SUSPEND_TO_IDLE) | if (state == ACPI_STATE_UNKNOWN) | ||||
| return; | return; | ||||
| if (!acpi_PowerTransitionIsEnabled()) | |||||
| return; | |||||
| /* Request that the system prepare to enter the given suspend state. */ | /* Request that the system prepare to enter the given suspend state. */ | ||||
| ret = acpi_ReqSleepState(sc, type); | ret = acpi_ReqSleepState(sc, state); | ||||
| if (ret != 0) { | if (ret != 0) | ||||
| char state[2] = { 0 }; | |||||
| state[0] = type + '0'; | |||||
| device_printf(sc->acpi_dev, | device_printf(sc->acpi_dev, | ||||
| "request to enter state S%s failed (err %d)\n", | "request to enter state S%d failed (err %d)\n", state, ret); | ||||
| type == SUSPEND_TO_IDLE ? "0IDLE" : state, | |||||
| ret); | |||||
| } | |||||
| return_VOID; | return_VOID; | ||||
| } | } | ||||
| static void | static void | ||||
| acpi_system_eventhandler_wakeup(void *arg, enum sleep_type type) | acpi_system_eventhandler_wakeup(void *arg, int state) | ||||
| { | { | ||||
| ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, type); | ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, state); | ||||
| /* Currently, nothing to do for wakeup. */ | /* Currently, nothing to do for wakeup. */ | ||||
| MPASS(acpi_PowerTransitionIsEnabled()); | |||||
| return_VOID; | return_VOID; | ||||
| } | } | ||||
| /* | /* | ||||
| * ACPICA Event Handlers (FixedEvent, also called from button notify handler) | * ACPICA Event Handlers (FixedEvent, also called from button notify handler) | ||||
| */ | */ | ||||
| static void | static void | ||||
| Show All 18 Lines | |||||
| #else | #else | ||||
| (void)context; | (void)context; | ||||
| #endif | #endif | ||||
| ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | ||||
| #if defined(__amd64__) || defined(__i386__) | #if defined(__amd64__) || defined(__i386__) | ||||
| if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER, | if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER, | ||||
| acpi_invoke_sleep_eventhandler, &sc->acpi_power_button.sx))) | acpi_invoke_sleep_eventhandler, &sc->acpi_power_button_sx))) | ||||
| return_VALUE (ACPI_INTERRUPT_NOT_HANDLED); | return_VALUE (ACPI_INTERRUPT_NOT_HANDLED); | ||||
| #else | #else | ||||
| shutdown_nice(RB_POWEROFF); | shutdown_nice(RB_POWEROFF); | ||||
| #endif | #endif | ||||
| return_VALUE (ACPI_INTERRUPT_HANDLED); | return_VALUE (ACPI_INTERRUPT_HANDLED); | ||||
| } | } | ||||
| UINT32 | UINT32 | ||||
| acpi_event_power_button_wake(void *context) | acpi_event_power_button_wake(void *context) | ||||
| { | { | ||||
| ACPI_STATUS status; | |||||
| ACPI_EVENT_STATUS power_button_status; | |||||
| struct acpi_softc *sc = (struct acpi_softc *)context; | struct acpi_softc *sc = (struct acpi_softc *)context; | ||||
| ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | ||||
| status = AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_invoke_wake_eventhandler, | if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER, | ||||
| &sc->acpi_power_button.sx); | acpi_invoke_wake_eventhandler, &sc->acpi_power_button_sx))) | ||||
| if (ACPI_FAILURE(status)) | |||||
| return_VALUE (ACPI_INTERRUPT_NOT_HANDLED); | return_VALUE (ACPI_INTERRUPT_NOT_HANDLED); | ||||
| status = AcpiGetEventStatus(ACPI_EVENT_POWER_BUTTON, &power_button_status); | |||||
| if (ACPI_SUCCESS(status) && power_button_status != 0) { | |||||
| AcpiClearEvent(ACPI_EVENT_POWER_BUTTON); | |||||
| device_printf(sc->acpi_dev, "cleared fixed power button status\n"); | |||||
| } | |||||
| return_VALUE (ACPI_INTERRUPT_HANDLED); | return_VALUE (ACPI_INTERRUPT_HANDLED); | ||||
| } | } | ||||
| UINT32 | UINT32 | ||||
| acpi_event_sleep_button_sleep(void *context) | acpi_event_sleep_button_sleep(void *context) | ||||
| { | { | ||||
| struct acpi_softc *sc = (struct acpi_softc *)context; | struct acpi_softc *sc = (struct acpi_softc *)context; | ||||
| ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | ||||
| if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER, | if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER, | ||||
| acpi_invoke_sleep_eventhandler, &sc->acpi_sleep_button.sx))) | acpi_invoke_sleep_eventhandler, &sc->acpi_sleep_button_sx))) | ||||
| return_VALUE (ACPI_INTERRUPT_NOT_HANDLED); | return_VALUE (ACPI_INTERRUPT_NOT_HANDLED); | ||||
| return_VALUE (ACPI_INTERRUPT_HANDLED); | return_VALUE (ACPI_INTERRUPT_HANDLED); | ||||
| } | } | ||||
| UINT32 | UINT32 | ||||
| acpi_event_sleep_button_wake(void *context) | acpi_event_sleep_button_wake(void *context) | ||||
| { | { | ||||
| struct acpi_softc *sc = (struct acpi_softc *)context; | struct acpi_softc *sc = (struct acpi_softc *)context; | ||||
| ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | ||||
| if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER, | if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER, | ||||
| acpi_invoke_wake_eventhandler, &sc->acpi_sleep_button.sx))) | acpi_invoke_wake_eventhandler, &sc->acpi_sleep_button_sx))) | ||||
| return_VALUE (ACPI_INTERRUPT_NOT_HANDLED); | return_VALUE (ACPI_INTERRUPT_NOT_HANDLED); | ||||
| return_VALUE (ACPI_INTERRUPT_HANDLED); | return_VALUE (ACPI_INTERRUPT_HANDLED); | ||||
| } | } | ||||
| /* | /* | ||||
| * XXX This static buffer is suboptimal. There is no locking so only | * XXX This static buffer is suboptimal. There is no locking so only | ||||
| * use this for single-threaded callers. | * use this for single-threaded callers. | ||||
| */ | */ | ||||
| ▲ Show 20 Lines • Show All 211 Lines • ▼ Show 20 Lines | return (hp->fn(cmd, addr, hp->arg)); | ||||
| if ((flag & FWRITE) == 0) | if ((flag & FWRITE) == 0) | ||||
| return (EPERM); | return (EPERM); | ||||
| /* Core system ioctls. */ | /* Core system ioctls. */ | ||||
| switch (cmd) { | switch (cmd) { | ||||
| case ACPIIO_REQSLPSTATE: | case ACPIIO_REQSLPSTATE: | ||||
| state = *(int *)addr; | state = *(int *)addr; | ||||
| if (state != ACPI_STATE_S5) | if (state != ACPI_STATE_S5) | ||||
| return (acpi_ReqSleepState(sc, select_sleep_type(sc, state))); | return (acpi_ReqSleepState(sc, state)); | ||||
| device_printf(sc->acpi_dev, "power off via acpi ioctl not supported\n"); | device_printf(sc->acpi_dev, "power off via acpi ioctl not supported\n"); | ||||
| error = EOPNOTSUPP; | error = EOPNOTSUPP; | ||||
| break; | break; | ||||
| case ACPIIO_ACKSLPSTATE: | case ACPIIO_ACKSLPSTATE: | ||||
| error = *(int *)addr; | error = *(int *)addr; | ||||
| error = acpi_AckSleepState(sc->acpi_clone, error); | error = acpi_AckSleepState(sc->acpi_clone, error); | ||||
| break; | break; | ||||
| case ACPIIO_SETSLPSTATE: /* DEPRECATED */ | case ACPIIO_SETSLPSTATE: /* DEPRECATED */ | ||||
| state = *(int *)addr; | state = *(int *)addr; | ||||
| if (state < ACPI_STATE_S0 || state > ACPI_S_STATES_MAX) | if (state < ACPI_STATE_S0 || state > ACPI_S_STATES_MAX) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| if (!acpi_sleep_states[state]) | if (!acpi_sleep_states[state]) | ||||
| return (EOPNOTSUPP); | return (EOPNOTSUPP); | ||||
| if (ACPI_FAILURE(acpi_SetSleepState(sc, (enum sleep_type) state))) | if (ACPI_FAILURE(acpi_SetSleepState(sc, state))) | ||||
| error = ENXIO; | error = ENXIO; | ||||
| break; | break; | ||||
| default: | default: | ||||
| error = ENXIO; | error = ENXIO; | ||||
| break; | break; | ||||
| } | } | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| static int | static int | ||||
| acpi_stype2sstate(enum sleep_type type) | |||||
| { | |||||
| if (type == SUSPEND_TO_IDLE) | |||||
| return ACPI_STATE_UNKNOWN; | |||||
| return (int)type; | |||||
| } | |||||
| static int | |||||
| acpi_sname2sstate(const char *sname) | acpi_sname2sstate(const char *sname) | ||||
| { | { | ||||
| int sstate; | int sstate; | ||||
| if (toupper(sname[0]) == 'S') { | if (toupper(sname[0]) == 'S') { | ||||
| sstate = sname[1] - '0'; | sstate = sname[1] - '0'; | ||||
| if (sstate >= ACPI_STATE_S0 && sstate <= ACPI_STATE_S5 && | if (sstate >= ACPI_STATE_S0 && sstate <= ACPI_STATE_S5 && | ||||
| sname[2] == '\0') | sname[2] == '\0') | ||||
| Show All 21 Lines | acpi_supported_sleep_state_sysctl(SYSCTL_HANDLER_ARGS) | ||||
| int error; | int error; | ||||
| struct sbuf sb; | struct sbuf sb; | ||||
| UINT8 state; | UINT8 state; | ||||
| sbuf_new(&sb, NULL, 32, SBUF_AUTOEXTEND); | sbuf_new(&sb, NULL, 32, SBUF_AUTOEXTEND); | ||||
| for (state = ACPI_STATE_S1; state < ACPI_S_STATE_COUNT; state++) | for (state = ACPI_STATE_S1; state < ACPI_S_STATE_COUNT; state++) | ||||
| if (acpi_sleep_states[state]) | if (acpi_sleep_states[state]) | ||||
| sbuf_printf(&sb, "%s ", acpi_sstate2sname(state)); | sbuf_printf(&sb, "%s ", acpi_sstate2sname(state)); | ||||
| #if defined(__i386__) || defined(__amd64__) | |||||
| sbuf_printf(&sb, "S0IDLE "); | |||||
| #endif | |||||
| sbuf_trim(&sb); | sbuf_trim(&sb); | ||||
| sbuf_finish(&sb); | sbuf_finish(&sb); | ||||
| error = sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req); | error = sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req); | ||||
| sbuf_delete(&sb); | sbuf_delete(&sb); | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| static int | static int | ||||
| update_state(struct sysctl_oid *oidp, int new_state, int old_state) | |||||
| { | |||||
| if (new_state < ACPI_STATE_S1) | |||||
| return (EINVAL); | |||||
| if (new_state < ACPI_S_STATE_COUNT && !acpi_sleep_states[new_state]) | |||||
| return (EOPNOTSUPP); | |||||
| if (new_state != old_state) | |||||
| *(int *)oidp->oid_arg1 = new_state; | |||||
| return (0); | |||||
| } | |||||
| static int | |||||
| acpi_sleep_state_sysctl(SYSCTL_HANDLER_ARGS) | acpi_sleep_state_sysctl(SYSCTL_HANDLER_ARGS) | ||||
| { | { | ||||
| #if defined(__i386__) || defined(__amd64__) | |||||
| struct acpi_softc *sc; | |||||
| char sleep_state[10]; | char sleep_state[10]; | ||||
| int error, new_state, old_state; | int error, new_state, old_state; | ||||
| int *sx, *saved_sx; | |||||
| old_state = *(int *)oidp->oid_arg1; | old_state = *(int *)oidp->oid_arg1; | ||||
| #define SETUP_VARIABLES(event) do { \ | |||||
| sc = __containerof(__containerof(arg1, __typeof(sc->acpi_##event), sx), \ | |||||
| struct acpi_softc, acpi_##event); \ | |||||
| MPASS(sc); \ | |||||
| sx = &sc->acpi_##event.sx; \ | |||||
| MPASS(sx); \ | |||||
| saved_sx = &sc->acpi_##event.saved_sx; \ | |||||
| MPASS(saved_sx); \ | |||||
| } while (0) | |||||
| switch (arg2) { | |||||
| case POWER_BUTTON_S2I: | |||||
| SETUP_VARIABLES(power_button); | |||||
| break; | |||||
| case SLEEP_BUTTON_S2I: | |||||
| SETUP_VARIABLES(sleep_button); | |||||
| break; | |||||
| case LID_SWITCH_S2I: | |||||
| SETUP_VARIABLES(lid_switch); | |||||
| break; | |||||
| case STANDBY_S2I: | |||||
| SETUP_VARIABLES(standby); | |||||
| break; | |||||
| case SUSPEND_S2I: | |||||
| SETUP_VARIABLES(suspend); | |||||
| break; | |||||
| default: | |||||
| __unreachable(); | |||||
| }; | |||||
| #undef SETUP_VARIABLES | |||||
| if (*sx == SUSPEND_TO_IDLE) | |||||
| strlcpy(sleep_state, "S0IDLE", 7); | |||||
| else | |||||
| strlcpy(sleep_state, acpi_sstate2sname(old_state), sizeof(sleep_state)); | strlcpy(sleep_state, acpi_sstate2sname(old_state), sizeof(sleep_state)); | ||||
| error = sysctl_handle_string(oidp, sleep_state, sizeof(sleep_state), req); | error = sysctl_handle_string(oidp, sleep_state, sizeof(sleep_state), req); | ||||
| if (error || req->newptr == NULL) | |||||
| return (error); | |||||
| if (*sx == acpi_sname2sstate(sleep_state)) | |||||
| return (0); | |||||
| /* When "enabling" s0idle, leave don't touch the default */ | |||||
| if (!strncasecmp(sleep_state, "s0idle", 6) || | |||||
| !strncasecmp(sleep_state, "s2idle", 6)) { | |||||
| *saved_sx = *sx; | |||||
| *sx = SUSPEND_TO_IDLE; | |||||
| return (0); | |||||
| } else { | |||||
| new_state = acpi_sname2sstate(sleep_state); | |||||
| error = update_state(oidp, new_state, old_state); | |||||
| if (!error) | |||||
| *saved_sx = *sx; | |||||
| return (error); | |||||
| } | |||||
| #else | |||||
| char sleep_state[10]; | |||||
| int error, new_state, old_state; | |||||
| old_state = *(int *)oidp->oid_arg1; | |||||
| strlcpy(sleep_state, acpi_sstate2sname(old_state), sizeof(sleep_state)); | |||||
| error = sysctl_handle_string(oidp, sleep_state, sizeof(sleep_state), req); | |||||
| if (error == 0 && req->newptr != NULL) { | if (error == 0 && req->newptr != NULL) { | ||||
| new_state = acpi_sname2sstate(sleep_state); | new_state = acpi_sname2sstate(sleep_state); | ||||
| error = update_state(oidp, new_state, old_state); | if (new_state < ACPI_STATE_S1) | ||||
| return (EINVAL); | |||||
| if (new_state < ACPI_S_STATE_COUNT && !acpi_sleep_states[new_state]) | |||||
| return (EOPNOTSUPP); | |||||
| if (new_state != old_state) | |||||
| *(int *)oidp->oid_arg1 = new_state; | |||||
| } | } | ||||
| return (error); | return (error); | ||||
| #endif | |||||
| } | } | ||||
| /* Inform devctl(4) when we receive a Notify. */ | /* Inform devctl(4) when we receive a Notify. */ | ||||
| void | void | ||||
| acpi_UserNotify(const char *subsystem, ACPI_HANDLE h, uint8_t notify) | acpi_UserNotify(const char *subsystem, ACPI_HANDLE h, uint8_t notify) | ||||
| { | { | ||||
| char notify_buf[16]; | char notify_buf[16]; | ||||
| ACPI_BUFFER handle_buf; | ACPI_BUFFER handle_buf; | ||||
| ▲ Show 20 Lines • Show All 354 Lines • ▼ Show 20 Lines | case POWER_CMD_SUSPEND: | ||||
| } | } | ||||
| va_start(ap, arg); | va_start(ap, arg); | ||||
| state = va_arg(ap, int); | state = va_arg(ap, int); | ||||
| va_end(ap); | va_end(ap); | ||||
| switch (state) { | switch (state) { | ||||
| case POWER_SLEEP_STATE_STANDBY: | case POWER_SLEEP_STATE_STANDBY: | ||||
| acpi_state = sc->acpi_standby.sx; | acpi_state = sc->acpi_standby_sx; | ||||
| break; | break; | ||||
| case POWER_SLEEP_STATE_SUSPEND: | case POWER_SLEEP_STATE_SUSPEND: | ||||
| acpi_state = sc->acpi_suspend.sx; | acpi_state = sc->acpi_suspend_sx; | ||||
| break; | break; | ||||
| case POWER_SLEEP_STATE_HIBERNATE: | case POWER_SLEEP_STATE_HIBERNATE: | ||||
| /* FIXME: Needs support for s2idle */ | |||||
| acpi_state = ACPI_STATE_S4; | acpi_state = ACPI_STATE_S4; | ||||
| break; | break; | ||||
| default: | default: | ||||
| error = EINVAL; | error = EINVAL; | ||||
| goto out; | goto out; | ||||
| } | } | ||||
| if (ACPI_FAILURE(acpi_EnterSleepState(sc, acpi_state))) | if (ACPI_FAILURE(acpi_EnterSleepState(sc, acpi_state))) | ||||
| Show All 21 Lines | |||||