blob: 9a130c43d0d9179113f1dc5fe7e75e8dedde8dd8 [file] [log] [blame]
Andre Przywara26123ca2020-11-28 01:39:17 +00001/*
2 * Copyright (c) 2017-2020, ARM Limited. All rights reserved.
3 * Copyright (c) 2018, Icenowy Zheng <icenowy@aosc.io>
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8#include <errno.h>
9#include <string.h>
10
11#include <arch_helpers.h>
12#include <common/debug.h>
Andre Przywaraa0597ba2024-03-21 14:54:36 +000013#include <common/fdt_wrappers.h>
Andre Przywara26123ca2020-11-28 01:39:17 +000014#include <drivers/allwinner/axp.h>
15#include <drivers/allwinner/sunxi_rsb.h>
Andre Przywara04445892024-03-21 14:56:07 +000016#include <drivers/mentor/mi2cv.h>
Andre Przywara26123ca2020-11-28 01:39:17 +000017#include <lib/mmio.h>
Andre Przywaraa0597ba2024-03-21 14:54:36 +000018#include <libfdt.h>
Andre Przywara26123ca2020-11-28 01:39:17 +000019
20#include <sunxi_cpucfg.h>
21#include <sunxi_def.h>
22#include <sunxi_mmap.h>
23#include <sunxi_private.h>
24
Andre Przywaraa0597ba2024-03-21 14:54:36 +000025static uint16_t pmic_bus_addr;
26static uint8_t rsb_rt_addr;
Andre Przywara26123ca2020-11-28 01:39:17 +000027
Andre Przywara04445892024-03-21 14:56:07 +000028static bool is_using_rsb(void)
29{
30 return rsb_rt_addr != 0;
31}
32
Andre Przywara26123ca2020-11-28 01:39:17 +000033static enum pmic_type {
34 UNKNOWN,
35 AXP305,
36} pmic;
37
Andre Przywaraa0597ba2024-03-21 14:54:36 +000038static uint8_t get_rsb_rt_address(uint16_t hw_addr)
Andre Przywara26123ca2020-11-28 01:39:17 +000039{
Andre Przywaraa0597ba2024-03-21 14:54:36 +000040 switch (hw_addr) {
41 case 0x745: return 0x3a;
Andre Przywara26123ca2020-11-28 01:39:17 +000042 }
43
Andre Przywara26123ca2020-11-28 01:39:17 +000044 return 0;
45}
46
Andre Przywaraa0597ba2024-03-21 14:54:36 +000047int axp_read(uint8_t reg)
48{
Andre Przywara04445892024-03-21 14:56:07 +000049 uint8_t val;
50 int ret;
51
52 if (is_using_rsb()) {
53 return rsb_read(rsb_rt_addr, reg);
54 }
55
56 ret = i2c_write(pmic_bus_addr, 0, 0, &reg, 1);
57 if (ret == 0) {
58 ret = i2c_read(pmic_bus_addr, 0, 0, &val, 1);
59 }
60 if (ret) {
61 ERROR("PMIC: Cannot read PMIC register %02x\n", reg);
62 return ret;
63 }
64
65 return val;
Andre Przywaraa0597ba2024-03-21 14:54:36 +000066}
67
68int axp_write(uint8_t reg, uint8_t val)
69{
Andre Przywara04445892024-03-21 14:56:07 +000070 int ret;
71
72 if (is_using_rsb()) {
73 return rsb_write(rsb_rt_addr, reg, val);
74 }
75
76 ret = i2c_write(pmic_bus_addr, reg, 1, &val, 1);
77 if (ret) {
78 ERROR("PMIC: Cannot write PMIC register %02x\n", reg);
79 }
80
81 return ret;
Andre Przywaraa0597ba2024-03-21 14:54:36 +000082}
83
84static int rsb_init(int rsb_hw_addr)
85{
86 int ret;
87
88 ret = rsb_init_controller();
89 if (ret) {
90 return ret;
91 }
92
93 /* Switch to the recommended 3 MHz bus clock. */
94 ret = rsb_set_bus_speed(SUNXI_OSC24M_CLK_IN_HZ, 3000000);
95 if (ret) {
96 return ret;
97 }
98
99 /* Initiate an I2C transaction to switch the PMIC to RSB mode. */
100 ret = rsb_set_device_mode(AXP20X_MODE_RSB << 16 | AXP20X_MODE_REG << 8);
101 if (ret) {
102 return ret;
103 }
104
105 /* Associate the 8-bit runtime address with the 12-bit bus address. */
106 ret = rsb_assign_runtime_address(rsb_hw_addr, rsb_rt_addr);
107 if (ret) {
108 return ret;
109 }
110
111 return 0;
112}
113
114static int pmic_bus_init(uint16_t socid, uint16_t rsb_hw_addr)
115{
116 int ret;
117
Andre Przywara04445892024-03-21 14:56:07 +0000118 ret = sunxi_init_platform_r_twi(socid, is_using_rsb());
Andre Przywaraa0597ba2024-03-21 14:54:36 +0000119 if (ret) {
120 INFO("Could not init platform bus: %d\n", ret);
121 pmic = UNKNOWN;
122 return ret;
123 }
124
Andre Przywara04445892024-03-21 14:56:07 +0000125 if (is_using_rsb()) {
126 ret = rsb_init(rsb_hw_addr);
127 if (ret) {
128 pmic = UNKNOWN;
129 return ret;
130 }
131 } else {
132 /* initialise mi2cv driver */
133 i2c_init((void *)SUNXI_R_I2C_BASE);
Andre Przywaraa0597ba2024-03-21 14:54:36 +0000134 }
135
136 return 0;
137}
138
139int sunxi_pmic_setup(uint16_t socid, const void *fdt)
140{
Andre Przywara04445892024-03-21 14:56:07 +0000141 int node, parent, ret;
Andre Przywaraa0597ba2024-03-21 14:54:36 +0000142 uint32_t reg;
143
144 node = fdt_node_offset_by_compatible(fdt, 0, "x-powers,axp806");
145 if (node >= 0) {
146 pmic = AXP305;
147 }
148
149 if (pmic == UNKNOWN) {
150 INFO("PMIC: No known PMIC in DT, skipping setup.\n");
151 return -ENODEV;
152 }
153
154 if (fdt_read_uint32(fdt, node, "reg", &reg)) {
155 ERROR("PMIC: PMIC DT node does not contain reg property.\n");
156 return -EINVAL;
157 }
158
159 pmic_bus_addr = reg;
Andre Przywara04445892024-03-21 14:56:07 +0000160 parent = fdt_parent_offset(fdt, node);
161 ret = fdt_node_check_compatible(fdt, parent, "allwinner,sun8i-a23-rsb");
162 if (ret == 0) {
163 rsb_rt_addr = get_rsb_rt_address(pmic_bus_addr);
164 if (rsb_rt_addr == 0) {
165 ERROR("PMIC: no mapping for RSB address 0x%x\n",
166 pmic_bus_addr);
167 return -EINVAL;
168 }
Andre Przywaraa0597ba2024-03-21 14:54:36 +0000169 }
170
Andre Przywara04445892024-03-21 14:56:07 +0000171 INFO("Probing for PMIC on %s:\n", is_using_rsb() ? "RSB" : "I2C");
Andre Przywaraa0597ba2024-03-21 14:54:36 +0000172
173 ret = pmic_bus_init(socid, pmic_bus_addr);
174 if (ret) {
175 return ret;
176 }
177
178 ret = axp_read(0x03);
179 switch (ret & 0xcf) {
180 case 0x40: /* AXP305 */
181 if (pmic == AXP305) {
182 INFO("PMIC: found AXP305, setting up regulators\n");
183 axp_setup_regulators(fdt);
184 } else {
185 pmic = UNKNOWN;
186 }
187 break;
188 }
189
Andre Przywara04445892024-03-21 14:56:07 +0000190 if (is_using_rsb()) {
191 /* Switch the PMIC back to I2C mode. */
192 return rsb_write(rsb_rt_addr, AXP20X_MODE_REG, AXP20X_MODE_I2C);
193 }
194
195 if (pmic == UNKNOWN) {
196 INFO("Incompatible or unknown PMIC found.\n");
197 return -ENODEV;
198 }
199
200 return 0;
Andre Przywaraa0597ba2024-03-21 14:54:36 +0000201}
202
Andre Przywara26123ca2020-11-28 01:39:17 +0000203void sunxi_power_down(void)
204{
Andre Przywaraa0597ba2024-03-21 14:54:36 +0000205 int ret;
206
207 if (pmic == UNKNOWN) {
208 return;
209 }
210
211 /* Re-initialise after rich OS might have used it. */
212 ret = pmic_bus_init(SUNXI_SOC_H616, pmic_bus_addr);
213 if (ret) {
214 return;
215 }
216
Andre Przywara26123ca2020-11-28 01:39:17 +0000217 switch (pmic) {
218 case AXP305:
Andre Przywaraa0597ba2024-03-21 14:54:36 +0000219 axp_setbits(0x32, BIT(7));
Andre Przywara26123ca2020-11-28 01:39:17 +0000220 break;
221 default:
222 break;
223 }
224}
225
226void sunxi_cpu_power_off_self(void)
227{
228 u_register_t mpidr = read_mpidr();
229 unsigned int core = MPIDR_AFFLVL0_VAL(mpidr);
230
231 /* Enable the CPUIDLE hardware (only really needs to be done once). */
232 mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0x16aa0000);
233 mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0xaa160001);
234
235 /* Trigger power off for this core. */
236 mmio_write_32(SUNXI_CORE_CLOSE_REG, BIT_32(core));
237}