1/* $OpenBSD: gpiokeys.c,v 1.3 2023/03/31 12:07:54 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 */ 43enum gpiokeys_event_type { 44 GPIOKEYS_EV_KEY = 1, 45 GPIOKEYS_EV_SW = 5, 46}; 47 48enum gpiokeys_switch_event { 49 GPIOKEYS_SW_LID = 0, /* set = lid closed */ 50}; 51 52struct 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 60struct 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 67int gpiokeys_match(struct device *, void *, void *); 68void gpiokeys_attach(struct device *, struct device *, void *); 69 70const struct cfattach gpiokeys_ca = { 71 sizeof (struct gpiokeys_softc), gpiokeys_match, gpiokeys_attach 72}; 73 74struct cfdriver gpiokeys_cd = { 75 NULL, "gpiokeys", DV_DULL 76}; 77 78void gpiokeys_update_key(void *); 79 80int 81gpiokeys_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 89void 90gpiokeys_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; 98 int have_labels = 0, have_sensors = 0; 99 100 SLIST_INIT(&sc->sc_keys); 101 102 pinctrl_byname(faa->fa_node, "default"); 103 104 for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) { 105 if (OF_getprop(node, "linux,code", &code, sizeof(code)) == -1) 106 continue; 107 gpios_len = OF_getproplen(node, "gpios"); 108 if (gpios_len <= 0) 109 continue; 110 label = NULL; 111 len = OF_getproplen(node, "label"); 112 if (len > 0) { 113 label = malloc(len, M_TEMP, M_WAITOK); 114 if (OF_getprop(node, "label", label, len) != len) { 115 free(label, M_TEMP, len); 116 continue; 117 } 118 } 119 key = malloc(sizeof(*key), M_DEVBUF, M_WAITOK | M_ZERO); 120 key->key_input_type = OF_getpropint(node, "linux,input-type", 121 GPIOKEYS_EV_KEY); 122 key->key_code = code; 123 key->key_pin = malloc(gpios_len, M_DEVBUF, M_WAITOK); 124 OF_getpropintarray(node, "gpios", key->key_pin, gpios_len); 125 gpio_controller_config_pin(key->key_pin, GPIO_CONFIG_INPUT); 126 127 switch (key->key_input_type) { 128 case GPIOKEYS_EV_SW: 129 switch (key->key_code) { 130 case GPIOKEYS_SW_LID: 131 strlcpy(key->key_sensor.desc, "lid open", 132 sizeof(key->key_sensor.desc)); 133 key->key_sensor.type = SENSOR_INDICATOR; 134 sensor_attach(&sc->sc_sensordev, &key->key_sensor); 135 sensor_task_register(key, gpiokeys_update_key, 1); 136 have_sensors = 1; 137 break; 138 } 139 break; 140 } 141 142 if (label) { 143 printf("%s \"%s\"", have_labels ? "," : ":", label); 144 free(label, M_TEMP, len); 145 have_labels = 1; 146 } 147 148 SLIST_INSERT_HEAD(&sc->sc_keys, key, entries); 149 } 150 151 if (have_sensors) { 152 strlcpy(sc->sc_sensordev.xname, DEVNAME(sc), 153 sizeof(sc->sc_sensordev.xname)); 154 sensordev_install(&sc->sc_sensordev); 155 } 156 157 if (SLIST_EMPTY(&sc->sc_keys)) 158 printf(": no keys"); 159 printf("\n"); 160} 161 162void 163gpiokeys_update_key(void *arg) 164{ 165 struct gpiokeys_key *key = arg; 166 int val; 167 168 val = gpio_controller_get_pin(key->key_pin); 169 170 switch (key->key_input_type) { 171 case GPIOKEYS_EV_SW: 172 switch (key->key_code) { 173 case GPIOKEYS_SW_LID: 174 /* 175 * Match acpibtn(4), i.e. closed ThinkPad lid yields 176 * hw.sensors.acpibtn1.indicator0=Off (lid open) 177 */ 178 key->key_sensor.value = !val; 179 break; 180 } 181 break; 182 } 183} 184