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