Index: head/sys/arm/allwinner/aw_rsb.c =================================================================== --- head/sys/arm/allwinner/aw_rsb.c (revision 299861) +++ head/sys/arm/allwinner/aw_rsb.c (revision 299862) @@ -1,519 +1,477 @@ /*- * Copyright (c) 2016 Jared McNeill * 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$ */ /* * Allwinner RSB (Reduced Serial Bus) */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #include "iicbus_if.h" #define RSB_CTRL 0x00 #define START_TRANS (1 << 7) #define GLOBAL_INT_ENB (1 << 1) #define SOFT_RESET (1 << 0) #define RSB_CCR 0x04 #define RSB_INTE 0x08 #define RSB_INTS 0x0c #define INT_TRANS_ERR_ID(x) (((x) >> 8) & 0xf) #define INT_LOAD_BSY (1 << 2) #define INT_TRANS_ERR (1 << 1) #define INT_TRANS_OVER (1 << 0) #define INT_MASK (INT_LOAD_BSY|INT_TRANS_ERR|INT_TRANS_OVER) #define RSB_DADDR0 0x10 #define RSB_DADDR1 0x14 #define RSB_DLEN 0x18 #define DLEN_READ (1 << 4) #define RSB_DATA0 0x1c #define RSB_DATA1 0x20 #define RSB_CMD 0x2c #define CMD_SRTA 0xe8 #define CMD_RD8 0x8b #define CMD_RD16 0x9c #define CMD_RD32 0xa6 #define CMD_WR8 0x4e #define CMD_WR16 0x59 #define CMD_WR32 0x63 #define RSB_DAR 0x30 #define DAR_RTA (0xff << 16) #define DAR_RTA_SHIFT 16 #define DAR_DA (0xffff << 0) #define DAR_DA_SHIFT 0 #define RSB_MAXLEN 8 #define RSB_RESET_RETRY 100 #define RSB_I2C_TIMEOUT hz #define RSB_ADDR_PMIC_PRIMARY 0x3a3 #define RSB_ADDR_PMIC_SECONDARY 0x745 #define RSB_ADDR_PERIPH_IC 0xe89 static struct ofw_compat_data compat_data[] = { { "allwinner,sun8i-a23-rsb", 1 }, { NULL, 0 } }; static struct resource_spec rsb_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, - { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; /* * Device address to Run-time address mappings. * * Run-time address (RTA) is an 8-bit value used to address the device during * a read or write transaction. The following are valid RTAs: * 0x17 0x2d 0x3a 0x4e 0x59 0x63 0x74 0x8b 0x9c 0xa6 0xb1 0xc5 0xd2 0xe8 0xff * * Allwinner uses RTA 0x2d for the primary PMIC, 0x3a for the secondary PMIC, * and 0x4e for the peripheral IC (where applicable). */ static const struct { uint16_t addr; uint8_t rta; } rsb_rtamap[] = { { .addr = RSB_ADDR_PMIC_PRIMARY, .rta = 0x2d }, { .addr = RSB_ADDR_PMIC_SECONDARY, .rta = 0x3a }, { .addr = RSB_ADDR_PERIPH_IC, .rta = 0x4e }, { .addr = 0, .rta = 0 } }; struct rsb_softc { - struct resource *res[2]; + struct resource *res; struct mtx mtx; clk_t clk; hwreset_t rst; device_t iicbus; - void *ih; int busy; uint32_t status; uint16_t cur_addr; struct iic_msg *msg; }; #define RSB_LOCK(sc) mtx_lock(&(sc)->mtx) #define RSB_UNLOCK(sc) mtx_unlock(&(sc)->mtx) #define RSB_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED) -#define RSB_READ(sc, reg) bus_read_4((sc)->res[0], (reg)) -#define RSB_WRITE(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) +#define RSB_READ(sc, reg) bus_read_4((sc)->res, (reg)) +#define RSB_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) static phandle_t rsb_get_node(device_t bus, device_t dev) { return (ofw_bus_get_node(bus)); } static int rsb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) { struct rsb_softc *sc; int retry; sc = device_get_softc(dev); RSB_LOCK(sc); /* Write soft-reset bit and wait for it to self-clear. */ RSB_WRITE(sc, RSB_CTRL, SOFT_RESET); for (retry = RSB_RESET_RETRY; retry > 0; retry--) if ((RSB_READ(sc, RSB_CTRL) & SOFT_RESET) == 0) break; RSB_UNLOCK(sc); if (retry == 0) { device_printf(dev, "soft reset timeout\n"); return (ETIMEDOUT); } return (IIC_ENOADDR); } static uint32_t rsb_encode(const uint8_t *buf, u_int len, u_int off) { uint32_t val; u_int n; val = 0; for (n = off; n < MIN(len, 4 + off); n++) val |= ((uint32_t)buf[n] << ((n - off) * NBBY)); return val; } static void rsb_decode(const uint32_t val, uint8_t *buf, u_int len, u_int off) { u_int n; for (n = off; n < MIN(len, 4 + off); n++) buf[n] = (val >> ((n - off) * NBBY)) & 0xff; } static int rsb_start(device_t dev) { struct rsb_softc *sc; - int error, retry, polling; + int error, retry; sc = device_get_softc(dev); - polling = cold || !THREAD_CAN_SLEEP(); RSB_ASSERT_LOCKED(sc); - /* Enable interrupts */ - if (!polling) - RSB_WRITE(sc, RSB_INTE, INT_MASK); - /* Start the transfer */ RSB_WRITE(sc, RSB_CTRL, GLOBAL_INT_ENB | START_TRANS); /* Wait for transfer to complete */ - if (polling) { - error = ETIMEDOUT; - for (retry = RSB_I2C_TIMEOUT; retry > 0; retry--) { - sc->status |= RSB_READ(sc, RSB_INTS); - if ((sc->status & INT_TRANS_OVER) != 0) { - error = 0; - break; - } - DELAY((1000 * hz) / RSB_I2C_TIMEOUT); + error = ETIMEDOUT; + for (retry = RSB_I2C_TIMEOUT; retry > 0; retry--) { + sc->status |= RSB_READ(sc, RSB_INTS); + if ((sc->status & INT_TRANS_OVER) != 0) { + error = 0; + break; } - } else { - error = mtx_sleep(sc, &sc->mtx, 0, "i2ciowait", - RSB_I2C_TIMEOUT); + DELAY((1000 * hz) / RSB_I2C_TIMEOUT); } if (error == 0 && (sc->status & INT_TRANS_OVER) == 0) { device_printf(dev, "transfer error, status 0x%08x\n", sc->status); error = EIO; } - /* Disable interrupts */ - RSB_WRITE(sc, RSB_INTE, 0); - return (error); } static int rsb_set_rta(device_t dev, uint16_t addr) { struct rsb_softc *sc; uint8_t rta; int i; sc = device_get_softc(dev); RSB_ASSERT_LOCKED(sc); /* Lookup run-time address for given device address */ for (rta = 0, i = 0; rsb_rtamap[i].rta != 0; i++) if (rsb_rtamap[i].addr == addr) { rta = rsb_rtamap[i].rta; break; } if (rta == 0) { device_printf(dev, "RTA not known for address %#x\n", addr); return (ENXIO); } /* Set run-time address */ RSB_WRITE(sc, RSB_INTS, RSB_READ(sc, RSB_INTS)); RSB_WRITE(sc, RSB_DAR, (addr << DAR_DA_SHIFT) | (rta << DAR_RTA_SHIFT)); RSB_WRITE(sc, RSB_CMD, CMD_SRTA); return (rsb_start(dev)); } static int rsb_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { struct rsb_softc *sc; uint32_t daddr[2], data[2], dlen; uint16_t device_addr; uint8_t cmd; int error; sc = device_get_softc(dev); /* * RSB is not really an I2C or SMBus controller, so there are some * restrictions imposed by the driver. * * Transfers must contain exactly two messages. The first is always * a write, containing a single data byte offset. Data will either * be read from or written to the corresponding data byte in the * second message. The slave address in both messages must be the * same. */ if (nmsgs != 2 || (msgs[0].flags & IIC_M_RD) == IIC_M_RD || (msgs[0].slave >> 1) != (msgs[1].slave >> 1) || msgs[0].len != 1 || msgs[1].len > RSB_MAXLEN) return (EINVAL); /* The controller can read or write 1, 2, or 4 bytes at a time. */ if ((msgs[1].flags & IIC_M_RD) != 0) { switch (msgs[1].len) { case 1: cmd = CMD_RD8; break; case 2: cmd = CMD_RD16; break; case 4: cmd = CMD_RD32; break; default: return (EINVAL); } } else { switch (msgs[1].len) { case 1: cmd = CMD_WR8; break; case 2: cmd = CMD_WR16; break; case 4: cmd = CMD_WR32; break; default: return (EINVAL); } } RSB_LOCK(sc); while (sc->busy) mtx_sleep(sc, &sc->mtx, 0, "i2cbuswait", 0); sc->busy = 1; sc->status = 0; /* Select current run-time address if necessary */ device_addr = msgs[0].slave >> 1; if (sc->cur_addr != device_addr) { error = rsb_set_rta(dev, device_addr); if (error != 0) goto done; sc->cur_addr = device_addr; sc->status = 0; } /* Clear interrupt status */ RSB_WRITE(sc, RSB_INTS, RSB_READ(sc, RSB_INTS)); /* Program data access address registers */ daddr[0] = rsb_encode(msgs[0].buf, msgs[0].len, 0); RSB_WRITE(sc, RSB_DADDR0, daddr[0]); /* Write data */ if ((msgs[1].flags & IIC_M_RD) == 0) { data[0] = rsb_encode(msgs[1].buf, msgs[1].len, 0); RSB_WRITE(sc, RSB_DATA0, data[0]); } /* Set command type */ RSB_WRITE(sc, RSB_CMD, cmd); /* Program data length register and transfer direction */ dlen = msgs[0].len - 1; if ((msgs[1].flags & IIC_M_RD) == IIC_M_RD) dlen |= DLEN_READ; RSB_WRITE(sc, RSB_DLEN, dlen); /* Start transfer */ error = rsb_start(dev); if (error != 0) goto done; /* Read data */ if ((msgs[1].flags & IIC_M_RD) == IIC_M_RD) { data[0] = RSB_READ(sc, RSB_DATA0); rsb_decode(data[0], msgs[1].buf, msgs[1].len, 0); } done: sc->msg = NULL; sc->busy = 0; wakeup(sc); RSB_UNLOCK(sc); return (error); } -static void -rsb_intr(void *arg) -{ - struct rsb_softc *sc; - uint32_t val; - - sc = arg; - - RSB_LOCK(sc); - val = RSB_READ(sc, RSB_INTS); - RSB_WRITE(sc, RSB_INTS, val); - sc->status |= val; - if ((sc->status & INT_MASK) != 0) - wakeup(sc); - RSB_UNLOCK(sc); -} - static int rsb_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Allwinner RSB"); return (BUS_PROBE_DEFAULT); } static int rsb_attach(device_t dev) { struct rsb_softc *sc; int error; sc = device_get_softc(dev); mtx_init(&sc->mtx, device_get_nameunit(dev), "rsb", MTX_DEF); if (clk_get_by_ofw_index(dev, 0, &sc->clk) == 0) { error = clk_enable(sc->clk); if (error != 0) { device_printf(dev, "cannot enable clock\n"); goto fail; } } if (hwreset_get_by_ofw_idx(dev, 0, &sc->rst) == 0) { error = hwreset_deassert(sc->rst); if (error != 0) { device_printf(dev, "cannot de-assert reset\n"); goto fail; } } - if (bus_alloc_resources(dev, rsb_spec, sc->res) != 0) { + if (bus_alloc_resources(dev, rsb_spec, &sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); error = ENXIO; goto fail; } - error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, - NULL, rsb_intr, sc, &sc->ih); - if (error != 0) { - device_printf(dev, "cannot setup interrupt handler\n"); - goto fail; - } - sc->iicbus = device_add_child(dev, "iicbus", -1); if (sc->iicbus == NULL) { device_printf(dev, "cannot add iicbus child device\n"); error = ENXIO; goto fail; } bus_generic_attach(dev); return (0); fail: - if (sc->ih != NULL) - bus_teardown_intr(dev, sc->res[1], sc->ih); - bus_release_resources(dev, rsb_spec, sc->res); + bus_release_resources(dev, rsb_spec, &sc->res); if (sc->rst != NULL) hwreset_release(sc->rst); if (sc->clk != NULL) clk_release(sc->clk); mtx_destroy(&sc->mtx); return (error); } static device_method_t rsb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rsb_probe), DEVMETHOD(device_attach, rsb_attach), /* Bus interface */ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), /* OFW methods */ DEVMETHOD(ofw_bus_get_node, rsb_get_node), /* iicbus interface */ DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_reset, rsb_reset), DEVMETHOD(iicbus_transfer, rsb_transfer), DEVMETHOD_END }; static driver_t rsb_driver = { "iichb", rsb_methods, sizeof(struct rsb_softc), }; static devclass_t rsb_devclass; DRIVER_MODULE(iicbus, rsb, iicbus_driver, iicbus_devclass, 0, 0); DRIVER_MODULE(rsb, simplebus, rsb_driver, rsb_devclass, 0, 0); MODULE_VERSION(rsb, 1); Index: head/sys/arm/allwinner/axp81x.c =================================================================== --- head/sys/arm/allwinner/axp81x.c (revision 299861) +++ head/sys/arm/allwinner/axp81x.c (revision 299862) @@ -1,530 +1,519 @@ /*- * Copyright (c) 2016 Jared McNeill * 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$ */ /* * X-Powers AXP813/818 PMU for Allwinner SoCs */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #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 }, { "x-powers,axp818", 1 }, { 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; 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) { struct axp81x_softc *sc; struct iic_msg msg[2]; sc = device_get_softc(dev); msg[0].slave = sc->addr; msg[0].flags = IIC_M_WR; msg[0].len = 1; msg[0].buf = ® msg[1].slave = sc->addr; msg[1].flags = IIC_M_RD; msg[1].len = size; msg[1].buf = data; return (iicbus_transfer(dev, msg, 2)); } static int axp81x_write(device_t dev, uint8_t reg, uint8_t val) { struct axp81x_softc *sc; struct iic_msg msg[2]; sc = device_get_softc(dev); msg[0].slave = sc->addr; msg[0].flags = IIC_M_WR; msg[0].len = 1; msg[0].buf = ® msg[1].slave = sc->addr; msg[1].flags = IIC_M_WR; msg[1].len = 1; msg[1].buf = &val; return (iicbus_transfer(dev, msg, 2)); } static void axp81x_shutdown(void *devp, int howto) { device_t dev; if ((howto & RB_POWEROFF) == 0) return; dev = devp; if (bootverbose) device_printf(dev, "Shutdown AXP81x\n"); 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) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "X-Powers AXP81x Power Management Unit"); return (BUS_PROBE_DEFAULT); } static int 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); } static device_method_t axp81x_methods[] = { /* Device interface */ 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 }; static driver_t axp81x_driver = { "axp81x_pmu", axp81x_methods, sizeof(struct axp81x_softc), }; 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);