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