xref: /openbsd/sys/dev/acpi/acpibtn.c (revision cca36db2)
1 /* $OpenBSD: acpibtn.c,v 1.34 2011/01/02 04:56:57 jordan 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 	int			sc_btn_type;
50 #define	ACPIBTN_UNKNOWN	0
51 #define ACPIBTN_LID	1
52 #define ACPIBTN_POWER	2
53 #define ACPIBTN_SLEEP	3
54 };
55 
56 int	acpibtn_getsta(struct acpibtn_softc *);
57 int	acpibtn_setpsw(struct acpibtn_softc *, int);
58 
59 struct acpi_lid {
60 	struct acpibtn_softc	*abl_softc;
61 	SLIST_ENTRY(acpi_lid)	abl_link;
62 };
63 SLIST_HEAD(acpi_lid_head, acpi_lid) acpibtn_lids =
64     SLIST_HEAD_INITIALIZER(acpibtn_lids);
65 
66 struct cfattach acpibtn_ca = {
67 	sizeof(struct acpibtn_softc), acpibtn_match, acpibtn_attach
68 };
69 
70 struct cfdriver acpibtn_cd = {
71 	NULL, "acpibtn", DV_DULL
72 };
73 
74 const char *acpibtn_hids[] = { ACPI_DEV_LD, ACPI_DEV_PBD, ACPI_DEV_SBD, 0 };
75 
76 int
77 acpibtn_setpsw(struct acpibtn_softc *sc, int psw)
78 {
79 	struct aml_value	val;
80 
81 	bzero(&val, sizeof val);
82 	val.type = AML_OBJTYPE_INTEGER;
83 	val.v_integer = psw;
84 	val.length = 1;
85 
86 	return (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSW", 1, &val,
87 	    NULL));
88 }
89 
90 void
91 acpibtn_disable_psw(void)
92 {
93 	struct acpi_lid *lid;
94 
95 	/* disable _LID for wakeup */
96 	SLIST_FOREACH(lid, &acpibtn_lids, abl_link)
97 		acpibtn_setpsw(lid->abl_softc, 0);
98 }
99 
100 void
101 acpibtn_enable_psw(void)
102 {
103 	struct acpi_lid		*lid;
104 
105 	/* enable _LID for wakeup */
106 	SLIST_FOREACH(lid, &acpibtn_lids, abl_link)
107 		acpibtn_setpsw(lid->abl_softc, 1);
108 }
109 
110 int
111 acpibtn_match(struct device *parent, void *match, void *aux)
112 {
113 	struct acpi_attach_args	*aa = aux;
114 	struct cfdata		*cf = match;
115 
116 	/* sanity */
117 	return (acpi_matchhids(aa, acpibtn_hids, cf->cf_driver->cd_name));
118 }
119 
120 void
121 acpibtn_attach(struct device *parent, struct device *self, void *aux)
122 {
123 	struct acpibtn_softc	*sc = (struct acpibtn_softc *)self;
124 	struct acpi_attach_args *aa = aux;
125 	struct acpi_lid		*lid;
126 
127 	sc->sc_acpi = (struct acpi_softc *)parent;
128 	sc->sc_devnode = aa->aaa_node;
129 
130 	if (!strcmp(aa->aaa_dev, ACPI_DEV_LD)) {
131 		sc->sc_btn_type = ACPIBTN_LID;
132 		if (acpibtn_setpsw(sc, 0) == 0) {
133 			lid = malloc(sizeof(*lid), M_DEVBUF, M_WAITOK | M_ZERO);
134 			lid->abl_softc = sc;
135 			SLIST_INSERT_HEAD(&acpibtn_lids, lid, abl_link);
136 		}
137 	} else if (!strcmp(aa->aaa_dev, ACPI_DEV_PBD))
138 		sc->sc_btn_type = ACPIBTN_POWER;
139 	else if (!strcmp(aa->aaa_dev, ACPI_DEV_SBD))
140 		sc->sc_btn_type = ACPIBTN_SLEEP;
141 
142 	acpibtn_getsta(sc);
143 
144 	printf(": %s\n", sc->sc_devnode->name);
145 
146 	aml_register_notify(sc->sc_devnode, aa->aaa_dev, acpibtn_notify,
147 	    sc, ACPIDEV_NOPOLL);
148 }
149 
150 int
151 acpibtn_getsta(struct acpibtn_softc *sc)
152 {
153 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, NULL) != 0) {
154 		dnprintf(20, "%s: no _STA\n", DEVNAME(sc));
155 		/* XXX not all buttons have _STA so FALLTROUGH */
156 	}
157 
158 	return (0);
159 }
160 
161 int
162 acpibtn_notify(struct aml_node *node, int notify_type, void *arg)
163 {
164 	struct acpibtn_softc	*sc = arg;
165 #ifndef SMALL_KERNEL
166 	extern int lid_suspend;
167 	int64_t lid;
168 #endif
169 
170 	dnprintf(10, "acpibtn_notify: %.2x %s\n", notify_type,
171 	    sc->sc_devnode->name);
172 
173 	switch (sc->sc_btn_type) {
174 	case ACPIBTN_LID:
175 #ifndef SMALL_KERNEL
176 		/*
177 		 * Notification of 0x80 for lid opens or closes.  We
178 		 * need to check the current status by calling the
179 		 * _LID method.  0 means the lid is closed and we
180 		 * should go to sleep.
181 		 */
182 		if (lid_suspend == 0)
183 			break;
184 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
185 		    "_LID", 0, NULL, &lid))
186 			return (0);
187 		if (lid == 0)
188 			goto sleep;
189 #endif /* SMALL_KERNEL */
190 		break;
191 	case ACPIBTN_SLEEP:
192 #ifndef SMALL_KERNEL
193 		switch (notify_type) {
194 		case 0x02:
195 			/* "something" has been taken care of by the system */
196 			break;
197 		case 0x80:
198 sleep:
199 			/* Request to go to sleep */
200 			if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ))
201 				acpi_addtask(sc->sc_acpi, acpi_sleep_task,
202 				    sc->sc_acpi, ACPI_STATE_S3);
203 			break;
204 		}
205 #endif /* SMALL_KERNEL */
206 		break;
207 	case ACPIBTN_POWER:
208 		if (notify_type == 0x80)
209 			acpi_addtask(sc->sc_acpi, acpi_powerdown_task,
210 			    sc->sc_acpi, 0);
211 		break;
212 	default:
213 		printf("%s: spurious acpi button interrupt %i\n", DEVNAME(sc),
214 		    sc->sc_btn_type);
215 		break;
216 	}
217 
218 	return (0);
219 }
220