xref: /rk3399_ARM-atf/drivers/allwinner/axp/common.c (revision 67412e4d7ae3defaac78ef5e351c63e06cfd907a)
10bc752c9SSamuel Holland /*
20bc752c9SSamuel Holland  * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
30bc752c9SSamuel Holland  *
40bc752c9SSamuel Holland  * SPDX-License-Identifier: BSD-3-Clause
50bc752c9SSamuel Holland  */
60bc752c9SSamuel Holland 
70bc752c9SSamuel Holland #include <errno.h>
80bc752c9SSamuel Holland 
90bc752c9SSamuel Holland #include <libfdt.h>
100bc752c9SSamuel Holland 
110bc752c9SSamuel Holland #include <common/debug.h>
120bc752c9SSamuel Holland #include <drivers/allwinner/axp.h>
130bc752c9SSamuel Holland 
140bc752c9SSamuel Holland int axp_check_id(void)
150bc752c9SSamuel Holland {
160bc752c9SSamuel Holland 	int ret;
170bc752c9SSamuel Holland 
180bc752c9SSamuel Holland 	ret = axp_read(0x03);
190bc752c9SSamuel Holland 	if (ret < 0)
200bc752c9SSamuel Holland 		return ret;
210bc752c9SSamuel Holland 
220bc752c9SSamuel Holland 	ret &= 0xcf;
230bc752c9SSamuel Holland 	if (ret != axp_chip_id) {
240bc752c9SSamuel Holland 		ERROR("PMIC: Found unknown PMIC %02x\n", ret);
250bc752c9SSamuel Holland 		return ret;
260bc752c9SSamuel Holland 	}
270bc752c9SSamuel Holland 
280bc752c9SSamuel Holland 	return 0;
290bc752c9SSamuel Holland }
300bc752c9SSamuel Holland 
310bc752c9SSamuel Holland int axp_clrsetbits(uint8_t reg, uint8_t clr_mask, uint8_t set_mask)
320bc752c9SSamuel Holland {
330bc752c9SSamuel Holland 	uint8_t val;
340bc752c9SSamuel Holland 	int ret;
350bc752c9SSamuel Holland 
360bc752c9SSamuel Holland 	ret = axp_read(reg);
370bc752c9SSamuel Holland 	if (ret < 0)
380bc752c9SSamuel Holland 		return ret;
390bc752c9SSamuel Holland 
400bc752c9SSamuel Holland 	val = (ret & ~clr_mask) | set_mask;
410bc752c9SSamuel Holland 
420bc752c9SSamuel Holland 	return axp_write(reg, val);
430bc752c9SSamuel Holland }
440bc752c9SSamuel Holland 
450bc752c9SSamuel Holland void axp_power_off(void)
460bc752c9SSamuel Holland {
470bc752c9SSamuel Holland 	/* Set "power disable control" bit */
480bc752c9SSamuel Holland 	axp_setbits(0x32, BIT(7));
490bc752c9SSamuel Holland }
500bc752c9SSamuel Holland 
51*67412e4dSAndre Przywara #if SUNXI_SETUP_REGULATORS == 1
520bc752c9SSamuel Holland /*
530bc752c9SSamuel Holland  * Retrieve the voltage from a given regulator DTB node.
540bc752c9SSamuel Holland  * Both the regulator-{min,max}-microvolt properties must be present and
550bc752c9SSamuel Holland  * have the same value. Return that value in millivolts.
560bc752c9SSamuel Holland  */
570bc752c9SSamuel Holland static int fdt_get_regulator_millivolt(const void *fdt, int node)
580bc752c9SSamuel Holland {
590bc752c9SSamuel Holland 	const fdt32_t *prop;
600bc752c9SSamuel Holland 	uint32_t min_volt;
610bc752c9SSamuel Holland 
620bc752c9SSamuel Holland 	prop = fdt_getprop(fdt, node, "regulator-min-microvolt", NULL);
630bc752c9SSamuel Holland 	if (prop == NULL)
640bc752c9SSamuel Holland 		return -EINVAL;
650bc752c9SSamuel Holland 	min_volt = fdt32_to_cpu(*prop);
660bc752c9SSamuel Holland 
670bc752c9SSamuel Holland 	prop = fdt_getprop(fdt, node, "regulator-max-microvolt", NULL);
680bc752c9SSamuel Holland 	if (prop == NULL)
690bc752c9SSamuel Holland 		return -EINVAL;
700bc752c9SSamuel Holland 
710bc752c9SSamuel Holland 	if (fdt32_to_cpu(*prop) != min_volt)
720bc752c9SSamuel Holland 		return -EINVAL;
730bc752c9SSamuel Holland 
740bc752c9SSamuel Holland 	return min_volt / 1000;
750bc752c9SSamuel Holland }
760bc752c9SSamuel Holland 
770bc752c9SSamuel Holland static int setup_regulator(const void *fdt, int node,
780bc752c9SSamuel Holland 			   const struct axp_regulator *reg)
790bc752c9SSamuel Holland {
800bc752c9SSamuel Holland 	uint8_t val;
810bc752c9SSamuel Holland 	int mvolt;
820bc752c9SSamuel Holland 
830bc752c9SSamuel Holland 	mvolt = fdt_get_regulator_millivolt(fdt, node);
840bc752c9SSamuel Holland 	if (mvolt < reg->min_volt || mvolt > reg->max_volt)
850bc752c9SSamuel Holland 		return -EINVAL;
860bc752c9SSamuel Holland 
870bc752c9SSamuel Holland 	val = (mvolt / reg->step) - (reg->min_volt / reg->step);
880bc752c9SSamuel Holland 	if (val > reg->split)
890bc752c9SSamuel Holland 		val = ((val - reg->split) / 2) + reg->split;
900bc752c9SSamuel Holland 
910bc752c9SSamuel Holland 	axp_write(reg->volt_reg, val);
920bc752c9SSamuel Holland 	axp_setbits(reg->switch_reg, BIT(reg->switch_bit));
930bc752c9SSamuel Holland 
940bc752c9SSamuel Holland 	INFO("PMIC: %s voltage: %d.%03dV\n", reg->dt_name,
950bc752c9SSamuel Holland 	     mvolt / 1000, mvolt % 1000);
960bc752c9SSamuel Holland 
970bc752c9SSamuel Holland 	return 0;
980bc752c9SSamuel Holland }
990bc752c9SSamuel Holland 
1009655a1f5SRoman Beranek static bool is_node_disabled(const void *fdt, int node)
1019655a1f5SRoman Beranek {
1029655a1f5SRoman Beranek 	const char *cell;
1039655a1f5SRoman Beranek 	cell = fdt_getprop(fdt, node, "status", NULL);
1049655a1f5SRoman Beranek 	if (cell == NULL) {
1059655a1f5SRoman Beranek 		return false;
1069655a1f5SRoman Beranek 	}
1079655a1f5SRoman Beranek 	return strcmp(cell, "okay") != 0;
1089655a1f5SRoman Beranek }
1099655a1f5SRoman Beranek 
1100bc752c9SSamuel Holland static bool should_enable_regulator(const void *fdt, int node)
1110bc752c9SSamuel Holland {
1129655a1f5SRoman Beranek 	if (is_node_disabled(fdt, node)) {
1139655a1f5SRoman Beranek 		return false;
1149655a1f5SRoman Beranek 	}
1159655a1f5SRoman Beranek 	if (fdt_getprop(fdt, node, "phandle", NULL) != NULL) {
1160bc752c9SSamuel Holland 		return true;
1179655a1f5SRoman Beranek 	}
1189655a1f5SRoman Beranek 	if (fdt_getprop(fdt, node, "regulator-always-on", NULL) != NULL) {
1190bc752c9SSamuel Holland 		return true;
1209655a1f5SRoman Beranek 	}
1210bc752c9SSamuel Holland 	return false;
1220bc752c9SSamuel Holland }
1230bc752c9SSamuel Holland 
12493fa305cSAndre Przywara static bool board_uses_usb0_host_mode(const void *fdt)
12593fa305cSAndre Przywara {
12693fa305cSAndre Przywara 	int node, length;
12793fa305cSAndre Przywara 	const char *prop;
12893fa305cSAndre Przywara 
12993fa305cSAndre Przywara 	node = fdt_node_offset_by_compatible(fdt, -1,
13093fa305cSAndre Przywara 					     "allwinner,sun8i-a33-musb");
13193fa305cSAndre Przywara 	if (node < 0) {
13293fa305cSAndre Przywara 		return false;
13393fa305cSAndre Przywara 	}
13493fa305cSAndre Przywara 
13593fa305cSAndre Przywara 	prop = fdt_getprop(fdt, node, "dr_mode", &length);
13693fa305cSAndre Przywara 	if (!prop) {
13793fa305cSAndre Przywara 		return false;
13893fa305cSAndre Przywara 	}
13993fa305cSAndre Przywara 
14093fa305cSAndre Przywara 	return !strncmp(prop, "host", length);
14193fa305cSAndre Przywara }
14293fa305cSAndre Przywara 
1430bc752c9SSamuel Holland void axp_setup_regulators(const void *fdt)
1440bc752c9SSamuel Holland {
1450bc752c9SSamuel Holland 	int node;
146f6d9c4caSSamuel Holland 	bool sw = false;
1470bc752c9SSamuel Holland 
1480bc752c9SSamuel Holland 	if (fdt == NULL)
1490bc752c9SSamuel Holland 		return;
1500bc752c9SSamuel Holland 
1510bc752c9SSamuel Holland 	/* locate the PMIC DT node, bail out if not found */
1520bc752c9SSamuel Holland 	node = fdt_node_offset_by_compatible(fdt, -1, axp_compatible);
1530bc752c9SSamuel Holland 	if (node < 0) {
1540bc752c9SSamuel Holland 		WARN("PMIC: No PMIC DT node, skipping setup\n");
1550bc752c9SSamuel Holland 		return;
1560bc752c9SSamuel Holland 	}
1570bc752c9SSamuel Holland 
158f6d9c4caSSamuel Holland 	/* This applies to AXP803 only. */
15993fa305cSAndre Przywara 	if (fdt_getprop(fdt, node, "x-powers,drive-vbus-en", NULL) &&
16093fa305cSAndre Przywara 	    board_uses_usb0_host_mode(fdt)) {
1610bc752c9SSamuel Holland 		axp_clrbits(0x8f, BIT(4));
1620bc752c9SSamuel Holland 		axp_setbits(0x30, BIT(2));
1630bc752c9SSamuel Holland 		INFO("PMIC: Enabling DRIVEVBUS\n");
1640bc752c9SSamuel Holland 	}
1650bc752c9SSamuel Holland 
1660bc752c9SSamuel Holland 	/* descend into the "regulators" subnode */
1670bc752c9SSamuel Holland 	node = fdt_subnode_offset(fdt, node, "regulators");
1680bc752c9SSamuel Holland 	if (node < 0) {
1690bc752c9SSamuel Holland 		WARN("PMIC: No regulators DT node, skipping setup\n");
1700bc752c9SSamuel Holland 		return;
1710bc752c9SSamuel Holland 	}
1720bc752c9SSamuel Holland 
1730bc752c9SSamuel Holland 	/* iterate over all regulators to find used ones */
1740bc752c9SSamuel Holland 	fdt_for_each_subnode(node, fdt, node) {
1750bc752c9SSamuel Holland 		const struct axp_regulator *reg;
1760bc752c9SSamuel Holland 		const char *name;
1770bc752c9SSamuel Holland 		int length;
1780bc752c9SSamuel Holland 
1790bc752c9SSamuel Holland 		/* We only care if it's always on or referenced. */
1800bc752c9SSamuel Holland 		if (!should_enable_regulator(fdt, node))
1810bc752c9SSamuel Holland 			continue;
1820bc752c9SSamuel Holland 
1830bc752c9SSamuel Holland 		name = fdt_get_name(fdt, node, &length);
184f6d9c4caSSamuel Holland 
185f6d9c4caSSamuel Holland 		/* Enable the switch last to avoid overheating. */
186f6d9c4caSSamuel Holland 		if (!strncmp(name, "dc1sw", length) ||
187f6d9c4caSSamuel Holland 		    !strncmp(name, "sw", length)) {
188f6d9c4caSSamuel Holland 			sw = true;
189f6d9c4caSSamuel Holland 			continue;
190f6d9c4caSSamuel Holland 		}
191f6d9c4caSSamuel Holland 
1920bc752c9SSamuel Holland 		for (reg = axp_regulators; reg->dt_name; reg++) {
1930bc752c9SSamuel Holland 			if (!strncmp(name, reg->dt_name, length)) {
1940bc752c9SSamuel Holland 				setup_regulator(fdt, node, reg);
1950bc752c9SSamuel Holland 				break;
1960bc752c9SSamuel Holland 			}
1970bc752c9SSamuel Holland 		}
1980bc752c9SSamuel Holland 	}
1990bc752c9SSamuel Holland 
2000bc752c9SSamuel Holland 	/*
201f6d9c4caSSamuel Holland 	 * On the AXP803, if DLDO2 is enabled after DC1SW, the PMIC overheats
202f6d9c4caSSamuel Holland 	 * and shuts down. So always enable DC1SW as the very last regulator.
2030bc752c9SSamuel Holland 	 */
204f6d9c4caSSamuel Holland 	if (sw) {
205f6d9c4caSSamuel Holland 		INFO("PMIC: Enabling DC SW\n");
206f6d9c4caSSamuel Holland 		if (axp_chip_id == AXP803_CHIP_ID)
2070bc752c9SSamuel Holland 			axp_setbits(0x12, BIT(7));
208f6d9c4caSSamuel Holland 		if (axp_chip_id == AXP805_CHIP_ID)
209f6d9c4caSSamuel Holland 			axp_setbits(0x11, BIT(7));
2100bc752c9SSamuel Holland 	}
2110bc752c9SSamuel Holland }
212*67412e4dSAndre Przywara #endif	/* SUNXI_SETUP_REGULATORS */
213