1 1.30 jmcneill /* $NetBSD: acpi_wakedev.c,v 1.30 2024/12/09 23:41:36 jmcneill Exp $ */ 2 1.1 jmcneill 3 1.1 jmcneill /*- 4 1.22 jruoho * Copyright (c) 2009, 2010, 2011 Jared D. McNeill <jmcneill (at) invisible.ca> 5 1.1 jmcneill * All rights reserved. 6 1.1 jmcneill * 7 1.1 jmcneill * Redistribution and use in source and binary forms, with or without 8 1.1 jmcneill * modification, are permitted provided that the following conditions 9 1.1 jmcneill * are met: 10 1.1 jmcneill * 1. Redistributions of source code must retain the above copyright 11 1.1 jmcneill * notice, this list of conditions and the following disclaimer. 12 1.1 jmcneill * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 jmcneill * notice, this list of conditions and the following disclaimer in the 14 1.1 jmcneill * documentation and/or other materials provided with the distribution. 15 1.1 jmcneill * 16 1.1 jmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 1.1 jmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 1.1 jmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 1.1 jmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 1.1 jmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 1.1 jmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 1.1 jmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 1.1 jmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 1.1 jmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 1.1 jmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 1.1 jmcneill * POSSIBILITY OF SUCH DAMAGE. 27 1.1 jmcneill */ 28 1.1 jmcneill 29 1.1 jmcneill #include <sys/cdefs.h> 30 1.30 jmcneill __KERNEL_RCSID(0, "$NetBSD: acpi_wakedev.c,v 1.30 2024/12/09 23:41:36 jmcneill Exp $"); 31 1.29 mrg 32 1.29 mrg #include "pci.h" 33 1.1 jmcneill 34 1.1 jmcneill #include <sys/param.h> 35 1.3 jruoho #include <sys/device.h> 36 1.23 jruoho #include <sys/kmem.h> 37 1.1 jmcneill #include <sys/sysctl.h> 38 1.1 jmcneill 39 1.3 jruoho #include <dev/acpi/acpireg.h> 40 1.1 jmcneill #include <dev/acpi/acpivar.h> 41 1.19 jruoho #include <dev/acpi/acpi_pci.h> 42 1.13 jruoho #include <dev/acpi/acpi_power.h> 43 1.1 jmcneill #include <dev/acpi/acpi_wakedev.h> 44 1.1 jmcneill 45 1.13 jruoho #define _COMPONENT ACPI_BUS_COMPONENT 46 1.13 jruoho ACPI_MODULE_NAME ("acpi_wakedev") 47 1.5 jruoho 48 1.1 jmcneill static const char * const acpi_wakedev_default[] = { 49 1.1 jmcneill "PNP0C0C", /* power button */ 50 1.1 jmcneill "PNP0C0E", /* sleep button */ 51 1.1 jmcneill "PNP0C0D", /* lid switch */ 52 1.1 jmcneill "PNP03??", /* PC KBD port */ 53 1.1 jmcneill NULL, 54 1.1 jmcneill }; 55 1.1 jmcneill 56 1.23 jruoho static int32_t acpi_wakedev_acpinode = CTL_EOL; 57 1.23 jruoho static int32_t acpi_wakedev_wakenode = CTL_EOL; 58 1.9 jruoho 59 1.24 jruoho static void acpi_wakedev_power_add(struct acpi_devnode *, ACPI_OBJECT *); 60 1.24 jruoho static void acpi_wakedev_power_set(struct acpi_devnode *, bool); 61 1.22 jruoho static void acpi_wakedev_method(struct acpi_devnode *, int); 62 1.5 jruoho 63 1.22 jruoho void 64 1.22 jruoho acpi_wakedev_init(struct acpi_devnode *ad) 65 1.1 jmcneill { 66 1.22 jruoho ACPI_OBJECT *elm, *obj; 67 1.23 jruoho ACPI_INTEGER val; 68 1.23 jruoho ACPI_HANDLE hdl; 69 1.22 jruoho ACPI_BUFFER buf; 70 1.22 jruoho ACPI_STATUS rv; 71 1.22 jruoho 72 1.23 jruoho KASSERT(ad != NULL && ad->ad_wakedev == NULL); 73 1.23 jruoho KASSERT(ad->ad_devinfo->Type == ACPI_TYPE_DEVICE); 74 1.23 jruoho 75 1.22 jruoho rv = acpi_eval_struct(ad->ad_handle, "_PRW", &buf); 76 1.22 jruoho 77 1.22 jruoho if (ACPI_FAILURE(rv)) 78 1.22 jruoho goto out; 79 1.22 jruoho 80 1.22 jruoho obj = buf.Pointer; 81 1.22 jruoho 82 1.22 jruoho if (obj->Type != ACPI_TYPE_PACKAGE) { 83 1.22 jruoho rv = AE_TYPE; 84 1.22 jruoho goto out; 85 1.22 jruoho } 86 1.22 jruoho 87 1.23 jruoho if (obj->Package.Count < 2 || obj->Package.Count > UINT32_MAX) { 88 1.22 jruoho rv = AE_LIMIT; 89 1.22 jruoho goto out; 90 1.22 jruoho } 91 1.22 jruoho 92 1.22 jruoho /* 93 1.22 jruoho * As noted in ACPI 3.0 (section 7.2.10), the _PRW object is 94 1.22 jruoho * a package in which the first element is either an integer 95 1.22 jruoho * or again a package. In the latter case the package inside 96 1.22 jruoho * the package element has two elements, a reference handle 97 1.22 jruoho * and the GPE number. 98 1.22 jruoho */ 99 1.22 jruoho elm = &obj->Package.Elements[0]; 100 1.22 jruoho 101 1.22 jruoho switch (elm->Type) { 102 1.22 jruoho 103 1.22 jruoho case ACPI_TYPE_INTEGER: 104 1.22 jruoho val = elm->Integer.Value; 105 1.23 jruoho hdl = NULL; 106 1.22 jruoho break; 107 1.22 jruoho 108 1.22 jruoho case ACPI_TYPE_PACKAGE: 109 1.22 jruoho 110 1.22 jruoho if (elm->Package.Count < 2) { 111 1.22 jruoho rv = AE_LIMIT; 112 1.22 jruoho goto out; 113 1.22 jruoho } 114 1.22 jruoho 115 1.22 jruoho rv = AE_TYPE; 116 1.22 jruoho 117 1.22 jruoho if (elm->Package.Elements[0].Type != ACPI_TYPE_LOCAL_REFERENCE) 118 1.22 jruoho goto out; 119 1.22 jruoho 120 1.22 jruoho if (elm->Package.Elements[1].Type != ACPI_TYPE_INTEGER) 121 1.22 jruoho goto out; 122 1.22 jruoho 123 1.22 jruoho hdl = elm->Package.Elements[0].Reference.Handle; 124 1.22 jruoho val = elm->Package.Elements[1].Integer.Value; 125 1.22 jruoho break; 126 1.22 jruoho 127 1.22 jruoho default: 128 1.22 jruoho rv = AE_TYPE; 129 1.22 jruoho goto out; 130 1.22 jruoho } 131 1.1 jmcneill 132 1.25 jruoho ad->ad_wakedev = kmem_zalloc(sizeof(*ad->ad_wakedev), KM_SLEEP); 133 1.22 jruoho ad->ad_wakedev->aw_handle = hdl; 134 1.22 jruoho ad->ad_wakedev->aw_number = val; 135 1.22 jruoho 136 1.22 jruoho /* 137 1.22 jruoho * The second element in _PRW is an integer 138 1.22 jruoho * that contains the lowest sleep state that 139 1.22 jruoho * can be entered while still providing wakeup. 140 1.22 jruoho */ 141 1.22 jruoho elm = &obj->Package.Elements[1]; 142 1.22 jruoho 143 1.22 jruoho if (elm->Type == ACPI_TYPE_INTEGER) 144 1.24 jruoho ad->ad_wakedev->aw_state = elm->Integer.Value; 145 1.22 jruoho 146 1.22 jruoho /* 147 1.23 jruoho * The rest of the elements are reference 148 1.23 jruoho * handles to power resources. Store these. 149 1.22 jruoho */ 150 1.24 jruoho acpi_wakedev_power_add(ad, obj); 151 1.22 jruoho 152 1.22 jruoho /* 153 1.23 jruoho * Last but not least, mark the GPE for wake. 154 1.22 jruoho */ 155 1.30 jmcneill if (!AcpiGbl_ReducedHardware) { 156 1.30 jmcneill rv = AcpiSetupGpeForWake(ad->ad_handle, hdl, val); 157 1.30 jmcneill } else { 158 1.30 jmcneill rv = AE_OK; 159 1.30 jmcneill } 160 1.22 jruoho 161 1.22 jruoho out: 162 1.22 jruoho if (buf.Pointer != NULL) 163 1.22 jruoho ACPI_FREE(buf.Pointer); 164 1.22 jruoho 165 1.22 jruoho if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) 166 1.22 jruoho aprint_error_dev(ad->ad_root, "failed to evaluate _PRW " 167 1.22 jruoho "for %s: %s\n", ad->ad_name, AcpiFormatException(rv)); 168 1.22 jruoho } 169 1.22 jruoho 170 1.22 jruoho static void 171 1.24 jruoho acpi_wakedev_power_add(struct acpi_devnode *ad, ACPI_OBJECT *obj) 172 1.22 jruoho { 173 1.22 jruoho struct acpi_wakedev *aw = ad->ad_wakedev; 174 1.22 jruoho uint32_t i, j, n; 175 1.22 jruoho ACPI_OBJECT *elm; 176 1.22 jruoho ACPI_HANDLE hdl; 177 1.22 jruoho ACPI_STATUS rv; 178 1.22 jruoho 179 1.22 jruoho for (i = 0; i < __arraycount(aw->aw_power); i++) 180 1.22 jruoho aw->aw_power[i] = NULL; 181 1.22 jruoho 182 1.22 jruoho n = obj->Package.Count; 183 1.9 jruoho 184 1.22 jruoho if (n < 3 || n - 2 > __arraycount(aw->aw_power)) 185 1.18 gsutre return; 186 1.18 gsutre 187 1.22 jruoho for (i = 2, j = 0; i < n; i++, j++) { 188 1.9 jruoho 189 1.22 jruoho elm = &obj->Package.Elements[i]; 190 1.22 jruoho rv = acpi_eval_reference_handle(elm, &hdl); 191 1.9 jruoho 192 1.22 jruoho if (ACPI_FAILURE(rv)) 193 1.22 jruoho continue; 194 1.9 jruoho 195 1.22 jruoho ad->ad_wakedev->aw_power[j] = hdl; 196 1.22 jruoho } 197 1.1 jmcneill } 198 1.1 jmcneill 199 1.24 jruoho static void 200 1.24 jruoho acpi_wakedev_power_set(struct acpi_devnode *ad, bool enable) 201 1.24 jruoho { 202 1.24 jruoho struct acpi_wakedev *aw = ad->ad_wakedev; 203 1.24 jruoho uint8_t i; 204 1.24 jruoho 205 1.24 jruoho for (i = 0; i < __arraycount(aw->aw_power); i++) { 206 1.24 jruoho 207 1.24 jruoho if (aw->aw_power[i] == NULL) 208 1.24 jruoho continue; 209 1.24 jruoho 210 1.24 jruoho (void)acpi_power_res(aw->aw_power[i], ad->ad_handle, enable); 211 1.24 jruoho } 212 1.24 jruoho } 213 1.24 jruoho 214 1.7 jruoho void 215 1.7 jruoho acpi_wakedev_add(struct acpi_devnode *ad) 216 1.1 jmcneill { 217 1.22 jruoho struct acpi_wakedev *aw; 218 1.19 jruoho const char *str = NULL; 219 1.1 jmcneill int err; 220 1.1 jmcneill 221 1.22 jruoho KASSERT(ad != NULL && ad->ad_wakedev != NULL); 222 1.7 jruoho KASSERT((ad->ad_flags & ACPI_DEVICE_WAKEUP) != 0); 223 1.1 jmcneill 224 1.22 jruoho aw = ad->ad_wakedev; 225 1.22 jruoho aw->aw_enable = false; 226 1.1 jmcneill 227 1.1 jmcneill if (acpi_match_hid(ad->ad_devinfo, acpi_wakedev_default)) 228 1.23 jruoho aw->aw_enable = true; 229 1.1 jmcneill 230 1.18 gsutre if (acpi_wakedev_acpinode == CTL_EOL || 231 1.18 gsutre acpi_wakedev_wakenode == CTL_EOL) 232 1.7 jruoho return; 233 1.1 jmcneill 234 1.19 jruoho if (ad->ad_device != NULL) 235 1.19 jruoho str = device_xname(ad->ad_device); 236 1.29 mrg #if NPCI > 0 237 1.19 jruoho else { 238 1.29 mrg device_t dev = acpi_pcidev_find_dev(ad); 239 1.19 jruoho 240 1.19 jruoho if (dev != NULL) 241 1.19 jruoho str = device_xname(dev); 242 1.19 jruoho } 243 1.29 mrg #endif 244 1.19 jruoho 245 1.19 jruoho if (str == NULL) 246 1.19 jruoho return; 247 1.19 jruoho 248 1.18 gsutre err = sysctl_createv(NULL, 0, NULL, NULL, 249 1.19 jruoho CTLFLAG_READWRITE, CTLTYPE_BOOL, str, 250 1.23 jruoho NULL, NULL, 0, &aw->aw_enable, 0, CTL_HW, 251 1.19 jruoho acpi_wakedev_acpinode, acpi_wakedev_wakenode, 252 1.9 jruoho CTL_CREATE, CTL_EOL); 253 1.6 jruoho 254 1.7 jruoho if (err != 0) 255 1.12 jruoho aprint_error_dev(ad->ad_root, "sysctl_createv" 256 1.20 jruoho "(hw.acpi.wake.%s) failed (err %d)\n", str, err); 257 1.1 jmcneill } 258 1.1 jmcneill 259 1.22 jruoho SYSCTL_SETUP(sysctl_acpi_wakedev_setup, "sysctl hw.acpi.wake subtree setup") 260 1.22 jruoho { 261 1.22 jruoho const struct sysctlnode *rnode; 262 1.22 jruoho int err; 263 1.22 jruoho 264 1.22 jruoho err = sysctl_createv(NULL, 0, NULL, &rnode, 265 1.22 jruoho CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi", 266 1.22 jruoho NULL, NULL, 0, NULL, 0, 267 1.26 pooka CTL_HW, CTL_CREATE, CTL_EOL); 268 1.22 jruoho 269 1.22 jruoho if (err != 0) 270 1.22 jruoho return; 271 1.22 jruoho 272 1.22 jruoho acpi_wakedev_acpinode = rnode->sysctl_num; 273 1.22 jruoho 274 1.22 jruoho err = sysctl_createv(NULL, 0, &rnode, &rnode, 275 1.22 jruoho CTLFLAG_PERMANENT, CTLTYPE_NODE, 276 1.22 jruoho "wake", SYSCTL_DESCR("ACPI device wake-up"), 277 1.22 jruoho NULL, 0, NULL, 0, 278 1.22 jruoho CTL_CREATE, CTL_EOL); 279 1.22 jruoho 280 1.22 jruoho if (err != 0) 281 1.22 jruoho return; 282 1.22 jruoho 283 1.22 jruoho acpi_wakedev_wakenode = rnode->sysctl_num; 284 1.22 jruoho } 285 1.22 jruoho 286 1.1 jmcneill void 287 1.4 jruoho acpi_wakedev_commit(struct acpi_softc *sc, int state) 288 1.1 jmcneill { 289 1.7 jruoho struct acpi_devnode *ad; 290 1.24 jruoho ACPI_INTEGER val; 291 1.24 jruoho ACPI_HANDLE hdl; 292 1.1 jmcneill 293 1.4 jruoho /* 294 1.13 jruoho * To prepare a device for wakeup: 295 1.4 jruoho * 296 1.24 jruoho * 1. Set the wake GPE. 297 1.4 jruoho * 298 1.24 jruoho * 2. Turn on power resources. 299 1.4 jruoho * 300 1.24 jruoho * 3. Execute _DSW or _PSW method. 301 1.4 jruoho */ 302 1.28 skrll SIMPLEQ_FOREACH(ad, &sc->sc_head, ad_list) { 303 1.7 jruoho 304 1.22 jruoho if (ad->ad_wakedev == NULL) 305 1.7 jruoho continue; 306 1.4 jruoho 307 1.24 jruoho if (state > ad->ad_wakedev->aw_state) 308 1.24 jruoho continue; 309 1.22 jruoho 310 1.24 jruoho hdl = ad->ad_wakedev->aw_handle; 311 1.24 jruoho val = ad->ad_wakedev->aw_number; 312 1.22 jruoho 313 1.24 jruoho if (state == ACPI_STATE_S0) { 314 1.30 jmcneill if (!AcpiGbl_ReducedHardware) { 315 1.30 jmcneill AcpiSetGpeWakeMask(hdl, val, ACPI_GPE_DISABLE); 316 1.30 jmcneill } 317 1.24 jruoho continue; 318 1.24 jruoho } 319 1.22 jruoho 320 1.30 jmcneill if (!AcpiGbl_ReducedHardware) { 321 1.30 jmcneill AcpiSetGpeWakeMask(hdl, val, ACPI_GPE_ENABLE); 322 1.30 jmcneill } 323 1.22 jruoho 324 1.24 jruoho acpi_wakedev_power_set(ad, true); 325 1.24 jruoho acpi_wakedev_method(ad, state); 326 1.22 jruoho } 327 1.22 jruoho } 328 1.22 jruoho 329 1.22 jruoho static void 330 1.22 jruoho acpi_wakedev_method(struct acpi_devnode *ad, int state) 331 1.22 jruoho { 332 1.22 jruoho const bool enable = ad->ad_wakedev->aw_enable; 333 1.4 jruoho ACPI_OBJECT_LIST arg; 334 1.4 jruoho ACPI_OBJECT obj[3]; 335 1.4 jruoho ACPI_STATUS rv; 336 1.4 jruoho 337 1.4 jruoho /* 338 1.4 jruoho * First try to call the Device Sleep Wake control method, _DSW. 339 1.4 jruoho * Only if this is not available, resort to to the Power State 340 1.4 jruoho * Wake control method, _PSW, which was deprecated in ACPI 3.0. 341 1.4 jruoho * 342 1.4 jruoho * The arguments to these methods are as follows: 343 1.4 jruoho * 344 1.4 jruoho * arg0 arg1 arg2 345 1.4 jruoho * ---- ---- ---- 346 1.4 jruoho * _PSW 0: disable 347 1.4 jruoho * 1: enable 348 1.4 jruoho * 349 1.4 jruoho * _DSW 0: disable 0: S0 0: D0 350 1.4 jruoho * 1: enable 1: S1 1: D0 or D1 351 1.4 jruoho * 2: D0, D1, or D2 352 1.4 jruoho * x: Sx 3: D0, D1, D2 or D3 353 1.4 jruoho */ 354 1.4 jruoho arg.Count = 3; 355 1.4 jruoho arg.Pointer = obj; 356 1.4 jruoho 357 1.4 jruoho obj[0].Integer.Value = enable; 358 1.4 jruoho obj[1].Integer.Value = state; 359 1.14 jruoho obj[2].Integer.Value = ACPI_STATE_D0; 360 1.4 jruoho 361 1.4 jruoho obj[0].Type = obj[1].Type = obj[2].Type = ACPI_TYPE_INTEGER; 362 1.4 jruoho 363 1.4 jruoho rv = AcpiEvaluateObject(ad->ad_handle, "_DSW", &arg, NULL); 364 1.4 jruoho 365 1.4 jruoho if (ACPI_SUCCESS(rv)) 366 1.4 jruoho return; 367 1.4 jruoho 368 1.4 jruoho if (rv != AE_NOT_FOUND) 369 1.4 jruoho goto fail; 370 1.4 jruoho 371 1.4 jruoho rv = acpi_eval_set_integer(ad->ad_handle, "_PSW", enable); 372 1.4 jruoho 373 1.4 jruoho if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) 374 1.4 jruoho goto fail; 375 1.4 jruoho 376 1.4 jruoho return; 377 1.4 jruoho 378 1.4 jruoho fail: 379 1.12 jruoho aprint_error_dev(ad->ad_root, "failed to evaluate wake " 380 1.4 jruoho "control method: %s\n", AcpiFormatException(rv)); 381 1.4 jruoho } 382