Sun Nov 26 12:13:19 2023 UTC ()
Pull up following revision(s) (requested by brad in ticket #463):
	share/man/man4/gpiosim.4: revision 1.7
	sys/dev/gpio/gpiosim.c: revision 1.25
Simple simulated interrupts for the simulated GPIO device gpiosim(4)


(bouyer)
diff -r1.6 -r1.6.16.1 src/share/man/man4/gpiosim.4
diff -r1.23 -r1.23.6.1 src/sys/dev/gpio/gpiosim.c

cvs diff -r1.6 -r1.6.16.1 src/share/man/man4/gpiosim.4 (expand / switch to unified diff)

--- src/share/man/man4/gpiosim.4 2017/07/03 21:30:58 1.6
+++ src/share/man/man4/gpiosim.4 2023/11/26 12:13:19 1.6.16.1
@@ -1,48 +1,57 @@ @@ -1,48 +1,57 @@
1.\" $NetBSD: gpiosim.4,v 1.6 2017/07/03 21:30:58 wiz Exp $ 1.\" $NetBSD: gpiosim.4,v 1.6.16.1 2023/11/26 12:13:19 bouyer Exp $
2.\" 2.\"
3.\" Copyright (c) 2009, 2013 Marc Balmer <marc@msys.ch> 3.\" Copyright (c) 2009, 2013 Marc Balmer <marc@msys.ch>
4.\" All rights reserved. 4.\" All rights reserved.
5.\" 5.\"
6.\" Permission to use, copy, modify, and distribute this software for any 6.\" Permission to use, copy, modify, and distribute this software for any
7.\" purpose with or without fee is hereby granted, provided that the above 7.\" purpose with or without fee is hereby granted, provided that the above
8.\" copyright notice and this permission notice appear in all copies. 8.\" copyright notice and this permission notice appear in all copies.
9.\" 9.\"
10.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17.\" 17.\"
18.Dd May 20, 2013 18.Dd November 8, 2013
19.Dt GPIOSIM 4 19.Dt GPIOSIM 4
20.Os 20.Os
21.Sh NAME 21.Sh NAME
22.Nm gpiosim 22.Nm gpiosim
23.Nd General Purpose Input/Output Simulator 23.Nd General Purpose Input/Output Simulator
24.Sh SYNOPSIS 24.Sh SYNOPSIS
25.Cd "pseudo-device gpiosim" 25.Cd "pseudo-device gpiosim"
26.Sh DESCRIPTION 26.Sh DESCRIPTION
27The 27The
28.Nm 28.Nm
29pseudo-device simulates a 64-bit wide 29pseudo-device simulates a 64-bit wide
30.Xr gpio 4 30.Xr gpio 4
31device. 31device.
32The state of the simulated device can be manipulated through the 32The state of the simulated device can be manipulated through the
33.Xr sysctl 8 33.Xr sysctl 8
34interface. 34interface.
35For this purpose, access the "hw.gpiosim<N>.value" 35For this purpose, access the "hw.gpiosim<N>.value"
36.Xr sysctl 8 36.Xr sysctl 8
37variable, where "<N>" denotes the number of the 37variable, where "<N>" denotes the number of the
38.Nm 38.Nm
39instance. 39instance.
 40.Pp
 41Both edge and level interrupts are simulated. The "hw.gpiosim<N>.ms"
 42.Xr sysctl 8
 43variable will change the amount of time between
 44.Xr callout 9
 45ticks for simulated level interrupts.
40.Sh SEE ALSO 46.Sh SEE ALSO
41.Xr gpio 4 , 47.Xr gpio 4 ,
 48.Xr gpioirq 4 ,
42.Xr sysctl 8 49.Xr sysctl 8
43.Sh AUTHORS 50.Sh AUTHORS
44.An -nosplit 51.An -nosplit
45The 52The
46.Nm 53.Nm
47driver was written by 54driver was written by
48.An Marc Balmer Aq Mt marc@msys.ch . 55.An Marc Balmer Aq Mt marc@msys.ch
 56Simulated interrupts added by
 57.An Brad Spencer Aq Mt brad@anduin.eldar.org .

cvs diff -r1.23 -r1.23.6.1 src/sys/dev/gpio/gpiosim.c (expand / switch to unified diff)

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