/* $NetBSD: axp20x.c,v 1.21 2021/08/07 16:19:11 thorpej Exp $ */ /*- * Copyright (c) 2014-2017 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 __KERNEL_RCSID(0, "$NetBSD: axp20x.c,v 1.21 2021/08/07 16:19:11 thorpej Exp $"); #include #include #include #include #include #include #include #include #include #define AXP20X_DCDC2 2 #define AXP20X_DCDC3 3 #define AXP209_I2C_ADDR 0x34 #define AXP_INPUT_STATUS 0x00 #define AXP_INPUT_STATUS_AC_PRESENT __BIT(7) #define AXP_INPUT_STATUS_AC_OK __BIT(6) #define AXP_INPUT_STATUS_VBUS_PRESENT __BIT(5) #define AXP_INPUT_STATUS_VBUS_OK __BIT(4) #define AXP_POWER_MODE 0x01 #define AXP_POWER_MODE_OVERTEMP __BIT(7) #define AXP_POWER_MODE_CHARGING __BIT(6) #define AXP_POWER_MODE_BATTOK __BIT(5) #define AXP_POWEROUT_CTRL 0x12 #define AXP_POWEROUT_CTRL_LDO3 __BIT(6) #define AXP_POWEROUT_CTRL_DCDC2 __BIT(4) #define AXP_POWEROUT_CTRL_LDO4 __BIT(3) #define AXP_POWEROUT_CTRL_LDO2 __BIT(2) #define AXP_POWEROUT_CTRL_DCDC3 __BIT(1) #define AXP_POWEROUT_CTRL_EXTEN __BIT(0) #define AXP_DCDC2 0x23 #define AXP_DCDC2_VOLT_MASK __BITS(0,5) #define AXP_DCDC2_VOLT_SHIFT 0 #define AXP_DCDC2_LDO3_VRC 0x25 #define AXP_DCDC3 0x27 #define AXP_DCDC3_VOLT_MASK __BITS(0,6) #define AXP_DCDC3_VOLT_SHIFT 0 #define AXP_LDO2_4 0x28 #define AXP_LDO2_VOLT_MASK __BITS(4,7) #define AXP_LDO2_VOLT_SHIFT 4 #define AXP_LDO4_VOLT_MASK __BITS(0,3) #define AXP_LDO4_VOLT_SHIFT 0 static int ldo4_mvV[] = { 1250, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 2000, 2500, 2700, 2800, 3000, 3100, 3200, 3300 }; #define AXP_LDO3 0x29 #define AXP_LDO3_TRACK __BIT(7) #define AXP_LDO3_VOLT_MASK __BITS(0,6) #define AXP_LDO3_VOLT_SHIFT 0 #define AXP_SHUTDOWN 0x32 #define AXP_SHUTDOWN_CTRL __BIT(7) #define AXP_BKUP_CTRL 0x35 #define AXP_BKUP_CTRL_ENABLE __BIT(7) #define AXP_BKUP_CTRL_VOLT_MASK __BITS(5,6) #define AXP_BKUP_CTRL_VOLT_SHIFT 5 #define AXP_BKUP_CTRL_VOLT_3V1 0 #define AXP_BKUP_CTRL_VOLT_3V0 1 #define AXP_BKUP_CTRL_VOLT_3V6 2 #define AXP_BKUP_CTRL_VOLT_2V5 3 static int bkup_volt[] = { 3100, 3000, 3600, 2500 }; #define AXP_BKUP_CTRL_CURR_MASK __BITS(0,1) #define AXP_BKUP_CTRL_CURR_SHIFT 0 #define AXP_BKUP_CTRL_CURR_50U 0 #define AXP_BKUP_CTRL_CURR_100U 1 #define AXP_BKUP_CTRL_CURR_200U 2 #define AXP_BKUP_CTRL_CURR_400U 3 static int bkup_curr[] = { 50, 100, 200, 400 }; #define AXP_ACV_MON_REG 0x56 /* 2 bytes */ #define AXP_ACI_MON_REG 0x58 /* 2 bytes */ #define AXP_VBUSV_MON_REG 0x5a /* 2 bytes */ #define AXP_VBUSI_MON_REG 0x5c /* 2 bytes */ #define AXP_TEMP_MON_REG 0x5e /* 2 bytes */ #define AXP_BATTV_MON_REG 0x78 /* 2 bytes */ #define AXP_BATTCI_MON_REG 0x7a /* 2 bytes */ #define AXP_BATTDI_MON_REG 0x7c /* 2 bytes */ #define AXP_APSV_MON_REG 0x7e /* 2 bytes */ #define AXP_ADC_EN1 0x82 #define AXP_ADC_EN1_BATTV __BIT(7) #define AXP_ADC_EN1_BATTI __BIT(6) #define AXP_ADC_EN1_ACV __BIT(5) #define AXP_ADC_EN1_ACI __BIT(4) #define AXP_ADC_EN1_VBUSV __BIT(3) #define AXP_ADC_EN1_VBUSI __BIT(2) #define AXP_ADC_EN1_APSV __BIT(1) #define AXP_ADC_EN1_TS __BIT(0) #define AXP_ADC_EN2 0x83 #define AXP_ADC_EN2_TEMP __BIT(7) #define AXP_SENSOR_ACOK 0 #define AXP_SENSOR_ACV 1 #define AXP_SENSOR_ACI 2 #define AXP_SENSOR_VBUSOK 3 #define AXP_SENSOR_VBUSV 4 #define AXP_SENSOR_VBUSI 5 #define AXP_SENSOR_BATTOK 6 #define AXP_SENSOR_BATTV 7 #define AXP_SENSOR_BATTI 8 #define AXP_SENSOR_APSV 9 #define AXP_SENSOR_TEMP 10 #define AXP_NSENSORS (AXP_SENSOR_TEMP + 1) /* define per-ADC LSB to uV/uA values */ static int axp20x_sensors_lsb[] = { 0, /* AXP_SENSOR_ACOK */ 1700, /* AXP_SENSOR_ACV */ 625, /* AXP_SENSOR_ACI */ 0, 1700, /* AXP_SENSOR_VBUSV */ 375, /* AXP_SENSOR_VBUSI */ 0, 1100, /* AXP_SENSOR_BATTV */ 500, /* AXP_SENSOR_BATTI */ 1400, /* AXP_SENSOR_APSV */ }; struct axp20x_softc { device_t sc_dev; i2c_tag_t sc_i2c; i2c_addr_t sc_addr; int sc_phandle; uint8_t sc_inputstatus; uint8_t sc_powermode; struct sysmon_envsys *sc_sme; envsys_data_t sc_sensor[AXP_NSENSORS]; }; static int axp20x_match(device_t, cfdata_t, void *); static void axp20x_attach(device_t, device_t, void *); static void axp20x_sensors_refresh(struct sysmon_envsys *, envsys_data_t *); static int axp20x_read(struct axp20x_softc *, uint8_t, uint8_t *, size_t); static int axp20x_write(struct axp20x_softc *, uint8_t, uint8_t *, size_t); static void axp20x_fdt_attach(struct axp20x_softc *); CFATTACH_DECL_NEW(axp20x, sizeof(struct axp20x_softc), axp20x_match, axp20x_attach, NULL, NULL); static const struct device_compatible_entry compat_data[] = { { .compat = "x-powers,axp209" }, DEVICE_COMPAT_EOL }; static int axp20x_match(device_t parent, cfdata_t match, void *aux) { struct i2c_attach_args * const ia = aux; int match_result; if (iic_use_direct_match(ia, match, compat_data, &match_result)) return match_result; /* This device is direct-config only. */ return 0; } static void axp20x_attach(device_t parent, device_t self, void *aux) { struct axp20x_softc *sc = device_private(self); struct i2c_attach_args *ia = aux; int first; int error; uint8_t value; sc->sc_dev = self; sc->sc_i2c = ia->ia_tag; sc->sc_addr = ia->ia_addr; sc->sc_phandle = ia->ia_cookie; error = axp20x_read(sc, AXP_INPUT_STATUS, &sc->sc_inputstatus, 1); if (error) { aprint_error(": can't read status: %d\n", error); return; } error = axp20x_read(sc, AXP_POWER_MODE, &sc->sc_powermode, 1); if (error) { aprint_error(": can't read power mode: %d\n", error); return; } value = AXP_ADC_EN1_ACV | AXP_ADC_EN1_ACI | AXP_ADC_EN1_VBUSV | AXP_ADC_EN1_VBUSI | AXP_ADC_EN1_APSV | AXP_ADC_EN1_TS; if (sc->sc_powermode & AXP_POWER_MODE_BATTOK) value |= AXP_ADC_EN1_BATTV | AXP_ADC_EN1_BATTI; error = axp20x_write(sc, AXP_ADC_EN1, &value, 1); if (error) { aprint_error(": can't set AXP_ADC_EN1\n"); return; } error = axp20x_read(sc, AXP_ADC_EN2, &value, 1); if (error) { aprint_error(": can't read AXP_ADC_EN2\n"); return; } value |= AXP_ADC_EN2_TEMP; error = axp20x_write(sc, AXP_ADC_EN2, &value, 1); if (error) { aprint_error(": can't set AXP_ADC_EN2\n"); return; } aprint_naive("\n"); first = 1; if (sc->sc_inputstatus & AXP_INPUT_STATUS_AC_OK) { aprint_verbose(": AC used"); first = 0; } else if (sc->sc_inputstatus & AXP_INPUT_STATUS_AC_PRESENT) { aprint_verbose(": AC present (but unused)"); first = 0; } if (sc->sc_inputstatus & AXP_INPUT_STATUS_VBUS_OK) { aprint_verbose("%s VBUS used", first ? ":" : ","); first = 0; } else if (sc->sc_inputstatus & AXP_INPUT_STATUS_VBUS_PRESENT) { aprint_verbose("%s VBUS present (but unused)", first ? ":" : ","); first = 0; } if (sc->sc_powermode & AXP_POWER_MODE_BATTOK) { aprint_verbose("%s battery present", first ? ":" : ","); } aprint_normal("\n"); sc->sc_sme = sysmon_envsys_create(); sc->sc_sme->sme_name = device_xname(self); sc->sc_sme->sme_cookie = sc; sc->sc_sme->sme_refresh = axp20x_sensors_refresh; sc->sc_sensor[AXP_SENSOR_ACOK].units = ENVSYS_INDICATOR; sc->sc_sensor[AXP_SENSOR_ACOK].state = ENVSYS_SVALID; sc->sc_sensor[AXP_SENSOR_ACOK].value_cur = (sc->sc_inputstatus & AXP_INPUT_STATUS_AC_OK) ? 1 : 0; snprintf(sc->sc_sensor[AXP_SENSOR_ACOK].desc, sizeof(sc->sc_sensor[AXP_SENSOR_ACOK].desc), "AC input"); sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_ACOK]); sc->sc_sensor[AXP_SENSOR_ACV].units = ENVSYS_SVOLTS_DC; sc->sc_sensor[AXP_SENSOR_ACV].state = ENVSYS_SINVALID; sc->sc_sensor[AXP_SENSOR_ACV].flags = ENVSYS_FHAS_ENTROPY; snprintf(sc->sc_sensor[AXP_SENSOR_ACV].desc, sizeof(sc->sc_sensor[AXP_SENSOR_ACV].desc), "AC input voltage"); sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_ACV]); sc->sc_sensor[AXP_SENSOR_ACI].units = ENVSYS_SAMPS; sc->sc_sensor[AXP_SENSOR_ACI].state = ENVSYS_SINVALID; sc->sc_sensor[AXP_SENSOR_ACI].flags = ENVSYS_FHAS_ENTROPY; snprintf(sc->sc_sensor[AXP_SENSOR_ACI].desc, sizeof(sc->sc_sensor[AXP_SENSOR_ACI].desc), "AC input current"); sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_ACI]); sc->sc_sensor[AXP_SENSOR_VBUSOK].units = ENVSYS_INDICATOR; sc->sc_sensor[AXP_SENSOR_VBUSOK].state = ENVSYS_SVALID; sc->sc_sensor[AXP_SENSOR_VBUSOK].value_cur = (sc->sc_inputstatus & AXP_INPUT_STATUS_VBUS_OK) ? 1 : 0; snprintf(sc->sc_sensor[AXP_SENSOR_VBUSOK].desc, sizeof(sc->sc_sensor[AXP_SENSOR_VBUSOK].desc), "VBUS input"); sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_VBUSOK]); sc->sc_sensor[AXP_SENSOR_VBUSV].units = ENVSYS_SVOLTS_DC; sc->sc_sensor[AXP_SENSOR_VBUSV].state = ENVSYS_SINVALID; sc->sc_sensor[AXP_SENSOR_VBUSV].flags = ENVSYS_FHAS_ENTROPY; snprintf(sc->sc_sensor[AXP_SENSOR_VBUSV].desc, sizeof(sc->sc_sensor[AXP_SENSOR_VBUSV].desc), "VBUS input voltage"); sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_VBUSV]); sc->sc_sensor[AXP_SENSOR_VBUSI].units = ENVSYS_SAMPS; sc->sc_sensor[AXP_SENSOR_VBUSI].state = ENVSYS_SINVALID; sc->sc_sensor[AXP_SENSOR_VBUSI].flags = ENVSYS_FHAS_ENTROPY; snprintf(sc->sc_sensor[AXP_SENSOR_VBUSI].desc, sizeof(sc->sc_sensor[AXP_SENSOR_VBUSI].desc), "VBUS input current"); sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_VBUSI]); sc->sc_sensor[AXP_SENSOR_BATTOK].units = ENVSYS_INDICATOR; sc->sc_sensor[AXP_SENSOR_BATTOK].state = ENVSYS_SVALID; sc->sc_sensor[AXP_SENSOR_BATTOK].value_cur = (sc->sc_powermode & AXP_POWER_MODE_BATTOK) ? 1 : 0; snprintf(sc->sc_sensor[AXP_SENSOR_BATTOK].desc, sizeof(sc->sc_sensor[AXP_SENSOR_BATTOK].desc), "battery"); sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_BATTOK]); sc->sc_sensor[AXP_SENSOR_BATTV].units = ENVSYS_SVOLTS_DC; sc->sc_sensor[AXP_SENSOR_BATTV].state = ENVSYS_SINVALID; sc->sc_sensor[AXP_SENSOR_BATTV].flags = ENVSYS_FHAS_ENTROPY; snprintf(sc->sc_sensor[AXP_SENSOR_BATTV].desc, sizeof(sc->sc_sensor[AXP_SENSOR_BATTV].desc), "battery voltage"); sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_BATTV]); sc->sc_sensor[AXP_SENSOR_BATTI].units = ENVSYS_SAMPS; sc->sc_sensor[AXP_SENSOR_BATTI].state = ENVSYS_SINVALID; sc->sc_sensor[AXP_SENSOR_BATTI].flags = ENVSYS_FHAS_ENTROPY; snprintf(sc->sc_sensor[AXP_SENSOR_BATTI].desc, sizeof(sc->sc_sensor[AXP_SENSOR_BATTI].desc), "battery current"); sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_BATTI]); sc->sc_sensor[AXP_SENSOR_APSV].units = ENVSYS_SVOLTS_DC; sc->sc_sensor[AXP_SENSOR_APSV].state = ENVSYS_SINVALID; sc->sc_sensor[AXP_SENSOR_APSV].flags = ENVSYS_FHAS_ENTROPY; snprintf(sc->sc_sensor[AXP_SENSOR_APSV].desc, sizeof(sc->sc_sensor[AXP_SENSOR_APSV].desc), "APS output voltage"); sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_APSV]); sc->sc_sensor[AXP_SENSOR_TEMP].units = ENVSYS_STEMP; sc->sc_sensor[AXP_SENSOR_TEMP].state = ENVSYS_SINVALID; sc->sc_sensor[AXP_SENSOR_TEMP].flags = ENVSYS_FHAS_ENTROPY; snprintf(sc->sc_sensor[AXP_SENSOR_TEMP].desc, sizeof(sc->sc_sensor[AXP_SENSOR_TEMP].desc), "internal temperature"); sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_TEMP]); sysmon_envsys_register(sc->sc_sme); if (axp20x_read(sc, AXP_DCDC2, &value, 1) == 0) { aprint_verbose_dev(sc->sc_dev, "DCDC2 %dmV\n", (int)(700 + (value & AXP_DCDC2_VOLT_MASK) * 25)); } if (axp20x_read(sc, AXP_DCDC3, &value, 1) == 0) { aprint_verbose_dev(sc->sc_dev, "DCDC3 %dmV\n", (int)(700 + (value & AXP_DCDC3_VOLT_MASK) * 25)); } if (axp20x_read(sc, AXP_LDO2_4, &value, 1) == 0) { aprint_verbose_dev(sc->sc_dev, "LDO2 %dmV, LDO4 %dmV\n", (int)(1800 + ((value & AXP_LDO2_VOLT_MASK) >> AXP_LDO2_VOLT_SHIFT) * 100 ), ldo4_mvV[(value & AXP_LDO4_VOLT_MASK) >> AXP_LDO4_VOLT_SHIFT]); } if (axp20x_read(sc, AXP_LDO3, &value, 1) == 0) { if (value & AXP_LDO3_TRACK) { aprint_verbose_dev(sc->sc_dev, "LDO3: tracking\n"); } else { aprint_verbose_dev(sc->sc_dev, "LDO3 %dmV\n", (int)(700 + (value & AXP_LDO3_VOLT_MASK) * 25)); } } if (axp20x_read(sc, AXP_BKUP_CTRL, &value, 1) == 0) { if (value & AXP_BKUP_CTRL_ENABLE) { aprint_verbose_dev(sc->sc_dev, "RTC supercap charger enabled: %dmV at %duA\n", bkup_volt[(value & AXP_BKUP_CTRL_VOLT_MASK) >> AXP_BKUP_CTRL_VOLT_SHIFT], bkup_curr[(value & AXP_BKUP_CTRL_CURR_MASK) >> AXP_BKUP_CTRL_CURR_SHIFT] ); } } axp20x_fdt_attach(sc); } static void axp20x_sensors_refresh_volt(struct axp20x_softc *sc, int reg, envsys_data_t *edata) { uint8_t buf[2]; int error; error = axp20x_read(sc, reg, buf, sizeof(buf)); if (error) { edata->state = ENVSYS_SINVALID; } else { edata->value_cur = ((buf[0] << 4) | (buf[1] & 0xf)) * axp20x_sensors_lsb[edata->sensor]; edata->state = ENVSYS_SVALID; } } static void axp20x_sensors_refresh_amp(struct axp20x_softc *sc, int reg, envsys_data_t *edata) { uint8_t buf[2]; int error; error = axp20x_read(sc, reg, buf, sizeof(buf)); if (error) { edata->state = ENVSYS_SINVALID; } else { edata->value_cur = ((buf[0] << 4) | (buf[1] & 0xf)) * axp20x_sensors_lsb[edata->sensor]; edata->state = ENVSYS_SVALID; } } static void axp20x_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) { struct axp20x_softc *sc = sme->sme_cookie; uint8_t buf[2]; int error; switch(edata->sensor) { case AXP_SENSOR_ACOK: case AXP_SENSOR_VBUSOK: error = axp20x_read(sc, AXP_INPUT_STATUS, &sc->sc_inputstatus, 1); if (error) { edata->state = ENVSYS_SINVALID; return; } if (edata->sensor == AXP_SENSOR_ACOK) { edata->value_cur = (sc->sc_inputstatus & AXP_INPUT_STATUS_AC_OK) ? 1 : 0; } else { edata->value_cur = (sc->sc_inputstatus & AXP_INPUT_STATUS_VBUS_OK) ? 1 : 0; } edata->state = ENVSYS_SVALID; return; case AXP_SENSOR_BATTOK: error = axp20x_read(sc, AXP_POWER_MODE, &sc->sc_powermode, 1); if (error) { edata->state = ENVSYS_SINVALID; return; } edata->value_cur = (sc->sc_powermode & AXP_POWER_MODE_BATTOK) ? 1 : 0; return; case AXP_SENSOR_ACV: if (sc->sc_inputstatus & AXP_INPUT_STATUS_AC_OK) axp20x_sensors_refresh_volt(sc, AXP_ACV_MON_REG, edata); else edata->state = ENVSYS_SINVALID; return; case AXP_SENSOR_ACI: if (sc->sc_inputstatus & AXP_INPUT_STATUS_AC_OK) axp20x_sensors_refresh_amp(sc, AXP_ACI_MON_REG, edata); else edata->state = ENVSYS_SINVALID; return; case AXP_SENSOR_VBUSV: if (sc->sc_inputstatus & AXP_INPUT_STATUS_VBUS_OK) axp20x_sensors_refresh_volt(sc, AXP_VBUSV_MON_REG, edata); else edata->state = ENVSYS_SINVALID; return; case AXP_SENSOR_VBUSI: if (sc->sc_inputstatus & AXP_INPUT_STATUS_VBUS_OK) axp20x_sensors_refresh_amp(sc, AXP_VBUSI_MON_REG, edata); else edata->state = ENVSYS_SINVALID; return; case AXP_SENSOR_BATTV: if (sc->sc_powermode & AXP_POWER_MODE_BATTOK) axp20x_sensors_refresh_volt(sc, AXP_BATTV_MON_REG, edata); else edata->state = ENVSYS_SINVALID; return; case AXP_SENSOR_BATTI: if ((sc->sc_powermode & AXP_POWER_MODE_BATTOK) == 0) { edata->state = ENVSYS_SINVALID; return; } error = axp20x_read(sc, AXP_POWER_MODE, &sc->sc_inputstatus, 1); if (error) { edata->state = ENVSYS_SINVALID; return; } if (sc->sc_inputstatus & AXP_POWER_MODE_CHARGING) { axp20x_sensors_refresh_amp(sc, AXP_BATTCI_MON_REG, edata); edata->value_cur = -edata->value_cur; } else { axp20x_sensors_refresh_amp(sc, AXP_BATTDI_MON_REG, edata); } return; case AXP_SENSOR_APSV: axp20x_sensors_refresh_volt(sc, AXP_APSV_MON_REG, edata); return; case AXP_SENSOR_TEMP: error = axp20x_read(sc, AXP_TEMP_MON_REG, buf, sizeof(buf)); if (error) { edata->state = ENVSYS_SINVALID; } else { /* between -144.7C and 264.8C, step +0.1C */ edata->value_cur = (((buf[0] << 4) | (buf[1] & 0xf)) - 1447) * 100000 + 273150000; edata->state = ENVSYS_SVALID; } return; default: aprint_error_dev(sc->sc_dev, "invalid sensor %d\n", edata->sensor); } } static int axp20x_read(struct axp20x_softc *sc, uint8_t reg, uint8_t *val, size_t len) { int ret; ret = iic_acquire_bus(sc->sc_i2c, 0); if (ret == 0) { ret = iic_exec(sc->sc_i2c, I2C_OP_READ_WITH_STOP, sc->sc_addr, ®, 1, val, len, 0); iic_release_bus(sc->sc_i2c, 0); } return ret; } static int axp20x_write(struct axp20x_softc *sc, uint8_t reg, uint8_t *val, size_t len) { int ret; ret = iic_acquire_bus(sc->sc_i2c, 0); if (ret == 0) { ret = iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, ®, 1, val, len, 0); iic_release_bus(sc->sc_i2c, 0); } return ret; } static int axp20x_set_dcdc(device_t dev, int dcdc, int mvolt) { struct axp20x_softc *sc = device_private(dev); int ret; int value; uint8_t reg; KASSERT(sc != NULL); value = (mvolt - 700) / 25; switch (dcdc) { case AXP20X_DCDC2: value <<= AXP_DCDC2_VOLT_SHIFT; if (value > AXP_DCDC2_VOLT_MASK) return EINVAL; reg = value & AXP_DCDC2_VOLT_MASK; ret = axp20x_write(sc, AXP_DCDC2, ®, 1); if (ret) return ret; if (axp20x_read(sc, AXP_DCDC2, ®, 1) == 0) { aprint_debug_dev(sc->sc_dev, "DCDC2 changed to %dmV\n", (int)(700 + (reg & AXP_DCDC2_VOLT_MASK) * 25)); } return 0; case AXP20X_DCDC3: value <<= AXP_DCDC3_VOLT_SHIFT; if (value > AXP_DCDC3_VOLT_MASK) return EINVAL; reg = value & AXP_DCDC3_VOLT_MASK; ret = axp20x_write(sc, AXP_DCDC3, ®, 1); if (ret) return ret; if (axp20x_read(sc, AXP_DCDC3, ®, 1) == 0) { aprint_debug_dev(sc->sc_dev, "DCDC3 changed to %dmV\n", (int)(700 + (reg & AXP_DCDC3_VOLT_MASK) * 25)); } return 0; default: aprint_error_dev(dev, "wrong DCDC %d\n", dcdc); return EINVAL; } } static int axp20x_get_dcdc(device_t dev, int dcdc, int *pmvolt) { struct axp20x_softc *sc = device_private(dev); uint8_t reg; int error; switch (dcdc) { case AXP20X_DCDC2: error = axp20x_read(sc, AXP_DCDC2, ®, 1); if (error != 0) return error; *pmvolt = __SHIFTOUT(reg, AXP_DCDC2_VOLT_MASK) * 25 + 700; return 0; case AXP20X_DCDC3: error = axp20x_read(sc, AXP_DCDC3, ®, 1); if (error != 0) return error; *pmvolt = __SHIFTOUT(reg, AXP_DCDC3_VOLT_MASK) * 25 + 700; return 0; default: return EINVAL; } } static void axp20x_poweroff(device_t dev) { struct axp20x_softc * const sc = device_private(dev); uint8_t reg = AXP_SHUTDOWN_CTRL; int error; error = axp20x_write(sc, AXP_SHUTDOWN, ®, 1); if (error) { device_printf(dev, "WARNING: unable to power off, error %d\n", error); } } static const struct axp20xregdef { const char *name; int dcdc; } axp20x_regdefs[] = { { "dcdc2", AXP20X_DCDC2 }, { "dcdc3", AXP20X_DCDC3 }, }; struct axp20xreg_softc { device_t sc_dev; int sc_phandle; const struct axp20xregdef *sc_regdef; }; struct axp20xreg_attach_args { int reg_phandle; }; static int axp20xreg_acquire(device_t dev) { return 0; } static void axp20xreg_release(device_t dev) { } static int axp20xreg_enable(device_t dev, bool enable) { /* TODO */ return enable ? 0 : EINVAL; } static int axp20xreg_set_voltage(device_t dev, u_int min_uvol, u_int max_uvol) { struct axp20xreg_softc * const sc = device_private(dev); return axp20x_set_dcdc(device_parent(dev), sc->sc_regdef->dcdc, min_uvol / 1000); } static int axp20xreg_get_voltage(device_t dev, u_int *puvol) { struct axp20xreg_softc * const sc = device_private(dev); int mvol, error; error = axp20x_get_dcdc(device_parent(dev), sc->sc_regdef->dcdc, &mvol); if (error != 0) return error; *puvol = mvol * 1000; return 0; } static struct fdtbus_regulator_controller_func axp20xreg_funcs = { .acquire = axp20xreg_acquire, .release = axp20xreg_release, .enable = axp20xreg_enable, .set_voltage = axp20xreg_set_voltage, .get_voltage = axp20xreg_get_voltage, }; static const struct axp20xregdef * axp20xreg_lookup(int phandle) { const char *name; int n; name = fdtbus_get_string(phandle, "name"); if (name == NULL) return NULL; for (n = 0; n < __arraycount(axp20x_regdefs); n++) if (strcmp(name, axp20x_regdefs[n].name) == 0) return &axp20x_regdefs[n]; return NULL; } static int axp20xreg_match(device_t parent, cfdata_t match, void *aux) { const struct axp20xreg_attach_args *reg = aux; return axp20xreg_lookup(reg->reg_phandle) != NULL; } static void axp20xreg_attach(device_t parent, device_t self, void *aux) { struct axp20xreg_softc * const sc = device_private(self); const struct axp20xreg_attach_args *reg = aux; const char *regulator_name; sc->sc_dev = self; sc->sc_phandle = reg->reg_phandle; sc->sc_regdef = axp20xreg_lookup(reg->reg_phandle); regulator_name = fdtbus_get_string(reg->reg_phandle, "regulator-name"); aprint_naive("\n"); if (regulator_name) aprint_normal(": %s (%s)\n", sc->sc_regdef->name, regulator_name); else aprint_normal(": %s\n", sc->sc_regdef->name); fdtbus_register_regulator_controller(self, sc->sc_phandle, &axp20xreg_funcs); } CFATTACH_DECL_NEW(axp20xreg, sizeof(struct axp20xreg_softc), axp20xreg_match, axp20xreg_attach, NULL, NULL); static void axp20x_fdt_poweroff(device_t dev) { delay(1000000); axp20x_poweroff(dev); } static struct fdtbus_power_controller_func axp20x_fdt_power_funcs = { .poweroff = axp20x_fdt_poweroff, }; static void axp20x_fdt_attach(struct axp20x_softc *sc) { int regulators_phandle, child; fdtbus_register_power_controller(sc->sc_dev, sc->sc_phandle, &axp20x_fdt_power_funcs); regulators_phandle = of_find_firstchild_byname(sc->sc_phandle, "regulators"); if (regulators_phandle == -1) return; for (child = OF_child(regulators_phandle); child; child = OF_peer(child)) { struct axp20xreg_attach_args reg = { .reg_phandle = child }; config_found(sc->sc_dev, ®, NULL, CFARGS_NONE); } }