Index: head/sys/arm/allwinner/axp209.c =================================================================== --- head/sys/arm/allwinner/axp209.c +++ head/sys/arm/allwinner/axp209.c @@ -1,5 +1,6 @@ /*- - * Copyright (c) 2015 Emmanuel Vadot + * Copyright (c) 2015-2016 Emmanuel Vadot + * Copyright (c) 2016 Jared McNeill * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -46,40 +48,45 @@ #include #include +#include + #include #include #include -#include "iicbus_if.h" - -/* Power State Register */ -#define AXP209_PSR 0x00 -#define AXP209_PSR_ACIN 0x80 -#define AXP209_PSR_ACIN_SHIFT 7 -#define AXP209_PSR_VBUS 0x20 -#define AXP209_PSR_VBUS_SHIFT 5 - -/* Shutdown and battery control */ -#define AXP209_SHUTBAT 0x32 -#define AXP209_SHUTBAT_SHUTDOWN 0x80 - -/* Temperature monitor */ -#define AXP209_TEMPMON 0x5e -#define AXP209_TEMPMON_H(a) ((a) << 4) -#define AXP209_TEMPMON_L(a) ((a) & 0xf) -#define AXP209_TEMPMON_MIN 1447 /* -144.7C */ +#include -#define AXP209_0C_TO_K 2731 +#include "iicbus_if.h" +#include "gpio_if.h" struct axp209_softc { + device_t dev; uint32_t addr; - struct intr_config_hook enum_hook; + struct resource * res[1]; + void * intrcookie; + struct intr_config_hook intr_hook; + device_t gpiodev; + struct mtx mtx; +}; + +/* GPIO3 is different, don't expose it for now */ +static const struct { + const char *name; + uint8_t ctrl_reg; +} axp209_pins[] = { + { "GPIO0", AXP209_GPIO0_CTRL }, + { "GPIO1", AXP209_GPIO1_CTRL }, + { "GPIO2", AXP209_GPIO2_CTRL }, }; -enum axp209_sensor { - AXP209_TEMP +static struct resource_spec axp_res_spec[] = { + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0, 0 } }; +#define AXP_LOCK(sc) mtx_lock(&(sc)->mtx) +#define AXP_UNLOCK(sc) mtx_unlock(&(sc)->mtx) + static int axp209_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) { @@ -125,16 +132,75 @@ uint8_t data[2]; int val, error; - if (sensor != AXP209_TEMP) + switch (sensor) { + case AXP209_TEMP: + error = axp209_read(dev, AXP209_TEMPMON, data, 2); + if (error != 0) + return (error); + + /* Temperature is between -144.7C and 264.8C, step +0.1C */ + val = (AXP209_SENSOR_H(data[0]) | AXP209_SENSOR_L(data[1])) - + AXP209_TEMPMON_MIN + AXP209_0C_TO_K; + break; + case AXP209_ACVOLT: + error = axp209_read(dev, AXP209_ACIN_VOLTAGE, data, 2); + if (error != 0) + return (error); + + val = (AXP209_SENSOR_H(data[0]) | AXP209_SENSOR_L(data[1])) * + AXP209_VOLT_STEP; + break; + case AXP209_ACCURRENT: + error = axp209_read(dev, AXP209_ACIN_CURRENT, data, 2); + if (error != 0) + return (error); + + val = (AXP209_SENSOR_H(data[0]) | AXP209_SENSOR_L(data[1])) * + AXP209_ACCURRENT_STEP; + break; + case AXP209_VBUSVOLT: + error = axp209_read(dev, AXP209_VBUS_VOLTAGE, data, 2); + if (error != 0) + return (error); + + val = (AXP209_SENSOR_H(data[0]) | AXP209_SENSOR_L(data[1])) * + AXP209_VOLT_STEP; + break; + case AXP209_VBUSCURRENT: + error = axp209_read(dev, AXP209_VBUS_CURRENT, data, 2); + if (error != 0) + return (error); + + val = (AXP209_SENSOR_H(data[0]) | AXP209_SENSOR_L(data[1])) * + AXP209_VBUSCURRENT_STEP; + break; + case AXP209_BATVOLT: + error = axp209_read(dev, AXP209_BAT_VOLTAGE, data, 2); + if (error != 0) + return (error); + + val = (AXP209_SENSOR_H(data[0]) | AXP209_SENSOR_L(data[1])) * + AXP209_BATVOLT_STEP; + break; + case AXP209_BATCHARGECURRENT: + error = axp209_read(dev, AXP209_BAT_CHARGE_CURRENT, data, 2); + if (error != 0) + return (error); + + val = (AXP209_SENSOR_H(data[0]) | AXP209_SENSOR_L(data[1])) * + AXP209_BATCURRENT_STEP; + break; + case AXP209_BATDISCHARGECURRENT: + error = axp209_read(dev, AXP209_BAT_DISCHARGE_CURRENT, data, 2); + if (error != 0) + return (error); + + val = (AXP209_SENSOR_BAT_H(data[0]) | + AXP209_SENSOR_BAT_L(data[1])) * AXP209_BATCURRENT_STEP; + break; + default: return (ENOENT); - - error = axp209_read(dev, AXP209_TEMPMON, data, 2); - if (error != 0) - return (error); - - /* Temperature is between -144.7C and 264.8C, step +0.1C */ - val = (AXP209_TEMPMON_H(data[0]) | AXP209_TEMPMON_L(data[1])) - - AXP209_TEMPMON_MIN + AXP209_0C_TO_K; + } return sysctl_handle_opaque(oidp, &val, sizeof(val), req); } @@ -154,32 +220,317 @@ axp209_write(dev, AXP209_SHUTBAT, AXP209_SHUTBAT_SHUTDOWN); } +static void +axp_intr(void *arg) +{ + struct axp209_softc *sc; + uint8_t reg; + + sc = arg; + + axp209_read(sc->dev, AXP209_IRQ1_STATUS, ®, 1); + if (reg) { + if (reg & AXP209_IRQ1_AC_OVERVOLT) + devctl_notify("PMU", "AC", "overvoltage", NULL); + if (reg & AXP209_IRQ1_VBUS_OVERVOLT) + devctl_notify("PMU", "USB", "overvoltage", NULL); + if (reg & AXP209_IRQ1_VBUS_LOW) + devctl_notify("PMU", "USB", "undervoltage", NULL); + if (reg & AXP209_IRQ1_AC_CONN) + devctl_notify("PMU", "AC", "plugged", NULL); + if (reg & AXP209_IRQ1_AC_DISCONN) + devctl_notify("PMU", "AC", "unplugged", NULL); + if (reg & AXP209_IRQ1_VBUS_CONN) + devctl_notify("PMU", "USB", "plugged", NULL); + if (reg & AXP209_IRQ1_VBUS_DISCONN) + devctl_notify("PMU", "USB", "unplugged", NULL); + axp209_write(sc->dev, AXP209_IRQ1_STATUS, AXP209_IRQ_ACK); + } + + axp209_read(sc->dev, AXP209_IRQ2_STATUS, ®, 1); + if (reg) { + if (reg & AXP209_IRQ2_BATT_CHARGED) + devctl_notify("PMU", "Battery", "charged", NULL); + if (reg & AXP209_IRQ2_BATT_CHARGING) + devctl_notify("PMU", "Battery", "charging", NULL); + if (reg & AXP209_IRQ2_BATT_CONN) + devctl_notify("PMU", "Battery", "connected", NULL); + if (reg & AXP209_IRQ2_BATT_DISCONN) + devctl_notify("PMU", "Battery", "disconnected", NULL); + if (reg & AXP209_IRQ2_BATT_TEMP_LOW) + devctl_notify("PMU", "Battery", "low temp", NULL); + if (reg & AXP209_IRQ2_BATT_TEMP_OVER) + devctl_notify("PMU", "Battery", "high temp", NULL); + axp209_write(sc->dev, AXP209_IRQ2_STATUS, AXP209_IRQ_ACK); + } + + axp209_read(sc->dev, AXP209_IRQ3_STATUS, ®, 1); + if (reg) { + if (reg & AXP209_IRQ3_PEK_SHORT) + shutdown_nice(RB_POWEROFF); + axp209_write(sc->dev, AXP209_IRQ3_STATUS, AXP209_IRQ_ACK); + } + + axp209_read(sc->dev, AXP209_IRQ4_STATUS, ®, 1); + if (reg) { + axp209_write(sc->dev, AXP209_IRQ4_STATUS, AXP209_IRQ_ACK); + } + + axp209_read(sc->dev, AXP209_IRQ5_STATUS, ®, 1); + if (reg) { + axp209_write(sc->dev, AXP209_IRQ5_STATUS, AXP209_IRQ_ACK); + } +} + +static device_t +axp209_gpio_get_bus(device_t dev) +{ + struct axp209_softc *sc; + + sc = device_get_softc(dev); + + return (sc->gpiodev); +} + static int -axp209_probe(device_t dev) +axp209_gpio_pin_max(device_t dev, int *maxpin) { + *maxpin = nitems(axp209_pins) - 1; - if (!ofw_bus_status_okay(dev)) - return (ENXIO); + return (0); +} - if (!ofw_bus_is_compatible(dev, "x-powers,axp209")) - return (ENXIO); +static int +axp209_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + if (pin >= nitems(axp209_pins)) + return (EINVAL); - device_set_desc(dev, "X-Power AXP209 Power Management Unit"); + snprintf(name, GPIOMAXNAME, "%s", axp209_pins[pin].name); - return (BUS_PROBE_DEFAULT); + return (0); } static int -axp209_attach(device_t dev) +axp209_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + if (pin >= nitems(axp209_pins)) + return (EINVAL); + + *caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT; + + return (0); +} + +static int +axp209_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct axp209_softc *sc; + uint8_t data, func; + int error; + + if (pin >= nitems(axp209_pins)) + return (EINVAL); + + sc = device_get_softc(dev); + + AXP_LOCK(sc); + error = axp209_read(dev, axp209_pins[pin].ctrl_reg, &data, 1); + if (error == 0) { + func = data & AXP209_GPIO_FUNC_MASK; + if (func == AXP209_GPIO_FUNC_INPUT) + *flags = GPIO_PIN_INPUT; + else if (func == AXP209_GPIO_FUNC_DRVLO || + func == AXP209_GPIO_FUNC_DRVHI) + *flags = GPIO_PIN_OUTPUT; + else + *flags = 0; + } + AXP_UNLOCK(sc); + + return (error); +} + +static int +axp209_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct axp209_softc *sc; + uint8_t data; + int error; + + if (pin >= nitems(axp209_pins)) + return (EINVAL); + + sc = device_get_softc(dev); + + AXP_LOCK(sc); + error = axp209_read(dev, axp209_pins[pin].ctrl_reg, &data, 1); + if (error == 0) { + data &= ~AXP209_GPIO_FUNC_MASK; + if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) != 0) { + if ((flags & GPIO_PIN_OUTPUT) == 0) + data |= AXP209_GPIO_FUNC_INPUT; + } + error = axp209_write(dev, axp209_pins[pin].ctrl_reg, data); + } + AXP_UNLOCK(sc); + + return (error); +} + +static int +axp209_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) +{ + struct axp209_softc *sc; + uint8_t data, func; + int error; + + if (pin >= nitems(axp209_pins)) + return (EINVAL); + + sc = device_get_softc(dev); + + AXP_LOCK(sc); + error = axp209_read(dev, axp209_pins[pin].ctrl_reg, &data, 1); + if (error == 0) { + func = data & AXP209_GPIO_FUNC_MASK; + switch (func) { + case AXP209_GPIO_FUNC_DRVLO: + *val = 0; + break; + case AXP209_GPIO_FUNC_DRVHI: + *val = 1; + break; + case AXP209_GPIO_FUNC_INPUT: + error = axp209_read(dev, AXP209_GPIO_STATUS, &data, 1); + if (error == 0) + *val = (data & AXP209_GPIO_DATA(pin)) ? 1 : 0; + break; + default: + error = EIO; + break; + } + } + AXP_UNLOCK(sc); + + return (error); +} + +static int +axp209_gpio_pin_set(device_t dev, uint32_t pin, unsigned int val) +{ + struct axp209_softc *sc; + uint8_t data, func; + int error; + + if (pin >= nitems(axp209_pins)) + return (EINVAL); + + sc = device_get_softc(dev); + + AXP_LOCK(sc); + error = axp209_read(dev, axp209_pins[pin].ctrl_reg, &data, 1); + if (error == 0) { + func = data & AXP209_GPIO_FUNC_MASK; + switch (func) { + case AXP209_GPIO_FUNC_DRVLO: + case AXP209_GPIO_FUNC_DRVHI: + /* GPIO2 can't be set to 1 */ + if (pin == 2 && val == 1) { + error = EINVAL; + break; + } + data &= ~AXP209_GPIO_FUNC_MASK; + data |= val; + break; + default: + error = EIO; + break; + } + } + if (error == 0) + error = axp209_write(dev, axp209_pins[pin].ctrl_reg, data); + AXP_UNLOCK(sc); + + return (error); +} + + +static int +axp209_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct axp209_softc *sc; + uint8_t data, func; + int error; + + if (pin >= nitems(axp209_pins)) + return (EINVAL); + + sc = device_get_softc(dev); + + AXP_LOCK(sc); + error = axp209_read(dev, axp209_pins[pin].ctrl_reg, &data, 1); + if (error == 0) { + func = data & AXP209_GPIO_FUNC_MASK; + switch (func) { + case AXP209_GPIO_FUNC_DRVLO: + /* Pin 2 can't be set to 1*/ + if (pin == 2) { + error = EINVAL; + break; + } + data &= ~AXP209_GPIO_FUNC_MASK; + data |= AXP209_GPIO_FUNC_DRVHI; + break; + case AXP209_GPIO_FUNC_DRVHI: + data &= ~AXP209_GPIO_FUNC_MASK; + data |= AXP209_GPIO_FUNC_DRVLO; + break; + default: + error = EIO; + break; + } + } + if (error == 0) + error = axp209_write(dev, axp209_pins[pin].ctrl_reg, data); + AXP_UNLOCK(sc); + + return (error); +} + +static int +axp209_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(axp209_pins)) + return (EINVAL); + + *pin = gpios[0]; + *flags = gpios[1]; + + return (0); +} + +static phandle_t +axp209_get_node(device_t dev, device_t bus) +{ + return (ofw_bus_get_node(dev)); +} + +static void +axp209_start(void *pdev) +{ + device_t dev; + struct axp209_softc *sc; const char *pwr_name[] = {"Battery", "AC", "USB", "AC and USB"}; uint8_t data; uint8_t pwr_src; - sc = device_get_softc(dev); + dev = pdev; + sc = device_get_softc(dev); sc->addr = iicbus_get_addr(dev); + sc->dev = dev; if (bootverbose) { /* @@ -195,22 +546,154 @@ pwr_name[pwr_src]); } + /* Only enable interrupts that we are interested in */ + axp209_write(dev, AXP209_IRQ1_ENABLE, + AXP209_IRQ1_AC_OVERVOLT | + AXP209_IRQ1_AC_DISCONN | + AXP209_IRQ1_AC_CONN | + AXP209_IRQ1_VBUS_OVERVOLT | + AXP209_IRQ1_VBUS_DISCONN | + AXP209_IRQ1_VBUS_CONN); + axp209_write(dev, AXP209_IRQ2_ENABLE, + AXP209_IRQ2_BATT_CONN | + AXP209_IRQ2_BATT_DISCONN | + AXP209_IRQ2_BATT_CHARGE_ACCT_ON | + AXP209_IRQ2_BATT_CHARGE_ACCT_OFF | + AXP209_IRQ2_BATT_CHARGING | + AXP209_IRQ2_BATT_CHARGED | + AXP209_IRQ2_BATT_TEMP_OVER | + AXP209_IRQ2_BATT_TEMP_LOW); + axp209_write(dev, AXP209_IRQ3_ENABLE, + AXP209_IRQ3_PEK_SHORT | AXP209_IRQ3_PEK_LONG); + axp209_write(dev, AXP209_IRQ4_ENABLE, AXP209_IRQ4_APS_LOW_2); + axp209_write(dev, AXP209_IRQ5_ENABLE, 0x0); + EVENTHANDLER_REGISTER(shutdown_final, axp209_shutdown, dev, SHUTDOWN_PRI_LAST); + /* Enable ADC sensors */ + if (axp209_write(dev, AXP209_ADC_ENABLE1, + AXP209_ADC1_BATVOLT | AXP209_ADC1_BATCURRENT | + AXP209_ADC1_ACVOLT | AXP209_ADC1_ACCURRENT | + AXP209_ADC1_VBUSVOLT | AXP209_ADC1_VBUSCURRENT) != -1) { + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "acvolt", + CTLTYPE_INT | CTLFLAG_RD, + dev, AXP209_ACVOLT, axp209_sysctl, "I", + "AC Voltage (microVolt)"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "accurrent", + CTLTYPE_INT | CTLFLAG_RD, + dev, AXP209_ACCURRENT, axp209_sysctl, "I", + "AC Current (microAmpere)"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "vbusvolt", + CTLTYPE_INT | CTLFLAG_RD, + dev, AXP209_VBUSVOLT, axp209_sysctl, "I", + "VBUS Voltage (microVolt)"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "vbuscurrent", + CTLTYPE_INT | CTLFLAG_RD, + dev, AXP209_VBUSCURRENT, axp209_sysctl, "I", + "VBUS Current (microAmpere)"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "batvolt", + CTLTYPE_INT | CTLFLAG_RD, + dev, AXP209_BATVOLT, axp209_sysctl, "I", + "Battery Voltage (microVolt)"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "batchargecurrent", + CTLTYPE_INT | CTLFLAG_RD, + dev, AXP209_BATCHARGECURRENT, axp209_sysctl, "I", + "Battery Charging Current (microAmpere)"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "batdischargecurrent", + CTLTYPE_INT | CTLFLAG_RD, + dev, AXP209_BATDISCHARGECURRENT, axp209_sysctl, "I", + "Battery Discharging Current (microAmpere)"); + } else { + device_printf(dev, "Couldn't enable ADC sensors\n"); + } + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "temp", CTLTYPE_INT | CTLFLAG_RD, dev, AXP209_TEMP, axp209_sysctl, "IK", "Internal temperature"); + if ((bus_setup_intr(dev, sc->res[0], INTR_TYPE_MISC | INTR_MPSAFE, + NULL, axp_intr, sc, &sc->intrcookie))) + device_printf(dev, "unable to register interrupt handler\n"); + + config_intrhook_disestablish(&sc->intr_hook); +} + +static int +axp209_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "x-powers,axp209")) + return (ENXIO); + + device_set_desc(dev, "X-Powers AXP209 Power Management Unit"); + + return (BUS_PROBE_DEFAULT); +} + +static int +axp209_attach(device_t dev) +{ + struct axp209_softc *sc; + + sc = device_get_softc(dev); + mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + if (bus_alloc_resources(dev, axp_res_spec, sc->res) != 0) { + device_printf(dev, "can't allocate device resources\n"); + return (ENXIO); + } + + sc->intr_hook.ich_func = axp209_start; + sc->intr_hook.ich_arg = dev; + + if (config_intrhook_establish(&sc->intr_hook) != 0) + return (ENOMEM); + + sc->gpiodev = gpiobus_attach_bus(dev); + return (0); } static device_method_t axp209_methods[] = { DEVMETHOD(device_probe, axp209_probe), DEVMETHOD(device_attach, axp209_attach), - {0, 0}, + + /* GPIO interface */ + DEVMETHOD(gpio_get_bus, axp209_gpio_get_bus), + DEVMETHOD(gpio_pin_max, axp209_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, axp209_gpio_pin_getname), + DEVMETHOD(gpio_pin_getcaps, axp209_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_getflags, axp209_gpio_pin_getflags), + DEVMETHOD(gpio_pin_setflags, axp209_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, axp209_gpio_pin_get), + DEVMETHOD(gpio_pin_set, axp209_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, axp209_gpio_pin_toggle), + DEVMETHOD(gpio_map_gpios, axp209_gpio_map_gpios), + + /* OFW bus interface */ + DEVMETHOD(ofw_bus_get_node, axp209_get_node), + + DEVMETHOD_END }; static driver_t axp209_driver = { @@ -220,7 +703,14 @@ }; static devclass_t axp209_devclass; +extern devclass_t ofwgpiobus_devclass, gpioc_devclass; +extern driver_t ofw_gpiobus_driver, gpioc_driver; -DRIVER_MODULE(axp209, iicbus, axp209_driver, axp209_devclass, 0, 0); +EARLY_DRIVER_MODULE(axp209, iicbus, axp209_driver, axp209_devclass, + 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE); +EARLY_DRIVER_MODULE(ofw_gpiobus, axp209_pmu, ofw_gpiobus_driver, + ofwgpiobus_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE); +EARLY_DRIVER_MODULE(gpioc, axp209_pmu, gpioc_driver, gpioc_devclass, + 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE); MODULE_VERSION(axp209, 1); MODULE_DEPEND(axp209, iicbus, 1, 1, 1); Index: head/sys/arm/allwinner/axp209reg.h =================================================================== --- head/sys/arm/allwinner/axp209reg.h +++ head/sys/arm/allwinner/axp209reg.h @@ -0,0 +1,160 @@ +/*- + * Copyright (c) 2016 Emmanuel Vadot + * 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. + * + * $FreeBSD$ + */ + +#ifndef _AXP209REG_H_ +#define _AXP209REG_H_ + +/* Power State Register */ +#define AXP209_PSR 0x00 +#define AXP209_PSR_ACIN 0x80 +#define AXP209_PSR_ACIN_SHIFT 7 +#define AXP209_PSR_VBUS 0x20 +#define AXP209_PSR_VBUS_SHIFT 5 + +/* Shutdown and battery control */ +#define AXP209_SHUTBAT 0x32 +#define AXP209_SHUTBAT_SHUTDOWN 0x80 + +/* Voltage/Current Monitor */ +#define AXP209_ACIN_VOLTAGE 0x56 +#define AXP209_ACIN_CURRENT 0x58 +#define AXP209_VBUS_VOLTAGE 0x5A +#define AXP209_VBUS_CURRENT 0x5C +#define AXP209_BAT_VOLTAGE 0x78 +#define AXP209_BAT_CHARGE_CURRENT 0x7A +#define AXP209_BAT_DISCHARGE_CURRENT 0x7C + +#define AXP209_VOLT_STEP 1700 +#define AXP209_BATVOLT_STEP 1100 +#define AXP209_ACCURRENT_STEP 625 +#define AXP209_VBUSCURRENT_STEP 375 +#define AXP209_BATCURRENT_STEP 500 + +/* Temperature monitor */ +#define AXP209_TEMPMON 0x5e +#define AXP209_TEMPMON_MIN 1447 /* -144.7C */ + +/* Sensors conversion macros */ +#define AXP209_SENSOR_H(a) ((a) << 4) +#define AXP209_SENSOR_L(a) ((a) & 0xf) +#define AXP209_SENSOR_BAT_H(a) ((a) << 5) +#define AXP209_SENSOR_BAT_L(a) ((a) & 0x1f) + +#define AXP209_0C_TO_K 2732 + +/* ADC Sensors */ +#define AXP209_ADC_ENABLE1 0x82 +#define AXP209_ADC_ENABLE2 0x83 + +#define AXP209_ADC1_BATVOLT (1 << 7) +#define AXP209_ADC1_BATCURRENT (1 << 6) +#define AXP209_ADC1_ACVOLT (1 << 5) +#define AXP209_ADC1_ACCURRENT (1 << 4) +#define AXP209_ADC1_VBUSVOLT (1 << 3) +#define AXP209_ADC1_VBUSCURRENT (1 << 2) + +/* Interrupt related registers */ +#define AXP209_IRQ1_ENABLE 0x40 +#define AXP209_IRQ1_STATUS 0x48 +#define AXP209_IRQ1_AC_OVERVOLT (1 << 7) +#define AXP209_IRQ1_AC_CONN (1 << 6) +#define AXP209_IRQ1_AC_DISCONN (1 << 5) +#define AXP209_IRQ1_VBUS_OVERVOLT (1 << 4) +#define AXP209_IRQ1_VBUS_CONN (1 << 3) +#define AXP209_IRQ1_VBUS_DISCONN (1 << 2) +#define AXP209_IRQ1_VBUS_LOW (1 << 1) + +#define AXP209_IRQ2_ENABLE 0x41 +#define AXP209_IRQ2_STATUS 0x49 +#define AXP209_IRQ2_BATT_CONN (1 << 7) +#define AXP209_IRQ2_BATT_DISCONN (1 << 6) +#define AXP209_IRQ2_BATT_CHARGE_ACCT_ON (1 << 5) +#define AXP209_IRQ2_BATT_CHARGE_ACCT_OFF (1 << 4) +#define AXP209_IRQ2_BATT_CHARGING (1 << 3) +#define AXP209_IRQ2_BATT_CHARGED (1 << 2) +#define AXP209_IRQ2_BATT_TEMP_OVER (1 << 1) +#define AXP209_IRQ2_BATT_TEMP_LOW (1 << 0) + +#define AXP209_IRQ3_ENABLE 0x42 +#define AXP209_IRQ3_STATUS 0x4A +#define AXP209_IRQ3_TEMP_OVER (1 << 7) +#define AXP209_IRQ3_CHARGE_CURRENT_LOW (1 << 6) +#define AXP209_IRQ3_DCDC2_LOW (1 << 4) +#define AXP209_IRQ3_DCDC3_LOW (1 << 3) +#define AXP209_IRQ3_LDO3_LOW (1 << 2) +#define AXP209_IRQ3_PEK_SHORT (1 << 1) +#define AXP209_IRQ3_PEK_LONG (1 << 0) + +#define AXP209_IRQ4_ENABLE 0x43 +#define AXP209_IRQ4_STATUS 0x4B +#define AXP209_IRQ4_NOE_START (1 << 7) +#define AXP209_IRQ4_NOE_SHUT (1 << 6) +#define AXP209_IRQ4_VBUS_VALID (1 << 5) +#define AXP209_IRQ4_VBUS_INVALID (1 << 4) +#define AXP209_IRQ4_VBUS_SESSION (1 << 3) +#define AXP209_IRQ4_VBUS_SESSION_END (1 << 2) +#define AXP209_IRQ4_APS_LOW_1 (1 << 1) +#define AXP209_IRQ4_APS_LOW_2 (1 << 0) + +#define AXP209_IRQ5_ENABLE 0x44 +#define AXP209_IRQ5_STATUS 0x4C +#define AXP209_IRQ5_TIMER_EXPIRE (1 << 7) +#define AXP209_IRQ5_PEK_RISE_EDGE (1 << 6) +#define AXP209_IRQ5_PEK_FALL_EDGE (1 << 5) +#define AXP209_IRQ5_GPIO3 (1 << 3) +#define AXP209_IRQ5_GPIO2 (1 << 2) +#define AXP209_IRQ5_GPIO1 (1 << 1) +#define AXP209_IRQ5_GPIO0 (1 << 0) + +#define AXP209_IRQ_ACK 0xff + +/* GPIOs registers */ +#define AXP209_GPIO_FUNC_MASK 0x7 + +#define AXP209_GPIO_FUNC_DRVLO 0x0 +#define AXP209_GPIO_FUNC_DRVHI 0x1 +#define AXP209_GPIO_FUNC_INPUT 0x2 + +#define AXP209_GPIO0_CTRL 0x90 +#define AXP209_GPIO1_CTRL 0x92 +#define AXP209_GPIO2_CTRL 0x93 +#define AXP209_GPIO_STATUS 0x94 + +#define AXP209_GPIO_DATA(x) (1 << (x + 4)) + +enum axp209_sensor { + AXP209_TEMP, + AXP209_ACVOLT, + AXP209_ACCURRENT, + AXP209_VBUSVOLT, + AXP209_VBUSCURRENT, + AXP209_BATVOLT, + AXP209_BATCHARGECURRENT, + AXP209_BATDISCHARGECURRENT, +}; + +#endif /* _AXP209REG_H_ */ Index: head/sys/boot/fdt/dts/arm/bananapi.dts =================================================================== --- head/sys/boot/fdt/dts/arm/bananapi.dts +++ head/sys/boot/fdt/dts/arm/bananapi.dts @@ -28,6 +28,7 @@ #include "sun7i-a20-bananapi.dts" #include "sun7i-a20-hdmi.dtsi" +#include "xpowers-axp209.dtsi" / { soc@01c00000 { Index: head/sys/boot/fdt/dts/arm/cubieboard.dts =================================================================== --- head/sys/boot/fdt/dts/arm/cubieboard.dts +++ head/sys/boot/fdt/dts/arm/cubieboard.dts @@ -27,3 +27,4 @@ */ #include "sun4i-a10-cubieboard.dts" +#include "xpowers-axp209.dtsi" Index: head/sys/boot/fdt/dts/arm/cubieboard2.dts =================================================================== --- head/sys/boot/fdt/dts/arm/cubieboard2.dts +++ head/sys/boot/fdt/dts/arm/cubieboard2.dts @@ -29,6 +29,7 @@ #include "sun7i-a20-cubieboard2.dts" #include "sun7i-a20-hdmi.dtsi" +#include "xpowers-axp209.dtsi" / { soc@01c00000 { Index: head/sys/boot/fdt/dts/arm/olimex-a20-som-evb.dts =================================================================== --- head/sys/boot/fdt/dts/arm/olimex-a20-som-evb.dts +++ head/sys/boot/fdt/dts/arm/olimex-a20-som-evb.dts @@ -28,6 +28,7 @@ #include "sun7i-a20-olimex-som-evb.dts" #include "sun7i-a20-hdmi.dtsi" +#include "xpowers-axp209.dtsi" / { soc@01c00000 { Index: head/sys/boot/fdt/dts/arm/olinuxino-lime.dts =================================================================== --- head/sys/boot/fdt/dts/arm/olinuxino-lime.dts +++ head/sys/boot/fdt/dts/arm/olinuxino-lime.dts @@ -27,3 +27,4 @@ */ #include "sun4i-a10-olinuxino-lime.dts" +#include "xpowers-axp209.dtsi" Index: head/sys/boot/fdt/dts/arm/xpowers-axp209.dtsi =================================================================== --- head/sys/boot/fdt/dts/arm/xpowers-axp209.dtsi +++ head/sys/boot/fdt/dts/arm/xpowers-axp209.dtsi @@ -0,0 +1,33 @@ +/*- + * Copyright (c) 2016 Emmanuel Vadot + * 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 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$ + */ + + +&axp209 { + gpio-controller; + #gpio-cells = <1>; +}; Index: head/sys/dev/iicbus/twsi/a10_twsi.c =================================================================== --- head/sys/dev/iicbus/twsi/a10_twsi.c +++ head/sys/dev/iicbus/twsi/a10_twsi.c @@ -153,6 +153,8 @@ static devclass_t a10_twsi_devclass; -DRIVER_MODULE(a10_twsi, simplebus, a10_twsi_driver, a10_twsi_devclass, 0, 0); -DRIVER_MODULE(iicbus, a10_twsi, iicbus_driver, iicbus_devclass, 0, 0); +EARLY_DRIVER_MODULE(a10_twsi, simplebus, a10_twsi_driver, a10_twsi_devclass, + 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +EARLY_DRIVER_MODULE(iicbus, a10_twsi, iicbus_driver, iicbus_devclass, + 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); MODULE_DEPEND(a10_twsi, iicbus, 1, 1, 1);