xref: /openbsd-src/sys/dev/acpi/acpials.c (revision c90a81c56dcebd6a1b73fe4aff9b03385b8e63b3)
1 /* $OpenBSD: acpials.c,v 1.3 2017/03/13 14:44:37 jcs Exp $ */
2 /*
3  * Ambient Light Sensor device driver
4  * ACPI 5.0 spec section 9.2
5  *
6  * Copyright (c) 2016 joshua stein <jcs@openbsd.org>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/param.h>
22 #include <sys/systm.h>
23 #include <sys/device.h>
24 
25 #include <dev/acpi/acpireg.h>
26 #include <dev/acpi/acpivar.h>
27 #include <dev/acpi/acpidev.h>
28 #include <dev/acpi/amltypes.h>
29 #include <dev/acpi/dsdt.h>
30 
31 #include <sys/sensors.h>
32 
33 /* #define ACPIALS_DEBUG */
34 
35 #ifdef ACPIALS_DEBUG
36 #define DPRINTF(x) printf x
37 #else
38 #define DPRINTF(x)
39 #endif
40 
41 struct acpials_softc {
42 	struct device		sc_dev;
43 
44 	bus_space_tag_t		sc_iot;
45 	bus_space_handle_t	sc_ioh;
46 
47 	struct acpi_softc	*sc_acpi;
48 	struct aml_node		*sc_devnode;
49 
50 	struct ksensor		sc_sensor;
51 	struct ksensordev	sc_sensordev;
52 	struct sensor_task	*sc_sensor_task;
53 };
54 
55 int	acpials_match(struct device *, void *, void *);
56 void	acpials_attach(struct device *, struct device *, void *);
57 int	acpials_read(struct acpials_softc *);
58 int	acpials_notify(struct aml_node *, int, void *);
59 void	acpials_addtask(void *);
60 void	acpials_update(void *, int);
61 
62 const struct cfattach acpials_ca = {
63 	sizeof(struct acpials_softc),
64 	acpials_match,
65 	acpials_attach,
66 };
67 
68 struct cfdriver acpials_cd = {
69 	NULL, "acpials", DV_DULL
70 };
71 
72 const char *acpials_hids[] = {
73 	"ACPI0008",
74 	NULL
75 };
76 
77 extern char *hw_vendor;
78 
79 int
80 acpials_match(struct device *parent, void *match, void *aux)
81 {
82 	struct acpi_attach_args *aa = aux;
83 	struct cfdata *cf = match;
84 
85 	/*
86 	 * Apple hardware will most likely have asmc(4) which also provides an
87 	 * illuminance sensor.
88 	 */
89 	if (hw_vendor != NULL && strncmp(hw_vendor, "Apple", 5) == 0)
90 		return 0;
91 
92 	return (acpi_matchhids(aa, acpials_hids, cf->cf_driver->cd_name));
93 }
94 
95 void
96 acpials_attach(struct device *parent, struct device *self, void *aux)
97 {
98 	struct acpials_softc *sc = (struct acpials_softc *)self;
99 	struct acpi_attach_args *aa = aux;
100 	int64_t st;
101 
102 	sc->sc_acpi = (struct acpi_softc *)parent;
103 	sc->sc_devnode = aa->aaa_node;
104 
105 	printf(": %s\n", sc->sc_devnode->name);
106 
107 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &st))
108 		st = STA_PRESENT | STA_ENABLED | STA_DEV_OK;
109 	if ((st & (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) !=
110 	    (STA_PRESENT | STA_ENABLED | STA_DEV_OK))
111 		return;
112 
113 	if (acpials_read(sc))
114 		return;
115 
116 	strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
117 	    sizeof(sc->sc_sensordev.xname));
118 	strlcpy(sc->sc_sensor.desc, "ambient light sensor",
119 	    sizeof(sc->sc_sensor.desc));
120 	sc->sc_sensor.type = SENSOR_LUX;
121 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
122 
123 	/*
124 	 * aml_register_notify with ACPIDEV_POLL is too slow (10 second
125 	 * intervals), so register the task with sensors so we can specify the
126 	 * interval, which will then just inject an acpi task and tell it to
127 	 * wakeup to handle the task.
128 	 */
129 	if (!(sc->sc_sensor_task = sensor_task_register(sc, acpials_addtask,
130 	    1))) {
131 		printf("%s: unable to register task\n", sc->sc_dev.dv_xname);
132 		return;
133 	}
134 
135 	/*
136 	 * But also install an event handler in case AML Notify()s us of any
137 	 * large changes - 9.2.7
138 	 */
139 	aml_register_notify(sc->sc_devnode, aa->aaa_dev, acpials_notify,
140 	    sc, ACPIDEV_NOPOLL);
141 
142 	sensordev_install(&sc->sc_sensordev);
143 }
144 
145 int
146 acpials_read(struct acpials_softc *sc)
147 {
148 	int64_t	ali = 0;
149 
150 	/* 9.2.2 - "Current ambient light illuminance reading in lux" */
151 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_ALI", 0, NULL,
152 	    &ali))
153 		return 1;
154 
155 	sc->sc_sensor.value = (ali * 1000000);
156 
157 	return 0;
158 }
159 
160 int
161 acpials_notify(struct aml_node *node, int notify_type, void *arg)
162 {
163 	struct acpials_softc *sc = arg;
164 
165 	DPRINTF(("%s: %s: %d\n", sc->sc_dev.dv_xname, __func__, notify_type));
166 
167 	if (notify_type == 0x80)
168 		acpials_read(sc);
169 
170 	return 0;
171 }
172 
173 void
174 acpials_addtask(void *arg)
175 {
176 	struct acpials_softc *sc = arg;
177 
178 	acpi_addtask(sc->sc_acpi, acpials_update, sc, 0);
179 	acpi_wakeup(sc->sc_acpi);
180 }
181 
182 void
183 acpials_update(void *arg0, int arg1)
184 {
185 	struct acpials_softc *sc = arg0;
186 
187 	if (acpials_read(sc) == 0) {
188 		DPRINTF(("%s: %s: %lld\n", sc->sc_dev.dv_xname, __func__,
189 		    sc->sc_sensor.value));
190 		sc->sc_sensor.flags &= ~SENSOR_FINVALID;
191 	} else
192 		sc->sc_sensor.flags |= SENSOR_FINVALID;
193 }
194