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