From owner-freebsd-mobile@FreeBSD.ORG Sat Jan 1 19:20:01 2005 Return-Path: Delivered-To: freebsd-mobile@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id B95A316A4CE for ; Sat, 1 Jan 2005 19:20:01 +0000 (GMT) Received: from postal3.es.net (postal3.es.net [198.128.3.207]) by mx1.FreeBSD.org (Postfix) with ESMTP id 5C47B43D1D for ; Sat, 1 Jan 2005 19:20:01 +0000 (GMT) (envelope-from oberman@es.net) Received: from ptavv.es.net ([198.128.4.29]) by postal3.es.net (Postal Node 3) with ESMTP (SSL) id IBA74465; Sat, 01 Jan 2005 11:20:00 -0800 Received: from ptavv (localhost [127.0.0.1]) by ptavv.es.net (Tachyon Server) with ESMTP id 2923F5D04; Sat, 1 Jan 2005 11:20:00 -0800 (PST) X-Mailer: exmh version 2.7.0 06/18/2004 with nmh-1.0.4 To: Rainer Duffner In-reply-to: Your message of "Sat, 01 Jan 2005 20:18:18 +0100." <41D6F77A.1030905@ultra-secure.de> Mime-Version: 1.0 Content-Type: multipart/mixed ; boundary="==_Exmh_20371033920" Date: Sat, 01 Jan 2005 11:20:00 -0800 From: "Kevin Oberman" Message-Id: <20050101192000.2923F5D04@ptavv.es.net> cc: mobile@freebsd.org Subject: Re: Buying a laptop. (HP, Compaq, or Gateway) X-BeenThere: freebsd-mobile@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list List-Id: Mobile computing with FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 01 Jan 2005 19:20:01 -0000 This is a multipart MIME message. --==_Exmh_20371033920 Content-Type: text/plain; charset=us-ascii > Date: Sat, 01 Jan 2005 20:18:18 +0100 > From: Rainer Duffner > Sender: owner-freebsd-mobile@freebsd.org > > Erich Dollansky wrote: > > > Hi, > > > > Mark A-J. Raught (from the laptop) wrote: > > > > my ideas of a laptop are on the opposite of your scale (small, long > > battery life ...). > > > > > Indeed. > > > > All I know is that there is also Acer which offers at least three > > machines in your class. > > > >> > >> Gateway 7405GX (AMD 64 3200+) > >> HP zv5320us (AMD XP-M 3000+) > >> Compaq r3306us (AMD XP-M 3000+) > >> > >> On to the big problem; I can't find any info on any of the laptops > >> with FreeBSD. I checked the usual compatibility web pages, but > >> couldn't find > > > > > > I only have read about the Acer Ferraris. > > > > > I'd also be wary about consumer-grade laptops. > The customer-service is often non-existant and the low price comes with > (more often than not) ultra-low quality - just search Google for your > favourite laptop-brand, together with the word "hinges"...) > My advice, if you need the laptop to earn money, is to set for a > business-model (like HP NX7010 or so - the FSC E8010 I own will not be > so easy to get where you live, I'm afraid). > You can buy additional warranty, too. > I've only seen Acer PCs, end they seemed less than stellar. > > > >> Extra info: The laptop will be wiped (no windows please) and I will > >> be running FreeBSD. I have no problem replacing a miniPCI card if > >> need be, > > > > > > FreeBSD is not as good as Windows in a single point: power consumption. > > > > It's even worse than Linux (SuSE 9.2). At least on my Fujitsu-Siemens E8010. > But SuSE totally drops the ball in the ACPI-departement. I can at least > suspend-to-RAM with 5.3 (unfortunately not -to-disk), but SuSE 9.2 > doesn't even do that. > Well, it can suspend to RAM, but the SuSE doesn't wake-up anymore. Or it > wakes up and shows only a black screen. > FreeBSD works very good in that specific area. If you are running 5.3, you might try the acpi_pwr5 patch that njl@ posted about a month ago. It will substantially improve power performance in FreeBSD. It may (or may not) help with resume from suspend, I'll attach it an see of it makes it through the mailing list. -- R. Kevin Oberman, Network Engineer Energy Sciences Network (ESnet) Ernest O. Lawrence Berkeley National Laboratory (Berkeley Lab) E-mail: oberman@es.net Phone: +1 510 486-8634 --==_Exmh_20371033920 Content-Type: text/plain ; name="acpi_pwr5.diff"; charset=us-ascii Content-Description: acpi_pwr5.diff Content-Disposition: attachment; filename="acpi_pwr5.diff" 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 #include +#include "pci_if.h" +#include +#include + 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 +#include +#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); --==_Exmh_20371033920--