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