1*85fcfc35Svisa /* $OpenBSD: acpials.c,v 1.4 2020/08/26 03:29:06 visa Exp $ */
250e2d8d7Sjcs /*
350e2d8d7Sjcs * Ambient Light Sensor device driver
450e2d8d7Sjcs * ACPI 5.0 spec section 9.2
550e2d8d7Sjcs *
650e2d8d7Sjcs * Copyright (c) 2016 joshua stein <jcs@openbsd.org>
750e2d8d7Sjcs *
850e2d8d7Sjcs * Permission to use, copy, modify, and distribute this software for any
950e2d8d7Sjcs * purpose with or without fee is hereby granted, provided that the above
1050e2d8d7Sjcs * copyright notice and this permission notice appear in all copies.
1150e2d8d7Sjcs *
1250e2d8d7Sjcs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1350e2d8d7Sjcs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1450e2d8d7Sjcs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1550e2d8d7Sjcs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1650e2d8d7Sjcs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1750e2d8d7Sjcs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1850e2d8d7Sjcs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1950e2d8d7Sjcs */
2050e2d8d7Sjcs
2150e2d8d7Sjcs #include <sys/param.h>
2250e2d8d7Sjcs #include <sys/systm.h>
2350e2d8d7Sjcs #include <sys/device.h>
2450e2d8d7Sjcs
2550e2d8d7Sjcs #include <dev/acpi/acpireg.h>
2650e2d8d7Sjcs #include <dev/acpi/acpivar.h>
2750e2d8d7Sjcs #include <dev/acpi/acpidev.h>
2850e2d8d7Sjcs #include <dev/acpi/amltypes.h>
2950e2d8d7Sjcs #include <dev/acpi/dsdt.h>
3050e2d8d7Sjcs
3150e2d8d7Sjcs #include <sys/sensors.h>
3250e2d8d7Sjcs
3350e2d8d7Sjcs /* #define ACPIALS_DEBUG */
3450e2d8d7Sjcs
3550e2d8d7Sjcs #ifdef ACPIALS_DEBUG
3650e2d8d7Sjcs #define DPRINTF(x) printf x
3750e2d8d7Sjcs #else
3850e2d8d7Sjcs #define DPRINTF(x)
3950e2d8d7Sjcs #endif
4050e2d8d7Sjcs
4150e2d8d7Sjcs struct acpials_softc {
4250e2d8d7Sjcs struct device sc_dev;
4350e2d8d7Sjcs
4450e2d8d7Sjcs bus_space_tag_t sc_iot;
4550e2d8d7Sjcs bus_space_handle_t sc_ioh;
4650e2d8d7Sjcs
4750e2d8d7Sjcs struct acpi_softc *sc_acpi;
4850e2d8d7Sjcs struct aml_node *sc_devnode;
4950e2d8d7Sjcs
5050e2d8d7Sjcs struct ksensor sc_sensor;
5150e2d8d7Sjcs struct ksensordev sc_sensordev;
5250e2d8d7Sjcs struct sensor_task *sc_sensor_task;
5350e2d8d7Sjcs };
5450e2d8d7Sjcs
5550e2d8d7Sjcs int acpials_match(struct device *, void *, void *);
5650e2d8d7Sjcs void acpials_attach(struct device *, struct device *, void *);
5750e2d8d7Sjcs int acpials_read(struct acpials_softc *);
5850e2d8d7Sjcs int acpials_notify(struct aml_node *, int, void *);
5950e2d8d7Sjcs void acpials_addtask(void *);
6050e2d8d7Sjcs void acpials_update(void *, int);
6150e2d8d7Sjcs
62d24e5186Sjcs const struct cfattach acpials_ca = {
6350e2d8d7Sjcs sizeof(struct acpials_softc),
6450e2d8d7Sjcs acpials_match,
6550e2d8d7Sjcs acpials_attach,
6650e2d8d7Sjcs };
6750e2d8d7Sjcs
6850e2d8d7Sjcs struct cfdriver acpials_cd = {
6950e2d8d7Sjcs NULL, "acpials", DV_DULL
7050e2d8d7Sjcs };
7150e2d8d7Sjcs
7250e2d8d7Sjcs const char *acpials_hids[] = {
7350e2d8d7Sjcs "ACPI0008",
748816acbcSkettenis NULL
7550e2d8d7Sjcs };
7650e2d8d7Sjcs
7750e2d8d7Sjcs int
acpials_match(struct device * parent,void * match,void * aux)7850e2d8d7Sjcs acpials_match(struct device *parent, void *match, void *aux)
7950e2d8d7Sjcs {
8050e2d8d7Sjcs struct acpi_attach_args *aa = aux;
8150e2d8d7Sjcs struct cfdata *cf = match;
8250e2d8d7Sjcs
83d24e5186Sjcs /*
84d24e5186Sjcs * Apple hardware will most likely have asmc(4) which also provides an
85d24e5186Sjcs * illuminance sensor.
86d24e5186Sjcs */
87d24e5186Sjcs if (hw_vendor != NULL && strncmp(hw_vendor, "Apple", 5) == 0)
88d24e5186Sjcs return 0;
89d24e5186Sjcs
9050e2d8d7Sjcs return (acpi_matchhids(aa, acpials_hids, cf->cf_driver->cd_name));
9150e2d8d7Sjcs }
9250e2d8d7Sjcs
9350e2d8d7Sjcs void
acpials_attach(struct device * parent,struct device * self,void * aux)9450e2d8d7Sjcs acpials_attach(struct device *parent, struct device *self, void *aux)
9550e2d8d7Sjcs {
9650e2d8d7Sjcs struct acpials_softc *sc = (struct acpials_softc *)self;
9750e2d8d7Sjcs struct acpi_attach_args *aa = aux;
9850e2d8d7Sjcs int64_t st;
9950e2d8d7Sjcs
10050e2d8d7Sjcs sc->sc_acpi = (struct acpi_softc *)parent;
10150e2d8d7Sjcs sc->sc_devnode = aa->aaa_node;
10250e2d8d7Sjcs
10350e2d8d7Sjcs printf(": %s\n", sc->sc_devnode->name);
10450e2d8d7Sjcs
10550e2d8d7Sjcs if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &st))
10650e2d8d7Sjcs st = STA_PRESENT | STA_ENABLED | STA_DEV_OK;
10750e2d8d7Sjcs if ((st & (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) !=
10850e2d8d7Sjcs (STA_PRESENT | STA_ENABLED | STA_DEV_OK))
10950e2d8d7Sjcs return;
11050e2d8d7Sjcs
11150e2d8d7Sjcs if (acpials_read(sc))
11250e2d8d7Sjcs return;
11350e2d8d7Sjcs
11450e2d8d7Sjcs strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
11550e2d8d7Sjcs sizeof(sc->sc_sensordev.xname));
11650e2d8d7Sjcs strlcpy(sc->sc_sensor.desc, "ambient light sensor",
11750e2d8d7Sjcs sizeof(sc->sc_sensor.desc));
11850e2d8d7Sjcs sc->sc_sensor.type = SENSOR_LUX;
11950e2d8d7Sjcs sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
12050e2d8d7Sjcs
12150e2d8d7Sjcs /*
12250e2d8d7Sjcs * aml_register_notify with ACPIDEV_POLL is too slow (10 second
12350e2d8d7Sjcs * intervals), so register the task with sensors so we can specify the
12450e2d8d7Sjcs * interval, which will then just inject an acpi task and tell it to
12550e2d8d7Sjcs * wakeup to handle the task.
12650e2d8d7Sjcs */
12750e2d8d7Sjcs if (!(sc->sc_sensor_task = sensor_task_register(sc, acpials_addtask,
12850e2d8d7Sjcs 1))) {
12950e2d8d7Sjcs printf("%s: unable to register task\n", sc->sc_dev.dv_xname);
13050e2d8d7Sjcs return;
13150e2d8d7Sjcs }
13250e2d8d7Sjcs
13350e2d8d7Sjcs /*
13450e2d8d7Sjcs * But also install an event handler in case AML Notify()s us of any
13550e2d8d7Sjcs * large changes - 9.2.7
13650e2d8d7Sjcs */
13750e2d8d7Sjcs aml_register_notify(sc->sc_devnode, aa->aaa_dev, acpials_notify,
13850e2d8d7Sjcs sc, ACPIDEV_NOPOLL);
13950e2d8d7Sjcs
14050e2d8d7Sjcs sensordev_install(&sc->sc_sensordev);
14150e2d8d7Sjcs }
14250e2d8d7Sjcs
14350e2d8d7Sjcs int
acpials_read(struct acpials_softc * sc)14450e2d8d7Sjcs acpials_read(struct acpials_softc *sc)
14550e2d8d7Sjcs {
14650e2d8d7Sjcs int64_t ali = 0;
14750e2d8d7Sjcs
14850e2d8d7Sjcs /* 9.2.2 - "Current ambient light illuminance reading in lux" */
14950e2d8d7Sjcs if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_ALI", 0, NULL,
15050e2d8d7Sjcs &ali))
15150e2d8d7Sjcs return 1;
15250e2d8d7Sjcs
15350e2d8d7Sjcs sc->sc_sensor.value = (ali * 1000000);
15450e2d8d7Sjcs
15550e2d8d7Sjcs return 0;
15650e2d8d7Sjcs }
15750e2d8d7Sjcs
15850e2d8d7Sjcs int
acpials_notify(struct aml_node * node,int notify_type,void * arg)15950e2d8d7Sjcs acpials_notify(struct aml_node *node, int notify_type, void *arg)
16050e2d8d7Sjcs {
16150e2d8d7Sjcs struct acpials_softc *sc = arg;
16250e2d8d7Sjcs
16350e2d8d7Sjcs DPRINTF(("%s: %s: %d\n", sc->sc_dev.dv_xname, __func__, notify_type));
16450e2d8d7Sjcs
16550e2d8d7Sjcs if (notify_type == 0x80)
16650e2d8d7Sjcs acpials_read(sc);
16750e2d8d7Sjcs
16850e2d8d7Sjcs return 0;
16950e2d8d7Sjcs }
17050e2d8d7Sjcs
17150e2d8d7Sjcs void
acpials_addtask(void * arg)17250e2d8d7Sjcs acpials_addtask(void *arg)
17350e2d8d7Sjcs {
17450e2d8d7Sjcs struct acpials_softc *sc = arg;
17550e2d8d7Sjcs
17650e2d8d7Sjcs acpi_addtask(sc->sc_acpi, acpials_update, sc, 0);
17750e2d8d7Sjcs acpi_wakeup(sc->sc_acpi);
17850e2d8d7Sjcs }
17950e2d8d7Sjcs
18050e2d8d7Sjcs void
acpials_update(void * arg0,int arg1)18150e2d8d7Sjcs acpials_update(void *arg0, int arg1)
18250e2d8d7Sjcs {
18350e2d8d7Sjcs struct acpials_softc *sc = arg0;
18450e2d8d7Sjcs
18550e2d8d7Sjcs if (acpials_read(sc) == 0) {
18650e2d8d7Sjcs DPRINTF(("%s: %s: %lld\n", sc->sc_dev.dv_xname, __func__,
18750e2d8d7Sjcs sc->sc_sensor.value));
18850e2d8d7Sjcs sc->sc_sensor.flags &= ~SENSOR_FINVALID;
18950e2d8d7Sjcs } else
19050e2d8d7Sjcs sc->sc_sensor.flags |= SENSOR_FINVALID;
19150e2d8d7Sjcs }
192