xref: /rk3399_ARM-atf/drivers/allwinner/axp/common.c (revision 0bc752c9adedbda4434fddf68d3bc18c5274360b)
1*0bc752c9SSamuel Holland /*
2*0bc752c9SSamuel Holland  * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
3*0bc752c9SSamuel Holland  *
4*0bc752c9SSamuel Holland  * SPDX-License-Identifier: BSD-3-Clause
5*0bc752c9SSamuel Holland  */
6*0bc752c9SSamuel Holland 
7*0bc752c9SSamuel Holland #include <errno.h>
8*0bc752c9SSamuel Holland 
9*0bc752c9SSamuel Holland #include <libfdt.h>
10*0bc752c9SSamuel Holland 
11*0bc752c9SSamuel Holland #include <common/debug.h>
12*0bc752c9SSamuel Holland #include <drivers/allwinner/axp.h>
13*0bc752c9SSamuel Holland 
14*0bc752c9SSamuel Holland int axp_check_id(void)
15*0bc752c9SSamuel Holland {
16*0bc752c9SSamuel Holland 	int ret;
17*0bc752c9SSamuel Holland 
18*0bc752c9SSamuel Holland 	ret = axp_read(0x03);
19*0bc752c9SSamuel Holland 	if (ret < 0)
20*0bc752c9SSamuel Holland 		return ret;
21*0bc752c9SSamuel Holland 
22*0bc752c9SSamuel Holland 	ret &= 0xcf;
23*0bc752c9SSamuel Holland 	if (ret != axp_chip_id) {
24*0bc752c9SSamuel Holland 		ERROR("PMIC: Found unknown PMIC %02x\n", ret);
25*0bc752c9SSamuel Holland 		return ret;
26*0bc752c9SSamuel Holland 	}
27*0bc752c9SSamuel Holland 
28*0bc752c9SSamuel Holland 	return 0;
29*0bc752c9SSamuel Holland }
30*0bc752c9SSamuel Holland 
31*0bc752c9SSamuel Holland int axp_clrsetbits(uint8_t reg, uint8_t clr_mask, uint8_t set_mask)
32*0bc752c9SSamuel Holland {
33*0bc752c9SSamuel Holland 	uint8_t val;
34*0bc752c9SSamuel Holland 	int ret;
35*0bc752c9SSamuel Holland 
36*0bc752c9SSamuel Holland 	ret = axp_read(reg);
37*0bc752c9SSamuel Holland 	if (ret < 0)
38*0bc752c9SSamuel Holland 		return ret;
39*0bc752c9SSamuel Holland 
40*0bc752c9SSamuel Holland 	val = (ret & ~clr_mask) | set_mask;
41*0bc752c9SSamuel Holland 
42*0bc752c9SSamuel Holland 	return axp_write(reg, val);
43*0bc752c9SSamuel Holland }
44*0bc752c9SSamuel Holland 
45*0bc752c9SSamuel Holland void axp_power_off(void)
46*0bc752c9SSamuel Holland {
47*0bc752c9SSamuel Holland 	/* Set "power disable control" bit */
48*0bc752c9SSamuel Holland 	axp_setbits(0x32, BIT(7));
49*0bc752c9SSamuel Holland }
50*0bc752c9SSamuel Holland 
51*0bc752c9SSamuel Holland /*
52*0bc752c9SSamuel Holland  * Retrieve the voltage from a given regulator DTB node.
53*0bc752c9SSamuel Holland  * Both the regulator-{min,max}-microvolt properties must be present and
54*0bc752c9SSamuel Holland  * have the same value. Return that value in millivolts.
55*0bc752c9SSamuel Holland  */
56*0bc752c9SSamuel Holland static int fdt_get_regulator_millivolt(const void *fdt, int node)
57*0bc752c9SSamuel Holland {
58*0bc752c9SSamuel Holland 	const fdt32_t *prop;
59*0bc752c9SSamuel Holland 	uint32_t min_volt;
60*0bc752c9SSamuel Holland 
61*0bc752c9SSamuel Holland 	prop = fdt_getprop(fdt, node, "regulator-min-microvolt", NULL);
62*0bc752c9SSamuel Holland 	if (prop == NULL)
63*0bc752c9SSamuel Holland 		return -EINVAL;
64*0bc752c9SSamuel Holland 	min_volt = fdt32_to_cpu(*prop);
65*0bc752c9SSamuel Holland 
66*0bc752c9SSamuel Holland 	prop = fdt_getprop(fdt, node, "regulator-max-microvolt", NULL);
67*0bc752c9SSamuel Holland 	if (prop == NULL)
68*0bc752c9SSamuel Holland 		return -EINVAL;
69*0bc752c9SSamuel Holland 
70*0bc752c9SSamuel Holland 	if (fdt32_to_cpu(*prop) != min_volt)
71*0bc752c9SSamuel Holland 		return -EINVAL;
72*0bc752c9SSamuel Holland 
73*0bc752c9SSamuel Holland 	return min_volt / 1000;
74*0bc752c9SSamuel Holland }
75*0bc752c9SSamuel Holland 
76*0bc752c9SSamuel Holland static int setup_regulator(const void *fdt, int node,
77*0bc752c9SSamuel Holland 			   const struct axp_regulator *reg)
78*0bc752c9SSamuel Holland {
79*0bc752c9SSamuel Holland 	uint8_t val;
80*0bc752c9SSamuel Holland 	int mvolt;
81*0bc752c9SSamuel Holland 
82*0bc752c9SSamuel Holland 	mvolt = fdt_get_regulator_millivolt(fdt, node);
83*0bc752c9SSamuel Holland 	if (mvolt < reg->min_volt || mvolt > reg->max_volt)
84*0bc752c9SSamuel Holland 		return -EINVAL;
85*0bc752c9SSamuel Holland 
86*0bc752c9SSamuel Holland 	val = (mvolt / reg->step) - (reg->min_volt / reg->step);
87*0bc752c9SSamuel Holland 	if (val > reg->split)
88*0bc752c9SSamuel Holland 		val = ((val - reg->split) / 2) + reg->split;
89*0bc752c9SSamuel Holland 
90*0bc752c9SSamuel Holland 	axp_write(reg->volt_reg, val);
91*0bc752c9SSamuel Holland 	axp_setbits(reg->switch_reg, BIT(reg->switch_bit));
92*0bc752c9SSamuel Holland 
93*0bc752c9SSamuel Holland 	INFO("PMIC: %s voltage: %d.%03dV\n", reg->dt_name,
94*0bc752c9SSamuel Holland 	     mvolt / 1000, mvolt % 1000);
95*0bc752c9SSamuel Holland 
96*0bc752c9SSamuel Holland 	return 0;
97*0bc752c9SSamuel Holland }
98*0bc752c9SSamuel Holland 
99*0bc752c9SSamuel Holland static bool should_enable_regulator(const void *fdt, int node)
100*0bc752c9SSamuel Holland {
101*0bc752c9SSamuel Holland 	if (fdt_getprop(fdt, node, "phandle", NULL) != NULL)
102*0bc752c9SSamuel Holland 		return true;
103*0bc752c9SSamuel Holland 	if (fdt_getprop(fdt, node, "regulator-always-on", NULL) != NULL)
104*0bc752c9SSamuel Holland 		return true;
105*0bc752c9SSamuel Holland 	return false;
106*0bc752c9SSamuel Holland }
107*0bc752c9SSamuel Holland 
108*0bc752c9SSamuel Holland void axp_setup_regulators(const void *fdt)
109*0bc752c9SSamuel Holland {
110*0bc752c9SSamuel Holland 	int node;
111*0bc752c9SSamuel Holland 	bool dc1sw = false;
112*0bc752c9SSamuel Holland 
113*0bc752c9SSamuel Holland 	if (fdt == NULL)
114*0bc752c9SSamuel Holland 		return;
115*0bc752c9SSamuel Holland 
116*0bc752c9SSamuel Holland 	/* locate the PMIC DT node, bail out if not found */
117*0bc752c9SSamuel Holland 	node = fdt_node_offset_by_compatible(fdt, -1, axp_compatible);
118*0bc752c9SSamuel Holland 	if (node < 0) {
119*0bc752c9SSamuel Holland 		WARN("PMIC: No PMIC DT node, skipping setup\n");
120*0bc752c9SSamuel Holland 		return;
121*0bc752c9SSamuel Holland 	}
122*0bc752c9SSamuel Holland 
123*0bc752c9SSamuel Holland 	if (fdt_getprop(fdt, node, "x-powers,drive-vbus-en", NULL)) {
124*0bc752c9SSamuel Holland 		axp_clrbits(0x8f, BIT(4));
125*0bc752c9SSamuel Holland 		axp_setbits(0x30, BIT(2));
126*0bc752c9SSamuel Holland 		INFO("PMIC: Enabling DRIVEVBUS\n");
127*0bc752c9SSamuel Holland 	}
128*0bc752c9SSamuel Holland 
129*0bc752c9SSamuel Holland 	/* descend into the "regulators" subnode */
130*0bc752c9SSamuel Holland 	node = fdt_subnode_offset(fdt, node, "regulators");
131*0bc752c9SSamuel Holland 	if (node < 0) {
132*0bc752c9SSamuel Holland 		WARN("PMIC: No regulators DT node, skipping setup\n");
133*0bc752c9SSamuel Holland 		return;
134*0bc752c9SSamuel Holland 	}
135*0bc752c9SSamuel Holland 
136*0bc752c9SSamuel Holland 	/* iterate over all regulators to find used ones */
137*0bc752c9SSamuel Holland 	fdt_for_each_subnode(node, fdt, node) {
138*0bc752c9SSamuel Holland 		const struct axp_regulator *reg;
139*0bc752c9SSamuel Holland 		const char *name;
140*0bc752c9SSamuel Holland 		int length;
141*0bc752c9SSamuel Holland 
142*0bc752c9SSamuel Holland 		/* We only care if it's always on or referenced. */
143*0bc752c9SSamuel Holland 		if (!should_enable_regulator(fdt, node))
144*0bc752c9SSamuel Holland 			continue;
145*0bc752c9SSamuel Holland 
146*0bc752c9SSamuel Holland 		name = fdt_get_name(fdt, node, &length);
147*0bc752c9SSamuel Holland 		for (reg = axp_regulators; reg->dt_name; reg++) {
148*0bc752c9SSamuel Holland 			if (!strncmp(name, reg->dt_name, length)) {
149*0bc752c9SSamuel Holland 				setup_regulator(fdt, node, reg);
150*0bc752c9SSamuel Holland 				break;
151*0bc752c9SSamuel Holland 			}
152*0bc752c9SSamuel Holland 		}
153*0bc752c9SSamuel Holland 
154*0bc752c9SSamuel Holland 		if (!strncmp(name, "dc1sw", length)) {
155*0bc752c9SSamuel Holland 			/* Delay DC1SW enablement to avoid overheating. */
156*0bc752c9SSamuel Holland 			dc1sw = true;
157*0bc752c9SSamuel Holland 			continue;
158*0bc752c9SSamuel Holland 		}
159*0bc752c9SSamuel Holland 	}
160*0bc752c9SSamuel Holland 
161*0bc752c9SSamuel Holland 	/*
162*0bc752c9SSamuel Holland 	 * If DLDO2 is enabled after DC1SW, the PMIC overheats and shuts
163*0bc752c9SSamuel Holland 	 * down. So always enable DC1SW as the very last regulator.
164*0bc752c9SSamuel Holland 	 */
165*0bc752c9SSamuel Holland 	if (dc1sw) {
166*0bc752c9SSamuel Holland 		INFO("PMIC: Enabling DC1SW\n");
167*0bc752c9SSamuel Holland 		axp_setbits(0x12, BIT(7));
168*0bc752c9SSamuel Holland 	}
169*0bc752c9SSamuel Holland }
170