1 /* $OpenBSD: acpials.c,v 1.4 2020/08/26 03:29:06 visa 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 int
acpials_match(struct device * parent,void * match,void * aux)78 acpials_match(struct device *parent, void *match, void *aux)
79 {
80 struct acpi_attach_args *aa = aux;
81 struct cfdata *cf = match;
82
83 /*
84 * Apple hardware will most likely have asmc(4) which also provides an
85 * illuminance sensor.
86 */
87 if (hw_vendor != NULL && strncmp(hw_vendor, "Apple", 5) == 0)
88 return 0;
89
90 return (acpi_matchhids(aa, acpials_hids, cf->cf_driver->cd_name));
91 }
92
93 void
acpials_attach(struct device * parent,struct device * self,void * aux)94 acpials_attach(struct device *parent, struct device *self, void *aux)
95 {
96 struct acpials_softc *sc = (struct acpials_softc *)self;
97 struct acpi_attach_args *aa = aux;
98 int64_t st;
99
100 sc->sc_acpi = (struct acpi_softc *)parent;
101 sc->sc_devnode = aa->aaa_node;
102
103 printf(": %s\n", sc->sc_devnode->name);
104
105 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &st))
106 st = STA_PRESENT | STA_ENABLED | STA_DEV_OK;
107 if ((st & (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) !=
108 (STA_PRESENT | STA_ENABLED | STA_DEV_OK))
109 return;
110
111 if (acpials_read(sc))
112 return;
113
114 strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
115 sizeof(sc->sc_sensordev.xname));
116 strlcpy(sc->sc_sensor.desc, "ambient light sensor",
117 sizeof(sc->sc_sensor.desc));
118 sc->sc_sensor.type = SENSOR_LUX;
119 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
120
121 /*
122 * aml_register_notify with ACPIDEV_POLL is too slow (10 second
123 * intervals), so register the task with sensors so we can specify the
124 * interval, which will then just inject an acpi task and tell it to
125 * wakeup to handle the task.
126 */
127 if (!(sc->sc_sensor_task = sensor_task_register(sc, acpials_addtask,
128 1))) {
129 printf("%s: unable to register task\n", sc->sc_dev.dv_xname);
130 return;
131 }
132
133 /*
134 * But also install an event handler in case AML Notify()s us of any
135 * large changes - 9.2.7
136 */
137 aml_register_notify(sc->sc_devnode, aa->aaa_dev, acpials_notify,
138 sc, ACPIDEV_NOPOLL);
139
140 sensordev_install(&sc->sc_sensordev);
141 }
142
143 int
acpials_read(struct acpials_softc * sc)144 acpials_read(struct acpials_softc *sc)
145 {
146 int64_t ali = 0;
147
148 /* 9.2.2 - "Current ambient light illuminance reading in lux" */
149 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_ALI", 0, NULL,
150 &ali))
151 return 1;
152
153 sc->sc_sensor.value = (ali * 1000000);
154
155 return 0;
156 }
157
158 int
acpials_notify(struct aml_node * node,int notify_type,void * arg)159 acpials_notify(struct aml_node *node, int notify_type, void *arg)
160 {
161 struct acpials_softc *sc = arg;
162
163 DPRINTF(("%s: %s: %d\n", sc->sc_dev.dv_xname, __func__, notify_type));
164
165 if (notify_type == 0x80)
166 acpials_read(sc);
167
168 return 0;
169 }
170
171 void
acpials_addtask(void * arg)172 acpials_addtask(void *arg)
173 {
174 struct acpials_softc *sc = arg;
175
176 acpi_addtask(sc->sc_acpi, acpials_update, sc, 0);
177 acpi_wakeup(sc->sc_acpi);
178 }
179
180 void
acpials_update(void * arg0,int arg1)181 acpials_update(void *arg0, int arg1)
182 {
183 struct acpials_softc *sc = arg0;
184
185 if (acpials_read(sc) == 0) {
186 DPRINTF(("%s: %s: %lld\n", sc->sc_dev.dv_xname, __func__,
187 sc->sc_sensor.value));
188 sc->sc_sensor.flags &= ~SENSOR_FINVALID;
189 } else
190 sc->sc_sensor.flags |= SENSOR_FINVALID;
191 }
192