Module Name: src Committed By: mbalmer Date: Sun Nov 13 12:33:01 UTC 2011
Modified Files: src/sys/dev/gpio: files.gpio Added Files: src/sys/dev/gpio: gpiopwm.c Log Message: gpiopwm(4) is a driver to pulse GPIO pins in software. This obsoletes the software pulsing facilities in gpio(4) (and gpioctl(8)) which will later be removed. To generate a diff of this commit: cvs rdiff -u -r1.9 -r1.10 src/sys/dev/gpio/files.gpio cvs rdiff -u -r0 -r1.1 src/sys/dev/gpio/gpiopwm.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/gpio/files.gpio diff -u src/sys/dev/gpio/files.gpio:1.9 src/sys/dev/gpio/files.gpio:1.10 --- src/sys/dev/gpio/files.gpio:1.9 Sun Oct 2 10:01:25 2011 +++ src/sys/dev/gpio/files.gpio Sun Nov 13 12:33:00 2011 @@ -1,4 +1,4 @@ -# $NetBSD: files.gpio,v 1.9 2011/10/02 10:01:25 mbalmer Exp $ +# $NetBSD: files.gpio,v 1.10 2011/11/13 12:33:00 mbalmer Exp $ define gpio {[offset = -1], [mask = 0], [flag = 0]} @@ -24,3 +24,8 @@ file dev/gpio/gpioow.c gpioow device gpiolock: gpiobus attach gpiolock at gpio file dev/gpio/gpiolock.c gpiolock + +# PWM +device gpiopwm: gpiobus +attach gpiopwm at gpio +file dev/gpio/gpiopwm.c gpiopwm Added files: Index: src/sys/dev/gpio/gpiopwm.c diff -u /dev/null src/sys/dev/gpio/gpiopwm.c:1.1 --- /dev/null Sun Nov 13 12:33:01 2011 +++ src/sys/dev/gpio/gpiopwm.c Sun Nov 13 12:33:01 2011 @@ -0,0 +1,246 @@ +/* $NetBSD: gpiopwm.c,v 1.1 2011/11/13 12:33:01 mbalmer Exp $ */ + +/* + * Copyright (c) 2011 Marc Balmer <m...@msys.ch> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Driver for pulsing GPIO pins in software + */ + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/gpio.h> +#include <sys/sysctl.h> + +#include <dev/gpio/gpiovar.h> + +#define GPIOPWM_NPINS 1 + +struct gpiopwm_softc { + device_t sc_dev; + void *sc_gpio; + struct gpio_pinmap sc_map; + int _map[GPIOPWM_NPINS]; + + callout_t sc_pulse; + int sc_ticks_on; + int sc_ticks_off; + + struct sysctllog *sc_log; + int sc_dying; +}; + +int gpiopwm_match(device_t, cfdata_t, void *); +void gpiopwm_attach(device_t, device_t, void *); +int gpiopwm_detach(device_t, int); +int gpiopwm_activate(device_t, enum devact); +static int gpiopwm_set_on(SYSCTLFN_ARGS); +static int gpiopwm_set_off(SYSCTLFN_ARGS); +static void gpiopwm_pulse(void *); + +CFATTACH_DECL_NEW(gpiopwm, sizeof(struct gpiopwm_softc), + gpiopwm_match, gpiopwm_attach, gpiopwm_detach, gpiopwm_activate); + +extern struct cfdriver gpiopwm_cd; + +int +gpiopwm_match(device_t parent, cfdata_t cf, + void *aux) +{ + struct gpio_attach_args *ga = aux; + + if (strcmp(ga->ga_dvname, cf->cf_name)) + return 0; + + if (ga->ga_offset == -1) + return 0; + + /* Check number of pins, must be 1 */ + if (gpio_npins(ga->ga_mask) != GPIOPWM_NPINS) { + aprint_debug("%s: invalid pin mask 0x%02x\n", cf->cf_name, + ga->ga_mask); + return 0; + } + return 1; +} + +void +gpiopwm_attach(device_t parent, device_t self, void *aux) +{ + struct gpiopwm_softc *sc = device_private(self); + struct gpio_attach_args *ga = aux; + const struct sysctlnode *node; + + sc->sc_dev = self; + + /* Map pin */ + sc->sc_gpio = ga->ga_gpio; + sc->sc_map.pm_map = sc->_map; + if (gpio_pin_map(sc->sc_gpio, ga->ga_offset, ga->ga_mask, + &sc->sc_map)) { + aprint_error(": can't map pin\n"); + return; + } + aprint_normal(" [%d]", sc->sc_map.pm_map[0]); + pmf_device_register(self, NULL, NULL); + + callout_init(&sc->sc_pulse, CALLOUT_MPSAFE); + callout_setfunc(&sc->sc_pulse, gpiopwm_pulse, sc); + + sysctl_createv(NULL, 0, NULL, NULL, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "hw", NULL, + NULL, 0, NULL, 0, + CTL_HW, CTL_EOL); + sysctl_createv(&sc->sc_log, 0, NULL, &node, + 0, + CTLTYPE_NODE, device_xname(sc->sc_dev), + SYSCTL_DESCR("GPIO software PWM"), + NULL, 0, NULL, 0, + CTL_HW, CTL_CREATE, CTL_EOL); + + if (node == NULL) { + printf(": can't create sysctl node\n"); + return; + } + + sysctl_createv(&sc->sc_log, 0, &node, NULL, + CTLFLAG_READWRITE, + CTLTYPE_INT, "on", + SYSCTL_DESCR("PWM 'on' period in ticks"), + gpiopwm_set_on, 0, sc, 0, + CTL_CREATE, CTL_EOL); + sysctl_createv(&sc->sc_log, 0, &node, NULL, + CTLFLAG_READWRITE, + CTLTYPE_INT, "off", + SYSCTL_DESCR("PWM 'off' period in ticks"), + gpiopwm_set_off, 0, sc, 0, + CTL_CREATE, CTL_EOL); + + aprint_normal("\n"); + return; +} + +int +gpiopwm_detach(device_t self, int flags) +{ + struct gpiopwm_softc *sc = device_private(self); + + callout_halt(&sc->sc_pulse, NULL); + callout_destroy(&sc->sc_pulse); + + pmf_device_deregister(self); + gpio_pin_unmap(sc->sc_gpio, &sc->sc_map); + + if (sc->sc_log != NULL) { + sysctl_teardown(&sc->sc_log); + sc->sc_log = NULL; + } + return 0; +} + +static int +gpiopwm_set_on(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct gpiopwm_softc *sc; + int val, error; + + node = *rnode; + sc = node.sysctl_data; + + callout_halt(&sc->sc_pulse, NULL); + gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_LOW); + node.sysctl_data = &val; + + val = sc->sc_ticks_on; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + sc->sc_ticks_on = val; + if (sc->sc_ticks_on > 0 && sc->sc_ticks_off > 0) { + gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_HIGH); + callout_schedule(&sc->sc_pulse, sc->sc_ticks_on); + } + return 0; +} + +static int +gpiopwm_set_off(SYSCTLFN_ARGS) +{ + struct sysctlnode node; + struct gpiopwm_softc *sc; + int val, error; + + node = *rnode; + sc = node.sysctl_data; + + callout_halt(&sc->sc_pulse, NULL); + gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_LOW); + node.sysctl_data = &val; + + val = sc->sc_ticks_off; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + sc->sc_ticks_off = val; + if (sc->sc_ticks_on > 0 && sc->sc_ticks_off > 0) { + gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_HIGH); + callout_schedule(&sc->sc_pulse, sc->sc_ticks_on); + } + return 0; +} + +static void +gpiopwm_pulse(void *arg) +{ + struct gpiopwm_softc *sc; + + sc = arg; + if (gpio_pin_read(sc->sc_gpio, &sc->sc_map, 0) == GPIO_PIN_HIGH) { + gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_LOW); + callout_schedule(&sc->sc_pulse, sc->sc_ticks_off); + } else { + gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_HIGH); + callout_schedule(&sc->sc_pulse, sc->sc_ticks_on); + } +} + +int +gpiopwm_activate(device_t self, enum devact act) +{ + struct gpiopwm_softc *sc = device_private(self); + + switch (act) { + case DVACT_DEACTIVATE: + sc->sc_dying = 1; + return 0; + default: + return EOPNOTSUPP; + } + +}