1 /* $NetBSD: acpi_wakedev.c,v 1.12 2010/04/18 14:05:26 jruoho Exp $ */ 2 3 /*- 4 * Copyright (c) 2009, 2010 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: acpi_wakedev.c,v 1.12 2010/04/18 14:05:26 jruoho Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/device.h> 34 #include <sys/sysctl.h> 35 #include <sys/systm.h> 36 37 #include <dev/acpi/acpireg.h> 38 #include <dev/acpi/acpivar.h> 39 #include <dev/acpi/acpi_wakedev.h> 40 41 #define _COMPONENT ACPI_BUS_COMPONENT 42 ACPI_MODULE_NAME ("acpi_wakedev") 43 44 static const char * const acpi_wakedev_default[] = { 45 "PNP0C0C", /* power button */ 46 "PNP0C0E", /* sleep button */ 47 "PNP0C0D", /* lid switch */ 48 "PNP03??", /* PC KBD port */ 49 NULL, 50 }; 51 52 static const struct sysctlnode *rnode = NULL; 53 54 static void acpi_wakedev_prepare(struct acpi_devnode *, int, int); 55 static void acpi_wakedev_gpe(struct acpi_devnode *, int); 56 57 SYSCTL_SETUP(sysctl_acpi_wakedev_setup, "sysctl hw.acpi.wake subtree setup") 58 { 59 int err; 60 61 err = sysctl_createv(NULL, 0, NULL, &rnode, 62 CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", 63 NULL, NULL, 0, NULL, 0, 64 CTL_HW, CTL_EOL); 65 66 if (err != 0) 67 goto fail; 68 69 err = sysctl_createv(NULL, 0, &rnode, &rnode, 70 CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi", 71 NULL, NULL, 0, NULL, 0, 72 CTL_CREATE, CTL_EOL); 73 74 if (err != 0) 75 goto fail; 76 77 err = sysctl_createv(NULL, 0, &rnode, &rnode, 78 CTLFLAG_PERMANENT, CTLTYPE_NODE, 79 "wake", SYSCTL_DESCR("ACPI device wake-up"), 80 NULL, 0, NULL, 0, 81 CTL_CREATE, CTL_EOL); 82 83 if (err != 0) 84 goto fail; 85 86 return; 87 88 fail: 89 rnode = NULL; 90 } 91 92 void 93 acpi_wakedev_add(struct acpi_devnode *ad) 94 { 95 int err; 96 97 KASSERT(ad != NULL && ad->ad_root != NULL); 98 KASSERT((ad->ad_flags & ACPI_DEVICE_WAKEUP) != 0); 99 100 ad->ad_wake = 0; 101 102 if (acpi_match_hid(ad->ad_devinfo, acpi_wakedev_default)) 103 ad->ad_wake = 1; 104 105 if (rnode == NULL) 106 return; 107 108 err = sysctl_createv(NULL, 0, &rnode, NULL, 109 CTLFLAG_READWRITE, CTLTYPE_BOOL, ad->ad_name, 110 NULL, NULL, 0, &ad->ad_wake, 0, 111 CTL_CREATE, CTL_EOL); 112 113 if (err != 0) 114 aprint_error_dev(ad->ad_root, "sysctl_createv" 115 "(hw.acpi.wake.%s) failed (err %d)\n", ad->ad_name, err); 116 } 117 118 void 119 acpi_wakedev_commit(struct acpi_softc *sc, int state) 120 { 121 struct acpi_devnode *ad; 122 123 /* 124 * As noted in ACPI 3.0 (p. 243), preparing 125 * a device for wakeup is a two-step process: 126 * 127 * 1. Enable all power resources in _PRW. 128 * 129 * 2. If present, execute _DSW/_PSW method. 130 * 131 * XXX: The first one is yet to be implemented. 132 */ 133 SIMPLEQ_FOREACH(ad, &sc->ad_head, ad_list) { 134 135 if ((ad->ad_flags & ACPI_DEVICE_WAKEUP) == 0) 136 continue; 137 138 acpi_wakedev_gpe(ad, ad->ad_wake); 139 acpi_wakedev_prepare(ad, ad->ad_wake, state); 140 } 141 } 142 143 static void 144 acpi_wakedev_prepare(struct acpi_devnode *ad, int enable, int state) 145 { 146 ACPI_OBJECT_LIST arg; 147 ACPI_OBJECT obj[3]; 148 ACPI_STATUS rv; 149 150 /* 151 * First try to call the Device Sleep Wake control method, _DSW. 152 * Only if this is not available, resort to to the Power State 153 * Wake control method, _PSW, which was deprecated in ACPI 3.0. 154 * 155 * The arguments to these methods are as follows: 156 * 157 * arg0 arg1 arg2 158 * ---- ---- ---- 159 * _PSW 0: disable 160 * 1: enable 161 * 162 * _DSW 0: disable 0: S0 0: D0 163 * 1: enable 1: S1 1: D0 or D1 164 * 2: D0, D1, or D2 165 * x: Sx 3: D0, D1, D2 or D3 166 */ 167 arg.Count = 3; 168 arg.Pointer = obj; 169 170 obj[0].Integer.Value = enable; 171 obj[1].Integer.Value = state; 172 obj[2].Integer.Value = 3; 173 174 obj[0].Type = obj[1].Type = obj[2].Type = ACPI_TYPE_INTEGER; 175 176 rv = AcpiEvaluateObject(ad->ad_handle, "_DSW", &arg, NULL); 177 178 if (ACPI_SUCCESS(rv)) 179 return; 180 181 if (rv != AE_NOT_FOUND) 182 goto fail; 183 184 rv = acpi_eval_set_integer(ad->ad_handle, "_PSW", enable); 185 186 if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) 187 goto fail; 188 189 return; 190 191 fail: 192 aprint_error_dev(ad->ad_root, "failed to evaluate wake " 193 "control method: %s\n", AcpiFormatException(rv)); 194 } 195 196 static void 197 acpi_wakedev_gpe(struct acpi_devnode *ad, int enable) 198 { 199 ACPI_OBJECT *elm, *obj; 200 ACPI_INTEGER val; 201 ACPI_BUFFER buf; 202 ACPI_STATUS rv; 203 204 rv = acpi_eval_struct(ad->ad_handle, METHOD_NAME__PRW, &buf); 205 206 if (ACPI_FAILURE(rv)) 207 return; 208 209 obj = buf.Pointer; 210 211 if (obj->Type != ACPI_TYPE_PACKAGE || obj->Package.Count < 2) 212 goto out; 213 214 /* 215 * As noted in ACPI 3.0 (section 7.2.10), the _PRW object is 216 * a package in which the first element is either an integer 217 * or again a package. In the latter case the package inside 218 * the package element has two elements, a reference handle 219 * and the GPE number. 220 */ 221 elm = &obj->Package.Elements[0]; 222 223 switch (elm->Type) { 224 225 case ACPI_TYPE_INTEGER: 226 val = elm->Integer.Value; 227 break; 228 229 case ACPI_TYPE_PACKAGE: 230 231 if (elm->Package.Count < 2) 232 goto out; 233 234 if (elm->Package.Elements[0].Type != ACPI_TYPE_LOCAL_REFERENCE) 235 goto out; 236 237 if (elm->Package.Elements[1].Type != ACPI_TYPE_INTEGER) 238 goto out; 239 240 val = elm->Package.Elements[1].Integer.Value; 241 break; 242 243 default: 244 goto out; 245 } 246 247 /* 248 * Set or unset a GPE as both runtime and wake. 249 */ 250 if (enable == 0) 251 (void)AcpiDisableGpe(NULL, val, ACPI_NOT_ISR); 252 else { 253 (void)AcpiSetGpeType(NULL, val, ACPI_GPE_TYPE_WAKE_RUN); 254 (void)AcpiEnableGpe(NULL, val, ACPI_NOT_ISR); 255 } 256 257 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "wake GPE %s for %s\n", 258 (enable != 0) ? "enabled" : "disabled", ad->ad_name)); 259 260 out: 261 ACPI_FREE(buf.Pointer); 262 } 263