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