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