summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authormbalmer <mbalmer@NetBSD.org>2011-08-28 07:48:50 +0000
committermbalmer <mbalmer@NetBSD.org>2011-08-28 07:48:50 +0000
commitda668cdf91cdc35a69a5162bd24e7a5eca2bda4e (patch)
tree06c3388b9e3be3a02e3b03181b9f6ea020ecd9c0 /sys/dev
parent1b3a256adc36e54133a189084ba985a7dca7fd3d (diff)
Add a new ioctl, GPIOPULSE to gpio(4) to allow for pulsing a pin.
If a pin can pulse in hardware, that will be used, else it will be pulsed in software. There is no way yet to set the pulse frequency for pins that pulse in hardware. While here, make the code mpsafe and allow more than one thread in the driver (access to ioctl is serialized).
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_ */