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