From owner-svn-src-all@FreeBSD.ORG Wed Dec 28 05:57:04 2011 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 025B3106564A; Wed, 28 Dec 2011 05:57:04 +0000 (UTC) (envelope-from gonzo@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id DF0658FC0C; Wed, 28 Dec 2011 05:57:03 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id pBS5v3Nt085559; Wed, 28 Dec 2011 05:57:03 GMT (envelope-from gonzo@svn.freebsd.org) Received: (from gonzo@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id pBS5v3HZ085555; Wed, 28 Dec 2011 05:57:03 GMT (envelope-from gonzo@svn.freebsd.org) Message-Id: <201112280557.pBS5v3HZ085555@svn.freebsd.org> From: Oleksandr Tymoshenko Date: Wed, 28 Dec 2011 05:57:03 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r228925 - head/sys/mips/cavium X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 28 Dec 2011 05:57:04 -0000 Author: gonzo Date: Wed Dec 28 05:57:03 2011 New Revision: 228925 URL: http://svn.freebsd.org/changeset/base/228925 Log: - Add generic GPIO driver for Cavium Octeon. At the moment pin definition is hardcoded but will be changed later with more flexible way to define them. Added: head/sys/mips/cavium/octeon_gpio.c (contents, props changed) head/sys/mips/cavium/octeon_gpiovar.h (contents, props changed) Modified: head/sys/mips/cavium/files.octeon1 Modified: head/sys/mips/cavium/files.octeon1 ============================================================================== --- head/sys/mips/cavium/files.octeon1 Wed Dec 28 05:35:33 2011 (r228924) +++ head/sys/mips/cavium/files.octeon1 Wed Dec 28 05:57:03 2011 (r228925) @@ -49,6 +49,8 @@ mips/cavium/usb/octusb_octeon.c option contrib/octeon-sdk/cvmx-usb.c optional octusb +mips/cavium/octeon_gpio.c optional gpio + # XXX Some files could be excluded in some configurations. Making them # optional but on in the default config would seem reasonable. contrib/octeon-sdk/cvmx-cmd-queue.c standard Added: head/sys/mips/cavium/octeon_gpio.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/mips/cavium/octeon_gpio.c Wed Dec 28 05:57:03 2011 (r228925) @@ -0,0 +1,494 @@ +/*- + * Copyright (c) 2011, Oleksandr Tymoshenko + * 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 unmodified, 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + */ + +/* + * GPIO driver for Cavium Octeon + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include "gpio_if.h" + +#define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) + +struct octeon_gpio_pin { + const char *name; + int pin; + int flags; +}; + +/* + * on CAP100 GPIO 7 is "Factory defaults" button + * + */ +static struct octeon_gpio_pin octeon_gpio_pins[] = { + { "F/D", 7, GPIO_PIN_INPUT}, + { NULL, 0, 0}, +}; + +/* + * Helpers + */ +static void octeon_gpio_pin_configure(struct octeon_gpio_softc *sc, + struct gpio_pin *pin, uint32_t flags); + +/* + * Driver stuff + */ +static void octeon_gpio_identify(driver_t *, device_t); +static int octeon_gpio_probe(device_t dev); +static int octeon_gpio_attach(device_t dev); +static int octeon_gpio_detach(device_t dev); +static int octeon_gpio_filter(void *arg); +static void octeon_gpio_intr(void *arg); + +/* + * GPIO interface + */ +static int octeon_gpio_pin_max(device_t dev, int *maxpin); +static int octeon_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps); +static int octeon_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t + *flags); +static int octeon_gpio_pin_getname(device_t dev, uint32_t pin, char *name); +static int octeon_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags); +static int octeon_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value); +static int octeon_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val); +static int octeon_gpio_pin_toggle(device_t dev, uint32_t pin); + +static void +octeon_gpio_pin_configure(struct octeon_gpio_softc *sc, struct gpio_pin *pin, + unsigned int flags) +{ + uint32_t mask; + cvmx_gpio_bit_cfgx_t gpio_cfgx; + + mask = 1 << pin->gp_pin; + GPIO_LOCK(sc); + + /* + * Manage input/output + */ + if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { + gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(pin->gp_pin)); + pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); + if (flags & GPIO_PIN_OUTPUT) { + pin->gp_flags |= GPIO_PIN_OUTPUT; + gpio_cfgx.s.tx_oe = 1; + } + else { + pin->gp_flags |= GPIO_PIN_INPUT; + gpio_cfgx.s.tx_oe = 0; + } + if (flags & GPIO_PIN_INVIN) + gpio_cfgx.s.rx_xor = 1; + else + gpio_cfgx.s.rx_xor = 0; + cvmx_write_csr(CVMX_GPIO_BIT_CFGX(pin->gp_pin), gpio_cfgx.u64); + } + + GPIO_UNLOCK(sc); +} + +static int +octeon_gpio_pin_max(device_t dev, int *maxpin) +{ + + *maxpin = OCTEON_GPIO_PINS - 1; + return (0); +} + +static int +octeon_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct octeon_gpio_softc *sc = device_get_softc(dev); + int i; + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + *caps = sc->gpio_pins[i].gp_caps; + GPIO_UNLOCK(sc); + + return (0); +} + +static int +octeon_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct octeon_gpio_softc *sc = device_get_softc(dev); + int i; + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + *flags = sc->gpio_pins[i].gp_flags; + GPIO_UNLOCK(sc); + + return (0); +} + +static int +octeon_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct octeon_gpio_softc *sc = device_get_softc(dev); + int i; + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +octeon_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + int i; + struct octeon_gpio_softc *sc = device_get_softc(dev); + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + /* Filter out unwanted flags */ + if ((flags &= sc->gpio_pins[i].gp_caps) != flags) + return (EINVAL); + + /* Can't mix input/output together */ + if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == + (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) + return (EINVAL); + + octeon_gpio_pin_configure(sc, &sc->gpio_pins[i], flags); + return (0); +} + +static int +octeon_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) +{ + struct octeon_gpio_softc *sc = device_get_softc(dev); + int i; + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + if (value) + cvmx_gpio_set(1 << pin); + else + cvmx_gpio_clear(1 << pin); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +octeon_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) +{ + struct octeon_gpio_softc *sc = device_get_softc(dev); + int i; + uint64_t state; + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + state = cvmx_gpio_read(); + *val = (state & (1 << pin)) ? 1 : 0; + GPIO_UNLOCK(sc); + + return (0); +} + +static int +octeon_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + int i; + uint64_t state; + struct octeon_gpio_softc *sc = device_get_softc(dev); + + for (i = 0; i < sc->gpio_npins; i++) { + if (sc->gpio_pins[i].gp_pin == pin) + break; + } + + if (i >= sc->gpio_npins) + return (EINVAL); + + GPIO_LOCK(sc); + /* + * XXX: Need to check if read returns actual state of output + * pins or we need to keep this information by ourself + */ + state = cvmx_gpio_read(); + if (state & (1 << pin)) + cvmx_gpio_clear(1 << pin); + else + cvmx_gpio_set(1 << pin); + GPIO_UNLOCK(sc); + + return (0); +} + +static int +octeon_gpio_filter(void *arg) +{ + cvmx_gpio_bit_cfgx_t gpio_cfgx; + void **cookie = arg; + struct octeon_gpio_softc *sc = *cookie; + long int irq = (cookie - sc->gpio_intr_cookies); + + if ((irq < 0) || (irq >= OCTEON_GPIO_IRQS)) + return (FILTER_STRAY); + + gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(irq)); + /* Clear rising edge detector */ + if (gpio_cfgx.s.int_type == OCTEON_GPIO_IRQ_EDGE) + cvmx_gpio_interrupt_clear(1 << irq); + /* disable interrupt */ + gpio_cfgx.s.int_en = 0; + cvmx_write_csr(CVMX_GPIO_BIT_CFGX(irq), gpio_cfgx.u64); + + return (FILTER_SCHEDULE_THREAD); +} + +static void +octeon_gpio_intr(void *arg) +{ + cvmx_gpio_bit_cfgx_t gpio_cfgx; + void **cookie = arg; + struct octeon_gpio_softc *sc = *cookie; + long int irq = (cookie - sc->gpio_intr_cookies); + + if ((irq < 0) || (irq >= OCTEON_GPIO_IRQS)) { + printf("%s: invalid GPIO IRQ: %ld\n", + __func__, irq); + return; + } + + GPIO_LOCK(sc); + gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(irq)); + /* disable interrupt */ + gpio_cfgx.s.int_en = 1; + cvmx_write_csr(CVMX_GPIO_BIT_CFGX(irq), gpio_cfgx.u64); + + /* TODO: notify bus here or something */ + printf("GPIO IRQ for pin %ld\n", irq); + GPIO_UNLOCK(sc); +} + +static void +octeon_gpio_identify(driver_t *drv, device_t parent) +{ + + BUS_ADD_CHILD(parent, 0, "gpio", 0); +} + +static int +octeon_gpio_probe(device_t dev) +{ + + device_set_desc(dev, "Cavium Octeon GPIO driver"); + return (0); +} + +static int +octeon_gpio_attach(device_t dev) +{ + struct octeon_gpio_softc *sc = device_get_softc(dev); + struct octeon_gpio_pin *pinp; + cvmx_gpio_bit_cfgx_t gpio_cfgx; + + int i; + + KASSERT((device_get_unit(dev) == 0), + ("octeon_gpio: Only one gpio module supported")); + + mtx_init(&sc->gpio_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF); + + for ( i = 0; i < OCTEON_GPIO_IRQS; i++) { + if ((sc->gpio_irq_res[i] = bus_alloc_resource(dev, + SYS_RES_IRQ, &sc->gpio_irq_rid[i], + CVMX_IRQ_GPIO0 + i, CVMX_IRQ_GPIO0 + i, 1, + RF_SHAREABLE | RF_ACTIVE)) == NULL) { + device_printf(dev, "unable to allocate IRQ resource\n"); + return (ENXIO); + } + + sc->gpio_intr_cookies[i] = sc; + if ((bus_setup_intr(dev, sc->gpio_irq_res[i], INTR_TYPE_MISC, + octeon_gpio_filter, octeon_gpio_intr, + &(sc->gpio_intr_cookies[i]), &sc->gpio_ih[i]))) { + device_printf(dev, + "WARNING: unable to register interrupt handler\n"); + return (ENXIO); + } + } + + sc->dev = dev; + /* Configure all pins as input */ + /* disable interrupts for all pins */ + pinp = octeon_gpio_pins; + i = 0; + while (pinp->name) { + strncpy(sc->gpio_pins[i].gp_name, pinp->name, GPIOMAXNAME); + sc->gpio_pins[i].gp_pin = pinp->pin; + sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; + sc->gpio_pins[i].gp_flags = 0; + octeon_gpio_pin_configure(sc, &sc->gpio_pins[i], pinp->flags); + pinp++; + i++; + } + + sc->gpio_npins = i; + +#if 0 + /* + * Sample: how to enable edge-triggered interrupt + * for GPIO pin + */ + gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(7)); + gpio_cfgx.s.int_en = 1; + gpio_cfgx.s.int_type = OCTEON_GPIO_IRQ_EDGE; + cvmx_write_csr(CVMX_GPIO_BIT_CFGX(7), gpio_cfgx.u64); +#endif + + if (bootverbose) { + for (i = 0; i < 16; i++) { + gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(i)); + device_printf(dev, "[pin%d] output=%d, invinput=%d, intr=%d, intr_type=%s\n", + i, gpio_cfgx.s.tx_oe, gpio_cfgx.s.rx_xor, + gpio_cfgx.s.int_en, gpio_cfgx.s.int_type ? "rising edge" : "level"); + } + } + + device_add_child(dev, "gpioc", device_get_unit(dev)); + device_add_child(dev, "gpiobus", device_get_unit(dev)); + return (bus_generic_attach(dev)); +} + +static int +octeon_gpio_detach(device_t dev) +{ + struct octeon_gpio_softc *sc = device_get_softc(dev); + int i; + + KASSERT(mtx_initialized(&sc->gpio_mtx), ("gpio mutex not initialized")); + + for ( i = 0; i < OCTEON_GPIO_IRQS; i++) { + bus_release_resource(dev, SYS_RES_IRQ, + sc->gpio_irq_rid[i], sc->gpio_irq_res[i]); + } + bus_generic_detach(dev); + + mtx_destroy(&sc->gpio_mtx); + + return(0); +} + +static device_method_t octeon_gpio_methods[] = { + DEVMETHOD(device_identify, octeon_gpio_identify), + DEVMETHOD(device_probe, octeon_gpio_probe), + DEVMETHOD(device_attach, octeon_gpio_attach), + DEVMETHOD(device_detach, octeon_gpio_detach), + + /* GPIO protocol */ + DEVMETHOD(gpio_pin_max, octeon_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, octeon_gpio_pin_getname), + DEVMETHOD(gpio_pin_getflags, octeon_gpio_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, octeon_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, octeon_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, octeon_gpio_pin_get), + DEVMETHOD(gpio_pin_set, octeon_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, octeon_gpio_pin_toggle), + {0, 0}, +}; + +static driver_t octeon_gpio_driver = { + "gpio", + octeon_gpio_methods, + sizeof(struct octeon_gpio_softc), +}; +static devclass_t octeon_gpio_devclass; + +DRIVER_MODULE(octeon_gpio, ciu, octeon_gpio_driver, octeon_gpio_devclass, 0, 0); Added: head/sys/mips/cavium/octeon_gpiovar.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/mips/cavium/octeon_gpiovar.h Wed Dec 28 05:57:03 2011 (r228925) @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 2011, Oleksandr Tymoshenko + * 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 unmodified, 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + * + */ + +#ifndef __OCTEON_GPIOVAR_H__ +#define __OCTEON_GPIOVAR_H__ + +#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->gpio_mtx) +#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->gpio_mtx) +#define GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->gpio_mtx, MA_OWNED) + +#define OCTEON_GPIO_IRQ_LEVEL 0 +#define OCTEON_GPIO_IRQ_EDGE 1 + +#define OCTEON_GPIO_PINS 24 +#define OCTEON_GPIO_IRQS 16 + +struct octeon_gpio_softc { + device_t dev; + struct mtx gpio_mtx; + struct resource *gpio_irq_res[OCTEON_GPIO_IRQS]; + int gpio_irq_rid[OCTEON_GPIO_IRQS]; + void *gpio_ih[OCTEON_GPIO_IRQS]; + void *gpio_intr_cookies[OCTEON_GPIO_IRQS]; + int gpio_npins; + struct gpio_pin gpio_pins[OCTEON_GPIO_PINS]; +}; + +#endif /* __OCTEON_GPIOVAR_H__ */