summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/gpio/gpio.c231
-rw-r--r--sys/dev/gpio/gpiovar.h32
2 files changed, 195 insertions, 68 deletions
diff --git a/sys/dev/gpio/gpio.c b/sys/dev/gpio/gpio.c
index bb215b505c9..2b7466ea634 100644
--- a/sys/dev/gpio/gpio.c
+++ b/sys/dev/gpio/gpio.c
@@ -1,4 +1,4 @@
-/* $NetBSD: gpio.c,v 1.35 2011/08/12 08:00:52 mbalmer Exp $ */
+/* $NetBSD: gpio.c,v 1.36 2011/08/28 07:48:50 mbalmer Exp $ */
/* $OpenBSD: gpio.c,v 1.6 2006/01/14 12:33:49 grange Exp $ */
/*
@@ -19,21 +19,25 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: gpio.c,v 1.35 2011/08/12 08:00:52 mbalmer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: gpio.c,v 1.36 2011/08/28 07:48:50 mbalmer Exp $");
/*
* General Purpose Input/Output framework.
*/
#include <sys/param.h>
+#include <sys/callout.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/gpio.h>
+#include <sys/kernel.h>
#include <sys/vnode.h>
#include <sys/kmem.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
#include <sys/queue.h>
#include <sys/kauth.h>
#ifdef _MODULE
@@ -58,7 +62,9 @@ struct gpio_softc {
gpio_pin_t *sc_pins; /* pins array */
int sc_npins; /* number of pins */
- int sc_opened;
+ kmutex_t sc_mtx;
+ kcondvar_t sc_ioctl; /* ioctl in progress */
+ int sc_ioctl_busy; /* ioctl is busy */
LIST_HEAD(, gpio_dev) sc_devs; /* devices */
LIST_HEAD(, gpio_name) sc_names; /* named pins */
};
@@ -73,6 +79,9 @@ static int gpio_detach(device_t, int);
static int gpio_search(device_t, cfdata_t, const int *, void *);
static int gpio_print(void *, const char *);
static int gpio_pinbyname(struct gpio_softc *, char *);
+static void gpio_pulse(void *);
+static int gpio_ioctl(struct gpio_softc *, u_long, void *, int,
+ struct lwp *);
/* Old API */
static int gpio_ioctl_oapi(struct gpio_softc *, u_long, void *, int,
@@ -85,10 +94,11 @@ CFATTACH_DECL3_NEW(gpio, sizeof(struct gpio_softc),
dev_type_open(gpioopen);
dev_type_close(gpioclose);
dev_type_ioctl(gpioioctl);
+dev_type_ioctl(gpioioctl_locked);
const struct cdevsw gpio_cdevsw = {
gpioopen, gpioclose, noread, nowrite, gpioioctl,
- nostop, notty, nopoll, nommap, nokqfilter, D_OTHER,
+ nostop, notty, nopoll, nommap, nokqfilter, D_OTHER | D_MPSAFE
};
extern struct cfdriver gpio_cd;
@@ -144,7 +154,7 @@ gpio_attach(device_t parent, device_t self, void *aux)
{
struct gpio_softc *sc = device_private(self);
struct gpiobus_attach_args *gba = aux;
-
+ int pin;
sc->sc_dev = self;
sc->sc_gc = gba->gba_gc;
sc->sc_pins = gba->gba_pins;
@@ -152,8 +162,15 @@ gpio_attach(device_t parent, device_t self, void *aux)
printf(": %d pins\n", sc->sc_npins);
+ for (pin = 0; pin < sc->sc_npins; pin++) {
+ callout_init(&sc->sc_pins[pin].pin_pulse, CALLOUT_MPSAFE);
+ callout_setfunc(&sc->sc_pins[pin].pin_pulse, gpio_pulse,
+ &sc->sc_pins[pin]);
+ }
if (!pmf_device_register(self, NULL, gpio_resume))
aprint_error_dev(self, "couldn't establish power handler\n");
+ mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_VM);
+ cv_init(&sc->sc_ioctl, "gpioctl");
/*
* Attach all devices that can be connected to the GPIO pins
@@ -165,11 +182,23 @@ gpio_attach(device_t parent, device_t self, void *aux)
static int
gpio_detach(device_t self, int flags)
{
- int rc;
+ struct gpio_softc *sc;
+ int pin, rc;
+
+ sc = device_private(self);
+
+ for (pin = 0; pin < sc->sc_npins; pin++) {
+ if (sc->sc_pins[pin].pin_state & GPIO_PIN_PULSE) {
+ callout_halt(&sc->sc_pins[pin].pin_pulse, NULL);
+ callout_destroy(&sc->sc_pins[pin].pin_pulse);
+ sc->sc_pins[pin].pin_state &= ~GPIO_PIN_PULSE;
+ }
+ }
if ((rc = config_detach_children(self, flags)) != 0)
return rc;
-
+ mutex_destroy(&sc->sc_mtx);
+ cv_destroy(&sc->sc_ioctl);
#if 0
int maj, mn;
@@ -228,7 +257,7 @@ gpiobus_print(void *aux, const char *pnp)
/* return 1 if all pins can be mapped, 0 if not */
int
-gpio_pin_can_map(void *gpio, int offset, u_int32_t mask)
+gpio_pin_can_map(void *gpio, int offset, uint32_t mask)
{
struct gpio_softc *sc = gpio;
int npins, pin, i;
@@ -250,7 +279,7 @@ gpio_pin_can_map(void *gpio, int offset, u_int32_t mask)
}
int
-gpio_pin_map(void *gpio, int offset, u_int32_t mask, struct gpio_pinmap *map)
+gpio_pin_map(void *gpio, int offset, uint32_t mask, struct gpio_pinmap *map)
{
struct gpio_softc *sc = gpio;
int npins, pin, i;
@@ -320,7 +349,7 @@ gpio_pin_caps(void *gpio, struct gpio_pinmap *map, int pin)
}
int
-gpio_npins(u_int32_t mask)
+gpio_npins(uint32_t mask)
{
int npins, i;
@@ -332,30 +361,47 @@ gpio_npins(u_int32_t mask)
}
int
+gpio_lock(void *data)
+{
+ struct gpio_softc *sc;
+ int error;
+
+ error = 0;
+ sc = (struct gpio_softc *)data;
+ mutex_enter(&sc->sc_mtx);
+ while (sc->sc_ioctl_busy) {
+ error = cv_wait_sig(&sc->sc_ioctl, &sc->sc_mtx);
+ if (error)
+ break;
+ }
+ if (!error)
+ sc->sc_ioctl_busy = 1;
+ mutex_exit(&sc->sc_mtx);
+ return error;
+}
+
+void
+gpio_unlock(void *data)
+{
+ struct gpio_softc *sc;
+
+ sc = (struct gpio_softc *)data;
+ mutex_enter(&sc->sc_mtx);
+ sc->sc_ioctl_busy = 0;
+ cv_signal(&sc->sc_ioctl);
+ mutex_exit(&sc->sc_mtx);
+}
+
+int
gpioopen(dev_t dev, int flag, int mode, struct lwp *l)
{
struct gpio_softc *sc;
- int ret;
sc = device_lookup_private(&gpio_cd, minor(dev));
if (sc == NULL)
return ENXIO;
- DPRINTF(("%s: opening\n", device_xname(sc->sc_dev)));
- if (sc->sc_opened) {
- DPRINTF(("%s: already opened\n", device_xname(sc->sc_dev)));
- return EBUSY;
- }
-
- if ((ret = gpiobus_open(sc->sc_gc, sc->sc_dev))) {
- DPRINTF(("%s: gpiobus_open returned %d\n",
- device_xname(sc->sc_dev),
- ret));
- return ret;
- }
-
- sc->sc_opened = 1;
- return 0;
+ return gpiobus_open(sc->sc_gc, sc->sc_dev);
}
int
@@ -364,10 +410,7 @@ gpioclose(dev_t dev, int flag, int mode, struct lwp *l)
struct gpio_softc *sc;
sc = device_lookup_private(&gpio_cd, minor(dev));
- DPRINTF(("%s: closing\n", device_xname(sc->sc_dev)));
- (void)gpiobus_close(sc->sc_gc, sc->sc_dev);
- sc->sc_opened = 0;
-
+ gpiobus_close(sc->sc_gc, sc->sc_dev);
return 0;
}
@@ -382,25 +425,63 @@ gpio_pinbyname(struct gpio_softc *sc, char *gp_name)
return -1;
}
+static void
+gpio_pulse(void *arg)
+{
+ struct gpio_pin *pin;
+
+ pin = (struct gpio_pin *)arg;
+ if ((pin->pin_state & GPIO_PIN_PULSE) == 0)
+ return;
+
+ if (pin->pin_state & GPIO_PIN_HIGH) {
+ gpiobus_pin_write(pin->pin_gc, pin->pin_num, GPIO_PIN_LOW);
+ pin->pin_state &= ~GPIO_PIN_HIGH;
+ callout_schedule(&pin->pin_pulse, pin->pin_ticks_off);
+ } else {
+ gpiobus_pin_write(pin->pin_gc, pin->pin_num, GPIO_PIN_HIGH);
+ pin->pin_state |= GPIO_PIN_HIGH;
+ callout_schedule(&pin->pin_pulse, pin->pin_ticks_on);
+ }
+}
+
int
gpioioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
{
+ int error;
struct gpio_softc *sc;
+
+ sc = device_lookup_private(&gpio_cd, minor(dev));
+
+ error = gpio_lock(sc);
+ if (error)
+ return error;
+
+ error = gpio_ioctl(sc, cmd, data, flag, l);
+ gpio_unlock(sc);
+ return error;
+}
+
+static int
+gpio_ioctl(struct gpio_softc *sc, u_long cmd, void *data, int flag,
+ struct lwp *l)
+{
gpio_chipset_tag_t gc;
struct gpio_info *info;
struct gpio_attach *attach;
struct gpio_attach_args ga;
struct gpio_dev *gdev;
struct gpio_req *req;
+ struct gpio_pulse *pulse;
struct gpio_name *nm;
struct gpio_set *set;
+ struct gpio_pin *gpin;
device_t dv;
cfdata_t cf;
kauth_cred_t cred;
int locs[GPIOCF_NLOCS];
int pin, value, flags, npins;
- sc = device_lookup_private(&gpio_cd, minor(dev));
gc = sc->sc_gc;
if (cmd != GPIOINFO && !device_is_active(sc->sc_dev)) {
@@ -427,11 +508,9 @@ gpioioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
case GPIOREAD:
req = (struct gpio_req *)data;
- if (req->gp_name[0] != '\0') {
+ if (req->gp_name[0] != '\0')
pin = gpio_pinbyname(sc, req->gp_name);
- if (pin == -1)
- return EINVAL;
- } else
+ else
pin = req->gp_pin;
if (pin < 0 || pin >= sc->sc_npins)
@@ -451,11 +530,9 @@ gpioioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
req = (struct gpio_req *)data;
- if (req->gp_name[0] != '\0') {
+ if (req->gp_name[0] != '\0')
pin = gpio_pinbyname(sc, req->gp_name);
- if (pin == -1)
- return EINVAL;
- } else
+ else
pin = req->gp_pin;
if (pin < 0 || pin >= sc->sc_npins)
@@ -473,23 +550,68 @@ gpioioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
if (value != GPIO_PIN_LOW && value != GPIO_PIN_HIGH)
return EINVAL;
+ if (sc->sc_pins[pin].pin_state & GPIO_PIN_PULSE) {
+ callout_halt(&sc->sc_pins[pin].pin_pulse, NULL);
+ sc->sc_pins[pin].pin_state &= ~GPIO_PIN_PULSE;
+ }
gpiobus_pin_write(gc, pin, value);
/* return old value */
req->gp_value = sc->sc_pins[pin].pin_state;
/* update current value */
sc->sc_pins[pin].pin_state = value;
break;
+ case GPIOPULSE:
+ if ((flag & FWRITE) == 0)
+ return EBADF;
+
+ pulse = (struct gpio_pulse *)data;
+ if (pulse->gp_name[0] != '\0')
+ pin = gpio_pinbyname(sc, pulse->gp_name);
+ else
+ pin = pulse->gp_pin;
+
+ if (pin < 0 || pin >= sc->sc_npins)
+ return EINVAL;
+
+ gpin = &sc->sc_pins[pin];
+ if (gpin->pin_mapped)
+ return EBUSY;
+
+ if (!(gpin->pin_flags & GPIO_PIN_SET) &&
+ kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
+ NULL, NULL, NULL, NULL))
+ return EPERM;
+
+ if (gpin->pin_flags & GPIO_PIN_PULSATE) {
+ gpiobus_pin_write(gc, pin, GPIO_PIN_HIGH);
+ gpin->pin_state = GPIO_PIN_PULSE;
+ return 0;
+ }
+
+ if (gpin->pin_state & GPIO_PIN_PULSE)
+ callout_halt(&gpin->pin_pulse, NULL);
+
+ gpin->pin_gc = gc;
+
+ gpin->pin_ticks_on = tvtohz(&pulse->gp_pulse_on);
+ gpin->pin_ticks_off = tvtohz(&pulse->gp_pulse_off);
+ if (gpin->pin_ticks_on == 0 || gpin->pin_ticks_off == 0) {
+ gpin->pin_ticks_on = hz / 2;
+ gpin->pin_ticks_off = hz / 2;
+ }
+ gpiobus_pin_write(gc, pin, GPIO_PIN_HIGH);
+ gpin->pin_state = GPIO_PIN_HIGH | GPIO_PIN_PULSE;
+ callout_schedule(&gpin->pin_pulse, gpin->pin_ticks_on);
+ break;
case GPIOTOGGLE:
if ((flag & FWRITE) == 0)
return EBADF;
req = (struct gpio_req *)data;
- if (req->gp_name[0] != '\0') {
+ if (req->gp_name[0] != '\0')
pin = gpio_pinbyname(sc, req->gp_name);
- if (pin == -1)
- return EINVAL;
- } else
+ else
pin = req->gp_pin;
if (pin < 0 || pin >= sc->sc_npins)
@@ -526,9 +648,10 @@ gpioioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
ga.ga_dvname = attach->ga_dvname;
ga.ga_offset = attach->ga_offset;
ga.ga_mask = attach->ga_mask;
- DPRINTF(("%s: attach %s with offset %d and mask 0x%02x\n",
- device_xname(sc->sc_dev), ga.ga_dvname, ga.ga_offset,
- ga.ga_mask));
+ ga.ga_nolock = 1;
+ DPRINTF(("%s: attach %s with offset %d and mask "
+ "0x%02x\n", device_xname(sc->sc_dev), ga.ga_dvname,
+ ga.ga_offset, ga.ga_mask));
locs[GPIOCF_OFFSET] = ga.ga_offset;
locs[GPIOCF_MASK] = ga.ga_mask;
@@ -546,7 +669,7 @@ gpioioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
return EINVAL;
} else
return EINVAL;
- break;
+ return 0;
case GPIODETACH:
if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
NULL, NULL, NULL, NULL))
@@ -566,7 +689,6 @@ gpioioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
}
}
return EINVAL;
- break;
case GPIOSET:
if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
NULL, NULL, NULL, NULL))
@@ -574,12 +696,11 @@ gpioioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
set = (struct gpio_set *)data;
- if (set->gp_name[0] != '\0') {
+ if (set->gp_name[0] != '\0')
pin = gpio_pinbyname(sc, set->gp_name);
- if (pin == -1)
- return EINVAL;
- } else
+ else
pin = set->gp_pin;
+
if (pin < 0 || pin >= sc->sc_npins)
return EINVAL;
flags = set->gp_flags;
@@ -629,11 +750,9 @@ gpioioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
return EPERM;
set = (struct gpio_set *)data;
- if (set->gp_name[0] != '\0') {
+ if (set->gp_name[0] != '\0')
pin = gpio_pinbyname(sc, set->gp_name);
- if (pin == -1)
- return EINVAL;
- } else
+ else
pin = set->gp_pin;
if (pin < 0 || pin >= sc->sc_npins)
diff --git a/sys/dev/gpio/gpiovar.h b/sys/dev/gpio/gpiovar.h
index 15040566f49..f4784cee9ce 100644
--- a/sys/dev/gpio/gpiovar.h
+++ b/sys/dev/gpio/gpiovar.h
@@ -1,4 +1,4 @@
-/* $NetBSD: gpiovar.h,v 1.11 2011/08/12 08:00:52 mbalmer Exp $ */
+/* $NetBSD: gpiovar.h,v 1.12 2011/08/28 07:48:50 mbalmer Exp $ */
/* $OpenBSD: gpiovar.h,v 1.3 2006/01/14 12:33:49 grange Exp $ */
/*
@@ -35,18 +35,22 @@ typedef struct gpio_chipset_tag {
/* GPIO pin description */
typedef struct gpio_pin {
- int pin_num; /* number */
- int pin_caps; /* capabilities */
- int pin_flags; /* current configuration */
- int pin_state; /* current state */
- int pin_mapped; /* is mapped */
+ int pin_num; /* number */
+ int pin_caps; /* capabilities */
+ int pin_flags; /* current configuration */
+ int pin_state; /* current state */
+ int pin_mapped; /* is mapped */
+ callout_t pin_pulse; /* for pulsing */
+ int pin_ticks_on; /* "on" period */
+ int pin_ticks_off; /* "off" period */
+ gpio_chipset_tag_t pin_gc; /* reference the controller */
} gpio_pin_t;
/* Attach GPIO framework to the controller */
struct gpiobus_attach_args {
- gpio_chipset_tag_t gba_gc; /* underlying controller */
+ gpio_chipset_tag_t gba_gc; /* underlying controller */
gpio_pin_t *gba_pins; /* pins array */
- int gba_npins; /* total number of pins */
+ int gba_npins; /* total number of pins */
};
int gpiobus_print(void *, const char *);
@@ -67,8 +71,9 @@ int gpiobus_print(void *, const char *);
struct gpio_attach_args {
void *ga_gpio;
int ga_offset;
- u_int32_t ga_mask;
+ uint32_t ga_mask;
char *ga_dvname;
+ int ga_nolock;
};
/* GPIO pin map */
@@ -88,13 +93,16 @@ struct gpio_name {
LIST_ENTRY(gpio_name) gp_next;
};
-int gpio_pin_can_map(void *, int, u_int32_t);
-int gpio_pin_map(void *, int, u_int32_t, struct gpio_pinmap *);
+int gpio_pin_can_map(void *, int, uint32_t);
+int gpio_pin_map(void *, int, uint32_t, struct gpio_pinmap *);
void gpio_pin_unmap(void *, struct gpio_pinmap *);
int gpio_pin_read(void *, struct gpio_pinmap *, int);
void gpio_pin_write(void *, struct gpio_pinmap *, int, int);
void gpio_pin_ctl(void *, struct gpio_pinmap *, int, int);
int gpio_pin_caps(void *, struct gpio_pinmap *, int);
-int gpio_npins(u_int32_t);
+int gpio_npins(uint32_t);
+
+int gpio_lock(void *);
+void gpio_unlock(void *);
#endif /* !_DEV_GPIO_GPIOVAR_H_ */