1 /* $OpenBSD: gpiokeys.c,v 1.5 2025/01/09 22:03:38 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2021 Klemens Nanni <kn@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/systm.h> 20 #include <sys/device.h> 21 #include <sys/gpio.h> 22 #include <sys/malloc.h> 23 #include <sys/queue.h> 24 25 #include <machine/bus.h> 26 #include <machine/fdt.h> 27 28 #include <dev/gpio/gpiovar.h> 29 #include <dev/ofw/ofw_gpio.h> 30 #include <dev/ofw/ofw_pinctrl.h> 31 #include <dev/ofw/openfirm.h> 32 #include <dev/ofw/fdt.h> 33 34 #include <sys/sensors.h> 35 36 #define DEVNAME(_s) ((_s)->sc_dev.dv_xname) 37 38 /* 39 * Defines from Linux, see: 40 * Documentation/input/event-codes.rst 41 * include/dt-bindings/input/linux-event-codes.h 42 */ 43 enum gpiokeys_event_type { 44 GPIOKEYS_EV_KEY = 1, 45 GPIOKEYS_EV_SW = 5, 46 }; 47 48 enum gpiokeys_switch_event { 49 GPIOKEYS_SW_LID = 0, /* set = lid closed */ 50 }; 51 52 struct gpiokeys_key { 53 uint32_t *key_pin; 54 uint32_t key_input_type; 55 uint32_t key_code; 56 struct ksensor key_sensor; 57 SLIST_ENTRY(gpiokeys_key) key_next; 58 void (*key_func)(void *); 59 void *key_ih; 60 }; 61 62 struct gpiokeys_softc { 63 struct device sc_dev; 64 struct ksensordev sc_sensordev; 65 SLIST_HEAD(, gpiokeys_key) sc_keys; 66 }; 67 68 int gpiokeys_match(struct device *, void *, void *); 69 void gpiokeys_attach(struct device *, struct device *, void *); 70 71 const struct cfattach gpiokeys_ca = { 72 sizeof (struct gpiokeys_softc), gpiokeys_match, gpiokeys_attach 73 }; 74 75 struct cfdriver gpiokeys_cd = { 76 NULL, "gpiokeys", DV_DULL 77 }; 78 79 void gpiokeys_update_key(void *); 80 int gpiokeys_intr(void *); 81 82 int 83 gpiokeys_match(struct device *parent, void *match, void *aux) 84 { 85 const struct fdt_attach_args *faa = aux; 86 87 return OF_is_compatible(faa->fa_node, "gpio-keys") || 88 OF_is_compatible(faa->fa_node, "gpio-keys-polled"); 89 } 90 91 void 92 gpiokeys_attach(struct device *parent, struct device *self, void *aux) 93 { 94 struct gpiokeys_softc *sc = (struct gpiokeys_softc *)self; 95 struct fdt_attach_args *faa = aux; 96 struct gpiokeys_key *key; 97 char *label; 98 uint32_t code; 99 int node, len, gpios_len; 100 int have_labels = 0, have_sensors = 0; 101 102 SLIST_INIT(&sc->sc_keys); 103 104 pinctrl_byname(faa->fa_node, "default"); 105 106 for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) { 107 if (OF_getprop(node, "linux,code", &code, sizeof(code)) == -1) 108 continue; 109 gpios_len = OF_getproplen(node, "gpios"); 110 if (gpios_len <= 0) 111 continue; 112 label = NULL; 113 len = OF_getproplen(node, "label"); 114 if (len > 0) { 115 label = malloc(len, M_TEMP, M_WAITOK); 116 if (OF_getprop(node, "label", label, len) != len) { 117 free(label, M_TEMP, len); 118 continue; 119 } 120 } 121 key = malloc(sizeof(*key), M_DEVBUF, M_WAITOK | M_ZERO); 122 key->key_input_type = OF_getpropint(node, "linux,input-type", 123 GPIOKEYS_EV_KEY); 124 key->key_code = code; 125 key->key_pin = malloc(gpios_len, M_DEVBUF, M_WAITOK); 126 OF_getpropintarray(node, "gpios", key->key_pin, gpios_len); 127 gpio_controller_config_pin(key->key_pin, GPIO_CONFIG_INPUT); 128 129 switch (key->key_input_type) { 130 case GPIOKEYS_EV_SW: 131 switch (key->key_code) { 132 case GPIOKEYS_SW_LID: 133 strlcpy(key->key_sensor.desc, "lid open", 134 sizeof(key->key_sensor.desc)); 135 key->key_sensor.type = SENSOR_INDICATOR; 136 sensor_attach(&sc->sc_sensordev, 137 &key->key_sensor); 138 key->key_func = gpiokeys_update_key; 139 have_sensors = 1; 140 break; 141 } 142 break; 143 } 144 145 if (label) { 146 printf("%s \"%s\"", have_labels ? "," : ":", label); 147 free(label, M_TEMP, len); 148 have_labels = 1; 149 } 150 151 SLIST_INSERT_HEAD(&sc->sc_keys, key, key_next); 152 } 153 154 SLIST_FOREACH(key, &sc->sc_keys, key_next) { 155 if (!key->key_func) 156 continue; 157 158 if (OF_is_compatible(faa->fa_node, "gpio-keys")) { 159 key->key_ih = gpio_controller_intr_establish(key->key_pin, 160 IPL_BIO, NULL, gpiokeys_intr, key, DEVNAME(sc)); 161 } 162 if (key->key_ih == NULL) 163 sensor_task_register(key, gpiokeys_update_key, 1); 164 else 165 gpiokeys_update_key(key); 166 } 167 168 if (have_sensors) { 169 strlcpy(sc->sc_sensordev.xname, DEVNAME(sc), 170 sizeof(sc->sc_sensordev.xname)); 171 sensordev_install(&sc->sc_sensordev); 172 } 173 174 if (SLIST_EMPTY(&sc->sc_keys)) 175 printf(": no keys"); 176 printf("\n"); 177 } 178 179 void 180 gpiokeys_update_key(void *arg) 181 { 182 struct gpiokeys_key *key = arg; 183 int val; 184 185 val = gpio_controller_get_pin(key->key_pin); 186 187 switch (key->key_input_type) { 188 case GPIOKEYS_EV_SW: 189 switch (key->key_code) { 190 case GPIOKEYS_SW_LID: 191 /* 192 * Match acpibtn(4), i.e. closed ThinkPad lid yields 193 * hw.sensors.acpibtn1.indicator0=Off (lid open) 194 */ 195 key->key_sensor.value = !val; 196 break; 197 } 198 break; 199 } 200 } 201 202 int 203 gpiokeys_intr(void *arg) 204 { 205 struct gpiokeys_key *key = arg; 206 207 key->key_func(key); 208 return 1; 209 } 210