1*c7fb772bSthorpej /* $NetBSD: axp20x.c,v 1.21 2021/08/07 16:19:11 thorpej Exp $ */
2e5f864cfSjmcneill
3e5f864cfSjmcneill /*-
490be26ceSjmcneill * Copyright (c) 2014-2017 Jared McNeill <jmcneill@invisible.ca>
5e5f864cfSjmcneill * All rights reserved.
6e5f864cfSjmcneill *
7e5f864cfSjmcneill * Redistribution and use in source and binary forms, with or without
8e5f864cfSjmcneill * modification, are permitted provided that the following conditions
9e5f864cfSjmcneill * are met:
10e5f864cfSjmcneill * 1. Redistributions of source code must retain the above copyright
11e5f864cfSjmcneill * notice, this list of conditions and the following disclaimer.
12e5f864cfSjmcneill * 2. Redistributions in binary form must reproduce the above copyright
13e5f864cfSjmcneill * notice, this list of conditions and the following disclaimer in the
14e5f864cfSjmcneill * documentation and/or other materials provided with the distribution.
15e5f864cfSjmcneill *
16e5f864cfSjmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17e5f864cfSjmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18e5f864cfSjmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19e5f864cfSjmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20e5f864cfSjmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21e5f864cfSjmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22e5f864cfSjmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23e5f864cfSjmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24e5f864cfSjmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25e5f864cfSjmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26e5f864cfSjmcneill * POSSIBILITY OF SUCH DAMAGE.
27e5f864cfSjmcneill */
28e5f864cfSjmcneill
29e5f864cfSjmcneill #include <sys/cdefs.h>
30*c7fb772bSthorpej __KERNEL_RCSID(0, "$NetBSD: axp20x.c,v 1.21 2021/08/07 16:19:11 thorpej Exp $");
31e5f864cfSjmcneill
32e5f864cfSjmcneill #include <sys/param.h>
33e5f864cfSjmcneill #include <sys/systm.h>
34e5f864cfSjmcneill #include <sys/device.h>
35e5f864cfSjmcneill #include <sys/conf.h>
36e5f864cfSjmcneill #include <sys/bus.h>
37e5f864cfSjmcneill #include <sys/kmem.h>
38e5f864cfSjmcneill
39e5f864cfSjmcneill #include <dev/i2c/i2cvar.h>
40e5f864cfSjmcneill
41e5f864cfSjmcneill #include <dev/sysmon/sysmonvar.h>
42e5f864cfSjmcneill
4315cd199cSjmcneill #include <dev/fdt/fdtvar.h>
448acfda79Sthorpej
458acfda79Sthorpej #define AXP20X_DCDC2 2
468acfda79Sthorpej #define AXP20X_DCDC3 3
4715cd199cSjmcneill
48aa41e992Sthorpej #define AXP209_I2C_ADDR 0x34
49aa41e992Sthorpej
501296b5d4Sbouyer #define AXP_INPUT_STATUS 0x00
511296b5d4Sbouyer #define AXP_INPUT_STATUS_AC_PRESENT __BIT(7)
521296b5d4Sbouyer #define AXP_INPUT_STATUS_AC_OK __BIT(6)
531296b5d4Sbouyer #define AXP_INPUT_STATUS_VBUS_PRESENT __BIT(5)
541296b5d4Sbouyer #define AXP_INPUT_STATUS_VBUS_OK __BIT(4)
551296b5d4Sbouyer
561296b5d4Sbouyer #define AXP_POWER_MODE 0x01
571296b5d4Sbouyer #define AXP_POWER_MODE_OVERTEMP __BIT(7)
581296b5d4Sbouyer #define AXP_POWER_MODE_CHARGING __BIT(6)
591296b5d4Sbouyer #define AXP_POWER_MODE_BATTOK __BIT(5)
601296b5d4Sbouyer
611296b5d4Sbouyer #define AXP_POWEROUT_CTRL 0x12
621296b5d4Sbouyer #define AXP_POWEROUT_CTRL_LDO3 __BIT(6)
631296b5d4Sbouyer #define AXP_POWEROUT_CTRL_DCDC2 __BIT(4)
641296b5d4Sbouyer #define AXP_POWEROUT_CTRL_LDO4 __BIT(3)
651296b5d4Sbouyer #define AXP_POWEROUT_CTRL_LDO2 __BIT(2)
661296b5d4Sbouyer #define AXP_POWEROUT_CTRL_DCDC3 __BIT(1)
671296b5d4Sbouyer #define AXP_POWEROUT_CTRL_EXTEN __BIT(0)
681296b5d4Sbouyer
691296b5d4Sbouyer #define AXP_DCDC2 0x23
701296b5d4Sbouyer #define AXP_DCDC2_VOLT_MASK __BITS(0,5)
711296b5d4Sbouyer #define AXP_DCDC2_VOLT_SHIFT 0
721296b5d4Sbouyer
731296b5d4Sbouyer #define AXP_DCDC2_LDO3_VRC 0x25
741296b5d4Sbouyer
751296b5d4Sbouyer #define AXP_DCDC3 0x27
761296b5d4Sbouyer #define AXP_DCDC3_VOLT_MASK __BITS(0,6)
771296b5d4Sbouyer #define AXP_DCDC3_VOLT_SHIFT 0
781296b5d4Sbouyer
791296b5d4Sbouyer #define AXP_LDO2_4 0x28
801296b5d4Sbouyer #define AXP_LDO2_VOLT_MASK __BITS(4,7)
811296b5d4Sbouyer #define AXP_LDO2_VOLT_SHIFT 4
821296b5d4Sbouyer #define AXP_LDO4_VOLT_MASK __BITS(0,3)
831296b5d4Sbouyer #define AXP_LDO4_VOLT_SHIFT 0
841296b5d4Sbouyer static int ldo4_mvV[] = {
851296b5d4Sbouyer 1250,
861296b5d4Sbouyer 1300,
871296b5d4Sbouyer 1400,
881296b5d4Sbouyer 1500,
891296b5d4Sbouyer 1600,
901296b5d4Sbouyer 1700,
911296b5d4Sbouyer 1800,
921296b5d4Sbouyer 1900,
931296b5d4Sbouyer 2000,
941296b5d4Sbouyer 2500,
951296b5d4Sbouyer 2700,
961296b5d4Sbouyer 2800,
971296b5d4Sbouyer 3000,
981296b5d4Sbouyer 3100,
991296b5d4Sbouyer 3200,
1001296b5d4Sbouyer 3300
1011296b5d4Sbouyer };
1021296b5d4Sbouyer
1031296b5d4Sbouyer #define AXP_LDO3 0x29
1041296b5d4Sbouyer #define AXP_LDO3_TRACK __BIT(7)
1051296b5d4Sbouyer #define AXP_LDO3_VOLT_MASK __BITS(0,6)
1061296b5d4Sbouyer #define AXP_LDO3_VOLT_SHIFT 0
1071296b5d4Sbouyer
10815cd199cSjmcneill #define AXP_SHUTDOWN 0x32
10915cd199cSjmcneill #define AXP_SHUTDOWN_CTRL __BIT(7)
11015cd199cSjmcneill
111ad3df8fcStnn #define AXP_BKUP_CTRL 0x35
112ad3df8fcStnn #define AXP_BKUP_CTRL_ENABLE __BIT(7)
113ad3df8fcStnn #define AXP_BKUP_CTRL_VOLT_MASK __BITS(5,6)
114ad3df8fcStnn #define AXP_BKUP_CTRL_VOLT_SHIFT 5
115ad3df8fcStnn #define AXP_BKUP_CTRL_VOLT_3V1 0
116ad3df8fcStnn #define AXP_BKUP_CTRL_VOLT_3V0 1
117ad3df8fcStnn #define AXP_BKUP_CTRL_VOLT_3V6 2
118ad3df8fcStnn #define AXP_BKUP_CTRL_VOLT_2V5 3
119ad3df8fcStnn static int bkup_volt[] = {
120ad3df8fcStnn 3100,
121ad3df8fcStnn 3000,
122ad3df8fcStnn 3600,
123ad3df8fcStnn 2500
124ad3df8fcStnn };
125ad3df8fcStnn #define AXP_BKUP_CTRL_CURR_MASK __BITS(0,1)
126ad3df8fcStnn #define AXP_BKUP_CTRL_CURR_SHIFT 0
127ad3df8fcStnn #define AXP_BKUP_CTRL_CURR_50U 0
128ad3df8fcStnn #define AXP_BKUP_CTRL_CURR_100U 1
129ad3df8fcStnn #define AXP_BKUP_CTRL_CURR_200U 2
130ad3df8fcStnn #define AXP_BKUP_CTRL_CURR_400U 3
131ad3df8fcStnn static int bkup_curr[] = {
132ad3df8fcStnn 50,
133ad3df8fcStnn 100,
134ad3df8fcStnn 200,
135ad3df8fcStnn 400
136ad3df8fcStnn };
137ad3df8fcStnn
1381296b5d4Sbouyer #define AXP_ACV_MON_REG 0x56 /* 2 bytes */
1391296b5d4Sbouyer #define AXP_ACI_MON_REG 0x58 /* 2 bytes */
1401296b5d4Sbouyer #define AXP_VBUSV_MON_REG 0x5a /* 2 bytes */
1411296b5d4Sbouyer #define AXP_VBUSI_MON_REG 0x5c /* 2 bytes */
142e5f864cfSjmcneill #define AXP_TEMP_MON_REG 0x5e /* 2 bytes */
1431296b5d4Sbouyer #define AXP_BATTV_MON_REG 0x78 /* 2 bytes */
1441296b5d4Sbouyer #define AXP_BATTCI_MON_REG 0x7a /* 2 bytes */
1451296b5d4Sbouyer #define AXP_BATTDI_MON_REG 0x7c /* 2 bytes */
1461296b5d4Sbouyer #define AXP_APSV_MON_REG 0x7e /* 2 bytes */
1471296b5d4Sbouyer
1481296b5d4Sbouyer #define AXP_ADC_EN1 0x82
1491296b5d4Sbouyer #define AXP_ADC_EN1_BATTV __BIT(7)
1501296b5d4Sbouyer #define AXP_ADC_EN1_BATTI __BIT(6)
1511296b5d4Sbouyer #define AXP_ADC_EN1_ACV __BIT(5)
1521296b5d4Sbouyer #define AXP_ADC_EN1_ACI __BIT(4)
1531296b5d4Sbouyer #define AXP_ADC_EN1_VBUSV __BIT(3)
1541296b5d4Sbouyer #define AXP_ADC_EN1_VBUSI __BIT(2)
1551296b5d4Sbouyer #define AXP_ADC_EN1_APSV __BIT(1)
1561296b5d4Sbouyer #define AXP_ADC_EN1_TS __BIT(0)
1571296b5d4Sbouyer #define AXP_ADC_EN2 0x83
1581296b5d4Sbouyer #define AXP_ADC_EN2_TEMP __BIT(7)
1591296b5d4Sbouyer
1601296b5d4Sbouyer #define AXP_SENSOR_ACOK 0
1611296b5d4Sbouyer #define AXP_SENSOR_ACV 1
1621296b5d4Sbouyer #define AXP_SENSOR_ACI 2
1631296b5d4Sbouyer #define AXP_SENSOR_VBUSOK 3
1641296b5d4Sbouyer #define AXP_SENSOR_VBUSV 4
1651296b5d4Sbouyer #define AXP_SENSOR_VBUSI 5
1661296b5d4Sbouyer #define AXP_SENSOR_BATTOK 6
1671296b5d4Sbouyer #define AXP_SENSOR_BATTV 7
1681296b5d4Sbouyer #define AXP_SENSOR_BATTI 8
1691296b5d4Sbouyer #define AXP_SENSOR_APSV 9
1701296b5d4Sbouyer #define AXP_SENSOR_TEMP 10
1711296b5d4Sbouyer #define AXP_NSENSORS (AXP_SENSOR_TEMP + 1)
1721296b5d4Sbouyer
1731296b5d4Sbouyer /* define per-ADC LSB to uV/uA values */
1741296b5d4Sbouyer static int axp20x_sensors_lsb[] = {
1751296b5d4Sbouyer 0, /* AXP_SENSOR_ACOK */
1761296b5d4Sbouyer 1700, /* AXP_SENSOR_ACV */
1771296b5d4Sbouyer 625, /* AXP_SENSOR_ACI */
1781296b5d4Sbouyer 0,
1791296b5d4Sbouyer 1700, /* AXP_SENSOR_VBUSV */
1801296b5d4Sbouyer 375, /* AXP_SENSOR_VBUSI */
1811296b5d4Sbouyer 0,
1821296b5d4Sbouyer 1100, /* AXP_SENSOR_BATTV */
1831296b5d4Sbouyer 500, /* AXP_SENSOR_BATTI */
1841296b5d4Sbouyer 1400, /* AXP_SENSOR_APSV */
1851296b5d4Sbouyer };
1861296b5d4Sbouyer
187e5f864cfSjmcneill
188e5f864cfSjmcneill struct axp20x_softc {
189e5f864cfSjmcneill device_t sc_dev;
190e5f864cfSjmcneill i2c_tag_t sc_i2c;
191e5f864cfSjmcneill i2c_addr_t sc_addr;
19290be26ceSjmcneill int sc_phandle;
193e5f864cfSjmcneill
1941296b5d4Sbouyer uint8_t sc_inputstatus;
1951296b5d4Sbouyer uint8_t sc_powermode;
1961296b5d4Sbouyer
197e5f864cfSjmcneill struct sysmon_envsys *sc_sme;
1981296b5d4Sbouyer envsys_data_t sc_sensor[AXP_NSENSORS];
199e5f864cfSjmcneill };
200e5f864cfSjmcneill
201e5f864cfSjmcneill static int axp20x_match(device_t, cfdata_t, void *);
202e5f864cfSjmcneill static void axp20x_attach(device_t, device_t, void *);
203e5f864cfSjmcneill
204e5f864cfSjmcneill static void axp20x_sensors_refresh(struct sysmon_envsys *, envsys_data_t *);
2058acfda79Sthorpej static int axp20x_read(struct axp20x_softc *, uint8_t, uint8_t *, size_t);
2068acfda79Sthorpej static int axp20x_write(struct axp20x_softc *, uint8_t, uint8_t *, size_t);
207e5f864cfSjmcneill
20815cd199cSjmcneill static void axp20x_fdt_attach(struct axp20x_softc *);
20915cd199cSjmcneill
210e5f864cfSjmcneill CFATTACH_DECL_NEW(axp20x, sizeof(struct axp20x_softc),
211e5f864cfSjmcneill axp20x_match, axp20x_attach, NULL, NULL);
212e5f864cfSjmcneill
213feee3a19Sthorpej static const struct device_compatible_entry compat_data[] = {
2147246a945Sthorpej { .compat = "x-powers,axp209" },
21518f3098cSthorpej DEVICE_COMPAT_EOL
2164f9e5a44Sthorpej };
2174f9e5a44Sthorpej
218e5f864cfSjmcneill static int
axp20x_match(device_t parent,cfdata_t match,void * aux)219e5f864cfSjmcneill axp20x_match(device_t parent, cfdata_t match, void *aux)
220e5f864cfSjmcneill {
22190be26ceSjmcneill struct i2c_attach_args * const ia = aux;
222aa41e992Sthorpej int match_result;
22390be26ceSjmcneill
224feee3a19Sthorpej if (iic_use_direct_match(ia, match, compat_data, &match_result))
225aa41e992Sthorpej return match_result;
22690be26ceSjmcneill
227aa41e992Sthorpej /* This device is direct-config only. */
228aa41e992Sthorpej
229aa41e992Sthorpej return 0;
230e5f864cfSjmcneill }
231e5f864cfSjmcneill
232e5f864cfSjmcneill static void
axp20x_attach(device_t parent,device_t self,void * aux)233e5f864cfSjmcneill axp20x_attach(device_t parent, device_t self, void *aux)
234e5f864cfSjmcneill {
235e5f864cfSjmcneill struct axp20x_softc *sc = device_private(self);
236e5f864cfSjmcneill struct i2c_attach_args *ia = aux;
2371296b5d4Sbouyer int first;
2381296b5d4Sbouyer int error;
2391296b5d4Sbouyer uint8_t value;
240e5f864cfSjmcneill
241e5f864cfSjmcneill sc->sc_dev = self;
242e5f864cfSjmcneill sc->sc_i2c = ia->ia_tag;
243e5f864cfSjmcneill sc->sc_addr = ia->ia_addr;
24490be26ceSjmcneill sc->sc_phandle = ia->ia_cookie;
245e5f864cfSjmcneill
2461296b5d4Sbouyer error = axp20x_read(sc, AXP_INPUT_STATUS,
2478acfda79Sthorpej &sc->sc_inputstatus, 1);
2481296b5d4Sbouyer if (error) {
2491296b5d4Sbouyer aprint_error(": can't read status: %d\n", error);
2501296b5d4Sbouyer return;
2511296b5d4Sbouyer }
2521296b5d4Sbouyer error = axp20x_read(sc, AXP_POWER_MODE,
2538acfda79Sthorpej &sc->sc_powermode, 1);
2541296b5d4Sbouyer if (error) {
2551296b5d4Sbouyer aprint_error(": can't read power mode: %d\n", error);
2561296b5d4Sbouyer return;
2571296b5d4Sbouyer }
2581296b5d4Sbouyer 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;
2591296b5d4Sbouyer if (sc->sc_powermode & AXP_POWER_MODE_BATTOK)
2601296b5d4Sbouyer value |= AXP_ADC_EN1_BATTV | AXP_ADC_EN1_BATTI;
2618acfda79Sthorpej error = axp20x_write(sc, AXP_ADC_EN1, &value, 1);
2621296b5d4Sbouyer if (error) {
2631296b5d4Sbouyer aprint_error(": can't set AXP_ADC_EN1\n");
2641296b5d4Sbouyer return;
2651296b5d4Sbouyer }
2668acfda79Sthorpej error = axp20x_read(sc, AXP_ADC_EN2, &value, 1);
2671296b5d4Sbouyer if (error) {
2681296b5d4Sbouyer aprint_error(": can't read AXP_ADC_EN2\n");
2691296b5d4Sbouyer return;
2701296b5d4Sbouyer }
2711296b5d4Sbouyer value |= AXP_ADC_EN2_TEMP;
2728acfda79Sthorpej error = axp20x_write(sc, AXP_ADC_EN2, &value, 1);
2731296b5d4Sbouyer if (error) {
2741296b5d4Sbouyer aprint_error(": can't set AXP_ADC_EN2\n");
2751296b5d4Sbouyer return;
2761296b5d4Sbouyer }
2771296b5d4Sbouyer
278e5f864cfSjmcneill aprint_naive("\n");
2791296b5d4Sbouyer first = 1;
2801296b5d4Sbouyer if (sc->sc_inputstatus & AXP_INPUT_STATUS_AC_OK) {
28174a232a6Sbouyer aprint_verbose(": AC used");
2821296b5d4Sbouyer first = 0;
2831296b5d4Sbouyer } else if (sc->sc_inputstatus & AXP_INPUT_STATUS_AC_PRESENT) {
28474a232a6Sbouyer aprint_verbose(": AC present (but unused)");
2851296b5d4Sbouyer first = 0;
2861296b5d4Sbouyer }
2871296b5d4Sbouyer if (sc->sc_inputstatus & AXP_INPUT_STATUS_VBUS_OK) {
28874a232a6Sbouyer aprint_verbose("%s VBUS used", first ? ":" : ",");
2891296b5d4Sbouyer first = 0;
2901296b5d4Sbouyer } else if (sc->sc_inputstatus & AXP_INPUT_STATUS_VBUS_PRESENT) {
29174a232a6Sbouyer aprint_verbose("%s VBUS present (but unused)", first ? ":" : ",");
2921296b5d4Sbouyer first = 0;
2931296b5d4Sbouyer }
2941296b5d4Sbouyer if (sc->sc_powermode & AXP_POWER_MODE_BATTOK) {
29574a232a6Sbouyer aprint_verbose("%s battery present", first ? ":" : ",");
2961296b5d4Sbouyer }
297e5f864cfSjmcneill aprint_normal("\n");
298e5f864cfSjmcneill
299e5f864cfSjmcneill sc->sc_sme = sysmon_envsys_create();
300e5f864cfSjmcneill sc->sc_sme->sme_name = device_xname(self);
301e5f864cfSjmcneill sc->sc_sme->sme_cookie = sc;
302e5f864cfSjmcneill sc->sc_sme->sme_refresh = axp20x_sensors_refresh;
303e5f864cfSjmcneill
3041296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_ACOK].units = ENVSYS_INDICATOR;
3051296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_ACOK].state = ENVSYS_SVALID;
3061296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_ACOK].value_cur =
3071296b5d4Sbouyer (sc->sc_inputstatus & AXP_INPUT_STATUS_AC_OK) ? 1 : 0;
3081296b5d4Sbouyer snprintf(sc->sc_sensor[AXP_SENSOR_ACOK].desc,
3091296b5d4Sbouyer sizeof(sc->sc_sensor[AXP_SENSOR_ACOK].desc), "AC input");
3101296b5d4Sbouyer sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_ACOK]);
3111296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_ACV].units = ENVSYS_SVOLTS_DC;
3121296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_ACV].state = ENVSYS_SINVALID;
3131296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_ACV].flags = ENVSYS_FHAS_ENTROPY;
3141296b5d4Sbouyer snprintf(sc->sc_sensor[AXP_SENSOR_ACV].desc,
3151296b5d4Sbouyer sizeof(sc->sc_sensor[AXP_SENSOR_ACV].desc), "AC input voltage");
3161296b5d4Sbouyer sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_ACV]);
3171296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_ACI].units = ENVSYS_SAMPS;
3181296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_ACI].state = ENVSYS_SINVALID;
3191296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_ACI].flags = ENVSYS_FHAS_ENTROPY;
3201296b5d4Sbouyer snprintf(sc->sc_sensor[AXP_SENSOR_ACI].desc,
3211296b5d4Sbouyer sizeof(sc->sc_sensor[AXP_SENSOR_ACI].desc), "AC input current");
3221296b5d4Sbouyer sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_ACI]);
3231296b5d4Sbouyer
3241296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_VBUSOK].units = ENVSYS_INDICATOR;
3251296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_VBUSOK].state = ENVSYS_SVALID;
3261296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_VBUSOK].value_cur =
3271296b5d4Sbouyer (sc->sc_inputstatus & AXP_INPUT_STATUS_VBUS_OK) ? 1 : 0;
3281296b5d4Sbouyer snprintf(sc->sc_sensor[AXP_SENSOR_VBUSOK].desc,
3291296b5d4Sbouyer sizeof(sc->sc_sensor[AXP_SENSOR_VBUSOK].desc), "VBUS input");
3301296b5d4Sbouyer sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_VBUSOK]);
3311296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_VBUSV].units = ENVSYS_SVOLTS_DC;
3321296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_VBUSV].state = ENVSYS_SINVALID;
3331296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_VBUSV].flags = ENVSYS_FHAS_ENTROPY;
3341296b5d4Sbouyer snprintf(sc->sc_sensor[AXP_SENSOR_VBUSV].desc,
3351296b5d4Sbouyer sizeof(sc->sc_sensor[AXP_SENSOR_VBUSV].desc), "VBUS input voltage");
3361296b5d4Sbouyer sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_VBUSV]);
3371296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_VBUSI].units = ENVSYS_SAMPS;
3381296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_VBUSI].state = ENVSYS_SINVALID;
3391296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_VBUSI].flags = ENVSYS_FHAS_ENTROPY;
3401296b5d4Sbouyer snprintf(sc->sc_sensor[AXP_SENSOR_VBUSI].desc,
3411296b5d4Sbouyer sizeof(sc->sc_sensor[AXP_SENSOR_VBUSI].desc), "VBUS input current");
3421296b5d4Sbouyer sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_VBUSI]);
3431296b5d4Sbouyer
3441296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_BATTOK].units = ENVSYS_INDICATOR;
3451296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_BATTOK].state = ENVSYS_SVALID;
3461296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_BATTOK].value_cur =
3471296b5d4Sbouyer (sc->sc_powermode & AXP_POWER_MODE_BATTOK) ? 1 : 0;
3481296b5d4Sbouyer snprintf(sc->sc_sensor[AXP_SENSOR_BATTOK].desc,
3491296b5d4Sbouyer sizeof(sc->sc_sensor[AXP_SENSOR_BATTOK].desc), "battery");
3501296b5d4Sbouyer sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_BATTOK]);
3511296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_BATTV].units = ENVSYS_SVOLTS_DC;
3521296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_BATTV].state = ENVSYS_SINVALID;
3531296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_BATTV].flags = ENVSYS_FHAS_ENTROPY;
3541296b5d4Sbouyer snprintf(sc->sc_sensor[AXP_SENSOR_BATTV].desc,
3551296b5d4Sbouyer sizeof(sc->sc_sensor[AXP_SENSOR_BATTV].desc), "battery voltage");
3561296b5d4Sbouyer sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_BATTV]);
3571296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_BATTI].units = ENVSYS_SAMPS;
3581296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_BATTI].state = ENVSYS_SINVALID;
3591296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_BATTI].flags = ENVSYS_FHAS_ENTROPY;
3601296b5d4Sbouyer snprintf(sc->sc_sensor[AXP_SENSOR_BATTI].desc,
3611296b5d4Sbouyer sizeof(sc->sc_sensor[AXP_SENSOR_BATTI].desc), "battery current");
3621296b5d4Sbouyer sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_BATTI]);
3631296b5d4Sbouyer
3641296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_APSV].units = ENVSYS_SVOLTS_DC;
3651296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_APSV].state = ENVSYS_SINVALID;
3661296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_APSV].flags = ENVSYS_FHAS_ENTROPY;
3671296b5d4Sbouyer snprintf(sc->sc_sensor[AXP_SENSOR_APSV].desc,
3681296b5d4Sbouyer sizeof(sc->sc_sensor[AXP_SENSOR_APSV].desc), "APS output voltage");
3691296b5d4Sbouyer sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_APSV]);
3701296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_TEMP].units = ENVSYS_STEMP;
3711296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_TEMP].state = ENVSYS_SINVALID;
3721296b5d4Sbouyer sc->sc_sensor[AXP_SENSOR_TEMP].flags = ENVSYS_FHAS_ENTROPY;
3731296b5d4Sbouyer snprintf(sc->sc_sensor[AXP_SENSOR_TEMP].desc,
3741296b5d4Sbouyer sizeof(sc->sc_sensor[AXP_SENSOR_TEMP].desc),
375e5f864cfSjmcneill "internal temperature");
3761296b5d4Sbouyer sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[AXP_SENSOR_TEMP]);
377e5f864cfSjmcneill
378e5f864cfSjmcneill sysmon_envsys_register(sc->sc_sme);
3791296b5d4Sbouyer
3808acfda79Sthorpej if (axp20x_read(sc, AXP_DCDC2, &value, 1) == 0) {
3812c74340eSjmcneill aprint_verbose_dev(sc->sc_dev, "DCDC2 %dmV\n",
3821296b5d4Sbouyer (int)(700 + (value & AXP_DCDC2_VOLT_MASK) * 25));
3831296b5d4Sbouyer }
3848acfda79Sthorpej if (axp20x_read(sc, AXP_DCDC3, &value, 1) == 0) {
3852c74340eSjmcneill aprint_verbose_dev(sc->sc_dev, "DCDC3 %dmV\n",
3861296b5d4Sbouyer (int)(700 + (value & AXP_DCDC3_VOLT_MASK) * 25));
3871296b5d4Sbouyer }
3888acfda79Sthorpej if (axp20x_read(sc, AXP_LDO2_4, &value, 1) == 0) {
3892c74340eSjmcneill aprint_verbose_dev(sc->sc_dev, "LDO2 %dmV, LDO4 %dmV\n",
3901296b5d4Sbouyer (int)(1800 +
3911296b5d4Sbouyer ((value & AXP_LDO2_VOLT_MASK) >> AXP_LDO2_VOLT_SHIFT) * 100
3921296b5d4Sbouyer ),
3931296b5d4Sbouyer ldo4_mvV[(value & AXP_LDO4_VOLT_MASK) >> AXP_LDO4_VOLT_SHIFT]);
3941296b5d4Sbouyer }
3958acfda79Sthorpej if (axp20x_read(sc, AXP_LDO3, &value, 1) == 0) {
3961296b5d4Sbouyer if (value & AXP_LDO3_TRACK) {
3972c74340eSjmcneill aprint_verbose_dev(sc->sc_dev, "LDO3: tracking\n");
3981296b5d4Sbouyer } else {
3992c74340eSjmcneill aprint_verbose_dev(sc->sc_dev, "LDO3 %dmV\n",
4001296b5d4Sbouyer (int)(700 + (value & AXP_LDO3_VOLT_MASK) * 25));
4011296b5d4Sbouyer }
4021296b5d4Sbouyer }
403ad3df8fcStnn
4048acfda79Sthorpej if (axp20x_read(sc, AXP_BKUP_CTRL, &value, 1) == 0) {
405ad3df8fcStnn if (value & AXP_BKUP_CTRL_ENABLE) {
406ad3df8fcStnn aprint_verbose_dev(sc->sc_dev,
407ad3df8fcStnn "RTC supercap charger enabled: %dmV at %duA\n",
408ad3df8fcStnn bkup_volt[(value & AXP_BKUP_CTRL_VOLT_MASK) >>
409ad3df8fcStnn AXP_BKUP_CTRL_VOLT_SHIFT],
410ad3df8fcStnn bkup_curr[(value & AXP_BKUP_CTRL_CURR_MASK) >>
411ad3df8fcStnn AXP_BKUP_CTRL_CURR_SHIFT]
412ad3df8fcStnn );
413ad3df8fcStnn }
414ad3df8fcStnn }
41515cd199cSjmcneill
41615cd199cSjmcneill axp20x_fdt_attach(sc);
4171296b5d4Sbouyer }
4181296b5d4Sbouyer
4191296b5d4Sbouyer static void
axp20x_sensors_refresh_volt(struct axp20x_softc * sc,int reg,envsys_data_t * edata)4201296b5d4Sbouyer axp20x_sensors_refresh_volt(struct axp20x_softc *sc, int reg,
4211296b5d4Sbouyer envsys_data_t *edata)
4221296b5d4Sbouyer {
4231296b5d4Sbouyer uint8_t buf[2];
4241296b5d4Sbouyer int error;
4251296b5d4Sbouyer
4268acfda79Sthorpej error = axp20x_read(sc, reg, buf, sizeof(buf));
4271296b5d4Sbouyer if (error) {
4281296b5d4Sbouyer edata->state = ENVSYS_SINVALID;
4291296b5d4Sbouyer } else {
4301296b5d4Sbouyer edata->value_cur = ((buf[0] << 4) | (buf[1] & 0xf)) *
4311296b5d4Sbouyer axp20x_sensors_lsb[edata->sensor];
4321296b5d4Sbouyer edata->state = ENVSYS_SVALID;
4331296b5d4Sbouyer }
4341296b5d4Sbouyer }
4351296b5d4Sbouyer
4361296b5d4Sbouyer static void
axp20x_sensors_refresh_amp(struct axp20x_softc * sc,int reg,envsys_data_t * edata)4371296b5d4Sbouyer axp20x_sensors_refresh_amp(struct axp20x_softc *sc, int reg,
4381296b5d4Sbouyer envsys_data_t *edata)
4391296b5d4Sbouyer {
4401296b5d4Sbouyer uint8_t buf[2];
4411296b5d4Sbouyer int error;
4421296b5d4Sbouyer
4438acfda79Sthorpej error = axp20x_read(sc, reg, buf, sizeof(buf));
4441296b5d4Sbouyer if (error) {
4451296b5d4Sbouyer edata->state = ENVSYS_SINVALID;
4461296b5d4Sbouyer } else {
4471296b5d4Sbouyer edata->value_cur = ((buf[0] << 4) | (buf[1] & 0xf)) *
4481296b5d4Sbouyer axp20x_sensors_lsb[edata->sensor];
4491296b5d4Sbouyer edata->state = ENVSYS_SVALID;
4501296b5d4Sbouyer }
451e5f864cfSjmcneill }
452e5f864cfSjmcneill
453e5f864cfSjmcneill static void
axp20x_sensors_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)454e5f864cfSjmcneill axp20x_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
455e5f864cfSjmcneill {
456e5f864cfSjmcneill struct axp20x_softc *sc = sme->sme_cookie;
457e5f864cfSjmcneill uint8_t buf[2];
458e5f864cfSjmcneill int error;
459e5f864cfSjmcneill
4601296b5d4Sbouyer switch(edata->sensor) {
4611296b5d4Sbouyer case AXP_SENSOR_ACOK:
4621296b5d4Sbouyer case AXP_SENSOR_VBUSOK:
4631296b5d4Sbouyer error = axp20x_read(sc, AXP_INPUT_STATUS,
4648acfda79Sthorpej &sc->sc_inputstatus, 1);
4651296b5d4Sbouyer if (error) {
4661296b5d4Sbouyer edata->state = ENVSYS_SINVALID;
4671296b5d4Sbouyer return;
4681296b5d4Sbouyer }
4691296b5d4Sbouyer if (edata->sensor == AXP_SENSOR_ACOK) {
4701296b5d4Sbouyer edata->value_cur =
4711296b5d4Sbouyer (sc->sc_inputstatus & AXP_INPUT_STATUS_AC_OK) ? 1 : 0;
4721296b5d4Sbouyer } else {
4731296b5d4Sbouyer edata->value_cur =
4741296b5d4Sbouyer (sc->sc_inputstatus & AXP_INPUT_STATUS_VBUS_OK) ? 1 : 0;
4751296b5d4Sbouyer }
4761296b5d4Sbouyer edata->state = ENVSYS_SVALID;
4771296b5d4Sbouyer return;
4781296b5d4Sbouyer case AXP_SENSOR_BATTOK:
4791296b5d4Sbouyer error = axp20x_read(sc, AXP_POWER_MODE,
4808acfda79Sthorpej &sc->sc_powermode, 1);
4811296b5d4Sbouyer if (error) {
4821296b5d4Sbouyer edata->state = ENVSYS_SINVALID;
4831296b5d4Sbouyer return;
4841296b5d4Sbouyer }
4851296b5d4Sbouyer edata->value_cur =
4861296b5d4Sbouyer (sc->sc_powermode & AXP_POWER_MODE_BATTOK) ? 1 : 0;
4871296b5d4Sbouyer return;
4881296b5d4Sbouyer case AXP_SENSOR_ACV:
4891296b5d4Sbouyer if (sc->sc_inputstatus & AXP_INPUT_STATUS_AC_OK)
4901296b5d4Sbouyer axp20x_sensors_refresh_volt(sc, AXP_ACV_MON_REG, edata);
4911296b5d4Sbouyer else
4921296b5d4Sbouyer edata->state = ENVSYS_SINVALID;
4931296b5d4Sbouyer return;
4941296b5d4Sbouyer case AXP_SENSOR_ACI:
4951296b5d4Sbouyer if (sc->sc_inputstatus & AXP_INPUT_STATUS_AC_OK)
4961296b5d4Sbouyer axp20x_sensors_refresh_amp(sc, AXP_ACI_MON_REG, edata);
4971296b5d4Sbouyer else
4981296b5d4Sbouyer edata->state = ENVSYS_SINVALID;
4991296b5d4Sbouyer return;
5001296b5d4Sbouyer case AXP_SENSOR_VBUSV:
5011296b5d4Sbouyer if (sc->sc_inputstatus & AXP_INPUT_STATUS_VBUS_OK)
5021296b5d4Sbouyer axp20x_sensors_refresh_volt(sc, AXP_VBUSV_MON_REG, edata);
5031296b5d4Sbouyer else
5041296b5d4Sbouyer edata->state = ENVSYS_SINVALID;
5051296b5d4Sbouyer return;
5061296b5d4Sbouyer case AXP_SENSOR_VBUSI:
5071296b5d4Sbouyer if (sc->sc_inputstatus & AXP_INPUT_STATUS_VBUS_OK)
5081296b5d4Sbouyer axp20x_sensors_refresh_amp(sc, AXP_VBUSI_MON_REG, edata);
5091296b5d4Sbouyer else
5101296b5d4Sbouyer edata->state = ENVSYS_SINVALID;
5111296b5d4Sbouyer return;
5121296b5d4Sbouyer case AXP_SENSOR_BATTV:
5131296b5d4Sbouyer if (sc->sc_powermode & AXP_POWER_MODE_BATTOK)
5141296b5d4Sbouyer axp20x_sensors_refresh_volt(sc, AXP_BATTV_MON_REG, edata);
5151296b5d4Sbouyer else
5161296b5d4Sbouyer edata->state = ENVSYS_SINVALID;
5171296b5d4Sbouyer return;
5181296b5d4Sbouyer case AXP_SENSOR_BATTI:
5191296b5d4Sbouyer if ((sc->sc_powermode & AXP_POWER_MODE_BATTOK) == 0) {
5201296b5d4Sbouyer edata->state = ENVSYS_SINVALID;
5211296b5d4Sbouyer return;
5221296b5d4Sbouyer }
5231296b5d4Sbouyer error = axp20x_read(sc, AXP_POWER_MODE,
5248acfda79Sthorpej &sc->sc_inputstatus, 1);
5251296b5d4Sbouyer if (error) {
5261296b5d4Sbouyer edata->state = ENVSYS_SINVALID;
5271296b5d4Sbouyer return;
5281296b5d4Sbouyer }
5291296b5d4Sbouyer if (sc->sc_inputstatus & AXP_POWER_MODE_CHARGING) {
5301296b5d4Sbouyer axp20x_sensors_refresh_amp(sc, AXP_BATTCI_MON_REG,
5311296b5d4Sbouyer edata);
5321296b5d4Sbouyer edata->value_cur = -edata->value_cur;
5331296b5d4Sbouyer } else {
5341296b5d4Sbouyer axp20x_sensors_refresh_amp(sc, AXP_BATTDI_MON_REG,
5351296b5d4Sbouyer edata);
5361296b5d4Sbouyer }
5371296b5d4Sbouyer return;
5381296b5d4Sbouyer case AXP_SENSOR_APSV:
5391296b5d4Sbouyer axp20x_sensors_refresh_volt(sc, AXP_APSV_MON_REG, edata);
5401296b5d4Sbouyer return;
5411296b5d4Sbouyer case AXP_SENSOR_TEMP:
5428acfda79Sthorpej error = axp20x_read(sc, AXP_TEMP_MON_REG, buf, sizeof(buf));
543e5f864cfSjmcneill if (error) {
544e5f864cfSjmcneill edata->state = ENVSYS_SINVALID;
545e5f864cfSjmcneill } else {
546e5f864cfSjmcneill /* between -144.7C and 264.8C, step +0.1C */
5471296b5d4Sbouyer edata->value_cur =
5481296b5d4Sbouyer (((buf[0] << 4) | (buf[1] & 0xf)) - 1447)
549e5f864cfSjmcneill * 100000 + 273150000;
550e5f864cfSjmcneill edata->state = ENVSYS_SVALID;
551e5f864cfSjmcneill }
5521296b5d4Sbouyer return;
5531296b5d4Sbouyer default:
5542c74340eSjmcneill aprint_error_dev(sc->sc_dev, "invalid sensor %d\n",
5551296b5d4Sbouyer edata->sensor);
5561296b5d4Sbouyer }
557e5f864cfSjmcneill }
558e5f864cfSjmcneill
559e5f864cfSjmcneill static int
axp20x_read(struct axp20x_softc * sc,uint8_t reg,uint8_t * val,size_t len)5608acfda79Sthorpej axp20x_read(struct axp20x_softc *sc, uint8_t reg, uint8_t *val, size_t len)
561e5f864cfSjmcneill {
5621296b5d4Sbouyer int ret;
5636eb08ce9Sthorpej
5648acfda79Sthorpej ret = iic_acquire_bus(sc->sc_i2c, 0);
5656eb08ce9Sthorpej if (ret == 0) {
5667a54b186Sjmcneill ret = iic_exec(sc->sc_i2c, I2C_OP_READ_WITH_STOP, sc->sc_addr,
5678acfda79Sthorpej ®, 1, val, len, 0);
5688acfda79Sthorpej iic_release_bus(sc->sc_i2c, 0);
5696eb08ce9Sthorpej }
5706eb08ce9Sthorpej
5711296b5d4Sbouyer return ret;
5721296b5d4Sbouyer
5731296b5d4Sbouyer }
5741296b5d4Sbouyer
5751296b5d4Sbouyer static int
axp20x_write(struct axp20x_softc * sc,uint8_t reg,uint8_t * val,size_t len)5768acfda79Sthorpej axp20x_write(struct axp20x_softc *sc, uint8_t reg, uint8_t *val, size_t len)
5771296b5d4Sbouyer {
5781296b5d4Sbouyer int ret;
5796eb08ce9Sthorpej
5808acfda79Sthorpej ret = iic_acquire_bus(sc->sc_i2c, 0);
5816eb08ce9Sthorpej if (ret == 0) {
5827a54b186Sjmcneill ret = iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
5838acfda79Sthorpej ®, 1, val, len, 0);
5848acfda79Sthorpej iic_release_bus(sc->sc_i2c, 0);
5856eb08ce9Sthorpej }
5866eb08ce9Sthorpej
5871296b5d4Sbouyer return ret;
5881296b5d4Sbouyer }
5891296b5d4Sbouyer
5908acfda79Sthorpej static int
axp20x_set_dcdc(device_t dev,int dcdc,int mvolt)5918acfda79Sthorpej axp20x_set_dcdc(device_t dev, int dcdc, int mvolt)
5921296b5d4Sbouyer {
5931296b5d4Sbouyer struct axp20x_softc *sc = device_private(dev);
5941296b5d4Sbouyer int ret;
5951296b5d4Sbouyer int value;
5961296b5d4Sbouyer uint8_t reg;
5971296b5d4Sbouyer
5981296b5d4Sbouyer KASSERT(sc != NULL);
5991296b5d4Sbouyer value = (mvolt - 700) / 25;
6001296b5d4Sbouyer switch (dcdc) {
6011296b5d4Sbouyer case AXP20X_DCDC2:
6021296b5d4Sbouyer value <<= AXP_DCDC2_VOLT_SHIFT;
6031296b5d4Sbouyer if (value > AXP_DCDC2_VOLT_MASK)
6041296b5d4Sbouyer return EINVAL;
6051296b5d4Sbouyer reg = value & AXP_DCDC2_VOLT_MASK;
6068acfda79Sthorpej ret = axp20x_write(sc, AXP_DCDC2, ®, 1);
6071296b5d4Sbouyer if (ret)
6081296b5d4Sbouyer return ret;
6098acfda79Sthorpej if (axp20x_read(sc, AXP_DCDC2, ®, 1) == 0) {
6102c74340eSjmcneill aprint_debug_dev(sc->sc_dev,
6112c74340eSjmcneill "DCDC2 changed to %dmV\n",
6121296b5d4Sbouyer (int)(700 + (reg & AXP_DCDC2_VOLT_MASK) * 25));
6131296b5d4Sbouyer }
6141296b5d4Sbouyer return 0;
6151296b5d4Sbouyer
6161296b5d4Sbouyer case AXP20X_DCDC3:
61774a232a6Sbouyer value <<= AXP_DCDC3_VOLT_SHIFT;
61874a232a6Sbouyer if (value > AXP_DCDC3_VOLT_MASK)
6191296b5d4Sbouyer return EINVAL;
62074a232a6Sbouyer reg = value & AXP_DCDC3_VOLT_MASK;
6218acfda79Sthorpej ret = axp20x_write(sc, AXP_DCDC3, ®, 1);
6221296b5d4Sbouyer if (ret)
6231296b5d4Sbouyer return ret;
6248acfda79Sthorpej if (axp20x_read(sc, AXP_DCDC3, ®, 1) == 0) {
6252c74340eSjmcneill aprint_debug_dev(sc->sc_dev,
6262c74340eSjmcneill "DCDC3 changed to %dmV\n",
62774a232a6Sbouyer (int)(700 + (reg & AXP_DCDC3_VOLT_MASK) * 25));
6281296b5d4Sbouyer }
6291296b5d4Sbouyer return 0;
6301296b5d4Sbouyer default:
6311296b5d4Sbouyer aprint_error_dev(dev, "wrong DCDC %d\n", dcdc);
6321296b5d4Sbouyer return EINVAL;
6331296b5d4Sbouyer }
634e5f864cfSjmcneill }
63515cd199cSjmcneill
6368acfda79Sthorpej static int
axp20x_get_dcdc(device_t dev,int dcdc,int * pmvolt)6378acfda79Sthorpej axp20x_get_dcdc(device_t dev, int dcdc, int *pmvolt)
638774ffdd6Sjmcneill {
639774ffdd6Sjmcneill struct axp20x_softc *sc = device_private(dev);
640774ffdd6Sjmcneill uint8_t reg;
641774ffdd6Sjmcneill int error;
642774ffdd6Sjmcneill
643774ffdd6Sjmcneill switch (dcdc) {
644774ffdd6Sjmcneill case AXP20X_DCDC2:
6458acfda79Sthorpej error = axp20x_read(sc, AXP_DCDC2, ®, 1);
646774ffdd6Sjmcneill if (error != 0)
647774ffdd6Sjmcneill return error;
648774ffdd6Sjmcneill *pmvolt = __SHIFTOUT(reg, AXP_DCDC2_VOLT_MASK) * 25 + 700;
649774ffdd6Sjmcneill return 0;
650774ffdd6Sjmcneill case AXP20X_DCDC3:
6518acfda79Sthorpej error = axp20x_read(sc, AXP_DCDC3, ®, 1);
652774ffdd6Sjmcneill if (error != 0)
653774ffdd6Sjmcneill return error;
654774ffdd6Sjmcneill *pmvolt = __SHIFTOUT(reg, AXP_DCDC3_VOLT_MASK) * 25 + 700;
655774ffdd6Sjmcneill return 0;
656774ffdd6Sjmcneill default:
657774ffdd6Sjmcneill return EINVAL;
658774ffdd6Sjmcneill }
659774ffdd6Sjmcneill }
660774ffdd6Sjmcneill
6618acfda79Sthorpej static void
axp20x_poweroff(device_t dev)66215cd199cSjmcneill axp20x_poweroff(device_t dev)
66315cd199cSjmcneill {
66415cd199cSjmcneill struct axp20x_softc * const sc = device_private(dev);
66515cd199cSjmcneill uint8_t reg = AXP_SHUTDOWN_CTRL;
6666eb08ce9Sthorpej int error;
66715cd199cSjmcneill
6688acfda79Sthorpej error = axp20x_write(sc, AXP_SHUTDOWN, ®, 1);
6696eb08ce9Sthorpej if (error) {
6706eb08ce9Sthorpej device_printf(dev, "WARNING: unable to power off, error %d\n",
6716eb08ce9Sthorpej error);
6726eb08ce9Sthorpej }
67315cd199cSjmcneill }
67415cd199cSjmcneill
675774ffdd6Sjmcneill static const struct axp20xregdef {
676774ffdd6Sjmcneill const char *name;
677774ffdd6Sjmcneill int dcdc;
678774ffdd6Sjmcneill } axp20x_regdefs[] = {
679774ffdd6Sjmcneill { "dcdc2", AXP20X_DCDC2 },
680774ffdd6Sjmcneill { "dcdc3", AXP20X_DCDC3 },
681774ffdd6Sjmcneill };
682774ffdd6Sjmcneill
683774ffdd6Sjmcneill struct axp20xreg_softc {
684774ffdd6Sjmcneill device_t sc_dev;
685774ffdd6Sjmcneill int sc_phandle;
686774ffdd6Sjmcneill const struct axp20xregdef *sc_regdef;
687774ffdd6Sjmcneill };
688774ffdd6Sjmcneill
689774ffdd6Sjmcneill struct axp20xreg_attach_args {
690774ffdd6Sjmcneill int reg_phandle;
691774ffdd6Sjmcneill };
692774ffdd6Sjmcneill
693774ffdd6Sjmcneill static int
axp20xreg_acquire(device_t dev)694774ffdd6Sjmcneill axp20xreg_acquire(device_t dev)
695774ffdd6Sjmcneill {
696774ffdd6Sjmcneill return 0;
697774ffdd6Sjmcneill }
698774ffdd6Sjmcneill
699774ffdd6Sjmcneill static void
axp20xreg_release(device_t dev)700774ffdd6Sjmcneill axp20xreg_release(device_t dev)
701774ffdd6Sjmcneill {
702774ffdd6Sjmcneill }
703774ffdd6Sjmcneill
704774ffdd6Sjmcneill static int
axp20xreg_enable(device_t dev,bool enable)705774ffdd6Sjmcneill axp20xreg_enable(device_t dev, bool enable)
706774ffdd6Sjmcneill {
707774ffdd6Sjmcneill /* TODO */
708774ffdd6Sjmcneill return enable ? 0 : EINVAL;
709774ffdd6Sjmcneill }
710774ffdd6Sjmcneill
711774ffdd6Sjmcneill static int
axp20xreg_set_voltage(device_t dev,u_int min_uvol,u_int max_uvol)712774ffdd6Sjmcneill axp20xreg_set_voltage(device_t dev, u_int min_uvol, u_int max_uvol)
713774ffdd6Sjmcneill {
714774ffdd6Sjmcneill struct axp20xreg_softc * const sc = device_private(dev);
715774ffdd6Sjmcneill
7168acfda79Sthorpej return axp20x_set_dcdc(device_parent(dev), sc->sc_regdef->dcdc, min_uvol / 1000);
717774ffdd6Sjmcneill }
718774ffdd6Sjmcneill
719774ffdd6Sjmcneill static int
axp20xreg_get_voltage(device_t dev,u_int * puvol)720774ffdd6Sjmcneill axp20xreg_get_voltage(device_t dev, u_int *puvol)
721774ffdd6Sjmcneill {
722774ffdd6Sjmcneill struct axp20xreg_softc * const sc = device_private(dev);
723774ffdd6Sjmcneill int mvol, error;
724774ffdd6Sjmcneill
7258acfda79Sthorpej error = axp20x_get_dcdc(device_parent(dev), sc->sc_regdef->dcdc, &mvol);
726774ffdd6Sjmcneill if (error != 0)
727774ffdd6Sjmcneill return error;
728774ffdd6Sjmcneill
729774ffdd6Sjmcneill *puvol = mvol * 1000;
730774ffdd6Sjmcneill return 0;
731774ffdd6Sjmcneill }
732774ffdd6Sjmcneill
733774ffdd6Sjmcneill static struct fdtbus_regulator_controller_func axp20xreg_funcs = {
734774ffdd6Sjmcneill .acquire = axp20xreg_acquire,
735774ffdd6Sjmcneill .release = axp20xreg_release,
736774ffdd6Sjmcneill .enable = axp20xreg_enable,
737774ffdd6Sjmcneill .set_voltage = axp20xreg_set_voltage,
738774ffdd6Sjmcneill .get_voltage = axp20xreg_get_voltage,
739774ffdd6Sjmcneill };
740774ffdd6Sjmcneill
741774ffdd6Sjmcneill static const struct axp20xregdef *
axp20xreg_lookup(int phandle)742774ffdd6Sjmcneill axp20xreg_lookup(int phandle)
743774ffdd6Sjmcneill {
744774ffdd6Sjmcneill const char *name;
745774ffdd6Sjmcneill int n;
746774ffdd6Sjmcneill
747774ffdd6Sjmcneill name = fdtbus_get_string(phandle, "name");
748774ffdd6Sjmcneill if (name == NULL)
749774ffdd6Sjmcneill return NULL;
750774ffdd6Sjmcneill
751774ffdd6Sjmcneill for (n = 0; n < __arraycount(axp20x_regdefs); n++)
752774ffdd6Sjmcneill if (strcmp(name, axp20x_regdefs[n].name) == 0)
753774ffdd6Sjmcneill return &axp20x_regdefs[n];
754774ffdd6Sjmcneill
755774ffdd6Sjmcneill return NULL;
756774ffdd6Sjmcneill }
757774ffdd6Sjmcneill
758774ffdd6Sjmcneill static int
axp20xreg_match(device_t parent,cfdata_t match,void * aux)759774ffdd6Sjmcneill axp20xreg_match(device_t parent, cfdata_t match, void *aux)
760774ffdd6Sjmcneill {
761774ffdd6Sjmcneill const struct axp20xreg_attach_args *reg = aux;
762774ffdd6Sjmcneill
763774ffdd6Sjmcneill return axp20xreg_lookup(reg->reg_phandle) != NULL;
764774ffdd6Sjmcneill }
765774ffdd6Sjmcneill
766774ffdd6Sjmcneill static void
axp20xreg_attach(device_t parent,device_t self,void * aux)767774ffdd6Sjmcneill axp20xreg_attach(device_t parent, device_t self, void *aux)
768774ffdd6Sjmcneill {
769774ffdd6Sjmcneill struct axp20xreg_softc * const sc = device_private(self);
770774ffdd6Sjmcneill const struct axp20xreg_attach_args *reg = aux;
771774ffdd6Sjmcneill const char *regulator_name;
772774ffdd6Sjmcneill
773774ffdd6Sjmcneill sc->sc_dev = self;
774774ffdd6Sjmcneill sc->sc_phandle = reg->reg_phandle;
775774ffdd6Sjmcneill sc->sc_regdef = axp20xreg_lookup(reg->reg_phandle);
776774ffdd6Sjmcneill
777774ffdd6Sjmcneill regulator_name = fdtbus_get_string(reg->reg_phandle, "regulator-name");
778774ffdd6Sjmcneill
779774ffdd6Sjmcneill aprint_naive("\n");
780774ffdd6Sjmcneill if (regulator_name)
781774ffdd6Sjmcneill aprint_normal(": %s (%s)\n", sc->sc_regdef->name, regulator_name);
782774ffdd6Sjmcneill else
783774ffdd6Sjmcneill aprint_normal(": %s\n", sc->sc_regdef->name);
784774ffdd6Sjmcneill
785774ffdd6Sjmcneill fdtbus_register_regulator_controller(self, sc->sc_phandle, &axp20xreg_funcs);
786774ffdd6Sjmcneill }
787774ffdd6Sjmcneill
788774ffdd6Sjmcneill CFATTACH_DECL_NEW(axp20xreg, sizeof(struct axp20xreg_softc),
789774ffdd6Sjmcneill axp20xreg_match, axp20xreg_attach, NULL, NULL);
790774ffdd6Sjmcneill
79115cd199cSjmcneill static void
axp20x_fdt_poweroff(device_t dev)79215cd199cSjmcneill axp20x_fdt_poweroff(device_t dev)
79315cd199cSjmcneill {
79415cd199cSjmcneill delay(1000000);
79515cd199cSjmcneill axp20x_poweroff(dev);
79615cd199cSjmcneill }
79715cd199cSjmcneill
79815cd199cSjmcneill static struct fdtbus_power_controller_func axp20x_fdt_power_funcs = {
79915cd199cSjmcneill .poweroff = axp20x_fdt_poweroff,
80015cd199cSjmcneill };
80115cd199cSjmcneill
80215cd199cSjmcneill static void
axp20x_fdt_attach(struct axp20x_softc * sc)80315cd199cSjmcneill axp20x_fdt_attach(struct axp20x_softc *sc)
80415cd199cSjmcneill {
805774ffdd6Sjmcneill int regulators_phandle, child;
806774ffdd6Sjmcneill
80715cd199cSjmcneill fdtbus_register_power_controller(sc->sc_dev, sc->sc_phandle,
80815cd199cSjmcneill &axp20x_fdt_power_funcs);
809774ffdd6Sjmcneill
810774ffdd6Sjmcneill regulators_phandle = of_find_firstchild_byname(sc->sc_phandle, "regulators");
811774ffdd6Sjmcneill if (regulators_phandle == -1)
812774ffdd6Sjmcneill return;
813774ffdd6Sjmcneill
814774ffdd6Sjmcneill for (child = OF_child(regulators_phandle); child; child = OF_peer(child)) {
815774ffdd6Sjmcneill struct axp20xreg_attach_args reg = { .reg_phandle = child };
816*c7fb772bSthorpej config_found(sc->sc_dev, ®, NULL, CFARGS_NONE);
817774ffdd6Sjmcneill }
81815cd199cSjmcneill }
819