xref: /openbsd-src/sys/dev/acpi/acpibtn.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /* $OpenBSD: acpibtn.c,v 1.35 2013/08/21 20:10:47 landry Exp $ */
2 /*
3  * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/proc.h>
20 #include <sys/signalvar.h>
21 #include <sys/systm.h>
22 #include <sys/device.h>
23 #include <sys/malloc.h>
24 
25 #include <machine/bus.h>
26 #include <machine/apmvar.h>
27 
28 #include <dev/acpi/acpireg.h>
29 #include <dev/acpi/acpivar.h>
30 #include <dev/acpi/acpidev.h>
31 #include <dev/acpi/amltypes.h>
32 #include <dev/acpi/dsdt.h>
33 
34 #include <sys/sensors.h>
35 
36 int	acpibtn_match(struct device *, void *, void *);
37 void	acpibtn_attach(struct device *, struct device *, void *);
38 int	acpibtn_notify(struct aml_node *, int, void *);
39 
40 struct acpibtn_softc {
41 	struct device		sc_dev;
42 
43 	bus_space_tag_t		sc_iot;
44 	bus_space_handle_t	sc_ioh;
45 
46 	struct acpi_softc	*sc_acpi;
47 	struct aml_node		*sc_devnode;
48 
49 	struct ksensor		sc_sens;
50 	struct ksensordev	sc_sensdev;
51 
52 	int			sc_btn_type;
53 #define	ACPIBTN_UNKNOWN	0
54 #define ACPIBTN_LID	1
55 #define ACPIBTN_POWER	2
56 #define ACPIBTN_SLEEP	3
57 };
58 
59 int	acpibtn_getsta(struct acpibtn_softc *);
60 int	acpibtn_setpsw(struct acpibtn_softc *, int);
61 
62 struct acpi_lid {
63 	struct acpibtn_softc	*abl_softc;
64 	SLIST_ENTRY(acpi_lid)	abl_link;
65 };
66 SLIST_HEAD(acpi_lid_head, acpi_lid) acpibtn_lids =
67     SLIST_HEAD_INITIALIZER(acpibtn_lids);
68 
69 struct cfattach acpibtn_ca = {
70 	sizeof(struct acpibtn_softc), acpibtn_match, acpibtn_attach
71 };
72 
73 struct cfdriver acpibtn_cd = {
74 	NULL, "acpibtn", DV_DULL
75 };
76 
77 const char *acpibtn_hids[] = { ACPI_DEV_LD, ACPI_DEV_PBD, ACPI_DEV_SBD, 0 };
78 
79 int
80 acpibtn_setpsw(struct acpibtn_softc *sc, int psw)
81 {
82 	struct aml_value	val;
83 
84 	bzero(&val, sizeof val);
85 	val.type = AML_OBJTYPE_INTEGER;
86 	val.v_integer = psw;
87 	val.length = 1;
88 
89 	return (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSW", 1, &val,
90 	    NULL));
91 }
92 
93 void
94 acpibtn_disable_psw(void)
95 {
96 	struct acpi_lid *lid;
97 
98 	/* disable _LID for wakeup */
99 	SLIST_FOREACH(lid, &acpibtn_lids, abl_link)
100 		acpibtn_setpsw(lid->abl_softc, 0);
101 }
102 
103 void
104 acpibtn_enable_psw(void)
105 {
106 	struct acpi_lid		*lid;
107 
108 	/* enable _LID for wakeup */
109 	SLIST_FOREACH(lid, &acpibtn_lids, abl_link)
110 		acpibtn_setpsw(lid->abl_softc, 1);
111 }
112 
113 int
114 acpibtn_match(struct device *parent, void *match, void *aux)
115 {
116 	struct acpi_attach_args	*aa = aux;
117 	struct cfdata		*cf = match;
118 
119 	/* sanity */
120 	return (acpi_matchhids(aa, acpibtn_hids, cf->cf_driver->cd_name));
121 }
122 
123 void
124 acpibtn_attach(struct device *parent, struct device *self, void *aux)
125 {
126 	struct acpibtn_softc	*sc = (struct acpibtn_softc *)self;
127 	struct acpi_attach_args *aa = aux;
128 	struct acpi_lid		*lid;
129 	int64_t			lid_open;
130 
131 	sc->sc_acpi = (struct acpi_softc *)parent;
132 	sc->sc_devnode = aa->aaa_node;
133 
134 	if (!strcmp(aa->aaa_dev, ACPI_DEV_LD)) {
135 		sc->sc_btn_type = ACPIBTN_LID;
136 		if (acpibtn_setpsw(sc, 0) == 0) {
137 			lid = malloc(sizeof(*lid), M_DEVBUF, M_WAITOK | M_ZERO);
138 			lid->abl_softc = sc;
139 			SLIST_INSERT_HEAD(&acpibtn_lids, lid, abl_link);
140 		}
141 	} else if (!strcmp(aa->aaa_dev, ACPI_DEV_PBD))
142 		sc->sc_btn_type = ACPIBTN_POWER;
143 	else if (!strcmp(aa->aaa_dev, ACPI_DEV_SBD))
144 		sc->sc_btn_type = ACPIBTN_SLEEP;
145 
146 	acpibtn_getsta(sc);
147 
148 	printf(": %s\n", sc->sc_devnode->name);
149 
150 	if (sc->sc_btn_type == ACPIBTN_LID) {
151 		strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
152 		    sizeof(sc->sc_sensdev.xname));
153 		strlcpy(sc->sc_sens.desc, "lid open",
154 		    sizeof(sc->sc_sens.desc));
155 		sc->sc_sens.type = SENSOR_INDICATOR;
156 		sensor_attach(&sc->sc_sensdev, &sc->sc_sens);
157 		sensordev_install(&sc->sc_sensdev);
158 
159 		aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
160 		    "_LID", 0, NULL, &lid_open);
161 		sc->sc_sens.value = lid_open;
162 	}
163 
164 	aml_register_notify(sc->sc_devnode, aa->aaa_dev, acpibtn_notify,
165 	    sc, ACPIDEV_NOPOLL);
166 }
167 
168 int
169 acpibtn_getsta(struct acpibtn_softc *sc)
170 {
171 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, NULL) != 0) {
172 		dnprintf(20, "%s: no _STA\n", DEVNAME(sc));
173 		/* XXX not all buttons have _STA so FALLTROUGH */
174 	}
175 
176 	return (0);
177 }
178 
179 int
180 acpibtn_notify(struct aml_node *node, int notify_type, void *arg)
181 {
182 	struct acpibtn_softc	*sc = arg;
183 #ifndef SMALL_KERNEL
184 	extern int lid_suspend;
185 	int64_t lid;
186 #endif
187 
188 	dnprintf(10, "acpibtn_notify: %.2x %s\n", notify_type,
189 	    sc->sc_devnode->name);
190 
191 	switch (sc->sc_btn_type) {
192 	case ACPIBTN_LID:
193 #ifndef SMALL_KERNEL
194 		/*
195 		 * Notification of 0x80 for lid opens or closes.  We
196 		 * need to check the current status by calling the
197 		 * _LID method.  0 means the lid is closed and we
198 		 * should go to sleep.
199 		 */
200 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
201 		    "_LID", 0, NULL, &lid))
202 			return (0);
203 		sc->sc_sens.value = lid;
204 		if (lid_suspend == 0)
205 			break;
206 		if (lid == 0)
207 			goto sleep;
208 #endif /* SMALL_KERNEL */
209 		break;
210 	case ACPIBTN_SLEEP:
211 #ifndef SMALL_KERNEL
212 		switch (notify_type) {
213 		case 0x02:
214 			/* "something" has been taken care of by the system */
215 			break;
216 		case 0x80:
217 sleep:
218 			/* Request to go to sleep */
219 			if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ))
220 				acpi_addtask(sc->sc_acpi, acpi_sleep_task,
221 				    sc->sc_acpi, ACPI_STATE_S3);
222 			break;
223 		}
224 #endif /* SMALL_KERNEL */
225 		break;
226 	case ACPIBTN_POWER:
227 		if (notify_type == 0x80)
228 			acpi_addtask(sc->sc_acpi, acpi_powerdown_task,
229 			    sc->sc_acpi, 0);
230 		break;
231 	default:
232 		printf("%s: spurious acpi button interrupt %i\n", DEVNAME(sc),
233 		    sc->sc_btn_type);
234 		break;
235 	}
236 
237 	return (0);
238 }
239