1 /* $NetBSD: acpi_wakedev.c,v 1.25 2012/08/14 14:38:02 jruoho Exp $ */ 2 3 /*- 4 * Copyright (c) 2009, 2010, 2011 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.25 2012/08/14 14:38:02 jruoho Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/device.h> 34 #include <sys/kmem.h> 35 #include <sys/sysctl.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_power_add(struct acpi_devnode *, ACPI_OBJECT *); 58 static void acpi_wakedev_power_set(struct acpi_devnode *, bool); 59 static void acpi_wakedev_method(struct acpi_devnode *, int); 60 61 void 62 acpi_wakedev_init(struct acpi_devnode *ad) 63 { 64 ACPI_OBJECT *elm, *obj; 65 ACPI_INTEGER val; 66 ACPI_HANDLE hdl; 67 ACPI_BUFFER buf; 68 ACPI_STATUS rv; 69 70 KASSERT(ad != NULL && ad->ad_wakedev == NULL); 71 KASSERT(ad->ad_devinfo->Type == ACPI_TYPE_DEVICE); 72 73 rv = acpi_eval_struct(ad->ad_handle, "_PRW", &buf); 74 75 if (ACPI_FAILURE(rv)) 76 goto out; 77 78 obj = buf.Pointer; 79 80 if (obj->Type != ACPI_TYPE_PACKAGE) { 81 rv = AE_TYPE; 82 goto out; 83 } 84 85 if (obj->Package.Count < 2 || obj->Package.Count > UINT32_MAX) { 86 rv = AE_LIMIT; 87 goto out; 88 } 89 90 /* 91 * As noted in ACPI 3.0 (section 7.2.10), the _PRW object is 92 * a package in which the first element is either an integer 93 * or again a package. In the latter case the package inside 94 * the package element has two elements, a reference handle 95 * and the GPE number. 96 */ 97 elm = &obj->Package.Elements[0]; 98 99 switch (elm->Type) { 100 101 case ACPI_TYPE_INTEGER: 102 val = elm->Integer.Value; 103 hdl = NULL; 104 break; 105 106 case ACPI_TYPE_PACKAGE: 107 108 if (elm->Package.Count < 2) { 109 rv = AE_LIMIT; 110 goto out; 111 } 112 113 rv = AE_TYPE; 114 115 if (elm->Package.Elements[0].Type != ACPI_TYPE_LOCAL_REFERENCE) 116 goto out; 117 118 if (elm->Package.Elements[1].Type != ACPI_TYPE_INTEGER) 119 goto out; 120 121 hdl = elm->Package.Elements[0].Reference.Handle; 122 val = elm->Package.Elements[1].Integer.Value; 123 break; 124 125 default: 126 rv = AE_TYPE; 127 goto out; 128 } 129 130 ad->ad_wakedev = kmem_zalloc(sizeof(*ad->ad_wakedev), KM_SLEEP); 131 132 if (ad->ad_wakedev == NULL) 133 return; 134 135 ad->ad_wakedev->aw_handle = hdl; 136 ad->ad_wakedev->aw_number = val; 137 138 /* 139 * The second element in _PRW is an integer 140 * that contains the lowest sleep state that 141 * can be entered while still providing wakeup. 142 */ 143 elm = &obj->Package.Elements[1]; 144 145 if (elm->Type == ACPI_TYPE_INTEGER) 146 ad->ad_wakedev->aw_state = elm->Integer.Value; 147 148 /* 149 * The rest of the elements are reference 150 * handles to power resources. Store these. 151 */ 152 acpi_wakedev_power_add(ad, obj); 153 154 /* 155 * Last but not least, mark the GPE for wake. 156 */ 157 rv = AcpiSetupGpeForWake(ad->ad_handle, hdl, val); 158 159 out: 160 if (buf.Pointer != NULL) 161 ACPI_FREE(buf.Pointer); 162 163 if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) 164 aprint_error_dev(ad->ad_root, "failed to evaluate _PRW " 165 "for %s: %s\n", ad->ad_name, AcpiFormatException(rv)); 166 } 167 168 static void 169 acpi_wakedev_power_add(struct acpi_devnode *ad, ACPI_OBJECT *obj) 170 { 171 struct acpi_wakedev *aw = ad->ad_wakedev; 172 uint32_t i, j, n; 173 ACPI_OBJECT *elm; 174 ACPI_HANDLE hdl; 175 ACPI_STATUS rv; 176 177 for (i = 0; i < __arraycount(aw->aw_power); i++) 178 aw->aw_power[i] = NULL; 179 180 n = obj->Package.Count; 181 182 if (n < 3 || n - 2 > __arraycount(aw->aw_power)) 183 return; 184 185 for (i = 2, j = 0; i < n; i++, j++) { 186 187 elm = &obj->Package.Elements[i]; 188 rv = acpi_eval_reference_handle(elm, &hdl); 189 190 if (ACPI_FAILURE(rv)) 191 continue; 192 193 ad->ad_wakedev->aw_power[j] = hdl; 194 } 195 } 196 197 static void 198 acpi_wakedev_power_set(struct acpi_devnode *ad, bool enable) 199 { 200 struct acpi_wakedev *aw = ad->ad_wakedev; 201 uint8_t i; 202 203 for (i = 0; i < __arraycount(aw->aw_power); i++) { 204 205 if (aw->aw_power[i] == NULL) 206 continue; 207 208 (void)acpi_power_res(aw->aw_power[i], ad->ad_handle, enable); 209 } 210 } 211 212 void 213 acpi_wakedev_add(struct acpi_devnode *ad) 214 { 215 struct acpi_wakedev *aw; 216 const char *str = NULL; 217 device_t dev; 218 int err; 219 220 KASSERT(ad != NULL && ad->ad_wakedev != NULL); 221 KASSERT((ad->ad_flags & ACPI_DEVICE_WAKEUP) != 0); 222 223 aw = ad->ad_wakedev; 224 aw->aw_enable = false; 225 226 if (acpi_match_hid(ad->ad_devinfo, acpi_wakedev_default)) 227 aw->aw_enable = true; 228 229 if (acpi_wakedev_acpinode == CTL_EOL || 230 acpi_wakedev_wakenode == CTL_EOL) 231 return; 232 233 if (ad->ad_device != NULL) 234 str = device_xname(ad->ad_device); 235 else { 236 dev = acpi_pcidev_find_dev(ad); 237 238 if (dev != NULL) 239 str = device_xname(dev); 240 } 241 242 if (str == NULL) 243 return; 244 245 err = sysctl_createv(NULL, 0, NULL, NULL, 246 CTLFLAG_READWRITE, CTLTYPE_BOOL, str, 247 NULL, NULL, 0, &aw->aw_enable, 0, CTL_HW, 248 acpi_wakedev_acpinode, acpi_wakedev_wakenode, 249 CTL_CREATE, CTL_EOL); 250 251 if (err != 0) 252 aprint_error_dev(ad->ad_root, "sysctl_createv" 253 "(hw.acpi.wake.%s) failed (err %d)\n", str, err); 254 } 255 256 SYSCTL_SETUP(sysctl_acpi_wakedev_setup, "sysctl hw.acpi.wake subtree setup") 257 { 258 const struct sysctlnode *rnode; 259 int err; 260 261 err = sysctl_createv(NULL, 0, NULL, &rnode, 262 CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", 263 NULL, NULL, 0, NULL, 0, 264 CTL_HW, CTL_EOL); 265 266 if (err != 0) 267 return; 268 269 err = sysctl_createv(NULL, 0, &rnode, &rnode, 270 CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi", 271 NULL, NULL, 0, NULL, 0, 272 CTL_CREATE, CTL_EOL); 273 274 if (err != 0) 275 return; 276 277 acpi_wakedev_acpinode = rnode->sysctl_num; 278 279 err = sysctl_createv(NULL, 0, &rnode, &rnode, 280 CTLFLAG_PERMANENT, CTLTYPE_NODE, 281 "wake", SYSCTL_DESCR("ACPI device wake-up"), 282 NULL, 0, NULL, 0, 283 CTL_CREATE, CTL_EOL); 284 285 if (err != 0) 286 return; 287 288 acpi_wakedev_wakenode = rnode->sysctl_num; 289 } 290 291 void 292 acpi_wakedev_commit(struct acpi_softc *sc, int state) 293 { 294 struct acpi_devnode *ad; 295 ACPI_INTEGER val; 296 ACPI_HANDLE hdl; 297 298 /* 299 * To prepare a device for wakeup: 300 * 301 * 1. Set the wake GPE. 302 * 303 * 2. Turn on power resources. 304 * 305 * 3. Execute _DSW or _PSW method. 306 */ 307 SIMPLEQ_FOREACH(ad, &sc->ad_head, ad_list) { 308 309 if (ad->ad_wakedev == NULL) 310 continue; 311 312 if (state > ad->ad_wakedev->aw_state) 313 continue; 314 315 hdl = ad->ad_wakedev->aw_handle; 316 val = ad->ad_wakedev->aw_number; 317 318 if (state == ACPI_STATE_S0) { 319 (void)AcpiSetGpeWakeMask(hdl, val, ACPI_GPE_DISABLE); 320 continue; 321 } 322 323 (void)AcpiSetGpeWakeMask(hdl, val, ACPI_GPE_ENABLE); 324 325 acpi_wakedev_power_set(ad, true); 326 acpi_wakedev_method(ad, state); 327 } 328 } 329 330 static void 331 acpi_wakedev_method(struct acpi_devnode *ad, int state) 332 { 333 const bool enable = ad->ad_wakedev->aw_enable; 334 ACPI_OBJECT_LIST arg; 335 ACPI_OBJECT obj[3]; 336 ACPI_STATUS rv; 337 338 /* 339 * First try to call the Device Sleep Wake control method, _DSW. 340 * Only if this is not available, resort to to the Power State 341 * Wake control method, _PSW, which was deprecated in ACPI 3.0. 342 * 343 * The arguments to these methods are as follows: 344 * 345 * arg0 arg1 arg2 346 * ---- ---- ---- 347 * _PSW 0: disable 348 * 1: enable 349 * 350 * _DSW 0: disable 0: S0 0: D0 351 * 1: enable 1: S1 1: D0 or D1 352 * 2: D0, D1, or D2 353 * x: Sx 3: D0, D1, D2 or D3 354 */ 355 arg.Count = 3; 356 arg.Pointer = obj; 357 358 obj[0].Integer.Value = enable; 359 obj[1].Integer.Value = state; 360 obj[2].Integer.Value = ACPI_STATE_D0; 361 362 obj[0].Type = obj[1].Type = obj[2].Type = ACPI_TYPE_INTEGER; 363 364 rv = AcpiEvaluateObject(ad->ad_handle, "_DSW", &arg, NULL); 365 366 if (ACPI_SUCCESS(rv)) 367 return; 368 369 if (rv != AE_NOT_FOUND) 370 goto fail; 371 372 rv = acpi_eval_set_integer(ad->ad_handle, "_PSW", enable); 373 374 if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) 375 goto fail; 376 377 return; 378 379 fail: 380 aprint_error_dev(ad->ad_root, "failed to evaluate wake " 381 "control method: %s\n", AcpiFormatException(rv)); 382 } 383