xref: /openbsd-src/sys/dev/fdt/gpiokeys.c (revision abf7a02e91576a0c9c13dc71b770fe3c0710b077)
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