blob: ac8a68ec5d64631211bf07643c6136aa9d30b5df [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,
Andre Przywara03851362024-03-21 14:18:47 +000036 AXP313,
Andre Przywara26123ca2020-11-28 01:39:17 +000037} pmic;
38
Andre Przywaraa0597ba2024-03-21 14:54:36 +000039static uint8_t get_rsb_rt_address(uint16_t hw_addr)
Andre Przywara26123ca2020-11-28 01:39:17 +000040{
Andre Przywaraa0597ba2024-03-21 14:54:36 +000041 switch (hw_addr) {
42 case 0x745: return 0x3a;
Andre Przywara26123ca2020-11-28 01:39:17 +000043 }
44
Andre Przywara26123ca2020-11-28 01:39:17 +000045 return 0;
46}
47
Andre Przywaraa0597ba2024-03-21 14:54:36 +000048int axp_read(uint8_t reg)
49{
Andre Przywara04445892024-03-21 14:56:07 +000050 uint8_t val;
51 int ret;
52
53 if (is_using_rsb()) {
54 return rsb_read(rsb_rt_addr, reg);
55 }
56
57 ret = i2c_write(pmic_bus_addr, 0, 0, &reg, 1);
58 if (ret == 0) {
59 ret = i2c_read(pmic_bus_addr, 0, 0, &val, 1);
60 }
61 if (ret) {
62 ERROR("PMIC: Cannot read PMIC register %02x\n", reg);
63 return ret;
64 }
65
66 return val;
Andre Przywaraa0597ba2024-03-21 14:54:36 +000067}
68
69int axp_write(uint8_t reg, uint8_t val)
70{
Andre Przywara04445892024-03-21 14:56:07 +000071 int ret;
72
73 if (is_using_rsb()) {
74 return rsb_write(rsb_rt_addr, reg, val);
75 }
76
77 ret = i2c_write(pmic_bus_addr, reg, 1, &val, 1);
78 if (ret) {
79 ERROR("PMIC: Cannot write PMIC register %02x\n", reg);
80 }
81
82 return ret;
Andre Przywaraa0597ba2024-03-21 14:54:36 +000083}
84
85static int rsb_init(int rsb_hw_addr)
86{
87 int ret;
88
89 ret = rsb_init_controller();
90 if (ret) {
91 return ret;
92 }
93
94 /* Switch to the recommended 3 MHz bus clock. */
95 ret = rsb_set_bus_speed(SUNXI_OSC24M_CLK_IN_HZ, 3000000);
96 if (ret) {
97 return ret;
98 }
99
100 /* Initiate an I2C transaction to switch the PMIC to RSB mode. */
101 ret = rsb_set_device_mode(AXP20X_MODE_RSB << 16 | AXP20X_MODE_REG << 8);
102 if (ret) {
103 return ret;
104 }
105
106 /* Associate the 8-bit runtime address with the 12-bit bus address. */
107 ret = rsb_assign_runtime_address(rsb_hw_addr, rsb_rt_addr);
108 if (ret) {
109 return ret;
110 }
111
112 return 0;
113}
114
115static int pmic_bus_init(uint16_t socid, uint16_t rsb_hw_addr)
116{
117 int ret;
118
Andre Przywara04445892024-03-21 14:56:07 +0000119 ret = sunxi_init_platform_r_twi(socid, is_using_rsb());
Andre Przywaraa0597ba2024-03-21 14:54:36 +0000120 if (ret) {
121 INFO("Could not init platform bus: %d\n", ret);
122 pmic = UNKNOWN;
123 return ret;
124 }
125
Andre Przywara04445892024-03-21 14:56:07 +0000126 if (is_using_rsb()) {
127 ret = rsb_init(rsb_hw_addr);
128 if (ret) {
129 pmic = UNKNOWN;
130 return ret;
131 }
132 } else {
133 /* initialise mi2cv driver */
134 i2c_init((void *)SUNXI_R_I2C_BASE);
Andre Przywaraa0597ba2024-03-21 14:54:36 +0000135 }
136
137 return 0;
138}
139
140int sunxi_pmic_setup(uint16_t socid, const void *fdt)
141{
Andre Przywara04445892024-03-21 14:56:07 +0000142 int node, parent, ret;
Andre Przywaraa0597ba2024-03-21 14:54:36 +0000143 uint32_t reg;
144
145 node = fdt_node_offset_by_compatible(fdt, 0, "x-powers,axp806");
146 if (node >= 0) {
147 pmic = AXP305;
148 }
149
150 if (pmic == UNKNOWN) {
Andre Przywara03851362024-03-21 14:18:47 +0000151 node = fdt_node_offset_by_compatible(fdt, 0, "x-powers,axp313a");
152 if (node >= 0) {
153 pmic = AXP313;
154 }
155 }
156
157 if (pmic == UNKNOWN) {
Andre Przywaraa0597ba2024-03-21 14:54:36 +0000158 INFO("PMIC: No known PMIC in DT, skipping setup.\n");
159 return -ENODEV;
160 }
161
162 if (fdt_read_uint32(fdt, node, "reg", &reg)) {
163 ERROR("PMIC: PMIC DT node does not contain reg property.\n");
164 return -EINVAL;
165 }
166
167 pmic_bus_addr = reg;
Andre Przywara04445892024-03-21 14:56:07 +0000168 parent = fdt_parent_offset(fdt, node);
169 ret = fdt_node_check_compatible(fdt, parent, "allwinner,sun8i-a23-rsb");
170 if (ret == 0) {
171 rsb_rt_addr = get_rsb_rt_address(pmic_bus_addr);
172 if (rsb_rt_addr == 0) {
173 ERROR("PMIC: no mapping for RSB address 0x%x\n",
174 pmic_bus_addr);
175 return -EINVAL;
176 }
Andre Przywaraa0597ba2024-03-21 14:54:36 +0000177 }
178
Andre Przywara04445892024-03-21 14:56:07 +0000179 INFO("Probing for PMIC on %s:\n", is_using_rsb() ? "RSB" : "I2C");
Andre Przywaraa0597ba2024-03-21 14:54:36 +0000180
181 ret = pmic_bus_init(socid, pmic_bus_addr);
182 if (ret) {
183 return ret;
184 }
185
186 ret = axp_read(0x03);
187 switch (ret & 0xcf) {
188 case 0x40: /* AXP305 */
189 if (pmic == AXP305) {
190 INFO("PMIC: found AXP305, setting up regulators\n");
191 axp_setup_regulators(fdt);
192 } else {
193 pmic = UNKNOWN;
194 }
195 break;
Andre Przywara03851362024-03-21 14:18:47 +0000196 case 0x48: /* AXP1530 */
197 case 0x4b: /* AXP313A */
198 case 0x4c: /* AXP313B */
199 if (pmic == AXP313) {
200 INFO("PMIC: found AXP313\n");
201 /* no regulators to set up */
202 } else {
203 pmic = UNKNOWN;
204 }
205 break;
Andre Przywaraa0597ba2024-03-21 14:54:36 +0000206 }
207
Andre Przywara04445892024-03-21 14:56:07 +0000208 if (is_using_rsb()) {
209 /* Switch the PMIC back to I2C mode. */
210 return rsb_write(rsb_rt_addr, AXP20X_MODE_REG, AXP20X_MODE_I2C);
211 }
212
213 if (pmic == UNKNOWN) {
214 INFO("Incompatible or unknown PMIC found.\n");
215 return -ENODEV;
216 }
217
218 return 0;
Andre Przywaraa0597ba2024-03-21 14:54:36 +0000219}
220
Andre Przywara26123ca2020-11-28 01:39:17 +0000221void sunxi_power_down(void)
222{
Andre Przywaraa0597ba2024-03-21 14:54:36 +0000223 int ret;
224
225 if (pmic == UNKNOWN) {
226 return;
227 }
228
229 /* Re-initialise after rich OS might have used it. */
230 ret = pmic_bus_init(SUNXI_SOC_H616, pmic_bus_addr);
231 if (ret) {
232 return;
233 }
234
Andre Przywara26123ca2020-11-28 01:39:17 +0000235 switch (pmic) {
236 case AXP305:
Andre Przywaraa0597ba2024-03-21 14:54:36 +0000237 axp_setbits(0x32, BIT(7));
Andre Przywara26123ca2020-11-28 01:39:17 +0000238 break;
Andre Przywara03851362024-03-21 14:18:47 +0000239 case AXP313:
240 axp_setbits(0x1a, BIT(7));
241 break;
Andre Przywara26123ca2020-11-28 01:39:17 +0000242 default:
243 break;
244 }
245}
246
247void sunxi_cpu_power_off_self(void)
248{
249 u_register_t mpidr = read_mpidr();
250 unsigned int core = MPIDR_AFFLVL0_VAL(mpidr);
251
252 /* Enable the CPUIDLE hardware (only really needs to be done once). */
253 mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0x16aa0000);
254 mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0xaa160001);
255
256 /* Trigger power off for this core. */
257 mmio_write_32(SUNXI_CORE_CLOSE_REG, BIT_32(core));
258}