1 /* $NetBSD: acpi_wakedev.c,v 1.20 2011/01/02 12:06:02 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.20 2011/01/02 12:06:02 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_pci.h> 40 #include <dev/acpi/acpi_power.h> 41 #include <dev/acpi/acpi_wakedev.h> 42 43 #define _COMPONENT ACPI_BUS_COMPONENT 44 ACPI_MODULE_NAME ("acpi_wakedev") 45 46 static const char * const acpi_wakedev_default[] = { 47 "PNP0C0C", /* power button */ 48 "PNP0C0E", /* sleep button */ 49 "PNP0C0D", /* lid switch */ 50 "PNP03??", /* PC KBD port */ 51 NULL, 52 }; 53 54 static int32_t acpi_wakedev_acpinode = CTL_EOL; 55 static int32_t acpi_wakedev_wakenode = CTL_EOL; 56 57 static void acpi_wakedev_method(struct acpi_devnode *, int, int); 58 static void acpi_wakedev_gpe(struct acpi_devnode *, int, int); 59 static void acpi_wakedev_power(struct acpi_devnode *, ACPI_OBJECT *); 60 61 SYSCTL_SETUP(sysctl_acpi_wakedev_setup, "sysctl hw.acpi.wake subtree setup") 62 { 63 const struct sysctlnode *rnode; 64 int err; 65 66 err = sysctl_createv(NULL, 0, NULL, &rnode, 67 CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", 68 NULL, NULL, 0, NULL, 0, 69 CTL_HW, CTL_EOL); 70 71 if (err != 0) 72 return; 73 74 err = sysctl_createv(NULL, 0, &rnode, &rnode, 75 CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi", 76 NULL, NULL, 0, NULL, 0, 77 CTL_CREATE, CTL_EOL); 78 79 if (err != 0) 80 return; 81 82 acpi_wakedev_acpinode = rnode->sysctl_num; 83 84 err = sysctl_createv(NULL, 0, &rnode, &rnode, 85 CTLFLAG_PERMANENT, CTLTYPE_NODE, 86 "wake", SYSCTL_DESCR("ACPI device wake-up"), 87 NULL, 0, NULL, 0, 88 CTL_CREATE, CTL_EOL); 89 90 if (err != 0) 91 return; 92 93 acpi_wakedev_wakenode = rnode->sysctl_num; 94 } 95 96 void 97 acpi_wakedev_add(struct acpi_devnode *ad) 98 { 99 const char *str = NULL; 100 device_t dev; 101 int err; 102 103 KASSERT(ad != NULL && ad->ad_root != NULL); 104 KASSERT((ad->ad_flags & ACPI_DEVICE_WAKEUP) != 0); 105 106 ad->ad_wake = 0; 107 108 if (acpi_match_hid(ad->ad_devinfo, acpi_wakedev_default)) 109 ad->ad_wake = 1; 110 111 if (acpi_wakedev_acpinode == CTL_EOL || 112 acpi_wakedev_wakenode == CTL_EOL) 113 return; 114 115 if (ad->ad_device != NULL) 116 str = device_xname(ad->ad_device); 117 else { 118 dev = acpi_pcidev_find_dev(ad); 119 120 if (dev != NULL) 121 str = device_xname(dev); 122 } 123 124 if (str == NULL) 125 return; 126 127 err = sysctl_createv(NULL, 0, NULL, NULL, 128 CTLFLAG_READWRITE, CTLTYPE_BOOL, str, 129 NULL, NULL, 0, &ad->ad_wake, 0, CTL_HW, 130 acpi_wakedev_acpinode, acpi_wakedev_wakenode, 131 CTL_CREATE, CTL_EOL); 132 133 if (err != 0) 134 aprint_error_dev(ad->ad_root, "sysctl_createv" 135 "(hw.acpi.wake.%s) failed (err %d)\n", str, err); 136 } 137 138 void 139 acpi_wakedev_commit(struct acpi_softc *sc, int state) 140 { 141 struct acpi_devnode *ad; 142 143 /* 144 * To prepare a device for wakeup: 145 * 146 * 1. Set appropriate GPEs. 147 * 148 * 2. Enable all power resources in _PRW. 149 * 150 * 3. If present, execute _DSW/_PSW method. 151 */ 152 SIMPLEQ_FOREACH(ad, &sc->ad_head, ad_list) { 153 154 if ((ad->ad_flags & ACPI_DEVICE_WAKEUP) == 0) 155 continue; 156 157 acpi_wakedev_gpe(ad, ad->ad_wake, state); 158 acpi_wakedev_method(ad, ad->ad_wake, state); 159 } 160 } 161 162 static void 163 acpi_wakedev_method(struct acpi_devnode *ad, int enable, int state) 164 { 165 ACPI_OBJECT_LIST arg; 166 ACPI_OBJECT obj[3]; 167 ACPI_STATUS rv; 168 169 /* 170 * First try to call the Device Sleep Wake control method, _DSW. 171 * Only if this is not available, resort to to the Power State 172 * Wake control method, _PSW, which was deprecated in ACPI 3.0. 173 * 174 * The arguments to these methods are as follows: 175 * 176 * arg0 arg1 arg2 177 * ---- ---- ---- 178 * _PSW 0: disable 179 * 1: enable 180 * 181 * _DSW 0: disable 0: S0 0: D0 182 * 1: enable 1: S1 1: D0 or D1 183 * 2: D0, D1, or D2 184 * x: Sx 3: D0, D1, D2 or D3 185 */ 186 arg.Count = 3; 187 arg.Pointer = obj; 188 189 obj[0].Integer.Value = enable; 190 obj[1].Integer.Value = state; 191 obj[2].Integer.Value = ACPI_STATE_D0; 192 193 obj[0].Type = obj[1].Type = obj[2].Type = ACPI_TYPE_INTEGER; 194 195 rv = AcpiEvaluateObject(ad->ad_handle, "_DSW", &arg, NULL); 196 197 if (ACPI_SUCCESS(rv)) 198 return; 199 200 if (rv != AE_NOT_FOUND) 201 goto fail; 202 203 rv = acpi_eval_set_integer(ad->ad_handle, "_PSW", enable); 204 205 if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) 206 goto fail; 207 208 return; 209 210 fail: 211 aprint_error_dev(ad->ad_root, "failed to evaluate wake " 212 "control method: %s\n", AcpiFormatException(rv)); 213 } 214 215 static void 216 acpi_wakedev_gpe(struct acpi_devnode *ad, int enable, int state) 217 { 218 ACPI_OBJECT *elm, *obj; 219 ACPI_HANDLE hdl = NULL; 220 ACPI_INTEGER val; 221 ACPI_BUFFER buf; 222 ACPI_STATUS rv; 223 224 rv = acpi_eval_struct(ad->ad_handle, "_PRW", &buf); 225 226 if (ACPI_FAILURE(rv)) 227 return; 228 229 obj = buf.Pointer; 230 231 if (obj->Type != ACPI_TYPE_PACKAGE || obj->Package.Count < 2) 232 goto out; 233 234 /* 235 * As noted in ACPI 3.0 (section 7.2.10), the _PRW object is 236 * a package in which the first element is either an integer 237 * or again a package. In the latter case the package inside 238 * the package element has two elements, a reference handle 239 * and the GPE number. 240 */ 241 elm = &obj->Package.Elements[0]; 242 243 switch (elm->Type) { 244 245 case ACPI_TYPE_INTEGER: 246 val = elm->Integer.Value; 247 break; 248 249 case ACPI_TYPE_PACKAGE: 250 251 if (elm->Package.Count < 2) 252 goto out; 253 254 if (elm->Package.Elements[0].Type != ACPI_TYPE_LOCAL_REFERENCE) 255 goto out; 256 257 if (elm->Package.Elements[1].Type != ACPI_TYPE_INTEGER) 258 goto out; 259 260 hdl = elm->Package.Elements[0].Reference.Handle; 261 val = elm->Package.Elements[1].Integer.Value; 262 break; 263 264 default: 265 goto out; 266 } 267 268 /* 269 * The second element is an integer that contains the 270 * lowest sleep state that can be entered while still 271 * providing wake-up functionality. The rest of the 272 * elements are references to power resources. 273 */ 274 elm = &obj->Package.Elements[1]; 275 276 if (elm->Type != ACPI_TYPE_INTEGER) 277 goto out; 278 279 if (state > elm->Integer.Value) 280 aprint_error_dev(ad->ad_root, "sleep state S%d " 281 "loses wake for %s\n", state, ad->ad_name); 282 283 /* 284 * Turn on power resources. 285 */ 286 if (enable != 0) 287 acpi_wakedev_power(ad, obj); 288 289 /* 290 * Set both runtime and wake GPEs, but unset only wake GPEs. 291 */ 292 if (enable != 0) 293 (void)AcpiEnableGpe(hdl, val, ACPI_GPE_TYPE_WAKE_RUN); 294 else 295 (void)AcpiDisableGpe(hdl, val, ACPI_GPE_TYPE_WAKE); 296 297 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "wake GPE %s for %s\n", 298 (enable != 0) ? "enabled" : "disabled", ad->ad_name)); 299 300 out: 301 ACPI_FREE(buf.Pointer); 302 } 303 304 static void 305 acpi_wakedev_power(struct acpi_devnode *ad, ACPI_OBJECT *obj) 306 { 307 ACPI_OBJECT *elm; 308 ACPI_HANDLE hdl; 309 ACPI_STATUS rv; 310 uint32_t i, n; 311 312 n = obj->Package.Count; 313 314 if (n < 3) 315 return; 316 317 for (i = 2; i < n; i++) { 318 319 elm = &obj->Package.Elements[i]; 320 rv = acpi_eval_reference_handle(elm, &hdl); 321 322 if (ACPI_FAILURE(rv)) 323 continue; 324 325 (void)acpi_power_res(hdl, ad->ad_handle, true); 326 } 327 } 328