svn commit: r276533 - in head/sys: arm/altera/socfpga boot/fdt/dts/arm

Ruslan Bukin br at FreeBSD.org
Fri Jan 2 13:15:38 UTC 2015


Author: br
Date: Fri Jan  2 13:15:36 2015
New Revision: 276533
URL: https://svnweb.freebsd.org/changeset/base/276533

Log:
  Add driver for general-purpose I/O (GPIO).
  
  Sponsored by:	DARPA, AFRL

Added:
  head/sys/arm/altera/socfpga/socfpga_gpio.c   (contents, props changed)
Modified:
  head/sys/arm/altera/socfpga/files.socfpga
  head/sys/boot/fdt/dts/arm/socfpga.dtsi

Modified: head/sys/arm/altera/socfpga/files.socfpga
==============================================================================
--- head/sys/arm/altera/socfpga/files.socfpga	Fri Jan  2 13:10:33 2015	(r276532)
+++ head/sys/arm/altera/socfpga/files.socfpga	Fri Jan  2 13:15:36 2015	(r276533)
@@ -18,6 +18,7 @@ arm/altera/socfpga/socfpga_machdep.c		st
 arm/altera/socfpga/socfpga_manager.c		standard
 arm/altera/socfpga/socfpga_rstmgr.c		standard
 arm/altera/socfpga/socfpga_mp.c			optional smp
+arm/altera/socfpga/socfpga_gpio.c		optional gpio
 
 dev/dwc/if_dwc.c				optional dwc
 dev/mii/micphy.c				optional micphy

