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