blob: 143fb0f2d2515c357d2a8e4b362351afad5baa28 [file] [log] [blame]
Samuel Holland56147892019-10-20 20:50:57 -05001/*
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
14int 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
31int 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
45void 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 */
56static 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
76static 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
Roman Beranek5864c9d2021-03-15 08:52:17 +010099static 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
Samuel Holland56147892019-10-20 20:50:57 -0500109static bool should_enable_regulator(const void *fdt, int node)
110{
Roman Beranek5864c9d2021-03-15 08:52:17 +0100111 if (is_node_disabled(fdt, node)) {
112 return false;
113 }
114 if (fdt_getprop(fdt, node, "phandle", NULL) != NULL) {
Samuel Holland56147892019-10-20 20:50:57 -0500115 return true;
Roman Beranek5864c9d2021-03-15 08:52:17 +0100116 }
117 if (fdt_getprop(fdt, node, "regulator-always-on", NULL) != NULL) {
Samuel Holland56147892019-10-20 20:50:57 -0500118 return true;
Roman Beranek5864c9d2021-03-15 08:52:17 +0100119 }
Samuel Holland56147892019-10-20 20:50:57 -0500120 return false;
121}
122
Andre Przywara939f8bb2020-08-03 00:25:21 +0100123static 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
Samuel Holland56147892019-10-20 20:50:57 -0500142void axp_setup_regulators(const void *fdt)
143{
144 int node;
Samuel Hollande62c5dd2019-10-20 22:23:33 -0500145 bool sw = false;
Samuel Holland56147892019-10-20 20:50:57 -0500146
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
Samuel Hollande62c5dd2019-10-20 22:23:33 -0500157 /* This applies to AXP803 only. */
Andre Przywara939f8bb2020-08-03 00:25:21 +0100158 if (fdt_getprop(fdt, node, "x-powers,drive-vbus-en", NULL) &&
159 board_uses_usb0_host_mode(fdt)) {
Samuel Holland56147892019-10-20 20:50:57 -0500160 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);
Samuel Hollande62c5dd2019-10-20 22:23:33 -0500183
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
Samuel Holland56147892019-10-20 20:50:57 -0500191 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 }
Samuel Holland56147892019-10-20 20:50:57 -0500197 }
198
199 /*
Samuel Hollande62c5dd2019-10-20 22:23:33 -0500200 * 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.
Samuel Holland56147892019-10-20 20:50:57 -0500202 */
Samuel Hollande62c5dd2019-10-20 22:23:33 -0500203 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));
Samuel Holland56147892019-10-20 20:50:57 -0500209 }
210}