1 /* $NetBSD: acpi_wakedev.c,v 1.17 2010/06/07 14:12:20 jruoho Exp $ */ 2 3 /*- 4 * Copyright (c) 2009, 2010 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.17 2010/06/07 14:12:20 jruoho Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/device.h> 34 #include <sys/sysctl.h> 35 #include <sys/systm.h> 36 37 #include <dev/acpi/acpireg.h> 38 #include <dev/acpi/acpivar.h> 39 #include <dev/acpi/acpi_power.h> 40 #include <dev/acpi/acpi_wakedev.h> 41 42 #define _COMPONENT ACPI_BUS_COMPONENT 43 ACPI_MODULE_NAME ("acpi_wakedev") 44 45 static const char * const acpi_wakedev_default[] = { 46 "PNP0C0C", /* power button */ 47 "PNP0C0E", /* sleep button */ 48 "PNP0C0D", /* lid switch */ 49 "PNP03??", /* PC KBD port */ 50 NULL, 51 }; 52 53 static const struct sysctlnode *rnode = NULL; 54 55 static void acpi_wakedev_method(struct acpi_devnode *, int, int); 56 static void acpi_wakedev_gpe(struct acpi_devnode *, int, int); 57 static void acpi_wakedev_power(struct acpi_devnode *, ACPI_OBJECT *); 58 59 SYSCTL_SETUP(sysctl_acpi_wakedev_setup, "sysctl hw.acpi.wake subtree setup") 60 { 61 int err; 62 63 err = sysctl_createv(NULL, 0, NULL, &rnode, 64 CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", 65 NULL, NULL, 0, NULL, 0, 66 CTL_HW, CTL_EOL); 67 68 if (err != 0) 69 goto fail; 70 71 err = sysctl_createv(NULL, 0, &rnode, &rnode, 72 CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi", 73 NULL, NULL, 0, NULL, 0, 74 CTL_CREATE, CTL_EOL); 75 76 if (err != 0) 77 goto fail; 78 79 err = sysctl_createv(NULL, 0, &rnode, &rnode, 80 CTLFLAG_PERMANENT, CTLTYPE_NODE, 81 "wake", SYSCTL_DESCR("ACPI device wake-up"), 82 NULL, 0, NULL, 0, 83 CTL_CREATE, CTL_EOL); 84 85 if (err != 0) 86 goto fail; 87 88 return; 89 90 fail: 91 rnode = NULL; 92 } 93 94 void 95 acpi_wakedev_add(struct acpi_devnode *ad) 96 { 97 int err; 98 99 KASSERT(ad != NULL && ad->ad_root != NULL); 100 KASSERT((ad->ad_flags & ACPI_DEVICE_WAKEUP) != 0); 101 102 ad->ad_wake = 0; 103 104 if (acpi_match_hid(ad->ad_devinfo, acpi_wakedev_default)) 105 ad->ad_wake = 1; 106 107 if (rnode == NULL) 108 return; 109 110 err = sysctl_createv(NULL, 0, &rnode, NULL, 111 CTLFLAG_READWRITE, CTLTYPE_BOOL, ad->ad_name, 112 NULL, NULL, 0, &ad->ad_wake, 0, 113 CTL_CREATE, CTL_EOL); 114 115 if (err != 0) 116 aprint_error_dev(ad->ad_root, "sysctl_createv" 117 "(hw.acpi.wake.%s) failed (err %d)\n", ad->ad_name, err); 118 } 119 120 void 121 acpi_wakedev_commit(struct acpi_softc *sc, int state) 122 { 123 struct acpi_devnode *ad; 124 125 /* 126 * To prepare a device for wakeup: 127 * 128 * 1. Set appropriate GPEs. 129 * 130 * 2. Enable all power resources in _PRW. 131 * 132 * 3. If present, execute _DSW/_PSW method. 133 */ 134 SIMPLEQ_FOREACH(ad, &sc->ad_head, ad_list) { 135 136 if ((ad->ad_flags & ACPI_DEVICE_WAKEUP) == 0) 137 continue; 138 139 acpi_wakedev_gpe(ad, ad->ad_wake, state); 140 acpi_wakedev_method(ad, ad->ad_wake, state); 141 } 142 } 143 144 static void 145 acpi_wakedev_method(struct acpi_devnode *ad, int enable, int state) 146 { 147 ACPI_OBJECT_LIST arg; 148 ACPI_OBJECT obj[3]; 149 ACPI_STATUS rv; 150 151 /* 152 * First try to call the Device Sleep Wake control method, _DSW. 153 * Only if this is not available, resort to to the Power State 154 * Wake control method, _PSW, which was deprecated in ACPI 3.0. 155 * 156 * The arguments to these methods are as follows: 157 * 158 * arg0 arg1 arg2 159 * ---- ---- ---- 160 * _PSW 0: disable 161 * 1: enable 162 * 163 * _DSW 0: disable 0: S0 0: D0 164 * 1: enable 1: S1 1: D0 or D1 165 * 2: D0, D1, or D2 166 * x: Sx 3: D0, D1, D2 or D3 167 */ 168 arg.Count = 3; 169 arg.Pointer = obj; 170 171 obj[0].Integer.Value = enable; 172 obj[1].Integer.Value = state; 173 obj[2].Integer.Value = ACPI_STATE_D0; 174 175 obj[0].Type = obj[1].Type = obj[2].Type = ACPI_TYPE_INTEGER; 176 177 rv = AcpiEvaluateObject(ad->ad_handle, "_DSW", &arg, NULL); 178 179 if (ACPI_SUCCESS(rv)) 180 return; 181 182 if (rv != AE_NOT_FOUND) 183 goto fail; 184 185 rv = acpi_eval_set_integer(ad->ad_handle, "_PSW", enable); 186 187 if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) 188 goto fail; 189 190 return; 191 192 fail: 193 aprint_error_dev(ad->ad_root, "failed to evaluate wake " 194 "control method: %s\n", AcpiFormatException(rv)); 195 } 196 197 static void 198 acpi_wakedev_gpe(struct acpi_devnode *ad, int enable, int state) 199 { 200 ACPI_OBJECT *elm, *obj; 201 ACPI_HANDLE hdl = NULL; 202 ACPI_INTEGER val; 203 ACPI_BUFFER buf; 204 ACPI_STATUS rv; 205 206 rv = acpi_eval_struct(ad->ad_handle, "_PRW", &buf); 207 208 if (ACPI_FAILURE(rv)) 209 return; 210 211 obj = buf.Pointer; 212 213 if (obj->Type != ACPI_TYPE_PACKAGE || obj->Package.Count < 2) 214 goto out; 215 216 /* 217 * As noted in ACPI 3.0 (section 7.2.10), the _PRW object is 218 * a package in which the first element is either an integer 219 * or again a package. In the latter case the package inside 220 * the package element has two elements, a reference handle 221 * and the GPE number. 222 */ 223 elm = &obj->Package.Elements[0]; 224 225 switch (elm->Type) { 226 227 case ACPI_TYPE_INTEGER: 228 val = elm->Integer.Value; 229 break; 230 231 case ACPI_TYPE_PACKAGE: 232 233 if (elm->Package.Count < 2) 234 goto out; 235 236 if (elm->Package.Elements[0].Type != ACPI_TYPE_LOCAL_REFERENCE) 237 goto out; 238 239 if (elm->Package.Elements[1].Type != ACPI_TYPE_INTEGER) 240 goto out; 241 242 hdl = elm->Package.Elements[0].Reference.Handle; 243 val = elm->Package.Elements[1].Integer.Value; 244 break; 245 246 default: 247 goto out; 248 } 249 250 /* 251 * The second element is an integer that contains the 252 * lowest sleep state that can be entered while still 253 * providing wake-up functionality. The rest of the 254 * elements are references to power resources. 255 */ 256 elm = &obj->Package.Elements[1]; 257 258 if (elm->Type != ACPI_TYPE_INTEGER) 259 goto out; 260 261 if (state > elm->Integer.Value) 262 aprint_error_dev(ad->ad_root, "sleep state S%d " 263 "loses wake for %s\n", state, ad->ad_name); 264 265 /* 266 * Turn on power resources. 267 */ 268 if (enable != 0) 269 acpi_wakedev_power(ad, obj); 270 271 /* 272 * Set both runtime and wake GPEs, but unset only wake GPEs. 273 */ 274 if (enable != 0) 275 (void)AcpiEnableGpe(hdl, val, ACPI_GPE_TYPE_WAKE_RUN); 276 else 277 (void)AcpiDisableGpe(hdl, val, ACPI_GPE_TYPE_WAKE); 278 279 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "wake GPE %s for %s\n", 280 (enable != 0) ? "enabled" : "disabled", ad->ad_name)); 281 282 out: 283 ACPI_FREE(buf.Pointer); 284 } 285 286 static void 287 acpi_wakedev_power(struct acpi_devnode *ad, ACPI_OBJECT *obj) 288 { 289 ACPI_OBJECT *elm; 290 ACPI_HANDLE hdl; 291 ACPI_STATUS rv; 292 uint32_t i, n; 293 294 n = obj->Package.Count; 295 296 if (n < 3) 297 return; 298 299 for (i = 2; i < n; i++) { 300 301 elm = &obj->Package.Elements[i]; 302 rv = acpi_eval_reference_handle(elm, &hdl); 303 304 if (ACPI_FAILURE(rv)) 305 continue; 306 307 (void)acpi_power_res(hdl, ad->ad_handle, true); 308 } 309 } 310