Module Name: src Committed By: jruoho Date: Thu Feb 17 19:36:49 UTC 2011
Modified Files: src/sys/dev/acpi: acpi.c acpi_wakedev.c acpi_wakedev.h acpivar.h Log Message: As explained in the new ACPICA documentation, as of ACPICA 20101207, the _PRW methods are no longer automatically executed as part of the ACPICA initialization. Refactor and rewrite the wake-device code to account this. To generate a diff of this commit: cvs rdiff -u -r1.235 -r1.236 src/sys/dev/acpi/acpi.c cvs rdiff -u -r1.21 -r1.22 src/sys/dev/acpi/acpi_wakedev.c cvs rdiff -u -r1.4 -r1.5 src/sys/dev/acpi/acpi_wakedev.h cvs rdiff -u -r1.67 -r1.68 src/sys/dev/acpi/acpivar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/acpi/acpi.c diff -u src/sys/dev/acpi/acpi.c:1.235 src/sys/dev/acpi/acpi.c:1.236 --- src/sys/dev/acpi/acpi.c:1.235 Tue Feb 15 20:24:11 2011 +++ src/sys/dev/acpi/acpi.c Thu Feb 17 19:36:49 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: acpi.c,v 1.235 2011/02/15 20:24:11 jruoho Exp $ */ +/* $NetBSD: acpi.c,v 1.236 2011/02/17 19:36:49 jruoho Exp $ */ /*- * Copyright (c) 2003, 2007 The NetBSD Foundation, Inc. @@ -100,7 +100,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: acpi.c,v 1.235 2011/02/15 20:24:11 jruoho Exp $"); +__KERNEL_RCSID(0, "$NetBSD: acpi.c,v 1.236 2011/02/17 19:36:49 jruoho Exp $"); #include "opt_acpi.h" #include "opt_pcifixup.h" @@ -685,6 +685,14 @@ (void)acpi_rescan(sc->sc_dev, NULL, NULL); /* + * Update GPE information. + * + * Note that this must be called after + * all GPE handlers have been installed. + */ + (void)AcpiUpdateAllGpes(); + + /* * Defer rest of the configuration. */ (void)config_defer(sc->sc_dev, acpi_rescan_capabilities); @@ -724,6 +732,7 @@ ad->ad_device = NULL; ad->ad_notify = NULL; ad->ad_pciinfo = NULL; + ad->ad_wakedev = NULL; ad->ad_type = type; ad->ad_handle = handle; @@ -735,6 +744,13 @@ acpi_set_node(ad); acpi_make_name(ad, devinfo->Name); + /* + * Identify wake GPEs from the _PRW. Note that + * AcpiUpdateAllGpes() must be called afterwards. + */ + if (ad->ad_devinfo->Type == ACPI_TYPE_DEVICE) + acpi_wakedev_init(ad); + SIMPLEQ_INIT(&ad->ad_child_head); SIMPLEQ_INSERT_TAIL(&sc->ad_head, ad, ad_list); @@ -940,9 +956,7 @@ /* * Scan wake-up capabilities. */ - rv = AcpiGetHandle(ad->ad_handle, "_PRW", &tmp); - - if (ACPI_SUCCESS(rv)) { + if (ad->ad_wakedev != NULL) { ad->ad_flags |= ACPI_DEVICE_WAKEUP; acpi_wakedev_add(ad); } Index: src/sys/dev/acpi/acpi_wakedev.c diff -u src/sys/dev/acpi/acpi_wakedev.c:1.21 src/sys/dev/acpi/acpi_wakedev.c:1.22 --- src/sys/dev/acpi/acpi_wakedev.c:1.21 Thu Feb 17 10:49:29 2011 +++ src/sys/dev/acpi/acpi_wakedev.c Thu Feb 17 19:36:49 2011 @@ -1,7 +1,7 @@ -/* $NetBSD: acpi_wakedev.c,v 1.21 2011/02/17 10:49:29 jruoho Exp $ */ +/* $NetBSD: acpi_wakedev.c,v 1.22 2011/02/17 19:36:49 jruoho Exp $ */ /*- - * Copyright (c) 2009, 2010 Jared D. McNeill <jmcne...@invisible.ca> + * Copyright (c) 2009, 2010, 2011 Jared D. McNeill <jmcne...@invisible.ca> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,12 +27,13 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: acpi_wakedev.c,v 1.21 2011/02/17 10:49:29 jruoho Exp $"); +__KERNEL_RCSID(0, "$NetBSD: acpi_wakedev.c,v 1.22 2011/02/17 19:36:49 jruoho Exp $"); #include <sys/param.h> #include <sys/device.h> #include <sys/sysctl.h> #include <sys/systm.h> +#include <sys/malloc.h> #include <dev/acpi/acpireg.h> #include <dev/acpi/acpivar.h> @@ -51,62 +52,164 @@ NULL, }; +MALLOC_DECLARE(M_ACPI); + static int32_t acpi_wakedev_acpinode = CTL_EOL; static int32_t acpi_wakedev_wakenode = CTL_EOL; -static void acpi_wakedev_method(struct acpi_devnode *, int, int); -static void acpi_wakedev_gpe(struct acpi_devnode *, int, int); static void acpi_wakedev_power(struct acpi_devnode *, ACPI_OBJECT *); +static void acpi_wakedev_set(struct acpi_devnode *, int); +static void acpi_wakedev_method(struct acpi_devnode *, int); -SYSCTL_SETUP(sysctl_acpi_wakedev_setup, "sysctl hw.acpi.wake subtree setup") +void +acpi_wakedev_init(struct acpi_devnode *ad) { - const struct sysctlnode *rnode; - int err; + ACPI_OBJECT *elm, *obj; + ACPI_HANDLE hdl = NULL; + ACPI_INTEGER val = 0; + ACPI_BUFFER buf; + ACPI_STATUS rv; - err = sysctl_createv(NULL, 0, NULL, &rnode, - CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", - NULL, NULL, 0, NULL, 0, - CTL_HW, CTL_EOL); + rv = acpi_eval_struct(ad->ad_handle, "_PRW", &buf); - if (err != 0) - return; + if (ACPI_FAILURE(rv)) + goto out; - err = sysctl_createv(NULL, 0, &rnode, &rnode, - CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi", - NULL, NULL, 0, NULL, 0, - CTL_CREATE, CTL_EOL); + obj = buf.Pointer; - if (err != 0) + if (obj->Type != ACPI_TYPE_PACKAGE) { + rv = AE_TYPE; + goto out; + } + + if (obj->Package.Count < 2) { + rv = AE_LIMIT; + goto out; + } + + /* + * As noted in ACPI 3.0 (section 7.2.10), the _PRW object is + * a package in which the first element is either an integer + * or again a package. In the latter case the package inside + * the package element has two elements, a reference handle + * and the GPE number. + */ + elm = &obj->Package.Elements[0]; + + switch (elm->Type) { + + case ACPI_TYPE_INTEGER: + val = elm->Integer.Value; + break; + + case ACPI_TYPE_PACKAGE: + + if (elm->Package.Count < 2) { + rv = AE_LIMIT; + goto out; + } + + rv = AE_TYPE; + + if (elm->Package.Elements[0].Type != ACPI_TYPE_LOCAL_REFERENCE) + goto out; + + if (elm->Package.Elements[1].Type != ACPI_TYPE_INTEGER) + goto out; + + hdl = elm->Package.Elements[0].Reference.Handle; + val = elm->Package.Elements[1].Integer.Value; + break; + + default: + rv = AE_TYPE; + goto out; + } + + ad->ad_wakedev = malloc(sizeof(*ad->ad_wakedev), + M_ACPI, M_NOWAIT | M_ZERO); + + if (ad->ad_wakedev == NULL) return; - acpi_wakedev_acpinode = rnode->sysctl_num; + ad->ad_wakedev->aw_handle = hdl; + ad->ad_wakedev->aw_number = val; - err = sysctl_createv(NULL, 0, &rnode, &rnode, - CTLFLAG_PERMANENT, CTLTYPE_NODE, - "wake", SYSCTL_DESCR("ACPI device wake-up"), - NULL, 0, NULL, 0, - CTL_CREATE, CTL_EOL); + /* + * The second element in _PRW is an integer + * that contains the lowest sleep state that + * can be entered while still providing wakeup. + */ + elm = &obj->Package.Elements[1]; - if (err != 0) + if (elm->Type == ACPI_TYPE_INTEGER) + ad->ad_wakedev->aw_sleep = elm->Integer.Value; + + /* + * Rest of the elements are references + * to power resources. Store these. + */ + acpi_wakedev_power(ad, obj); + + /* + * Last but not least, mark GPEs for wake. + */ + rv = AcpiSetupGpeForWake(ad->ad_handle, hdl, val); + +out: + if (buf.Pointer != NULL) + ACPI_FREE(buf.Pointer); + + if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) + aprint_error_dev(ad->ad_root, "failed to evaluate _PRW " + "for %s: %s\n", ad->ad_name, AcpiFormatException(rv)); +} + +static void +acpi_wakedev_power(struct acpi_devnode *ad, ACPI_OBJECT *obj) +{ + struct acpi_wakedev *aw = ad->ad_wakedev; + uint32_t i, j, n; + ACPI_OBJECT *elm; + ACPI_HANDLE hdl; + ACPI_STATUS rv; + + for (i = 0; i < __arraycount(aw->aw_power); i++) + aw->aw_power[i] = NULL; + + n = obj->Package.Count; + + if (n < 3 || n - 2 > __arraycount(aw->aw_power)) return; - acpi_wakedev_wakenode = rnode->sysctl_num; + for (i = 2, j = 0; i < n; i++, j++) { + + elm = &obj->Package.Elements[i]; + rv = acpi_eval_reference_handle(elm, &hdl); + + if (ACPI_FAILURE(rv)) + continue; + + ad->ad_wakedev->aw_power[j] = hdl; + } } void acpi_wakedev_add(struct acpi_devnode *ad) { + struct acpi_wakedev *aw; const char *str = NULL; device_t dev; int err; - KASSERT(ad != NULL && ad->ad_root != NULL); + KASSERT(ad != NULL && ad->ad_wakedev != NULL); KASSERT((ad->ad_flags & ACPI_DEVICE_WAKEUP) != 0); - ad->ad_wake = 0; + aw = ad->ad_wakedev; + aw->aw_enable = false; if (acpi_match_hid(ad->ad_devinfo, acpi_wakedev_default)) - ad->ad_wake = 1; + ad->ad_wakedev->aw_enable = true; if (acpi_wakedev_acpinode == CTL_EOL || acpi_wakedev_wakenode == CTL_EOL) @@ -126,7 +229,7 @@ err = sysctl_createv(NULL, 0, NULL, NULL, CTLFLAG_READWRITE, CTLTYPE_BOOL, str, - NULL, NULL, 0, &ad->ad_wake, 0, CTL_HW, + NULL, NULL, 0, &ad->ad_wakedev->aw_enable, 0, CTL_HW, acpi_wakedev_acpinode, acpi_wakedev_wakenode, CTL_CREATE, CTL_EOL); @@ -135,6 +238,41 @@ "(hw.acpi.wake.%s) failed (err %d)\n", str, err); } +SYSCTL_SETUP(sysctl_acpi_wakedev_setup, "sysctl hw.acpi.wake subtree setup") +{ + const struct sysctlnode *rnode; + int err; + + err = sysctl_createv(NULL, 0, NULL, &rnode, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", + NULL, NULL, 0, NULL, 0, + CTL_HW, CTL_EOL); + + if (err != 0) + return; + + err = sysctl_createv(NULL, 0, &rnode, &rnode, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi", + NULL, NULL, 0, NULL, 0, + CTL_CREATE, CTL_EOL); + + if (err != 0) + return; + + acpi_wakedev_acpinode = rnode->sysctl_num; + + err = sysctl_createv(NULL, 0, &rnode, &rnode, + CTLFLAG_PERMANENT, CTLTYPE_NODE, + "wake", SYSCTL_DESCR("ACPI device wake-up"), + NULL, 0, NULL, 0, + CTL_CREATE, CTL_EOL); + + if (err != 0) + return; + + acpi_wakedev_wakenode = rnode->sysctl_num; +} + void acpi_wakedev_commit(struct acpi_softc *sc, int state) { @@ -151,17 +289,66 @@ */ SIMPLEQ_FOREACH(ad, &sc->ad_head, ad_list) { - if ((ad->ad_flags & ACPI_DEVICE_WAKEUP) == 0) + if (ad->ad_wakedev == NULL) continue; - acpi_wakedev_gpe(ad, ad->ad_wake, state); - acpi_wakedev_method(ad, ad->ad_wake, state); + acpi_wakedev_set(ad, state); + acpi_wakedev_method(ad, state); } } static void -acpi_wakedev_method(struct acpi_devnode *ad, int enable, int state) +acpi_wakedev_set(struct acpi_devnode *ad, int state) { + struct acpi_wakedev *aw = ad->ad_wakedev; + ACPI_INTEGER val = aw->aw_number; + ACPI_HANDLE hdl = aw->aw_handle; + ACPI_STATUS rv; + uint8_t i; + + /* + * Enable or disable wake GPEs. + */ + if (aw->aw_enable != true) + rv = AcpiSetGpeWakeMask(hdl, val, ACPI_GPE_DISABLE); + + else { + rv = AcpiSetGpeWakeMask(hdl, val, ACPI_GPE_ENABLE); + + if (ACPI_FAILURE(rv)) + goto out; + + rv = AcpiEnableGpe(hdl, val); + + if (ACPI_FAILURE(rv)) + goto out; + + /* + * Turn on power resources. + */ + for (i = 0; i < __arraycount(aw->aw_power); i++) { + + if (aw->aw_power[i] == NULL) + continue; + + (void)acpi_power_res(aw->aw_power[i], hdl, true); + } + } + + if (state > aw->aw_sleep) + aprint_error_dev(ad->ad_root, "sleep state S%d " + "loses wake for %s\n", state, ad->ad_name); + +out: + if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) + aprint_error_dev(ad->ad_root, "failed to set wake GPE " + "for %s: %s\n", ad->ad_name, AcpiFormatException(rv)); +} + +static void +acpi_wakedev_method(struct acpi_devnode *ad, int state) +{ + const bool enable = ad->ad_wakedev->aw_enable; ACPI_OBJECT_LIST arg; ACPI_OBJECT obj[3]; ACPI_STATUS rv; @@ -211,122 +398,3 @@ aprint_error_dev(ad->ad_root, "failed to evaluate wake " "control method: %s\n", AcpiFormatException(rv)); } - -static void -acpi_wakedev_gpe(struct acpi_devnode *ad, int enable, int state) -{ - ACPI_OBJECT *elm, *obj; - ACPI_HANDLE hdl = NULL; - ACPI_INTEGER val; - ACPI_BUFFER buf; - ACPI_STATUS rv; - - rv = acpi_eval_struct(ad->ad_handle, "_PRW", &buf); - - if (ACPI_FAILURE(rv)) - return; - - obj = buf.Pointer; - - if (obj->Type != ACPI_TYPE_PACKAGE || obj->Package.Count < 2) - goto out; - - /* - * As noted in ACPI 3.0 (section 7.2.10), the _PRW object is - * a package in which the first element is either an integer - * or again a package. In the latter case the package inside - * the package element has two elements, a reference handle - * and the GPE number. - */ - elm = &obj->Package.Elements[0]; - - switch (elm->Type) { - - case ACPI_TYPE_INTEGER: - val = elm->Integer.Value; - break; - - case ACPI_TYPE_PACKAGE: - - if (elm->Package.Count < 2) - goto out; - - if (elm->Package.Elements[0].Type != ACPI_TYPE_LOCAL_REFERENCE) - goto out; - - if (elm->Package.Elements[1].Type != ACPI_TYPE_INTEGER) - goto out; - - hdl = elm->Package.Elements[0].Reference.Handle; - val = elm->Package.Elements[1].Integer.Value; - break; - - default: - goto out; - } - - /* - * The second element is an integer that contains the - * lowest sleep state that can be entered while still - * providing wake-up functionality. The rest of the - * elements are references to power resources. - */ - elm = &obj->Package.Elements[1]; - - if (elm->Type != ACPI_TYPE_INTEGER) - goto out; - - if (state > elm->Integer.Value) - aprint_error_dev(ad->ad_root, "sleep state S%d " - "loses wake for %s\n", state, ad->ad_name); - - /* - * Turn on power resources. - */ - if (enable != 0) - acpi_wakedev_power(ad, obj); - - /* - * This affects only wake GPEs, provided that _PRW works. - */ - if (enable != 0) - (void)AcpiSetGpe(hdl, val, ACPI_GPE_ENABLE); - else - (void)AcpiSetGpe(hdl, val, ACPI_GPE_DISABLE); - - /* - * XXX: Is this right? - */ - (void)AcpiUpdateAllGpes(); - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "wake GPE %s for %s\n", - (enable != 0) ? "enabled" : "disabled", ad->ad_name)); - -out: - ACPI_FREE(buf.Pointer); -} - -static void -acpi_wakedev_power(struct acpi_devnode *ad, ACPI_OBJECT *obj) -{ - ACPI_OBJECT *elm; - ACPI_HANDLE hdl; - ACPI_STATUS rv; - uint32_t i, n; - - n = obj->Package.Count; - - if (n < 3) - return; - - for (i = 2; i < n; i++) { - - elm = &obj->Package.Elements[i]; - rv = acpi_eval_reference_handle(elm, &hdl); - - if (ACPI_FAILURE(rv)) - continue; - - (void)acpi_power_res(hdl, ad->ad_handle, true); - } -} Index: src/sys/dev/acpi/acpi_wakedev.h diff -u src/sys/dev/acpi/acpi_wakedev.h:1.4 src/sys/dev/acpi/acpi_wakedev.h:1.5 --- src/sys/dev/acpi/acpi_wakedev.h:1.4 Tue Mar 16 05:48:43 2010 +++ src/sys/dev/acpi/acpi_wakedev.h Thu Feb 17 19:36:49 2011 @@ -1,7 +1,7 @@ -/* $NetBSD: acpi_wakedev.h,v 1.4 2010/03/16 05:48:43 jruoho Exp $ */ +/* $NetBSD: acpi_wakedev.h,v 1.5 2011/02/17 19:36:49 jruoho Exp $ */ /*- - * Copyright (c) 2009 Jared D. McNeill <jmcne...@invisible.ca> + * Copyright (c) 2009, 2011 Jared D. McNeill <jmcne...@invisible.ca> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,7 +29,16 @@ #ifndef _SYS_DEV_ACPI_ACPI_WAKEDEV_H #define _SYS_DEV_ACPI_ACPI_WAKEDEV_H -void acpi_wakedev_add(struct acpi_devnode *); -void acpi_wakedev_commit(struct acpi_softc *, int); +struct acpi_wakedev { + ACPI_HANDLE aw_power[8]; /* Power resources */ + ACPI_HANDLE aw_handle; /* Wake GPE handle */ + ACPI_INTEGER aw_number; /* Wake GPE number */ + ACPI_INTEGER aw_sleep; /* Highest sleep state for wake */ + bool aw_enable; /* Wake enabled? */ +}; + +void acpi_wakedev_init(struct acpi_devnode *); +void acpi_wakedev_add(struct acpi_devnode *); +void acpi_wakedev_commit(struct acpi_softc *, int); #endif /* !_SYS_DEV_ACPI_ACPI_WAKEDEV_H */ Index: src/sys/dev/acpi/acpivar.h diff -u src/sys/dev/acpi/acpivar.h:1.67 src/sys/dev/acpi/acpivar.h:1.68 --- src/sys/dev/acpi/acpivar.h:1.67 Mon Jan 17 15:49:13 2011 +++ src/sys/dev/acpi/acpivar.h Thu Feb 17 19:36:49 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: acpivar.h,v 1.67 2011/01/17 15:49:13 jmcneill Exp $ */ +/* $NetBSD: acpivar.h,v 1.68 2011/02/17 19:36:49 jruoho Exp $ */ /* * Copyright 2001 Wasabi Systems, Inc. @@ -108,6 +108,7 @@ * ad_root never NULL * ad_parent only NULL if root of the tree ("\") * ad_pciinfo NULL if not a PCI device + * ad_wakedev NULL if no wakeup capabilities * ad_notify NULL if there is no notify handler * ad_devinfo never NULL * ad_handle never NULL @@ -120,6 +121,7 @@ device_t ad_root; /* Backpointer to acpi_softc */ struct acpi_devnode *ad_parent; /* Backpointer to parent */ struct acpi_pci_info *ad_pciinfo; /* PCI info */ + struct acpi_wakedev *ad_wakedev; /* Device wake */ ACPI_NOTIFY_HANDLER ad_notify; /* Device notify */ ACPI_DEVICE_INFO *ad_devinfo; /* Device info */ ACPI_HANDLE ad_handle; /* Device handle */ @@ -127,7 +129,6 @@ uint32_t ad_flags; /* Device flags */ uint32_t ad_type; /* Device type */ int ad_state; /* Device power state */ - int ad_wake; /* Device wakeup */ SIMPLEQ_ENTRY(acpi_devnode) ad_list; SIMPLEQ_ENTRY(acpi_devnode) ad_child_list;