From owner-svn-src-all@freebsd.org Sun May 15 15:54:43 2016 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 1DE86B3C896; Sun, 15 May 2016 15:54:43 +0000 (UTC) (envelope-from jmcneill@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id D03F11787; Sun, 15 May 2016 15:54:42 +0000 (UTC) (envelope-from jmcneill@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u4FFsfCf088527; Sun, 15 May 2016 15:54:41 GMT (envelope-from jmcneill@FreeBSD.org) Received: (from jmcneill@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u4FFsfh6088526; Sun, 15 May 2016 15:54:41 GMT (envelope-from jmcneill@FreeBSD.org) Message-Id: <201605151554.u4FFsfh6088526@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: jmcneill set sender to jmcneill@FreeBSD.org using -f From: Jared McNeill Date: Sun, 15 May 2016 15:54:41 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r299860 - head/sys/arm/allwinner X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.22 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: Sun, 15 May 2016 15:54:43 -0000 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 #include #include +#include +#include #include #include #include #include +#include + #include #include #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);