Added: head/sys/arm/altera/socfpga/socfpga_gpio.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm/altera/socfpga/socfpga_gpio.c	Fri Jan  2 13:15:36 2015	(r276533)
@@ -0,0 +1,437 @@
+/*-
+ * Copyright (c) 2015 Ruslan Bukin <br at bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 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.
+ */
+
+/*
+ * SOCFPGA General-Purpose I/O Interface.
+ * Chapter 22, Cyclone V Device Handbook (CV-5V2 2014.07.22)
+ */
+
+/*
+ * The GPIO modules are instances of the Synopsys® DesignWare® APB General
+ * Purpose Programming I/O (DW_apb_gpio) peripheral.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <sys/mutex.h>
+#include <sys/gpio.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include "gpio_if.h"
+
+#define READ4(_sc, _reg) \
+	bus_read_4((_sc)->res[0], _reg)
+#define WRITE4(_sc, _reg, _val) \
+	bus_write_4((_sc)->res[0], _reg, _val)
+
+#define	GPIO_SWPORTA_DR		0x00	/* Port A Data Register */
+#define	GPIO_SWPORTA_DDR	0x04	/* Port A Data Direction Register */
+#define	GPIO_INTEN		0x30	/* Interrupt Enable Register */
+#define	GPIO_INTMASK		0x34	/* Interrupt Mask Register */
+#define	GPIO_INTTYPE_LEVEL	0x38	/* Interrupt Level Register */
+#define	GPIO_INT_POLARITY	0x3C	/* Interrupt Polarity Register */
+#define	GPIO_INTSTATUS		0x40	/* Interrupt Status Register */
+#define	GPIO_RAW_INTSTATUS	0x44	/* Raw Interrupt Status Register */
+#define	GPIO_DEBOUNCE		0x48	/* Debounce Enable Register */
+#define	GPIO_PORTA_EOI		0x4C	/* Clear Interrupt Register */
+#define	GPIO_EXT_PORTA		0x50	/* External Port A Register */
+#define	GPIO_LS_SYNC		0x60	/* Synchronization Level Register */
+#define	GPIO_ID_CODE		0x64	/* ID Code Register */
+#define	GPIO_VER_ID_CODE	0x6C	/* GPIO Version Register */
+#define	GPIO_CONFIG_REG2	0x70	/* Configuration Register 2 */
+#define	 ENCODED_ID_PWIDTH_M	0x1f	/* Width of GPIO Port N Mask */
+#define	 ENCODED_ID_PWIDTH_S(n)	(5 * n)	/* Width of GPIO Port N Shift */
+#define	GPIO_CONFIG_REG1	0x74	/* Configuration Register 1 */
+
+enum port_no {
+	PORTA,
+	PORTB,
+	PORTC,
+	PORTD,
+};
+
+#define	NR_GPIO_MAX	32	/* Maximum pins per port */
+
+#define	GPIO_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
+#define	GPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->sc_mtx)
+
+#define	DEFAULT_CAPS	(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)
+
+/*
+ * GPIO interface
+ */
+static int socfpga_gpio_pin_max(device_t, int *);
+static int socfpga_gpio_pin_getcaps(device_t, uint32_t, uint32_t *);
+static int socfpga_gpio_pin_getname(device_t, uint32_t, char *);
+static int socfpga_gpio_pin_getflags(device_t, uint32_t, uint32_t *);
+static int socfpga_gpio_pin_setflags(device_t, uint32_t, uint32_t);
+static int socfpga_gpio_pin_set(device_t, uint32_t, unsigned int);
+static int socfpga_gpio_pin_get(device_t, uint32_t, unsigned int *);
+static int socfpga_gpio_pin_toggle(device_t, uint32_t pin);
+
+struct socfpga_gpio_softc {
+	struct resource		*res[1];
+	bus_space_tag_t		bst;
+	bus_space_handle_t	bsh;
+
+	device_t		dev;
+	struct mtx		sc_mtx;
+	int			gpio_npins;
+	struct gpio_pin		gpio_pins[NR_GPIO_MAX];
+};
+
+struct socfpga_gpio_softc *gpio_sc;
+
+static struct resource_spec socfpga_gpio_spec[] = {
+	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
+	{ -1, 0 }
+};
+
+static int
+socfpga_gpio_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (!ofw_bus_is_compatible(dev, "snps,dw-apb-gpio"))
+		return (ENXIO);
+
+	device_set_desc(dev, "DesignWare General-Purpose I/O Interface");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+socfpga_gpio_attach(device_t dev)
+{
+	struct socfpga_gpio_softc *sc;
+	int version;
+	int nr_pins;
+	int cfg2;
+	int i;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+	if (bus_alloc_resources(dev, socfpga_gpio_spec, sc->res)) {
+		device_printf(dev, "could not allocate resources\n");
+		return (ENXIO);
+	}
+
+	/* Memory interface */
+	sc->bst = rman_get_bustag(sc->res[0]);
+	sc->bsh = rman_get_bushandle(sc->res[0]);
+
+	gpio_sc = sc;
+
+	version =  READ4(sc, GPIO_VER_ID_CODE);
+#if 0
+	device_printf(sc->dev, "Version = 0x%08x\n", version);
+#endif
+
+	/*
+	 * Take number of pins from hardware.
+	 * XXX: Assume we have GPIO port A only.
+	 */
+	cfg2 = READ4(sc, GPIO_CONFIG_REG2);
+	nr_pins = (cfg2 >> ENCODED_ID_PWIDTH_S(PORTA)) & \
+			ENCODED_ID_PWIDTH_M;
+	sc->gpio_npins = nr_pins + 1;
+
+	for (i = 0; i < sc->gpio_npins; i++) {
+		sc->gpio_pins[i].gp_pin = i;
+		sc->gpio_pins[i].gp_caps = DEFAULT_CAPS;
+		sc->gpio_pins[i].gp_flags =
+		    (READ4(sc, GPIO_SWPORTA_DDR) & (1 << i)) ?
+		    GPIO_PIN_OUTPUT: GPIO_PIN_INPUT;
+		snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME,
+		    "socfpga_gpio%d.%d", device_get_unit(dev), i);
+	}
+
+	device_add_child(dev, "gpioc", -1);
+	device_add_child(dev, "gpiobus", -1);
+
+	return (bus_generic_attach(dev));
+}
+
+static int
+socfpga_gpio_pin_max(device_t dev, int *maxpin)
+{
+	struct socfpga_gpio_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	*maxpin = sc->gpio_npins - 1;
+
+	return (0);
+}
+
+static int
+socfpga_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+	struct socfpga_gpio_softc *sc;
+	int i;
+
+	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);
+	memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME);
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+socfpga_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+	struct socfpga_gpio_softc *sc;
+	int i;
+
+	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);
+	*caps = sc->gpio_pins[i].gp_caps;
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+socfpga_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+	struct socfpga_gpio_softc *sc;
+	int i;
+
+	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);
+	*flags = sc->gpio_pins[i].gp_flags;
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+socfpga_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+	struct socfpga_gpio_softc *sc;
+	int i;
+
+	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);
+	*val = (READ4(sc, GPIO_EXT_PORTA) & (1 << i)) ? 1 : 0;
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+socfpga_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+	struct socfpga_gpio_softc *sc;
+	int reg;
+	int i;
+
+	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);
+	reg = READ4(sc, GPIO_SWPORTA_DR);
+	if (reg & (1 << i))
+		reg &= ~(1 << i);
+	else
+		reg |= (1 << i);
+	WRITE4(sc, GPIO_SWPORTA_DR, reg);
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+
+static void
+socfpga_gpio_pin_configure(struct socfpga_gpio_softc *sc,
+    struct gpio_pin *pin, unsigned int flags)
+{
+	int reg;
+
+	GPIO_LOCK(sc);
+
+	/*
+	 * Manage input/output
+	 */
+
+	reg = READ4(sc, GPIO_SWPORTA_DDR);
+	if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
+		pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT);
+		if (flags & GPIO_PIN_OUTPUT) {
+			pin->gp_flags |= GPIO_PIN_OUTPUT;
+			reg |= (1 << pin->gp_pin);
+		} else {
+			pin->gp_flags |= GPIO_PIN_INPUT;
+			reg &= ~(1 << pin->gp_pin);
+		}
+	}
+
+	WRITE4(sc, GPIO_SWPORTA_DDR, reg);
+	GPIO_UNLOCK(sc);
+}
+
+
+static int
+socfpga_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+	struct socfpga_gpio_softc *sc;
+	int i;
+
+	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);
+
+	socfpga_gpio_pin_configure(sc, &sc->gpio_pins[i], flags);
+
+	return (0);
+}
+
+static int
+socfpga_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
+{
+	struct socfpga_gpio_softc *sc;
+	int reg;
+	int i;
+
+	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);
+	reg = READ4(sc, GPIO_SWPORTA_DR);
+	if (value)
+		reg |= (1 << i);
+	else
+		reg &= ~(1 << i);
+	WRITE4(sc, GPIO_SWPORTA_DR, reg);
+	GPIO_UNLOCK(sc);
+
+	return (0);
+}
+
+static device_method_t socfpga_gpio_methods[] = {
+	DEVMETHOD(device_probe,		socfpga_gpio_probe),
+	DEVMETHOD(device_attach,	socfpga_gpio_attach),
+
+	/* GPIO protocol */
+	DEVMETHOD(gpio_pin_max,		socfpga_gpio_pin_max),
+	DEVMETHOD(gpio_pin_getname,	socfpga_gpio_pin_getname),
+	DEVMETHOD(gpio_pin_getcaps,	socfpga_gpio_pin_getcaps),
+	DEVMETHOD(gpio_pin_getflags,	socfpga_gpio_pin_getflags),
+	DEVMETHOD(gpio_pin_get,		socfpga_gpio_pin_get),
+	DEVMETHOD(gpio_pin_toggle,	socfpga_gpio_pin_toggle),
+	DEVMETHOD(gpio_pin_setflags,	socfpga_gpio_pin_setflags),
+	DEVMETHOD(gpio_pin_set,		socfpga_gpio_pin_set),
+	{ 0, 0 }
+};
+
+static driver_t socfpga_gpio_driver = {
+	"gpio",
+	socfpga_gpio_methods,
+	sizeof(struct socfpga_gpio_softc),
+};
+
+static devclass_t socfpga_gpio_devclass;
+
+DRIVER_MODULE(socfpga_gpio, simplebus, socfpga_gpio_driver,
+    socfpga_gpio_devclass, 0, 0);

