Home | History | Annotate | Line # | Download | only in gpio
gpiosim.c revision 1.26
      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