xref: /netbsd-src/sys/dev/i2c/axp20x.c (revision c7fb772b85b2b5d4cfb282f868f454b4701534fd)
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 		    &reg, 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 		    &reg, 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, &reg, 1);
6071296b5d4Sbouyer 		if (ret)
6081296b5d4Sbouyer 			return ret;
6098acfda79Sthorpej 		if (axp20x_read(sc, AXP_DCDC2, &reg, 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, &reg, 1);
6221296b5d4Sbouyer 		if (ret)
6231296b5d4Sbouyer 			return ret;
6248acfda79Sthorpej 		if (axp20x_read(sc, AXP_DCDC3, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, NULL, CFARGS_NONE);
817774ffdd6Sjmcneill 	}
81815cd199cSjmcneill }
819