1 /* $OpenBSD: gpiokeys.c,v 1.1 2021/11/09 16:16:11 kn 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) entries; 58 }; 59 60 struct gpiokeys_softc { 61 struct device sc_dev; 62 int sc_node; 63 struct ksensordev sc_sensordev; 64 SLIST_HEAD(, gpiokeys_key) sc_keys; 65 }; 66 67 int gpiokeys_match(struct device *, void *, void *); 68 void gpiokeys_attach(struct device *, struct device *, void *); 69 70 const struct cfattach gpiokeys_ca = { 71 sizeof (struct gpiokeys_softc), gpiokeys_match, gpiokeys_attach 72 }; 73 74 struct cfdriver gpiokeys_cd = { 75 NULL, "gpiokeys", DV_DULL 76 }; 77 78 void gpiokeys_update_key(void *); 79 80 int 81 gpiokeys_match(struct device *parent, void *match, void *aux) 82 { 83 const struct fdt_attach_args *faa = aux; 84 85 return OF_is_compatible(faa->fa_node, "gpio-keys") || 86 OF_is_compatible(faa->fa_node, "gpio-keys-polled"); 87 } 88 89 void 90 gpiokeys_attach(struct device *parent, struct device *self, void *aux) 91 { 92 struct gpiokeys_softc *sc = (struct gpiokeys_softc *)self; 93 struct fdt_attach_args *faa = aux; 94 struct gpiokeys_key *key; 95 char *label; 96 uint32_t code; 97 int node, len, gpios_len, have_sensors = 0; 98 99 SLIST_INIT(&sc->sc_keys); 100 101 pinctrl_byname(faa->fa_node, "default"); 102 103 for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) { 104 if (OF_getprop(node, "linux,code", &code, sizeof(code)) == -1) 105 continue; 106 gpios_len = OF_getproplen(node, "gpios"); 107 if (gpios_len <= 0) 108 continue; 109 len = OF_getproplen(node, "label"); 110 if (len <= 0) 111 continue; 112 label = malloc(len, M_TEMP, M_WAITOK); 113 if (OF_getprop(node, "label", label, len) != len) { 114 free(label, M_TEMP, len); 115 continue; 116 } 117 key = malloc(sizeof(*key), M_DEVBUF, M_WAITOK | M_ZERO); 118 key->key_input_type = OF_getpropint(node, "linux,input-type", 119 GPIOKEYS_EV_KEY); 120 key->key_code = code; 121 key->key_pin = malloc(gpios_len, M_DEVBUF, M_WAITOK); 122 OF_getpropintarray(node, "gpios", key->key_pin, gpios_len); 123 gpio_controller_config_pin(key->key_pin, GPIO_CONFIG_INPUT); 124 125 switch (key->key_input_type) { 126 case GPIOKEYS_EV_SW: 127 switch (key->key_code) { 128 case GPIOKEYS_SW_LID: 129 strlcpy(key->key_sensor.desc, "lid open", 130 sizeof(key->key_sensor.desc)); 131 key->key_sensor.type = SENSOR_INDICATOR; 132 sensor_attach(&sc->sc_sensordev, &key->key_sensor); 133 sensor_task_register(key, gpiokeys_update_key, 1); 134 have_sensors = 1; 135 break; 136 } 137 break; 138 } 139 140 printf("%s \"%s\"", SLIST_EMPTY(&sc->sc_keys) ? ":" : ",", 141 label); 142 free(label, M_TEMP, len); 143 144 SLIST_INSERT_HEAD(&sc->sc_keys, key, entries); 145 } 146 147 if (have_sensors) { 148 strlcpy(sc->sc_sensordev.xname, DEVNAME(sc), 149 sizeof(sc->sc_sensordev.xname)); 150 sensordev_install(&sc->sc_sensordev); 151 } 152 153 if (SLIST_EMPTY(&sc->sc_keys)) 154 printf(": no keys"); 155 printf("\n"); 156 } 157 158 void 159 gpiokeys_update_key(void *arg) 160 { 161 struct gpiokeys_key *key = arg; 162 int val; 163 164 val = gpio_controller_get_pin(key->key_pin); 165 166 switch (key->key_input_type) { 167 case GPIOKEYS_EV_SW: 168 switch (key->key_code) { 169 case GPIOKEYS_SW_LID: 170 /* 171 * Match acpibtn(4), i.e. closed ThinkPad lid yields 172 * hw.sensors.acpibtn1.indicator0=Off (lid open) 173 */ 174 key->key_sensor.value = !val; 175 break; 176 } 177 break; 178 } 179 } 180