1 /* $NetBSD: gpiokeys.c,v 1.2 2015/12/16 19:33:55 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: gpiokeys.c,v 1.2 2015/12/16 19:33:55 jmcneill Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/kernel.h> 34 #include <sys/systm.h> 35 #include <sys/device.h> 36 #include <sys/kmem.h> 37 #include <sys/bus.h> 38 #include <sys/gpio.h> 39 40 #include <dev/sysmon/sysmonvar.h> 41 #include <dev/sysmon/sysmon_taskq.h> 42 43 #include <dev/fdt/fdtvar.h> 44 45 #define GPIOKEYS_POLL_INTERVAL mstohz(200) 46 47 #define KEY_POWER 116 48 #define KEY_SLEEP 142 49 50 static int gpiokeys_match(device_t, cfdata_t, void *); 51 static void gpiokeys_attach(device_t, device_t, void *); 52 53 static void gpiokeys_tick(void *); 54 static void gpiokeys_task(void *); 55 56 struct gpiokeys_softc; 57 58 struct gpiokeys_key { 59 int key_phandle; 60 char *key_label; 61 struct fdtbus_gpio_pin *key_pin; 62 u_int key_debounce; 63 u_int key_code; 64 struct sysmon_pswitch key_pswitch; 65 u_int key_state; 66 67 struct gpiokeys_key *key_next; 68 }; 69 70 struct gpiokeys_softc { 71 device_t sc_dev; 72 int sc_phandle; 73 74 struct fdtbus_gpio_pin *sc_pin; 75 bool sc_always_on; 76 bool sc_enable_val; 77 78 struct gpiokeys_key *sc_keys; 79 callout_t sc_tick; 80 }; 81 82 CFATTACH_DECL_NEW(gpiokeys, sizeof(struct gpiokeys_softc), 83 gpiokeys_match, gpiokeys_attach, NULL, NULL); 84 85 static int 86 gpiokeys_match(device_t parent, cfdata_t cf, void *aux) 87 { 88 const char * const compatible[] = { "gpio-keys", NULL }; 89 const struct fdt_attach_args *faa = aux; 90 91 return of_match_compatible(faa->faa_phandle, compatible); 92 } 93 94 static void 95 gpiokeys_attach(device_t parent, device_t self, void *aux) 96 { 97 struct gpiokeys_softc * const sc = device_private(self); 98 const struct fdt_attach_args *faa = aux; 99 const int phandle = faa->faa_phandle; 100 struct gpiokeys_key *key; 101 int child, len; 102 u_int debounce, code; 103 104 sc->sc_dev = self; 105 sc->sc_phandle = phandle; 106 107 aprint_naive("\n"); 108 aprint_normal(":"); 109 110 for (child = OF_child(phandle); child; child = OF_peer(child)) { 111 if (of_getprop_uint32(child, "linux,code", &code)) 112 continue; 113 if (of_getprop_uint32(child, "debounce-interval", &debounce)) 114 debounce = 5; /* default */ 115 len = OF_getproplen(child, "label"); 116 if (len <= 0) { 117 continue; 118 } 119 key = kmem_zalloc(sizeof(*key), KM_SLEEP); 120 key->key_phandle = child; 121 key->key_code = code; 122 key->key_label = kmem_zalloc(len, KM_SLEEP); 123 if (OF_getprop(child, "label", key->key_label, len) != len) { 124 kmem_free(key->key_label, len); 125 kmem_free(key, sizeof(*key)); 126 continue; 127 } 128 key->key_debounce = debounce; 129 key->key_pin = fdtbus_gpio_acquire(child, "gpios", 130 GPIO_PIN_INPUT); 131 132 key->key_pswitch.smpsw_name = key->key_label; 133 switch (code) { 134 case KEY_POWER: 135 key->key_pswitch.smpsw_type = PSWITCH_TYPE_POWER; 136 break; 137 case KEY_SLEEP: 138 key->key_pswitch.smpsw_type = PSWITCH_TYPE_SLEEP; 139 break; 140 default: 141 key->key_pswitch.smpsw_type = PSWITCH_TYPE_HOTKEY; 142 break; 143 } 144 145 if (sysmon_pswitch_register(&key->key_pswitch) != 0) { 146 aprint_error(" %s:ERROR", key->key_label); 147 kmem_free(key->key_label, len); 148 kmem_free(key, sizeof(*key)); 149 continue; 150 } 151 152 if (sc->sc_keys) { 153 aprint_normal(", %s", key->key_label); 154 } else { 155 aprint_normal(" %s", key->key_label); 156 } 157 158 key->key_next = sc->sc_keys; 159 sc->sc_keys = key; 160 } 161 162 if (sc->sc_keys == NULL) { 163 aprint_normal(" no keys configured\n"); 164 return; 165 } 166 167 aprint_normal("\n"); 168 169 callout_init(&sc->sc_tick, CALLOUT_MPSAFE); 170 callout_setfunc(&sc->sc_tick, gpiokeys_tick, sc); 171 172 gpiokeys_tick(sc); 173 } 174 175 static void 176 gpiokeys_tick(void *priv) 177 { 178 struct gpiokeys_softc * const sc = priv; 179 struct gpiokeys_key *key; 180 181 for (key = sc->sc_keys; key; key = key->key_next) { 182 if (key->key_pin == NULL) { 183 continue; 184 } 185 const int new_state = fdtbus_gpio_read(key->key_pin); 186 if (new_state != key->key_state) { 187 key->key_state = new_state; 188 sysmon_task_queue_sched(0, gpiokeys_task, key); 189 } 190 } 191 callout_schedule(&sc->sc_tick, GPIOKEYS_POLL_INTERVAL); 192 } 193 194 static void 195 gpiokeys_task(void *priv) 196 { 197 struct gpiokeys_key *key = priv; 198 199 sysmon_pswitch_event(&key->key_pswitch, 200 key->key_state ? PSWITCH_EVENT_PRESSED : PSWITCH_EVENT_RELEASED); 201 } 202