S3 on a Sony VGN-A290
Josef Karthauser
joe at FreeBSD.org
Fri Dec 10 17:14:27 PST 2004
Wicked, thanks :)
J.
On Fri, Dec 10, 2004 at 10:47:16AM -0800, Nate Lawson wrote:
> Josef Karthauser wrote:
> >On Fri, Dec 10, 2004 at 10:39:22AM -0800, Nate Lawson wrote:
> >
> >>Josef Karthauser wrote:
> >>
> >>>Grump. I leave my new Sony in S3 and come back to it in the morning to
> >>>find that it's run out of battery :/. I thought that S3 was a low
> >>>energy state. Anyone else got a similar machine? Is it a problem with
> >>>the machine or our ACPI? (I'm running RELENG_5 on it).
> >>
> >>Try a -current kernel. It has more code to power down devices while in
> >>suspend but this part is too experimental to MFC for a while.
> >>
> >
> >
> >Can I run a current kernel on a RELENG_5 userland?
>
> For testing, should work ok. The recent mount changes may have hosed
> that path though. Instead, just use the attached patch against 5.x
>
> --
> Nate
> Index: sys/dev/acpica/acpi.c
> ===================================================================
> RCS file: /home/ncvs/src/sys/dev/acpica/acpi.c,v
> retrieving revision 1.186.2.6
> diff -u -r1.186.2.6 acpi.c
> --- sys/dev/acpica/acpi.c 7 Nov 2004 20:24:05 -0000 1.186.2.6
> +++ sys/dev/acpica/acpi.c 30 Nov 2004 20:32:31 -0000
> @@ -59,6 +59,10 @@
> #include <dev/acpica/acpiio.h>
> #include <contrib/dev/acpica/acnamesp.h>
>
> +#include "pci_if.h"
> +#include <dev/pci/pcivar.h>
> +#include <dev/pci/pci_private.h>
> +
> MALLOC_DEFINE(M_ACPIDEV, "acpidev", "ACPI devices");
>
> /* Hooks for the ACPI CA debugging infrastructure */
> @@ -87,10 +91,14 @@
> static void acpi_identify(driver_t *driver, device_t parent);
> static int acpi_probe(device_t dev);
> static int acpi_attach(device_t dev);
> +static int acpi_suspend(device_t dev);
> +static int acpi_resume(device_t dev);
> static int acpi_shutdown(device_t dev);
> static device_t acpi_add_child(device_t bus, int order, const char *name,
> int unit);
> static int acpi_print_child(device_t bus, device_t child);
> +static void acpi_probe_nomatch(device_t bus, device_t child);
> +static void acpi_driver_added(device_t dev, driver_t *driver);
> static int acpi_read_ivar(device_t dev, device_t child, int index,
> uintptr_t *result);
> static int acpi_write_ivar(device_t dev, device_t child, int index,
> @@ -110,10 +118,14 @@
> static ACPI_STATUS acpi_device_eval_obj(device_t bus, device_t dev,
> ACPI_STRING pathname, ACPI_OBJECT_LIST *parameters,
> ACPI_BUFFER *ret);
> +static int acpi_device_pwr_for_sleep(device_t bus, device_t dev,
> + int *dstate);
> static ACPI_STATUS acpi_device_scan_cb(ACPI_HANDLE h, UINT32 level,
> void *context, void **retval);
> static ACPI_STATUS acpi_device_scan_children(device_t bus, device_t dev,
> int max_depth, acpi_scan_cb_t user_fn, void *arg);
> +static int acpi_set_powerstate_method(device_t bus, device_t child,
> + int state);
> static int acpi_isa_pnp_probe(device_t bus, device_t child,
> struct isa_pnp_id *ids);
> static void acpi_probe_children(device_t bus);
> @@ -145,12 +157,14 @@
> DEVMETHOD(device_attach, acpi_attach),
> DEVMETHOD(device_shutdown, acpi_shutdown),
> DEVMETHOD(device_detach, bus_generic_detach),
> - DEVMETHOD(device_suspend, bus_generic_suspend),
> - DEVMETHOD(device_resume, bus_generic_resume),
> + DEVMETHOD(device_suspend, acpi_suspend),
> + DEVMETHOD(device_resume, acpi_resume),
>
> /* Bus interface */
> DEVMETHOD(bus_add_child, acpi_add_child),
> DEVMETHOD(bus_print_child, acpi_print_child),
> + DEVMETHOD(bus_probe_nomatch, acpi_probe_nomatch),
> + DEVMETHOD(bus_driver_added, acpi_driver_added),
> DEVMETHOD(bus_read_ivar, acpi_read_ivar),
> DEVMETHOD(bus_write_ivar, acpi_write_ivar),
> DEVMETHOD(bus_get_resource_list, acpi_get_rlist),
> @@ -160,7 +174,6 @@
> DEVMETHOD(bus_release_resource, acpi_release_resource),
> DEVMETHOD(bus_child_pnpinfo_str, acpi_child_pnpinfo_str_method),
> DEVMETHOD(bus_child_location_str, acpi_child_location_str_method),
> - DEVMETHOD(bus_driver_added, bus_generic_driver_added),
> DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
> DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
> DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
> @@ -169,8 +182,12 @@
> /* ACPI bus */
> DEVMETHOD(acpi_id_probe, acpi_device_id_probe),
> DEVMETHOD(acpi_evaluate_object, acpi_device_eval_obj),
> + DEVMETHOD(acpi_pwr_for_sleep, acpi_device_pwr_for_sleep),
> DEVMETHOD(acpi_scan_children, acpi_device_scan_children),
>
> + /* PCI emulation */
> + DEVMETHOD(pci_set_powerstate, acpi_set_powerstate_method),
> +
> /* ISA emulation */
> DEVMETHOD(isa_pnp_probe, acpi_isa_pnp_probe),
>
> @@ -212,6 +229,12 @@
> static int acpi_serialize_methods;
> TUNABLE_INT("hw.acpi.serialize_methods", &acpi_serialize_methods);
>
> +/* Power devices off and on in suspend and resume. XXX Remove once tested. */
> +static int acpi_do_powerstate = 1;
> +TUNABLE_INT("debug.acpi.do_powerstate", &acpi_do_powerstate);
> +SYSCTL_INT(_debug_acpi, OID_AUTO, do_powerstate, CTLFLAG_RW,
> + &acpi_do_powerstate, 1, "Turn off devices when suspending.");
> +
> /*
> * ACPI can only be loaded as a module by the loader; activating it after
> * system bootstrap time is not useful, and can be fatal to the system.
> @@ -570,6 +593,72 @@
> }
>
> static int
> +acpi_suspend(device_t dev)
> +{
> + struct acpi_softc *sc;
> + device_t child, *devlist;
> + int error, i, numdevs, pstate;
> +
> + /* First give child devices a chance to suspend. */
> + error = bus_generic_suspend(dev);
> + if (error)
> + return (error);
> +
> + /*
> + * Now, set them into the appropriate power state, usually D3. If the
> + * device has an _SxD method for the next sleep state, use that power
> + * state instead.
> + */
> + sc = device_get_softc(dev);
> + device_get_children(dev, &devlist, &numdevs);
> + for (i = 0; i < numdevs; i++) {
> + /* If the device is not attached, we've powered it down elsewhere. */
> + child = devlist[i];
> + if (!device_is_attached(child))
> + continue;
> +
> + /*
> + * Default to D3 for all sleep states. The _SxD method is optional
> + * so set the powerstate even if it's absent.
> + */
> + pstate = PCI_POWERSTATE_D3;
> + error = acpi_device_pwr_for_sleep(device_get_parent(child),
> + child, &pstate);
> + if ((error == 0 || error == ESRCH) && acpi_do_powerstate)
> + pci_set_powerstate(child, pstate);
> + }
> + free(devlist, M_TEMP);
> + error = 0;
> +
> + return (error);
> +}
> +
> +static int
> +acpi_resume(device_t dev)
> +{
> + ACPI_HANDLE handle;
> + int i, numdevs;
> + device_t child, *devlist;
> +
> + /*
> + * Put all devices in D0 before resuming them. Call _S0D on each one
> + * since some systems expect this.
> + */
> + device_get_children(dev, &devlist, &numdevs);
> + for (i = 0; i < numdevs; i++) {
> + child = devlist[i];
> + handle = acpi_get_handle(child);
> + if (handle)
> + AcpiEvaluateObject(handle, "_S0D", NULL, NULL);
> + if (device_is_attached(child) && acpi_do_powerstate)
> + pci_set_powerstate(child, PCI_POWERSTATE_D0);
> + }
> + free(devlist, M_TEMP);
> +
> + return (bus_generic_resume(dev));
> +}
> +
> +static int
> acpi_shutdown(device_t dev)
> {
>
> @@ -624,6 +713,45 @@
> return (retval);
> }
>
> +/*
> + * If this device is an ACPI child but no one claimed it, attempt
> + * to power it off. We'll power it back up when a driver is added.
> + *
> + * XXX Disabled for now since many necessary devices (like fdc and
> + * ATA) don't claim the devices we created for them but still expect
> + * them to be powered up.
> + */
> +static void
> +acpi_probe_nomatch(device_t bus, device_t child)
> +{
> +
> + /* pci_set_powerstate(child, PCI_POWERSTATE_D3); */
> +}
> +
> +/*
> + * If a new driver has a chance to probe a child, first power it up.
> + *
> + * XXX Disabled for now (see acpi_probe_nomatch for details).
> + */
> +static void
> +acpi_driver_added(device_t dev, driver_t *driver)
> +{
> + device_t child, *devlist;
> + int i, numdevs;
> +
> + DEVICE_IDENTIFY(driver, dev);
> + device_get_children(dev, &devlist, &numdevs);
> + for (i = 0; i < numdevs; i++) {
> + child = devlist[i];
> + if (device_get_state(child) == DS_NOTPRESENT) {
> + /* pci_set_powerstate(child, PCI_POWERSTATE_D0); */
> + if (device_probe_and_attach(child) != 0)
> + ; /* pci_set_powerstate(child, PCI_POWERSTATE_D3); */
> + }
> + }
> + free(devlist, M_TEMP);
> +}
> +
> /* Location hint for devctl(8) */
> static int
> acpi_child_location_str_method(device_t cbdev, device_t child, char *buf,
> @@ -1064,6 +1192,57 @@
> return (AcpiEvaluateObject(h, pathname, parameters, ret));
> }
>
> +static int
> +acpi_device_pwr_for_sleep(device_t bus, device_t dev, int *dstate)
> +{
> + struct acpi_softc *sc;
> + ACPI_HANDLE handle;
> + ACPI_STATUS status;
> + char sxd[8];
> + int error;
> +
> + sc = device_get_softc(bus);
> + handle = acpi_get_handle(dev);
> +
> + /*
> + * XXX If we find these devices, don't try to power them down.
> + * The serial and IRDA ports on my T23 hang the system when
> + * set to D3 and it appears that such legacy devices may
> + * need special handling in their drivers.
> + */
> + if (handle == NULL ||
> + acpi_MatchHid(handle, "PNP0500") ||
> + acpi_MatchHid(handle, "PNP0501") ||
> + acpi_MatchHid(handle, "PNP0502") ||
> + acpi_MatchHid(handle, "PNP0510") ||
> + acpi_MatchHid(handle, "PNP0511"))
> + return (ENXIO);
> +
> + /*
> + * Override next state with the value from _SxD, if present. If no
> + * dstate argument was provided, don't fetch the return value.
> + */
> + snprintf(sxd, sizeof(sxd), "_S%dD", sc->acpi_sstate);
> + if (dstate)
> + status = acpi_GetInteger(handle, sxd, dstate);
> + else
> + status = AcpiEvaluateObject(handle, sxd, NULL, NULL);
> +
> + switch (status) {
> + case AE_OK:
> + error = 0;
> + break;
> + case AE_NOT_FOUND:
> + error = ESRCH;
> + break;
> + default:
> + error = ENXIO;
> + break;
> + }
> +
> + return (error);
> +}
> +
> /* Callback arg for our implementation of walking the namespace. */
> struct acpi_device_scan_ctx {
> acpi_scan_cb_t user_fn;
> @@ -1138,6 +1317,34 @@
> acpi_device_scan_cb, &ctx, NULL));
> }
>
> +/*
> + * Even though ACPI devices are not PCI, we use the PCI approach for setting
> + * device power states since it's close enough to ACPI.
> + */
> +static int
> +acpi_set_powerstate_method(device_t bus, device_t child, int state)
> +{
> + ACPI_HANDLE h;
> + ACPI_STATUS status;
> + int error;
> +
> + error = 0;
> + h = acpi_get_handle(child);
> + if (state < ACPI_STATE_D0 || state > ACPI_STATE_D3)
> + return (EINVAL);
> + if (h == NULL)
> + return (0);
> +
> + /* Ignore errors if the power methods aren't present. */
> + status = acpi_pwr_switch_consumer(h, state);
> + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND
> + && status != AE_BAD_PARAMETER)
> + device_printf(bus, "failed to set ACPI power state D%d on %s: %s\n",
> + state, acpi_name(h), AcpiFormatException(status));
> +
> + return (error);
> +}
> +
> static int
> acpi_isa_pnp_probe(device_t bus, device_t child, struct isa_pnp_id *ids)
> {
> Index: sys/dev/acpica/acpi_if.m
> ===================================================================
> RCS file: /home/ncvs/src/sys/dev/acpica/acpi_if.m,v
> retrieving revision 1.2
> diff -u -r1.2 acpi_if.m
> --- sys/dev/acpica/acpi_if.m 15 Jul 2004 16:29:08 -0000 1.2
> +++ sys/dev/acpica/acpi_if.m 30 Nov 2004 20:32:31 -0000
> @@ -109,6 +109,26 @@
> };
>
> #
> +# Get the highest power state (D0-D3) that is usable for a device when
> +# suspending/resuming. If a bus calls this when suspending a device, it
> +# must also call it when resuming.
> +#
> +# device_t bus: parent bus for the device
> +#
> +# device_t dev: check this device's appropriate power state
> +#
> +# int *dstate: if successful, contains the highest valid sleep state
> +#
> +# Returns: 0 on success, ESRCH if device has no special state, or
> +# some other error value.
> +#
> +METHOD int pwr_for_sleep {
> + device_t bus;
> + device_t dev;
> + int *dstate;
> +};
> +
> +#
> # Rescan a subtree and optionally reattach devices to handles. Users
> # specify a callback that is called for each ACPI_HANDLE of type Device
> # that is a child of "dev".
> Index: sys/dev/pci/pci.c
> ===================================================================
> RCS file: /home/ncvs/src/sys/dev/pci/pci.c,v
> retrieving revision 1.264
> diff -u -r1.264 pci.c
> --- sys/dev/pci/pci.c 2 Jul 2004 13:42:36 -0000 1.264
> +++ sys/dev/pci/pci.c 30 Nov 2004 20:33:15 -0000
> @@ -60,6 +60,10 @@
> #include "pcib_if.h"
> #include "pci_if.h"
>
> +#include <contrib/dev/acpica/acpi.h>
> +#include <dev/acpica/acpivar.h>
> +#include "acpi_if.h"
> +
> static uint32_t pci_mapbase(unsigned mapreg);
> static int pci_maptype(unsigned mapreg);
> static int pci_mapsize(unsigned testval);
> @@ -169,15 +173,15 @@
> SYSCTL_NODE(_hw, OID_AUTO, pci, CTLFLAG_RD, 0, "PCI bus tuning parameters");
>
> static int pci_enable_io_modes = 1;
> -TUNABLE_INT("hw.pci.enable_io_modes", (int *)&pci_enable_io_modes);
> +TUNABLE_INT("hw.pci.enable_io_modes", &pci_enable_io_modes);
> SYSCTL_INT(_hw_pci, OID_AUTO, enable_io_modes, CTLFLAG_RW,
> &pci_enable_io_modes, 1,
> "Enable I/O and memory bits in the config register. Some BIOSes do not\n\
> enable these bits correctly. We'd like to do this all the time, but there\n\
> are some peripherals that this causes problems with.");
>
> -static int pci_do_powerstate = 0;
> -TUNABLE_INT("hw.pci.do_powerstate", (int *)&pci_do_powerstate);
> +static int pci_do_powerstate = 1;
> +TUNABLE_INT("hw.pci.do_powerstate", &pci_do_powerstate);
> SYSCTL_INT(_hw_pci, OID_AUTO, do_powerstate, CTLFLAG_RW,
> &pci_do_powerstate, 0,
> "Power down devices into D3 state when no driver attaches to them.\n\
> @@ -1015,43 +1019,78 @@
> int
> pci_suspend(device_t dev)
> {
> - int numdevs;
> - device_t *devlist;
> - device_t child;
> + int dstate, error, i, numdevs;
> + device_t acpi_dev, child, *devlist;
> struct pci_devinfo *dinfo;
> - int i;
>
> /*
> - * Save the pci configuration space for each child. We don't need
> - * to do this, unless the BIOS suspend code powers down the bus and
> - * the devices on the bus.
> + * Save the PCI configuration space for each child and set the
> + * device in the appropriate power state for this sleep state.
> */
> + acpi_dev = NULL;
> + if (pci_do_powerstate)
> + acpi_dev = devclass_get_device(devclass_find("acpi"), 0);
> device_get_children(dev, &devlist, &numdevs);
> for (i = 0; i < numdevs; i++) {
> child = devlist[i];
> dinfo = (struct pci_devinfo *) device_get_ivars(child);
> pci_cfg_save(child, dinfo, 0);
> }
> +
> + /* Suspend devices before potentially powering them down. */
> + error = bus_generic_suspend(dev);
> + if (error)
> + return (error);
> +
> + /*
> + * Always set the device to D3. If ACPI suggests a different
> + * power state, use it instead. If ACPI is not present, the
> + * firmware is responsible for managing device power. Skip
> + * children who aren't attached since they are powered down
> + * separately. Only manage type 0 devices for now.
> + */
> + for (i = 0; acpi_dev && i < numdevs; i++) {
> + child = devlist[i];
> + dinfo = (struct pci_devinfo *) device_get_ivars(child);
> + if (device_is_attached(child) && dinfo->cfg.hdrtype == 0) {
> + dstate = PCI_POWERSTATE_D3;
> + ACPI_PWR_FOR_SLEEP(acpi_dev, child, &dstate);
> + pci_set_powerstate(child, dstate);
> + }
> + }
> free(devlist, M_TEMP);
> - return (bus_generic_suspend(dev));
> + return (0);
> }
>
> int
> pci_resume(device_t dev)
> {
> - int numdevs;
> - device_t *devlist;
> - device_t child;
> + int i, numdevs;
> + device_t acpi_dev, child, *devlist;
> struct pci_devinfo *dinfo;
> - int i;
>
> /*
> - * Restore the pci configuration space for each child.
> + * Set each child to D0 and restore its PCI configuration space.
> */
> + acpi_dev = NULL;
> + if (pci_do_powerstate)
> + acpi_dev = devclass_get_device(devclass_find("acpi"), 0);
> device_get_children(dev, &devlist, &numdevs);
> for (i = 0; i < numdevs; i++) {
> + /*
> + * Notify ACPI we're going to D0 but ignore the result. If
> + * ACPI is not present, the firmware is responsible for
> + * managing device power. Only manage type 0 devices for now.
> + */
> child = devlist[i];
> dinfo = (struct pci_devinfo *) device_get_ivars(child);
> + if (acpi_dev && device_is_attached(child) &&
> + dinfo->cfg.hdrtype == 0) {
> + ACPI_PWR_FOR_SLEEP(acpi_dev, child, NULL);
> + pci_set_powerstate(child, PCI_POWERSTATE_D0);
> + }
> +
> + /* Now the device is powered up, restore its config space. */
> pci_cfg_restore(child, dinfo);
> }
> free(devlist, M_TEMP);
--
Josef Karthauser (joe at tao.org.uk) http://www.josef-k.net/
FreeBSD (cvs meister, admin and hacker) http://www.uk.FreeBSD.org/
Physics Particle Theory (student) http://www.pact.cpes.sussex.ac.uk/
================ An eclectic mix of fact and theory. =================
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 195 bytes
Desc: not available
Url : http://lists.freebsd.org/pipermail/freebsd-acpi/attachments/20041211/c92579ad/attachment.bin
More information about the freebsd-acpi
mailing list