acpi_wakedev.c revision 1.2.6.1 1 /* $NetBSD: acpi_wakedev.c,v 1.2.6.1 2010/04/30 14:43:06 uebayasi Exp $ */
2
3 /*-
4 * Copyright (c) 2009, 2010 Jared D. McNeill <jmcneill (at) 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.2.6.1 2010/04/30 14:43:06 uebayasi 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_wakedev.h>
40
41 #define _COMPONENT ACPI_BUS_COMPONENT
42 ACPI_MODULE_NAME ("acpi_wakedev")
43
44 static const char * const acpi_wakedev_default[] = {
45 "PNP0C0C", /* power button */
46 "PNP0C0E", /* sleep button */
47 "PNP0C0D", /* lid switch */
48 "PNP03??", /* PC KBD port */
49 NULL,
50 };
51
52 static const struct sysctlnode *rnode = NULL;
53
54 static void acpi_wakedev_prepare(struct acpi_devnode *, int, int);
55 static void acpi_wakedev_gpe(struct acpi_devnode *, int);
56
57 SYSCTL_SETUP(sysctl_acpi_wakedev_setup, "sysctl hw.acpi.wake subtree setup")
58 {
59 int err;
60
61 err = sysctl_createv(NULL, 0, NULL, &rnode,
62 CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw",
63 NULL, NULL, 0, NULL, 0,
64 CTL_HW, CTL_EOL);
65
66 if (err != 0)
67 goto fail;
68
69 err = sysctl_createv(NULL, 0, &rnode, &rnode,
70 CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi",
71 NULL, NULL, 0, NULL, 0,
72 CTL_CREATE, CTL_EOL);
73
74 if (err != 0)
75 goto fail;
76
77 err = sysctl_createv(NULL, 0, &rnode, &rnode,
78 CTLFLAG_PERMANENT, CTLTYPE_NODE,
79 "wake", SYSCTL_DESCR("ACPI device wake-up"),
80 NULL, 0, NULL, 0,
81 CTL_CREATE, CTL_EOL);
82
83 if (err != 0)
84 goto fail;
85
86 return;
87
88 fail:
89 rnode = NULL;
90 }
91
92 void
93 acpi_wakedev_add(struct acpi_devnode *ad)
94 {
95 int err;
96
97 KASSERT(ad != NULL && ad->ad_root != NULL);
98 KASSERT((ad->ad_flags & ACPI_DEVICE_WAKEUP) != 0);
99
100 ad->ad_wake = 0;
101
102 if (acpi_match_hid(ad->ad_devinfo, acpi_wakedev_default))
103 ad->ad_wake = 1;
104
105 if (rnode == NULL)
106 return;
107
108 err = sysctl_createv(NULL, 0, &rnode, NULL,
109 CTLFLAG_READWRITE, CTLTYPE_BOOL, ad->ad_name,
110 NULL, NULL, 0, &ad->ad_wake, 0,
111 CTL_CREATE, CTL_EOL);
112
113 if (err != 0)
114 aprint_error_dev(ad->ad_root, "sysctl_createv"
115 "(hw.acpi.wake.%s) failed (err %d)\n", ad->ad_name, err);
116 }
117
118 void
119 acpi_wakedev_commit(struct acpi_softc *sc, int state)
120 {
121 struct acpi_devnode *ad;
122
123 /*
124 * As noted in ACPI 3.0 (p. 243), preparing
125 * a device for wakeup is a two-step process:
126 *
127 * 1. Enable all power resources in _PRW.
128 *
129 * 2. If present, execute _DSW/_PSW method.
130 *
131 * XXX: The first one is yet to be implemented.
132 */
133 SIMPLEQ_FOREACH(ad, &sc->ad_head, ad_list) {
134
135 if ((ad->ad_flags & ACPI_DEVICE_WAKEUP) == 0)
136 continue;
137
138 acpi_wakedev_gpe(ad, ad->ad_wake);
139 acpi_wakedev_prepare(ad, ad->ad_wake, state);
140 }
141 }
142
143 static void
144 acpi_wakedev_prepare(struct acpi_devnode *ad, int enable, int state)
145 {
146 ACPI_OBJECT_LIST arg;
147 ACPI_OBJECT obj[3];
148 ACPI_STATUS rv;
149
150 /*
151 * First try to call the Device Sleep Wake control method, _DSW.
152 * Only if this is not available, resort to to the Power State
153 * Wake control method, _PSW, which was deprecated in ACPI 3.0.
154 *
155 * The arguments to these methods are as follows:
156 *
157 * arg0 arg1 arg2
158 * ---- ---- ----
159 * _PSW 0: disable
160 * 1: enable
161 *
162 * _DSW 0: disable 0: S0 0: D0
163 * 1: enable 1: S1 1: D0 or D1
164 * 2: D0, D1, or D2
165 * x: Sx 3: D0, D1, D2 or D3
166 */
167 arg.Count = 3;
168 arg.Pointer = obj;
169
170 obj[0].Integer.Value = enable;
171 obj[1].Integer.Value = state;
172 obj[2].Integer.Value = 3;
173
174 obj[0].Type = obj[1].Type = obj[2].Type = ACPI_TYPE_INTEGER;
175
176 rv = AcpiEvaluateObject(ad->ad_handle, "_DSW", &arg, NULL);
177
178 if (ACPI_SUCCESS(rv))
179 return;
180
181 if (rv != AE_NOT_FOUND)
182 goto fail;
183
184 rv = acpi_eval_set_integer(ad->ad_handle, "_PSW", enable);
185
186 if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND)
187 goto fail;
188
189 return;
190
191 fail:
192 aprint_error_dev(ad->ad_root, "failed to evaluate wake "
193 "control method: %s\n", AcpiFormatException(rv));
194 }
195
196 static void
197 acpi_wakedev_gpe(struct acpi_devnode *ad, int enable)
198 {
199 ACPI_OBJECT *elm, *obj;
200 ACPI_INTEGER val;
201 ACPI_BUFFER buf;
202 ACPI_STATUS rv;
203
204 rv = acpi_eval_struct(ad->ad_handle, METHOD_NAME__PRW, &buf);
205
206 if (ACPI_FAILURE(rv))
207 return;
208
209 obj = buf.Pointer;
210
211 if (obj->Type != ACPI_TYPE_PACKAGE || obj->Package.Count < 2)
212 goto out;
213
214 /*
215 * As noted in ACPI 3.0 (section 7.2.10), the _PRW object is
216 * a package in which the first element is either an integer
217 * or again a package. In the latter case the package inside
218 * the package element has two elements, a reference handle
219 * and the GPE number.
220 */
221 elm = &obj->Package.Elements[0];
222
223 switch (elm->Type) {
224
225 case ACPI_TYPE_INTEGER:
226 val = elm->Integer.Value;
227 break;
228
229 case ACPI_TYPE_PACKAGE:
230
231 if (elm->Package.Count < 2)
232 goto out;
233
234 if (elm->Package.Elements[0].Type != ACPI_TYPE_LOCAL_REFERENCE)
235 goto out;
236
237 if (elm->Package.Elements[1].Type != ACPI_TYPE_INTEGER)
238 goto out;
239
240 val = elm->Package.Elements[1].Integer.Value;
241 break;
242
243 default:
244 goto out;
245 }
246
247 /*
248 * Set or unset a GPE as both runtime and wake.
249 */
250 if (enable == 0)
251 (void)AcpiDisableGpe(NULL, val, ACPI_NOT_ISR);
252 else {
253 (void)AcpiSetGpeType(NULL, val, ACPI_GPE_TYPE_WAKE_RUN);
254 (void)AcpiEnableGpe(NULL, val, ACPI_NOT_ISR);
255 }
256
257 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "wake GPE %s for %s\n",
258 (enable != 0) ? "enabled" : "disabled", ad->ad_name));
259
260 out:
261 ACPI_FREE(buf.Pointer);
262 }
263