15f958b85SOleksandr Tymoshenko /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3718cf2ccSPedro F. Giffuni * 45f958b85SOleksandr Tymoshenko * Copyright (c) 2009 Oleksandr Tymoshenko <gonzo@freebsd.org> 55f958b85SOleksandr Tymoshenko * All rights reserved. 65f958b85SOleksandr Tymoshenko * 75f958b85SOleksandr Tymoshenko * Redistribution and use in source and binary forms, with or without 85f958b85SOleksandr Tymoshenko * modification, are permitted provided that the following conditions 95f958b85SOleksandr Tymoshenko * are met: 105f958b85SOleksandr Tymoshenko * 1. Redistributions of source code must retain the above copyright 115f958b85SOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer. 125f958b85SOleksandr Tymoshenko * 2. Redistributions in binary form must reproduce the above copyright 135f958b85SOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer in the 145f958b85SOleksandr Tymoshenko * documentation and/or other materials provided with the distribution. 155f958b85SOleksandr Tymoshenko * 165f958b85SOleksandr Tymoshenko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 175f958b85SOleksandr Tymoshenko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 185f958b85SOleksandr Tymoshenko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 195f958b85SOleksandr Tymoshenko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 205f958b85SOleksandr Tymoshenko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 215f958b85SOleksandr Tymoshenko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 225f958b85SOleksandr Tymoshenko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 235f958b85SOleksandr Tymoshenko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 245f958b85SOleksandr Tymoshenko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 255f958b85SOleksandr Tymoshenko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 265f958b85SOleksandr Tymoshenko * SUCH DAMAGE. 275f958b85SOleksandr Tymoshenko */ 285f958b85SOleksandr Tymoshenko 296b34b16eSOleksandr Tymoshenko #include <sys/cdefs.h> 306b34b16eSOleksandr Tymoshenko __FBSDID("$FreeBSD$"); 316b34b16eSOleksandr Tymoshenko 326b34b16eSOleksandr Tymoshenko #include <sys/param.h> 336b34b16eSOleksandr Tymoshenko #include <sys/systm.h> 346b34b16eSOleksandr Tymoshenko #include <sys/bus.h> 356b34b16eSOleksandr Tymoshenko #include <sys/conf.h> 36667357dcSLuiz Otavio O Souza #include <sys/gpio.h> 376b34b16eSOleksandr Tymoshenko #include <sys/ioccom.h> 38ff3468acSIan Lepore #include <sys/filio.h> 39ff3468acSIan Lepore #include <sys/fcntl.h> 40ff3468acSIan Lepore #include <sys/sigio.h> 41ff3468acSIan Lepore #include <sys/signalvar.h> 426b34b16eSOleksandr Tymoshenko #include <sys/kernel.h> 436b34b16eSOleksandr Tymoshenko #include <sys/malloc.h> 44ff3468acSIan Lepore #include <sys/uio.h> 45ff3468acSIan Lepore #include <sys/poll.h> 46ff3468acSIan Lepore #include <sys/selinfo.h> 476b34b16eSOleksandr Tymoshenko #include <sys/module.h> 486b34b16eSOleksandr Tymoshenko 49667357dcSLuiz Otavio O Souza #include <dev/gpio/gpiobusvar.h> 50667357dcSLuiz Otavio O Souza 516b34b16eSOleksandr Tymoshenko #include "gpio_if.h" 52d752f0f6SLuiz Otavio O Souza #include "gpiobus_if.h" 536b34b16eSOleksandr Tymoshenko 546b34b16eSOleksandr Tymoshenko #undef GPIOC_DEBUG 556b34b16eSOleksandr Tymoshenko #ifdef GPIOC_DEBUG 566b34b16eSOleksandr Tymoshenko #define dprintf printf 57ff3468acSIan Lepore #define ddevice_printf device_printf 586b34b16eSOleksandr Tymoshenko #else 596b34b16eSOleksandr Tymoshenko #define dprintf(x, arg...) 60ff3468acSIan Lepore #define ddevice_printf(dev, x, arg...) 616b34b16eSOleksandr Tymoshenko #endif 626b34b16eSOleksandr Tymoshenko 636b34b16eSOleksandr Tymoshenko struct gpioc_softc { 646b34b16eSOleksandr Tymoshenko device_t sc_dev; /* gpiocX dev */ 656b34b16eSOleksandr Tymoshenko device_t sc_pdev; /* gpioX dev */ 666b34b16eSOleksandr Tymoshenko struct cdev *sc_ctl_dev; /* controller device */ 676b34b16eSOleksandr Tymoshenko int sc_unit; 68ff3468acSIan Lepore int sc_npins; 69ff3468acSIan Lepore struct gpioc_pin_intr *sc_pin_intr; 706b34b16eSOleksandr Tymoshenko }; 716b34b16eSOleksandr Tymoshenko 72ff3468acSIan Lepore struct gpioc_pin_intr { 73ff3468acSIan Lepore struct gpioc_softc *sc; 74ff3468acSIan Lepore gpio_pin_t pin; 75ff3468acSIan Lepore bool config_locked; 76ff3468acSIan Lepore int intr_rid; 77ff3468acSIan Lepore struct resource *intr_res; 78ff3468acSIan Lepore void *intr_cookie; 79ff3468acSIan Lepore struct mtx mtx; 80ff3468acSIan Lepore SLIST_HEAD(gpioc_privs_list, gpioc_privs) privs; 81ff3468acSIan Lepore }; 82ff3468acSIan Lepore 83ff3468acSIan Lepore 84ff3468acSIan Lepore struct gpioc_cdevpriv { 85ff3468acSIan Lepore struct gpioc_softc *sc; 86ff3468acSIan Lepore struct selinfo selinfo; 87ff3468acSIan Lepore bool async; 88ff3468acSIan Lepore uint8_t report_option; 89ff3468acSIan Lepore struct sigio *sigio; 90ff3468acSIan Lepore struct mtx mtx; 91ff3468acSIan Lepore struct gpioc_pin_event *events; 92ff3468acSIan Lepore int numevents; 93ff3468acSIan Lepore int evidx_head; 94ff3468acSIan Lepore int evidx_tail; 95ff3468acSIan Lepore SLIST_HEAD(gpioc_pins_list, gpioc_pins) pins; 96ff3468acSIan Lepore }; 97ff3468acSIan Lepore 98ff3468acSIan Lepore struct gpioc_privs { 99ff3468acSIan Lepore struct gpioc_cdevpriv *priv; 100ff3468acSIan Lepore SLIST_ENTRY(gpioc_privs) next; 101ff3468acSIan Lepore }; 102ff3468acSIan Lepore 103ff3468acSIan Lepore struct gpioc_pins { 104ff3468acSIan Lepore struct gpioc_pin_intr *pin; 105ff3468acSIan Lepore int eventcount; 106ff3468acSIan Lepore int firstevent; 107ff3468acSIan Lepore SLIST_ENTRY(gpioc_pins) next; 108ff3468acSIan Lepore }; 109ff3468acSIan Lepore 110ff3468acSIan Lepore struct gpioc_pin_event { 111ff3468acSIan Lepore struct gpioc_pins *privpin; 112ff3468acSIan Lepore sbintime_t event_time; 113ff3468acSIan Lepore bool event_pin_state; 114ff3468acSIan Lepore }; 115ff3468acSIan Lepore 116ff3468acSIan Lepore static MALLOC_DEFINE(M_GPIOC, "gpioc", "gpioc device data"); 117ff3468acSIan Lepore 118ff3468acSIan Lepore static int gpioc_allocate_pin_intr(struct gpioc_pin_intr*, uint32_t); 119ff3468acSIan Lepore static int gpioc_release_pin_intr(struct gpioc_pin_intr*); 120ff3468acSIan Lepore static int gpioc_attach_priv_pin(struct gpioc_cdevpriv*, 121ff3468acSIan Lepore struct gpioc_pin_intr*); 122ff3468acSIan Lepore static int gpioc_detach_priv_pin(struct gpioc_cdevpriv*, 123ff3468acSIan Lepore struct gpioc_pin_intr*); 124ff3468acSIan Lepore static bool gpioc_intr_reconfig_allowed(struct gpioc_cdevpriv*, 125ff3468acSIan Lepore struct gpioc_pin_intr *intr_conf); 126ff3468acSIan Lepore static uint32_t gpioc_get_intr_config(struct gpioc_softc*, 127ff3468acSIan Lepore struct gpioc_cdevpriv*, uint32_t pin); 128ff3468acSIan Lepore static int gpioc_set_intr_config(struct gpioc_softc*, 129ff3468acSIan Lepore struct gpioc_cdevpriv*, uint32_t, uint32_t); 130ff3468acSIan Lepore static void gpioc_interrupt_handler(void*); 131ff3468acSIan Lepore 132ff3468acSIan Lepore static int gpioc_kqread(struct knote*, long); 133ff3468acSIan Lepore static void gpioc_kqdetach(struct knote*); 134ff3468acSIan Lepore 135ff3468acSIan Lepore static int gpioc_probe(device_t dev); 136ff3468acSIan Lepore static int gpioc_attach(device_t dev); 137ff3468acSIan Lepore static int gpioc_detach(device_t dev); 138ff3468acSIan Lepore 139ff3468acSIan Lepore static void gpioc_cdevpriv_dtor(void*); 140ff3468acSIan Lepore 141ff3468acSIan Lepore static d_open_t gpioc_open; 142ff3468acSIan Lepore static d_read_t gpioc_read; 143ff3468acSIan Lepore static d_ioctl_t gpioc_ioctl; 144ff3468acSIan Lepore static d_poll_t gpioc_poll; 145ff3468acSIan Lepore static d_kqfilter_t gpioc_kqfilter; 146ff3468acSIan Lepore 147ff3468acSIan Lepore static struct cdevsw gpioc_cdevsw = { 148ff3468acSIan Lepore .d_version = D_VERSION, 149ff3468acSIan Lepore .d_open = gpioc_open, 150ff3468acSIan Lepore .d_read = gpioc_read, 151ff3468acSIan Lepore .d_ioctl = gpioc_ioctl, 152ff3468acSIan Lepore .d_poll = gpioc_poll, 153ff3468acSIan Lepore .d_kqfilter = gpioc_kqfilter, 154ff3468acSIan Lepore .d_name = "gpioc", 155ff3468acSIan Lepore }; 156ff3468acSIan Lepore 157ff3468acSIan Lepore static struct filterops gpioc_read_filterops = { 158ff3468acSIan Lepore .f_isfd = true, 159ff3468acSIan Lepore .f_attach = NULL, 160ff3468acSIan Lepore .f_detach = gpioc_kqdetach, 161ff3468acSIan Lepore .f_event = gpioc_kqread, 162ff3468acSIan Lepore .f_touch = NULL 163ff3468acSIan Lepore }; 164ff3468acSIan Lepore 165ff3468acSIan Lepore static struct gpioc_pin_event * 166ff3468acSIan Lepore next_head_event(struct gpioc_cdevpriv *priv) 167ff3468acSIan Lepore { 168ff3468acSIan Lepore struct gpioc_pin_event *rv; 169ff3468acSIan Lepore 170ff3468acSIan Lepore rv = &priv->events[priv->evidx_head++]; 171ff3468acSIan Lepore if (priv->evidx_head == priv->numevents) 172ff3468acSIan Lepore priv->evidx_head = 0; 173ff3468acSIan Lepore return (rv); 174ff3468acSIan Lepore } 175ff3468acSIan Lepore 176ff3468acSIan Lepore static struct gpioc_pin_event * 177ff3468acSIan Lepore next_tail_event(struct gpioc_cdevpriv *priv) 178ff3468acSIan Lepore { 179ff3468acSIan Lepore struct gpioc_pin_event *rv; 180ff3468acSIan Lepore 181ff3468acSIan Lepore rv = &priv->events[priv->evidx_tail++]; 182ff3468acSIan Lepore if (priv->evidx_tail == priv->numevents) 183ff3468acSIan Lepore priv->evidx_tail = 0; 184ff3468acSIan Lepore return (rv); 185ff3468acSIan Lepore } 186ff3468acSIan Lepore 187ff3468acSIan Lepore static size_t 188ff3468acSIan Lepore number_of_events(struct gpioc_cdevpriv *priv) 189ff3468acSIan Lepore { 190ff3468acSIan Lepore if (priv->evidx_head >= priv->evidx_tail) 191ff3468acSIan Lepore return (priv->evidx_head - priv->evidx_tail); 192ff3468acSIan Lepore else 193ff3468acSIan Lepore return (priv->numevents + priv->evidx_head - priv->evidx_tail); 194ff3468acSIan Lepore } 195ff3468acSIan Lepore 196ff3468acSIan Lepore static int 197ff3468acSIan Lepore gpioc_allocate_pin_intr(struct gpioc_pin_intr *intr_conf, uint32_t flags) 198ff3468acSIan Lepore { 199ff3468acSIan Lepore int err; 200ff3468acSIan Lepore 201ff3468acSIan Lepore intr_conf->config_locked = true; 202ff3468acSIan Lepore mtx_unlock(&intr_conf->mtx); 203ff3468acSIan Lepore 204ff3468acSIan Lepore intr_conf->intr_res = gpio_alloc_intr_resource(intr_conf->pin->dev, 205ff3468acSIan Lepore &intr_conf->intr_rid, RF_ACTIVE, intr_conf->pin, flags); 206ff3468acSIan Lepore if (intr_conf->intr_res == NULL) { 207ff3468acSIan Lepore err = ENXIO; 208ff3468acSIan Lepore goto error_exit; 209ff3468acSIan Lepore } 210ff3468acSIan Lepore 211ff3468acSIan Lepore err = bus_setup_intr(intr_conf->pin->dev, intr_conf->intr_res, 212ff3468acSIan Lepore INTR_TYPE_MISC | INTR_MPSAFE, NULL, gpioc_interrupt_handler, 213ff3468acSIan Lepore intr_conf, &intr_conf->intr_cookie); 214ff3468acSIan Lepore if (err != 0) 215ff3468acSIan Lepore goto error_exit; 216ff3468acSIan Lepore 217ff3468acSIan Lepore intr_conf->pin->flags = flags; 218ff3468acSIan Lepore 219ff3468acSIan Lepore error_exit: 220ff3468acSIan Lepore mtx_lock(&intr_conf->mtx); 221ff3468acSIan Lepore intr_conf->config_locked = false; 222ff3468acSIan Lepore wakeup(&intr_conf->config_locked); 223ff3468acSIan Lepore 224ff3468acSIan Lepore return (err); 225ff3468acSIan Lepore } 226ff3468acSIan Lepore 227ff3468acSIan Lepore static int 228ff3468acSIan Lepore gpioc_release_pin_intr(struct gpioc_pin_intr *intr_conf) 229ff3468acSIan Lepore { 230ff3468acSIan Lepore int err; 231ff3468acSIan Lepore 232ff3468acSIan Lepore intr_conf->config_locked = true; 233ff3468acSIan Lepore mtx_unlock(&intr_conf->mtx); 234ff3468acSIan Lepore 235ff3468acSIan Lepore if (intr_conf->intr_cookie != NULL) { 236ff3468acSIan Lepore err = bus_teardown_intr(intr_conf->pin->dev, 237ff3468acSIan Lepore intr_conf->intr_res, intr_conf->intr_cookie); 238ff3468acSIan Lepore if (err != 0) 239ff3468acSIan Lepore goto error_exit; 240ff3468acSIan Lepore else 241ff3468acSIan Lepore intr_conf->intr_cookie = NULL; 242ff3468acSIan Lepore } 243ff3468acSIan Lepore 244ff3468acSIan Lepore if (intr_conf->intr_res != NULL) { 245ff3468acSIan Lepore err = bus_release_resource(intr_conf->pin->dev, SYS_RES_IRQ, 246ff3468acSIan Lepore intr_conf->intr_rid, intr_conf->intr_res); 247ff3468acSIan Lepore if (err != 0) 248ff3468acSIan Lepore goto error_exit; 249ff3468acSIan Lepore else { 250ff3468acSIan Lepore intr_conf->intr_rid = 0; 251ff3468acSIan Lepore intr_conf->intr_res = NULL; 252ff3468acSIan Lepore } 253ff3468acSIan Lepore } 254ff3468acSIan Lepore 255ff3468acSIan Lepore intr_conf->pin->flags = 0; 256ff3468acSIan Lepore err = 0; 257ff3468acSIan Lepore 258ff3468acSIan Lepore error_exit: 259ff3468acSIan Lepore mtx_lock(&intr_conf->mtx); 260ff3468acSIan Lepore intr_conf->config_locked = false; 261ff3468acSIan Lepore wakeup(&intr_conf->config_locked); 262ff3468acSIan Lepore 263ff3468acSIan Lepore return (err); 264ff3468acSIan Lepore } 265ff3468acSIan Lepore 266ff3468acSIan Lepore static int 267ff3468acSIan Lepore gpioc_attach_priv_pin(struct gpioc_cdevpriv *priv, 268ff3468acSIan Lepore struct gpioc_pin_intr *intr_conf) 269ff3468acSIan Lepore { 270ff3468acSIan Lepore struct gpioc_privs *priv_link; 271ff3468acSIan Lepore struct gpioc_pins *pin_link; 272*7dc4d511SEd Maste unsigned int consistency_a __diagused; 273*7dc4d511SEd Maste unsigned int consistency_b __diagused; 274ff3468acSIan Lepore 275ff3468acSIan Lepore consistency_a = 0; 276ff3468acSIan Lepore consistency_b = 0; 277ff3468acSIan Lepore mtx_assert(&intr_conf->mtx, MA_OWNED); 278ff3468acSIan Lepore mtx_lock(&priv->mtx); 279ff3468acSIan Lepore SLIST_FOREACH(priv_link, &intr_conf->privs, next) { 280ff3468acSIan Lepore if (priv_link->priv == priv) 281ff3468acSIan Lepore consistency_a++; 282ff3468acSIan Lepore } 283ff3468acSIan Lepore KASSERT(consistency_a <= 1, 284ff3468acSIan Lepore ("inconsistent links between pin config and cdevpriv")); 285ff3468acSIan Lepore SLIST_FOREACH(pin_link, &priv->pins, next) { 286ff3468acSIan Lepore if (pin_link->pin == intr_conf) 287ff3468acSIan Lepore consistency_b++; 288ff3468acSIan Lepore } 289ff3468acSIan Lepore KASSERT(consistency_a == consistency_b, 290ff3468acSIan Lepore ("inconsistent links between pin config and cdevpriv")); 291ff3468acSIan Lepore if (consistency_a == 1 && consistency_b == 1) { 292ff3468acSIan Lepore mtx_unlock(&priv->mtx); 293ff3468acSIan Lepore return (EEXIST); 294ff3468acSIan Lepore } 295ff3468acSIan Lepore priv_link = malloc(sizeof(struct gpioc_privs), M_GPIOC, 296ff3468acSIan Lepore M_NOWAIT | M_ZERO); 297ff3468acSIan Lepore if (priv_link == NULL) 298ff3468acSIan Lepore { 299ff3468acSIan Lepore mtx_unlock(&priv->mtx); 300ff3468acSIan Lepore return (ENOMEM); 301ff3468acSIan Lepore } 302ff3468acSIan Lepore pin_link = malloc(sizeof(struct gpioc_pins), M_GPIOC, 303ff3468acSIan Lepore M_NOWAIT | M_ZERO); 304ff3468acSIan Lepore if (pin_link == NULL) { 305ff3468acSIan Lepore mtx_unlock(&priv->mtx); 306ff3468acSIan Lepore return (ENOMEM); 307ff3468acSIan Lepore } 308ff3468acSIan Lepore priv_link->priv = priv; 309ff3468acSIan Lepore pin_link->pin = intr_conf; 310ff3468acSIan Lepore SLIST_INSERT_HEAD(&intr_conf->privs, priv_link, next); 311ff3468acSIan Lepore SLIST_INSERT_HEAD(&priv->pins, pin_link, next); 312ff3468acSIan Lepore mtx_unlock(&priv->mtx); 313ff3468acSIan Lepore 314ff3468acSIan Lepore return (0); 315ff3468acSIan Lepore } 316ff3468acSIan Lepore 317ff3468acSIan Lepore static int 318ff3468acSIan Lepore gpioc_detach_priv_pin(struct gpioc_cdevpriv *priv, 319ff3468acSIan Lepore struct gpioc_pin_intr *intr_conf) 320ff3468acSIan Lepore { 321ff3468acSIan Lepore struct gpioc_privs *priv_link, *priv_link_temp; 322ff3468acSIan Lepore struct gpioc_pins *pin_link, *pin_link_temp; 323ff3468acSIan Lepore unsigned int consistency_a, consistency_b; 324ff3468acSIan Lepore 325ff3468acSIan Lepore consistency_a = 0; 326ff3468acSIan Lepore consistency_b = 0; 327ff3468acSIan Lepore mtx_assert(&intr_conf->mtx, MA_OWNED); 328ff3468acSIan Lepore mtx_lock(&priv->mtx); 329ff3468acSIan Lepore SLIST_FOREACH_SAFE(priv_link, &intr_conf->privs, next, priv_link_temp) { 330ff3468acSIan Lepore if (priv_link->priv == priv) { 331ff3468acSIan Lepore SLIST_REMOVE(&intr_conf->privs, priv_link, gpioc_privs, 332ff3468acSIan Lepore next); 333ff3468acSIan Lepore free(priv_link, M_GPIOC); 334ff3468acSIan Lepore consistency_a++; 335ff3468acSIan Lepore } 336ff3468acSIan Lepore } 337ff3468acSIan Lepore KASSERT(consistency_a <= 1, 338ff3468acSIan Lepore ("inconsistent links between pin config and cdevpriv")); 339ff3468acSIan Lepore SLIST_FOREACH_SAFE(pin_link, &priv->pins, next, pin_link_temp) { 340ff3468acSIan Lepore if (pin_link->pin == intr_conf) { 341ff3468acSIan Lepore /* 342ff3468acSIan Lepore * If the pin we're removing has events in the priv's 343ff3468acSIan Lepore * event fifo, we can't leave dangling pointers from 344ff3468acSIan Lepore * those events to the gpioc_pins struct we're about to 345ff3468acSIan Lepore * free. We also can't remove random items and leave 346ff3468acSIan Lepore * holes in the events fifo, so just empty it out. 347ff3468acSIan Lepore */ 348ff3468acSIan Lepore if (pin_link->eventcount > 0) { 349ff3468acSIan Lepore priv->evidx_head = priv->evidx_tail = 0; 350ff3468acSIan Lepore } 351ff3468acSIan Lepore SLIST_REMOVE(&priv->pins, pin_link, gpioc_pins, next); 352ff3468acSIan Lepore free(pin_link, M_GPIOC); 353ff3468acSIan Lepore consistency_b++; 354ff3468acSIan Lepore } 355ff3468acSIan Lepore } 356ff3468acSIan Lepore KASSERT(consistency_a == consistency_b, 357ff3468acSIan Lepore ("inconsistent links between pin config and cdevpriv")); 358ff3468acSIan Lepore mtx_unlock(&priv->mtx); 359ff3468acSIan Lepore 360ff3468acSIan Lepore return (0); 361ff3468acSIan Lepore } 362ff3468acSIan Lepore 363ff3468acSIan Lepore static bool 364ff3468acSIan Lepore gpioc_intr_reconfig_allowed(struct gpioc_cdevpriv *priv, 365ff3468acSIan Lepore struct gpioc_pin_intr *intr_conf) 366ff3468acSIan Lepore { 367ff3468acSIan Lepore struct gpioc_privs *priv_link; 368ff3468acSIan Lepore 369ff3468acSIan Lepore mtx_assert(&intr_conf->mtx, MA_OWNED); 370ff3468acSIan Lepore 371ff3468acSIan Lepore if (SLIST_EMPTY(&intr_conf->privs)) 372ff3468acSIan Lepore return (true); 373ff3468acSIan Lepore 374ff3468acSIan Lepore SLIST_FOREACH(priv_link, &intr_conf->privs, next) { 375ff3468acSIan Lepore if (priv_link->priv != priv) 376ff3468acSIan Lepore return (false); 377ff3468acSIan Lepore } 378ff3468acSIan Lepore 379ff3468acSIan Lepore return (true); 380ff3468acSIan Lepore } 381ff3468acSIan Lepore 382ff3468acSIan Lepore 383ff3468acSIan Lepore static uint32_t 384ff3468acSIan Lepore gpioc_get_intr_config(struct gpioc_softc *sc, struct gpioc_cdevpriv *priv, 385ff3468acSIan Lepore uint32_t pin) 386ff3468acSIan Lepore { 387ff3468acSIan Lepore struct gpioc_pin_intr *intr_conf = &sc->sc_pin_intr[pin]; 388ff3468acSIan Lepore struct gpioc_privs *priv_link; 389ff3468acSIan Lepore uint32_t flags; 390ff3468acSIan Lepore 391ff3468acSIan Lepore flags = intr_conf->pin->flags; 392ff3468acSIan Lepore 393ff3468acSIan Lepore if (flags == 0) 394ff3468acSIan Lepore return (0); 395ff3468acSIan Lepore 396ff3468acSIan Lepore mtx_lock(&intr_conf->mtx); 397ff3468acSIan Lepore SLIST_FOREACH(priv_link, &intr_conf->privs, next) { 398ff3468acSIan Lepore if (priv_link->priv == priv) { 399ff3468acSIan Lepore flags |= GPIO_INTR_ATTACHED; 400ff3468acSIan Lepore break; 401ff3468acSIan Lepore } 402ff3468acSIan Lepore } 403ff3468acSIan Lepore mtx_unlock(&intr_conf->mtx); 404ff3468acSIan Lepore 405ff3468acSIan Lepore return (flags); 406ff3468acSIan Lepore } 407ff3468acSIan Lepore 408ff3468acSIan Lepore static int 409ff3468acSIan Lepore gpioc_set_intr_config(struct gpioc_softc *sc, struct gpioc_cdevpriv *priv, 410ff3468acSIan Lepore uint32_t pin, uint32_t flags) 411ff3468acSIan Lepore { 412ff3468acSIan Lepore struct gpioc_pin_intr *intr_conf = &sc->sc_pin_intr[pin]; 413ff3468acSIan Lepore int res; 414ff3468acSIan Lepore 415ff3468acSIan Lepore res = 0; 416ff3468acSIan Lepore if (intr_conf->pin->flags == 0 && flags == 0) { 417ff3468acSIan Lepore /* No interrupt configured and none requested: Do nothing. */ 418ff3468acSIan Lepore return (0); 419ff3468acSIan Lepore } 420ff3468acSIan Lepore mtx_lock(&intr_conf->mtx); 421ff3468acSIan Lepore while (intr_conf->config_locked == true) 422ff3468acSIan Lepore mtx_sleep(&intr_conf->config_locked, &intr_conf->mtx, 0, 423ff3468acSIan Lepore "gpicfg", 0); 424ff3468acSIan Lepore if (intr_conf->pin->flags == 0 && flags != 0) { 425ff3468acSIan Lepore /* 426ff3468acSIan Lepore * No interrupt is configured, but one is requested: Allocate 427ff3468acSIan Lepore * and setup interrupt on the according pin. 428ff3468acSIan Lepore */ 429ff3468acSIan Lepore res = gpioc_allocate_pin_intr(intr_conf, flags); 430ff3468acSIan Lepore if (res == 0) 431ff3468acSIan Lepore res = gpioc_attach_priv_pin(priv, intr_conf); 432ff3468acSIan Lepore if (res == EEXIST) 433ff3468acSIan Lepore res = 0; 434ff3468acSIan Lepore } else if (intr_conf->pin->flags == flags) { 435ff3468acSIan Lepore /* 436ff3468acSIan Lepore * Same interrupt requested as already configured: Attach the 437ff3468acSIan Lepore * cdevpriv to the corresponding pin. 438ff3468acSIan Lepore */ 439ff3468acSIan Lepore res = gpioc_attach_priv_pin(priv, intr_conf); 440ff3468acSIan Lepore if (res == EEXIST) 441ff3468acSIan Lepore res = 0; 442ff3468acSIan Lepore } else if (intr_conf->pin->flags != 0 && flags == 0) { 443ff3468acSIan Lepore /* 444ff3468acSIan Lepore * Interrupt configured, but none requested: Teardown and 445ff3468acSIan Lepore * release the pin when no other cdevpriv is attached. Otherwise 446ff3468acSIan Lepore * just detach pin and cdevpriv from each other. 447ff3468acSIan Lepore */ 448ff3468acSIan Lepore if (gpioc_intr_reconfig_allowed(priv, intr_conf)) { 449ff3468acSIan Lepore res = gpioc_release_pin_intr(intr_conf); 450ff3468acSIan Lepore } 451ff3468acSIan Lepore if (res == 0) 452ff3468acSIan Lepore res = gpioc_detach_priv_pin(priv, intr_conf); 453ff3468acSIan Lepore } else { 454ff3468acSIan Lepore /* 455ff3468acSIan Lepore * Other flag requested than configured: Reconfigure when no 456ff3468acSIan Lepore * other cdevpriv is are attached to the pin. 457ff3468acSIan Lepore */ 458ff3468acSIan Lepore if (!gpioc_intr_reconfig_allowed(priv, intr_conf)) 459ff3468acSIan Lepore res = EBUSY; 460ff3468acSIan Lepore else { 461ff3468acSIan Lepore res = gpioc_release_pin_intr(intr_conf); 462ff3468acSIan Lepore if (res == 0) 463ff3468acSIan Lepore res = gpioc_allocate_pin_intr(intr_conf, flags); 464ff3468acSIan Lepore if (res == 0) 465ff3468acSIan Lepore res = gpioc_attach_priv_pin(priv, intr_conf); 466ff3468acSIan Lepore if (res == EEXIST) 467ff3468acSIan Lepore res = 0; 468ff3468acSIan Lepore } 469ff3468acSIan Lepore } 470ff3468acSIan Lepore mtx_unlock(&intr_conf->mtx); 471ff3468acSIan Lepore 472ff3468acSIan Lepore return (res); 473ff3468acSIan Lepore } 474ff3468acSIan Lepore 475ff3468acSIan Lepore static void 476ff3468acSIan Lepore gpioc_interrupt_handler(void *arg) 477ff3468acSIan Lepore { 478ff3468acSIan Lepore struct gpioc_pin_intr *intr_conf; 479ff3468acSIan Lepore struct gpioc_privs *privs; 480ff3468acSIan Lepore struct gpioc_softc *sc; 481ff3468acSIan Lepore sbintime_t evtime; 482ff3468acSIan Lepore uint32_t pin_state; 483ff3468acSIan Lepore 484ff3468acSIan Lepore intr_conf = arg; 485ff3468acSIan Lepore sc = intr_conf->sc; 486ff3468acSIan Lepore 487ff3468acSIan Lepore /* Capture time and pin state first. */ 488ff3468acSIan Lepore evtime = sbinuptime(); 489ff3468acSIan Lepore if (intr_conf->pin->flags & GPIO_INTR_EDGE_BOTH) 490ff3468acSIan Lepore GPIO_PIN_GET(sc->sc_pdev, intr_conf->pin->pin, &pin_state); 491ff3468acSIan Lepore else if (intr_conf->pin->flags & GPIO_INTR_EDGE_RISING) 492ff3468acSIan Lepore pin_state = true; 493ff3468acSIan Lepore else 494ff3468acSIan Lepore pin_state = false; 495ff3468acSIan Lepore 496ff3468acSIan Lepore mtx_lock(&intr_conf->mtx); 497ff3468acSIan Lepore 498ff3468acSIan Lepore if (intr_conf->config_locked == true) { 499ff3468acSIan Lepore ddevice_printf(sc->sc_dev, "Interrupt configuration in " 500ff3468acSIan Lepore "progress. Discarding interrupt on pin %d.\n", 501ff3468acSIan Lepore intr_conf->pin->pin); 502ff3468acSIan Lepore mtx_unlock(&intr_conf->mtx); 503ff3468acSIan Lepore return; 504ff3468acSIan Lepore } 505ff3468acSIan Lepore 506ff3468acSIan Lepore if (SLIST_EMPTY(&intr_conf->privs)) { 507ff3468acSIan Lepore ddevice_printf(sc->sc_dev, "No file descriptor associated with " 508ff3468acSIan Lepore "occurred interrupt on pin %d.\n", intr_conf->pin->pin); 509ff3468acSIan Lepore mtx_unlock(&intr_conf->mtx); 510ff3468acSIan Lepore return; 511ff3468acSIan Lepore } 512ff3468acSIan Lepore 513ff3468acSIan Lepore SLIST_FOREACH(privs, &intr_conf->privs, next) { 514ff3468acSIan Lepore struct gpioc_cdevpriv *priv = privs->priv; 515ff3468acSIan Lepore struct gpioc_pins *privpin; 516ff3468acSIan Lepore struct gpioc_pin_event *event; 517ff3468acSIan Lepore mtx_lock(&priv->mtx); 518ff3468acSIan Lepore SLIST_FOREACH(privpin, &priv->pins, next) { 519ff3468acSIan Lepore if (privpin->pin == intr_conf) 520ff3468acSIan Lepore break; 521ff3468acSIan Lepore } 522ff3468acSIan Lepore if (privpin == NULL) { 523ff3468acSIan Lepore /* Should be impossible. */ 524ff3468acSIan Lepore ddevice_printf(sc->sc_dev, "Cannot find privpin\n"); 525ff3468acSIan Lepore mtx_unlock(&priv->mtx); 526ff3468acSIan Lepore continue; 527ff3468acSIan Lepore } 528ff3468acSIan Lepore 529ff3468acSIan Lepore if (priv->report_option == GPIO_EVENT_REPORT_DETAIL) { 530ff3468acSIan Lepore event = next_head_event(priv); 531ff3468acSIan Lepore /* If head is overtaking tail, advance tail. */ 532ff3468acSIan Lepore if (priv->evidx_head == priv->evidx_tail) 533ff3468acSIan Lepore next_tail_event(priv); 534ff3468acSIan Lepore } else { 535ff3468acSIan Lepore if (privpin->eventcount > 0) 536ff3468acSIan Lepore event = &priv->events[privpin->firstevent + 1]; 537ff3468acSIan Lepore else { 538ff3468acSIan Lepore privpin->firstevent = priv->evidx_head; 539ff3468acSIan Lepore event = next_head_event(priv); 540ff3468acSIan Lepore event->privpin = privpin; 541ff3468acSIan Lepore event->event_time = evtime; 542ff3468acSIan Lepore event->event_pin_state = pin_state; 543ff3468acSIan Lepore event = next_head_event(priv); 544ff3468acSIan Lepore } 545ff3468acSIan Lepore ++privpin->eventcount; 546ff3468acSIan Lepore } 547ff3468acSIan Lepore event->privpin = privpin; 548ff3468acSIan Lepore event->event_time = evtime; 549ff3468acSIan Lepore event->event_pin_state = pin_state; 550ff3468acSIan Lepore wakeup(priv); 551ff3468acSIan Lepore selwakeup(&priv->selinfo); 552ff3468acSIan Lepore KNOTE_LOCKED(&priv->selinfo.si_note, 0); 553ff3468acSIan Lepore if (priv->async == true && priv->sigio != NULL) 554ff3468acSIan Lepore pgsigio(&priv->sigio, SIGIO, 0); 555ff3468acSIan Lepore mtx_unlock(&priv->mtx); 556ff3468acSIan Lepore } 557ff3468acSIan Lepore 558ff3468acSIan Lepore mtx_unlock(&intr_conf->mtx); 559ff3468acSIan Lepore } 560ff3468acSIan Lepore 5616b34b16eSOleksandr Tymoshenko static int 5626b34b16eSOleksandr Tymoshenko gpioc_probe(device_t dev) 5636b34b16eSOleksandr Tymoshenko { 5646b34b16eSOleksandr Tymoshenko device_set_desc(dev, "GPIO controller"); 5656b34b16eSOleksandr Tymoshenko return (0); 5666b34b16eSOleksandr Tymoshenko } 5676b34b16eSOleksandr Tymoshenko 5686b34b16eSOleksandr Tymoshenko static int 5696b34b16eSOleksandr Tymoshenko gpioc_attach(device_t dev) 5706b34b16eSOleksandr Tymoshenko { 5714723c14fSLuiz Otavio O Souza int err; 5724723c14fSLuiz Otavio O Souza struct gpioc_softc *sc; 5734723c14fSLuiz Otavio O Souza struct make_dev_args devargs; 5746b34b16eSOleksandr Tymoshenko 5754723c14fSLuiz Otavio O Souza sc = device_get_softc(dev); 5766b34b16eSOleksandr Tymoshenko sc->sc_dev = dev; 5776b34b16eSOleksandr Tymoshenko sc->sc_pdev = device_get_parent(dev); 5786b34b16eSOleksandr Tymoshenko sc->sc_unit = device_get_unit(dev); 579ff3468acSIan Lepore 580ff3468acSIan Lepore err = GPIO_PIN_MAX(sc->sc_pdev, &sc->sc_npins); 581ff3468acSIan Lepore sc->sc_npins++; /* Number of pins is one more than max pin number. */ 582ff3468acSIan Lepore if (err != 0) 583ff3468acSIan Lepore return (err); 584ff3468acSIan Lepore sc->sc_pin_intr = malloc(sizeof(struct gpioc_pin_intr) * sc->sc_npins, 585ff3468acSIan Lepore M_GPIOC, M_WAITOK | M_ZERO); 586f2a7b434SHans Petter Selasky for (int i = 0; i < sc->sc_npins; i++) { 587ff3468acSIan Lepore sc->sc_pin_intr[i].pin = malloc(sizeof(struct gpiobus_pin), 588ff3468acSIan Lepore M_GPIOC, M_WAITOK | M_ZERO); 589ff3468acSIan Lepore sc->sc_pin_intr[i].sc = sc; 590ff3468acSIan Lepore sc->sc_pin_intr[i].pin->pin = i; 591ff3468acSIan Lepore sc->sc_pin_intr[i].pin->dev = sc->sc_pdev; 592ff3468acSIan Lepore mtx_init(&sc->sc_pin_intr[i].mtx, "gpioc pin", NULL, MTX_DEF); 593ff3468acSIan Lepore SLIST_INIT(&sc->sc_pin_intr[i].privs); 594ff3468acSIan Lepore } 595ff3468acSIan Lepore 5964723c14fSLuiz Otavio O Souza make_dev_args_init(&devargs); 5974723c14fSLuiz Otavio O Souza devargs.mda_devsw = &gpioc_cdevsw; 5984723c14fSLuiz Otavio O Souza devargs.mda_uid = UID_ROOT; 5994723c14fSLuiz Otavio O Souza devargs.mda_gid = GID_WHEEL; 6004723c14fSLuiz Otavio O Souza devargs.mda_mode = 0600; 6014723c14fSLuiz Otavio O Souza devargs.mda_si_drv1 = sc; 6024723c14fSLuiz Otavio O Souza err = make_dev_s(&devargs, &sc->sc_ctl_dev, "gpioc%d", sc->sc_unit); 6034723c14fSLuiz Otavio O Souza if (err != 0) { 604ff3468acSIan Lepore device_printf(dev, "Failed to create gpioc%d", sc->sc_unit); 6056b34b16eSOleksandr Tymoshenko return (ENXIO); 6066b34b16eSOleksandr Tymoshenko } 6076b34b16eSOleksandr Tymoshenko 6086b34b16eSOleksandr Tymoshenko return (0); 6096b34b16eSOleksandr Tymoshenko } 6106b34b16eSOleksandr Tymoshenko 6116b34b16eSOleksandr Tymoshenko static int 6126b34b16eSOleksandr Tymoshenko gpioc_detach(device_t dev) 6136b34b16eSOleksandr Tymoshenko { 6146b34b16eSOleksandr Tymoshenko struct gpioc_softc *sc = device_get_softc(dev); 6156b34b16eSOleksandr Tymoshenko int err; 6166b34b16eSOleksandr Tymoshenko 6176de0a4faSOleksandr Tymoshenko if (sc->sc_ctl_dev) 6186b34b16eSOleksandr Tymoshenko destroy_dev(sc->sc_ctl_dev); 6196b34b16eSOleksandr Tymoshenko 620f2a7b434SHans Petter Selasky for (int i = 0; i < sc->sc_npins; i++) { 621ff3468acSIan Lepore mtx_destroy(&sc->sc_pin_intr[i].mtx); 6223c6b5956SAndriy Gapon free(sc->sc_pin_intr[i].pin, M_GPIOC); 623ff3468acSIan Lepore } 624ff3468acSIan Lepore free(sc->sc_pin_intr, M_GPIOC); 625ff3468acSIan Lepore 6266b34b16eSOleksandr Tymoshenko if ((err = bus_generic_detach(dev)) != 0) 6276b34b16eSOleksandr Tymoshenko return (err); 6286b34b16eSOleksandr Tymoshenko 6296b34b16eSOleksandr Tymoshenko return (0); 6306b34b16eSOleksandr Tymoshenko } 6316b34b16eSOleksandr Tymoshenko 632ff3468acSIan Lepore static void 633ff3468acSIan Lepore gpioc_cdevpriv_dtor(void *data) 634ff3468acSIan Lepore { 635ff3468acSIan Lepore struct gpioc_cdevpriv *priv; 636ff3468acSIan Lepore struct gpioc_privs *priv_link, *priv_link_temp; 637ff3468acSIan Lepore struct gpioc_pins *pin_link, *pin_link_temp; 638*7dc4d511SEd Maste unsigned int consistency __diagused; 639ff3468acSIan Lepore 640ff3468acSIan Lepore priv = data; 641ff3468acSIan Lepore 642ff3468acSIan Lepore SLIST_FOREACH_SAFE(pin_link, &priv->pins, next, pin_link_temp) { 643ff3468acSIan Lepore consistency = 0; 644ff3468acSIan Lepore mtx_lock(&pin_link->pin->mtx); 645ff3468acSIan Lepore while (pin_link->pin->config_locked == true) 646ff3468acSIan Lepore mtx_sleep(&pin_link->pin->config_locked, 647ff3468acSIan Lepore &pin_link->pin->mtx, 0, "gpicfg", 0); 648ff3468acSIan Lepore SLIST_FOREACH_SAFE(priv_link, &pin_link->pin->privs, next, 649ff3468acSIan Lepore priv_link_temp) { 650ff3468acSIan Lepore if (priv_link->priv == priv) { 651ff3468acSIan Lepore SLIST_REMOVE(&pin_link->pin->privs, priv_link, 652ff3468acSIan Lepore gpioc_privs, next); 653ff3468acSIan Lepore free(priv_link, M_GPIOC); 654ff3468acSIan Lepore consistency++; 655ff3468acSIan Lepore } 656ff3468acSIan Lepore } 657ff3468acSIan Lepore KASSERT(consistency == 1, 658ff3468acSIan Lepore ("inconsistent links between pin config and cdevpriv")); 659ff3468acSIan Lepore if (gpioc_intr_reconfig_allowed(priv, pin_link->pin)) { 660ff3468acSIan Lepore gpioc_release_pin_intr(pin_link->pin); 661ff3468acSIan Lepore } 662ff3468acSIan Lepore mtx_unlock(&pin_link->pin->mtx); 663ff3468acSIan Lepore SLIST_REMOVE(&priv->pins, pin_link, gpioc_pins, next); 664ff3468acSIan Lepore free(pin_link, M_GPIOC); 665ff3468acSIan Lepore } 666ff3468acSIan Lepore 667ff3468acSIan Lepore wakeup(&priv); 668ff3468acSIan Lepore knlist_clear(&priv->selinfo.si_note, 0); 669ff3468acSIan Lepore seldrain(&priv->selinfo); 670ff3468acSIan Lepore knlist_destroy(&priv->selinfo.si_note); 671ff3468acSIan Lepore funsetown(&priv->sigio); 672ff3468acSIan Lepore 673ff3468acSIan Lepore mtx_destroy(&priv->mtx); 674ff3468acSIan Lepore free(priv->events, M_GPIOC); 675ff3468acSIan Lepore free(data, M_GPIOC); 676ff3468acSIan Lepore } 677ff3468acSIan Lepore 678ff3468acSIan Lepore static int 679ff3468acSIan Lepore gpioc_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 680ff3468acSIan Lepore { 681ff3468acSIan Lepore struct gpioc_cdevpriv *priv; 682ff3468acSIan Lepore int err; 683ff3468acSIan Lepore 684ff3468acSIan Lepore priv = malloc(sizeof(*priv), M_GPIOC, M_WAITOK | M_ZERO); 685ff3468acSIan Lepore priv->sc = dev->si_drv1; 686ff3468acSIan Lepore priv->report_option = GPIO_EVENT_REPORT_DETAIL; 687ff3468acSIan Lepore err = devfs_set_cdevpriv(priv, gpioc_cdevpriv_dtor); 688ff3468acSIan Lepore if (err != 0) { 689ff3468acSIan Lepore gpioc_cdevpriv_dtor(priv); 690ff3468acSIan Lepore return (err); 691ff3468acSIan Lepore } 692ff3468acSIan Lepore mtx_init(&priv->mtx, "gpioc priv", NULL, MTX_DEF); 693ff3468acSIan Lepore knlist_init_mtx(&priv->selinfo.si_note, &priv->mtx); 694ff3468acSIan Lepore 695ff3468acSIan Lepore /* 696ff3468acSIan Lepore * Allocate a circular buffer for events. The scheme we use for summary 697ff3468acSIan Lepore * reporting assumes there will always be a pair of events available to 698ff3468acSIan Lepore * record the first/last events on any pin, so we allocate 2 * npins. 699ff3468acSIan Lepore * Even though we actually default to detailed event reporting, 2 * 700ff3468acSIan Lepore * npins isn't a horrible fifo size for that either. 701ff3468acSIan Lepore */ 702ff3468acSIan Lepore priv->numevents = priv->sc->sc_npins * 2; 703ff3468acSIan Lepore priv->events = malloc(priv->numevents * sizeof(struct gpio_event_detail), 704ff3468acSIan Lepore M_GPIOC, M_WAITOK | M_ZERO); 705ff3468acSIan Lepore 706ff3468acSIan Lepore return (0); 707ff3468acSIan Lepore } 708ff3468acSIan Lepore 709ff3468acSIan Lepore static int 710ff3468acSIan Lepore gpioc_read(struct cdev *dev, struct uio *uio, int ioflag) 711ff3468acSIan Lepore { 712ff3468acSIan Lepore struct gpioc_cdevpriv *priv; 713ff3468acSIan Lepore struct gpioc_pin_event *event; 714ff3468acSIan Lepore union { 715ff3468acSIan Lepore struct gpio_event_summary sum; 716ff3468acSIan Lepore struct gpio_event_detail evt; 717ff3468acSIan Lepore uint8_t data[1]; 718ff3468acSIan Lepore } recbuf; 719ff3468acSIan Lepore size_t recsize; 720ff3468acSIan Lepore int err; 721ff3468acSIan Lepore 722ff3468acSIan Lepore if ((err = devfs_get_cdevpriv((void **)&priv)) != 0) 723ff3468acSIan Lepore return (err); 724ff3468acSIan Lepore 725ff3468acSIan Lepore if (priv->report_option == GPIO_EVENT_REPORT_SUMMARY) 726ff3468acSIan Lepore recsize = sizeof(struct gpio_event_summary); 727ff3468acSIan Lepore else 728ff3468acSIan Lepore recsize = sizeof(struct gpio_event_detail); 729ff3468acSIan Lepore 730ff3468acSIan Lepore if (uio->uio_resid < recsize) 731ff3468acSIan Lepore return (EINVAL); 732ff3468acSIan Lepore 733ff3468acSIan Lepore mtx_lock(&priv->mtx); 734ff3468acSIan Lepore while (priv->evidx_head == priv->evidx_tail) { 735ff3468acSIan Lepore if (SLIST_EMPTY(&priv->pins)) { 736ff3468acSIan Lepore err = ENXIO; 737ff3468acSIan Lepore break; 738ff3468acSIan Lepore } else if (ioflag & O_NONBLOCK) { 739ff3468acSIan Lepore err = EWOULDBLOCK; 740ff3468acSIan Lepore break; 741ff3468acSIan Lepore } else { 742ff3468acSIan Lepore err = mtx_sleep(priv, &priv->mtx, PCATCH, "gpintr", 0); 743ff3468acSIan Lepore if (err != 0) 744ff3468acSIan Lepore break; 745ff3468acSIan Lepore } 746ff3468acSIan Lepore } 747ff3468acSIan Lepore 748ff3468acSIan Lepore while (err == 0 && uio->uio_resid >= recsize && 749ff3468acSIan Lepore priv->evidx_tail != priv->evidx_head) { 750ff3468acSIan Lepore event = next_tail_event(priv); 751ff3468acSIan Lepore if (priv->report_option == GPIO_EVENT_REPORT_SUMMARY) { 752ff3468acSIan Lepore recbuf.sum.gp_first_time = event->event_time; 753ff3468acSIan Lepore recbuf.sum.gp_pin = event->privpin->pin->pin->pin; 754ff3468acSIan Lepore recbuf.sum.gp_count = event->privpin->eventcount; 755ff3468acSIan Lepore recbuf.sum.gp_first_state = event->event_pin_state; 756ff3468acSIan Lepore event = next_tail_event(priv); 757ff3468acSIan Lepore recbuf.sum.gp_last_time = event->event_time; 758ff3468acSIan Lepore recbuf.sum.gp_last_state = event->event_pin_state; 759ff3468acSIan Lepore event->privpin->eventcount = 0; 760ff3468acSIan Lepore event->privpin->firstevent = 0; 761ff3468acSIan Lepore } else { 762ff3468acSIan Lepore recbuf.evt.gp_time = event->event_time; 763ff3468acSIan Lepore recbuf.evt.gp_pin = event->privpin->pin->pin->pin; 764ff3468acSIan Lepore recbuf.evt.gp_pinstate = event->event_pin_state; 765ff3468acSIan Lepore } 766ff3468acSIan Lepore mtx_unlock(&priv->mtx); 767ff3468acSIan Lepore err = uiomove(recbuf.data, recsize, uio); 768ff3468acSIan Lepore mtx_lock(&priv->mtx); 769ff3468acSIan Lepore } 770ff3468acSIan Lepore mtx_unlock(&priv->mtx); 771ff3468acSIan Lepore return (err); 772ff3468acSIan Lepore } 773ff3468acSIan Lepore 7746b34b16eSOleksandr Tymoshenko static int 7756b34b16eSOleksandr Tymoshenko gpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, 7766b34b16eSOleksandr Tymoshenko struct thread *td) 7776b34b16eSOleksandr Tymoshenko { 778d752f0f6SLuiz Otavio O Souza device_t bus; 7796b34b16eSOleksandr Tymoshenko int max_pin, res; 7806b34b16eSOleksandr Tymoshenko struct gpioc_softc *sc = cdev->si_drv1; 781ff3468acSIan Lepore struct gpioc_cdevpriv *priv; 7826b34b16eSOleksandr Tymoshenko struct gpio_pin pin; 7836b34b16eSOleksandr Tymoshenko struct gpio_req req; 784e1275c68SIan Lepore struct gpio_access_32 *a32; 785e1275c68SIan Lepore struct gpio_config_32 *c32; 786ff3468acSIan Lepore struct gpio_event_config *evcfg; 787ff3468acSIan Lepore uint32_t caps, intrflags; 7886b34b16eSOleksandr Tymoshenko 789d752f0f6SLuiz Otavio O Souza bus = GPIO_GET_BUS(sc->sc_pdev); 790d752f0f6SLuiz Otavio O Souza if (bus == NULL) 791d752f0f6SLuiz Otavio O Souza return (EINVAL); 7926b34b16eSOleksandr Tymoshenko switch (cmd) { 7936b34b16eSOleksandr Tymoshenko case GPIOMAXPIN: 7946b34b16eSOleksandr Tymoshenko max_pin = -1; 7956b34b16eSOleksandr Tymoshenko res = GPIO_PIN_MAX(sc->sc_pdev, &max_pin); 7966b34b16eSOleksandr Tymoshenko bcopy(&max_pin, arg, sizeof(max_pin)); 7976b34b16eSOleksandr Tymoshenko break; 7986b34b16eSOleksandr Tymoshenko case GPIOGETCONFIG: 7996b34b16eSOleksandr Tymoshenko bcopy(arg, &pin, sizeof(pin)); 8006b34b16eSOleksandr Tymoshenko dprintf("get config pin %d\n", pin.gp_pin); 8016b34b16eSOleksandr Tymoshenko res = GPIO_PIN_GETFLAGS(sc->sc_pdev, pin.gp_pin, 8026b34b16eSOleksandr Tymoshenko &pin.gp_flags); 8036b34b16eSOleksandr Tymoshenko /* Fail early */ 8046b34b16eSOleksandr Tymoshenko if (res) 8056b34b16eSOleksandr Tymoshenko break; 806ff3468acSIan Lepore res = devfs_get_cdevpriv((void **)&priv); 807ff3468acSIan Lepore if (res) 808ff3468acSIan Lepore break; 809ff3468acSIan Lepore pin.gp_flags |= gpioc_get_intr_config(sc, priv, 810ff3468acSIan Lepore pin.gp_pin); 8116b34b16eSOleksandr Tymoshenko GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &pin.gp_caps); 812d752f0f6SLuiz Otavio O Souza GPIOBUS_PIN_GETNAME(bus, pin.gp_pin, pin.gp_name); 8136b34b16eSOleksandr Tymoshenko bcopy(&pin, arg, sizeof(pin)); 8146b34b16eSOleksandr Tymoshenko break; 8156b34b16eSOleksandr Tymoshenko case GPIOSETCONFIG: 8166b34b16eSOleksandr Tymoshenko bcopy(arg, &pin, sizeof(pin)); 8176b34b16eSOleksandr Tymoshenko dprintf("set config pin %d\n", pin.gp_pin); 818ff3468acSIan Lepore res = devfs_get_cdevpriv((void **)&priv); 819ff3468acSIan Lepore if (res != 0) 820ff3468acSIan Lepore break; 821667357dcSLuiz Otavio O Souza res = GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &caps); 822ff3468acSIan Lepore if (res != 0) 823ff3468acSIan Lepore break; 824667357dcSLuiz Otavio O Souza res = gpio_check_flags(caps, pin.gp_flags); 825ff3468acSIan Lepore if (res != 0) 826ff3468acSIan Lepore break; 827ff3468acSIan Lepore intrflags = pin.gp_flags & GPIO_INTR_MASK; 828ff3468acSIan Lepore /* 829ff3468acSIan Lepore * We can do only edge interrupts, and only if the 830ff3468acSIan Lepore * hardware supports that interrupt type on that pin. 831ff3468acSIan Lepore */ 832ff3468acSIan Lepore switch (intrflags) { 833ff3468acSIan Lepore case GPIO_INTR_NONE: 834ff3468acSIan Lepore break; 835ff3468acSIan Lepore case GPIO_INTR_EDGE_RISING: 836ff3468acSIan Lepore case GPIO_INTR_EDGE_FALLING: 837ff3468acSIan Lepore case GPIO_INTR_EDGE_BOTH: 838ff3468acSIan Lepore if ((intrflags & caps) == 0) 839ff3468acSIan Lepore res = EOPNOTSUPP; 840ff3468acSIan Lepore break; 841ff3468acSIan Lepore default: 842ff3468acSIan Lepore res = EINVAL; 843ff3468acSIan Lepore break; 844ff3468acSIan Lepore } 845ff3468acSIan Lepore if (res != 0) 846ff3468acSIan Lepore break; 8476b34b16eSOleksandr Tymoshenko res = GPIO_PIN_SETFLAGS(sc->sc_pdev, pin.gp_pin, 848ff3468acSIan Lepore (pin.gp_flags & ~GPIO_INTR_MASK)); 849ff3468acSIan Lepore if (res != 0) 850ff3468acSIan Lepore break; 851ff3468acSIan Lepore res = gpioc_set_intr_config(sc, priv, pin.gp_pin, 852ff3468acSIan Lepore intrflags); 8536b34b16eSOleksandr Tymoshenko break; 8546b34b16eSOleksandr Tymoshenko case GPIOGET: 8556b34b16eSOleksandr Tymoshenko bcopy(arg, &req, sizeof(req)); 8566b34b16eSOleksandr Tymoshenko res = GPIO_PIN_GET(sc->sc_pdev, req.gp_pin, 8576b34b16eSOleksandr Tymoshenko &req.gp_value); 8586b34b16eSOleksandr Tymoshenko dprintf("read pin %d -> %d\n", 8596b34b16eSOleksandr Tymoshenko req.gp_pin, req.gp_value); 8606b34b16eSOleksandr Tymoshenko bcopy(&req, arg, sizeof(req)); 8616b34b16eSOleksandr Tymoshenko break; 8626b34b16eSOleksandr Tymoshenko case GPIOSET: 8636b34b16eSOleksandr Tymoshenko bcopy(arg, &req, sizeof(req)); 8646b34b16eSOleksandr Tymoshenko res = GPIO_PIN_SET(sc->sc_pdev, req.gp_pin, 8656b34b16eSOleksandr Tymoshenko req.gp_value); 8666b34b16eSOleksandr Tymoshenko dprintf("write pin %d -> %d\n", 8676b34b16eSOleksandr Tymoshenko req.gp_pin, req.gp_value); 8686b34b16eSOleksandr Tymoshenko break; 8696b34b16eSOleksandr Tymoshenko case GPIOTOGGLE: 8706b34b16eSOleksandr Tymoshenko bcopy(arg, &req, sizeof(req)); 8716b34b16eSOleksandr Tymoshenko dprintf("toggle pin %d\n", 8726b34b16eSOleksandr Tymoshenko req.gp_pin); 8736b34b16eSOleksandr Tymoshenko res = GPIO_PIN_TOGGLE(sc->sc_pdev, req.gp_pin); 8746b34b16eSOleksandr Tymoshenko break; 875d752f0f6SLuiz Otavio O Souza case GPIOSETNAME: 876d752f0f6SLuiz Otavio O Souza bcopy(arg, &pin, sizeof(pin)); 877d752f0f6SLuiz Otavio O Souza dprintf("set name on pin %d\n", pin.gp_pin); 878d752f0f6SLuiz Otavio O Souza res = GPIOBUS_PIN_SETNAME(bus, pin.gp_pin, 879d752f0f6SLuiz Otavio O Souza pin.gp_name); 880d752f0f6SLuiz Otavio O Souza break; 881e1275c68SIan Lepore case GPIOACCESS32: 882e1275c68SIan Lepore a32 = (struct gpio_access_32 *)arg; 883e1275c68SIan Lepore res = GPIO_PIN_ACCESS_32(sc->sc_pdev, a32->first_pin, 88420105d31SIan Lepore a32->clear_pins, a32->change_pins, &a32->orig_pins); 885e1275c68SIan Lepore break; 886e1275c68SIan Lepore case GPIOCONFIG32: 887e1275c68SIan Lepore c32 = (struct gpio_config_32 *)arg; 888e1275c68SIan Lepore res = GPIO_PIN_CONFIG_32(sc->sc_pdev, c32->first_pin, 889e1275c68SIan Lepore c32->num_pins, c32->pin_flags); 890e1275c68SIan Lepore break; 891ff3468acSIan Lepore case GPIOCONFIGEVENTS: 892ff3468acSIan Lepore evcfg = (struct gpio_event_config *)arg; 893ff3468acSIan Lepore res = devfs_get_cdevpriv((void **)&priv); 894ff3468acSIan Lepore if (res != 0) 895ff3468acSIan Lepore break; 896ff3468acSIan Lepore /* If any pins have been configured, changes aren't allowed. */ 897ff3468acSIan Lepore if (!SLIST_EMPTY(&priv->pins)) { 898ff3468acSIan Lepore res = EINVAL; 899ff3468acSIan Lepore break; 900ff3468acSIan Lepore } 901ff3468acSIan Lepore if (evcfg->gp_report_type != GPIO_EVENT_REPORT_DETAIL && 902ff3468acSIan Lepore evcfg->gp_report_type != GPIO_EVENT_REPORT_SUMMARY) { 903ff3468acSIan Lepore res = EINVAL; 904ff3468acSIan Lepore break; 905ff3468acSIan Lepore } 906ff3468acSIan Lepore priv->report_option = evcfg->gp_report_type; 907ff3468acSIan Lepore /* Reallocate the events buffer if the user wants it bigger. */ 908ff3468acSIan Lepore if (priv->report_option == GPIO_EVENT_REPORT_DETAIL && 909ff3468acSIan Lepore priv->numevents < evcfg->gp_fifo_size) { 910ff3468acSIan Lepore free(priv->events, M_GPIOC); 911ff3468acSIan Lepore priv->numevents = evcfg->gp_fifo_size; 912ff3468acSIan Lepore priv->events = malloc(priv->numevents * 913ff3468acSIan Lepore sizeof(struct gpio_event_detail), M_GPIOC, 914ff3468acSIan Lepore M_WAITOK | M_ZERO); 915ff3468acSIan Lepore priv->evidx_head = priv->evidx_tail = 0; 916ff3468acSIan Lepore } 917ff3468acSIan Lepore break; 918ff3468acSIan Lepore case FIONBIO: 919ff3468acSIan Lepore /* 920ff3468acSIan Lepore * This dummy handler is necessary to prevent fcntl() 921ff3468acSIan Lepore * from failing. The actual handling of non-blocking IO 922ff3468acSIan Lepore * is done using the O_NONBLOCK ioflag passed to the 923ff3468acSIan Lepore * read() syscall. 924ff3468acSIan Lepore */ 925ff3468acSIan Lepore res = 0; 926ff3468acSIan Lepore break; 927ff3468acSIan Lepore case FIOASYNC: 928ff3468acSIan Lepore res = devfs_get_cdevpriv((void **)&priv); 929ff3468acSIan Lepore if (res == 0) { 930ff3468acSIan Lepore if (*(int *)arg == FASYNC) 931ff3468acSIan Lepore priv->async = true; 932ff3468acSIan Lepore else 933ff3468acSIan Lepore priv->async = false; 934ff3468acSIan Lepore } 935ff3468acSIan Lepore break; 936ff3468acSIan Lepore case FIOGETOWN: 937ff3468acSIan Lepore res = devfs_get_cdevpriv((void **)&priv); 938ff3468acSIan Lepore if (res == 0) 939ff3468acSIan Lepore *(int *)arg = fgetown(&priv->sigio); 940ff3468acSIan Lepore break; 941ff3468acSIan Lepore case FIOSETOWN: 942ff3468acSIan Lepore res = devfs_get_cdevpriv((void **)&priv); 943ff3468acSIan Lepore if (res == 0) 944ff3468acSIan Lepore res = fsetown(*(int *)arg, &priv->sigio); 945ff3468acSIan Lepore break; 9466b34b16eSOleksandr Tymoshenko default: 9476b34b16eSOleksandr Tymoshenko return (ENOTTY); 9486b34b16eSOleksandr Tymoshenko break; 9496b34b16eSOleksandr Tymoshenko } 9506b34b16eSOleksandr Tymoshenko 9516b34b16eSOleksandr Tymoshenko return (res); 9526b34b16eSOleksandr Tymoshenko } 9536b34b16eSOleksandr Tymoshenko 954ff3468acSIan Lepore static int 955ff3468acSIan Lepore gpioc_poll(struct cdev *dev, int events, struct thread *td) 956ff3468acSIan Lepore { 957ff3468acSIan Lepore struct gpioc_cdevpriv *priv; 958ff3468acSIan Lepore int err; 959ff3468acSIan Lepore int revents; 960ff3468acSIan Lepore 961ff3468acSIan Lepore revents = 0; 962ff3468acSIan Lepore 963ff3468acSIan Lepore err = devfs_get_cdevpriv((void **)&priv); 964ff3468acSIan Lepore if (err != 0) { 965ff3468acSIan Lepore revents = POLLERR; 966ff3468acSIan Lepore return (revents); 967ff3468acSIan Lepore } 968ff3468acSIan Lepore 969ff3468acSIan Lepore if (SLIST_EMPTY(&priv->pins)) { 970ff3468acSIan Lepore revents = POLLHUP; 971ff3468acSIan Lepore return (revents); 972ff3468acSIan Lepore } 973ff3468acSIan Lepore 974ff3468acSIan Lepore if (events & (POLLIN | POLLRDNORM)) { 975ff3468acSIan Lepore if (priv->evidx_head != priv->evidx_tail) 976ff3468acSIan Lepore revents |= events & (POLLIN | POLLRDNORM); 977ff3468acSIan Lepore else 978ff3468acSIan Lepore selrecord(td, &priv->selinfo); 979ff3468acSIan Lepore } 980ff3468acSIan Lepore 981ff3468acSIan Lepore return (revents); 982ff3468acSIan Lepore } 983ff3468acSIan Lepore 984ff3468acSIan Lepore static int 985ff3468acSIan Lepore gpioc_kqfilter(struct cdev *dev, struct knote *kn) 986ff3468acSIan Lepore { 987ff3468acSIan Lepore struct gpioc_cdevpriv *priv; 988ff3468acSIan Lepore struct knlist *knlist; 989ff3468acSIan Lepore int err; 990ff3468acSIan Lepore 991ff3468acSIan Lepore err = devfs_get_cdevpriv((void **)&priv); 992ff3468acSIan Lepore if (err != 0) 993ff3468acSIan Lepore return err; 994ff3468acSIan Lepore 995ff3468acSIan Lepore if (SLIST_EMPTY(&priv->pins)) 996ff3468acSIan Lepore return (ENXIO); 997ff3468acSIan Lepore 998ff3468acSIan Lepore switch(kn->kn_filter) { 999ff3468acSIan Lepore case EVFILT_READ: 1000ff3468acSIan Lepore kn->kn_fop = &gpioc_read_filterops; 1001ff3468acSIan Lepore kn->kn_hook = (void *)priv; 1002ff3468acSIan Lepore break; 1003ff3468acSIan Lepore default: 1004ff3468acSIan Lepore return (EOPNOTSUPP); 1005ff3468acSIan Lepore } 1006ff3468acSIan Lepore 1007ff3468acSIan Lepore knlist = &priv->selinfo.si_note; 1008ff3468acSIan Lepore knlist_add(knlist, kn, 0); 1009ff3468acSIan Lepore 1010ff3468acSIan Lepore return (0); 1011ff3468acSIan Lepore } 1012ff3468acSIan Lepore 1013ff3468acSIan Lepore static int 1014ff3468acSIan Lepore gpioc_kqread(struct knote *kn, long hint) 1015ff3468acSIan Lepore { 1016ff3468acSIan Lepore struct gpioc_cdevpriv *priv = kn->kn_hook; 1017ff3468acSIan Lepore size_t recsize; 1018ff3468acSIan Lepore 1019ff3468acSIan Lepore 1020ff3468acSIan Lepore if (SLIST_EMPTY(&priv->pins)) { 1021ff3468acSIan Lepore kn->kn_flags |= EV_EOF; 1022ff3468acSIan Lepore return (1); 1023ff3468acSIan Lepore } else { 1024ff3468acSIan Lepore if (priv->evidx_head != priv->evidx_tail) { 1025ff3468acSIan Lepore if (priv->report_option == GPIO_EVENT_REPORT_SUMMARY) 1026ff3468acSIan Lepore recsize = sizeof(struct gpio_event_summary); 1027ff3468acSIan Lepore else 1028ff3468acSIan Lepore recsize = sizeof(struct gpio_event_detail); 1029ff3468acSIan Lepore kn->kn_data = recsize * number_of_events(priv); 1030ff3468acSIan Lepore return (1); 1031ff3468acSIan Lepore } 1032ff3468acSIan Lepore } 1033ff3468acSIan Lepore return (0); 1034ff3468acSIan Lepore } 1035ff3468acSIan Lepore 1036ff3468acSIan Lepore static void 1037ff3468acSIan Lepore gpioc_kqdetach(struct knote *kn) 1038ff3468acSIan Lepore { 1039ff3468acSIan Lepore struct gpioc_cdevpriv *priv = kn->kn_hook; 1040ff3468acSIan Lepore struct knlist *knlist = &priv->selinfo.si_note; 1041ff3468acSIan Lepore 1042ff3468acSIan Lepore knlist_remove(knlist, kn, 0); 1043ff3468acSIan Lepore } 1044ff3468acSIan Lepore 10456b34b16eSOleksandr Tymoshenko static device_method_t gpioc_methods[] = { 10466b34b16eSOleksandr Tymoshenko /* Device interface */ 10476b34b16eSOleksandr Tymoshenko DEVMETHOD(device_probe, gpioc_probe), 10486b34b16eSOleksandr Tymoshenko DEVMETHOD(device_attach, gpioc_attach), 10496b34b16eSOleksandr Tymoshenko DEVMETHOD(device_detach, gpioc_detach), 10506b34b16eSOleksandr Tymoshenko DEVMETHOD(device_shutdown, bus_generic_shutdown), 10516b34b16eSOleksandr Tymoshenko DEVMETHOD(device_suspend, bus_generic_suspend), 10526b34b16eSOleksandr Tymoshenko DEVMETHOD(device_resume, bus_generic_resume), 10536b34b16eSOleksandr Tymoshenko 1054e2a1919dSOleksandr Tymoshenko DEVMETHOD_END 10556b34b16eSOleksandr Tymoshenko }; 10566b34b16eSOleksandr Tymoshenko 105754873b4cSAndrew Thompson driver_t gpioc_driver = { 10586b34b16eSOleksandr Tymoshenko "gpioc", 10596b34b16eSOleksandr Tymoshenko gpioc_methods, 10606b34b16eSOleksandr Tymoshenko sizeof(struct gpioc_softc) 10616b34b16eSOleksandr Tymoshenko }; 10626b34b16eSOleksandr Tymoshenko 1063d885615aSJohn Baldwin DRIVER_MODULE(gpioc, gpio, gpioc_driver, 0, 0); 10646b34b16eSOleksandr Tymoshenko MODULE_VERSION(gpioc, 1); 1065