1 1.26 brad /* $NetBSD: gpiosim.c,v 1.26 2023/11/24 15:13:35 brad Exp $ */ 2 1.1 mbalmer /* $OpenBSD: gpiosim.c,v 1.1 2008/11/23 18:46:49 mbalmer Exp $ */ 3 1.1 mbalmer 4 1.1 mbalmer /* 5 1.15 mbalmer * Copyright (c) 2007 - 2011, 2013 Marc Balmer <marc (at) msys.ch> 6 1.1 mbalmer * All rights reserved. 7 1.1 mbalmer * 8 1.1 mbalmer * Permission to use, copy, modify, and distribute this software for any 9 1.1 mbalmer * purpose with or without fee is hereby granted, provided that the above 10 1.1 mbalmer * copyright notice and this permission notice appear in all copies. 11 1.1 mbalmer * 12 1.1 mbalmer * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 1.1 mbalmer * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 1.1 mbalmer * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 1.1 mbalmer * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 1.1 mbalmer * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN 17 1.1 mbalmer * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 18 1.1 mbalmer * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 1.1 mbalmer */ 20 1.1 mbalmer 21 1.15 mbalmer /* 64 bit wide GPIO simulator */ 22 1.10 mbalmer 23 1.1 mbalmer #include <sys/param.h> 24 1.1 mbalmer #include <sys/systm.h> 25 1.1 mbalmer #include <sys/device.h> 26 1.1 mbalmer #include <sys/gpio.h> 27 1.1 mbalmer #include <sys/malloc.h> 28 1.8 mbalmer #include <sys/module.h> 29 1.1 mbalmer #include <sys/sysctl.h> 30 1.1 mbalmer #include <sys/ioccom.h> 31 1.1 mbalmer #include <dev/gpio/gpiovar.h> 32 1.25 brad #include <sys/callout.h> 33 1.25 brad #include <sys/workqueue.h> 34 1.1 mbalmer 35 1.17 uebayasi #include "gpiosim.h" 36 1.18 christos #include "ioconf.h" 37 1.17 uebayasi 38 1.15 mbalmer #define GPIOSIM_NPINS 64 39 1.1 mbalmer 40 1.25 brad struct gpiosim_irq { 41 1.25 brad int (*sc_gpio_irqfunc)(void *); 42 1.25 brad void *sc_gpio_irqarg; 43 1.25 brad int sc_gpio_irqmode; 44 1.25 brad bool sc_gpio_irqtriggered; 45 1.25 brad }; 46 1.25 brad 47 1.1 mbalmer struct gpiosim_softc { 48 1.2 mbalmer device_t sc_dev; 49 1.6 mbalmer device_t sc_gdev; /* gpio that attaches here */ 50 1.15 mbalmer uint64_t sc_state; 51 1.1 mbalmer struct gpio_chipset_tag sc_gpio_gc; 52 1.1 mbalmer gpio_pin_t sc_gpio_pins[GPIOSIM_NPINS]; 53 1.25 brad struct gpiosim_irq sc_gpio_irqs[GPIOSIM_NPINS]; 54 1.1 mbalmer 55 1.6 mbalmer struct sysctllog *sc_log; 56 1.25 brad struct workqueue *sc_wq; 57 1.25 brad callout_t sc_co; 58 1.25 brad bool sc_co_init; 59 1.25 brad bool sc_co_running; 60 1.25 brad int sc_ms; 61 1.25 brad kmutex_t sc_intr_mutex; 62 1.1 mbalmer }; 63 1.1 mbalmer 64 1.8 mbalmer static int gpiosim_match(device_t, cfdata_t, void *); 65 1.8 mbalmer static void gpiosim_attach(device_t, device_t, void *); 66 1.8 mbalmer static int gpiosim_detach(device_t, int); 67 1.8 mbalmer static int gpiosim_sysctl(SYSCTLFN_PROTO); 68 1.25 brad static int gpiosim_ms_sysctl(SYSCTLFN_PROTO); 69 1.8 mbalmer 70 1.8 mbalmer static int gpiosim_pin_read(void *, int); 71 1.8 mbalmer static void gpiosim_pin_write(void *, int, int); 72 1.8 mbalmer static void gpiosim_pin_ctl(void *, int, int); 73 1.1 mbalmer 74 1.25 brad static void * gpiosim_intr_establish(void *, int, int, int, 75 1.25 brad int (*)(void *), void *); 76 1.25 brad static void gpiosim_intr_disestablish(void *, void *); 77 1.25 brad static bool gpiosim_gpio_intrstr(void *, int, int, char *, size_t); 78 1.25 brad 79 1.26 brad void gpiosim_wq(struct work *, void *); 80 1.25 brad void gpiosim_co(void *); 81 1.25 brad 82 1.1 mbalmer CFATTACH_DECL_NEW(gpiosim, sizeof(struct gpiosim_softc), gpiosim_match, 83 1.7 dyoung gpiosim_attach, gpiosim_detach, NULL); 84 1.1 mbalmer 85 1.25 brad int gpiosim_work; 86 1.25 brad 87 1.25 brad #ifndef GPIOSIM_MS 88 1.25 brad #define GPIOSIM_MS 1000 89 1.25 brad #endif 90 1.25 brad 91 1.8 mbalmer static int 92 1.1 mbalmer gpiosim_match(device_t parent, cfdata_t match, void *aux) 93 1.1 mbalmer { 94 1.1 mbalmer return 1; 95 1.1 mbalmer } 96 1.1 mbalmer 97 1.1 mbalmer void 98 1.17 uebayasi gpiosimattach(int num __unused) 99 1.3 mbalmer { 100 1.3 mbalmer cfdata_t cf; 101 1.3 mbalmer int n, err; 102 1.3 mbalmer 103 1.3 mbalmer err = config_cfattach_attach(gpiosim_cd.cd_name, &gpiosim_ca); 104 1.3 mbalmer if (err) 105 1.3 mbalmer printf("%s: unable to register cfattach\n", gpiosim_cd.cd_name); 106 1.3 mbalmer 107 1.17 uebayasi for (n = 0; n < NGPIOSIM; n++) { 108 1.3 mbalmer cf = malloc(sizeof(*cf), M_DEVBUF, M_WAITOK); 109 1.3 mbalmer cf->cf_name = "gpiosim"; 110 1.3 mbalmer cf->cf_atname = "gpiosim"; 111 1.3 mbalmer cf->cf_unit = n; 112 1.3 mbalmer cf->cf_fstate = FSTATE_NOTFOUND; 113 1.3 mbalmer config_attach_pseudo(cf); 114 1.3 mbalmer } 115 1.3 mbalmer } 116 1.3 mbalmer 117 1.8 mbalmer static void 118 1.1 mbalmer gpiosim_attach(device_t parent, device_t self, void *aux) 119 1.1 mbalmer { 120 1.1 mbalmer struct gpiosim_softc *sc = device_private(self); 121 1.1 mbalmer struct gpiobus_attach_args gba; 122 1.6 mbalmer const struct sysctlnode *node; 123 1.1 mbalmer int i; 124 1.25 brad int error = 0; 125 1.1 mbalmer 126 1.2 mbalmer sc->sc_dev = self; 127 1.2 mbalmer 128 1.3 mbalmer printf("%s", device_xname(sc->sc_dev)); 129 1.3 mbalmer 130 1.1 mbalmer /* initialize pin array */ 131 1.1 mbalmer for (i = 0; i < GPIOSIM_NPINS; i++) { 132 1.1 mbalmer sc->sc_gpio_pins[i].pin_num = i; 133 1.1 mbalmer sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT | 134 1.1 mbalmer GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN | 135 1.1 mbalmer GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | 136 1.1 mbalmer GPIO_PIN_INVIN | GPIO_PIN_INVOUT; 137 1.1 mbalmer 138 1.25 brad /* Set up what interrupt types are allowed */ 139 1.25 brad sc->sc_gpio_pins[i].pin_intrcaps = 140 1.25 brad GPIO_INTR_POS_EDGE | 141 1.25 brad GPIO_INTR_NEG_EDGE | 142 1.25 brad GPIO_INTR_DOUBLE_EDGE | 143 1.25 brad GPIO_INTR_HIGH_LEVEL | 144 1.25 brad GPIO_INTR_LOW_LEVEL | 145 1.25 brad GPIO_INTR_MPSAFE; 146 1.25 brad sc->sc_gpio_irqs[i].sc_gpio_irqfunc = NULL; 147 1.25 brad sc->sc_gpio_irqs[i].sc_gpio_irqarg = NULL; 148 1.25 brad sc->sc_gpio_irqs[i].sc_gpio_irqmode = 0; 149 1.25 brad sc->sc_gpio_irqs[i].sc_gpio_irqtriggered = false; 150 1.25 brad 151 1.1 mbalmer /* read initial state */ 152 1.26 brad sc->sc_gpio_pins[i].pin_flags = GPIO_PIN_INPUT; 153 1.26 brad } 154 1.25 brad 155 1.15 mbalmer sc->sc_state = 0; 156 1.25 brad sc->sc_ms = GPIOSIM_MS; 157 1.25 brad sc->sc_co_init = false; 158 1.25 brad 159 1.25 brad mutex_init(&sc->sc_intr_mutex, MUTEX_DEFAULT, IPL_VM); 160 1.1 mbalmer 161 1.15 mbalmer /* create controller tag */ 162 1.15 mbalmer sc->sc_gpio_gc.gp_cookie = sc; 163 1.15 mbalmer sc->sc_gpio_gc.gp_pin_read = gpiosim_pin_read; 164 1.15 mbalmer sc->sc_gpio_gc.gp_pin_write = gpiosim_pin_write; 165 1.15 mbalmer sc->sc_gpio_gc.gp_pin_ctl = gpiosim_pin_ctl; 166 1.25 brad sc->sc_gpio_gc.gp_intr_establish = gpiosim_intr_establish; 167 1.25 brad sc->sc_gpio_gc.gp_intr_disestablish = gpiosim_intr_disestablish; 168 1.25 brad sc->sc_gpio_gc.gp_intr_str = gpiosim_gpio_intrstr; 169 1.15 mbalmer 170 1.15 mbalmer /* gba.gba_name = "gpio"; */ 171 1.15 mbalmer gba.gba_gc = &sc->sc_gpio_gc; 172 1.15 mbalmer gba.gba_pins = sc->sc_gpio_pins; 173 1.15 mbalmer gba.gba_npins = GPIOSIM_NPINS; 174 1.2 mbalmer 175 1.20 maya if (!pmf_device_register(self, NULL, NULL)) 176 1.20 maya aprint_error_dev(self, "couldn't establish power handler\n"); 177 1.4 drochner 178 1.6 mbalmer sysctl_createv(&sc->sc_log, 0, NULL, &node, 179 1.2 mbalmer 0, 180 1.2 mbalmer CTLTYPE_NODE, device_xname(sc->sc_dev), 181 1.2 mbalmer SYSCTL_DESCR("GPIO simulator"), 182 1.2 mbalmer NULL, 0, NULL, 0, 183 1.2 mbalmer CTL_HW, CTL_CREATE, CTL_EOL); 184 1.2 mbalmer 185 1.6 mbalmer if (node == NULL) { 186 1.19 msaitoh aprint_error(": can't create sysctl node\n"); 187 1.2 mbalmer return; 188 1.2 mbalmer } 189 1.2 mbalmer 190 1.6 mbalmer sysctl_createv(&sc->sc_log, 0, &node, NULL, 191 1.2 mbalmer CTLFLAG_READWRITE, 192 1.15 mbalmer CTLTYPE_QUAD, "value", 193 1.2 mbalmer SYSCTL_DESCR("Current GPIO simulator value"), 194 1.14 dsl gpiosim_sysctl, 0, (void *)sc, 0, 195 1.2 mbalmer CTL_CREATE, CTL_EOL); 196 1.2 mbalmer 197 1.25 brad sysctl_createv(&sc->sc_log, 0, &node, NULL, 198 1.25 brad CTLFLAG_READWRITE, 199 1.25 brad CTLTYPE_INT, "ms", 200 1.25 brad SYSCTL_DESCR("Number of ms for level interrupts"), 201 1.25 brad gpiosim_ms_sysctl, 0, &sc->sc_ms, 0, 202 1.25 brad CTL_CREATE, CTL_EOL); 203 1.25 brad 204 1.26 brad error = workqueue_create(&sc->sc_wq, 205 1.26 brad "gsimwq", 206 1.26 brad gpiosim_wq, 207 1.26 brad sc, 208 1.26 brad PRI_NONE, 209 1.26 brad IPL_VM, 210 1.26 brad WQ_MPSAFE); 211 1.25 brad if (error != 0) { 212 1.25 brad aprint_error(": can't create workqueue for interrupts\n"); 213 1.25 brad return; 214 1.25 brad } 215 1.25 brad 216 1.26 brad callout_init(&sc->sc_co, CALLOUT_MPSAFE); 217 1.26 brad callout_setfunc(&sc->sc_co, gpiosim_co, sc); 218 1.25 brad sc->sc_co_running = false; 219 1.25 brad sc->sc_co_init = true; 220 1.25 brad 221 1.19 msaitoh aprint_normal(": simulating %d pins\n", GPIOSIM_NPINS); 222 1.23 thorpej sc->sc_gdev = config_found(self, &gba, gpiobus_print, CFARGS_NONE); 223 1.1 mbalmer } 224 1.1 mbalmer 225 1.8 mbalmer static int 226 1.1 mbalmer gpiosim_detach(device_t self, int flags) 227 1.1 mbalmer { 228 1.2 mbalmer struct gpiosim_softc *sc = device_private(self); 229 1.24 riastrad int error; 230 1.6 mbalmer 231 1.6 mbalmer /* Detach the gpio driver that attached here */ 232 1.24 riastrad error = config_detach_children(self, flags); 233 1.24 riastrad if (error) 234 1.24 riastrad return error; 235 1.2 mbalmer 236 1.5 mbalmer pmf_device_deregister(self); 237 1.25 brad 238 1.6 mbalmer if (sc->sc_log != NULL) { 239 1.6 mbalmer sysctl_teardown(&sc->sc_log); 240 1.6 mbalmer sc->sc_log = NULL; 241 1.2 mbalmer } 242 1.25 brad 243 1.25 brad /* Destroy the workqueue, hope that it is empty */ 244 1.25 brad if (sc->sc_wq != NULL) { 245 1.25 brad workqueue_destroy(sc->sc_wq); 246 1.25 brad } 247 1.25 brad 248 1.25 brad sc->sc_co_running = false; 249 1.25 brad 250 1.25 brad /* Destroy any callouts */ 251 1.25 brad if (sc->sc_co_init) { 252 1.26 brad callout_halt(&sc->sc_co, NULL); 253 1.25 brad callout_destroy(&sc->sc_co); 254 1.25 brad } 255 1.2 mbalmer return 0; 256 1.1 mbalmer } 257 1.1 mbalmer 258 1.8 mbalmer static int 259 1.2 mbalmer gpiosim_sysctl(SYSCTLFN_ARGS) 260 1.1 mbalmer { 261 1.2 mbalmer struct sysctlnode node; 262 1.2 mbalmer struct gpiosim_softc *sc; 263 1.15 mbalmer uint64_t val, error; 264 1.25 brad uint64_t previous_val; 265 1.25 brad int i; 266 1.25 brad struct gpiosim_irq *irq; 267 1.25 brad int t = 0; 268 1.1 mbalmer 269 1.2 mbalmer node = *rnode; 270 1.2 mbalmer sc = node.sysctl_data; 271 1.2 mbalmer 272 1.2 mbalmer node.sysctl_data = &val; 273 1.2 mbalmer 274 1.2 mbalmer val = sc->sc_state; 275 1.2 mbalmer error = sysctl_lookup(SYSCTLFN_CALL(&node)); 276 1.2 mbalmer if (error || newp == NULL) 277 1.2 mbalmer return error; 278 1.2 mbalmer 279 1.25 brad mutex_enter(&sc->sc_intr_mutex); 280 1.25 brad previous_val = sc->sc_state; 281 1.2 mbalmer sc->sc_state = val; 282 1.25 brad for (i = 0; i < GPIOSIM_NPINS; i++) { 283 1.25 brad irq = &sc->sc_gpio_irqs[i]; 284 1.25 brad /* Simulate edge interrupts ... */ 285 1.25 brad if ((previous_val & (1LL << i)) == 0 && (sc->sc_state & (1LL << i)) && 286 1.25 brad irq->sc_gpio_irqfunc != NULL && 287 1.25 brad (irq->sc_gpio_irqmode & (GPIO_INTR_POS_EDGE | GPIO_INTR_DOUBLE_EDGE))) { 288 1.25 brad irq->sc_gpio_irqtriggered = true; 289 1.25 brad t++; 290 1.25 brad } 291 1.25 brad if ((previous_val & (1LL << i)) && (sc->sc_state & (1LL << i)) == 0 && 292 1.25 brad irq->sc_gpio_irqfunc != NULL && 293 1.25 brad (irq->sc_gpio_irqmode & (GPIO_INTR_NEG_EDGE | GPIO_INTR_DOUBLE_EDGE))) { 294 1.25 brad irq->sc_gpio_irqtriggered = true; 295 1.25 brad t++; 296 1.25 brad } 297 1.25 brad /* Simulate level interrupts ... */ 298 1.25 brad if ((sc->sc_state & (1LL << i)) && irq->sc_gpio_irqfunc != NULL && 299 1.25 brad (irq->sc_gpio_irqmode & GPIO_INTR_HIGH_LEVEL)) { 300 1.25 brad irq->sc_gpio_irqtriggered = true; 301 1.25 brad } 302 1.25 brad if ((sc->sc_state & (1LL << i)) == 0 && irq->sc_gpio_irqfunc != NULL && 303 1.25 brad (irq->sc_gpio_irqmode & GPIO_INTR_LOW_LEVEL)) { 304 1.25 brad irq->sc_gpio_irqtriggered = true; 305 1.25 brad } 306 1.25 brad if ((sc->sc_state & (1LL << i)) && irq->sc_gpio_irqfunc != NULL && 307 1.25 brad (irq->sc_gpio_irqmode & GPIO_INTR_LOW_LEVEL)) { 308 1.25 brad irq->sc_gpio_irqtriggered = false; 309 1.25 brad } 310 1.25 brad if ((sc->sc_state & (1LL << i)) == 0 && irq->sc_gpio_irqfunc != NULL && 311 1.25 brad (irq->sc_gpio_irqmode & GPIO_INTR_HIGH_LEVEL)) { 312 1.25 brad irq->sc_gpio_irqtriggered = false; 313 1.25 brad } 314 1.25 brad } 315 1.25 brad mutex_exit(&sc->sc_intr_mutex); 316 1.25 brad 317 1.25 brad if (t > 0) { 318 1.26 brad workqueue_enqueue(sc->sc_wq, (struct work *)&gpiosim_work, NULL); 319 1.25 brad } 320 1.25 brad 321 1.1 mbalmer return 0; 322 1.1 mbalmer } 323 1.1 mbalmer 324 1.25 brad int 325 1.25 brad gpiosim_ms_sysctl(SYSCTLFN_ARGS) 326 1.25 brad { 327 1.25 brad int error, t; 328 1.25 brad struct sysctlnode node; 329 1.25 brad 330 1.25 brad node = *rnode; 331 1.25 brad t = *(int*)rnode->sysctl_data; 332 1.25 brad node.sysctl_data = &t; 333 1.25 brad error = sysctl_lookup(SYSCTLFN_CALL(&node)); 334 1.25 brad if (error || newp == NULL) 335 1.25 brad return (error); 336 1.25 brad 337 1.26 brad /* Make sure that this can not be zero */ 338 1.25 brad if (t < 1) 339 1.25 brad return (EINVAL); 340 1.25 brad 341 1.25 brad *(int*)rnode->sysctl_data = t; 342 1.25 brad 343 1.25 brad return (0); 344 1.25 brad } 345 1.25 brad 346 1.25 brad /* Interrupts though the read and write path are not simulated, 347 1.25 brad * that is, an interrupt on the setting of an output or an 348 1.25 brad * interrupt on a pin read. It is not at all clear that it makes 349 1.25 brad * any sense to do any of that, although real hardware in some cases 350 1.25 brad * might trigger an interrupt on an output pin. 351 1.25 brad */ 352 1.25 brad 353 1.8 mbalmer static int 354 1.1 mbalmer gpiosim_pin_read(void *arg, int pin) 355 1.1 mbalmer { 356 1.6 mbalmer struct gpiosim_softc *sc = arg; 357 1.1 mbalmer 358 1.15 mbalmer if (sc->sc_state & (1LL << pin)) 359 1.1 mbalmer return GPIO_PIN_HIGH; 360 1.1 mbalmer else 361 1.1 mbalmer return GPIO_PIN_LOW; 362 1.1 mbalmer } 363 1.1 mbalmer 364 1.8 mbalmer static void 365 1.1 mbalmer gpiosim_pin_write(void *arg, int pin, int value) 366 1.1 mbalmer { 367 1.6 mbalmer struct gpiosim_softc *sc = arg; 368 1.1 mbalmer 369 1.1 mbalmer if (value == 0) 370 1.15 mbalmer sc->sc_state &= ~(1LL << pin); 371 1.1 mbalmer else 372 1.15 mbalmer sc->sc_state |= (1LL << pin); 373 1.1 mbalmer } 374 1.1 mbalmer 375 1.8 mbalmer static void 376 1.1 mbalmer gpiosim_pin_ctl(void *arg, int pin, int flags) 377 1.1 mbalmer { 378 1.6 mbalmer struct gpiosim_softc *sc = arg; 379 1.1 mbalmer 380 1.1 mbalmer sc->sc_gpio_pins[pin].pin_flags = flags; 381 1.1 mbalmer } 382 1.8 mbalmer 383 1.25 brad static void * 384 1.25 brad gpiosim_intr_establish(void *vsc, int pin, int ipl, int irqmode, 385 1.25 brad int (*func)(void *), void *arg) 386 1.25 brad { 387 1.25 brad struct gpiosim_softc * const sc = vsc; 388 1.25 brad struct gpiosim_irq *irq; 389 1.25 brad 390 1.25 brad mutex_enter(&sc->sc_intr_mutex); 391 1.25 brad irq = &sc->sc_gpio_irqs[pin]; 392 1.25 brad irq->sc_gpio_irqfunc = func; 393 1.25 brad irq->sc_gpio_irqmode = irqmode; 394 1.25 brad irq->sc_gpio_irqarg = arg; 395 1.25 brad 396 1.25 brad /* The first level interrupt starts the callout if it is not running */ 397 1.25 brad if (((irqmode & GPIO_INTR_HIGH_LEVEL) || 398 1.25 brad (irqmode & GPIO_INTR_LOW_LEVEL)) && 399 1.25 brad (sc->sc_co_running == false)) { 400 1.26 brad callout_schedule(&sc->sc_co, mstohz(sc->sc_ms)); 401 1.25 brad sc->sc_co_running = true; 402 1.25 brad } 403 1.25 brad 404 1.25 brad /* Level interrupts can start as soon as a IRQ handler is installed */ 405 1.25 brad if (((irqmode & GPIO_INTR_HIGH_LEVEL) && (sc->sc_state & (1LL << pin))) || 406 1.25 brad ((irqmode & GPIO_INTR_LOW_LEVEL) && ((sc->sc_state & (1LL << pin)) == 0))) { 407 1.25 brad irq->sc_gpio_irqtriggered = true; 408 1.25 brad } 409 1.25 brad 410 1.25 brad mutex_exit(&sc->sc_intr_mutex); 411 1.25 brad 412 1.25 brad return(irq); 413 1.25 brad } 414 1.25 brad 415 1.25 brad static void 416 1.25 brad gpiosim_intr_disestablish(void *vsc, void *ih) 417 1.25 brad { 418 1.25 brad struct gpiosim_softc * const sc = vsc; 419 1.25 brad struct gpiosim_irq *irq = ih; 420 1.25 brad struct gpiosim_irq *lirq; 421 1.25 brad int i; 422 1.25 brad bool has_level = false; 423 1.25 brad 424 1.25 brad mutex_enter(&sc->sc_intr_mutex); 425 1.25 brad irq->sc_gpio_irqfunc = NULL; 426 1.25 brad irq->sc_gpio_irqmode = 0; 427 1.25 brad irq->sc_gpio_irqarg = NULL; 428 1.25 brad irq->sc_gpio_irqtriggered = false; 429 1.25 brad 430 1.25 brad /* Check for any level interrupts and stop the callout 431 1.25 brad * if there are none. 432 1.25 brad */ 433 1.25 brad for (i = 0;i < GPIOSIM_NPINS; i++) { 434 1.25 brad lirq = &sc->sc_gpio_irqs[i]; 435 1.25 brad if (lirq->sc_gpio_irqmode & (GPIO_INTR_HIGH_LEVEL | GPIO_INTR_LOW_LEVEL)) { 436 1.25 brad has_level = true; 437 1.25 brad break; 438 1.25 brad } 439 1.25 brad } 440 1.25 brad if (has_level == false) { 441 1.25 brad sc->sc_co_running = false; 442 1.25 brad } 443 1.25 brad mutex_exit(&sc->sc_intr_mutex); 444 1.25 brad } 445 1.25 brad 446 1.25 brad static bool 447 1.25 brad gpiosim_gpio_intrstr(void *vsc, int pin, int irqmode, char *buf, size_t buflen) 448 1.25 brad { 449 1.25 brad 450 1.25 brad if (pin < 0 || pin >= GPIOSIM_NPINS) 451 1.25 brad return (false); 452 1.25 brad 453 1.25 brad snprintf(buf, buflen, "GPIO %d", pin); 454 1.25 brad 455 1.25 brad return (true); 456 1.25 brad } 457 1.25 brad 458 1.25 brad /* The workqueue handles edge the simulation of edge interrupts */ 459 1.25 brad void 460 1.25 brad gpiosim_wq(struct work *wk, void *arg) 461 1.25 brad { 462 1.25 brad struct gpiosim_softc *sc = arg; 463 1.25 brad struct gpiosim_irq *irq; 464 1.25 brad int i; 465 1.25 brad 466 1.25 brad mutex_enter(&sc->sc_intr_mutex); 467 1.25 brad for (i = 0; i < GPIOSIM_NPINS; i++) { 468 1.25 brad irq = &sc->sc_gpio_irqs[i]; 469 1.25 brad if (irq->sc_gpio_irqtriggered && 470 1.25 brad irq->sc_gpio_irqfunc != NULL && 471 1.25 brad (irq->sc_gpio_irqmode & (GPIO_INTR_POS_EDGE | GPIO_INTR_NEG_EDGE | GPIO_INTR_DOUBLE_EDGE))) { 472 1.25 brad (*irq->sc_gpio_irqfunc)(irq->sc_gpio_irqarg); 473 1.25 brad irq->sc_gpio_irqtriggered = false; 474 1.25 brad } 475 1.25 brad } 476 1.25 brad mutex_exit(&sc->sc_intr_mutex); 477 1.25 brad } 478 1.25 brad 479 1.25 brad /* This runs as long as there are level interrupts to simulate */ 480 1.25 brad void 481 1.25 brad gpiosim_co(void *arg) 482 1.25 brad { 483 1.25 brad struct gpiosim_softc *sc = arg; 484 1.25 brad struct gpiosim_irq *irq; 485 1.25 brad int i; 486 1.25 brad 487 1.25 brad mutex_enter(&sc->sc_intr_mutex); 488 1.25 brad for (i = 0; i < GPIOSIM_NPINS; i++) { 489 1.25 brad irq = &sc->sc_gpio_irqs[i]; 490 1.25 brad if (irq->sc_gpio_irqtriggered && 491 1.25 brad irq->sc_gpio_irqfunc != NULL && 492 1.25 brad (irq->sc_gpio_irqmode & (GPIO_INTR_HIGH_LEVEL | GPIO_INTR_LOW_LEVEL))) { 493 1.25 brad (*irq->sc_gpio_irqfunc)(irq->sc_gpio_irqarg); 494 1.25 brad } 495 1.25 brad } 496 1.25 brad mutex_exit(&sc->sc_intr_mutex); 497 1.25 brad 498 1.25 brad if (sc->sc_co_running == true) { 499 1.26 brad callout_schedule(&sc->sc_co, mstohz(sc->sc_ms)); 500 1.25 brad } 501 1.25 brad } 502 1.25 brad 503 1.25 brad 504 1.8 mbalmer MODULE(MODULE_CLASS_DRIVER, gpiosim, "gpio"); 505 1.8 mbalmer 506 1.12 mbalmer #ifdef _MODULE 507 1.8 mbalmer static const struct cfiattrdata gpiobus_iattrdata = { 508 1.26 brad "gpiobus", 0, { { NULL, NULL, 0 }, } 509 1.8 mbalmer }; 510 1.8 mbalmer static const struct cfiattrdata *const gpiosim_attrs[] = { 511 1.8 mbalmer &gpiobus_iattrdata, NULL 512 1.8 mbalmer }; 513 1.8 mbalmer CFDRIVER_DECL(gpiosim, DV_DULL, gpiosim_attrs); 514 1.8 mbalmer extern struct cfattach gpiosim_ca; 515 1.8 mbalmer static int gpiosimloc[] = { 516 1.8 mbalmer -1, 517 1.8 mbalmer -1, 518 1.8 mbalmer -1 519 1.8 mbalmer }; 520 1.8 mbalmer static struct cfdata gpiosim_cfdata[] = { 521 1.8 mbalmer { 522 1.8 mbalmer .cf_name = "gpiosim", 523 1.8 mbalmer .cf_atname = "gpiosim", 524 1.8 mbalmer .cf_unit = 0, 525 1.8 mbalmer .cf_fstate = FSTATE_STAR, 526 1.8 mbalmer .cf_loc = gpiosimloc, 527 1.8 mbalmer .cf_flags = 0, 528 1.8 mbalmer .cf_pspec = NULL, 529 1.8 mbalmer }, 530 1.11 jmcneill { NULL, NULL, 0, FSTATE_NOTFOUND, NULL, 0, NULL } 531 1.8 mbalmer }; 532 1.12 mbalmer #endif 533 1.8 mbalmer 534 1.8 mbalmer static int 535 1.8 mbalmer gpiosim_modcmd(modcmd_t cmd, void *opaque) 536 1.8 mbalmer { 537 1.12 mbalmer #ifdef _MODULE 538 1.8 mbalmer int error = 0; 539 1.12 mbalmer #endif 540 1.8 mbalmer switch (cmd) { 541 1.8 mbalmer case MODULE_CMD_INIT: 542 1.12 mbalmer #ifdef _MODULE 543 1.8 mbalmer error = config_cfdriver_attach(&gpiosim_cd); 544 1.8 mbalmer if (error) 545 1.8 mbalmer return error; 546 1.8 mbalmer 547 1.8 mbalmer error = config_cfattach_attach(gpiosim_cd.cd_name, 548 1.8 mbalmer &gpiosim_ca); 549 1.8 mbalmer if (error) { 550 1.8 mbalmer config_cfdriver_detach(&gpiosim_cd); 551 1.8 mbalmer aprint_error("%s: unable to register cfattach\n", 552 1.9 mbalmer gpiosim_cd.cd_name); 553 1.8 mbalmer return error; 554 1.8 mbalmer } 555 1.8 mbalmer error = config_cfdata_attach(gpiosim_cfdata, 1); 556 1.8 mbalmer if (error) { 557 1.8 mbalmer config_cfattach_detach(gpiosim_cd.cd_name, 558 1.8 mbalmer &gpiosim_ca); 559 1.8 mbalmer config_cfdriver_detach(&gpiosim_cd); 560 1.8 mbalmer aprint_error("%s: unable to register cfdata\n", 561 1.9 mbalmer gpiosim_cd.cd_name); 562 1.8 mbalmer return error; 563 1.8 mbalmer } 564 1.12 mbalmer config_attach_pseudo(gpiosim_cfdata); 565 1.12 mbalmer #endif 566 1.8 mbalmer return 0; 567 1.8 mbalmer case MODULE_CMD_FINI: 568 1.12 mbalmer #ifdef _MODULE 569 1.8 mbalmer error = config_cfdata_detach(gpiosim_cfdata); 570 1.8 mbalmer if (error) 571 1.8 mbalmer return error; 572 1.8 mbalmer 573 1.8 mbalmer config_cfattach_detach(gpiosim_cd.cd_name, &gpiosim_ca); 574 1.8 mbalmer config_cfdriver_detach(&gpiosim_cd); 575 1.12 mbalmer #endif 576 1.8 mbalmer return 0; 577 1.8 mbalmer case MODULE_CMD_AUTOUNLOAD: 578 1.8 mbalmer /* no auto-unload */ 579 1.8 mbalmer return EBUSY; 580 1.8 mbalmer default: 581 1.8 mbalmer return ENOTTY; 582 1.8 mbalmer } 583 1.8 mbalmer } 584