Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 15 May 2016 15:54:41 +0000 (UTC)
From:      Jared McNeill <jmcneill@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r299860 - head/sys/arm/allwinner
Message-ID:  <201605151554.u4FFsfh6088526@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jmcneill
Date: Sun May 15 15:54:41 2016
New Revision: 299860
URL: https://svnweb.freebsd.org/changeset/base/299860

Log:
  Add support for the AXP813/AXP818 power key and GPIO pins.

Modified:
  head/sys/arm/allwinner/axp81x.c

Modified: head/sys/arm/allwinner/axp81x.c
==============================================================================
--- head/sys/arm/allwinner/axp81x.c	Sun May 15 15:52:34 2016	(r299859)
+++ head/sys/arm/allwinner/axp81x.c	Sun May 15 15:54:41 2016	(r299860)
@@ -40,20 +40,51 @@ __FBSDID("$FreeBSD$");
 #include <sys/rman.h>
 #include <sys/kernel.h>
 #include <sys/reboot.h>
+#include <sys/proc.h>
+#include <sys/gpio.h>
 #include <sys/module.h>
 #include <machine/bus.h>
 
 #include <dev/iicbus/iicbus.h>
 #include <dev/iicbus/iiconf.h>
 
+#include <dev/gpio/gpiobusvar.h>
+
 #include <dev/ofw/ofw_bus.h>
 #include <dev/ofw/ofw_bus_subr.h>
 
 #include "iicbus_if.h"
+#include "gpio_if.h"
 
 #define	AXP_ICTYPE		0x03
 #define	AXP_POWERBAT		0x32
 #define	 AXP_POWERBAT_SHUTDOWN	(1 << 7)
+#define	AXP_IRQEN1		0x40
+#define	AXP_IRQEN2		0x41
+#define	AXP_IRQEN3		0x42
+#define	AXP_IRQEN4		0x43
+#define	AXP_IRQEN5		0x44
+#define	 AXP_IRQEN5_POKSIRQ	(1 << 4)
+#define	AXP_IRQEN6		0x45
+#define	AXP_IRQSTAT5		0x4c
+#define	 AXP_IRQSTAT5_POKSIRQ	(1 << 4)
+#define	AXP_GPIO0_CTRL		0x90
+#define	AXP_GPIO1_CTRL		0x92
+#define	 AXP_GPIO_FUNC		(0x7 << 0)
+#define	 AXP_GPIO_FUNC_SHIFT	0
+#define	 AXP_GPIO_FUNC_DRVLO	0
+#define	 AXP_GPIO_FUNC_DRVHI	1
+#define	 AXP_GPIO_FUNC_INPUT	2
+#define	AXP_GPIO_SIGBIT		0x94
+#define	AXP_GPIO_PD		0x97
+
+static const struct {
+	const char *name;
+	uint8_t	ctrl_reg;
+} axp81x_pins[] = {
+	{ "GPIO0", AXP_GPIO0_CTRL },
+	{ "GPIO1", AXP_GPIO1_CTRL },
+};
 
 static struct ofw_compat_data compat_data[] = {
 	{ "x-powers,axp813",			1 },
@@ -61,11 +92,23 @@ static struct ofw_compat_data compat_dat
 	{ NULL,					0 }
 };
 
+static struct resource_spec axp81x_spec[] = {
+	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
+	{ -1, 0 }
+};
+
 struct axp81x_softc {
+	struct resource		*res;
 	uint16_t		addr;
-	struct intr_config_hook	enum_hook;
+	void			*ih;
+	device_t		gpiodev;
+	struct mtx		mtx;
+	int			busy;
 };
 
+#define	AXP_LOCK(sc)	mtx_lock(&(sc)->mtx)
+#define	AXP_UNLOCK(sc)	mtx_unlock(&(sc)->mtx)
+
 static int
 axp81x_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size)
 {
@@ -124,6 +167,267 @@ axp81x_shutdown(void *devp, int howto)
 	axp81x_write(dev, AXP_POWERBAT, AXP_POWERBAT_SHUTDOWN);
 }
 
