Index: head/sys/arm/allwinner/axp209.c =================================================================== --- head/sys/arm/allwinner/axp209.c (revision 354395) +++ head/sys/arm/allwinner/axp209.c (revision 354396) @@ -1,1408 +1,1409 @@ /*- * 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 * 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. */ #include __FBSDID("$FreeBSD$"); /* * X-Power AXP209/AXP211 PMU for Allwinner SoCs */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #include "regdev_if.h" MALLOC_DEFINE(M_AXP2XX_REG, "Axp2XX regulator", "Axp2XX power regulator"); struct axp2xx_regdef { intptr_t id; char *name; uint8_t enable_reg; uint8_t enable_mask; uint8_t voltage_reg; uint8_t voltage_mask; uint8_t voltage_shift; int voltage_min; int voltage_max; int voltage_step; int voltage_nstep; }; static struct axp2xx_regdef axp209_regdefs[] = { { .id = AXP209_REG_ID_DCDC2, .name = "dcdc2", .enable_reg = AXP209_POWERCTL, .enable_mask = AXP209_POWERCTL_DCDC2, .voltage_reg = AXP209_REG_DCDC2_VOLTAGE, .voltage_mask = 0x3f, .voltage_min = 700, .voltage_max = 2275, .voltage_step = 25, .voltage_nstep = 64, }, { .id = AXP209_REG_ID_DCDC3, .name = "dcdc3", .enable_reg = AXP209_POWERCTL, .enable_mask = AXP209_POWERCTL_DCDC3, .voltage_reg = AXP209_REG_DCDC3_VOLTAGE, .voltage_mask = 0x7f, .voltage_min = 700, .voltage_max = 3500, .voltage_step = 25, .voltage_nstep = 128, }, { .id = AXP209_REG_ID_LDO2, .name = "ldo2", .enable_reg = AXP209_POWERCTL, .enable_mask = AXP209_POWERCTL_LDO2, .voltage_reg = AXP209_REG_LDO24_VOLTAGE, .voltage_mask = 0xf0, .voltage_shift = 4, .voltage_min = 1800, .voltage_max = 3300, .voltage_step = 100, .voltage_nstep = 16, }, { .id = AXP209_REG_ID_LDO3, .name = "ldo3", .enable_reg = AXP209_POWERCTL, .enable_mask = AXP209_POWERCTL_LDO3, .voltage_reg = AXP209_REG_LDO3_VOLTAGE, .voltage_mask = 0x7f, .voltage_min = 700, .voltage_max = 2275, .voltage_step = 25, .voltage_nstep = 128, }, }; static struct axp2xx_regdef axp221_regdefs[] = { { .id = AXP221_REG_ID_DLDO1, .name = "dldo1", .enable_reg = AXP221_POWERCTL_2, .enable_mask = AXP221_POWERCTL2_DLDO1, .voltage_reg = AXP221_REG_DLDO1_VOLTAGE, .voltage_mask = 0x1f, .voltage_min = 700, .voltage_max = 3300, .voltage_step = 100, .voltage_nstep = 26, }, { .id = AXP221_REG_ID_DLDO2, .name = "dldo2", .enable_reg = AXP221_POWERCTL_2, .enable_mask = AXP221_POWERCTL2_DLDO2, .voltage_reg = AXP221_REG_DLDO2_VOLTAGE, .voltage_mask = 0x1f, .voltage_min = 700, .voltage_max = 3300, .voltage_step = 100, .voltage_nstep = 26, }, { .id = AXP221_REG_ID_DLDO3, .name = "dldo3", .enable_reg = AXP221_POWERCTL_2, .enable_mask = AXP221_POWERCTL2_DLDO3, .voltage_reg = AXP221_REG_DLDO3_VOLTAGE, .voltage_mask = 0x1f, .voltage_min = 700, .voltage_max = 3300, .voltage_step = 100, .voltage_nstep = 26, }, { .id = AXP221_REG_ID_DLDO4, .name = "dldo4", .enable_reg = AXP221_POWERCTL_2, .enable_mask = AXP221_POWERCTL2_DLDO4, .voltage_reg = AXP221_REG_DLDO4_VOLTAGE, .voltage_mask = 0x1f, .voltage_min = 700, .voltage_max = 3300, .voltage_step = 100, .voltage_nstep = 26, }, { .id = AXP221_REG_ID_ELDO1, .name = "eldo1", .enable_reg = AXP221_POWERCTL_2, .enable_mask = AXP221_POWERCTL2_ELDO1, .voltage_reg = AXP221_REG_ELDO1_VOLTAGE, .voltage_mask = 0x1f, .voltage_min = 700, .voltage_max = 3300, .voltage_step = 100, .voltage_nstep = 26, }, { .id = AXP221_REG_ID_ELDO2, .name = "eldo2", .enable_reg = AXP221_POWERCTL_2, .enable_mask = AXP221_POWERCTL2_ELDO2, .voltage_reg = AXP221_REG_ELDO2_VOLTAGE, .voltage_mask = 0x1f, .voltage_min = 700, .voltage_max = 3300, .voltage_step = 100, .voltage_nstep = 26, }, { .id = AXP221_REG_ID_ELDO3, .name = "eldo3", .enable_reg = AXP221_POWERCTL_2, .enable_mask = AXP221_POWERCTL2_ELDO3, .voltage_reg = AXP221_REG_ELDO3_VOLTAGE, .voltage_mask = 0x1f, .voltage_min = 700, .voltage_max = 3300, .voltage_step = 100, .voltage_nstep = 26, }, { .id = AXP221_REG_ID_DC5LDO, .name = "dc5ldo", .enable_reg = AXP221_POWERCTL_1, .enable_mask = AXP221_POWERCTL1_DC5LDO, .voltage_reg = AXP221_REG_DC5LDO_VOLTAGE, .voltage_mask = 0x3, .voltage_min = 700, .voltage_max = 1400, .voltage_step = 100, .voltage_nstep = 7, }, { .id = AXP221_REG_ID_DCDC1, .name = "dcdc1", .enable_reg = AXP221_POWERCTL_1, .enable_mask = AXP221_POWERCTL1_DCDC1, .voltage_reg = AXP221_REG_DCDC1_VOLTAGE, .voltage_mask = 0x1f, .voltage_min = 1600, .voltage_max = 3400, .voltage_step = 100, .voltage_nstep = 18, }, { .id = AXP221_REG_ID_DCDC2, .name = "dcdc2", .enable_reg = AXP221_POWERCTL_1, .enable_mask = AXP221_POWERCTL1_DCDC2, .voltage_reg = AXP221_REG_DCDC2_VOLTAGE, .voltage_mask = 0x3f, .voltage_min = 600, .voltage_max = 1540, .voltage_step = 20, .voltage_nstep = 47, }, { .id = AXP221_REG_ID_DCDC3, .name = "dcdc3", .enable_reg = AXP221_POWERCTL_1, .enable_mask = AXP221_POWERCTL1_DCDC3, .voltage_reg = AXP221_REG_DCDC3_VOLTAGE, .voltage_mask = 0x3f, .voltage_min = 600, .voltage_max = 1860, .voltage_step = 20, .voltage_nstep = 63, }, { .id = AXP221_REG_ID_DCDC4, .name = "dcdc4", .enable_reg = AXP221_POWERCTL_1, .enable_mask = AXP221_POWERCTL1_DCDC4, .voltage_reg = AXP221_REG_DCDC4_VOLTAGE, .voltage_mask = 0x3f, .voltage_min = 600, .voltage_max = 1540, .voltage_step = 20, .voltage_nstep = 47, }, { .id = AXP221_REG_ID_DCDC5, .name = "dcdc5", .enable_reg = AXP221_POWERCTL_1, .enable_mask = AXP221_POWERCTL1_DCDC5, .voltage_reg = AXP221_REG_DCDC5_VOLTAGE, .voltage_mask = 0x1f, .voltage_min = 1000, .voltage_max = 2550, .voltage_step = 50, .voltage_nstep = 31, }, { .id = AXP221_REG_ID_ALDO1, .name = "aldo1", .enable_reg = AXP221_POWERCTL_1, .enable_mask = AXP221_POWERCTL1_ALDO1, .voltage_reg = AXP221_REG_ALDO1_VOLTAGE, .voltage_mask = 0x1f, .voltage_min = 700, .voltage_max = 3300, .voltage_step = 100, .voltage_nstep = 26, }, { .id = AXP221_REG_ID_ALDO2, .name = "aldo2", .enable_reg = AXP221_POWERCTL_1, .enable_mask = AXP221_POWERCTL1_ALDO2, .voltage_reg = AXP221_REG_ALDO2_VOLTAGE, .voltage_mask = 0x1f, .voltage_min = 700, .voltage_max = 3300, .voltage_step = 100, .voltage_nstep = 26, }, { .id = AXP221_REG_ID_ALDO3, .name = "aldo3", .enable_reg = AXP221_POWERCTL_3, .enable_mask = AXP221_POWERCTL3_ALDO3, .voltage_reg = AXP221_REG_ALDO3_VOLTAGE, .voltage_mask = 0x1f, .voltage_min = 700, .voltage_max = 3300, .voltage_step = 100, .voltage_nstep = 26, }, { .id = AXP221_REG_ID_DC1SW, .name = "dc1sw", .enable_reg = AXP221_POWERCTL_2, .enable_mask = AXP221_POWERCTL2_DC1SW, }, }; struct axp2xx_reg_sc { struct regnode *regnode; device_t base_dev; struct axp2xx_regdef *def; phandle_t xref; struct regnode_std_param *param; }; struct axp2xx_pins { const char *name; uint8_t ctrl_reg; uint8_t status_reg; uint8_t status_mask; uint8_t status_shift; }; /* GPIO3 is different, don't expose it for now */ static const struct axp2xx_pins axp209_pins[] = { { .name = "GPIO0", .ctrl_reg = AXP2XX_GPIO0_CTRL, .status_reg = AXP2XX_GPIO_STATUS, .status_mask = 0x10, .status_shift = 4, }, { .name = "GPIO1", .ctrl_reg = AXP2XX_GPIO1_CTRL, .status_reg = AXP2XX_GPIO_STATUS, .status_mask = 0x20, .status_shift = 5, }, { .name = "GPIO2", .ctrl_reg = AXP209_GPIO2_CTRL, .status_reg = AXP2XX_GPIO_STATUS, .status_mask = 0x40, .status_shift = 6, }, }; static const struct axp2xx_pins axp221_pins[] = { { .name = "GPIO0", .ctrl_reg = AXP2XX_GPIO0_CTRL, .status_reg = AXP2XX_GPIO_STATUS, .status_mask = 0x1, .status_shift = 0x0, }, { .name = "GPIO1", .ctrl_reg = AXP2XX_GPIO0_CTRL, .status_reg = AXP2XX_GPIO_STATUS, .status_mask = 0x2, .status_shift = 0x1, }, }; struct axp2xx_sensors { int id; const char *name; const char *desc; const char *format; uint8_t enable_reg; uint8_t enable_mask; uint8_t value_reg; uint8_t value_size; uint8_t h_value_mask; uint8_t h_value_shift; uint8_t l_value_mask; uint8_t l_value_shift; int value_step; int value_convert; }; static const struct axp2xx_sensors axp209_sensors[] = { { .id = AXP209_ACVOLT, .name = "acvolt", .desc = "AC Voltage (microvolt)", .format = "I", .enable_reg = AXP2XX_ADC_ENABLE1, .enable_mask = AXP209_ADC1_ACVOLT, .value_reg = AXP209_ACIN_VOLTAGE, .value_size = 2, .h_value_mask = 0xff, .h_value_shift = 4, .l_value_mask = 0xf, .l_value_shift = 0, .value_step = AXP209_VOLT_STEP, }, { .id = AXP209_ACCURRENT, .name = "accurrent", .desc = "AC Current (microAmpere)", .format = "I", .enable_reg = AXP2XX_ADC_ENABLE1, .enable_mask = AXP209_ADC1_ACCURRENT, .value_reg = AXP209_ACIN_CURRENT, .value_size = 2, .h_value_mask = 0xff, .h_value_shift = 4, .l_value_mask = 0xf, .l_value_shift = 0, .value_step = AXP209_ACCURRENT_STEP, }, { .id = AXP209_VBUSVOLT, .name = "vbusvolt", .desc = "VBUS Voltage (microVolt)", .format = "I", .enable_reg = AXP2XX_ADC_ENABLE1, .enable_mask = AXP209_ADC1_VBUSVOLT, .value_reg = AXP209_VBUS_VOLTAGE, .value_size = 2, .h_value_mask = 0xff, .h_value_shift = 4, .l_value_mask = 0xf, .l_value_shift = 0, .value_step = AXP209_VOLT_STEP, }, { .id = AXP209_VBUSCURRENT, .name = "vbuscurrent", .desc = "VBUS Current (microAmpere)", .format = "I", .enable_reg = AXP2XX_ADC_ENABLE1, .enable_mask = AXP209_ADC1_VBUSCURRENT, .value_reg = AXP209_VBUS_CURRENT, .value_size = 2, .h_value_mask = 0xff, .h_value_shift = 4, .l_value_mask = 0xf, .l_value_shift = 0, .value_step = AXP209_VBUSCURRENT_STEP, }, { .id = AXP2XX_BATVOLT, .name = "batvolt", .desc = "Battery Voltage (microVolt)", .format = "I", .enable_reg = AXP2XX_ADC_ENABLE1, .enable_mask = AXP2XX_ADC1_BATVOLT, .value_reg = AXP2XX_BAT_VOLTAGE, .value_size = 2, .h_value_mask = 0xff, .h_value_shift = 4, .l_value_mask = 0xf, .l_value_shift = 0, .value_step = AXP2XX_BATVOLT_STEP, }, { .id = AXP2XX_BATCHARGECURRENT, .name = "batchargecurrent", .desc = "Battery Charging Current (microAmpere)", .format = "I", .enable_reg = AXP2XX_ADC_ENABLE1, .enable_mask = AXP2XX_ADC1_BATCURRENT, .value_reg = AXP2XX_BAT_CHARGE_CURRENT, .value_size = 2, .h_value_mask = 0xff, .h_value_shift = 5, .l_value_mask = 0x1f, .l_value_shift = 0, .value_step = AXP2XX_BATCURRENT_STEP, }, { .id = AXP2XX_BATDISCHARGECURRENT, .name = "batdischargecurrent", .desc = "Battery Discharging Current (microAmpere)", .format = "I", .enable_reg = AXP2XX_ADC_ENABLE1, .enable_mask = AXP2XX_ADC1_BATCURRENT, .value_reg = AXP2XX_BAT_DISCHARGE_CURRENT, .value_size = 2, .h_value_mask = 0xff, .h_value_shift = 5, .l_value_mask = 0x1f, .l_value_shift = 0, .value_step = AXP2XX_BATCURRENT_STEP, }, { .id = AXP2XX_TEMP, .name = "temp", .desc = "Internal Temperature", .format = "IK", .enable_reg = AXP209_ADC_ENABLE2, .enable_mask = AXP209_ADC2_TEMP, .value_reg = AXP209_TEMPMON, .value_size = 2, .h_value_mask = 0xff, .h_value_shift = 4, .l_value_mask = 0xf, .l_value_shift = 0, .value_step = 1, .value_convert = -(AXP209_TEMPMON_MIN - AXP209_0C_TO_K), }, }; static const struct axp2xx_sensors axp221_sensors[] = { { .id = AXP2XX_BATVOLT, .name = "batvolt", .desc = "Battery Voltage (microVolt)", .format = "I", .enable_reg = AXP2XX_ADC_ENABLE1, .enable_mask = AXP2XX_ADC1_BATVOLT, .value_reg = AXP2XX_BAT_VOLTAGE, .value_size = 2, .h_value_mask = 0xff, .h_value_shift = 4, .l_value_mask = 0xf, .l_value_shift = 0, .value_step = AXP2XX_BATVOLT_STEP, }, { .id = AXP2XX_BATCHARGECURRENT, .name = "batchargecurrent", .desc = "Battery Charging Current (microAmpere)", .format = "I", .enable_reg = AXP2XX_ADC_ENABLE1, .enable_mask = AXP2XX_ADC1_BATCURRENT, .value_reg = AXP2XX_BAT_CHARGE_CURRENT, .value_size = 2, .h_value_mask = 0xff, .h_value_shift = 5, .l_value_mask = 0x1f, .l_value_shift = 0, .value_step = AXP2XX_BATCURRENT_STEP, }, { .id = AXP2XX_BATDISCHARGECURRENT, .name = "batdischargecurrent", .desc = "Battery Discharging Current (microAmpere)", .format = "I", .enable_reg = AXP2XX_ADC_ENABLE1, .enable_mask = AXP2XX_ADC1_BATCURRENT, .value_reg = AXP2XX_BAT_DISCHARGE_CURRENT, .value_size = 2, .h_value_mask = 0xff, .h_value_shift = 5, .l_value_mask = 0x1f, .l_value_shift = 0, .value_step = AXP2XX_BATCURRENT_STEP, }, { .id = AXP2XX_TEMP, .name = "temp", .desc = "Internal Temperature", .format = "IK", .enable_reg = AXP2XX_ADC_ENABLE1, .enable_mask = AXP221_ADC1_TEMP, .value_reg = AXP221_TEMPMON, .value_size = 2, .h_value_mask = 0xff, .h_value_shift = 4, .l_value_mask = 0xf, .l_value_shift = 0, .value_step = 1, .value_convert = -(AXP221_TEMPMON_MIN - AXP209_0C_TO_K), }, }; enum AXP2XX_TYPE { AXP209 = 1, AXP221, }; struct axp2xx_softc { device_t dev; struct resource * res[1]; void * intrcookie; struct intr_config_hook intr_hook; struct mtx mtx; uint8_t type; /* GPIO */ device_t gpiodev; int npins; const struct axp2xx_pins *pins; /* Sensors */ const struct axp2xx_sensors *sensors; int nsensors; /* Regulators */ struct axp2xx_reg_sc **regs; int nregs; struct axp2xx_regdef *regdefs; }; static struct ofw_compat_data compat_data[] = { { "x-powers,axp209", AXP209 }, { "x-powers,axp221", AXP221 }, { NULL, 0 } }; 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 axp2xx_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) { return (iicdev_readfrom(dev, reg, data, size, IIC_INTRWAIT)); } static int axp2xx_write(device_t dev, uint8_t reg, uint8_t data) { return (iicdev_writeto(dev, reg, &data, sizeof(data), IIC_INTRWAIT)); } static int axp2xx_regnode_init(struct regnode *regnode) { return (0); } static int axp2xx_regnode_enable(struct regnode *regnode, bool enable, int *udelay) { struct axp2xx_reg_sc *sc; uint8_t val; sc = regnode_get_softc(regnode); axp2xx_read(sc->base_dev, sc->def->enable_reg, &val, 1); if (enable) val |= sc->def->enable_mask; else val &= ~sc->def->enable_mask; axp2xx_write(sc->base_dev, sc->def->enable_reg, val); *udelay = 0; return (0); } static void axp2xx_regnode_reg_to_voltage(struct axp2xx_reg_sc *sc, uint8_t val, int *uv) { if (val < sc->def->voltage_nstep) *uv = sc->def->voltage_min + val * sc->def->voltage_step; else *uv = sc->def->voltage_min + (sc->def->voltage_nstep * sc->def->voltage_step); *uv *= 1000; } static int axp2xx_regnode_voltage_to_reg(struct axp2xx_reg_sc *sc, int min_uvolt, int max_uvolt, uint8_t *val) { uint8_t nval; int nstep, uvolt; nval = 0; uvolt = sc->def->voltage_min * 1000; for (nstep = 0; nstep < sc->def->voltage_nstep && uvolt < min_uvolt; nstep++) { ++nval; uvolt += (sc->def->voltage_step * 1000); } if (uvolt > max_uvolt) return (EINVAL); *val = nval; return (0); } static int axp2xx_regnode_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt, int *udelay) { struct axp2xx_reg_sc *sc; uint8_t val; sc = regnode_get_softc(regnode); if (!sc->def->voltage_step) return (ENXIO); if (axp2xx_regnode_voltage_to_reg(sc, min_uvolt, max_uvolt, &val) != 0) return (ERANGE); axp2xx_write(sc->base_dev, sc->def->voltage_reg, val); *udelay = 0; return (0); } static int axp2xx_regnode_get_voltage(struct regnode *regnode, int *uvolt) { struct axp2xx_reg_sc *sc; uint8_t val; sc = regnode_get_softc(regnode); if (!sc->def->voltage_step) return (ENXIO); axp2xx_read(sc->base_dev, sc->def->voltage_reg, &val, 1); axp2xx_regnode_reg_to_voltage(sc, val & sc->def->voltage_mask, uvolt); return (0); } static regnode_method_t axp2xx_regnode_methods[] = { /* Regulator interface */ REGNODEMETHOD(regnode_init, axp2xx_regnode_init), REGNODEMETHOD(regnode_enable, axp2xx_regnode_enable), REGNODEMETHOD(regnode_set_voltage, axp2xx_regnode_set_voltage), REGNODEMETHOD(regnode_get_voltage, axp2xx_regnode_get_voltage), + REGNODEMETHOD(regnode_check_voltage, regnode_method_check_voltage), REGNODEMETHOD_END }; DEFINE_CLASS_1(axp2xx_regnode, axp2xx_regnode_class, axp2xx_regnode_methods, sizeof(struct axp2xx_reg_sc), regnode_class); static int axp2xx_sysctl(SYSCTL_HANDLER_ARGS) { struct axp2xx_softc *sc; device_t dev = arg1; enum axp2xx_sensor sensor = arg2; uint8_t data[2]; int val, error, i, found; sc = device_get_softc(dev); for (found = 0, i = 0; i < sc->nsensors; i++) { if (sc->sensors[i].id == sensor) { found = 1; break; } } if (found == 0) return (ENOENT); error = axp2xx_read(dev, sc->sensors[i].value_reg, data, 2); if (error != 0) return (error); val = ((data[0] & sc->sensors[i].h_value_mask) << sc->sensors[i].h_value_shift); val |= ((data[1] & sc->sensors[i].l_value_mask) << sc->sensors[i].l_value_shift); val *= sc->sensors[i].value_step; val += sc->sensors[i].value_convert; return sysctl_handle_opaque(oidp, &val, sizeof(val), req); } static void axp2xx_shutdown(void *devp, int howto) { device_t dev; if (!(howto & RB_POWEROFF)) return; dev = (device_t)devp; if (bootverbose) device_printf(dev, "Shutdown AXP2xx\n"); axp2xx_write(dev, AXP2XX_SHUTBAT, AXP2XX_SHUTBAT_SHUTDOWN); } static void axp2xx_intr(void *arg) { struct axp2xx_softc *sc; uint8_t reg; sc = arg; axp2xx_read(sc->dev, AXP2XX_IRQ1_STATUS, ®, 1); if (reg) { if (reg & AXP2XX_IRQ1_AC_OVERVOLT) devctl_notify("PMU", "AC", "overvoltage", NULL); if (reg & AXP2XX_IRQ1_VBUS_OVERVOLT) devctl_notify("PMU", "USB", "overvoltage", NULL); if (reg & AXP2XX_IRQ1_VBUS_LOW) devctl_notify("PMU", "USB", "undervoltage", NULL); if (reg & AXP2XX_IRQ1_AC_CONN) devctl_notify("PMU", "AC", "plugged", NULL); if (reg & AXP2XX_IRQ1_AC_DISCONN) devctl_notify("PMU", "AC", "unplugged", NULL); if (reg & AXP2XX_IRQ1_VBUS_CONN) devctl_notify("PMU", "USB", "plugged", NULL); if (reg & AXP2XX_IRQ1_VBUS_DISCONN) devctl_notify("PMU", "USB", "unplugged", NULL); axp2xx_write(sc->dev, AXP2XX_IRQ1_STATUS, AXP2XX_IRQ_ACK); } axp2xx_read(sc->dev, AXP2XX_IRQ2_STATUS, ®, 1); if (reg) { if (reg & AXP2XX_IRQ2_BATT_CHARGED) devctl_notify("PMU", "Battery", "charged", NULL); if (reg & AXP2XX_IRQ2_BATT_CHARGING) devctl_notify("PMU", "Battery", "charging", NULL); if (reg & AXP2XX_IRQ2_BATT_CONN) devctl_notify("PMU", "Battery", "connected", NULL); if (reg & AXP2XX_IRQ2_BATT_DISCONN) devctl_notify("PMU", "Battery", "disconnected", NULL); if (reg & AXP2XX_IRQ2_BATT_TEMP_LOW) devctl_notify("PMU", "Battery", "low temp", NULL); if (reg & AXP2XX_IRQ2_BATT_TEMP_OVER) devctl_notify("PMU", "Battery", "high temp", NULL); axp2xx_write(sc->dev, AXP2XX_IRQ2_STATUS, AXP2XX_IRQ_ACK); } axp2xx_read(sc->dev, AXP2XX_IRQ3_STATUS, ®, 1); if (reg) { if (reg & AXP2XX_IRQ3_PEK_SHORT) shutdown_nice(RB_POWEROFF); axp2xx_write(sc->dev, AXP2XX_IRQ3_STATUS, AXP2XX_IRQ_ACK); } axp2xx_read(sc->dev, AXP2XX_IRQ4_STATUS, ®, 1); if (reg) { axp2xx_write(sc->dev, AXP2XX_IRQ4_STATUS, AXP2XX_IRQ_ACK); } axp2xx_read(sc->dev, AXP2XX_IRQ5_STATUS, ®, 1); if (reg) { axp2xx_write(sc->dev, AXP2XX_IRQ5_STATUS, AXP2XX_IRQ_ACK); } } static device_t axp2xx_gpio_get_bus(device_t dev) { struct axp2xx_softc *sc; sc = device_get_softc(dev); return (sc->gpiodev); } static int axp2xx_gpio_pin_max(device_t dev, int *maxpin) { struct axp2xx_softc *sc; sc = device_get_softc(dev); *maxpin = sc->npins - 1; return (0); } static int axp2xx_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct axp2xx_softc *sc; sc = device_get_softc(dev); if (pin >= sc->npins) return (EINVAL); snprintf(name, GPIOMAXNAME, "%s", axp209_pins[pin].name); return (0); } static int axp2xx_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct axp2xx_softc *sc; sc = device_get_softc(dev); if (pin >= sc->npins) return (EINVAL); *caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT; return (0); } static int axp2xx_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct axp2xx_softc *sc; uint8_t data, func; int error; sc = device_get_softc(dev); if (pin >= sc->npins) return (EINVAL); AXP_LOCK(sc); error = axp2xx_read(dev, sc->pins[pin].ctrl_reg, &data, 1); if (error == 0) { func = data & AXP2XX_GPIO_FUNC_MASK; if (func == AXP2XX_GPIO_FUNC_INPUT) *flags = GPIO_PIN_INPUT; else if (func == AXP2XX_GPIO_FUNC_DRVLO || func == AXP2XX_GPIO_FUNC_DRVHI) *flags = GPIO_PIN_OUTPUT; else *flags = 0; } AXP_UNLOCK(sc); return (error); } static int axp2xx_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct axp2xx_softc *sc; uint8_t data; int error; sc = device_get_softc(dev); if (pin >= sc->npins) return (EINVAL); AXP_LOCK(sc); error = axp2xx_read(dev, sc->pins[pin].ctrl_reg, &data, 1); if (error == 0) { data &= ~AXP2XX_GPIO_FUNC_MASK; if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) != 0) { if ((flags & GPIO_PIN_OUTPUT) == 0) data |= AXP2XX_GPIO_FUNC_INPUT; } error = axp2xx_write(dev, sc->pins[pin].ctrl_reg, data); } AXP_UNLOCK(sc); return (error); } static int axp2xx_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct axp2xx_softc *sc; uint8_t data, func; int error; sc = device_get_softc(dev); if (pin >= sc->npins) return (EINVAL); AXP_LOCK(sc); error = axp2xx_read(dev, sc->pins[pin].ctrl_reg, &data, 1); if (error == 0) { func = data & AXP2XX_GPIO_FUNC_MASK; switch (func) { case AXP2XX_GPIO_FUNC_DRVLO: *val = 0; break; case AXP2XX_GPIO_FUNC_DRVHI: *val = 1; break; case AXP2XX_GPIO_FUNC_INPUT: error = axp2xx_read(dev, sc->pins[pin].status_reg, &data, 1); if (error == 0) { *val = (data & sc->pins[pin].status_mask); *val >>= sc->pins[pin].status_shift; } break; default: error = EIO; break; } } AXP_UNLOCK(sc); return (error); } static int axp2xx_gpio_pin_set(device_t dev, uint32_t pin, unsigned int val) { struct axp2xx_softc *sc; uint8_t data, func; int error; sc = device_get_softc(dev); if (pin >= sc->npins) return (EINVAL); AXP_LOCK(sc); error = axp2xx_read(dev, sc->pins[pin].ctrl_reg, &data, 1); if (error == 0) { func = data & AXP2XX_GPIO_FUNC_MASK; switch (func) { case AXP2XX_GPIO_FUNC_DRVLO: case AXP2XX_GPIO_FUNC_DRVHI: /* GPIO2 can't be set to 1 */ if (pin == 2 && val == 1) { error = EINVAL; break; } data &= ~AXP2XX_GPIO_FUNC_MASK; data |= val; break; default: error = EIO; break; } } if (error == 0) error = axp2xx_write(dev, sc->pins[pin].ctrl_reg, data); AXP_UNLOCK(sc); return (error); } static int axp2xx_gpio_pin_toggle(device_t dev, uint32_t pin) { struct axp2xx_softc *sc; uint8_t data, func; int error; sc = device_get_softc(dev); if (pin >= sc->npins) return (EINVAL); AXP_LOCK(sc); error = axp2xx_read(dev, sc->pins[pin].ctrl_reg, &data, 1); if (error == 0) { func = data & AXP2XX_GPIO_FUNC_MASK; switch (func) { case AXP2XX_GPIO_FUNC_DRVLO: /* Pin 2 can't be set to 1*/ if (pin == 2) { error = EINVAL; break; } data &= ~AXP2XX_GPIO_FUNC_MASK; data |= AXP2XX_GPIO_FUNC_DRVHI; break; case AXP2XX_GPIO_FUNC_DRVHI: data &= ~AXP2XX_GPIO_FUNC_MASK; data |= AXP2XX_GPIO_FUNC_DRVLO; break; default: error = EIO; break; } } if (error == 0) error = axp2xx_write(dev, sc->pins[pin].ctrl_reg, data); AXP_UNLOCK(sc); return (error); } static int axp2xx_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags) { struct axp2xx_softc *sc; sc = device_get_softc(bus); if (gpios[0] >= sc->npins) return (EINVAL); *pin = gpios[0]; *flags = gpios[1]; return (0); } static phandle_t axp2xx_get_node(device_t dev, device_t bus) { return (ofw_bus_get_node(dev)); } static struct axp2xx_reg_sc * axp2xx_reg_attach(device_t dev, phandle_t node, struct axp2xx_regdef *def) { struct axp2xx_reg_sc *reg_sc; struct regnode_init_def initdef; struct regnode *regnode; memset(&initdef, 0, sizeof(initdef)); if (regulator_parse_ofw_stdparam(dev, node, &initdef) != 0) { device_printf(dev, "cannot create regulator\n"); return (NULL); } if (initdef.std_param.min_uvolt == 0) initdef.std_param.min_uvolt = def->voltage_min * 1000; if (initdef.std_param.max_uvolt == 0) initdef.std_param.max_uvolt = def->voltage_max * 1000; initdef.id = def->id; initdef.ofw_node = node; regnode = regnode_create(dev, &axp2xx_regnode_class, &initdef); if (regnode == NULL) { device_printf(dev, "cannot create regulator\n"); return (NULL); } reg_sc = regnode_get_softc(regnode); reg_sc->regnode = regnode; reg_sc->base_dev = dev; reg_sc->def = def; reg_sc->xref = OF_xref_from_node(node); reg_sc->param = regnode_get_stdparam(regnode); regnode_register(regnode); return (reg_sc); } static int axp2xx_regdev_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells, intptr_t *num) { struct axp2xx_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->nregs; i++) { if (sc->regs[i] == NULL) continue; if (sc->regs[i]->xref == xref) { *num = sc->regs[i]->def->id; return (0); } } return (ENXIO); } static void axp2xx_start(void *pdev) { device_t dev; struct axp2xx_softc *sc; const char *pwr_name[] = {"Battery", "AC", "USB", "AC and USB"}; int i; uint8_t reg, data; uint8_t pwr_src; dev = pdev; sc = device_get_softc(dev); sc->dev = dev; if (bootverbose) { /* * Read the Power State register. * Shift the AC presence into bit 0. * Shift the Battery presence into bit 1. */ axp2xx_read(dev, AXP2XX_PSR, &data, 1); pwr_src = ((data & AXP2XX_PSR_ACIN) >> AXP2XX_PSR_ACIN_SHIFT) | ((data & AXP2XX_PSR_VBUS) >> (AXP2XX_PSR_VBUS_SHIFT - 1)); device_printf(dev, "Powered by %s\n", pwr_name[pwr_src]); } /* Only enable interrupts that we are interested in */ axp2xx_write(dev, AXP2XX_IRQ1_ENABLE, AXP2XX_IRQ1_AC_OVERVOLT | AXP2XX_IRQ1_AC_DISCONN | AXP2XX_IRQ1_AC_CONN | AXP2XX_IRQ1_VBUS_OVERVOLT | AXP2XX_IRQ1_VBUS_DISCONN | AXP2XX_IRQ1_VBUS_CONN); axp2xx_write(dev, AXP2XX_IRQ2_ENABLE, AXP2XX_IRQ2_BATT_CONN | AXP2XX_IRQ2_BATT_DISCONN | AXP2XX_IRQ2_BATT_CHARGE_ACCT_ON | AXP2XX_IRQ2_BATT_CHARGE_ACCT_OFF | AXP2XX_IRQ2_BATT_CHARGING | AXP2XX_IRQ2_BATT_CHARGED | AXP2XX_IRQ2_BATT_TEMP_OVER | AXP2XX_IRQ2_BATT_TEMP_LOW); axp2xx_write(dev, AXP2XX_IRQ3_ENABLE, AXP2XX_IRQ3_PEK_SHORT | AXP2XX_IRQ3_PEK_LONG); axp2xx_write(dev, AXP2XX_IRQ4_ENABLE, AXP2XX_IRQ4_APS_LOW_2); axp2xx_write(dev, AXP2XX_IRQ5_ENABLE, 0x0); EVENTHANDLER_REGISTER(shutdown_final, axp2xx_shutdown, dev, SHUTDOWN_PRI_LAST); /* Enable ADC sensors */ for (i = 0; i < sc->nsensors; i++) { if (axp2xx_read(dev, sc->sensors[i].enable_reg, ®, 1) == -1) { device_printf(dev, "Cannot enable sensor '%s'\n", sc->sensors[i].name); continue; } reg |= sc->sensors[i].enable_mask; if (axp2xx_write(dev, sc->sensors[i].enable_reg, reg) == -1) { device_printf(dev, "Cannot enable sensor '%s'\n", sc->sensors[i].name); continue; } SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, sc->sensors[i].name, CTLTYPE_INT | CTLFLAG_RD, dev, sc->sensors[i].id, axp2xx_sysctl, sc->sensors[i].format, sc->sensors[i].desc); } if ((bus_setup_intr(dev, sc->res[0], INTR_TYPE_MISC | INTR_MPSAFE, NULL, axp2xx_intr, sc, &sc->intrcookie))) device_printf(dev, "unable to register interrupt handler\n"); config_intrhook_disestablish(&sc->intr_hook); } static int axp2xx_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { case AXP209: device_set_desc(dev, "X-Powers AXP209 Power Management Unit"); break; case AXP221: device_set_desc(dev, "X-Powers AXP221 Power Management Unit"); break; default: return (ENXIO); } return (BUS_PROBE_DEFAULT); } static int axp2xx_attach(device_t dev) { struct axp2xx_softc *sc; struct axp2xx_reg_sc *reg; struct axp2xx_regdef *regdefs; phandle_t rnode, child; int i; 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->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; switch (sc->type) { case AXP209: sc->pins = axp209_pins; sc->npins = nitems(axp209_pins); sc->gpiodev = gpiobus_attach_bus(dev); sc->sensors = axp209_sensors; sc->nsensors = nitems(axp209_sensors); regdefs = axp209_regdefs; sc->nregs = nitems(axp209_regdefs); break; case AXP221: sc->pins = axp221_pins; sc->npins = nitems(axp221_pins); sc->gpiodev = gpiobus_attach_bus(dev); sc->sensors = axp221_sensors; sc->nsensors = nitems(axp221_sensors); regdefs = axp221_regdefs; sc->nregs = nitems(axp221_regdefs); break; } sc->regs = malloc(sizeof(struct axp2xx_reg_sc *) * sc->nregs, M_AXP2XX_REG, M_WAITOK | M_ZERO); sc->intr_hook.ich_func = axp2xx_start; sc->intr_hook.ich_arg = dev; if (config_intrhook_establish(&sc->intr_hook) != 0) return (ENOMEM); /* Attach known regulators that exist in the DT */ rnode = ofw_bus_find_child(ofw_bus_get_node(dev), "regulators"); if (rnode > 0) { for (i = 0; i < sc->nregs; i++) { child = ofw_bus_find_child(rnode, regdefs[i].name); if (child == 0) continue; reg = axp2xx_reg_attach(dev, child, ®defs[i]); if (reg == NULL) { device_printf(dev, "cannot attach regulator %s\n", regdefs[i].name); continue; } sc->regs[i] = reg; if (bootverbose) device_printf(dev, "Regulator %s attached\n", regdefs[i].name); } } return (0); } static device_method_t axp2xx_methods[] = { DEVMETHOD(device_probe, axp2xx_probe), DEVMETHOD(device_attach, axp2xx_attach), /* GPIO interface */ DEVMETHOD(gpio_get_bus, axp2xx_gpio_get_bus), DEVMETHOD(gpio_pin_max, axp2xx_gpio_pin_max), DEVMETHOD(gpio_pin_getname, axp2xx_gpio_pin_getname), DEVMETHOD(gpio_pin_getcaps, axp2xx_gpio_pin_getcaps), DEVMETHOD(gpio_pin_getflags, axp2xx_gpio_pin_getflags), DEVMETHOD(gpio_pin_setflags, axp2xx_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, axp2xx_gpio_pin_get), DEVMETHOD(gpio_pin_set, axp2xx_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, axp2xx_gpio_pin_toggle), DEVMETHOD(gpio_map_gpios, axp2xx_gpio_map_gpios), /* Regdev interface */ DEVMETHOD(regdev_map, axp2xx_regdev_map), /* OFW bus interface */ DEVMETHOD(ofw_bus_get_node, axp2xx_get_node), DEVMETHOD_END }; static driver_t axp2xx_driver = { "axp2xx_pmu", axp2xx_methods, sizeof(struct axp2xx_softc), }; static devclass_t axp2xx_devclass; extern devclass_t ofwgpiobus_devclass, gpioc_devclass; extern driver_t ofw_gpiobus_driver, gpioc_driver; EARLY_DRIVER_MODULE(axp2xx, iicbus, axp2xx_driver, axp2xx_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); EARLY_DRIVER_MODULE(ofw_gpiobus, axp2xx_pmu, ofw_gpiobus_driver, ofwgpiobus_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); DRIVER_MODULE(gpioc, axp2xx_pmu, gpioc_driver, gpioc_devclass, 0, 0); MODULE_VERSION(axp2xx, 1); MODULE_DEPEND(axp2xx, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); Index: head/sys/arm/allwinner/axp81x.c =================================================================== --- head/sys/arm/allwinner/axp81x.c (revision 354395) +++ head/sys/arm/allwinner/axp81x.c (revision 354396) @@ -1,1615 +1,1616 @@ /*- * Copyright (c) 2018 Emmanuel Vadot * 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 AXP803/813/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 "gpio_if.h" #include "iicbus_if.h" #include "regdev_if.h" MALLOC_DEFINE(M_AXP8XX_REG, "AXP8xx regulator", "AXP8xx power regulator"); #define AXP_POWERSRC 0x00 #define AXP_POWERSRC_ACIN (1 << 7) #define AXP_POWERSRC_VBUS (1 << 5) #define AXP_POWERSRC_VBAT (1 << 3) #define AXP_POWERSRC_CHARING (1 << 2) /* Charging Direction */ #define AXP_POWERSRC_SHORTED (1 << 1) #define AXP_POWERSRC_STARTUP (1 << 0) #define AXP_POWERMODE 0x01 #define AXP_POWERMODE_BAT_CHARGING (1 << 6) #define AXP_POWERMODE_BAT_PRESENT (1 << 5) #define AXP_POWERMODE_BAT_VALID (1 << 4) #define AXP_ICTYPE 0x03 #define AXP_POWERCTL1 0x10 #define AXP_POWERCTL1_DCDC7 (1 << 6) /* AXP813/818 only */ #define AXP_POWERCTL1_DCDC6 (1 << 5) #define AXP_POWERCTL1_DCDC5 (1 << 4) #define AXP_POWERCTL1_DCDC4 (1 << 3) #define AXP_POWERCTL1_DCDC3 (1 << 2) #define AXP_POWERCTL1_DCDC2 (1 << 1) #define AXP_POWERCTL1_DCDC1 (1 << 0) #define AXP_POWERCTL2 0x12 #define AXP_POWERCTL2_DC1SW (1 << 7) /* AXP803 only */ #define AXP_POWERCTL2_DLDO4 (1 << 6) #define AXP_POWERCTL2_DLDO3 (1 << 5) #define AXP_POWERCTL2_DLDO2 (1 << 4) #define AXP_POWERCTL2_DLDO1 (1 << 3) #define AXP_POWERCTL2_ELDO3 (1 << 2) #define AXP_POWERCTL2_ELDO2 (1 << 1) #define AXP_POWERCTL2_ELDO1 (1 << 0) #define AXP_POWERCTL3 0x13 #define AXP_POWERCTL3_ALDO3 (1 << 7) #define AXP_POWERCTL3_ALDO2 (1 << 6) #define AXP_POWERCTL3_ALDO1 (1 << 5) #define AXP_POWERCTL3_FLDO3 (1 << 4) /* AXP813/818 only */ #define AXP_POWERCTL3_FLDO2 (1 << 3) #define AXP_POWERCTL3_FLDO1 (1 << 2) #define AXP_VOLTCTL_DLDO1 0x15 #define AXP_VOLTCTL_DLDO2 0x16 #define AXP_VOLTCTL_DLDO3 0x17 #define AXP_VOLTCTL_DLDO4 0x18 #define AXP_VOLTCTL_ELDO1 0x19 #define AXP_VOLTCTL_ELDO2 0x1A #define AXP_VOLTCTL_ELDO3 0x1B #define AXP_VOLTCTL_FLDO1 0x1C #define AXP_VOLTCTL_FLDO2 0x1D #define AXP_VOLTCTL_DCDC1 0x20 #define AXP_VOLTCTL_DCDC2 0x21 #define AXP_VOLTCTL_DCDC3 0x22 #define AXP_VOLTCTL_DCDC4 0x23 #define AXP_VOLTCTL_DCDC5 0x24 #define AXP_VOLTCTL_DCDC6 0x25 #define AXP_VOLTCTL_DCDC7 0x26 #define AXP_VOLTCTL_ALDO1 0x28 #define AXP_VOLTCTL_ALDO2 0x29 #define AXP_VOLTCTL_ALDO3 0x2A #define AXP_VOLTCTL_STATUS (1 << 7) #define AXP_VOLTCTL_MASK 0x7f #define AXP_POWERBAT 0x32 #define AXP_POWERBAT_SHUTDOWN (1 << 7) #define AXP_CHARGERCTL1 0x33 #define AXP_CHARGERCTL1_MIN 0 #define AXP_CHARGERCTL1_MAX 13 #define AXP_CHARGERCTL1_CMASK 0xf #define AXP_IRQEN1 0x40 #define AXP_IRQEN1_ACIN_HI (1 << 6) #define AXP_IRQEN1_ACIN_LO (1 << 5) #define AXP_IRQEN1_VBUS_HI (1 << 3) #define AXP_IRQEN1_VBUS_LO (1 << 2) #define AXP_IRQEN2 0x41 #define AXP_IRQEN2_BAT_IN (1 << 7) #define AXP_IRQEN2_BAT_NO (1 << 6) #define AXP_IRQEN2_BATCHGC (1 << 3) #define AXP_IRQEN2_BATCHGD (1 << 2) #define AXP_IRQEN3 0x42 #define AXP_IRQEN4 0x43 #define AXP_IRQEN4_BATLVL_LO1 (1 << 1) #define AXP_IRQEN4_BATLVL_LO0 (1 << 0) #define AXP_IRQEN5 0x44 #define AXP_IRQEN5_POKSIRQ (1 << 4) #define AXP_IRQEN5_POKLIRQ (1 << 3) #define AXP_IRQEN6 0x45 #define AXP_IRQSTAT1 0x48 #define AXP_IRQSTAT1_ACIN_HI (1 << 6) #define AXP_IRQSTAT1_ACIN_LO (1 << 5) #define AXP_IRQSTAT1_VBUS_HI (1 << 3) #define AXP_IRQSTAT1_VBUS_LO (1 << 2) #define AXP_IRQSTAT2 0x49 #define AXP_IRQSTAT2_BAT_IN (1 << 7) #define AXP_IRQSTAT2_BAT_NO (1 << 6) #define AXP_IRQSTAT2_BATCHGC (1 << 3) #define AXP_IRQSTAT2_BATCHGD (1 << 2) #define AXP_IRQSTAT3 0x4a #define AXP_IRQSTAT4 0x4b #define AXP_IRQSTAT4_BATLVL_LO1 (1 << 1) #define AXP_IRQSTAT4_BATLVL_LO0 (1 << 0) #define AXP_IRQSTAT5 0x4c #define AXP_IRQSTAT5_POKSIRQ (1 << 4) #define AXP_IRQEN5_POKLIRQ (1 << 3) #define AXP_IRQSTAT6 0x4d #define AXP_BATSENSE_HI 0x78 #define AXP_BATSENSE_LO 0x79 #define AXP_BATCHG_HI 0x7a #define AXP_BATCHG_LO 0x7b #define AXP_BATDISCHG_HI 0x7c #define AXP_BATDISCHG_LO 0x7d #define AXP_GPIO0_CTRL 0x90 #define AXP_GPIO0LDO_CTRL 0x91 #define AXP_GPIO1_CTRL 0x92 #define AXP_GPIO1LDO_CTRL 0x93 #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_FUNC_LDO_ON 3 #define AXP_GPIO_FUNC_LDO_OFF 4 #define AXP_GPIO_SIGBIT 0x94 #define AXP_GPIO_PD 0x97 #define AXP_FUEL_GAUGECTL 0xb8 #define AXP_FUEL_GAUGECTL_EN (1 << 7) #define AXP_BAT_CAP 0xb9 #define AXP_BAT_CAP_VALID (1 << 7) #define AXP_BAT_CAP_PERCENT 0x7f #define AXP_BAT_MAX_CAP_HI 0xe0 #define AXP_BAT_MAX_CAP_VALID (1 << 7) #define AXP_BAT_MAX_CAP_LO 0xe1 #define AXP_BAT_COULOMB_HI 0xe2 #define AXP_BAT_COULOMB_VALID (1 << 7) #define AXP_BAT_COULOMB_LO 0xe3 #define AXP_BAT_CAP_WARN 0xe6 #define AXP_BAT_CAP_WARN_LV1 0xf0 /* Bits 4, 5, 6, 7 */ #define AXP_BAP_CAP_WARN_LV1BASE 5 /* 5-20%, 1% per step */ #define AXP_BAT_CAP_WARN_LV2 0xf /* Bits 0, 1, 2, 3 */ /* Sensor conversion macros */ #define AXP_SENSOR_BAT_H(hi) ((hi) << 4) #define AXP_SENSOR_BAT_L(lo) ((lo) & 0xf) #define AXP_SENSOR_COULOMB(hi, lo) (((hi & ~(1 << 7)) << 8) | (lo)) static const struct { const char *name; uint8_t ctrl_reg; } axp8xx_pins[] = { { "GPIO0", AXP_GPIO0_CTRL }, { "GPIO1", AXP_GPIO1_CTRL }, }; enum AXP8XX_TYPE { AXP803 = 1, AXP813, }; static struct ofw_compat_data compat_data[] = { { "x-powers,axp803", AXP803 }, { "x-powers,axp813", AXP813 }, { "x-powers,axp818", AXP813 }, { NULL, 0 } }; static struct resource_spec axp8xx_spec[] = { { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; struct axp8xx_regdef { intptr_t id; char *name; char *supply_name; uint8_t enable_reg; uint8_t enable_mask; uint8_t enable_value; uint8_t disable_value; uint8_t voltage_reg; int voltage_min; int voltage_max; int voltage_step1; int voltage_nstep1; int voltage_step2; int voltage_nstep2; }; enum axp8xx_reg_id { AXP8XX_REG_ID_DCDC1 = 100, AXP8XX_REG_ID_DCDC2, AXP8XX_REG_ID_DCDC3, AXP8XX_REG_ID_DCDC4, AXP8XX_REG_ID_DCDC5, AXP8XX_REG_ID_DCDC6, AXP813_REG_ID_DCDC7, AXP803_REG_ID_DC1SW, AXP8XX_REG_ID_DLDO1, AXP8XX_REG_ID_DLDO2, AXP8XX_REG_ID_DLDO3, AXP8XX_REG_ID_DLDO4, AXP8XX_REG_ID_ELDO1, AXP8XX_REG_ID_ELDO2, AXP8XX_REG_ID_ELDO3, AXP8XX_REG_ID_ALDO1, AXP8XX_REG_ID_ALDO2, AXP8XX_REG_ID_ALDO3, AXP8XX_REG_ID_FLDO1, AXP8XX_REG_ID_FLDO2, AXP813_REG_ID_FLDO3, AXP8XX_REG_ID_GPIO0_LDO, AXP8XX_REG_ID_GPIO1_LDO, }; static struct axp8xx_regdef axp803_regdefs[] = { { .id = AXP803_REG_ID_DC1SW, .name = "dc1sw", .enable_reg = AXP_POWERCTL2, .enable_mask = (uint8_t) AXP_POWERCTL2_DC1SW, .enable_value = AXP_POWERCTL2_DC1SW, }, }; static struct axp8xx_regdef axp813_regdefs[] = { { .id = AXP813_REG_ID_DCDC7, .name = "dcdc7", .enable_reg = AXP_POWERCTL1, .enable_mask = (uint8_t) AXP_POWERCTL1_DCDC7, .enable_value = AXP_POWERCTL1_DCDC7, .voltage_reg = AXP_VOLTCTL_DCDC7, .voltage_min = 600, .voltage_max = 1520, .voltage_step1 = 10, .voltage_nstep1 = 50, .voltage_step2 = 20, .voltage_nstep2 = 21, }, }; static struct axp8xx_regdef axp8xx_common_regdefs[] = { { .id = AXP8XX_REG_ID_DCDC1, .name = "dcdc1", .enable_reg = AXP_POWERCTL1, .enable_mask = (uint8_t) AXP_POWERCTL1_DCDC1, .enable_value = AXP_POWERCTL1_DCDC1, .voltage_reg = AXP_VOLTCTL_DCDC1, .voltage_min = 1600, .voltage_max = 3400, .voltage_step1 = 100, .voltage_nstep1 = 18, }, { .id = AXP8XX_REG_ID_DCDC2, .name = "dcdc2", .enable_reg = AXP_POWERCTL1, .enable_mask = (uint8_t) AXP_POWERCTL1_DCDC2, .enable_value = AXP_POWERCTL1_DCDC2, .voltage_reg = AXP_VOLTCTL_DCDC2, .voltage_min = 500, .voltage_max = 1300, .voltage_step1 = 10, .voltage_nstep1 = 70, .voltage_step2 = 20, .voltage_nstep2 = 5, }, { .id = AXP8XX_REG_ID_DCDC3, .name = "dcdc3", .enable_reg = AXP_POWERCTL1, .enable_mask = (uint8_t) AXP_POWERCTL1_DCDC3, .enable_value = AXP_POWERCTL1_DCDC3, .voltage_reg = AXP_VOLTCTL_DCDC3, .voltage_min = 500, .voltage_max = 1300, .voltage_step1 = 10, .voltage_nstep1 = 70, .voltage_step2 = 20, .voltage_nstep2 = 5, }, { .id = AXP8XX_REG_ID_DCDC4, .name = "dcdc4", .enable_reg = AXP_POWERCTL1, .enable_mask = (uint8_t) AXP_POWERCTL1_DCDC4, .enable_value = AXP_POWERCTL1_DCDC4, .voltage_reg = AXP_VOLTCTL_DCDC4, .voltage_min = 500, .voltage_max = 1300, .voltage_step1 = 10, .voltage_nstep1 = 70, .voltage_step2 = 20, .voltage_nstep2 = 5, }, { .id = AXP8XX_REG_ID_DCDC5, .name = "dcdc5", .enable_reg = AXP_POWERCTL1, .enable_mask = (uint8_t) AXP_POWERCTL1_DCDC5, .enable_value = AXP_POWERCTL1_DCDC5, .voltage_reg = AXP_VOLTCTL_DCDC5, .voltage_min = 800, .voltage_max = 1840, .voltage_step1 = 10, .voltage_nstep1 = 42, .voltage_step2 = 20, .voltage_nstep2 = 36, }, { .id = AXP8XX_REG_ID_DCDC6, .name = "dcdc6", .enable_reg = AXP_POWERCTL1, .enable_mask = (uint8_t) AXP_POWERCTL1_DCDC6, .enable_value = AXP_POWERCTL1_DCDC6, .voltage_reg = AXP_VOLTCTL_DCDC6, .voltage_min = 600, .voltage_max = 1520, .voltage_step1 = 10, .voltage_nstep1 = 50, .voltage_step2 = 20, .voltage_nstep2 = 21, }, { .id = AXP8XX_REG_ID_DLDO1, .name = "dldo1", .enable_reg = AXP_POWERCTL2, .enable_mask = (uint8_t) AXP_POWERCTL2_DLDO1, .enable_value = AXP_POWERCTL2_DLDO1, .voltage_reg = AXP_VOLTCTL_DLDO1, .voltage_min = 700, .voltage_max = 3300, .voltage_step1 = 100, .voltage_nstep1 = 26, }, { .id = AXP8XX_REG_ID_DLDO2, .name = "dldo2", .enable_reg = AXP_POWERCTL2, .enable_mask = (uint8_t) AXP_POWERCTL2_DLDO2, .enable_value = AXP_POWERCTL2_DLDO2, .voltage_reg = AXP_VOLTCTL_DLDO2, .voltage_min = 700, .voltage_max = 4200, .voltage_step1 = 100, .voltage_nstep1 = 27, .voltage_step2 = 200, .voltage_nstep2 = 4, }, { .id = AXP8XX_REG_ID_DLDO3, .name = "dldo3", .enable_reg = AXP_POWERCTL2, .enable_mask = (uint8_t) AXP_POWERCTL2_DLDO3, .enable_value = AXP_POWERCTL2_DLDO3, .voltage_reg = AXP_VOLTCTL_DLDO3, .voltage_min = 700, .voltage_max = 3300, .voltage_step1 = 100, .voltage_nstep1 = 26, }, { .id = AXP8XX_REG_ID_DLDO4, .name = "dldo4", .enable_reg = AXP_POWERCTL2, .enable_mask = (uint8_t) AXP_POWERCTL2_DLDO4, .enable_value = AXP_POWERCTL2_DLDO4, .voltage_reg = AXP_VOLTCTL_DLDO4, .voltage_min = 700, .voltage_max = 3300, .voltage_step1 = 100, .voltage_nstep1 = 26, }, { .id = AXP8XX_REG_ID_ALDO1, .name = "aldo1", .enable_reg = AXP_POWERCTL3, .enable_mask = (uint8_t) AXP_POWERCTL3_ALDO1, .enable_value = AXP_POWERCTL3_ALDO1, .voltage_min = 700, .voltage_max = 3300, .voltage_step1 = 100, .voltage_nstep1 = 26, }, { .id = AXP8XX_REG_ID_ALDO2, .name = "aldo2", .enable_reg = AXP_POWERCTL3, .enable_mask = (uint8_t) AXP_POWERCTL3_ALDO2, .enable_value = AXP_POWERCTL3_ALDO2, .voltage_min = 700, .voltage_max = 3300, .voltage_step1 = 100, .voltage_nstep1 = 26, }, { .id = AXP8XX_REG_ID_ALDO3, .name = "aldo3", .enable_reg = AXP_POWERCTL3, .enable_mask = (uint8_t) AXP_POWERCTL3_ALDO3, .enable_value = AXP_POWERCTL3_ALDO3, .voltage_min = 700, .voltage_max = 3300, .voltage_step1 = 100, .voltage_nstep1 = 26, }, { .id = AXP8XX_REG_ID_ELDO1, .name = "eldo1", .enable_reg = AXP_POWERCTL2, .enable_mask = (uint8_t) AXP_POWERCTL2_ELDO1, .enable_value = AXP_POWERCTL2_ELDO1, .voltage_min = 700, .voltage_max = 1900, .voltage_step1 = 50, .voltage_nstep1 = 24, }, { .id = AXP8XX_REG_ID_ELDO2, .name = "eldo2", .enable_reg = AXP_POWERCTL2, .enable_mask = (uint8_t) AXP_POWERCTL2_ELDO2, .enable_value = AXP_POWERCTL2_ELDO2, .voltage_min = 700, .voltage_max = 1900, .voltage_step1 = 50, .voltage_nstep1 = 24, }, { .id = AXP8XX_REG_ID_ELDO3, .name = "eldo3", .enable_reg = AXP_POWERCTL2, .enable_mask = (uint8_t) AXP_POWERCTL2_ELDO3, .enable_value = AXP_POWERCTL2_ELDO3, .voltage_min = 700, .voltage_max = 1900, .voltage_step1 = 50, .voltage_nstep1 = 24, }, { .id = AXP8XX_REG_ID_FLDO1, .name = "fldo1", .enable_reg = AXP_POWERCTL3, .enable_mask = (uint8_t) AXP_POWERCTL3_FLDO1, .enable_value = AXP_POWERCTL3_FLDO1, .voltage_min = 700, .voltage_max = 1450, .voltage_step1 = 50, .voltage_nstep1 = 15, }, { .id = AXP8XX_REG_ID_FLDO2, .name = "fldo2", .enable_reg = AXP_POWERCTL3, .enable_mask = (uint8_t) AXP_POWERCTL3_FLDO2, .enable_value = AXP_POWERCTL3_FLDO2, .voltage_min = 700, .voltage_max = 1450, .voltage_step1 = 50, .voltage_nstep1 = 15, }, { .id = AXP8XX_REG_ID_GPIO0_LDO, .name = "ldo-io0", .enable_reg = AXP_GPIO0_CTRL, .enable_mask = (uint8_t) AXP_GPIO_FUNC, .enable_value = AXP_GPIO_FUNC_LDO_ON, .disable_value = AXP_GPIO_FUNC_LDO_OFF, .voltage_reg = AXP_GPIO0LDO_CTRL, .voltage_min = 700, .voltage_max = 3300, .voltage_step1 = 100, .voltage_nstep1 = 26, }, { .id = AXP8XX_REG_ID_GPIO1_LDO, .name = "ldo-io1", .enable_reg = AXP_GPIO1_CTRL, .enable_mask = (uint8_t) AXP_GPIO_FUNC, .enable_value = AXP_GPIO_FUNC_LDO_ON, .disable_value = AXP_GPIO_FUNC_LDO_OFF, .voltage_reg = AXP_GPIO1LDO_CTRL, .voltage_min = 700, .voltage_max = 3300, .voltage_step1 = 100, .voltage_nstep1 = 26, }, }; enum axp8xx_sensor { AXP_SENSOR_ACIN_PRESENT, AXP_SENSOR_VBUS_PRESENT, AXP_SENSOR_BATT_PRESENT, AXP_SENSOR_BATT_CHARGING, AXP_SENSOR_BATT_CHARGE_STATE, AXP_SENSOR_BATT_VOLTAGE, AXP_SENSOR_BATT_CHARGE_CURRENT, AXP_SENSOR_BATT_DISCHARGE_CURRENT, AXP_SENSOR_BATT_CAPACITY_PERCENT, AXP_SENSOR_BATT_MAXIMUM_CAPACITY, AXP_SENSOR_BATT_CURRENT_CAPACITY, }; enum battery_capacity_state { BATT_CAPACITY_NORMAL = 1, /* normal cap in battery */ BATT_CAPACITY_WARNING, /* warning cap in battery */ BATT_CAPACITY_CRITICAL, /* critical cap in battery */ BATT_CAPACITY_HIGH, /* high cap in battery */ BATT_CAPACITY_MAX, /* maximum cap in battery */ BATT_CAPACITY_LOW /* low cap in battery */ }; struct axp8xx_sensors { int id; const char *name; const char *desc; const char *format; }; static const struct axp8xx_sensors axp8xx_common_sensors[] = { { .id = AXP_SENSOR_ACIN_PRESENT, .name = "acin", .format = "I", .desc = "ACIN Present", }, { .id = AXP_SENSOR_VBUS_PRESENT, .name = "vbus", .format = "I", .desc = "VBUS Present", }, { .id = AXP_SENSOR_BATT_PRESENT, .name = "bat", .format = "I", .desc = "Battery Present", }, { .id = AXP_SENSOR_BATT_CHARGING, .name = "batcharging", .format = "I", .desc = "Battery Charging", }, { .id = AXP_SENSOR_BATT_CHARGE_STATE, .name = "batchargestate", .format = "I", .desc = "Battery Charge State", }, { .id = AXP_SENSOR_BATT_VOLTAGE, .name = "batvolt", .format = "I", .desc = "Battery Voltage", }, { .id = AXP_SENSOR_BATT_CHARGE_CURRENT, .name = "batchargecurrent", .format = "I", .desc = "Average Battery Charging Current", }, { .id = AXP_SENSOR_BATT_DISCHARGE_CURRENT, .name = "batdischargecurrent", .format = "I", .desc = "Average Battery Discharging Current", }, { .id = AXP_SENSOR_BATT_CAPACITY_PERCENT, .name = "batcapacitypercent", .format = "I", .desc = "Battery Capacity Percentage", }, { .id = AXP_SENSOR_BATT_MAXIMUM_CAPACITY, .name = "batmaxcapacity", .format = "I", .desc = "Battery Maximum Capacity", }, { .id = AXP_SENSOR_BATT_CURRENT_CAPACITY, .name = "batcurrentcapacity", .format = "I", .desc = "Battery Current Capacity", }, }; struct axp8xx_config { const char *name; int batsense_step; /* uV */ int charge_step; /* uA */ int discharge_step; /* uA */ int maxcap_step; /* uAh */ int coulomb_step; /* uAh */ }; static struct axp8xx_config axp803_config = { .name = "AXP803", .batsense_step = 1100, .charge_step = 1000, .discharge_step = 1000, .maxcap_step = 1456, .coulomb_step = 1456, }; struct axp8xx_softc; struct axp8xx_reg_sc { struct regnode *regnode; device_t base_dev; struct axp8xx_regdef *def; phandle_t xref; struct regnode_std_param *param; }; struct axp8xx_softc { struct resource *res; uint16_t addr; void *ih; device_t gpiodev; struct mtx mtx; int busy; int type; /* Configs */ const struct axp8xx_config *config; /* Sensors */ const struct axp8xx_sensors *sensors; int nsensors; /* Regulators */ struct axp8xx_reg_sc **regs; int nregs; /* Warning, shutdown thresholds */ int warn_thres; int shut_thres; }; #define AXP_LOCK(sc) mtx_lock(&(sc)->mtx) #define AXP_UNLOCK(sc) mtx_unlock(&(sc)->mtx) static int axp8xx_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) { struct axp8xx_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 axp8xx_write(device_t dev, uint8_t reg, uint8_t val) { struct axp8xx_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 int axp8xx_regnode_enable(struct regnode *regnode, bool enable, int *udelay) { struct axp8xx_reg_sc *sc; uint8_t val; sc = regnode_get_softc(regnode); if (bootverbose) device_printf(sc->base_dev, "%sable %s (%s)\n", enable ? "En" : "Dis", regnode_get_name(regnode), sc->def->name); axp8xx_read(sc->base_dev, sc->def->enable_reg, &val, 1); val &= ~sc->def->enable_mask; if (enable) val |= sc->def->enable_value; else { if (sc->def->disable_value) val |= sc->def->disable_value; else val &= ~sc->def->enable_value; } axp8xx_write(sc->base_dev, sc->def->enable_reg, val); *udelay = 0; return (0); } static void axp8xx_regnode_reg_to_voltage(struct axp8xx_reg_sc *sc, uint8_t val, int *uv) { if (val < sc->def->voltage_nstep1) *uv = sc->def->voltage_min + val * sc->def->voltage_step1; else *uv = sc->def->voltage_min + (sc->def->voltage_nstep1 * sc->def->voltage_step1) + ((val - sc->def->voltage_nstep1) * sc->def->voltage_step2); *uv *= 1000; } static int axp8xx_regnode_voltage_to_reg(struct axp8xx_reg_sc *sc, int min_uvolt, int max_uvolt, uint8_t *val) { uint8_t nval; int nstep, uvolt; nval = 0; uvolt = sc->def->voltage_min * 1000; for (nstep = 0; nstep < sc->def->voltage_nstep1 && uvolt < min_uvolt; nstep++) { ++nval; uvolt += (sc->def->voltage_step1 * 1000); } for (nstep = 0; nstep < sc->def->voltage_nstep2 && uvolt < min_uvolt; nstep++) { ++nval; uvolt += (sc->def->voltage_step2 * 1000); } if (uvolt > max_uvolt) return (EINVAL); *val = nval; return (0); } static int axp8xx_regnode_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt, int *udelay) { struct axp8xx_reg_sc *sc; uint8_t val; sc = regnode_get_softc(regnode); if (bootverbose) device_printf(sc->base_dev, "Setting %s (%s) to %d<->%d\n", regnode_get_name(regnode), sc->def->name, min_uvolt, max_uvolt); if (sc->def->voltage_step1 == 0) return (ENXIO); if (axp8xx_regnode_voltage_to_reg(sc, min_uvolt, max_uvolt, &val) != 0) return (ERANGE); axp8xx_write(sc->base_dev, sc->def->voltage_reg, val); *udelay = 0; return (0); } static int axp8xx_regnode_get_voltage(struct regnode *regnode, int *uvolt) { struct axp8xx_reg_sc *sc; uint8_t val; sc = regnode_get_softc(regnode); if (!sc->def->voltage_step1 || !sc->def->voltage_step2) return (ENXIO); axp8xx_read(sc->base_dev, sc->def->voltage_reg, &val, 1); axp8xx_regnode_reg_to_voltage(sc, val & AXP_VOLTCTL_MASK, uvolt); return (0); } static regnode_method_t axp8xx_regnode_methods[] = { /* Regulator interface */ REGNODEMETHOD(regnode_enable, axp8xx_regnode_enable), REGNODEMETHOD(regnode_set_voltage, axp8xx_regnode_set_voltage), REGNODEMETHOD(regnode_get_voltage, axp8xx_regnode_get_voltage), + REGNODEMETHOD(regnode_check_voltage, regnode_method_check_voltage), REGNODEMETHOD_END }; DEFINE_CLASS_1(axp8xx_regnode, axp8xx_regnode_class, axp8xx_regnode_methods, sizeof(struct axp8xx_reg_sc), regnode_class); static void axp8xx_shutdown(void *devp, int howto) { device_t dev; if ((howto & RB_POWEROFF) == 0) return; dev = devp; if (bootverbose) device_printf(dev, "Shutdown Axp8xx\n"); axp8xx_write(dev, AXP_POWERBAT, AXP_POWERBAT_SHUTDOWN); } static int axp8xx_sysctl_chargecurrent(SYSCTL_HANDLER_ARGS) { device_t dev = arg1; uint8_t data; int val, error; error = axp8xx_read(dev, AXP_CHARGERCTL1, &data, 1); if (error != 0) return (error); if (bootverbose) device_printf(dev, "Raw CHARGECTL1 val: 0x%0x\n", data); val = (data & AXP_CHARGERCTL1_CMASK); error = sysctl_handle_int(oidp, &val, 0, req); if (error || !req->newptr) /* error || read request */ return (error); if ((val < AXP_CHARGERCTL1_MIN) || (val > AXP_CHARGERCTL1_MAX)) return (EINVAL); val |= (data & (AXP_CHARGERCTL1_CMASK << 4)); axp8xx_write(dev, AXP_CHARGERCTL1, val); return (0); } static int axp8xx_sysctl(SYSCTL_HANDLER_ARGS) { struct axp8xx_softc *sc; device_t dev = arg1; enum axp8xx_sensor sensor = arg2; const struct axp8xx_config *c; uint8_t data; int val, i, found, batt_val; uint8_t lo, hi; sc = device_get_softc(dev); c = sc->config; for (found = 0, i = 0; i < sc->nsensors; i++) { if (sc->sensors[i].id == sensor) { found = 1; break; } } if (found == 0) return (ENOENT); switch (sensor) { case AXP_SENSOR_ACIN_PRESENT: if (axp8xx_read(dev, AXP_POWERSRC, &data, 1) == 0) val = !!(data & AXP_POWERSRC_ACIN); break; case AXP_SENSOR_VBUS_PRESENT: if (axp8xx_read(dev, AXP_POWERSRC, &data, 1) == 0) val = !!(data & AXP_POWERSRC_VBUS); break; case AXP_SENSOR_BATT_PRESENT: if (axp8xx_read(dev, AXP_POWERMODE, &data, 1) == 0) { if (data & AXP_POWERMODE_BAT_VALID) val = !!(data & AXP_POWERMODE_BAT_PRESENT); } break; case AXP_SENSOR_BATT_CHARGING: if (axp8xx_read(dev, AXP_POWERMODE, &data, 1) == 0) val = !!(data & AXP_POWERMODE_BAT_CHARGING); break; case AXP_SENSOR_BATT_CHARGE_STATE: if (axp8xx_read(dev, AXP_BAT_CAP, &data, 1) == 0 && (data & AXP_BAT_CAP_VALID) != 0) { batt_val = (data & AXP_BAT_CAP_PERCENT); if (batt_val <= sc->shut_thres) val = BATT_CAPACITY_CRITICAL; else if (batt_val <= sc->warn_thres) val = BATT_CAPACITY_WARNING; else val = BATT_CAPACITY_NORMAL; } break; case AXP_SENSOR_BATT_CAPACITY_PERCENT: if (axp8xx_read(dev, AXP_BAT_CAP, &data, 1) == 0 && (data & AXP_BAT_CAP_VALID) != 0) val = (data & AXP_BAT_CAP_PERCENT); break; case AXP_SENSOR_BATT_VOLTAGE: if (axp8xx_read(dev, AXP_BATSENSE_HI, &hi, 1) == 0 && axp8xx_read(dev, AXP_BATSENSE_LO, &lo, 1) == 0) { val = (AXP_SENSOR_BAT_H(hi) | AXP_SENSOR_BAT_L(lo)); val *= c->batsense_step; } break; case AXP_SENSOR_BATT_CHARGE_CURRENT: if (axp8xx_read(dev, AXP_POWERSRC, &data, 1) == 0 && (data & AXP_POWERSRC_CHARING) != 0 && axp8xx_read(dev, AXP_BATCHG_HI, &hi, 1) == 0 && axp8xx_read(dev, AXP_BATCHG_LO, &lo, 1) == 0) { val = (AXP_SENSOR_BAT_H(hi) | AXP_SENSOR_BAT_L(lo)); val *= c->charge_step; } break; case AXP_SENSOR_BATT_DISCHARGE_CURRENT: if (axp8xx_read(dev, AXP_POWERSRC, &data, 1) == 0 && (data & AXP_POWERSRC_CHARING) == 0 && axp8xx_read(dev, AXP_BATDISCHG_HI, &hi, 1) == 0 && axp8xx_read(dev, AXP_BATDISCHG_LO, &lo, 1) == 0) { val = (AXP_SENSOR_BAT_H(hi) | AXP_SENSOR_BAT_L(lo)); val *= c->discharge_step; } break; case AXP_SENSOR_BATT_MAXIMUM_CAPACITY: if (axp8xx_read(dev, AXP_BAT_MAX_CAP_HI, &hi, 1) == 0 && axp8xx_read(dev, AXP_BAT_MAX_CAP_LO, &lo, 1) == 0) { val = AXP_SENSOR_COULOMB(hi, lo); val *= c->maxcap_step; } break; case AXP_SENSOR_BATT_CURRENT_CAPACITY: if (axp8xx_read(dev, AXP_BAT_COULOMB_HI, &hi, 1) == 0 && axp8xx_read(dev, AXP_BAT_COULOMB_LO, &lo, 1) == 0) { val = AXP_SENSOR_COULOMB(hi, lo); val *= c->coulomb_step; } break; } return sysctl_handle_opaque(oidp, &val, sizeof(val), req); } static void axp8xx_intr(void *arg) { device_t dev; uint8_t val; int error; dev = arg; error = axp8xx_read(dev, AXP_IRQSTAT1, &val, 1); if (error != 0) return; if (val) { if (bootverbose) device_printf(dev, "AXP_IRQSTAT1 val: %x\n", val); if (val & AXP_IRQSTAT1_ACIN_HI) devctl_notify("PMU", "AC", "plugged", NULL); if (val & AXP_IRQSTAT1_ACIN_LO) devctl_notify("PMU", "AC", "unplugged", NULL); if (val & AXP_IRQSTAT1_VBUS_HI) devctl_notify("PMU", "USB", "plugged", NULL); if (val & AXP_IRQSTAT1_VBUS_LO) devctl_notify("PMU", "USB", "unplugged", NULL); /* Acknowledge */ axp8xx_write(dev, AXP_IRQSTAT1, val); } error = axp8xx_read(dev, AXP_IRQSTAT2, &val, 1); if (error != 0) return; if (val) { if (bootverbose) device_printf(dev, "AXP_IRQSTAT2 val: %x\n", val); if (val & AXP_IRQSTAT2_BATCHGD) devctl_notify("PMU", "Battery", "charged", NULL); if (val & AXP_IRQSTAT2_BATCHGC) devctl_notify("PMU", "Battery", "charging", NULL); if (val & AXP_IRQSTAT2_BAT_NO) devctl_notify("PMU", "Battery", "absent", NULL); if (val & AXP_IRQSTAT2_BAT_IN) devctl_notify("PMU", "Battery", "plugged", NULL); /* Acknowledge */ axp8xx_write(dev, AXP_IRQSTAT2, val); } error = axp8xx_read(dev, AXP_IRQSTAT3, &val, 1); if (error != 0) return; if (val) { /* Acknowledge */ axp8xx_write(dev, AXP_IRQSTAT3, val); } error = axp8xx_read(dev, AXP_IRQSTAT4, &val, 1); if (error != 0) return; if (val) { if (bootverbose) device_printf(dev, "AXP_IRQSTAT4 val: %x\n", val); if (val & AXP_IRQSTAT4_BATLVL_LO0) devctl_notify("PMU", "Battery", "shutdown threshold", NULL); if (val & AXP_IRQSTAT4_BATLVL_LO1) devctl_notify("PMU", "Battery", "warning threshold", NULL); /* Acknowledge */ axp8xx_write(dev, AXP_IRQSTAT4, val); } error = axp8xx_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 */ axp8xx_write(dev, AXP_IRQSTAT5, val); } error = axp8xx_read(dev, AXP_IRQSTAT6, &val, 1); if (error != 0) return; if (val) { /* Acknowledge */ axp8xx_write(dev, AXP_IRQSTAT6, val); } } static device_t axp8xx_gpio_get_bus(device_t dev) { struct axp8xx_softc *sc; sc = device_get_softc(dev); return (sc->gpiodev); } static int axp8xx_gpio_pin_max(device_t dev, int *maxpin) { *maxpin = nitems(axp8xx_pins) - 1; return (0); } static int axp8xx_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { if (pin >= nitems(axp8xx_pins)) return (EINVAL); snprintf(name, GPIOMAXNAME, "%s", axp8xx_pins[pin].name); return (0); } static int axp8xx_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { if (pin >= nitems(axp8xx_pins)) return (EINVAL); *caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT; return (0); } static int axp8xx_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct axp8xx_softc *sc; uint8_t data, func; int error; if (pin >= nitems(axp8xx_pins)) return (EINVAL); sc = device_get_softc(dev); AXP_LOCK(sc); error = axp8xx_read(dev, axp8xx_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; } AXP_UNLOCK(sc); return (error); } static int axp8xx_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct axp8xx_softc *sc; uint8_t data; int error; if (pin >= nitems(axp8xx_pins)) return (EINVAL); sc = device_get_softc(dev); AXP_LOCK(sc); error = axp8xx_read(dev, axp8xx_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 = axp8xx_write(dev, axp8xx_pins[pin].ctrl_reg, data); } AXP_UNLOCK(sc); return (error); } static int axp8xx_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct axp8xx_softc *sc; uint8_t data, func; int error; if (pin >= nitems(axp8xx_pins)) return (EINVAL); sc = device_get_softc(dev); AXP_LOCK(sc); error = axp8xx_read(dev, axp8xx_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 = axp8xx_read(dev, AXP_GPIO_SIGBIT, &data, 1); if (error == 0) *val = (data & (1 << pin)) ? 1 : 0; break; default: error = EIO; break; } } AXP_UNLOCK(sc); return (error); } static int axp8xx_gpio_pin_set(device_t dev, uint32_t pin, unsigned int val) { struct axp8xx_softc *sc; uint8_t data, func; int error; if (pin >= nitems(axp8xx_pins)) return (EINVAL); sc = device_get_softc(dev); AXP_LOCK(sc); error = axp8xx_read(dev, axp8xx_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 = axp8xx_write(dev, axp8xx_pins[pin].ctrl_reg, data); AXP_UNLOCK(sc); return (error); } static int axp8xx_gpio_pin_toggle(device_t dev, uint32_t pin) { struct axp8xx_softc *sc; uint8_t data, func; int error; if (pin >= nitems(axp8xx_pins)) return (EINVAL); sc = device_get_softc(dev); AXP_LOCK(sc); error = axp8xx_read(dev, axp8xx_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 = axp8xx_write(dev, axp8xx_pins[pin].ctrl_reg, data); AXP_UNLOCK(sc); return (error); } static int axp8xx_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(axp8xx_pins)) return (EINVAL); *pin = gpios[0]; *flags = gpios[1]; return (0); } static phandle_t axp8xx_get_node(device_t dev, device_t bus) { return (ofw_bus_get_node(dev)); } static struct axp8xx_reg_sc * axp8xx_reg_attach(device_t dev, phandle_t node, struct axp8xx_regdef *def) { struct axp8xx_reg_sc *reg_sc; struct regnode_init_def initdef; struct regnode *regnode; memset(&initdef, 0, sizeof(initdef)); if (regulator_parse_ofw_stdparam(dev, node, &initdef) != 0) return (NULL); if (initdef.std_param.min_uvolt == 0) initdef.std_param.min_uvolt = def->voltage_min * 1000; if (initdef.std_param.max_uvolt == 0) initdef.std_param.max_uvolt = def->voltage_max * 1000; initdef.id = def->id; initdef.ofw_node = node; regnode = regnode_create(dev, &axp8xx_regnode_class, &initdef); if (regnode == NULL) { device_printf(dev, "cannot create regulator\n"); return (NULL); } reg_sc = regnode_get_softc(regnode); reg_sc->regnode = regnode; reg_sc->base_dev = dev; reg_sc->def = def; reg_sc->xref = OF_xref_from_node(node); reg_sc->param = regnode_get_stdparam(regnode); regnode_register(regnode); return (reg_sc); } static int axp8xx_regdev_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells, intptr_t *num) { struct axp8xx_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->nregs; i++) { if (sc->regs[i] == NULL) continue; if (sc->regs[i]->xref == xref) { *num = sc->regs[i]->def->id; return (0); } } return (ENXIO); } static int axp8xx_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { case AXP803: device_set_desc(dev, "X-Powers AXP803 Power Management Unit"); break; case AXP813: device_set_desc(dev, "X-Powers AXP813 Power Management Unit"); break; default: return (ENXIO); } return (BUS_PROBE_DEFAULT); } static int axp8xx_attach(device_t dev) { struct axp8xx_softc *sc; struct axp8xx_reg_sc *reg; uint8_t chip_id, val; phandle_t rnode, child; int error, i; 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, axp8xx_spec, &sc->res); if (error != 0) { device_printf(dev, "cannot allocate resources for device\n"); return (error); } if (bootverbose) { axp8xx_read(dev, AXP_ICTYPE, &chip_id, 1); device_printf(dev, "chip ID 0x%02x\n", chip_id); } sc->nregs = nitems(axp8xx_common_regdefs); sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; switch (sc->type) { case AXP803: sc->nregs += nitems(axp803_regdefs); break; case AXP813: sc->nregs += nitems(axp813_regdefs); break; } sc->config = &axp803_config; sc->sensors = axp8xx_common_sensors; sc->nsensors = nitems(axp8xx_common_sensors); sc->regs = malloc(sizeof(struct axp8xx_reg_sc *) * sc->nregs, M_AXP8XX_REG, M_WAITOK | M_ZERO); /* Attach known regulators that exist in the DT */ rnode = ofw_bus_find_child(ofw_bus_get_node(dev), "regulators"); if (rnode > 0) { for (i = 0; i < sc->nregs; i++) { char *regname; struct axp8xx_regdef *regdef; if (i <= nitems(axp8xx_common_regdefs)) { regname = axp8xx_common_regdefs[i].name; regdef = &axp8xx_common_regdefs[i]; } else { int off; off = i - nitems(axp8xx_common_regdefs); switch (sc->type) { case AXP803: regname = axp803_regdefs[off].name; regdef = &axp803_regdefs[off]; break; case AXP813: regname = axp813_regdefs[off].name; regdef = &axp813_regdefs[off]; break; } } child = ofw_bus_find_child(rnode, regname); if (child == 0) continue; reg = axp8xx_reg_attach(dev, child, regdef); if (reg == NULL) { device_printf(dev, "cannot attach regulator %s\n", regname); continue; } sc->regs[i] = reg; } } /* Add sensors */ for (i = 0; i < sc->nsensors; i++) { SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, sc->sensors[i].name, CTLTYPE_INT | CTLFLAG_RD, dev, sc->sensors[i].id, axp8xx_sysctl, sc->sensors[i].format, sc->sensors[i].desc); } SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "batchargecurrentstep", CTLTYPE_INT | CTLFLAG_RW, dev, 0, axp8xx_sysctl_chargecurrent, "I", "Battery Charging Current Step, " "0: 200mA, 1: 400mA, 2: 600mA, 3: 800mA, " "4: 1000mA, 5: 1200mA, 6: 1400mA, 7: 1600mA, " "8: 1800mA, 9: 2000mA, 10: 2200mA, 11: 2400mA, " "12: 2600mA, 13: 2800mA"); /* Get thresholds */ if (axp8xx_read(dev, AXP_BAT_CAP_WARN, &val, 1) == 0) { sc->warn_thres = (val & AXP_BAT_CAP_WARN_LV1) >> 4; sc->warn_thres += AXP_BAP_CAP_WARN_LV1BASE; sc->shut_thres = (val & AXP_BAT_CAP_WARN_LV2); if (bootverbose) { device_printf(dev, "Raw reg val: 0x%02x\n", val); device_printf(dev, "Warning threshold: 0x%02x\n", sc->warn_thres); device_printf(dev, "Shutdown threshold: 0x%02x\n", sc->shut_thres); } } /* Enable interrupts */ axp8xx_write(dev, AXP_IRQEN1, AXP_IRQEN1_VBUS_LO | AXP_IRQEN1_VBUS_HI | AXP_IRQEN1_ACIN_LO | AXP_IRQEN1_ACIN_HI); axp8xx_write(dev, AXP_IRQEN2, AXP_IRQEN2_BATCHGD | AXP_IRQEN2_BATCHGC | AXP_IRQEN2_BAT_NO | AXP_IRQEN2_BAT_IN); axp8xx_write(dev, AXP_IRQEN3, 0); axp8xx_write(dev, AXP_IRQEN4, AXP_IRQEN4_BATLVL_LO0 | AXP_IRQEN4_BATLVL_LO1); axp8xx_write(dev, AXP_IRQEN5, AXP_IRQEN5_POKSIRQ | AXP_IRQEN5_POKLIRQ); axp8xx_write(dev, AXP_IRQEN6, 0); /* Install interrupt handler */ error = bus_setup_intr(dev, sc->res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, axp8xx_intr, dev, &sc->ih); if (error != 0) { device_printf(dev, "cannot setup interrupt handler\n"); return (error); } EVENTHANDLER_REGISTER(shutdown_final, axp8xx_shutdown, dev, SHUTDOWN_PRI_LAST); sc->gpiodev = gpiobus_attach_bus(dev); return (0); } static device_method_t axp8xx_methods[] = { /* Device interface */ DEVMETHOD(device_probe, axp8xx_probe), DEVMETHOD(device_attach, axp8xx_attach), /* GPIO interface */ DEVMETHOD(gpio_get_bus, axp8xx_gpio_get_bus), DEVMETHOD(gpio_pin_max, axp8xx_gpio_pin_max), DEVMETHOD(gpio_pin_getname, axp8xx_gpio_pin_getname), DEVMETHOD(gpio_pin_getcaps, axp8xx_gpio_pin_getcaps), DEVMETHOD(gpio_pin_getflags, axp8xx_gpio_pin_getflags), DEVMETHOD(gpio_pin_setflags, axp8xx_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, axp8xx_gpio_pin_get), DEVMETHOD(gpio_pin_set, axp8xx_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, axp8xx_gpio_pin_toggle), DEVMETHOD(gpio_map_gpios, axp8xx_gpio_map_gpios), /* Regdev interface */ DEVMETHOD(regdev_map, axp8xx_regdev_map), /* OFW bus interface */ DEVMETHOD(ofw_bus_get_node, axp8xx_get_node), DEVMETHOD_END }; static driver_t axp8xx_driver = { "axp8xx_pmu", axp8xx_methods, sizeof(struct axp8xx_softc), }; static devclass_t axp8xx_devclass; extern devclass_t ofwgpiobus_devclass, gpioc_devclass; extern driver_t ofw_gpiobus_driver, gpioc_driver; EARLY_DRIVER_MODULE(axp8xx, iicbus, axp8xx_driver, axp8xx_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); EARLY_DRIVER_MODULE(ofw_gpiobus, axp8xx_pmu, ofw_gpiobus_driver, ofwgpiobus_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); DRIVER_MODULE(gpioc, axp8xx_pmu, gpioc_driver, gpioc_devclass, 0, 0); MODULE_VERSION(axp8xx, 1); MODULE_DEPEND(axp8xx, iicbus, 1, 1, 1); SIMPLEBUS_PNP_INFO(compat_data); Index: head/sys/arm64/rockchip/rk805.c =================================================================== --- head/sys/arm64/rockchip/rk805.c (revision 354395) +++ head/sys/arm64/rockchip/rk805.c (revision 354396) @@ -1,560 +1,561 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "regdev_if.h" MALLOC_DEFINE(M_RK805_REG, "RK805 regulator", "RK805 power regulator"); /* #define dprintf(sc, format, arg...) device_printf(sc->base_dev, "%s: " format, __func__, arg) */ #define dprintf(sc, format, arg...) enum rk_pmic_type { RK805 = 1, RK808, }; static struct ofw_compat_data compat_data[] = { {"rockchip,rk805", RK805}, {"rockchip,rk808", RK808}, {NULL, 0} }; struct rk805_regdef { intptr_t id; char *name; uint8_t enable_reg; uint8_t enable_mask; uint8_t voltage_reg; uint8_t voltage_mask; int voltage_min; int voltage_max; int voltage_step; int voltage_nstep; }; struct rk805_reg_sc { struct regnode *regnode; device_t base_dev; struct rk805_regdef *def; phandle_t xref; struct regnode_std_param *param; }; struct rk805_softc { device_t dev; struct mtx mtx; struct resource * res[1]; void * intrcookie; struct intr_config_hook intr_hook; enum rk_pmic_type type; struct rk805_reg_sc **regs; int nregs; }; static struct rk805_regdef rk805_regdefs[] = { { .id = RK805_DCDC1, .name = "DCDC_REG1", .enable_reg = RK805_DCDC_EN, .enable_mask = 0x11, .voltage_reg = RK805_DCDC1_ON_VSEL, .voltage_mask = 0x3F, .voltage_min = 712500, .voltage_max = 1450000, .voltage_step = 12500, .voltage_nstep = 64, }, { .id = RK805_DCDC2, .name = "DCDC_REG2", .enable_reg = RK805_DCDC_EN, .enable_mask = 0x22, .voltage_reg = RK805_DCDC2_ON_VSEL, .voltage_mask = 0x3F, .voltage_min = 712500, .voltage_max = 1450000, .voltage_step = 12500, .voltage_nstep = 64, }, { .id = RK805_DCDC3, .name = "DCDC_REG3", .enable_reg = RK805_DCDC_EN, .enable_mask = 0x44, }, { .id = RK805_DCDC4, .name = "DCDC_REG4", .enable_reg = RK805_DCDC_EN, .enable_mask = 0x88, .voltage_reg = RK805_DCDC4_ON_VSEL, .voltage_mask = 0x3F, .voltage_min = 800000, .voltage_max = 3500000, .voltage_step = 100000, .voltage_nstep = 28, }, { .id = RK805_LDO1, .name = "LDO_REG1", .enable_reg = RK805_LDO_EN, .enable_mask = 0x11, .voltage_reg = RK805_LDO1_ON_VSEL, .voltage_mask = 0x1F, .voltage_min = 800000, .voltage_max = 3400000, .voltage_step = 100000, .voltage_nstep = 27, }, { .id = RK805_LDO2, .name = "LDO_REG2", .enable_reg = RK805_LDO_EN, .enable_mask = 0x22, .voltage_reg = RK805_LDO2_ON_VSEL, .voltage_mask = 0x1F, .voltage_min = 800000, .voltage_max = 3400000, .voltage_step = 100000, .voltage_nstep = 27, }, { .id = RK805_LDO3, .name = "LDO_REG3", .enable_reg = RK805_LDO_EN, .enable_mask = 0x44, .voltage_reg = RK805_LDO3_ON_VSEL, .voltage_mask = 0x1F, .voltage_min = 800000, .voltage_max = 3400000, .voltage_step = 100000, .voltage_nstep = 27, }, }; static struct rk805_regdef rk808_regdefs[] = { { .id = RK805_DCDC1, .name = "DCDC_REG1", .enable_reg = RK805_DCDC_EN, .enable_mask = 0x1, .voltage_reg = RK805_DCDC1_ON_VSEL, .voltage_mask = 0x3F, .voltage_min = 712500, .voltage_max = 1500000, .voltage_step = 12500, .voltage_nstep = 64, }, { .id = RK805_DCDC2, .name = "DCDC_REG2", .enable_reg = RK805_DCDC_EN, .enable_mask = 0x2, .voltage_reg = RK805_DCDC2_ON_VSEL, .voltage_mask = 0x3F, .voltage_min = 712500, .voltage_max = 1500000, .voltage_step = 12500, .voltage_nstep = 64, }, { .id = RK805_DCDC3, .name = "DCDC_REG3", .enable_reg = RK805_DCDC_EN, .enable_mask = 0x4, }, { .id = RK805_DCDC4, .name = "DCDC_REG4", .enable_reg = RK805_DCDC_EN, .enable_mask = 0x8, .voltage_reg = RK805_DCDC4_ON_VSEL, .voltage_mask = 0xF, .voltage_min = 1800000, .voltage_max = 3300000, .voltage_step = 100000, .voltage_nstep = 16, }, }; static int rk805_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size) { int err; err = iicdev_readfrom(dev, reg, data, size, IIC_INTRWAIT); return (err); } static int rk805_write(device_t dev, uint8_t reg, uint8_t data) { return (iicdev_writeto(dev, reg, &data, 1, IIC_INTRWAIT)); } static int rk805_regnode_init(struct regnode *regnode) { return (0); } static int rk805_regnode_enable(struct regnode *regnode, bool enable, int *udelay) { struct rk805_reg_sc *sc; uint8_t val; sc = regnode_get_softc(regnode); dprintf(sc, "%sabling regulator %s\n", enable ? "En" : "Dis", sc->def->name); rk805_read(sc->base_dev, sc->def->enable_reg, &val, 1); if (enable) val |= sc->def->enable_mask; else val &= ~sc->def->enable_mask; rk805_write(sc->base_dev, sc->def->enable_reg, val); *udelay = 0; return (0); } static void rk805_regnode_reg_to_voltage(struct rk805_reg_sc *sc, uint8_t val, int *uv) { if (val < sc->def->voltage_nstep) *uv = sc->def->voltage_min + val * sc->def->voltage_step; else *uv = sc->def->voltage_min + (sc->def->voltage_nstep * sc->def->voltage_step); } static int rk805_regnode_voltage_to_reg(struct rk805_reg_sc *sc, int min_uvolt, int max_uvolt, uint8_t *val) { uint8_t nval; int nstep, uvolt; nval = 0; uvolt = sc->def->voltage_min; for (nstep = 0; nstep < sc->def->voltage_nstep && uvolt < min_uvolt; nstep++) { ++nval; uvolt += sc->def->voltage_step; } if (uvolt > max_uvolt) return (EINVAL); *val = nval; return (0); } static int rk805_regnode_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt, int *udelay) { struct rk805_reg_sc *sc; uint8_t val; int uvolt; sc = regnode_get_softc(regnode); if (!sc->def->voltage_step) return (ENXIO); dprintf(sc, "Setting %s to %d<->%d uvolts\n", sc->def->name, min_uvolt, max_uvolt); rk805_read(sc->base_dev, sc->def->voltage_reg, &val, 1); if (rk805_regnode_voltage_to_reg(sc, min_uvolt, max_uvolt, &val) != 0) return (ERANGE); rk805_write(sc->base_dev, sc->def->voltage_reg, val); rk805_read(sc->base_dev, sc->def->voltage_reg, &val, 1); *udelay = 0; rk805_regnode_reg_to_voltage(sc, val, &uvolt); dprintf(sc, "Regulator %s set to %d uvolt\n", sc->def->name, uvolt); return (0); } static int rk805_regnode_get_voltage(struct regnode *regnode, int *uvolt) { struct rk805_reg_sc *sc; uint8_t val; sc = regnode_get_softc(regnode); if (!sc->def->voltage_step) return (ENXIO); rk805_read(sc->base_dev, sc->def->voltage_reg, &val, 1); rk805_regnode_reg_to_voltage(sc, val & sc->def->voltage_mask, uvolt); dprintf(sc, "Regulator %s is at %d uvolt\n", sc->def->name, *uvolt); return (0); } static regnode_method_t rk805_regnode_methods[] = { /* Regulator interface */ REGNODEMETHOD(regnode_init, rk805_regnode_init), REGNODEMETHOD(regnode_enable, rk805_regnode_enable), REGNODEMETHOD(regnode_set_voltage, rk805_regnode_set_voltage), REGNODEMETHOD(regnode_get_voltage, rk805_regnode_get_voltage), + REGNODEMETHOD(regnode_check_voltage, regnode_method_check_voltage), REGNODEMETHOD_END }; DEFINE_CLASS_1(rk805_regnode, rk805_regnode_class, rk805_regnode_methods, sizeof(struct rk805_reg_sc), regnode_class); static struct rk805_reg_sc * rk805_reg_attach(device_t dev, phandle_t node, struct rk805_regdef *def) { struct rk805_reg_sc *reg_sc; struct regnode_init_def initdef; struct regnode *regnode; memset(&initdef, 0, sizeof(initdef)); if (regulator_parse_ofw_stdparam(dev, node, &initdef) != 0) { device_printf(dev, "cannot create regulator\n"); return (NULL); } if (initdef.std_param.min_uvolt == 0) initdef.std_param.min_uvolt = def->voltage_min; if (initdef.std_param.max_uvolt == 0) initdef.std_param.max_uvolt = def->voltage_max; initdef.id = def->id; initdef.ofw_node = node; regnode = regnode_create(dev, &rk805_regnode_class, &initdef); if (regnode == NULL) { device_printf(dev, "cannot create regulator\n"); return (NULL); } reg_sc = regnode_get_softc(regnode); reg_sc->regnode = regnode; reg_sc->base_dev = dev; reg_sc->def = def; reg_sc->xref = OF_xref_from_node(node); reg_sc->param = regnode_get_stdparam(regnode); regnode_register(regnode); return (reg_sc); } static int rk805_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, "RockChip RK805 PMIC"); return (BUS_PROBE_DEFAULT); } static void rk805_start(void *pdev) { struct rk805_softc *sc; device_t dev; uint8_t data[2]; int err; dev = pdev; sc = device_get_softc(dev); sc->dev = dev; /* No version register in RK808 */ if (bootverbose && sc->type == RK805) { err = rk805_read(dev, RK805_CHIP_NAME, data, 1); if (err != 0) { device_printf(dev, "Cannot read chip name reg\n"); return; } err = rk805_read(dev, RK805_CHIP_VER, data + 1, 1); if (err != 0) { device_printf(dev, "Cannot read chip version reg\n"); return; } device_printf(dev, "Chip Name: %x\n", data[0] << 4 | ((data[1] >> 4) & 0xf)); device_printf(dev, "Chip Version: %x\n", data[1] & 0xf); } config_intrhook_disestablish(&sc->intr_hook); } static int rk805_attach(device_t dev) { struct rk805_softc *sc; struct rk805_reg_sc *reg; struct rk805_regdef *regdefs; phandle_t rnode, child; int i; sc = device_get_softc(dev); sc->intr_hook.ich_func = rk805_start; sc->intr_hook.ich_arg = dev; if (config_intrhook_establish(&sc->intr_hook) != 0) return (ENOMEM); sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; switch (sc->type) { case RK805: regdefs = rk805_regdefs; sc->nregs = nitems(rk805_regdefs); break; case RK808: regdefs = rk808_regdefs; sc->nregs = nitems(rk808_regdefs); break; default: device_printf(dev, "Unknown type %d\n", sc->type); return (ENXIO); } sc->regs = malloc(sizeof(struct rk805_reg_sc *) * sc->nregs, M_RK805_REG, M_WAITOK | M_ZERO); rnode = ofw_bus_find_child(ofw_bus_get_node(dev), "regulators"); if (rnode > 0) { for (i = 0; i < sc->nregs; i++) { child = ofw_bus_find_child(rnode, regdefs[i].name); if (child == 0) continue; reg = rk805_reg_attach(dev, child, ®defs[i]); if (reg == NULL) { device_printf(dev, "cannot attach regulator %s\n", regdefs[i].name); continue; } sc->regs[i] = reg; if (bootverbose) device_printf(dev, "Regulator %s attached\n", regdefs[i].name); } } return (0); } static int rk805_detach(device_t dev) { /* We cannot detach regulators */ return (EBUSY); } static int rk805_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells, intptr_t *id) { struct rk805_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->nregs; i++) { if (sc->regs[i]->xref == xref) { *id = sc->regs[i]->def->id; return (0); } } return (ERANGE); } static device_method_t rk805_methods[] = { DEVMETHOD(device_probe, rk805_probe), DEVMETHOD(device_attach, rk805_attach), DEVMETHOD(device_detach, rk805_detach), /* regdev interface */ DEVMETHOD(regdev_map, rk805_map), DEVMETHOD_END }; static driver_t rk805_driver = { "rk805_pmu", rk805_methods, sizeof(struct rk805_softc), }; static devclass_t rk805_devclass; EARLY_DRIVER_MODULE(rk805, iicbus, rk805_driver, rk805_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); MODULE_DEPEND(rk805, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); MODULE_VERSION(rk805, 1); Index: head/sys/dev/extres/regulator/regnode_if.m =================================================================== --- head/sys/dev/extres/regulator/regnode_if.m (revision 354395) +++ head/sys/dev/extres/regulator/regnode_if.m (revision 354396) @@ -1,100 +1,109 @@ #- # Copyright (c) 2016 Michal Meloun # 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$ # INTERFACE regnode; HEADER { struct regnode; } CODE { static int regnode_default_stop(struct regnode *regnode, int *udelay) { return(REGNODE_ENABLE(regnode, false, udelay)); } } # # Initialize regulator # Returns 0 on success or a standard errno value. # METHOD int init { struct regnode *regnode; }; # # Enable/disable regulator # Returns 0 on success or a standard errno value. # - enable - input. # - delay - output, delay needed to stabilize voltage (in us) # METHOD int enable { struct regnode *regnode; bool enable; int *udelay; }; # # Get regulator status # Returns 0 on success or a standard errno value. # METHOD int status { struct regnode *regnode; int *status; /* REGULATOR_STATUS_* */ }; # # Set regulator voltage # Returns 0 on success or a standard errno value. # - min_uvolt, max_uvolt - input, requested voltage range (in uV) # - delay - output, delay needed to stabilize voltage (in us) METHOD int set_voltage { struct regnode *regnode; int min_uvolt; int max_uvolt; int *udelay; }; # # Get regulator voltage # Returns 0 on success or a standard errno value. # METHOD int get_voltage { struct regnode *regnode; int *uvolt; }; # +# Check if a given voltage is supported by the regulator +# Returns 0 on success or a standard errno value. +# +METHOD int check_voltage { + struct regnode *regnode; + int uvolt; +}; + +# # Stop (shutdown) regulator # Returns 0 on success or a standard errno value. # METHOD int stop { struct regnode *regnode; int *udelay; } DEFAULT regnode_default_stop; Index: head/sys/dev/extres/regulator/regulator.c =================================================================== --- head/sys/dev/extres/regulator/regulator.c (revision 354395) +++ head/sys/dev/extres/regulator/regulator.c (revision 354396) @@ -1,1272 +1,1299 @@ /*- * Copyright 2016 Michal Meloun * 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef FDT #include #include #include #endif #include #include "regdev_if.h" SYSCTL_NODE(_hw, OID_AUTO, regulator, CTLFLAG_RD, NULL, "Regulators"); MALLOC_DEFINE(M_REGULATOR, "regulator", "Regulator framework"); #define DIV_ROUND_UP(n,d) howmany(n, d) /* Forward declarations. */ struct regulator; struct regnode; typedef TAILQ_HEAD(regnode_list, regnode) regnode_list_t; typedef TAILQ_HEAD(regulator_list, regulator) regulator_list_t; /* Default regulator methods. */ static int regnode_method_init(struct regnode *regnode); static int regnode_method_enable(struct regnode *regnode, bool enable, int *udelay); static int regnode_method_status(struct regnode *regnode, int *status); static int regnode_method_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt, int *udelay); static int regnode_method_get_voltage(struct regnode *regnode, int *uvolt); static void regulator_constraint(void *dummy); static void regulator_shutdown(void *dummy); /* * Regulator controller methods. */ static regnode_method_t regnode_methods[] = { REGNODEMETHOD(regnode_init, regnode_method_init), REGNODEMETHOD(regnode_enable, regnode_method_enable), REGNODEMETHOD(regnode_status, regnode_method_status), REGNODEMETHOD(regnode_set_voltage, regnode_method_set_voltage), REGNODEMETHOD(regnode_get_voltage, regnode_method_get_voltage), + REGNODEMETHOD(regnode_check_voltage, regnode_method_check_voltage), REGNODEMETHOD_END }; DEFINE_CLASS_0(regnode, regnode_class, regnode_methods, 0); /* * Regulator node - basic element for modelling SOC and bard power supply * chains. Its contains producer data. */ struct regnode { KOBJ_FIELDS; TAILQ_ENTRY(regnode) reglist_link; /* Global list entry */ regulator_list_t consumers_list; /* Consumers list */ /* Cache for already resolved names */ struct regnode *parent; /* Resolved parent */ /* Details of this device. */ const char *name; /* Globally unique name */ const char *parent_name; /* Parent name */ device_t pdev; /* Producer device_t */ void *softc; /* Producer softc */ intptr_t id; /* Per producer unique id */ #ifdef FDT phandle_t ofw_node; /* OFW node of regulator */ #endif int flags; /* REGULATOR_FLAGS_ */ struct sx lock; /* Lock for this regulator */ int ref_cnt; /* Reference counter */ int enable_cnt; /* Enabled counter */ struct regnode_std_param std_param; /* Standard parameters */ struct sysctl_ctx_list sysctl_ctx; }; /* * Per consumer data, information about how a consumer is using a regulator * node. * A pointer to this structure is used as a handle in the consumer interface. */ struct regulator { device_t cdev; /* Consumer device */ struct regnode *regnode; TAILQ_ENTRY(regulator) link; /* Consumers list entry */ int enable_cnt; int min_uvolt; /* Requested uvolt range */ int max_uvolt; }; /* * Regulator names must be system wide unique. */ static regnode_list_t regnode_list = TAILQ_HEAD_INITIALIZER(regnode_list); static struct sx regnode_topo_lock; SX_SYSINIT(regulator_topology, ®node_topo_lock, "Regulator topology lock"); #define REG_TOPO_SLOCK() sx_slock(®node_topo_lock) #define REG_TOPO_XLOCK() sx_xlock(®node_topo_lock) #define REG_TOPO_UNLOCK() sx_unlock(®node_topo_lock) #define REG_TOPO_ASSERT() sx_assert(®node_topo_lock, SA_LOCKED) #define REG_TOPO_XASSERT() sx_assert(®node_topo_lock, SA_XLOCKED) #define REGNODE_SLOCK(_sc) sx_slock(&((_sc)->lock)) #define REGNODE_XLOCK(_sc) sx_xlock(&((_sc)->lock)) #define REGNODE_UNLOCK(_sc) sx_unlock(&((_sc)->lock)) SYSINIT(regulator_constraint, SI_SUB_LAST, SI_ORDER_ANY, regulator_constraint, NULL); SYSINIT(regulator_shutdown, SI_SUB_LAST, SI_ORDER_ANY, regulator_shutdown, NULL); static void regulator_constraint(void *dummy) { struct regnode *entry; int rv; REG_TOPO_SLOCK(); TAILQ_FOREACH(entry, ®node_list, reglist_link) { rv = regnode_set_constraint(entry); if (rv != 0 && bootverbose) printf("regulator: setting constraint on %s failed (%d)\n", entry->name, rv); } REG_TOPO_UNLOCK(); } /* * Disable unused regulator * We run this function at SI_SUB_LAST which mean that every driver that needs * regulator should have already enable them. * All the remaining regulators should be those left enabled by the bootloader * or enable by default by the PMIC. */ static void regulator_shutdown(void *dummy) { struct regnode *entry; int status, ret; int disable = 1; REG_TOPO_SLOCK(); TUNABLE_INT_FETCH("hw.regulator.disable_unused", &disable); TAILQ_FOREACH(entry, ®node_list, reglist_link) { if (!entry->std_param.always_on && disable) { if (bootverbose) printf("regulator: shutting down %s\n", entry->name); ret = regnode_status(entry, &status); if (ret == 0 && status == REGULATOR_STATUS_ENABLED) regnode_stop(entry, 0); } } REG_TOPO_UNLOCK(); } /* * sysctl handler */ static int regnode_uvolt_sysctl(SYSCTL_HANDLER_ARGS) { struct regnode *regnode = arg1; int rv, uvolt; if (regnode->std_param.min_uvolt == regnode->std_param.max_uvolt) { uvolt = regnode->std_param.min_uvolt; } else { REG_TOPO_SLOCK(); if ((rv = regnode_get_voltage(regnode, &uvolt)) != 0) { REG_TOPO_UNLOCK(); return (rv); } REG_TOPO_UNLOCK(); } return sysctl_handle_int(oidp, &uvolt, sizeof(uvolt), req); } /* ---------------------------------------------------------------------------- * * Default regulator methods for base class. * */ static int regnode_method_init(struct regnode *regnode) { return (0); } static int regnode_method_enable(struct regnode *regnode, bool enable, int *udelay) { if (!enable) return (ENXIO); *udelay = 0; return (0); } static int regnode_method_status(struct regnode *regnode, int *status) { *status = REGULATOR_STATUS_ENABLED; return (0); } static int regnode_method_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt, int *udelay) { if ((min_uvolt > regnode->std_param.max_uvolt) || (max_uvolt < regnode->std_param.min_uvolt)) return (ERANGE); *udelay = 0; return (0); } static int regnode_method_get_voltage(struct regnode *regnode, int *uvolt) { return (regnode->std_param.min_uvolt + (regnode->std_param.max_uvolt - regnode->std_param.min_uvolt) / 2); } +int +regnode_method_check_voltage(struct regnode *regnode, int uvolt) +{ + + if ((uvolt > regnode->std_param.max_uvolt) || + (uvolt < regnode->std_param.min_uvolt)) + return (ERANGE); + return (0); +} + /* ---------------------------------------------------------------------------- * * Internal functions. * */ static struct regnode * regnode_find_by_name(const char *name) { struct regnode *entry; REG_TOPO_ASSERT(); TAILQ_FOREACH(entry, ®node_list, reglist_link) { if (strcmp(entry->name, name) == 0) return (entry); } return (NULL); } static struct regnode * regnode_find_by_id(device_t dev, intptr_t id) { struct regnode *entry; REG_TOPO_ASSERT(); TAILQ_FOREACH(entry, ®node_list, reglist_link) { if ((entry->pdev == dev) && (entry->id == id)) return (entry); } return (NULL); } /* * Create and initialize regulator object, but do not register it. */ struct regnode * regnode_create(device_t pdev, regnode_class_t regnode_class, struct regnode_init_def *def) { struct regnode *regnode; struct sysctl_oid *regnode_oid; KASSERT(def->name != NULL, ("regulator name is NULL")); KASSERT(def->name[0] != '\0', ("regulator name is empty")); REG_TOPO_SLOCK(); if (regnode_find_by_name(def->name) != NULL) panic("Duplicated regulator registration: %s\n", def->name); REG_TOPO_UNLOCK(); /* Create object and initialize it. */ regnode = malloc(sizeof(struct regnode), M_REGULATOR, M_WAITOK | M_ZERO); kobj_init((kobj_t)regnode, (kobj_class_t)regnode_class); sx_init(®node->lock, "Regulator node lock"); /* Allocate softc if required. */ if (regnode_class->size > 0) { regnode->softc = malloc(regnode_class->size, M_REGULATOR, M_WAITOK | M_ZERO); } /* Copy all strings unless they're flagged as static. */ if (def->flags & REGULATOR_FLAGS_STATIC) { regnode->name = def->name; regnode->parent_name = def->parent_name; } else { regnode->name = strdup(def->name, M_REGULATOR); if (def->parent_name != NULL) regnode->parent_name = strdup(def->parent_name, M_REGULATOR); } /* Rest of init. */ TAILQ_INIT(®node->consumers_list); regnode->id = def->id; regnode->pdev = pdev; regnode->flags = def->flags; regnode->parent = NULL; regnode->std_param = def->std_param; #ifdef FDT regnode->ofw_node = def->ofw_node; #endif sysctl_ctx_init(®node->sysctl_ctx); regnode_oid = SYSCTL_ADD_NODE(®node->sysctl_ctx, SYSCTL_STATIC_CHILDREN(_hw_regulator), OID_AUTO, regnode->name, CTLFLAG_RD, 0, "A regulator node"); SYSCTL_ADD_INT(®node->sysctl_ctx, SYSCTL_CHILDREN(regnode_oid), OID_AUTO, "min_uvolt", CTLFLAG_RD, ®node->std_param.min_uvolt, 0, "Minimal voltage (in uV)"); SYSCTL_ADD_INT(®node->sysctl_ctx, SYSCTL_CHILDREN(regnode_oid), OID_AUTO, "max_uvolt", CTLFLAG_RD, ®node->std_param.max_uvolt, 0, "Maximal voltage (in uV)"); SYSCTL_ADD_INT(®node->sysctl_ctx, SYSCTL_CHILDREN(regnode_oid), OID_AUTO, "min_uamp", CTLFLAG_RD, ®node->std_param.min_uamp, 0, "Minimal amperage (in uA)"); SYSCTL_ADD_INT(®node->sysctl_ctx, SYSCTL_CHILDREN(regnode_oid), OID_AUTO, "max_uamp", CTLFLAG_RD, ®node->std_param.max_uamp, 0, "Maximal amperage (in uA)"); SYSCTL_ADD_INT(®node->sysctl_ctx, SYSCTL_CHILDREN(regnode_oid), OID_AUTO, "ramp_delay", CTLFLAG_RD, ®node->std_param.ramp_delay, 0, "Ramp delay (in uV/us)"); SYSCTL_ADD_INT(®node->sysctl_ctx, SYSCTL_CHILDREN(regnode_oid), OID_AUTO, "enable_delay", CTLFLAG_RD, ®node->std_param.enable_delay, 0, "Enable delay (in us)"); SYSCTL_ADD_INT(®node->sysctl_ctx, SYSCTL_CHILDREN(regnode_oid), OID_AUTO, "enable_cnt", CTLFLAG_RD, ®node->enable_cnt, 0, "The regulator enable counter"); SYSCTL_ADD_U8(®node->sysctl_ctx, SYSCTL_CHILDREN(regnode_oid), OID_AUTO, "boot_on", CTLFLAG_RD, (uint8_t *) ®node->std_param.boot_on, 0, "Is enabled on boot"); SYSCTL_ADD_U8(®node->sysctl_ctx, SYSCTL_CHILDREN(regnode_oid), OID_AUTO, "always_on", CTLFLAG_RD, (uint8_t *)®node->std_param.always_on, 0, "Is always enabled"); SYSCTL_ADD_PROC(®node->sysctl_ctx, SYSCTL_CHILDREN(regnode_oid), OID_AUTO, "uvolt", CTLTYPE_INT | CTLFLAG_RD, regnode, 0, regnode_uvolt_sysctl, "I", "Current voltage (in uV)"); return (regnode); } /* Register regulator object. */ struct regnode * regnode_register(struct regnode *regnode) { int rv; #ifdef FDT if (regnode->ofw_node <= 0) regnode->ofw_node = ofw_bus_get_node(regnode->pdev); if (regnode->ofw_node <= 0) return (NULL); #endif rv = REGNODE_INIT(regnode); if (rv != 0) { printf("REGNODE_INIT failed: %d\n", rv); return (NULL); } REG_TOPO_XLOCK(); TAILQ_INSERT_TAIL(®node_list, regnode, reglist_link); REG_TOPO_UNLOCK(); #ifdef FDT OF_device_register_xref(OF_xref_from_node(regnode->ofw_node), regnode->pdev); #endif return (regnode); } static int regnode_resolve_parent(struct regnode *regnode) { /* All ready resolved or no parent? */ if ((regnode->parent != NULL) || (regnode->parent_name == NULL)) return (0); regnode->parent = regnode_find_by_name(regnode->parent_name); if (regnode->parent == NULL) return (ENODEV); return (0); } static void regnode_delay(int usec) { int ticks; if (usec == 0) return; ticks = (usec * hz + 999999) / 1000000; if (cold || ticks < 2) DELAY(usec); else pause("REGULATOR", ticks); } /* -------------------------------------------------------------------------- * * Regulator providers interface * */ const char * regnode_get_name(struct regnode *regnode) { return (regnode->name); } const char * regnode_get_parent_name(struct regnode *regnode) { return (regnode->parent_name); } int regnode_get_flags(struct regnode *regnode) { return (regnode->flags); } void * regnode_get_softc(struct regnode *regnode) { return (regnode->softc); } device_t regnode_get_device(struct regnode *regnode) { return (regnode->pdev); } struct regnode_std_param *regnode_get_stdparam(struct regnode *regnode) { return (®node->std_param); } void regnode_topo_unlock(void) { REG_TOPO_UNLOCK(); } void regnode_topo_xlock(void) { REG_TOPO_XLOCK(); } void regnode_topo_slock(void) { REG_TOPO_SLOCK(); } /* -------------------------------------------------------------------------- * * Real consumers executive * */ struct regnode * regnode_get_parent(struct regnode *regnode) { int rv; REG_TOPO_ASSERT(); rv = regnode_resolve_parent(regnode); if (rv != 0) return (NULL); return (regnode->parent); } /* * Enable regulator. */ int regnode_enable(struct regnode *regnode) { int udelay; int rv; REG_TOPO_ASSERT(); /* Enable regulator for each node in chain, starting from source. */ rv = regnode_resolve_parent(regnode); if (rv != 0) return (rv); if (regnode->parent != NULL) { rv = regnode_enable(regnode->parent); if (rv != 0) return (rv); } /* Handle this node. */ REGNODE_XLOCK(regnode); if (regnode->enable_cnt == 0) { rv = REGNODE_ENABLE(regnode, true, &udelay); if (rv != 0) { REGNODE_UNLOCK(regnode); return (rv); } regnode_delay(udelay); } regnode->enable_cnt++; REGNODE_UNLOCK(regnode); return (0); } /* * Disable regulator. */ int regnode_disable(struct regnode *regnode) { int udelay; int rv; REG_TOPO_ASSERT(); rv = 0; REGNODE_XLOCK(regnode); /* Disable regulator for each node in chain, starting from consumer. */ if (regnode->enable_cnt == 1 && (regnode->flags & REGULATOR_FLAGS_NOT_DISABLE) == 0 && !regnode->std_param.always_on) { rv = REGNODE_ENABLE(regnode, false, &udelay); if (rv != 0) { REGNODE_UNLOCK(regnode); return (rv); } regnode_delay(udelay); } regnode->enable_cnt--; REGNODE_UNLOCK(regnode); rv = regnode_resolve_parent(regnode); if (rv != 0) return (rv); if (regnode->parent != NULL) rv = regnode_disable(regnode->parent); return (rv); } /* * Stop regulator. */ int regnode_stop(struct regnode *regnode, int depth) { int udelay; int rv; REG_TOPO_ASSERT(); rv = 0; REGNODE_XLOCK(regnode); /* The first node must not be enabled. */ if ((regnode->enable_cnt != 0) && (depth == 0)) { REGNODE_UNLOCK(regnode); return (EBUSY); } /* Disable regulator for each node in chain, starting from consumer */ if ((regnode->enable_cnt == 0) && ((regnode->flags & REGULATOR_FLAGS_NOT_DISABLE) == 0)) { rv = REGNODE_STOP(regnode, &udelay); if (rv != 0) { REGNODE_UNLOCK(regnode); return (rv); } regnode_delay(udelay); } REGNODE_UNLOCK(regnode); rv = regnode_resolve_parent(regnode); if (rv != 0) return (rv); if (regnode->parent != NULL && regnode->parent->enable_cnt == 0) rv = regnode_stop(regnode->parent, depth + 1); return (rv); } /* * Get regulator status. (REGULATOR_STATUS_*) */ int regnode_status(struct regnode *regnode, int *status) { int rv; REG_TOPO_ASSERT(); REGNODE_XLOCK(regnode); rv = REGNODE_STATUS(regnode, status); REGNODE_UNLOCK(regnode); return (rv); } /* * Get actual regulator voltage. */ int regnode_get_voltage(struct regnode *regnode, int *uvolt) { int rv; REG_TOPO_ASSERT(); REGNODE_XLOCK(regnode); rv = REGNODE_GET_VOLTAGE(regnode, uvolt); REGNODE_UNLOCK(regnode); /* Pass call into parent, if regulator is in bypass mode. */ if (rv == ENOENT) { rv = regnode_resolve_parent(regnode); if (rv != 0) return (rv); if (regnode->parent != NULL) rv = regnode_get_voltage(regnode->parent, uvolt); } return (rv); } /* * Set regulator voltage. */ int regnode_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt) { int udelay; int rv; REG_TOPO_ASSERT(); REGNODE_XLOCK(regnode); rv = REGNODE_SET_VOLTAGE(regnode, min_uvolt, max_uvolt, &udelay); if (rv == 0) regnode_delay(udelay); REGNODE_UNLOCK(regnode); return (rv); } /* * Consumer variant of regnode_set_voltage(). */ static int regnode_set_voltage_checked(struct regnode *regnode, struct regulator *reg, int min_uvolt, int max_uvolt) { int udelay; int all_max_uvolt; int all_min_uvolt; struct regulator *tmp; int rv; REG_TOPO_ASSERT(); REGNODE_XLOCK(regnode); /* Return error if requested range is outside of regulator range. */ if ((min_uvolt > regnode->std_param.max_uvolt) || (max_uvolt < regnode->std_param.min_uvolt)) { REGNODE_UNLOCK(regnode); return (ERANGE); } /* Get actual voltage range for all consumers. */ all_min_uvolt = regnode->std_param.min_uvolt; all_max_uvolt = regnode->std_param.max_uvolt; TAILQ_FOREACH(tmp, ®node->consumers_list, link) { /* Don't take requestor in account. */ if (tmp == reg) continue; if (all_min_uvolt < tmp->min_uvolt) all_min_uvolt = tmp->min_uvolt; if (all_max_uvolt > tmp->max_uvolt) all_max_uvolt = tmp->max_uvolt; } /* Test if request fits to actual contract. */ if ((min_uvolt > all_max_uvolt) || (max_uvolt < all_min_uvolt)) { REGNODE_UNLOCK(regnode); return (ERANGE); } /* Adjust new range.*/ if (min_uvolt < all_min_uvolt) min_uvolt = all_min_uvolt; if (max_uvolt > all_max_uvolt) max_uvolt = all_max_uvolt; rv = REGNODE_SET_VOLTAGE(regnode, min_uvolt, max_uvolt, &udelay); regnode_delay(udelay); REGNODE_UNLOCK(regnode); return (rv); } int regnode_set_constraint(struct regnode *regnode) { int status, rv, uvolt; if (regnode->std_param.boot_on != true && regnode->std_param.always_on != true) return (0); rv = regnode_status(regnode, &status); if (rv != 0) { if (bootverbose) printf("Cannot get regulator status for %s\n", regnode_get_name(regnode)); return (rv); } if (status == REGULATOR_STATUS_ENABLED) return (0); rv = regnode_get_voltage(regnode, &uvolt); if (rv != 0) { if (bootverbose) printf("Cannot get regulator voltage for %s\n", regnode_get_name(regnode)); return (rv); } if (uvolt < regnode->std_param.min_uvolt || uvolt > regnode->std_param.max_uvolt) { if (bootverbose) printf("Regulator %s current voltage %d is not in the" " acceptable range : %d<->%d\n", regnode_get_name(regnode), uvolt, regnode->std_param.min_uvolt, regnode->std_param.max_uvolt); return (ERANGE); } rv = regnode_enable(regnode); if (rv != 0) { if (bootverbose) printf("Cannot enable regulator %s\n", regnode_get_name(regnode)); return (rv); } return (0); } #ifdef FDT phandle_t regnode_get_ofw_node(struct regnode *regnode) { return (regnode->ofw_node); } #endif /* -------------------------------------------------------------------------- * * Regulator consumers interface. * */ /* Helper function for regulator_get*() */ static regulator_t regulator_create(struct regnode *regnode, device_t cdev) { struct regulator *reg; REG_TOPO_ASSERT(); reg = malloc(sizeof(struct regulator), M_REGULATOR, M_WAITOK | M_ZERO); reg->cdev = cdev; reg->regnode = regnode; reg->enable_cnt = 0; REGNODE_XLOCK(regnode); regnode->ref_cnt++; TAILQ_INSERT_TAIL(®node->consumers_list, reg, link); reg ->min_uvolt = regnode->std_param.min_uvolt; reg ->max_uvolt = regnode->std_param.max_uvolt; REGNODE_UNLOCK(regnode); return (reg); } int regulator_enable(regulator_t reg) { int rv; struct regnode *regnode; regnode = reg->regnode; KASSERT(regnode->ref_cnt > 0, ("Attempt to access unreferenced regulator: %s\n", regnode->name)); REG_TOPO_SLOCK(); rv = regnode_enable(regnode); if (rv == 0) reg->enable_cnt++; REG_TOPO_UNLOCK(); return (rv); } int regulator_disable(regulator_t reg) { int rv; struct regnode *regnode; regnode = reg->regnode; KASSERT(regnode->ref_cnt > 0, ("Attempt to access unreferenced regulator: %s\n", regnode->name)); KASSERT(reg->enable_cnt > 0, ("Attempt to disable already disabled regulator: %s\n", regnode->name)); REG_TOPO_SLOCK(); rv = regnode_disable(regnode); if (rv == 0) reg->enable_cnt--; REG_TOPO_UNLOCK(); return (rv); } int regulator_stop(regulator_t reg) { int rv; struct regnode *regnode; regnode = reg->regnode; KASSERT(regnode->ref_cnt > 0, ("Attempt to access unreferenced regulator: %s\n", regnode->name)); KASSERT(reg->enable_cnt == 0, ("Attempt to stop already enabled regulator: %s\n", regnode->name)); REG_TOPO_SLOCK(); rv = regnode_stop(regnode, 0); REG_TOPO_UNLOCK(); return (rv); } int regulator_status(regulator_t reg, int *status) { int rv; struct regnode *regnode; regnode = reg->regnode; KASSERT(regnode->ref_cnt > 0, ("Attempt to access unreferenced regulator: %s\n", regnode->name)); REG_TOPO_SLOCK(); rv = regnode_status(regnode, status); REG_TOPO_UNLOCK(); return (rv); } int regulator_get_voltage(regulator_t reg, int *uvolt) { int rv; struct regnode *regnode; regnode = reg->regnode; KASSERT(regnode->ref_cnt > 0, ("Attempt to access unreferenced regulator: %s\n", regnode->name)); REG_TOPO_SLOCK(); rv = regnode_get_voltage(regnode, uvolt); REG_TOPO_UNLOCK(); return (rv); } int regulator_set_voltage(regulator_t reg, int min_uvolt, int max_uvolt) { struct regnode *regnode; int rv; regnode = reg->regnode; KASSERT(regnode->ref_cnt > 0, ("Attempt to access unreferenced regulator: %s\n", regnode->name)); REG_TOPO_SLOCK(); rv = regnode_set_voltage_checked(regnode, reg, min_uvolt, max_uvolt); if (rv == 0) { reg->min_uvolt = min_uvolt; reg->max_uvolt = max_uvolt; } + REG_TOPO_UNLOCK(); + return (rv); +} + +int +regulator_check_voltage(regulator_t reg, int uvolt) +{ + int rv; + struct regnode *regnode; + + regnode = reg->regnode; + KASSERT(regnode->ref_cnt > 0, + ("Attempt to access unreferenced regulator: %s\n", regnode->name)); + + REG_TOPO_SLOCK(); + rv = REGNODE_CHECK_VOLTAGE(regnode, uvolt); REG_TOPO_UNLOCK(); return (rv); } const char * regulator_get_name(regulator_t reg) { struct regnode *regnode; regnode = reg->regnode; KASSERT(regnode->ref_cnt > 0, ("Attempt to access unreferenced regulator: %s\n", regnode->name)); return (regnode->name); } int regulator_get_by_name(device_t cdev, const char *name, regulator_t *reg) { struct regnode *regnode; REG_TOPO_SLOCK(); regnode = regnode_find_by_name(name); if (regnode == NULL) { REG_TOPO_UNLOCK(); return (ENODEV); } *reg = regulator_create(regnode, cdev); REG_TOPO_UNLOCK(); return (0); } int regulator_get_by_id(device_t cdev, device_t pdev, intptr_t id, regulator_t *reg) { struct regnode *regnode; REG_TOPO_SLOCK(); regnode = regnode_find_by_id(pdev, id); if (regnode == NULL) { REG_TOPO_UNLOCK(); return (ENODEV); } *reg = regulator_create(regnode, cdev); REG_TOPO_UNLOCK(); return (0); } int regulator_release(regulator_t reg) { struct regnode *regnode; regnode = reg->regnode; KASSERT(regnode->ref_cnt > 0, ("Attempt to access unreferenced regulator: %s\n", regnode->name)); REG_TOPO_SLOCK(); while (reg->enable_cnt > 0) { regnode_disable(regnode); reg->enable_cnt--; } REGNODE_XLOCK(regnode); TAILQ_REMOVE(®node->consumers_list, reg, link); regnode->ref_cnt--; REGNODE_UNLOCK(regnode); REG_TOPO_UNLOCK(); free(reg, M_REGULATOR); return (0); } #ifdef FDT /* Default DT mapper. */ int regdev_default_ofw_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells, intptr_t *id) { if (ncells == 0) *id = 1; else if (ncells == 1) *id = cells[0]; else return (ERANGE); return (0); } int regulator_parse_ofw_stdparam(device_t pdev, phandle_t node, struct regnode_init_def *def) { phandle_t supply_xref; struct regnode_std_param *par; int rv; par = &def->std_param; rv = OF_getprop_alloc(node, "regulator-name", (void **)&def->name); if (rv <= 0) { device_printf(pdev, "%s: Missing regulator name\n", __func__); return (ENXIO); } rv = OF_getencprop(node, "regulator-min-microvolt", &par->min_uvolt, sizeof(par->min_uvolt)); if (rv <= 0) par->min_uvolt = 0; rv = OF_getencprop(node, "regulator-max-microvolt", &par->max_uvolt, sizeof(par->max_uvolt)); if (rv <= 0) par->max_uvolt = 0; rv = OF_getencprop(node, "regulator-min-microamp", &par->min_uamp, sizeof(par->min_uamp)); if (rv <= 0) par->min_uamp = 0; rv = OF_getencprop(node, "regulator-max-microamp", &par->max_uamp, sizeof(par->max_uamp)); if (rv <= 0) par->max_uamp = 0; rv = OF_getencprop(node, "regulator-ramp-delay", &par->ramp_delay, sizeof(par->ramp_delay)); if (rv <= 0) par->ramp_delay = 0; rv = OF_getencprop(node, "regulator-enable-ramp-delay", &par->enable_delay, sizeof(par->enable_delay)); if (rv <= 0) par->enable_delay = 0; if (OF_hasprop(node, "regulator-boot-on")) par->boot_on = true; if (OF_hasprop(node, "regulator-always-on")) par->always_on = true; if (OF_hasprop(node, "enable-active-high")) par->enable_active_high = 1; rv = OF_getencprop(node, "vin-supply", &supply_xref, sizeof(supply_xref)); if (rv >= 0) { rv = OF_getprop_alloc(supply_xref, "regulator-name", (void **)&def->parent_name); if (rv <= 0) def->parent_name = NULL; } return (0); } int regulator_get_by_ofw_property(device_t cdev, phandle_t cnode, char *name, regulator_t *reg) { phandle_t *cells; device_t regdev; int ncells, rv; intptr_t id; *reg = NULL; if (cnode <= 0) cnode = ofw_bus_get_node(cdev); if (cnode <= 0) { device_printf(cdev, "%s called on not ofw based device\n", __func__); return (ENXIO); } cells = NULL; ncells = OF_getencprop_alloc_multi(cnode, name, sizeof(*cells), (void **)&cells); if (ncells <= 0) return (ENOENT); /* Translate xref to device */ regdev = OF_device_from_xref(cells[0]); if (regdev == NULL) { OF_prop_free(cells); return (ENODEV); } /* Map regulator to number */ rv = REGDEV_MAP(regdev, cells[0], ncells - 1, cells + 1, &id); OF_prop_free(cells); if (rv != 0) return (rv); return (regulator_get_by_id(cdev, regdev, id, reg)); } #endif /* -------------------------------------------------------------------------- * * Regulator utility functions. * */ /* Convert raw selector value to real voltage */ int regulator_range_sel8_to_volt(struct regulator_range *ranges, int nranges, uint8_t sel, int *volt) { struct regulator_range *range; int i; if (nranges == 0) panic("Voltage regulator have zero ranges\n"); for (i = 0; i < nranges ; i++) { range = ranges + i; if (!(sel >= range->min_sel && sel <= range->max_sel)) continue; sel -= range->min_sel; *volt = range->min_uvolt + sel * range->step_uvolt; return (0); } return (ERANGE); } int regulator_range_volt_to_sel8(struct regulator_range *ranges, int nranges, int min_uvolt, int max_uvolt, uint8_t *out_sel) { struct regulator_range *range; uint8_t sel; int uvolt; int rv, i; if (nranges == 0) panic("Voltage regulator have zero ranges\n"); for (i = 0; i < nranges; i++) { range = ranges + i; uvolt = range->min_uvolt + (range->max_sel - range->min_sel) * range->step_uvolt; if ((min_uvolt > uvolt) || (max_uvolt < range->min_uvolt)) continue; if (min_uvolt <= range->min_uvolt) min_uvolt = range->min_uvolt; /* if step == 0 -> fixed voltage range. */ if (range->step_uvolt == 0) sel = 0; else sel = DIV_ROUND_UP(min_uvolt - range->min_uvolt, range->step_uvolt); sel += range->min_sel; break; } if (i >= nranges) return (ERANGE); /* Verify new settings. */ rv = regulator_range_sel8_to_volt(ranges, nranges, sel, &uvolt); if (rv != 0) return (rv); if ((uvolt < min_uvolt) || (uvolt > max_uvolt)) return (ERANGE); *out_sel = sel; return (0); } Index: head/sys/dev/extres/regulator/regulator.h =================================================================== --- head/sys/dev/extres/regulator/regulator.h (revision 354395) +++ head/sys/dev/extres/regulator/regulator.h (revision 354396) @@ -1,151 +1,156 @@ /*- * Copyright 2016 Michal Meloun * 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$ */ #ifndef _DEV_EXTRES_REGULATOR_H_ #define _DEV_EXTRES_REGULATOR_H_ #include "opt_platform.h" #include #include #ifdef FDT #include #endif #include "regnode_if.h" SYSCTL_DECL(_hw_regulator); #define REGULATOR_FLAGS_STATIC 0x00000001 /* Static strings */ #define REGULATOR_FLAGS_NOT_DISABLE 0x00000002 /* Cannot be disabled */ #define REGULATOR_STATUS_ENABLED 0x00000001 #define REGULATOR_STATUS_OVERCURRENT 0x00000002 typedef struct regulator *regulator_t; /* Standard regulator parameters. */ struct regnode_std_param { int min_uvolt; /* In uV */ int max_uvolt; /* In uV */ int min_uamp; /* In uA */ int max_uamp; /* In uA */ int ramp_delay; /* In uV/usec */ int enable_delay; /* In usec */ bool boot_on; /* Is enabled on boot */ bool always_on; /* Must be enabled */ int enable_active_high; }; /* Initialization parameters. */ struct regnode_init_def { char *name; /* Regulator name */ char *parent_name; /* Name of parent regulator */ struct regnode_std_param std_param; /* Standard parameters */ intptr_t id; /* Regulator ID */ int flags; /* Flags */ #ifdef FDT phandle_t ofw_node; /* OFW node of regulator */ #endif }; struct regulator_range { int min_uvolt; int step_uvolt; uint8_t min_sel; uint8_t max_sel; }; #define REG_RANGE_INIT(_min_sel, _max_sel, _min_uvolt, _step_uvolt) { \ .min_sel = _min_sel, \ .max_sel = _max_sel, \ .min_uvolt = _min_uvolt, \ .step_uvolt = _step_uvolt, \ } /* * Shorthands for constructing method tables. */ #define REGNODEMETHOD KOBJMETHOD #define REGNODEMETHOD_END KOBJMETHOD_END #define regnode_method_t kobj_method_t #define regnode_class_t kobj_class_t DECLARE_CLASS(regnode_class); /* Providers interface. */ struct regnode *regnode_create(device_t pdev, regnode_class_t regnode_class, struct regnode_init_def *def); struct regnode *regnode_register(struct regnode *regnode); const char *regnode_get_name(struct regnode *regnode); const char *regnode_get_parent_name(struct regnode *regnode); struct regnode *regnode_get_parent(struct regnode *regnode); int regnode_get_flags(struct regnode *regnode); void *regnode_get_softc(struct regnode *regnode); device_t regnode_get_device(struct regnode *regnode); struct regnode_std_param *regnode_get_stdparam(struct regnode *regnode); void regnode_topo_unlock(void); void regnode_topo_xlock(void); void regnode_topo_slock(void); int regnode_enable(struct regnode *regnode); int regnode_disable(struct regnode *regnode); int regnode_stop(struct regnode *regnode, int depth); int regnode_status(struct regnode *regnode, int *status); int regnode_get_voltage(struct regnode *regnode, int *uvolt); int regnode_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt); int regnode_set_constraint(struct regnode *regnode); + +/* Standard method that aren't default */ +int regnode_method_check_voltage(struct regnode *regnode, int uvolt); + #ifdef FDT phandle_t regnode_get_ofw_node(struct regnode *regnode); #endif /* Consumers interface. */ int regulator_get_by_name(device_t cdev, const char *name, regulator_t *regulator); int regulator_get_by_id(device_t cdev, device_t pdev, intptr_t id, regulator_t *regulator); int regulator_release(regulator_t regulator); const char *regulator_get_name(regulator_t regulator); int regulator_enable(regulator_t reg); int regulator_disable(regulator_t reg); int regulator_stop(regulator_t reg); int regulator_status(regulator_t reg, int *status); int regulator_get_voltage(regulator_t reg, int *uvolt); int regulator_set_voltage(regulator_t reg, int min_uvolt, int max_uvolt); +int regulator_check_voltage(regulator_t reg, int uvolt); #ifdef FDT int regulator_get_by_ofw_property(device_t dev, phandle_t node, char *name, regulator_t *reg); int regulator_parse_ofw_stdparam(device_t dev, phandle_t node, struct regnode_init_def *def); #endif /* Utility functions */ int regulator_range_volt_to_sel8(struct regulator_range *ranges, int nranges, int min_uvolt, int max_uvolt, uint8_t *out_sel); int regulator_range_sel8_to_volt(struct regulator_range *ranges, int nranges, uint8_t sel, int *volt); #endif /* _DEV_EXTRES_REGULATOR_H_ */ Index: head/sys/dev/extres/regulator/regulator_fixed.c =================================================================== --- head/sys/dev/extres/regulator/regulator_fixed.c (revision 354395) +++ head/sys/dev/extres/regulator/regulator_fixed.c (revision 354396) @@ -1,499 +1,500 @@ /*- * Copyright 2016 Michal Meloun * 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #ifdef FDT #include #include #include #endif #include #include #include "regdev_if.h" MALLOC_DEFINE(M_FIXEDREGULATOR, "fixedregulator", "Fixed regulator"); /* GPIO list for shared pins. */ typedef TAILQ_HEAD(gpio_list, gpio_entry) gpio_list_t; struct gpio_entry { TAILQ_ENTRY(gpio_entry) link; struct gpiobus_pin gpio_pin; int use_cnt; int enable_cnt; bool always_on; }; static gpio_list_t gpio_list = TAILQ_HEAD_INITIALIZER(gpio_list); static struct mtx gpio_list_mtx; MTX_SYSINIT(gpio_list_lock, &gpio_list_mtx, "Regulator GPIO lock", MTX_DEF); struct regnode_fixed_sc { struct regnode_std_param *param; bool gpio_open_drain; struct gpio_entry *gpio_entry; }; static int regnode_fixed_init(struct regnode *regnode); static int regnode_fixed_enable(struct regnode *regnode, bool enable, int *udelay); static int regnode_fixed_status(struct regnode *regnode, int *status); static int regnode_fixed_stop(struct regnode *regnode, int *udelay); static regnode_method_t regnode_fixed_methods[] = { /* Regulator interface */ REGNODEMETHOD(regnode_init, regnode_fixed_init), REGNODEMETHOD(regnode_enable, regnode_fixed_enable), REGNODEMETHOD(regnode_status, regnode_fixed_status), REGNODEMETHOD(regnode_stop, regnode_fixed_stop), + REGNODEMETHOD(regnode_check_voltage, regnode_method_check_voltage), REGNODEMETHOD_END }; DEFINE_CLASS_1(regnode_fixed, regnode_fixed_class, regnode_fixed_methods, sizeof(struct regnode_fixed_sc), regnode_class); /* * GPIO list functions. * Two or more regulators can share single GPIO pins, so we must track all * GPIOs in gpio_list. * The GPIO pin is registerd and reseved for first consumer, all others share * gpio_entry with it. */ static struct gpio_entry * regnode_get_gpio_entry(struct gpiobus_pin *gpio_pin) { struct gpio_entry *entry, *tmp; device_t busdev; int rv; busdev = GPIO_GET_BUS(gpio_pin->dev); if (busdev == NULL) return (NULL); entry = malloc(sizeof(struct gpio_entry), M_FIXEDREGULATOR, M_WAITOK | M_ZERO); mtx_lock(&gpio_list_mtx); TAILQ_FOREACH(tmp, &gpio_list, link) { if (tmp->gpio_pin.dev == gpio_pin->dev && tmp->gpio_pin.pin == gpio_pin->pin) { tmp->use_cnt++; mtx_unlock(&gpio_list_mtx); free(entry, M_FIXEDREGULATOR); return (tmp); } } /* Reserve pin. */ /* XXX Can we call gpiobus_acquire_pin() with gpio_list_mtx held? */ rv = gpiobus_acquire_pin(busdev, gpio_pin->pin); if (rv != 0) { mtx_unlock(&gpio_list_mtx); free(entry, M_FIXEDREGULATOR); return (NULL); } /* Everything is OK, build new entry and insert it to list. */ entry->gpio_pin = *gpio_pin; entry->use_cnt = 1; TAILQ_INSERT_TAIL(&gpio_list, entry, link); mtx_unlock(&gpio_list_mtx); return (entry); } /* * Regulator class implementation. */ static int regnode_fixed_init(struct regnode *regnode) { device_t dev; struct regnode_fixed_sc *sc; struct gpiobus_pin *pin; uint32_t flags; int rv; sc = regnode_get_softc(regnode); dev = regnode_get_device(regnode); sc->param = regnode_get_stdparam(regnode); if (sc->gpio_entry == NULL) return (0); pin = &sc->gpio_entry->gpio_pin; flags = GPIO_PIN_OUTPUT; if (sc->gpio_open_drain) flags |= GPIO_PIN_OPENDRAIN; if (sc->param->boot_on || sc->param->always_on) { rv = GPIO_PIN_SET(pin->dev, pin->pin, sc->param->enable_active_high); if (rv != 0) { device_printf(dev, "Cannot set GPIO pin: %d\n", pin->pin); return (rv); } } rv = GPIO_PIN_SETFLAGS(pin->dev, pin->pin, flags); if (rv != 0) { device_printf(dev, "Cannot configure GPIO pin: %d\n", pin->pin); return (rv); } return (0); } /* * Enable/disable regulator. * Take shared GPIO pins in account */ static int regnode_fixed_enable(struct regnode *regnode, bool enable, int *udelay) { device_t dev; struct regnode_fixed_sc *sc; struct gpiobus_pin *pin; int rv; sc = regnode_get_softc(regnode); dev = regnode_get_device(regnode); *udelay = 0; if (sc->gpio_entry == NULL) return (0); pin = &sc->gpio_entry->gpio_pin; if (enable) { sc->gpio_entry->enable_cnt++; if (sc->gpio_entry->enable_cnt > 1) return (0); } else { KASSERT(sc->gpio_entry->enable_cnt > 0, ("Invalid enable count")); sc->gpio_entry->enable_cnt--; if (sc->gpio_entry->enable_cnt >= 1) return (0); } if (sc->gpio_entry->always_on && !enable) return (0); if (!sc->param->enable_active_high) enable = !enable; rv = GPIO_PIN_SET(pin->dev, pin->pin, enable); if (rv != 0) { device_printf(dev, "Cannot set GPIO pin: %d\n", pin->pin); return (rv); } *udelay = sc->param->enable_delay; return (0); } /* * Stop (physicaly shutdown) regulator. * Take shared GPIO pins in account */ static int regnode_fixed_stop(struct regnode *regnode, int *udelay) { device_t dev; struct regnode_fixed_sc *sc; struct gpiobus_pin *pin; int rv; sc = regnode_get_softc(regnode); dev = regnode_get_device(regnode); *udelay = 0; if (sc->gpio_entry == NULL) return (0); if (sc->gpio_entry->always_on) return (0); pin = &sc->gpio_entry->gpio_pin; if (sc->gpio_entry->enable_cnt > 0) { /* Other regulator(s) are enabled. */ /* XXXX Any diagnostic message? Or error? */ return (0); } rv = GPIO_PIN_SET(pin->dev, pin->pin, sc->param->enable_active_high ? false: true); if (rv != 0) { device_printf(dev, "Cannot set GPIO pin: %d\n", pin->pin); return (rv); } *udelay = sc->param->enable_delay; return (0); } static int regnode_fixed_status(struct regnode *regnode, int *status) { struct regnode_fixed_sc *sc; struct gpiobus_pin *pin; uint32_t val; int rv; sc = regnode_get_softc(regnode); *status = 0; if (sc->gpio_entry == NULL) { *status = REGULATOR_STATUS_ENABLED; return (0); } pin = &sc->gpio_entry->gpio_pin; rv = GPIO_PIN_GET(pin->dev, pin->pin, &val); if (rv == 0) { if (!sc->param->enable_active_high ^ (val != 0)) *status = REGULATOR_STATUS_ENABLED; } return (rv); } int regnode_fixed_register(device_t dev, struct regnode_fixed_init_def *init_def) { struct regnode *regnode; struct regnode_fixed_sc *sc; regnode = regnode_create(dev, ®node_fixed_class, &init_def->reg_init_def); if (regnode == NULL) { device_printf(dev, "Cannot create regulator.\n"); return(ENXIO); } sc = regnode_get_softc(regnode); sc->gpio_open_drain = init_def->gpio_open_drain; if (init_def->gpio_pin != NULL) { sc->gpio_entry = regnode_get_gpio_entry(init_def->gpio_pin); if (sc->gpio_entry == NULL) return(ENXIO); } regnode = regnode_register(regnode); if (regnode == NULL) { device_printf(dev, "Cannot register regulator.\n"); return(ENXIO); } if (sc->gpio_entry != NULL) sc->gpio_entry->always_on |= sc->param->always_on; return (0); } /* * OFW Driver implementation. */ #ifdef FDT struct regfix_softc { device_t dev; bool attach_done; struct regnode_fixed_init_def init_def; phandle_t gpio_prodxref; pcell_t *gpio_cells; int gpio_ncells; struct gpiobus_pin gpio_pin; }; static struct ofw_compat_data compat_data[] = { {"regulator-fixed", 1}, {NULL, 0}, }; static int regfix_get_gpio(struct regfix_softc * sc) { device_t busdev; phandle_t node; int rv; if (sc->gpio_prodxref == 0) return (0); node = ofw_bus_get_node(sc->dev); /* Test if controller exist. */ sc->gpio_pin.dev = OF_device_from_xref(sc->gpio_prodxref); if (sc->gpio_pin.dev == NULL) return (ENODEV); /* Test if GPIO bus already exist. */ busdev = GPIO_GET_BUS(sc->gpio_pin.dev); if (busdev == NULL) return (ENODEV); rv = gpio_map_gpios(sc->gpio_pin.dev, node, OF_node_from_xref(sc->gpio_prodxref), sc->gpio_ncells, sc->gpio_cells, &(sc->gpio_pin.pin), &(sc->gpio_pin.flags)); if (rv != 0) { device_printf(sc->dev, "Cannot map the gpio property.\n"); return (ENXIO); } sc->init_def.gpio_pin = &sc->gpio_pin; return (0); } static int regfix_parse_fdt(struct regfix_softc * sc) { phandle_t node; int rv; struct regnode_init_def *init_def; node = ofw_bus_get_node(sc->dev); init_def = &sc->init_def.reg_init_def; rv = regulator_parse_ofw_stdparam(sc->dev, node, init_def); if (rv != 0) { device_printf(sc->dev, "Cannot parse standard parameters.\n"); return(rv); } /* Fixed regulator uses 'startup-delay-us' property for enable_delay */ rv = OF_getencprop(node, "startup-delay-us", &init_def->std_param.enable_delay, sizeof(init_def->std_param.enable_delay)); if (rv <= 0) init_def->std_param.enable_delay = 0; /* GPIO pin */ if (OF_hasprop(node, "gpio-open-drain")) sc->init_def.gpio_open_drain = true; if (!OF_hasprop(node, "gpio")) return (0); rv = ofw_bus_parse_xref_list_alloc(node, "gpio", "#gpio-cells", 0, &sc->gpio_prodxref, &sc->gpio_ncells, &sc->gpio_cells); if (rv != 0) { sc->gpio_prodxref = 0; device_printf(sc->dev, "Malformed gpio property\n"); return (ENXIO); } return (0); } static void regfix_new_pass(device_t dev) { struct regfix_softc * sc; int rv; sc = device_get_softc(dev); bus_generic_new_pass(dev); if (sc->attach_done) return; /* Try to get and configure GPIO. */ rv = regfix_get_gpio(sc); if (rv != 0) return; /* Register regulator. */ regnode_fixed_register(sc->dev, &sc->init_def); sc->attach_done = true; } static int regfix_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) return (ENXIO); device_set_desc(dev, "Fixed Regulator"); return (BUS_PROBE_DEFAULT); } static int regfix_detach(device_t dev) { /* This device is always present. */ return (EBUSY); } static int regfix_attach(device_t dev) { struct regfix_softc * sc; int rv; sc = device_get_softc(dev); sc->dev = dev; /* Parse FDT data. */ rv = regfix_parse_fdt(sc); if (rv != 0) return(ENXIO); /* Fill reset of init. */ sc->init_def.reg_init_def.id = 1; sc->init_def.reg_init_def.flags = REGULATOR_FLAGS_STATIC; /* Try to get and configure GPIO. */ rv = regfix_get_gpio(sc); if (rv != 0) return (bus_generic_attach(dev)); /* Register regulator. */ regnode_fixed_register(sc->dev, &sc->init_def); sc->attach_done = true; return (bus_generic_attach(dev)); } static device_method_t regfix_methods[] = { /* Device interface */ DEVMETHOD(device_probe, regfix_probe), DEVMETHOD(device_attach, regfix_attach), DEVMETHOD(device_detach, regfix_detach), /* Bus interface */ DEVMETHOD(bus_new_pass, regfix_new_pass), /* Regdev interface */ DEVMETHOD(regdev_map, regdev_default_ofw_map), DEVMETHOD_END }; static devclass_t regfix_devclass; DEFINE_CLASS_0(regfix, regfix_driver, regfix_methods, sizeof(struct regfix_softc)); EARLY_DRIVER_MODULE(regfix, simplebus, regfix_driver, regfix_devclass, 0, 0, BUS_PASS_BUS); #endif /* FDT */