blob: 79f9089344049a118ca307aff991dd8cbc85652c [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>
Andre Przywaraafb89cb2023-02-03 11:00:26 +000012#include <common/fdt_wrappers.h>
Samuel Holland56147892019-10-20 20:50:57 -050013#include <drivers/allwinner/axp.h>
14
15int 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
32int 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
46void axp_power_off(void)
47{
48 /* Set "power disable control" bit */
49 axp_setbits(0x32, BIT(7));
50}
51
Andre Przywara71b5a1d2021-11-01 00:17:37 +000052#if SUNXI_SETUP_REGULATORS == 1
Samuel Holland56147892019-10-20 20:50:57 -050053/*
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 */
58static 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
78static 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
101static bool should_enable_regulator(const void *fdt, int node)
102{
Andre Przywaraafb89cb2023-02-03 11:00:26 +0000103 if (!fdt_node_is_enabled(fdt, node)) {
Roman Beranek5864c9d2021-03-15 08:52:17 +0100104 return false;
105 }
106 if (fdt_getprop(fdt, node, "phandle", NULL) != NULL) {
Samuel Holland56147892019-10-20 20:50:57 -0500107 return true;
Roman Beranek5864c9d2021-03-15 08:52:17 +0100108 }
109 if (fdt_getprop(fdt, node, "regulator-always-on", NULL) != NULL) {
Samuel Holland56147892019-10-20 20:50:57 -0500110 return true;
Roman Beranek5864c9d2021-03-15 08:52:17 +0100111 }
Samuel Holland56147892019-10-20 20:50:57 -0500112 return false;
113}
114
Andre Przywara939f8bb2020-08-03 00:25:21 +0100115static 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
Samuel Holland56147892019-10-20 20:50:57 -0500134void axp_setup_regulators(const void *fdt)
135{
136 int node;
Samuel Hollande62c5dd2019-10-20 22:23:33 -0500137 bool sw = false;
Samuel Holland56147892019-10-20 20:50:57 -0500138
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
Samuel Hollande62c5dd2019-10-20 22:23:33 -0500149 /* This applies to AXP803 only. */
Andre Przywara939f8bb2020-08-03 00:25:21 +0100150 if (fdt_getprop(fdt, node, "x-powers,drive-vbus-en", NULL) &&
151 board_uses_usb0_host_mode(fdt)) {
Samuel Holland56147892019-10-20 20:50:57 -0500152 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);
Samuel Hollande62c5dd2019-10-20 22:23:33 -0500175
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
Samuel Holland56147892019-10-20 20:50:57 -0500183 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 }
Samuel Holland56147892019-10-20 20:50:57 -0500189 }
190
191 /*
Samuel Hollande62c5dd2019-10-20 22:23:33 -0500192 * 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.
Samuel Holland56147892019-10-20 20:50:57 -0500194 */
Samuel Hollande62c5dd2019-10-20 22:23:33 -0500195 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));
Samuel Holland56147892019-10-20 20:50:57 -0500201 }
202}
Andre Przywara71b5a1d2021-11-01 00:17:37 +0000203#endif /* SUNXI_SETUP_REGULATORS */