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