acpi_wakedev.c revision 1.2.2.3 1 1.2.2.3 yamt /* $NetBSD: acpi_wakedev.c,v 1.2.2.3 2010/03/11 15:03:22 yamt Exp $ */
2 1.2.2.2 yamt
3 1.2.2.2 yamt /*-
4 1.2.2.2 yamt * Copyright (c) 2009 Jared D. McNeill <jmcneill (at) invisible.ca>
5 1.2.2.2 yamt * All rights reserved.
6 1.2.2.2 yamt *
7 1.2.2.2 yamt * Redistribution and use in source and binary forms, with or without
8 1.2.2.2 yamt * modification, are permitted provided that the following conditions
9 1.2.2.2 yamt * are met:
10 1.2.2.2 yamt * 1. Redistributions of source code must retain the above copyright
11 1.2.2.2 yamt * notice, this list of conditions and the following disclaimer.
12 1.2.2.2 yamt * 2. Redistributions in binary form must reproduce the above copyright
13 1.2.2.2 yamt * notice, this list of conditions and the following disclaimer in the
14 1.2.2.2 yamt * documentation and/or other materials provided with the distribution.
15 1.2.2.2 yamt *
16 1.2.2.2 yamt * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 1.2.2.2 yamt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 1.2.2.2 yamt * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1.2.2.2 yamt * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 1.2.2.2 yamt * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 1.2.2.2 yamt * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 1.2.2.2 yamt * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.2.2.2 yamt * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 1.2.2.2 yamt * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 1.2.2.2 yamt * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.2.2.2 yamt * POSSIBILITY OF SUCH DAMAGE.
27 1.2.2.2 yamt */
28 1.2.2.2 yamt
29 1.2.2.2 yamt #include <sys/cdefs.h>
30 1.2.2.3 yamt __KERNEL_RCSID(0, "$NetBSD: acpi_wakedev.c,v 1.2.2.3 2010/03/11 15:03:22 yamt Exp $");
31 1.2.2.2 yamt
32 1.2.2.2 yamt #include <sys/param.h>
33 1.2.2.2 yamt #include <sys/device.h>
34 1.2.2.3 yamt #include <sys/kmem.h>
35 1.2.2.2 yamt #include <sys/queue.h>
36 1.2.2.2 yamt #include <sys/sysctl.h>
37 1.2.2.3 yamt #include <sys/systm.h>
38 1.2.2.2 yamt
39 1.2.2.2 yamt #include <dev/acpi/acpireg.h>
40 1.2.2.3 yamt #include <dev/acpi/acpivar.h>
41 1.2.2.2 yamt #include <dev/acpi/acpi_wakedev.h>
42 1.2.2.2 yamt
43 1.2.2.3 yamt #define _COMPONENT ACPI_BUS_COMPONENT
44 1.2.2.3 yamt ACPI_MODULE_NAME ("acpi_wakedev")
45 1.2.2.3 yamt
46 1.2.2.3 yamt struct acpi_wakedev {
47 1.2.2.3 yamt struct acpi_devnode *aw_node;
48 1.2.2.3 yamt struct sysctllog *aw_sysctllog;
49 1.2.2.3 yamt int aw_enabled;
50 1.2.2.3 yamt
51 1.2.2.3 yamt TAILQ_ENTRY(acpi_wakedev) aw_list;
52 1.2.2.3 yamt };
53 1.2.2.3 yamt
54 1.2.2.2 yamt struct acpi_wakedev;
55 1.2.2.3 yamt static int acpi_wakedev_node = -1;
56 1.2.2.2 yamt
57 1.2.2.2 yamt static TAILQ_HEAD(, acpi_wakedev) acpi_wakedevlist =
58 1.2.2.2 yamt TAILQ_HEAD_INITIALIZER(acpi_wakedevlist);
59 1.2.2.2 yamt
60 1.2.2.2 yamt static const char * const acpi_wakedev_default[] = {
61 1.2.2.2 yamt "PNP0C0C", /* power button */
62 1.2.2.2 yamt "PNP0C0E", /* sleep button */
63 1.2.2.2 yamt "PNP0C0D", /* lid switch */
64 1.2.2.2 yamt "PNP03??", /* PC KBD port */
65 1.2.2.2 yamt NULL,
66 1.2.2.2 yamt };
67 1.2.2.2 yamt
68 1.2.2.3 yamt static void acpi_wakedev_sysctl_add(struct acpi_wakedev *);
69 1.2.2.3 yamt static bool acpi_wakedev_add(struct acpi_softc *, struct acpi_devnode *);
70 1.2.2.3 yamt static void acpi_wakedev_print(struct acpi_wakedev *);
71 1.2.2.3 yamt static void acpi_wakedev_prepare(struct acpi_devnode *, int, int);
72 1.2.2.3 yamt
73 1.2.2.2 yamt SYSCTL_SETUP(sysctl_acpi_wakedev_setup, "sysctl hw.wake subtree setup")
74 1.2.2.2 yamt {
75 1.2.2.2 yamt const struct sysctlnode *rnode;
76 1.2.2.2 yamt int err;
77 1.2.2.2 yamt
78 1.2.2.2 yamt err = sysctl_createv(NULL, 0, NULL, NULL,
79 1.2.2.2 yamt CTLFLAG_PERMANENT,
80 1.2.2.2 yamt CTLTYPE_NODE, "hw", NULL,
81 1.2.2.2 yamt NULL, 0, NULL, 0, CTL_HW, CTL_EOL);
82 1.2.2.2 yamt if (err)
83 1.2.2.2 yamt return;
84 1.2.2.2 yamt err = sysctl_createv(NULL, 0, NULL, &rnode,
85 1.2.2.2 yamt CTLFLAG_PERMANENT,
86 1.2.2.2 yamt CTLTYPE_NODE, "wake", NULL,
87 1.2.2.2 yamt NULL, 0, NULL, 0,
88 1.2.2.2 yamt CTL_HW, CTL_CREATE, CTL_EOL);
89 1.2.2.2 yamt if (err)
90 1.2.2.2 yamt return;
91 1.2.2.2 yamt acpi_wakedev_node = rnode->sysctl_num;
92 1.2.2.2 yamt }
93 1.2.2.2 yamt
94 1.2.2.2 yamt static void
95 1.2.2.2 yamt acpi_wakedev_sysctl_add(struct acpi_wakedev *aw)
96 1.2.2.2 yamt {
97 1.2.2.2 yamt int err;
98 1.2.2.2 yamt
99 1.2.2.2 yamt if (acpi_wakedev_node == -1)
100 1.2.2.2 yamt return;
101 1.2.2.2 yamt
102 1.2.2.2 yamt err = sysctl_createv(&aw->aw_sysctllog, 0, NULL, NULL,
103 1.2.2.2 yamt CTLFLAG_READWRITE, CTLTYPE_INT, aw->aw_node->ad_name,
104 1.2.2.2 yamt NULL, NULL, 0, &aw->aw_enabled, 0,
105 1.2.2.2 yamt CTL_HW, acpi_wakedev_node, CTL_CREATE, CTL_EOL);
106 1.2.2.2 yamt if (err)
107 1.2.2.3 yamt aprint_error("%s: sysctl_createv(hw.wake.%s) failed (%d)\n",
108 1.2.2.3 yamt __func__, aw->aw_node->ad_name, err);
109 1.2.2.2 yamt }
110 1.2.2.2 yamt
111 1.2.2.2 yamt static bool
112 1.2.2.2 yamt acpi_wakedev_add(struct acpi_softc *sc, struct acpi_devnode *ad)
113 1.2.2.2 yamt {
114 1.2.2.2 yamt struct acpi_wakedev *aw;
115 1.2.2.2 yamt ACPI_HANDLE hdl;
116 1.2.2.2 yamt
117 1.2.2.2 yamt if (ACPI_FAILURE(AcpiGetHandle(ad->ad_handle, "_PRW", &hdl)))
118 1.2.2.2 yamt return false;
119 1.2.2.2 yamt
120 1.2.2.2 yamt aw = kmem_alloc(sizeof(*aw), KM_SLEEP);
121 1.2.2.2 yamt if (aw == NULL) {
122 1.2.2.3 yamt aprint_error("%s: kmem_alloc failed\n", __func__);
123 1.2.2.2 yamt return false;
124 1.2.2.2 yamt }
125 1.2.2.2 yamt aw->aw_node = ad;
126 1.2.2.2 yamt aw->aw_sysctllog = NULL;
127 1.2.2.2 yamt if (acpi_match_hid(ad->ad_devinfo, acpi_wakedev_default))
128 1.2.2.2 yamt aw->aw_enabled = 1;
129 1.2.2.2 yamt else
130 1.2.2.2 yamt aw->aw_enabled = 0;
131 1.2.2.2 yamt
132 1.2.2.2 yamt TAILQ_INSERT_TAIL(&acpi_wakedevlist, aw, aw_list);
133 1.2.2.2 yamt
134 1.2.2.2 yamt acpi_wakedev_sysctl_add(aw);
135 1.2.2.2 yamt
136 1.2.2.2 yamt return true;
137 1.2.2.2 yamt }
138 1.2.2.2 yamt
139 1.2.2.2 yamt static void
140 1.2.2.2 yamt acpi_wakedev_print(struct acpi_wakedev *aw)
141 1.2.2.2 yamt {
142 1.2.2.2 yamt aprint_debug(" %s", aw->aw_node->ad_name);
143 1.2.2.2 yamt }
144 1.2.2.2 yamt
145 1.2.2.2 yamt int
146 1.2.2.2 yamt acpi_wakedev_scan(struct acpi_softc *sc)
147 1.2.2.2 yamt {
148 1.2.2.2 yamt struct acpi_devnode *ad;
149 1.2.2.2 yamt struct acpi_wakedev *aw;
150 1.2.2.2 yamt ACPI_DEVICE_INFO *di;
151 1.2.2.2 yamt int count = 0;
152 1.2.2.2 yamt
153 1.2.2.2 yamt #define ACPI_STA_DEV_VALID \
154 1.2.2.2 yamt (ACPI_STA_DEV_PRESENT|ACPI_STA_DEV_ENABLED|ACPI_STA_DEV_OK)
155 1.2.2.2 yamt
156 1.2.2.3 yamt SIMPLEQ_FOREACH(ad, &sc->sc_devnodes, ad_list) {
157 1.2.2.3 yamt
158 1.2.2.3 yamt di = ad->ad_devinfo;
159 1.2.2.3 yamt
160 1.2.2.3 yamt if (di->Type != ACPI_TYPE_DEVICE)
161 1.2.2.3 yamt continue;
162 1.2.2.3 yamt
163 1.2.2.3 yamt if ((di->Valid & ACPI_VALID_STA) != 0 &&
164 1.2.2.3 yamt (di->CurrentStatus & ACPI_STA_DEV_VALID) !=
165 1.2.2.3 yamt ACPI_STA_DEV_VALID)
166 1.2.2.3 yamt continue;
167 1.2.2.3 yamt
168 1.2.2.3 yamt if (acpi_wakedev_add(sc, ad) == true)
169 1.2.2.3 yamt ++count;
170 1.2.2.3 yamt }
171 1.2.2.2 yamt
172 1.2.2.2 yamt #undef ACPI_STA_DEV_VALID
173 1.2.2.2 yamt
174 1.2.2.2 yamt if (count == 0)
175 1.2.2.2 yamt return 0;
176 1.2.2.2 yamt
177 1.2.2.2 yamt aprint_debug_dev(sc->sc_dev, "wakeup devices:");
178 1.2.2.2 yamt TAILQ_FOREACH(aw, &acpi_wakedevlist, aw_list)
179 1.2.2.2 yamt acpi_wakedev_print(aw);
180 1.2.2.2 yamt aprint_debug("\n");
181 1.2.2.2 yamt
182 1.2.2.2 yamt return count;
183 1.2.2.2 yamt }
184 1.2.2.2 yamt
185 1.2.2.2 yamt void
186 1.2.2.3 yamt acpi_wakedev_commit(struct acpi_softc *sc, int state)
187 1.2.2.2 yamt {
188 1.2.2.2 yamt struct acpi_wakedev *aw;
189 1.2.2.2 yamt
190 1.2.2.3 yamt /*
191 1.2.2.3 yamt * As noted in ACPI 3.0 (p. 243), preparing
192 1.2.2.3 yamt * a device for wakeup is a two-step process:
193 1.2.2.3 yamt *
194 1.2.2.3 yamt * 1. Enable all power resources in _PRW.
195 1.2.2.3 yamt *
196 1.2.2.3 yamt * 2. If present, execute _DSW/_PSW method.
197 1.2.2.3 yamt *
198 1.2.2.3 yamt * XXX: The first one is yet to be implemented.
199 1.2.2.3 yamt */
200 1.2.2.2 yamt TAILQ_FOREACH(aw, &acpi_wakedevlist, aw_list) {
201 1.2.2.3 yamt
202 1.2.2.2 yamt if (aw->aw_enabled) {
203 1.2.2.2 yamt aprint_debug_dev(sc->sc_dev, "set wake GPE (%s)\n",
204 1.2.2.2 yamt aw->aw_node->ad_name);
205 1.2.2.2 yamt acpi_set_wake_gpe(aw->aw_node->ad_handle);
206 1.2.2.2 yamt } else
207 1.2.2.2 yamt acpi_clear_wake_gpe(aw->aw_node->ad_handle);
208 1.2.2.3 yamt
209 1.2.2.3 yamt acpi_wakedev_prepare(aw->aw_node, aw->aw_enabled, state);
210 1.2.2.2 yamt }
211 1.2.2.2 yamt }
212 1.2.2.3 yamt
213 1.2.2.3 yamt static void
214 1.2.2.3 yamt acpi_wakedev_prepare(struct acpi_devnode *ad, int enable, int state)
215 1.2.2.3 yamt {
216 1.2.2.3 yamt ACPI_OBJECT_LIST arg;
217 1.2.2.3 yamt ACPI_OBJECT obj[3];
218 1.2.2.3 yamt ACPI_STATUS rv;
219 1.2.2.3 yamt
220 1.2.2.3 yamt /*
221 1.2.2.3 yamt * First try to call the Device Sleep Wake control method, _DSW.
222 1.2.2.3 yamt * Only if this is not available, resort to to the Power State
223 1.2.2.3 yamt * Wake control method, _PSW, which was deprecated in ACPI 3.0.
224 1.2.2.3 yamt *
225 1.2.2.3 yamt * The arguments to these methods are as follows:
226 1.2.2.3 yamt *
227 1.2.2.3 yamt * arg0 arg1 arg2
228 1.2.2.3 yamt * ---- ---- ----
229 1.2.2.3 yamt * _PSW 0: disable
230 1.2.2.3 yamt * 1: enable
231 1.2.2.3 yamt *
232 1.2.2.3 yamt * _DSW 0: disable 0: S0 0: D0
233 1.2.2.3 yamt * 1: enable 1: S1 1: D0 or D1
234 1.2.2.3 yamt * 2: D0, D1, or D2
235 1.2.2.3 yamt * x: Sx 3: D0, D1, D2 or D3
236 1.2.2.3 yamt */
237 1.2.2.3 yamt arg.Count = 3;
238 1.2.2.3 yamt arg.Pointer = obj;
239 1.2.2.3 yamt
240 1.2.2.3 yamt obj[0].Integer.Value = enable;
241 1.2.2.3 yamt obj[1].Integer.Value = state;
242 1.2.2.3 yamt obj[2].Integer.Value = 3;
243 1.2.2.3 yamt
244 1.2.2.3 yamt obj[0].Type = obj[1].Type = obj[2].Type = ACPI_TYPE_INTEGER;
245 1.2.2.3 yamt
246 1.2.2.3 yamt rv = AcpiEvaluateObject(ad->ad_handle, "_DSW", &arg, NULL);
247 1.2.2.3 yamt
248 1.2.2.3 yamt if (ACPI_SUCCESS(rv))
249 1.2.2.3 yamt return;
250 1.2.2.3 yamt
251 1.2.2.3 yamt if (rv != AE_NOT_FOUND)
252 1.2.2.3 yamt goto fail;
253 1.2.2.3 yamt
254 1.2.2.3 yamt rv = acpi_eval_set_integer(ad->ad_handle, "_PSW", enable);
255 1.2.2.3 yamt
256 1.2.2.3 yamt if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND)
257 1.2.2.3 yamt goto fail;
258 1.2.2.3 yamt
259 1.2.2.3 yamt return;
260 1.2.2.3 yamt
261 1.2.2.3 yamt fail:
262 1.2.2.3 yamt aprint_error_dev(ad->ad_device, "failed to evaluate wake "
263 1.2.2.3 yamt "control method: %s\n", AcpiFormatException(rv));
264 1.2.2.3 yamt }
265