Modified: head/sys/boot/fdt/dts/arm/socfpga.dtsi
==============================================================================
--- head/sys/boot/fdt/dts/arm/socfpga.dtsi	Fri Jan  2 13:10:33 2015	(r276532)
+++ head/sys/boot/fdt/dts/arm/socfpga.dtsi	Fri Jan  2 13:15:36 2015	(r276533)
@@ -76,6 +76,11 @@
 			reg = <0xffd08000 0x1000>;
 		};
 
+		clkmgr: clkmgr at ffd04000 {
+			compatible = "altr,clk-mgr";
+			reg = <0xffd04000 0x1000>;
+		};
+
 		rstmgr: rstmgr at ffd05000 {
 			compatible = "altr,rst-mgr";
 			reg = <0xffd05000 0x1000>;
@@ -94,6 +99,36 @@
 			interrupt-parent = <&GIC>;
 		};
 
+		gpio0: gpio at ff708000 {
+			compatible = "snps,dw-apb-gpio";
+			reg = <0xff708000 0x1000>;
+			porta: gpio-controller at 0 {
+				compatible = "snps,dw-apb-gpio-port";
+				gpio-controller;
+				snps,nr-gpios = <29>;
+			};
+		};
+
+		gpio1: gpio at ff709000 {
+			compatible = "snps,dw-apb-gpio";
+			reg = <0xff709000 0x1000>;
+			portb: gpio-controller at 0 {
+				compatible = "snps,dw-apb-gpio-port";
+				gpio-controller;
+				snps,nr-gpios = <29>;
+			};
+		};
+
+		gpio2: gpio at ff70a000 {
+			compatible = "snps,dw-apb-gpio";
+			reg = <0xff70a000 0x1000>;
+			portc: gpio-controller at 0 {
+				compatible = "snps,dw-apb-gpio-port";
+				gpio-controller;
+				snps,nr-gpios = <27>;
+			};
+		};
+
 		serial0: serial at ffc02000 {
 			compatible = "ns16550";
 			reg = <0xffc02000 0x1000>;


More information about the svn-src-head mailing list