+static void
+axp81x_intr(void *arg)
+{
+	struct axp81x_softc *sc;
+	device_t dev;
+	uint8_t val;
+	int error;
+
+	dev = arg;
+	sc = device_get_softc(dev);
+
+	error = axp81x_read(dev, AXP_IRQSTAT5, &val, 1);
+	if (error != 0)
+		return;
+
+	if (val != 0) {
+		if ((val & AXP_IRQSTAT5_POKSIRQ) != 0) {
+			if (bootverbose)
+				device_printf(dev, "Power button pressed\n");
+			shutdown_nice(RB_POWEROFF);
+		}
+		/* Acknowledge */
+		axp81x_write(dev, AXP_IRQSTAT5, val);
+	}
+}
+
+static device_t
+axp81x_gpio_get_bus(device_t dev)
+{
+	struct axp81x_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	return (sc->gpiodev);
+}
+
+static int
+axp81x_gpio_pin_max(device_t dev, int *maxpin)
+{
+	*maxpin = nitems(axp81x_pins) - 1;
+
+	return (0);
+}
+
+static int
+axp81x_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+	if (pin >= nitems(axp81x_pins))
+		return (EINVAL);
+
+	snprintf(name, GPIOMAXNAME, "%s", axp81x_pins[pin].name);
+
+	return (0);
+}
+
+static int
+axp81x_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+	if (pin >= nitems(axp81x_pins))
+		return (EINVAL);
+
+	*caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
+
+	return (0);
+}
+
+static int
+axp81x_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+	struct axp81x_softc *sc;
+	uint8_t data, func;
+	int error;
+
+	if (pin >= nitems(axp81x_pins))
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+
+	AXP_LOCK(sc);
+	THREAD_SLEEPING_OK();
+	error = axp81x_read(dev, axp81x_pins[pin].ctrl_reg, &data, 1);
+	if (error == 0) {
+		func = (data & AXP_GPIO_FUNC) >> AXP_GPIO_FUNC_SHIFT;
+		if (func == AXP_GPIO_FUNC_INPUT)
+			*flags = GPIO_PIN_INPUT;
+		else if (func == AXP_GPIO_FUNC_DRVLO ||
+		    func == AXP_GPIO_FUNC_DRVHI)
+			*flags = GPIO_PIN_OUTPUT;
+		else
+			*flags = 0;
+	}
+	THREAD_NO_SLEEPING();
+	AXP_UNLOCK(sc);
+
+	return (error);
+}
+
+static int
+axp81x_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+	struct axp81x_softc *sc;
+	uint8_t data;
+	int error;
+
+	if (pin >= nitems(axp81x_pins))
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+
+	AXP_LOCK(sc);
+	THREAD_SLEEPING_OK();
+	error = axp81x_read(dev, axp81x_pins[pin].ctrl_reg, &data, 1);
+	if (error == 0) {
+		data &= ~AXP_GPIO_FUNC;
+		if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) != 0) {
+			if ((flags & GPIO_PIN_OUTPUT) == 0)
+				data |= AXP_GPIO_FUNC_INPUT;
+		}
+		error = axp81x_write(dev, axp81x_pins[pin].ctrl_reg, data);
+	}
+	THREAD_NO_SLEEPING();
+	AXP_UNLOCK(sc);
+
+	return (error);
+}
+
+static int
+axp81x_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+	struct axp81x_softc *sc;
+	uint8_t data, func;
+	int error;
+
+	if (pin >= nitems(axp81x_pins))
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+
+	AXP_LOCK(sc);
+	THREAD_SLEEPING_OK();
+	error = axp81x_read(dev, axp81x_pins[pin].ctrl_reg, &data, 1);
+	if (error == 0) {
+		func = (data & AXP_GPIO_FUNC) >> AXP_GPIO_FUNC_SHIFT;
+		switch (func) {
+		case AXP_GPIO_FUNC_DRVLO:
+			*val = 0;
+			break;
+		case AXP_GPIO_FUNC_DRVHI:
+			*val = 1;
+			break;
+		case AXP_GPIO_FUNC_INPUT:
+			error = axp81x_read(dev, AXP_GPIO_SIGBIT, &data, 1);
+			if (error == 0)
+				*val = (data & (1 << pin)) ? 1 : 0;
+			break;
+		default:
+			error = EIO;
+			break;
+		}
+	}
+	THREAD_NO_SLEEPING();
+	AXP_UNLOCK(sc);
+
+	return (error);
+}
+
+static int
+axp81x_gpio_pin_set(device_t dev, uint32_t pin, unsigned int val)
+{
+	struct axp81x_softc *sc;
+	uint8_t data, func;
+	int error;
+
+	if (pin >= nitems(axp81x_pins))
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+
+	AXP_LOCK(sc);
+	THREAD_SLEEPING_OK();
+	error = axp81x_read(dev, axp81x_pins[pin].ctrl_reg, &data, 1);
+	if (error == 0) {
+		func = (data & AXP_GPIO_FUNC) >> AXP_GPIO_FUNC_SHIFT;
+		switch (func) {
+		case AXP_GPIO_FUNC_DRVLO:
+		case AXP_GPIO_FUNC_DRVHI:
+			data &= ~AXP_GPIO_FUNC;
+			data |= (val << AXP_GPIO_FUNC_SHIFT);
+			break;
+		default:
+			error = EIO;
+			break;
+		}
+	}
+	if (error == 0)
+		error = axp81x_write(dev, axp81x_pins[pin].ctrl_reg, data);
+	THREAD_NO_SLEEPING();
+	AXP_UNLOCK(sc);
+
+	return (error);
+}
+
+
+static int
+axp81x_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+	struct axp81x_softc *sc;
+	uint8_t data, func;
+	int error;
+
+	if (pin >= nitems(axp81x_pins))
+		return (EINVAL);
+
+	sc = device_get_softc(dev);
+
+	AXP_LOCK(sc);
+	THREAD_SLEEPING_OK();
+	error = axp81x_read(dev, axp81x_pins[pin].ctrl_reg, &data, 1);
+	if (error == 0) {
+		func = (data & AXP_GPIO_FUNC) >> AXP_GPIO_FUNC_SHIFT;
+		switch (func) {
+		case AXP_GPIO_FUNC_DRVLO:
+			data &= ~AXP_GPIO_FUNC;
+			data |= (AXP_GPIO_FUNC_DRVHI << AXP_GPIO_FUNC_SHIFT);
+			break;
+		case AXP_GPIO_FUNC_DRVHI:
+			data &= ~AXP_GPIO_FUNC;
+			data |= (AXP_GPIO_FUNC_DRVLO << AXP_GPIO_FUNC_SHIFT);
+			break;
+		default:
+			error = EIO;
+			break;
+		}
+	}
+	if (error == 0)
+		error = axp81x_write(dev, axp81x_pins[pin].ctrl_reg, data);
+	THREAD_NO_SLEEPING();
+	AXP_UNLOCK(sc);
+
+	return (error);
+}
+
+static int
+axp81x_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent,
+    int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags)
+{
+	if (gpios[0] >= nitems(axp81x_pins))
+		return (EINVAL);
+
+	*pin = gpios[0];
+	*flags = gpios[1];
+
+	return (0);
+}
+
+static phandle_t
+axp81x_get_node(device_t dev, device_t bus)
+{
+	return (ofw_bus_get_node(dev));
+}
+
 static int
 axp81x_probe(device_t dev)
 {
@@ -143,19 +447,45 @@ axp81x_attach(device_t dev)
 {
 	struct axp81x_softc *sc;
 	uint8_t chip_id;
+	int error;
 
 	sc = device_get_softc(dev);
 
 	sc->addr = iicbus_get_addr(dev);
+	mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+	error = bus_alloc_resources(dev, axp81x_spec, &sc->res);
+	if (error != 0) {
+		device_printf(dev, "cannot allocate resources for device\n");
+		return (error);
+	}
 
 	if (bootverbose) {
 		axp81x_read(dev, AXP_ICTYPE, &chip_id, 1);
 		device_printf(dev, "chip ID 0x%02x\n", chip_id);
 	}
 
+	/* Enable IRQ on short power key press */
+	axp81x_write(dev, AXP_IRQEN1, 0);
+	axp81x_write(dev, AXP_IRQEN2, 0);
+	axp81x_write(dev, AXP_IRQEN3, 0);
+	axp81x_write(dev, AXP_IRQEN4, 0);
+	axp81x_write(dev, AXP_IRQEN5, AXP_IRQEN5_POKSIRQ);
+	axp81x_write(dev, AXP_IRQEN6, 0);
+
+	/* Install interrupt handler */
+	error = bus_setup_intr(dev, sc->res, INTR_TYPE_MISC | INTR_MPSAFE,
+	    NULL, axp81x_intr, dev, &sc->ih);
+	if (error != 0) {
+		device_printf(dev, "cannot setup interrupt handler\n");
+		return (error);
+	}
+
 	EVENTHANDLER_REGISTER(shutdown_final, axp81x_shutdown, dev,
 	    SHUTDOWN_PRI_LAST);
 
+	sc->gpiodev = gpiobus_attach_bus(dev);
+
 	return (0);
 }
 
@@ -164,6 +494,21 @@ static device_method_t axp81x_methods[] 
 	DEVMETHOD(device_probe,		axp81x_probe),
 	DEVMETHOD(device_attach,	axp81x_attach),
 
+	/* GPIO interface */
+	DEVMETHOD(gpio_get_bus,		axp81x_gpio_get_bus),
+	DEVMETHOD(gpio_pin_max,		axp81x_gpio_pin_max),
+	DEVMETHOD(gpio_pin_getname,	axp81x_gpio_pin_getname),
+	DEVMETHOD(gpio_pin_getcaps,	axp81x_gpio_pin_getcaps),
+	DEVMETHOD(gpio_pin_getflags,	axp81x_gpio_pin_getflags),
+	DEVMETHOD(gpio_pin_setflags,	axp81x_gpio_pin_setflags),
+	DEVMETHOD(gpio_pin_get,		axp81x_gpio_pin_get),
+	DEVMETHOD(gpio_pin_set,		axp81x_gpio_pin_set),
+	DEVMETHOD(gpio_pin_toggle,	axp81x_gpio_pin_toggle),
+	DEVMETHOD(gpio_map_gpios,	axp81x_gpio_map_gpios),
+
+	/* OFW bus interface */
+	DEVMETHOD(ofw_bus_get_node,	axp81x_get_node),
+
 	DEVMETHOD_END
 };
 
@@ -174,7 +519,12 @@ static driver_t axp81x_driver = {
 };
 
 static devclass_t axp81x_devclass;
+extern devclass_t ofwgpiobus_devclass, gpioc_devclass;
+extern driver_t ofw_gpiobus_driver, gpioc_driver;
 
 DRIVER_MODULE(axp81x, iicbus, axp81x_driver, axp81x_devclass, 0, 0);
+DRIVER_MODULE(ofw_gpiobus, axp81x_pmu, ofw_gpiobus_driver,
+    ofwgpiobus_devclass, 0, 0);
+DRIVER_MODULE(gpioc, axp81x_pmu, gpioc_driver, gpioc_devclass, 0, 0);
 MODULE_VERSION(axp81x, 1);
 MODULE_DEPEND(axp81x, iicbus, 1, 1, 1);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201605151554.u4FFsfh6088526>