1 /* $NetBSD: acpi_wakedev.c,v 1.27 2017/06/01 02:45:09 chs 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.27 2017/06/01 02:45:09 chs 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 ad->ad_wakedev->aw_handle = hdl; 132 ad->ad_wakedev->aw_number = val; 133 134 /* 135 * The second element in _PRW is an integer 136 * that contains the lowest sleep state that 137 * can be entered while still providing wakeup. 138 */ 139 elm = &obj->Package.Elements[1]; 140 141 if (elm->Type == ACPI_TYPE_INTEGER) 142 ad->ad_wakedev->aw_state = elm->Integer.Value; 143 144 /* 145 * The rest of the elements are reference 146 * handles to power resources. Store these. 147 */ 148 acpi_wakedev_power_add(ad, obj); 149 150 /* 151 * Last but not least, mark the GPE for wake. 152 */ 153 rv = AcpiSetupGpeForWake(ad->ad_handle, hdl, val); 154 155 out: 156 if (buf.Pointer != NULL) 157 ACPI_FREE(buf.Pointer); 158 159 if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) 160 aprint_error_dev(ad->ad_root, "failed to evaluate _PRW " 161 "for %s: %s\n", ad->ad_name, AcpiFormatException(rv)); 162 } 163 164 static void 165 acpi_wakedev_power_add(struct acpi_devnode *ad, ACPI_OBJECT *obj) 166 { 167 struct acpi_wakedev *aw = ad->ad_wakedev; 168 uint32_t i, j, n; 169 ACPI_OBJECT *elm; 170 ACPI_HANDLE hdl; 171 ACPI_STATUS rv; 172 173 for (i = 0; i < __arraycount(aw->aw_power); i++) 174 aw->aw_power[i] = NULL; 175 176 n = obj->Package.Count; 177 178 if (n < 3 || n - 2 > __arraycount(aw->aw_power)) 179 return; 180 181 for (i = 2, j = 0; i < n; i++, j++) { 182 183 elm = &obj->Package.Elements[i]; 184 rv = acpi_eval_reference_handle(elm, &hdl); 185 186 if (ACPI_FAILURE(rv)) 187 continue; 188 189 ad->ad_wakedev->aw_power[j] = hdl; 190 } 191 } 192 193 static void 194 acpi_wakedev_power_set(struct acpi_devnode *ad, bool enable) 195 { 196 struct acpi_wakedev *aw = ad->ad_wakedev; 197 uint8_t i; 198 199 for (i = 0; i < __arraycount(aw->aw_power); i++) { 200 201 if (aw->aw_power[i] == NULL) 202 continue; 203 204 (void)acpi_power_res(aw->aw_power[i], ad->ad_handle, enable); 205 } 206 } 207 208 void 209 acpi_wakedev_add(struct acpi_devnode *ad) 210 { 211 struct acpi_wakedev *aw; 212 const char *str = NULL; 213 device_t dev; 214 int err; 215 216 KASSERT(ad != NULL && ad->ad_wakedev != NULL); 217 KASSERT((ad->ad_flags & ACPI_DEVICE_WAKEUP) != 0); 218 219 aw = ad->ad_wakedev; 220 aw->aw_enable = false; 221 222 if (acpi_match_hid(ad->ad_devinfo, acpi_wakedev_default)) 223 aw->aw_enable = true; 224 225 if (acpi_wakedev_acpinode == CTL_EOL || 226 acpi_wakedev_wakenode == CTL_EOL) 227 return; 228 229 if (ad->ad_device != NULL) 230 str = device_xname(ad->ad_device); 231 else { 232 dev = acpi_pcidev_find_dev(ad); 233 234 if (dev != NULL) 235 str = device_xname(dev); 236 } 237 238 if (str == NULL) 239 return; 240 241 err = sysctl_createv(NULL, 0, NULL, NULL, 242 CTLFLAG_READWRITE, CTLTYPE_BOOL, str, 243 NULL, NULL, 0, &aw->aw_enable, 0, CTL_HW, 244 acpi_wakedev_acpinode, acpi_wakedev_wakenode, 245 CTL_CREATE, CTL_EOL); 246 247 if (err != 0) 248 aprint_error_dev(ad->ad_root, "sysctl_createv" 249 "(hw.acpi.wake.%s) failed (err %d)\n", str, err); 250 } 251 252 SYSCTL_SETUP(sysctl_acpi_wakedev_setup, "sysctl hw.acpi.wake subtree setup") 253 { 254 const struct sysctlnode *rnode; 255 int err; 256 257 err = sysctl_createv(NULL, 0, NULL, &rnode, 258 CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi", 259 NULL, NULL, 0, NULL, 0, 260 CTL_HW, CTL_CREATE, CTL_EOL); 261 262 if (err != 0) 263 return; 264 265 acpi_wakedev_acpinode = rnode->sysctl_num; 266 267 err = sysctl_createv(NULL, 0, &rnode, &rnode, 268 CTLFLAG_PERMANENT, CTLTYPE_NODE, 269 "wake", SYSCTL_DESCR("ACPI device wake-up"), 270 NULL, 0, NULL, 0, 271 CTL_CREATE, CTL_EOL); 272 273 if (err != 0) 274 return; 275 276 acpi_wakedev_wakenode = rnode->sysctl_num; 277 } 278 279 void 280 acpi_wakedev_commit(struct acpi_softc *sc, int state) 281 { 282 struct acpi_devnode *ad; 283 ACPI_INTEGER val; 284 ACPI_HANDLE hdl; 285 286 /* 287 * To prepare a device for wakeup: 288 * 289 * 1. Set the wake GPE. 290 * 291 * 2. Turn on power resources. 292 * 293 * 3. Execute _DSW or _PSW method. 294 */ 295 SIMPLEQ_FOREACH(ad, &sc->ad_head, ad_list) { 296 297 if (ad->ad_wakedev == NULL) 298 continue; 299 300 if (state > ad->ad_wakedev->aw_state) 301 continue; 302 303 hdl = ad->ad_wakedev->aw_handle; 304 val = ad->ad_wakedev->aw_number; 305 306 if (state == ACPI_STATE_S0) { 307 (void)AcpiSetGpeWakeMask(hdl, val, ACPI_GPE_DISABLE); 308 continue; 309 } 310 311 (void)AcpiSetGpeWakeMask(hdl, val, ACPI_GPE_ENABLE); 312 313 acpi_wakedev_power_set(ad, true); 314 acpi_wakedev_method(ad, state); 315 } 316 } 317 318 static void 319 acpi_wakedev_method(struct acpi_devnode *ad, int state) 320 { 321 const bool enable = ad->ad_wakedev->aw_enable; 322 ACPI_OBJECT_LIST arg; 323 ACPI_OBJECT obj[3]; 324 ACPI_STATUS rv; 325 326 /* 327 * First try to call the Device Sleep Wake control method, _DSW. 328 * Only if this is not available, resort to to the Power State 329 * Wake control method, _PSW, which was deprecated in ACPI 3.0. 330 * 331 * The arguments to these methods are as follows: 332 * 333 * arg0 arg1 arg2 334 * ---- ---- ---- 335 * _PSW 0: disable 336 * 1: enable 337 * 338 * _DSW 0: disable 0: S0 0: D0 339 * 1: enable 1: S1 1: D0 or D1 340 * 2: D0, D1, or D2 341 * x: Sx 3: D0, D1, D2 or D3 342 */ 343 arg.Count = 3; 344 arg.Pointer = obj; 345 346 obj[0].Integer.Value = enable; 347 obj[1].Integer.Value = state; 348 obj[2].Integer.Value = ACPI_STATE_D0; 349 350 obj[0].Type = obj[1].Type = obj[2].Type = ACPI_TYPE_INTEGER; 351 352 rv = AcpiEvaluateObject(ad->ad_handle, "_DSW", &arg, NULL); 353 354 if (ACPI_SUCCESS(rv)) 355 return; 356 357 if (rv != AE_NOT_FOUND) 358 goto fail; 359 360 rv = acpi_eval_set_integer(ad->ad_handle, "_PSW", enable); 361 362 if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) 363 goto fail; 364 365 return; 366 367 fail: 368 aprint_error_dev(ad->ad_root, "failed to evaluate wake " 369 "control method: %s\n", AcpiFormatException(rv)); 370 } 371