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; 2727dc4d511SEd Maste unsigned int consistency_a __diagused; 2737dc4d511SEd 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; 323*6e62d9a5SDimitry Andric unsigned int consistency_a __diagused; 324*6e62d9a5SDimitry Andric unsigned int consistency_b __diagused; 325ff3468acSIan Lepore 326ff3468acSIan Lepore consistency_a = 0; 327ff3468acSIan Lepore consistency_b = 0; 328ff3468acSIan Lepore mtx_assert(&intr_conf->mtx, MA_OWNED); 329ff3468acSIan Lepore mtx_lock(&priv->mtx); 330ff3468acSIan Lepore SLIST_FOREACH_SAFE(priv_link, &intr_conf->privs, next, priv_link_temp) { 331ff3468acSIan Lepore if (priv_link->priv == priv) { 332ff3468acSIan Lepore SLIST_REMOVE(&intr_conf->privs, priv_link, gpioc_privs, 333ff3468acSIan Lepore next); 334ff3468acSIan Lepore free(priv_link, M_GPIOC); 335ff3468acSIan Lepore consistency_a++; 336ff3468acSIan Lepore } 337ff3468acSIan Lepore } 338ff3468acSIan Lepore KASSERT(consistency_a <= 1, 339ff3468acSIan Lepore ("inconsistent links between pin config and cdevpriv")); 340ff3468acSIan Lepore SLIST_FOREACH_SAFE(pin_link, &priv->pins, next, pin_link_temp) { 341ff3468acSIan Lepore if (pin_link->pin == intr_conf) { 342ff3468acSIan Lepore /* 343ff3468acSIan Lepore * If the pin we're removing has events in the priv's 344ff3468acSIan Lepore * event fifo, we can't leave dangling pointers from 345ff3468acSIan Lepore * those events to the gpioc_pins struct we're about to 346ff3468acSIan Lepore * free. We also can't remove random items and leave 347ff3468acSIan Lepore * holes in the events fifo, so just empty it out. 348ff3468acSIan Lepore */ 349ff3468acSIan Lepore if (pin_link->eventcount > 0) { 350ff3468acSIan Lepore priv->evidx_head = priv->evidx_tail = 0; 351ff3468acSIan Lepore } 352ff3468acSIan Lepore SLIST_REMOVE(&priv->pins, pin_link, gpioc_pins, next); 353ff3468acSIan Lepore free(pin_link, M_GPIOC); 354ff3468acSIan Lepore consistency_b++; 355ff3468acSIan Lepore } 356ff3468acSIan Lepore } 357ff3468acSIan Lepore KASSERT(consistency_a == consistency_b, 358ff3468acSIan Lepore ("inconsistent links between pin config and cdevpriv")); 359ff3468acSIan Lepore mtx_unlock(&priv->mtx); 360ff3468acSIan Lepore 361ff3468acSIan Lepore return (0); 362ff3468acSIan Lepore } 363ff3468acSIan Lepore 364ff3468acSIan Lepore static bool 365ff3468acSIan Lepore gpioc_intr_reconfig_allowed(struct gpioc_cdevpriv *priv, 366ff3468acSIan Lepore struct gpioc_pin_intr *intr_conf) 367ff3468acSIan Lepore { 368ff3468acSIan Lepore struct gpioc_privs *priv_link; 369ff3468acSIan Lepore 370ff3468acSIan Lepore mtx_assert(&intr_conf->mtx, MA_OWNED); 371ff3468acSIan Lepore 372ff3468acSIan Lepore if (SLIST_EMPTY(&intr_conf->privs)) 373ff3468acSIan Lepore return (true); 374ff3468acSIan Lepore 375ff3468acSIan Lepore SLIST_FOREACH(priv_link, &intr_conf->privs, next) { 376ff3468acSIan Lepore if (priv_link->priv != priv) 377ff3468acSIan Lepore return (false); 378ff3468acSIan Lepore } 379ff3468acSIan Lepore 380ff3468acSIan Lepore return (true); 381ff3468acSIan Lepore } 382ff3468acSIan Lepore 383ff3468acSIan Lepore 384ff3468acSIan Lepore static uint32_t 385ff3468acSIan Lepore gpioc_get_intr_config(struct gpioc_softc *sc, struct gpioc_cdevpriv *priv, 386ff3468acSIan Lepore uint32_t pin) 387ff3468acSIan Lepore { 388ff3468acSIan Lepore struct gpioc_pin_intr *intr_conf = &sc->sc_pin_intr[pin]; 389ff3468acSIan Lepore struct gpioc_privs *priv_link; 390ff3468acSIan Lepore uint32_t flags; 391ff3468acSIan Lepore 392ff3468acSIan Lepore flags = intr_conf->pin->flags; 393ff3468acSIan Lepore 394ff3468acSIan Lepore if (flags == 0) 395ff3468acSIan Lepore return (0); 396ff3468acSIan Lepore 397ff3468acSIan Lepore mtx_lock(&intr_conf->mtx); 398ff3468acSIan Lepore SLIST_FOREACH(priv_link, &intr_conf->privs, next) { 399ff3468acSIan Lepore if (priv_link->priv == priv) { 400ff3468acSIan Lepore flags |= GPIO_INTR_ATTACHED; 401ff3468acSIan Lepore break; 402ff3468acSIan Lepore } 403ff3468acSIan Lepore } 404ff3468acSIan Lepore mtx_unlock(&intr_conf->mtx); 405ff3468acSIan Lepore 406ff3468acSIan Lepore return (flags); 407ff3468acSIan Lepore } 408ff3468acSIan Lepore 409ff3468acSIan Lepore static int 410ff3468acSIan Lepore gpioc_set_intr_config(struct gpioc_softc *sc, struct gpioc_cdevpriv *priv, 411ff3468acSIan Lepore uint32_t pin, uint32_t flags) 412ff3468acSIan Lepore { 413ff3468acSIan Lepore struct gpioc_pin_intr *intr_conf = &sc->sc_pin_intr[pin]; 414ff3468acSIan Lepore int res; 415ff3468acSIan Lepore 416ff3468acSIan Lepore res = 0; 417ff3468acSIan Lepore if (intr_conf->pin->flags == 0 && flags == 0) { 418ff3468acSIan Lepore /* No interrupt configured and none requested: Do nothing. */ 419ff3468acSIan Lepore return (0); 420ff3468acSIan Lepore } 421ff3468acSIan Lepore mtx_lock(&intr_conf->mtx); 422ff3468acSIan Lepore while (intr_conf->config_locked == true) 423ff3468acSIan Lepore mtx_sleep(&intr_conf->config_locked, &intr_conf->mtx, 0, 424ff3468acSIan Lepore "gpicfg", 0); 425ff3468acSIan Lepore if (intr_conf->pin->flags == 0 && flags != 0) { 426ff3468acSIan Lepore /* 427ff3468acSIan Lepore * No interrupt is configured, but one is requested: Allocate 428ff3468acSIan Lepore * and setup interrupt on the according pin. 429ff3468acSIan Lepore */ 430ff3468acSIan Lepore res = gpioc_allocate_pin_intr(intr_conf, flags); 431ff3468acSIan Lepore if (res == 0) 432ff3468acSIan Lepore res = gpioc_attach_priv_pin(priv, intr_conf); 433ff3468acSIan Lepore if (res == EEXIST) 434ff3468acSIan Lepore res = 0; 435ff3468acSIan Lepore } else if (intr_conf->pin->flags == flags) { 436ff3468acSIan Lepore /* 437ff3468acSIan Lepore * Same interrupt requested as already configured: Attach the 438ff3468acSIan Lepore * cdevpriv to the corresponding pin. 439ff3468acSIan Lepore */ 440ff3468acSIan Lepore res = gpioc_attach_priv_pin(priv, intr_conf); 441ff3468acSIan Lepore if (res == EEXIST) 442ff3468acSIan Lepore res = 0; 443ff3468acSIan Lepore } else if (intr_conf->pin->flags != 0 && flags == 0) { 444ff3468acSIan Lepore /* 445ff3468acSIan Lepore * Interrupt configured, but none requested: Teardown and 446ff3468acSIan Lepore * release the pin when no other cdevpriv is attached. Otherwise 447ff3468acSIan Lepore * just detach pin and cdevpriv from each other. 448ff3468acSIan Lepore */ 449ff3468acSIan Lepore if (gpioc_intr_reconfig_allowed(priv, intr_conf)) { 450ff3468acSIan Lepore res = gpioc_release_pin_intr(intr_conf); 451ff3468acSIan Lepore } 452ff3468acSIan Lepore if (res == 0) 453ff3468acSIan Lepore res = gpioc_detach_priv_pin(priv, intr_conf); 454ff3468acSIan Lepore } else { 455ff3468acSIan Lepore /* 456ff3468acSIan Lepore * Other flag requested than configured: Reconfigure when no 457ff3468acSIan Lepore * other cdevpriv is are attached to the pin. 458ff3468acSIan Lepore */ 459ff3468acSIan Lepore if (!gpioc_intr_reconfig_allowed(priv, intr_conf)) 460ff3468acSIan Lepore res = EBUSY; 461ff3468acSIan Lepore else { 462ff3468acSIan Lepore res = gpioc_release_pin_intr(intr_conf); 463ff3468acSIan Lepore if (res == 0) 464ff3468acSIan Lepore res = gpioc_allocate_pin_intr(intr_conf, flags); 465ff3468acSIan Lepore if (res == 0) 466ff3468acSIan Lepore res = gpioc_attach_priv_pin(priv, intr_conf); 467ff3468acSIan Lepore if (res == EEXIST) 468ff3468acSIan Lepore res = 0; 469ff3468acSIan Lepore } 470ff3468acSIan Lepore } 471ff3468acSIan Lepore mtx_unlock(&intr_conf->mtx); 472ff3468acSIan Lepore 473ff3468acSIan Lepore return (res); 474ff3468acSIan Lepore } 475ff3468acSIan Lepore 476ff3468acSIan Lepore static void 477ff3468acSIan Lepore gpioc_interrupt_handler(void *arg) 478ff3468acSIan Lepore { 479ff3468acSIan Lepore struct gpioc_pin_intr *intr_conf; 480ff3468acSIan Lepore struct gpioc_privs *privs; 481ff3468acSIan Lepore struct gpioc_softc *sc; 482ff3468acSIan Lepore sbintime_t evtime; 483ff3468acSIan Lepore uint32_t pin_state; 484ff3468acSIan Lepore 485ff3468acSIan Lepore intr_conf = arg; 486ff3468acSIan Lepore sc = intr_conf->sc; 487ff3468acSIan Lepore 488ff3468acSIan Lepore /* Capture time and pin state first. */ 489ff3468acSIan Lepore evtime = sbinuptime(); 490ff3468acSIan Lepore if (intr_conf->pin->flags & GPIO_INTR_EDGE_BOTH) 491ff3468acSIan Lepore GPIO_PIN_GET(sc->sc_pdev, intr_conf->pin->pin, &pin_state); 492ff3468acSIan Lepore else if (intr_conf->pin->flags & GPIO_INTR_EDGE_RISING) 493ff3468acSIan Lepore pin_state = true; 494ff3468acSIan Lepore else 495ff3468acSIan Lepore pin_state = false; 496ff3468acSIan Lepore 497ff3468acSIan Lepore mtx_lock(&intr_conf->mtx); 498ff3468acSIan Lepore 499ff3468acSIan Lepore if (intr_conf->config_locked == true) { 500ff3468acSIan Lepore ddevice_printf(sc->sc_dev, "Interrupt configuration in " 501ff3468acSIan Lepore "progress. Discarding interrupt on pin %d.\n", 502ff3468acSIan Lepore intr_conf->pin->pin); 503ff3468acSIan Lepore mtx_unlock(&intr_conf->mtx); 504ff3468acSIan Lepore return; 505ff3468acSIan Lepore } 506ff3468acSIan Lepore 507ff3468acSIan Lepore if (SLIST_EMPTY(&intr_conf->privs)) { 508ff3468acSIan Lepore ddevice_printf(sc->sc_dev, "No file descriptor associated with " 509ff3468acSIan Lepore "occurred interrupt on pin %d.\n", intr_conf->pin->pin); 510ff3468acSIan Lepore mtx_unlock(&intr_conf->mtx); 511ff3468acSIan Lepore return; 512ff3468acSIan Lepore } 513ff3468acSIan Lepore 514ff3468acSIan Lepore SLIST_FOREACH(privs, &intr_conf->privs, next) { 515ff3468acSIan Lepore struct gpioc_cdevpriv *priv = privs->priv; 516ff3468acSIan Lepore struct gpioc_pins *privpin; 517ff3468acSIan Lepore struct gpioc_pin_event *event; 518ff3468acSIan Lepore mtx_lock(&priv->mtx); 519ff3468acSIan Lepore SLIST_FOREACH(privpin, &priv->pins, next) { 520ff3468acSIan Lepore if (privpin->pin == intr_conf) 521ff3468acSIan Lepore break; 522ff3468acSIan Lepore } 523ff3468acSIan Lepore if (privpin == NULL) { 524ff3468acSIan Lepore /* Should be impossible. */ 525ff3468acSIan Lepore ddevice_printf(sc->sc_dev, "Cannot find privpin\n"); 526ff3468acSIan Lepore mtx_unlock(&priv->mtx); 527ff3468acSIan Lepore continue; 528ff3468acSIan Lepore } 529ff3468acSIan Lepore 530ff3468acSIan Lepore if (priv->report_option == GPIO_EVENT_REPORT_DETAIL) { 531ff3468acSIan Lepore event = next_head_event(priv); 532ff3468acSIan Lepore /* If head is overtaking tail, advance tail. */ 533ff3468acSIan Lepore if (priv->evidx_head == priv->evidx_tail) 534ff3468acSIan Lepore next_tail_event(priv); 535ff3468acSIan Lepore } else { 536ff3468acSIan Lepore if (privpin->eventcount > 0) 537ff3468acSIan Lepore event = &priv->events[privpin->firstevent + 1]; 538ff3468acSIan Lepore else { 539ff3468acSIan Lepore privpin->firstevent = priv->evidx_head; 540ff3468acSIan Lepore event = next_head_event(priv); 541ff3468acSIan Lepore event->privpin = privpin; 542ff3468acSIan Lepore event->event_time = evtime; 543ff3468acSIan Lepore event->event_pin_state = pin_state; 544ff3468acSIan Lepore event = next_head_event(priv); 545ff3468acSIan Lepore } 546ff3468acSIan Lepore ++privpin->eventcount; 547ff3468acSIan Lepore } 548ff3468acSIan Lepore event->privpin = privpin; 549ff3468acSIan Lepore event->event_time = evtime; 550ff3468acSIan Lepore event->event_pin_state = pin_state; 551ff3468acSIan Lepore wakeup(priv); 552ff3468acSIan Lepore selwakeup(&priv->selinfo); 553ff3468acSIan Lepore KNOTE_LOCKED(&priv->selinfo.si_note, 0); 554ff3468acSIan Lepore if (priv->async == true && priv->sigio != NULL) 555ff3468acSIan Lepore pgsigio(&priv->sigio, SIGIO, 0); 556ff3468acSIan Lepore mtx_unlock(&priv->mtx); 557ff3468acSIan Lepore } 558ff3468acSIan Lepore 559ff3468acSIan Lepore mtx_unlock(&intr_conf->mtx); 560ff3468acSIan Lepore } 561ff3468acSIan Lepore 5626b34b16eSOleksandr Tymoshenko static int 5636b34b16eSOleksandr Tymoshenko gpioc_probe(device_t dev) 5646b34b16eSOleksandr Tymoshenko { 5656b34b16eSOleksandr Tymoshenko device_set_desc(dev, "GPIO controller"); 5666b34b16eSOleksandr Tymoshenko return (0); 5676b34b16eSOleksandr Tymoshenko } 5686b34b16eSOleksandr Tymoshenko 5696b34b16eSOleksandr Tymoshenko static int 5706b34b16eSOleksandr Tymoshenko gpioc_attach(device_t dev) 5716b34b16eSOleksandr Tymoshenko { 5724723c14fSLuiz Otavio O Souza int err; 5734723c14fSLuiz Otavio O Souza struct gpioc_softc *sc; 5744723c14fSLuiz Otavio O Souza struct make_dev_args devargs; 5756b34b16eSOleksandr Tymoshenko 5764723c14fSLuiz Otavio O Souza sc = device_get_softc(dev); 5776b34b16eSOleksandr Tymoshenko sc->sc_dev = dev; 5786b34b16eSOleksandr Tymoshenko sc->sc_pdev = device_get_parent(dev); 5796b34b16eSOleksandr Tymoshenko sc->sc_unit = device_get_unit(dev); 580ff3468acSIan Lepore 581ff3468acSIan Lepore err = GPIO_PIN_MAX(sc->sc_pdev, &sc->sc_npins); 582ff3468acSIan Lepore sc->sc_npins++; /* Number of pins is one more than max pin number. */ 583ff3468acSIan Lepore if (err != 0) 584ff3468acSIan Lepore return (err); 585ff3468acSIan Lepore sc->sc_pin_intr = malloc(sizeof(struct gpioc_pin_intr) * sc->sc_npins, 586ff3468acSIan Lepore M_GPIOC, M_WAITOK | M_ZERO); 587f2a7b434SHans Petter Selasky for (int i = 0; i < sc->sc_npins; i++) { 588ff3468acSIan Lepore sc->sc_pin_intr[i].pin = malloc(sizeof(struct gpiobus_pin), 589ff3468acSIan Lepore M_GPIOC, M_WAITOK | M_ZERO); 590ff3468acSIan Lepore sc->sc_pin_intr[i].sc = sc; 591ff3468acSIan Lepore sc->sc_pin_intr[i].pin->pin = i; 592ff3468acSIan Lepore sc->sc_pin_intr[i].pin->dev = sc->sc_pdev; 593ff3468acSIan Lepore mtx_init(&sc->sc_pin_intr[i].mtx, "gpioc pin", NULL, MTX_DEF); 594ff3468acSIan Lepore SLIST_INIT(&sc->sc_pin_intr[i].privs); 595ff3468acSIan Lepore } 596ff3468acSIan Lepore 5974723c14fSLuiz Otavio O Souza make_dev_args_init(&devargs); 5984723c14fSLuiz Otavio O Souza devargs.mda_devsw = &gpioc_cdevsw; 5994723c14fSLuiz Otavio O Souza devargs.mda_uid = UID_ROOT; 6004723c14fSLuiz Otavio O Souza devargs.mda_gid = GID_WHEEL; 6014723c14fSLuiz Otavio O Souza devargs.mda_mode = 0600; 6024723c14fSLuiz Otavio O Souza devargs.mda_si_drv1 = sc; 6034723c14fSLuiz Otavio O Souza err = make_dev_s(&devargs, &sc->sc_ctl_dev, "gpioc%d", sc->sc_unit); 6044723c14fSLuiz Otavio O Souza if (err != 0) { 605ff3468acSIan Lepore device_printf(dev, "Failed to create gpioc%d", sc->sc_unit); 6066b34b16eSOleksandr Tymoshenko return (ENXIO); 6076b34b16eSOleksandr Tymoshenko } 6086b34b16eSOleksandr Tymoshenko 6096b34b16eSOleksandr Tymoshenko return (0); 6106b34b16eSOleksandr Tymoshenko } 6116b34b16eSOleksandr Tymoshenko 6126b34b16eSOleksandr Tymoshenko static int 6136b34b16eSOleksandr Tymoshenko gpioc_detach(device_t dev) 6146b34b16eSOleksandr Tymoshenko { 6156b34b16eSOleksandr Tymoshenko struct gpioc_softc *sc = device_get_softc(dev); 6166b34b16eSOleksandr Tymoshenko int err; 6176b34b16eSOleksandr Tymoshenko 6186de0a4faSOleksandr Tymoshenko if (sc->sc_ctl_dev) 6196b34b16eSOleksandr Tymoshenko destroy_dev(sc->sc_ctl_dev); 6206b34b16eSOleksandr Tymoshenko 621f2a7b434SHans Petter Selasky for (int i = 0; i < sc->sc_npins; i++) { 622ff3468acSIan Lepore mtx_destroy(&sc->sc_pin_intr[i].mtx); 6233c6b5956SAndriy Gapon free(sc->sc_pin_intr[i].pin, M_GPIOC); 624ff3468acSIan Lepore } 625ff3468acSIan Lepore free(sc->sc_pin_intr, M_GPIOC); 626ff3468acSIan Lepore 6276b34b16eSOleksandr Tymoshenko if ((err = bus_generic_detach(dev)) != 0) 6286b34b16eSOleksandr Tymoshenko return (err); 6296b34b16eSOleksandr Tymoshenko 6306b34b16eSOleksandr Tymoshenko return (0); 6316b34b16eSOleksandr Tymoshenko } 6326b34b16eSOleksandr Tymoshenko 633ff3468acSIan Lepore static void 634ff3468acSIan Lepore gpioc_cdevpriv_dtor(void *data) 635ff3468acSIan Lepore { 636ff3468acSIan Lepore struct gpioc_cdevpriv *priv; 637ff3468acSIan Lepore struct gpioc_privs *priv_link, *priv_link_temp; 638ff3468acSIan Lepore struct gpioc_pins *pin_link, *pin_link_temp; 6397dc4d511SEd Maste unsigned int consistency __diagused; 640ff3468acSIan Lepore 641ff3468acSIan Lepore priv = data; 642ff3468acSIan Lepore 643ff3468acSIan Lepore SLIST_FOREACH_SAFE(pin_link, &priv->pins, next, pin_link_temp) { 644ff3468acSIan Lepore consistency = 0; 645ff3468acSIan Lepore mtx_lock(&pin_link->pin->mtx); 646ff3468acSIan Lepore while (pin_link->pin->config_locked == true) 647ff3468acSIan Lepore mtx_sleep(&pin_link->pin->config_locked, 648ff3468acSIan Lepore &pin_link->pin->mtx, 0, "gpicfg", 0); 649ff3468acSIan Lepore SLIST_FOREACH_SAFE(priv_link, &pin_link->pin->privs, next, 650ff3468acSIan Lepore priv_link_temp) { 651ff3468acSIan Lepore if (priv_link->priv == priv) { 652ff3468acSIan Lepore SLIST_REMOVE(&pin_link->pin->privs, priv_link, 653ff3468acSIan Lepore gpioc_privs, next); 654ff3468acSIan Lepore free(priv_link, M_GPIOC); 655ff3468acSIan Lepore consistency++; 656ff3468acSIan Lepore } 657ff3468acSIan Lepore } 658ff3468acSIan Lepore KASSERT(consistency == 1, 659ff3468acSIan Lepore ("inconsistent links between pin config and cdevpriv")); 660ff3468acSIan Lepore if (gpioc_intr_reconfig_allowed(priv, pin_link->pin)) { 661ff3468acSIan Lepore gpioc_release_pin_intr(pin_link->pin); 662ff3468acSIan Lepore } 663ff3468acSIan Lepore mtx_unlock(&pin_link->pin->mtx); 664ff3468acSIan Lepore SLIST_REMOVE(&priv->pins, pin_link, gpioc_pins, next); 665ff3468acSIan Lepore free(pin_link, M_GPIOC); 666ff3468acSIan Lepore } 667ff3468acSIan Lepore 668ff3468acSIan Lepore wakeup(&priv); 669ff3468acSIan Lepore knlist_clear(&priv->selinfo.si_note, 0); 670ff3468acSIan Lepore seldrain(&priv->selinfo); 671ff3468acSIan Lepore knlist_destroy(&priv->selinfo.si_note); 672ff3468acSIan Lepore funsetown(&priv->sigio); 673ff3468acSIan Lepore 674ff3468acSIan Lepore mtx_destroy(&priv->mtx); 675ff3468acSIan Lepore free(priv->events, M_GPIOC); 676ff3468acSIan Lepore free(data, M_GPIOC); 677ff3468acSIan Lepore } 678ff3468acSIan Lepore 679ff3468acSIan Lepore static int 680ff3468acSIan Lepore gpioc_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 681ff3468acSIan Lepore { 682ff3468acSIan Lepore struct gpioc_cdevpriv *priv; 683ff3468acSIan Lepore int err; 684ff3468acSIan Lepore 685ff3468acSIan Lepore priv = malloc(sizeof(*priv), M_GPIOC, M_WAITOK | M_ZERO); 686ff3468acSIan Lepore priv->sc = dev->si_drv1; 687ff3468acSIan Lepore priv->report_option = GPIO_EVENT_REPORT_DETAIL; 688ff3468acSIan Lepore err = devfs_set_cdevpriv(priv, gpioc_cdevpriv_dtor); 689ff3468acSIan Lepore if (err != 0) { 690ff3468acSIan Lepore gpioc_cdevpriv_dtor(priv); 691ff3468acSIan Lepore return (err); 692ff3468acSIan Lepore } 693ff3468acSIan Lepore mtx_init(&priv->mtx, "gpioc priv", NULL, MTX_DEF); 694ff3468acSIan Lepore knlist_init_mtx(&priv->selinfo.si_note, &priv->mtx); 695ff3468acSIan Lepore 696ff3468acSIan Lepore /* 697ff3468acSIan Lepore * Allocate a circular buffer for events. The scheme we use for summary 698ff3468acSIan Lepore * reporting assumes there will always be a pair of events available to 699ff3468acSIan Lepore * record the first/last events on any pin, so we allocate 2 * npins. 700ff3468acSIan Lepore * Even though we actually default to detailed event reporting, 2 * 701ff3468acSIan Lepore * npins isn't a horrible fifo size for that either. 702ff3468acSIan Lepore */ 703ff3468acSIan Lepore priv->numevents = priv->sc->sc_npins * 2; 704ff3468acSIan Lepore priv->events = malloc(priv->numevents * sizeof(struct gpio_event_detail), 705ff3468acSIan Lepore M_GPIOC, M_WAITOK | M_ZERO); 706ff3468acSIan Lepore 707ff3468acSIan Lepore return (0); 708ff3468acSIan Lepore } 709ff3468acSIan Lepore 710ff3468acSIan Lepore static int 711ff3468acSIan Lepore gpioc_read(struct cdev *dev, struct uio *uio, int ioflag) 712ff3468acSIan Lepore { 713ff3468acSIan Lepore struct gpioc_cdevpriv *priv; 714ff3468acSIan Lepore struct gpioc_pin_event *event; 715ff3468acSIan Lepore union { 716ff3468acSIan Lepore struct gpio_event_summary sum; 717ff3468acSIan Lepore struct gpio_event_detail evt; 718ff3468acSIan Lepore uint8_t data[1]; 719ff3468acSIan Lepore } recbuf; 720ff3468acSIan Lepore size_t recsize; 721ff3468acSIan Lepore int err; 722ff3468acSIan Lepore 723ff3468acSIan Lepore if ((err = devfs_get_cdevpriv((void **)&priv)) != 0) 724ff3468acSIan Lepore return (err); 725ff3468acSIan Lepore 726ff3468acSIan Lepore if (priv->report_option == GPIO_EVENT_REPORT_SUMMARY) 727ff3468acSIan Lepore recsize = sizeof(struct gpio_event_summary); 728ff3468acSIan Lepore else 729ff3468acSIan Lepore recsize = sizeof(struct gpio_event_detail); 730ff3468acSIan Lepore 731ff3468acSIan Lepore if (uio->uio_resid < recsize) 732ff3468acSIan Lepore return (EINVAL); 733ff3468acSIan Lepore 734ff3468acSIan Lepore mtx_lock(&priv->mtx); 735ff3468acSIan Lepore while (priv->evidx_head == priv->evidx_tail) { 736ff3468acSIan Lepore if (SLIST_EMPTY(&priv->pins)) { 737ff3468acSIan Lepore err = ENXIO; 738ff3468acSIan Lepore break; 739ff3468acSIan Lepore } else if (ioflag & O_NONBLOCK) { 740ff3468acSIan Lepore err = EWOULDBLOCK; 741ff3468acSIan Lepore break; 742ff3468acSIan Lepore } else { 743ff3468acSIan Lepore err = mtx_sleep(priv, &priv->mtx, PCATCH, "gpintr", 0); 744ff3468acSIan Lepore if (err != 0) 745ff3468acSIan Lepore break; 746ff3468acSIan Lepore } 747ff3468acSIan Lepore } 748ff3468acSIan Lepore 749ff3468acSIan Lepore while (err == 0 && uio->uio_resid >= recsize && 750ff3468acSIan Lepore priv->evidx_tail != priv->evidx_head) { 751ff3468acSIan Lepore event = next_tail_event(priv); 752ff3468acSIan Lepore if (priv->report_option == GPIO_EVENT_REPORT_SUMMARY) { 753ff3468acSIan Lepore recbuf.sum.gp_first_time = event->event_time; 754ff3468acSIan Lepore recbuf.sum.gp_pin = event->privpin->pin->pin->pin; 755ff3468acSIan Lepore recbuf.sum.gp_count = event->privpin->eventcount; 756ff3468acSIan Lepore recbuf.sum.gp_first_state = event->event_pin_state; 757ff3468acSIan Lepore event = next_tail_event(priv); 758ff3468acSIan Lepore recbuf.sum.gp_last_time = event->event_time; 759ff3468acSIan Lepore recbuf.sum.gp_last_state = event->event_pin_state; 760ff3468acSIan Lepore event->privpin->eventcount = 0; 761ff3468acSIan Lepore event->privpin->firstevent = 0; 762ff3468acSIan Lepore } else { 763ff3468acSIan Lepore recbuf.evt.gp_time = event->event_time; 764ff3468acSIan Lepore recbuf.evt.gp_pin = event->privpin->pin->pin->pin; 765ff3468acSIan Lepore recbuf.evt.gp_pinstate = event->event_pin_state; 766ff3468acSIan Lepore } 767ff3468acSIan Lepore mtx_unlock(&priv->mtx); 768ff3468acSIan Lepore err = uiomove(recbuf.data, recsize, uio); 769ff3468acSIan Lepore mtx_lock(&priv->mtx); 770ff3468acSIan Lepore } 771ff3468acSIan Lepore mtx_unlock(&priv->mtx); 772ff3468acSIan Lepore return (err); 773ff3468acSIan Lepore } 774ff3468acSIan Lepore 7756b34b16eSOleksandr Tymoshenko static int 7766b34b16eSOleksandr Tymoshenko gpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag, 7776b34b16eSOleksandr Tymoshenko struct thread *td) 7786b34b16eSOleksandr Tymoshenko { 779d752f0f6SLuiz Otavio O Souza device_t bus; 7806b34b16eSOleksandr Tymoshenko int max_pin, res; 7816b34b16eSOleksandr Tymoshenko struct gpioc_softc *sc = cdev->si_drv1; 782ff3468acSIan Lepore struct gpioc_cdevpriv *priv; 7836b34b16eSOleksandr Tymoshenko struct gpio_pin pin; 7846b34b16eSOleksandr Tymoshenko struct gpio_req req; 785e1275c68SIan Lepore struct gpio_access_32 *a32; 786e1275c68SIan Lepore struct gpio_config_32 *c32; 787ff3468acSIan Lepore struct gpio_event_config *evcfg; 788ff3468acSIan Lepore uint32_t caps, intrflags; 7896b34b16eSOleksandr Tymoshenko 790d752f0f6SLuiz Otavio O Souza bus = GPIO_GET_BUS(sc->sc_pdev); 791d752f0f6SLuiz Otavio O Souza if (bus == NULL) 792d752f0f6SLuiz Otavio O Souza return (EINVAL); 7936b34b16eSOleksandr Tymoshenko switch (cmd) { 7946b34b16eSOleksandr Tymoshenko case GPIOMAXPIN: 7956b34b16eSOleksandr Tymoshenko max_pin = -1; 7966b34b16eSOleksandr Tymoshenko res = GPIO_PIN_MAX(sc->sc_pdev, &max_pin); 7976b34b16eSOleksandr Tymoshenko bcopy(&max_pin, arg, sizeof(max_pin)); 7986b34b16eSOleksandr Tymoshenko break; 7996b34b16eSOleksandr Tymoshenko case GPIOGETCONFIG: 8006b34b16eSOleksandr Tymoshenko bcopy(arg, &pin, sizeof(pin)); 8016b34b16eSOleksandr Tymoshenko dprintf("get config pin %d\n", pin.gp_pin); 8026b34b16eSOleksandr Tymoshenko res = GPIO_PIN_GETFLAGS(sc->sc_pdev, pin.gp_pin, 8036b34b16eSOleksandr Tymoshenko &pin.gp_flags); 8046b34b16eSOleksandr Tymoshenko /* Fail early */ 8056b34b16eSOleksandr Tymoshenko if (res) 8066b34b16eSOleksandr Tymoshenko break; 807ff3468acSIan Lepore res = devfs_get_cdevpriv((void **)&priv); 808ff3468acSIan Lepore if (res) 809ff3468acSIan Lepore break; 810ff3468acSIan Lepore pin.gp_flags |= gpioc_get_intr_config(sc, priv, 811ff3468acSIan Lepore pin.gp_pin); 8126b34b16eSOleksandr Tymoshenko GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &pin.gp_caps); 813d752f0f6SLuiz Otavio O Souza GPIOBUS_PIN_GETNAME(bus, pin.gp_pin, pin.gp_name); 8146b34b16eSOleksandr Tymoshenko bcopy(&pin, arg, sizeof(pin)); 8156b34b16eSOleksandr Tymoshenko break; 8166b34b16eSOleksandr Tymoshenko case GPIOSETCONFIG: 8176b34b16eSOleksandr Tymoshenko bcopy(arg, &pin, sizeof(pin)); 8186b34b16eSOleksandr Tymoshenko dprintf("set config pin %d\n", pin.gp_pin); 819ff3468acSIan Lepore res = devfs_get_cdevpriv((void **)&priv); 820ff3468acSIan Lepore if (res != 0) 821ff3468acSIan Lepore break; 822667357dcSLuiz Otavio O Souza res = GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &caps); 823ff3468acSIan Lepore if (res != 0) 824ff3468acSIan Lepore break; 825667357dcSLuiz Otavio O Souza res = gpio_check_flags(caps, pin.gp_flags); 826ff3468acSIan Lepore if (res != 0) 827ff3468acSIan Lepore break; 828ff3468acSIan Lepore intrflags = pin.gp_flags & GPIO_INTR_MASK; 829ff3468acSIan Lepore /* 830ff3468acSIan Lepore * We can do only edge interrupts, and only if the 831ff3468acSIan Lepore * hardware supports that interrupt type on that pin. 832ff3468acSIan Lepore */ 833ff3468acSIan Lepore switch (intrflags) { 834ff3468acSIan Lepore case GPIO_INTR_NONE: 835ff3468acSIan Lepore break; 836ff3468acSIan Lepore case GPIO_INTR_EDGE_RISING: 837ff3468acSIan Lepore case GPIO_INTR_EDGE_FALLING: 838ff3468acSIan Lepore case GPIO_INTR_EDGE_BOTH: 839ff3468acSIan Lepore if ((intrflags & caps) == 0) 840ff3468acSIan Lepore res = EOPNOTSUPP; 841ff3468acSIan Lepore break; 842ff3468acSIan Lepore default: 843ff3468acSIan Lepore res = EINVAL; 844ff3468acSIan Lepore break; 845ff3468acSIan Lepore } 846ff3468acSIan Lepore if (res != 0) 847ff3468acSIan Lepore break; 8486b34b16eSOleksandr Tymoshenko res = GPIO_PIN_SETFLAGS(sc->sc_pdev, pin.gp_pin, 849ff3468acSIan Lepore (pin.gp_flags & ~GPIO_INTR_MASK)); 850ff3468acSIan Lepore if (res != 0) 851ff3468acSIan Lepore break; 852ff3468acSIan Lepore res = gpioc_set_intr_config(sc, priv, pin.gp_pin, 853ff3468acSIan Lepore intrflags); 8546b34b16eSOleksandr Tymoshenko break; 8556b34b16eSOleksandr Tymoshenko case GPIOGET: 8566b34b16eSOleksandr Tymoshenko bcopy(arg, &req, sizeof(req)); 8576b34b16eSOleksandr Tymoshenko res = GPIO_PIN_GET(sc->sc_pdev, req.gp_pin, 8586b34b16eSOleksandr Tymoshenko &req.gp_value); 8596b34b16eSOleksandr Tymoshenko dprintf("read pin %d -> %d\n", 8606b34b16eSOleksandr Tymoshenko req.gp_pin, req.gp_value); 8616b34b16eSOleksandr Tymoshenko bcopy(&req, arg, sizeof(req)); 8626b34b16eSOleksandr Tymoshenko break; 8636b34b16eSOleksandr Tymoshenko case GPIOSET: 8646b34b16eSOleksandr Tymoshenko bcopy(arg, &req, sizeof(req)); 8656b34b16eSOleksandr Tymoshenko res = GPIO_PIN_SET(sc->sc_pdev, req.gp_pin, 8666b34b16eSOleksandr Tymoshenko req.gp_value); 8676b34b16eSOleksandr Tymoshenko dprintf("write pin %d -> %d\n", 8686b34b16eSOleksandr Tymoshenko req.gp_pin, req.gp_value); 8696b34b16eSOleksandr Tymoshenko break; 8706b34b16eSOleksandr Tymoshenko case GPIOTOGGLE: 8716b34b16eSOleksandr Tymoshenko bcopy(arg, &req, sizeof(req)); 8726b34b16eSOleksandr Tymoshenko dprintf("toggle pin %d\n", 8736b34b16eSOleksandr Tymoshenko req.gp_pin); 8746b34b16eSOleksandr Tymoshenko res = GPIO_PIN_TOGGLE(sc->sc_pdev, req.gp_pin); 8756b34b16eSOleksandr Tymoshenko break; 876d752f0f6SLuiz Otavio O Souza case GPIOSETNAME: 877d752f0f6SLuiz Otavio O Souza bcopy(arg, &pin, sizeof(pin)); 878d752f0f6SLuiz Otavio O Souza dprintf("set name on pin %d\n", pin.gp_pin); 879d752f0f6SLuiz Otavio O Souza res = GPIOBUS_PIN_SETNAME(bus, pin.gp_pin, 880d752f0f6SLuiz Otavio O Souza pin.gp_name); 881d752f0f6SLuiz Otavio O Souza break; 882e1275c68SIan Lepore case GPIOACCESS32: 883e1275c68SIan Lepore a32 = (struct gpio_access_32 *)arg; 884e1275c68SIan Lepore res = GPIO_PIN_ACCESS_32(sc->sc_pdev, a32->first_pin, 88520105d31SIan Lepore a32->clear_pins, a32->change_pins, &a32->orig_pins); 886e1275c68SIan Lepore break; 887e1275c68SIan Lepore case GPIOCONFIG32: 888e1275c68SIan Lepore c32 = (struct gpio_config_32 *)arg; 889e1275c68SIan Lepore res = GPIO_PIN_CONFIG_32(sc->sc_pdev, c32->first_pin, 890e1275c68SIan Lepore c32->num_pins, c32->pin_flags); 891e1275c68SIan Lepore break; 892ff3468acSIan Lepore case GPIOCONFIGEVENTS: 893ff3468acSIan Lepore evcfg = (struct gpio_event_config *)arg; 894ff3468acSIan Lepore res = devfs_get_cdevpriv((void **)&priv); 895ff3468acSIan Lepore if (res != 0) 896ff3468acSIan Lepore break; 897ff3468acSIan Lepore /* If any pins have been configured, changes aren't allowed. */ 898ff3468acSIan Lepore if (!SLIST_EMPTY(&priv->pins)) { 899ff3468acSIan Lepore res = EINVAL; 900ff3468acSIan Lepore break; 901ff3468acSIan Lepore } 902ff3468acSIan Lepore if (evcfg->gp_report_type != GPIO_EVENT_REPORT_DETAIL && 903ff3468acSIan Lepore evcfg->gp_report_type != GPIO_EVENT_REPORT_SUMMARY) { 904ff3468acSIan Lepore res = EINVAL; 905ff3468acSIan Lepore break; 906ff3468acSIan Lepore } 907ff3468acSIan Lepore priv->report_option = evcfg->gp_report_type; 908ff3468acSIan Lepore /* Reallocate the events buffer if the user wants it bigger. */ 909ff3468acSIan Lepore if (priv->report_option == GPIO_EVENT_REPORT_DETAIL && 910ff3468acSIan Lepore priv->numevents < evcfg->gp_fifo_size) { 911ff3468acSIan Lepore free(priv->events, M_GPIOC); 912ff3468acSIan Lepore priv->numevents = evcfg->gp_fifo_size; 913ff3468acSIan Lepore priv->events = malloc(priv->numevents * 914ff3468acSIan Lepore sizeof(struct gpio_event_detail), M_GPIOC, 915ff3468acSIan Lepore M_WAITOK | M_ZERO); 916ff3468acSIan Lepore priv->evidx_head = priv->evidx_tail = 0; 917ff3468acSIan Lepore } 918ff3468acSIan Lepore break; 919ff3468acSIan Lepore case FIONBIO: 920ff3468acSIan Lepore /* 921ff3468acSIan Lepore * This dummy handler is necessary to prevent fcntl() 922ff3468acSIan Lepore * from failing. The actual handling of non-blocking IO 923ff3468acSIan Lepore * is done using the O_NONBLOCK ioflag passed to the 924ff3468acSIan Lepore * read() syscall. 925ff3468acSIan Lepore */ 926ff3468acSIan Lepore res = 0; 927ff3468acSIan Lepore break; 928ff3468acSIan Lepore case FIOASYNC: 929ff3468acSIan Lepore res = devfs_get_cdevpriv((void **)&priv); 930ff3468acSIan Lepore if (res == 0) { 931ff3468acSIan Lepore if (*(int *)arg == FASYNC) 932ff3468acSIan Lepore priv->async = true; 933ff3468acSIan Lepore else 934ff3468acSIan Lepore priv->async = false; 935ff3468acSIan Lepore } 936ff3468acSIan Lepore break; 937ff3468acSIan Lepore case FIOGETOWN: 938ff3468acSIan Lepore res = devfs_get_cdevpriv((void **)&priv); 939ff3468acSIan Lepore if (res == 0) 940ff3468acSIan Lepore *(int *)arg = fgetown(&priv->sigio); 941ff3468acSIan Lepore break; 942ff3468acSIan Lepore case FIOSETOWN: 943ff3468acSIan Lepore res = devfs_get_cdevpriv((void **)&priv); 944ff3468acSIan Lepore if (res == 0) 945ff3468acSIan Lepore res = fsetown(*(int *)arg, &priv->sigio); 946ff3468acSIan Lepore break; 9476b34b16eSOleksandr Tymoshenko default: 9486b34b16eSOleksandr Tymoshenko return (ENOTTY); 9496b34b16eSOleksandr Tymoshenko break; 9506b34b16eSOleksandr Tymoshenko } 9516b34b16eSOleksandr Tymoshenko 9526b34b16eSOleksandr Tymoshenko return (res); 9536b34b16eSOleksandr Tymoshenko } 9546b34b16eSOleksandr Tymoshenko 955ff3468acSIan Lepore static int 956ff3468acSIan Lepore gpioc_poll(struct cdev *dev, int events, struct thread *td) 957ff3468acSIan Lepore { 958ff3468acSIan Lepore struct gpioc_cdevpriv *priv; 959ff3468acSIan Lepore int err; 960ff3468acSIan Lepore int revents; 961ff3468acSIan Lepore 962ff3468acSIan Lepore revents = 0; 963ff3468acSIan Lepore 964ff3468acSIan Lepore err = devfs_get_cdevpriv((void **)&priv); 965ff3468acSIan Lepore if (err != 0) { 966ff3468acSIan Lepore revents = POLLERR; 967ff3468acSIan Lepore return (revents); 968ff3468acSIan Lepore } 969ff3468acSIan Lepore 970ff3468acSIan Lepore if (SLIST_EMPTY(&priv->pins)) { 971ff3468acSIan Lepore revents = POLLHUP; 972ff3468acSIan Lepore return (revents); 973ff3468acSIan Lepore } 974ff3468acSIan Lepore 975ff3468acSIan Lepore if (events & (POLLIN | POLLRDNORM)) { 976ff3468acSIan Lepore if (priv->evidx_head != priv->evidx_tail) 977ff3468acSIan Lepore revents |= events & (POLLIN | POLLRDNORM); 978ff3468acSIan Lepore else 979ff3468acSIan Lepore selrecord(td, &priv->selinfo); 980ff3468acSIan Lepore } 981ff3468acSIan Lepore 982ff3468acSIan Lepore return (revents); 983ff3468acSIan Lepore } 984ff3468acSIan Lepore 985ff3468acSIan Lepore static int 986ff3468acSIan Lepore gpioc_kqfilter(struct cdev *dev, struct knote *kn) 987ff3468acSIan Lepore { 988ff3468acSIan Lepore struct gpioc_cdevpriv *priv; 989ff3468acSIan Lepore struct knlist *knlist; 990ff3468acSIan Lepore int err; 991ff3468acSIan Lepore 992ff3468acSIan Lepore err = devfs_get_cdevpriv((void **)&priv); 993ff3468acSIan Lepore if (err != 0) 994ff3468acSIan Lepore return err; 995ff3468acSIan Lepore 996ff3468acSIan Lepore if (SLIST_EMPTY(&priv->pins)) 997ff3468acSIan Lepore return (ENXIO); 998ff3468acSIan Lepore 999ff3468acSIan Lepore switch(kn->kn_filter) { 1000ff3468acSIan Lepore case EVFILT_READ: 1001ff3468acSIan Lepore kn->kn_fop = &gpioc_read_filterops; 1002ff3468acSIan Lepore kn->kn_hook = (void *)priv; 1003ff3468acSIan Lepore break; 1004ff3468acSIan Lepore default: 1005ff3468acSIan Lepore return (EOPNOTSUPP); 1006ff3468acSIan Lepore } 1007ff3468acSIan Lepore 1008ff3468acSIan Lepore knlist = &priv->selinfo.si_note; 1009ff3468acSIan Lepore knlist_add(knlist, kn, 0); 1010ff3468acSIan Lepore 1011ff3468acSIan Lepore return (0); 1012ff3468acSIan Lepore } 1013ff3468acSIan Lepore 1014ff3468acSIan Lepore static int 1015ff3468acSIan Lepore gpioc_kqread(struct knote *kn, long hint) 1016ff3468acSIan Lepore { 1017ff3468acSIan Lepore struct gpioc_cdevpriv *priv = kn->kn_hook; 1018ff3468acSIan Lepore size_t recsize; 1019ff3468acSIan Lepore 1020ff3468acSIan Lepore 1021ff3468acSIan Lepore if (SLIST_EMPTY(&priv->pins)) { 1022ff3468acSIan Lepore kn->kn_flags |= EV_EOF; 1023ff3468acSIan Lepore return (1); 1024ff3468acSIan Lepore } else { 1025ff3468acSIan Lepore if (priv->evidx_head != priv->evidx_tail) { 1026ff3468acSIan Lepore if (priv->report_option == GPIO_EVENT_REPORT_SUMMARY) 1027ff3468acSIan Lepore recsize = sizeof(struct gpio_event_summary); 1028ff3468acSIan Lepore else 1029ff3468acSIan Lepore recsize = sizeof(struct gpio_event_detail); 1030ff3468acSIan Lepore kn->kn_data = recsize * number_of_events(priv); 1031ff3468acSIan Lepore return (1); 1032ff3468acSIan Lepore } 1033ff3468acSIan Lepore } 1034ff3468acSIan Lepore return (0); 1035ff3468acSIan Lepore } 1036ff3468acSIan Lepore 1037ff3468acSIan Lepore static void 1038ff3468acSIan Lepore gpioc_kqdetach(struct knote *kn) 1039ff3468acSIan Lepore { 1040ff3468acSIan Lepore struct gpioc_cdevpriv *priv = kn->kn_hook; 1041ff3468acSIan Lepore struct knlist *knlist = &priv->selinfo.si_note; 1042ff3468acSIan Lepore 1043ff3468acSIan Lepore knlist_remove(knlist, kn, 0); 1044ff3468acSIan Lepore } 1045ff3468acSIan Lepore 10466b34b16eSOleksandr Tymoshenko static device_method_t gpioc_methods[] = { 10476b34b16eSOleksandr Tymoshenko /* Device interface */ 10486b34b16eSOleksandr Tymoshenko DEVMETHOD(device_probe, gpioc_probe), 10496b34b16eSOleksandr Tymoshenko DEVMETHOD(device_attach, gpioc_attach), 10506b34b16eSOleksandr Tymoshenko DEVMETHOD(device_detach, gpioc_detach), 10516b34b16eSOleksandr Tymoshenko DEVMETHOD(device_shutdown, bus_generic_shutdown), 10526b34b16eSOleksandr Tymoshenko DEVMETHOD(device_suspend, bus_generic_suspend), 10536b34b16eSOleksandr Tymoshenko DEVMETHOD(device_resume, bus_generic_resume), 10546b34b16eSOleksandr Tymoshenko 1055e2a1919dSOleksandr Tymoshenko DEVMETHOD_END 10566b34b16eSOleksandr Tymoshenko }; 10576b34b16eSOleksandr Tymoshenko 105854873b4cSAndrew Thompson driver_t gpioc_driver = { 10596b34b16eSOleksandr Tymoshenko "gpioc", 10606b34b16eSOleksandr Tymoshenko gpioc_methods, 10616b34b16eSOleksandr Tymoshenko sizeof(struct gpioc_softc) 10626b34b16eSOleksandr Tymoshenko }; 10636b34b16eSOleksandr Tymoshenko 1064d885615aSJohn Baldwin DRIVER_MODULE(gpioc, gpio, gpioc_driver, 0, 0); 10656b34b16eSOleksandr Tymoshenko MODULE_VERSION(gpioc, 1); 1066