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
gpiokeys_match(struct device * parent,void * match,void * aux)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
gpiokeys_attach(struct device * parent,struct device * self,void * aux)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
gpiokeys_update_key(void * arg)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
gpiokeys_intr(void * arg)203 gpiokeys_intr(void *arg)
204 {
205 struct gpiokeys_key *key = arg;
206
207 key->key_func(key);
208 return 1;
209 }
210