diff options
Diffstat (limited to 'sys/dev/gpio/gpio.c')
-rw-r--r-- | sys/dev/gpio/gpio.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/sys/dev/gpio/gpio.c b/sys/dev/gpio/gpio.c new file mode 100644 index 00000000000..fa6919745b8 --- /dev/null +++ b/sys/dev/gpio/gpio.c @@ -0,0 +1,241 @@ +/* $OpenBSD: gpio.c,v 1.1 2004/06/03 18:08:00 grange Exp $ */ +/* + * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * General Purpose I/O framework. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/gpio.h> + +#include <dev/gpio/gpiovar.h> + +struct gpio_softc { + struct device sc_dev; + + gpio_chipset_tag_t sc_gc; /* our GPIO controller */ + gpio_pin_t *sc_pins; /* pins array */ + int sc_npins; /* total number of pins */ + + int sc_opened; +}; + +int gpio_match(struct device *, void *, void *); +void gpio_attach(struct device *, struct device *, void *); +int gpio_search(struct device *, void *, void *); +int gpio_print(void *, const char *); + +struct cfattach gpio_ca = { + sizeof (struct gpio_softc), + gpio_match, + gpio_attach +}; + +struct cfdriver gpio_cd = { + NULL, "gpio", DV_DULL +}; + +int +gpio_match(struct device *parent, void *match, void *aux) +{ + struct cfdata *cf = match; + struct gpiobus_attach_args *gba = aux; + + if (strcmp(gba->gba_name, cf->cf_driver->cd_name) != 0) + return (0); + + return (1); +} + +void +gpio_attach(struct device *parent, struct device *self, void *aux) +{ + struct gpio_softc *sc = (struct gpio_softc *)self; + struct gpiobus_attach_args *gba = aux; + + sc->sc_gc = gba->gba_gc; + sc->sc_pins = gba->gba_pins; + sc->sc_npins = gba->gba_npins; + + printf(": %d pins\n", sc->sc_npins); + + /* + * Attach all devices that can be connected to the GPIO pins + * described in the kernel configuration file. + */ + config_search(gpio_search, self, NULL); +} + +int +gpio_search(struct device *parent, void *arg, void *aux) +{ + struct cfdata *cf = arg; + struct gpio_attach_args ga; + + ga.ga_pin = cf->cf_loc[0]; + ga.ga_mask = cf->cf_loc[1]; + + if (cf->cf_attach->ca_match(parent, cf, &ga) > 0) + config_attach(parent, cf, &ga, gpio_print); + + return (0); +} + +int +gpio_print(void *aux, const char *pnp) +{ + struct gpio_attach_args *ga = aux; + int i; + + printf(" pins"); + for (i = 0; i < 32; i++) + if (ga->ga_mask & (1 << i)) + printf(" %d", ga->ga_pin + i); + + return (UNCONF); +} + +int +gpiobus_print(void *aux, const char *pnp) +{ + struct gpiobus_attach_args *gba = aux; + + if (pnp != NULL) + printf("%s at %s", gba->gba_name, pnp); + + return (UNCONF); +} + +int +gpioopen(dev_t dev, int flag, int mode, struct proc *p) +{ + struct gpio_softc *sc; + + sc = (struct gpio_softc *)device_lookup(&gpio_cd, minor(dev)); + if (sc == NULL) + return (ENXIO); + + if (sc->sc_opened) + return (EBUSY); + sc->sc_opened = 1; + + return (0); +} + +int +gpioclose(dev_t dev, int flag, int mode, struct proc *p) +{ + struct gpio_softc *sc; + + sc = (struct gpio_softc *)device_lookup(&gpio_cd, minor(dev)); + sc->sc_opened = 0; + + return (0); +} + +int +gpioioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct gpio_softc *sc; + gpio_chipset_tag_t gc; + struct gpio_info *info; + struct gpio_pin_op *op; + struct gpio_pin_ctl *ctl; + int pin, value, flags; + + sc = (struct gpio_softc *)device_lookup(&gpio_cd, minor(dev)); + gc = sc->sc_gc; + + switch (cmd) { + case GPIOINFO: + info = (struct gpio_info *)data; + + info->gpio_npins = sc->sc_npins; + break; + case GPIOPINREAD: + op = (struct gpio_pin_op *)data; + + pin = op->gp_pin; + if (pin < 0 || pin >= sc->sc_npins) + return (EINVAL); + + /* return read value */ + op->gp_value = gpiobus_pin_read(gc, pin); + break; + case GPIOPINWRITE: + op = (struct gpio_pin_op *)data; + + pin = op->gp_pin; + if (pin < 0 || pin >= sc->sc_npins) + return (EINVAL); + + value = op->gp_value; + if (value != GPIO_PIN_LOW && value != GPIO_PIN_HIGH) + return (EINVAL); + + gpiobus_pin_write(gc, pin, value); + /* return old value */ + op->gp_value = sc->sc_pins[pin].pin_state; + /* update current value */ + sc->sc_pins[pin].pin_state = value; + break; + case GPIOPINTOGGLE: + op = (struct gpio_pin_op *)data; + + pin = op->gp_pin; + if (pin < 0 || pin >= sc->sc_npins) + return (EINVAL); + + value = (sc->sc_pins[pin].pin_state == GPIO_PIN_LOW ? + GPIO_PIN_HIGH : GPIO_PIN_LOW); + gpiobus_pin_write(gc, pin, value); + /* return old value */ + op->gp_value = sc->sc_pins[pin].pin_state; + /* update current value */ + sc->sc_pins[pin].pin_state = value; + break; + case GPIOPINCTL: + ctl = (struct gpio_pin_ctl *)data; + + pin = ctl->gp_pin; + if (pin < 0 || pin >= sc->sc_npins) + return (EINVAL); + + flags = ctl->gp_flags; + /* check that the controller supports all requested flags */ + if ((flags & sc->sc_pins[pin].pin_caps) != flags) + return (ENODEV); + + ctl->gp_caps = sc->sc_pins[pin].pin_caps; + /* return old value */ + ctl->gp_flags = sc->sc_pins[pin].pin_flags; + if (flags > 0) { + gpiobus_pin_ctl(gc, pin, flags); + /* update current value */ + sc->sc_pins[pin].pin_flags = flags; + } + break; + default: + return (ENOTTY); + } + + return (0); +} |