1*abf7a02eSkettenis /* $OpenBSD: gpiokeys.c,v 1.5 2025/01/09 22:03:38 kettenis Exp $ */ 2eb27f05fSkn /* 3eb27f05fSkn * Copyright (c) 2021 Klemens Nanni <kn@openbsd.org> 4eb27f05fSkn * 5eb27f05fSkn * Permission to use, copy, modify, and distribute this software for any 6eb27f05fSkn * purpose with or without fee is hereby granted, provided that the above 7eb27f05fSkn * copyright notice and this permission notice appear in all copies. 8eb27f05fSkn * 9eb27f05fSkn * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10eb27f05fSkn * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11eb27f05fSkn * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12eb27f05fSkn * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13eb27f05fSkn * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14eb27f05fSkn * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15eb27f05fSkn * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16eb27f05fSkn */ 17eb27f05fSkn 18eb27f05fSkn #include <sys/param.h> 19eb27f05fSkn #include <sys/systm.h> 20eb27f05fSkn #include <sys/device.h> 21eb27f05fSkn #include <sys/gpio.h> 22eb27f05fSkn #include <sys/malloc.h> 23eb27f05fSkn #include <sys/queue.h> 24eb27f05fSkn 25eb27f05fSkn #include <machine/bus.h> 26eb27f05fSkn #include <machine/fdt.h> 27eb27f05fSkn 28eb27f05fSkn #include <dev/gpio/gpiovar.h> 29eb27f05fSkn #include <dev/ofw/ofw_gpio.h> 30eb27f05fSkn #include <dev/ofw/ofw_pinctrl.h> 31eb27f05fSkn #include <dev/ofw/openfirm.h> 32eb27f05fSkn #include <dev/ofw/fdt.h> 33eb27f05fSkn 34eb27f05fSkn #include <sys/sensors.h> 35eb27f05fSkn 36eb27f05fSkn #define DEVNAME(_s) ((_s)->sc_dev.dv_xname) 37eb27f05fSkn 38eb27f05fSkn /* 39eb27f05fSkn * Defines from Linux, see: 40eb27f05fSkn * Documentation/input/event-codes.rst 41eb27f05fSkn * include/dt-bindings/input/linux-event-codes.h 42eb27f05fSkn */ 43eb27f05fSkn enum gpiokeys_event_type { 44eb27f05fSkn GPIOKEYS_EV_KEY = 1, 45eb27f05fSkn GPIOKEYS_EV_SW = 5, 46eb27f05fSkn }; 47eb27f05fSkn 48eb27f05fSkn enum gpiokeys_switch_event { 49eb27f05fSkn GPIOKEYS_SW_LID = 0, /* set = lid closed */ 50eb27f05fSkn }; 51eb27f05fSkn 52eb27f05fSkn struct gpiokeys_key { 53eb27f05fSkn uint32_t *key_pin; 54eb27f05fSkn uint32_t key_input_type; 55eb27f05fSkn uint32_t key_code; 56eb27f05fSkn struct ksensor key_sensor; 5763ecee53Skettenis SLIST_ENTRY(gpiokeys_key) key_next; 58*abf7a02eSkettenis void (*key_func)(void *); 59*abf7a02eSkettenis void *key_ih; 60eb27f05fSkn }; 61eb27f05fSkn 62eb27f05fSkn struct gpiokeys_softc { 63eb27f05fSkn struct device sc_dev; 64eb27f05fSkn struct ksensordev sc_sensordev; 65eb27f05fSkn SLIST_HEAD(, gpiokeys_key) sc_keys; 66eb27f05fSkn }; 67eb27f05fSkn 68eb27f05fSkn int gpiokeys_match(struct device *, void *, void *); 69eb27f05fSkn void gpiokeys_attach(struct device *, struct device *, void *); 70eb27f05fSkn 71eb27f05fSkn const struct cfattach gpiokeys_ca = { 72eb27f05fSkn sizeof (struct gpiokeys_softc), gpiokeys_match, gpiokeys_attach 73eb27f05fSkn }; 74eb27f05fSkn 75eb27f05fSkn struct cfdriver gpiokeys_cd = { 76eb27f05fSkn NULL, "gpiokeys", DV_DULL 77eb27f05fSkn }; 78eb27f05fSkn 79eb27f05fSkn void gpiokeys_update_key(void *); 80*abf7a02eSkettenis int gpiokeys_intr(void *); 81eb27f05fSkn 82eb27f05fSkn int 83eb27f05fSkn gpiokeys_match(struct device *parent, void *match, void *aux) 84eb27f05fSkn { 85eb27f05fSkn const struct fdt_attach_args *faa = aux; 86eb27f05fSkn 87eb27f05fSkn return OF_is_compatible(faa->fa_node, "gpio-keys") || 88eb27f05fSkn OF_is_compatible(faa->fa_node, "gpio-keys-polled"); 89eb27f05fSkn } 90eb27f05fSkn 91eb27f05fSkn void 92eb27f05fSkn gpiokeys_attach(struct device *parent, struct device *self, void *aux) 93eb27f05fSkn { 94eb27f05fSkn struct gpiokeys_softc *sc = (struct gpiokeys_softc *)self; 95eb27f05fSkn struct fdt_attach_args *faa = aux; 96eb27f05fSkn struct gpiokeys_key *key; 97eb27f05fSkn char *label; 98eb27f05fSkn uint32_t code; 994303d0d5Spatrick int node, len, gpios_len; 1004303d0d5Spatrick int have_labels = 0, have_sensors = 0; 101eb27f05fSkn 102eb27f05fSkn SLIST_INIT(&sc->sc_keys); 103eb27f05fSkn 104eb27f05fSkn pinctrl_byname(faa->fa_node, "default"); 105eb27f05fSkn 106eb27f05fSkn for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) { 107eb27f05fSkn if (OF_getprop(node, "linux,code", &code, sizeof(code)) == -1) 108eb27f05fSkn continue; 109eb27f05fSkn gpios_len = OF_getproplen(node, "gpios"); 110eb27f05fSkn if (gpios_len <= 0) 111eb27f05fSkn continue; 1124303d0d5Spatrick label = NULL; 113eb27f05fSkn len = OF_getproplen(node, "label"); 1144303d0d5Spatrick if (len > 0) { 115eb27f05fSkn label = malloc(len, M_TEMP, M_WAITOK); 116eb27f05fSkn if (OF_getprop(node, "label", label, len) != len) { 117eb27f05fSkn free(label, M_TEMP, len); 118eb27f05fSkn continue; 119eb27f05fSkn } 1204303d0d5Spatrick } 121eb27f05fSkn key = malloc(sizeof(*key), M_DEVBUF, M_WAITOK | M_ZERO); 122eb27f05fSkn key->key_input_type = OF_getpropint(node, "linux,input-type", 123eb27f05fSkn GPIOKEYS_EV_KEY); 124eb27f05fSkn key->key_code = code; 125eb27f05fSkn key->key_pin = malloc(gpios_len, M_DEVBUF, M_WAITOK); 126eb27f05fSkn OF_getpropintarray(node, "gpios", key->key_pin, gpios_len); 127eb27f05fSkn gpio_controller_config_pin(key->key_pin, GPIO_CONFIG_INPUT); 128eb27f05fSkn 129eb27f05fSkn switch (key->key_input_type) { 130eb27f05fSkn case GPIOKEYS_EV_SW: 131eb27f05fSkn switch (key->key_code) { 132eb27f05fSkn case GPIOKEYS_SW_LID: 133eb27f05fSkn strlcpy(key->key_sensor.desc, "lid open", 134eb27f05fSkn sizeof(key->key_sensor.desc)); 135eb27f05fSkn key->key_sensor.type = SENSOR_INDICATOR; 13663ecee53Skettenis sensor_attach(&sc->sc_sensordev, 13763ecee53Skettenis &key->key_sensor); 138*abf7a02eSkettenis key->key_func = gpiokeys_update_key; 139eb27f05fSkn have_sensors = 1; 140eb27f05fSkn break; 141eb27f05fSkn } 142eb27f05fSkn break; 143eb27f05fSkn } 144eb27f05fSkn 1454303d0d5Spatrick if (label) { 146bbaa01a8Skn printf("%s \"%s\"", have_labels ? "," : ":", label); 147eb27f05fSkn free(label, M_TEMP, len); 1484303d0d5Spatrick have_labels = 1; 1494303d0d5Spatrick } 150eb27f05fSkn 15163ecee53Skettenis SLIST_INSERT_HEAD(&sc->sc_keys, key, key_next); 152eb27f05fSkn } 153eb27f05fSkn 154*abf7a02eSkettenis SLIST_FOREACH(key, &sc->sc_keys, key_next) { 155*abf7a02eSkettenis if (!key->key_func) 156*abf7a02eSkettenis continue; 157*abf7a02eSkettenis 158*abf7a02eSkettenis if (OF_is_compatible(faa->fa_node, "gpio-keys")) { 159*abf7a02eSkettenis key->key_ih = gpio_controller_intr_establish(key->key_pin, 160*abf7a02eSkettenis IPL_BIO, NULL, gpiokeys_intr, key, DEVNAME(sc)); 161*abf7a02eSkettenis } 162*abf7a02eSkettenis if (key->key_ih == NULL) 163*abf7a02eSkettenis sensor_task_register(key, gpiokeys_update_key, 1); 164*abf7a02eSkettenis else 165*abf7a02eSkettenis gpiokeys_update_key(key); 166*abf7a02eSkettenis } 167*abf7a02eSkettenis 168eb27f05fSkn if (have_sensors) { 169eb27f05fSkn strlcpy(sc->sc_sensordev.xname, DEVNAME(sc), 170eb27f05fSkn sizeof(sc->sc_sensordev.xname)); 171eb27f05fSkn sensordev_install(&sc->sc_sensordev); 172eb27f05fSkn } 173eb27f05fSkn 174eb27f05fSkn if (SLIST_EMPTY(&sc->sc_keys)) 175eb27f05fSkn printf(": no keys"); 176eb27f05fSkn printf("\n"); 177eb27f05fSkn } 178eb27f05fSkn 179eb27f05fSkn void 180eb27f05fSkn gpiokeys_update_key(void *arg) 181eb27f05fSkn { 182eb27f05fSkn struct gpiokeys_key *key = arg; 183eb27f05fSkn int val; 184eb27f05fSkn 185eb27f05fSkn val = gpio_controller_get_pin(key->key_pin); 186eb27f05fSkn 187eb27f05fSkn switch (key->key_input_type) { 188eb27f05fSkn case GPIOKEYS_EV_SW: 189eb27f05fSkn switch (key->key_code) { 190eb27f05fSkn case GPIOKEYS_SW_LID: 191eb27f05fSkn /* 192eb27f05fSkn * Match acpibtn(4), i.e. closed ThinkPad lid yields 193eb27f05fSkn * hw.sensors.acpibtn1.indicator0=Off (lid open) 194eb27f05fSkn */ 195eb27f05fSkn key->key_sensor.value = !val; 196eb27f05fSkn break; 197eb27f05fSkn } 198eb27f05fSkn break; 199eb27f05fSkn } 200eb27f05fSkn } 201*abf7a02eSkettenis 202*abf7a02eSkettenis int 203*abf7a02eSkettenis gpiokeys_intr(void *arg) 204*abf7a02eSkettenis { 205*abf7a02eSkettenis struct gpiokeys_key *key = arg; 206*abf7a02eSkettenis 207*abf7a02eSkettenis key->key_func(key); 208*abf7a02eSkettenis return 1; 209*abf7a02eSkettenis } 210