Date: Sat, 11 Dec 2004 01:14:23 +0000 From: Josef Karthauser <joe@FreeBSD.org> To: Nate Lawson <nate@root.org> Cc: freebsd-acpi@FreeBSD.org Subject: Re: S3 on a Sony VGN-A290 Message-ID: <20041211011423.GL1615@genius.tao.org.uk> In-Reply-To: <41B9EF34.9020500@root.org> References: <20041210133615.GA1482@genius.tao.org.uk> <41B9ED5A.80503@root.org> <20041210184350.GJ1615@genius.tao.org.uk> <41B9EF34.9020500@root.org>
next in thread | previous in thread | raw e-mail | index | archive | help
--00hq2S6J2Jlg6EbK Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable 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= =20 > >>suspend but this part is too experimental to MFC for a while. > >> > > > > > >Can I run a current kernel on a RELENG_5 userland? >=20 > For testing, should work ok. The recent mount changes may have hosed=20 > that path though. Instead, just use the attached patch against 5.x >=20 > --=20 > Nate > Index: sys/dev/acpica/acpi.c > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > 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> > =20 > +#include "pci_if.h" > +#include <dev/pci/pcivar.h> > +#include <dev/pci/pci_private.h> > + > MALLOC_DEFINE(M_ACPIDEV, "acpidev", "ACPI devices"); > =20 > /* 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), > =20 > /* 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), > =20 > + /* PCI emulation */ > + DEVMETHOD(pci_set_powerstate, acpi_set_powerstate_method), > + > /* ISA emulation */ > DEVMETHOD(isa_pnp_probe, acpi_isa_pnp_probe), > =20 > @@ -212,6 +229,12 @@ > static int acpi_serialize_methods; > TUNABLE_INT("hw.acpi.serialize_methods", &acpi_serialize_methods); > =20 > +/* Power devices off and on in suspend and resume. XXX Remove once test= ed. */ > +static int acpi_do_powerstate =3D 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 @@ > } > =20 > 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 =3D bus_generic_suspend(dev); > + if (error) > + return (error); > + > + /* > + * Now, set them into the appropriate power state, usually D3. If t= he > + * device has an _SxD method for the next sleep state, use that power > + * state instead. > + */ > + sc =3D device_get_softc(dev); > + device_get_children(dev, &devlist, &numdevs); > + for (i =3D 0; i < numdevs; i++) { > + /* If the device is not attached, we've powered it down elsewhere. */ > + child =3D 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 =3D PCI_POWERSTATE_D3; > + error =3D acpi_device_pwr_for_sleep(device_get_parent(child), > + child, &pstate); > + if ((error =3D=3D 0 || error =3D=3D ESRCH) && acpi_do_powerstate) > + pci_set_powerstate(child, pstate); > + } > + free(devlist, M_TEMP); > + error =3D 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 =3D 0; i < numdevs; i++) { > + child =3D devlist[i]; > + handle =3D 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) > { > =20 > @@ -624,6 +713,45 @@ > return (retval); > } > =20 > +/* > + * 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 =3D 0; i < numdevs; i++) { > + child =3D devlist[i]; > + if (device_get_state(child) =3D=3D DS_NOTPRESENT) { > + /* pci_set_powerstate(child, PCI_POWERSTATE_D0); */ > + if (device_probe_and_attach(child) !=3D 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)); > } > =20 > +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 =3D device_get_softc(bus); > + handle =3D 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 =3D=3D 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 =3D acpi_GetInteger(handle, sxd, dstate); > + else > + status =3D AcpiEvaluateObject(handle, sxd, NULL, NULL); > + > + switch (status) { > + case AE_OK: > + error =3D 0; > + break; > + case AE_NOT_FOUND: > + error =3D ESRCH; > + break; > + default: > + error =3D 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)); > } > =20 > +/* > + * Even though ACPI devices are not PCI, we use the PCI approach for set= ting > + * 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 =3D 0; > + h =3D acpi_get_handle(child); > + if (state < ACPI_STATE_D0 || state > ACPI_STATE_D3) > + return (EINVAL); > + if (h =3D=3D NULL) > + return (0); > + > + /* Ignore errors if the power methods aren't present. */ > + status =3D acpi_pwr_switch_consumer(h, state); > + if (ACPI_FAILURE(status) && status !=3D AE_NOT_FOUND > + && status !=3D 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 > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > 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 @@ > }; > =20 > # > +# 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 > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > 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" > =20 > +#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 parameter= s"); > =20 > static int pci_enable_io_modes =3D 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 the= re\n\ > are some peripherals that this causes problems with."); > =20 > -static int pci_do_powerstate =3D 0; > -TUNABLE_INT("hw.pci.do_powerstate", (int *)&pci_do_powerstate); > +static int pci_do_powerstate =3D 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; > =20 > /* > - * 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 =3D NULL; > + if (pci_do_powerstate) > + acpi_dev =3D devclass_get_device(devclass_find("acpi"), 0); > device_get_children(dev, &devlist, &numdevs); > for (i =3D 0; i < numdevs; i++) { > child =3D devlist[i]; > dinfo =3D (struct pci_devinfo *) device_get_ivars(child); > pci_cfg_save(child, dinfo, 0); > } > + > + /* Suspend devices before potentially powering them down. */ > + error =3D 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 =3D 0; acpi_dev && i < numdevs; i++) { > + child =3D devlist[i]; > + dinfo =3D (struct pci_devinfo *) device_get_ivars(child); > + if (device_is_attached(child) && dinfo->cfg.hdrtype =3D=3D 0) { > + dstate =3D 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); > } > =20 > 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; > =20 > /* > - * Restore the pci configuration space for each child. > + * Set each child to D0 and restore its PCI configuration space. > */ > + acpi_dev =3D NULL; > + if (pci_do_powerstate) > + acpi_dev =3D devclass_get_device(devclass_find("acpi"), 0); > device_get_children(dev, &devlist, &numdevs); > for (i =3D 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 =3D devlist[i]; > dinfo =3D (struct pci_devinfo *) device_get_ivars(child); > + if (acpi_dev && device_is_attached(child) && > + dinfo->cfg.hdrtype =3D=3D 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); --=20 Josef Karthauser (joe@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/ =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D An eclectic mix of fact an= d theory. =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --00hq2S6J2Jlg6EbK Content-Type: application/pgp-signature Content-Disposition: inline -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.6 (FreeBSD) iEYEARECAAYFAkG6Se4ACgkQXVIcjOaxUBZ4jgCgvR9meiQwYBTBNPS9A5Z9jUo/ YO8AoJL+eVJ+l88wtAKmJEDdc/0uPF0A =Inly -----END PGP SIGNATURE----- --00hq2S6J2Jlg6EbK--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20041211011423.GL1615>