1 /* $NetBSD: acpi_wakedev.c,v 1.26 2014/02/25 18:30:09 pooka 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.26 2014/02/25 18:30:09 pooka 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, "acpi", 263 NULL, NULL, 0, NULL, 0, 264 CTL_HW, CTL_CREATE, CTL_EOL); 265 266 if (err != 0) 267 return; 268 269 acpi_wakedev_acpinode = rnode->sysctl_num; 270 271 err = sysctl_createv(NULL, 0, &rnode, &rnode, 272 CTLFLAG_PERMANENT, CTLTYPE_NODE, 273 "wake", SYSCTL_DESCR("ACPI device wake-up"), 274 NULL, 0, NULL, 0, 275 CTL_CREATE, CTL_EOL); 276 277 if (err != 0) 278 return; 279 280 acpi_wakedev_wakenode = rnode->sysctl_num; 281 } 282 283 void 284 acpi_wakedev_commit(struct acpi_softc *sc, int state) 285 { 286 struct acpi_devnode *ad; 287 ACPI_INTEGER val; 288 ACPI_HANDLE hdl; 289 290 /* 291 * To prepare a device for wakeup: 292 * 293 * 1. Set the wake GPE. 294 * 295 * 2. Turn on power resources. 296 * 297 * 3. Execute _DSW or _PSW method. 298 */ 299 SIMPLEQ_FOREACH(ad, &sc->ad_head, ad_list) { 300 301 if (ad->ad_wakedev == NULL) 302 continue; 303 304 if (state > ad->ad_wakedev->aw_state) 305 continue; 306 307 hdl = ad->ad_wakedev->aw_handle; 308 val = ad->ad_wakedev->aw_number; 309 310 if (state == ACPI_STATE_S0) { 311 (void)AcpiSetGpeWakeMask(hdl, val, ACPI_GPE_DISABLE); 312 continue; 313 } 314 315 (void)AcpiSetGpeWakeMask(hdl, val, ACPI_GPE_ENABLE); 316 317 acpi_wakedev_power_set(ad, true); 318 acpi_wakedev_method(ad, state); 319 } 320 } 321 322 static void 323 acpi_wakedev_method(struct acpi_devnode *ad, int state) 324 { 325 const bool enable = ad->ad_wakedev->aw_enable; 326 ACPI_OBJECT_LIST arg; 327 ACPI_OBJECT obj[3]; 328 ACPI_STATUS rv; 329 330 /* 331 * First try to call the Device Sleep Wake control method, _DSW. 332 * Only if this is not available, resort to to the Power State 333 * Wake control method, _PSW, which was deprecated in ACPI 3.0. 334 * 335 * The arguments to these methods are as follows: 336 * 337 * arg0 arg1 arg2 338 * ---- ---- ---- 339 * _PSW 0: disable 340 * 1: enable 341 * 342 * _DSW 0: disable 0: S0 0: D0 343 * 1: enable 1: S1 1: D0 or D1 344 * 2: D0, D1, or D2 345 * x: Sx 3: D0, D1, D2 or D3 346 */ 347 arg.Count = 3; 348 arg.Pointer = obj; 349 350 obj[0].Integer.Value = enable; 351 obj[1].Integer.Value = state; 352 obj[2].Integer.Value = ACPI_STATE_D0; 353 354 obj[0].Type = obj[1].Type = obj[2].Type = ACPI_TYPE_INTEGER; 355 356 rv = AcpiEvaluateObject(ad->ad_handle, "_DSW", &arg, NULL); 357 358 if (ACPI_SUCCESS(rv)) 359 return; 360 361 if (rv != AE_NOT_FOUND) 362 goto fail; 363 364 rv = acpi_eval_set_integer(ad->ad_handle, "_PSW", enable); 365 366 if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) 367 goto fail; 368 369 return; 370 371 fail: 372 aprint_error_dev(ad->ad_root, "failed to evaluate wake " 373 "control method: %s\n", AcpiFormatException(rv)); 374 } 375