126123ca3SAndre Przywara /*
2ce750f16SBoyan Karatotev * Copyright (c) 2017-2025, Arm Limited. All rights reserved.
326123ca3SAndre Przywara * Copyright (c) 2018, Icenowy Zheng <icenowy@aosc.io>
426123ca3SAndre Przywara *
526123ca3SAndre Przywara * SPDX-License-Identifier: BSD-3-Clause
626123ca3SAndre Przywara */
726123ca3SAndre Przywara
826123ca3SAndre Przywara #include <errno.h>
926123ca3SAndre Przywara #include <string.h>
1026123ca3SAndre Przywara
1126123ca3SAndre Przywara #include <arch_helpers.h>
1226123ca3SAndre Przywara #include <common/debug.h>
13a0597ba2SAndre Przywara #include <common/fdt_wrappers.h>
1426123ca3SAndre Przywara #include <drivers/allwinner/axp.h>
1526123ca3SAndre Przywara #include <drivers/allwinner/sunxi_rsb.h>
1604445898SAndre Przywara #include <drivers/mentor/mi2cv.h>
1726123ca3SAndre Przywara #include <lib/mmio.h>
18a0597ba2SAndre Przywara #include <libfdt.h>
1926123ca3SAndre Przywara
2026123ca3SAndre Przywara #include <sunxi_cpucfg.h>
2126123ca3SAndre Przywara #include <sunxi_def.h>
2226123ca3SAndre Przywara #include <sunxi_mmap.h>
2326123ca3SAndre Przywara #include <sunxi_private.h>
2426123ca3SAndre Przywara
25a0597ba2SAndre Przywara static uint16_t pmic_bus_addr;
26a0597ba2SAndre Przywara static uint8_t rsb_rt_addr;
2726123ca3SAndre Przywara
is_using_rsb(void)2804445898SAndre Przywara static bool is_using_rsb(void)
2904445898SAndre Przywara {
3004445898SAndre Przywara return rsb_rt_addr != 0;
3104445898SAndre Przywara }
3204445898SAndre Przywara
3326123ca3SAndre Przywara static enum pmic_type {
3426123ca3SAndre Przywara UNKNOWN,
3526123ca3SAndre Przywara AXP305,
3603851367SAndre Przywara AXP313,
37646d06b2SAndre Przywara AXP717,
3826123ca3SAndre Przywara } pmic;
3926123ca3SAndre Przywara
get_rsb_rt_address(uint16_t hw_addr)40a0597ba2SAndre Przywara static uint8_t get_rsb_rt_address(uint16_t hw_addr)
4126123ca3SAndre Przywara {
42a0597ba2SAndre Przywara switch (hw_addr) {
43646d06b2SAndre Przywara case 0x3a3: return 0x2d;
44a0597ba2SAndre Przywara case 0x745: return 0x3a;
4526123ca3SAndre Przywara }
4626123ca3SAndre Przywara
4726123ca3SAndre Przywara return 0;
4826123ca3SAndre Przywara }
4926123ca3SAndre Przywara
axp_read(uint8_t reg)50a0597ba2SAndre Przywara int axp_read(uint8_t reg)
51a0597ba2SAndre Przywara {
52ce750f16SBoyan Karatotev uint8_t val = 0;
5304445898SAndre Przywara int ret;
5404445898SAndre Przywara
5504445898SAndre Przywara if (is_using_rsb()) {
56a0597ba2SAndre Przywara return rsb_read(rsb_rt_addr, reg);
57a0597ba2SAndre Przywara }
58a0597ba2SAndre Przywara
5904445898SAndre Przywara ret = i2c_write(pmic_bus_addr, 0, 0, ®, 1);
6004445898SAndre Przywara if (ret == 0) {
6104445898SAndre Przywara ret = i2c_read(pmic_bus_addr, 0, 0, &val, 1);
6204445898SAndre Przywara }
6304445898SAndre Przywara if (ret) {
6404445898SAndre Przywara ERROR("PMIC: Cannot read PMIC register %02x\n", reg);
6504445898SAndre Przywara return ret;
6604445898SAndre Przywara }
6704445898SAndre Przywara
6804445898SAndre Przywara return val;
6904445898SAndre Przywara }
7004445898SAndre Przywara
axp_write(uint8_t reg,uint8_t val)71a0597ba2SAndre Przywara int axp_write(uint8_t reg, uint8_t val)
72a0597ba2SAndre Przywara {
7304445898SAndre Przywara int ret;
7404445898SAndre Przywara
7504445898SAndre Przywara if (is_using_rsb()) {
76a0597ba2SAndre Przywara return rsb_write(rsb_rt_addr, reg, val);
77a0597ba2SAndre Przywara }
78a0597ba2SAndre Przywara
7904445898SAndre Przywara ret = i2c_write(pmic_bus_addr, reg, 1, &val, 1);
8004445898SAndre Przywara if (ret) {
8104445898SAndre Przywara ERROR("PMIC: Cannot write PMIC register %02x\n", reg);
8204445898SAndre Przywara }
8304445898SAndre Przywara
8404445898SAndre Przywara return ret;
8504445898SAndre Przywara }
8604445898SAndre Przywara
rsb_init(int rsb_hw_addr)87a0597ba2SAndre Przywara static int rsb_init(int rsb_hw_addr)
88a0597ba2SAndre Przywara {
89a0597ba2SAndre Przywara int ret;
90a0597ba2SAndre Przywara
91a0597ba2SAndre Przywara ret = rsb_init_controller();
92a0597ba2SAndre Przywara if (ret) {
93a0597ba2SAndre Przywara return ret;
94a0597ba2SAndre Przywara }
95a0597ba2SAndre Przywara
96a0597ba2SAndre Przywara /* Switch to the recommended 3 MHz bus clock. */
97a0597ba2SAndre Przywara ret = rsb_set_bus_speed(SUNXI_OSC24M_CLK_IN_HZ, 3000000);
98a0597ba2SAndre Przywara if (ret) {
99a0597ba2SAndre Przywara return ret;
100a0597ba2SAndre Przywara }
101a0597ba2SAndre Przywara
102a0597ba2SAndre Przywara /* Initiate an I2C transaction to switch the PMIC to RSB mode. */
103a0597ba2SAndre Przywara ret = rsb_set_device_mode(AXP20X_MODE_RSB << 16 | AXP20X_MODE_REG << 8);
104a0597ba2SAndre Przywara if (ret) {
105a0597ba2SAndre Przywara return ret;
106a0597ba2SAndre Przywara }
107a0597ba2SAndre Przywara
108a0597ba2SAndre Przywara /* Associate the 8-bit runtime address with the 12-bit bus address. */
109a0597ba2SAndre Przywara ret = rsb_assign_runtime_address(rsb_hw_addr, rsb_rt_addr);
110a0597ba2SAndre Przywara if (ret) {
111a0597ba2SAndre Przywara return ret;
112a0597ba2SAndre Przywara }
113a0597ba2SAndre Przywara
114a0597ba2SAndre Przywara return 0;
115a0597ba2SAndre Przywara }
116a0597ba2SAndre Przywara
pmic_bus_init(uint16_t socid,uint16_t rsb_hw_addr)117a0597ba2SAndre Przywara static int pmic_bus_init(uint16_t socid, uint16_t rsb_hw_addr)
118a0597ba2SAndre Przywara {
119a0597ba2SAndre Przywara int ret;
120a0597ba2SAndre Przywara
12104445898SAndre Przywara ret = sunxi_init_platform_r_twi(socid, is_using_rsb());
122a0597ba2SAndre Przywara if (ret) {
123a0597ba2SAndre Przywara INFO("Could not init platform bus: %d\n", ret);
124a0597ba2SAndre Przywara pmic = UNKNOWN;
125a0597ba2SAndre Przywara return ret;
126a0597ba2SAndre Przywara }
127a0597ba2SAndre Przywara
12804445898SAndre Przywara if (is_using_rsb()) {
129a0597ba2SAndre Przywara ret = rsb_init(rsb_hw_addr);
130a0597ba2SAndre Przywara if (ret) {
131a0597ba2SAndre Przywara pmic = UNKNOWN;
132a0597ba2SAndre Przywara return ret;
133a0597ba2SAndre Przywara }
13404445898SAndre Przywara } else {
13504445898SAndre Przywara /* initialise mi2cv driver */
13604445898SAndre Przywara i2c_init((void *)SUNXI_R_I2C_BASE);
13704445898SAndre Przywara }
138a0597ba2SAndre Przywara
139a0597ba2SAndre Przywara return 0;
140a0597ba2SAndre Przywara }
141a0597ba2SAndre Przywara
sunxi_pmic_setup(uint16_t socid,const void * fdt)142a0597ba2SAndre Przywara int sunxi_pmic_setup(uint16_t socid, const void *fdt)
143a0597ba2SAndre Przywara {
14404445898SAndre Przywara int node, parent, ret;
145a0597ba2SAndre Przywara uint32_t reg;
146a0597ba2SAndre Przywara
147*69b4a591SAndre Przywara if (fdt == NULL) {
148*69b4a591SAndre Przywara INFO("No DTB, skipping PMIC detection and setup\n");
149*69b4a591SAndre Przywara return -ENOENT;
150*69b4a591SAndre Przywara }
151*69b4a591SAndre Przywara
152a0597ba2SAndre Przywara node = fdt_node_offset_by_compatible(fdt, 0, "x-powers,axp806");
153a0597ba2SAndre Przywara if (node >= 0) {
154a0597ba2SAndre Przywara pmic = AXP305;
155a0597ba2SAndre Przywara }
156a0597ba2SAndre Przywara
157a0597ba2SAndre Przywara if (pmic == UNKNOWN) {
15803851367SAndre Przywara node = fdt_node_offset_by_compatible(fdt, 0, "x-powers,axp313a");
15903851367SAndre Przywara if (node >= 0) {
16003851367SAndre Przywara pmic = AXP313;
16103851367SAndre Przywara }
16203851367SAndre Przywara }
16303851367SAndre Przywara
16403851367SAndre Przywara if (pmic == UNKNOWN) {
165646d06b2SAndre Przywara node = fdt_node_offset_by_compatible(fdt, 0, "x-powers,axp717");
166646d06b2SAndre Przywara if (node >= 0) {
167646d06b2SAndre Przywara pmic = AXP717;
168646d06b2SAndre Przywara }
169646d06b2SAndre Przywara }
170646d06b2SAndre Przywara
171646d06b2SAndre Przywara if (pmic == UNKNOWN) {
172a0597ba2SAndre Przywara INFO("PMIC: No known PMIC in DT, skipping setup.\n");
173a0597ba2SAndre Przywara return -ENODEV;
174a0597ba2SAndre Przywara }
175a0597ba2SAndre Przywara
176a0597ba2SAndre Przywara if (fdt_read_uint32(fdt, node, "reg", ®)) {
177a0597ba2SAndre Przywara ERROR("PMIC: PMIC DT node does not contain reg property.\n");
178a0597ba2SAndre Przywara return -EINVAL;
179a0597ba2SAndre Przywara }
180a0597ba2SAndre Przywara
181a0597ba2SAndre Przywara pmic_bus_addr = reg;
18204445898SAndre Przywara parent = fdt_parent_offset(fdt, node);
18304445898SAndre Przywara ret = fdt_node_check_compatible(fdt, parent, "allwinner,sun8i-a23-rsb");
18404445898SAndre Przywara if (ret == 0) {
185a0597ba2SAndre Przywara rsb_rt_addr = get_rsb_rt_address(pmic_bus_addr);
186a0597ba2SAndre Przywara if (rsb_rt_addr == 0) {
18704445898SAndre Przywara ERROR("PMIC: no mapping for RSB address 0x%x\n",
18804445898SAndre Przywara pmic_bus_addr);
189a0597ba2SAndre Przywara return -EINVAL;
190a0597ba2SAndre Przywara }
19104445898SAndre Przywara }
192a0597ba2SAndre Przywara
19304445898SAndre Przywara INFO("Probing for PMIC on %s:\n", is_using_rsb() ? "RSB" : "I2C");
194a0597ba2SAndre Przywara
195a0597ba2SAndre Przywara ret = pmic_bus_init(socid, pmic_bus_addr);
196a0597ba2SAndre Przywara if (ret) {
197a0597ba2SAndre Przywara return ret;
198a0597ba2SAndre Przywara }
199a0597ba2SAndre Przywara
200a0597ba2SAndre Przywara ret = axp_read(0x03);
201a0597ba2SAndre Przywara switch (ret & 0xcf) {
202a0597ba2SAndre Przywara case 0x40: /* AXP305 */
203a0597ba2SAndre Przywara if (pmic == AXP305) {
204a0597ba2SAndre Przywara INFO("PMIC: found AXP305, setting up regulators\n");
205a0597ba2SAndre Przywara axp_setup_regulators(fdt);
206a0597ba2SAndre Przywara } else {
207a0597ba2SAndre Przywara pmic = UNKNOWN;
208a0597ba2SAndre Przywara }
209a0597ba2SAndre Przywara break;
21003851367SAndre Przywara case 0x48: /* AXP1530 */
21103851367SAndre Przywara case 0x4b: /* AXP313A */
21203851367SAndre Przywara case 0x4c: /* AXP313B */
21303851367SAndre Przywara if (pmic == AXP313) {
21403851367SAndre Przywara INFO("PMIC: found AXP313\n");
21503851367SAndre Przywara /* no regulators to set up */
21603851367SAndre Przywara } else {
21703851367SAndre Przywara pmic = UNKNOWN;
21803851367SAndre Przywara }
21903851367SAndre Przywara break;
220646d06b2SAndre Przywara case 0xcf: /* version reg not implemented on AXP717 */
221646d06b2SAndre Przywara if (pmic == AXP717) {
222646d06b2SAndre Przywara INFO("PMIC: found AXP717\n");
223646d06b2SAndre Przywara /* no regulators to set up, U-Boot takes care of this */
224646d06b2SAndre Przywara } else {
225646d06b2SAndre Przywara pmic = UNKNOWN;
226646d06b2SAndre Przywara }
227646d06b2SAndre Przywara break;
228a0597ba2SAndre Przywara }
229a0597ba2SAndre Przywara
23004445898SAndre Przywara if (is_using_rsb()) {
231a0597ba2SAndre Przywara /* Switch the PMIC back to I2C mode. */
232a0597ba2SAndre Przywara return rsb_write(rsb_rt_addr, AXP20X_MODE_REG, AXP20X_MODE_I2C);
233a0597ba2SAndre Przywara }
234a0597ba2SAndre Przywara
23504445898SAndre Przywara if (pmic == UNKNOWN) {
23604445898SAndre Przywara INFO("Incompatible or unknown PMIC found.\n");
23704445898SAndre Przywara return -ENODEV;
23804445898SAndre Przywara }
23904445898SAndre Przywara
24004445898SAndre Przywara return 0;
24104445898SAndre Przywara }
24204445898SAndre Przywara
sunxi_power_down(void)24326123ca3SAndre Przywara void sunxi_power_down(void)
24426123ca3SAndre Przywara {
245a0597ba2SAndre Przywara int ret;
246a0597ba2SAndre Przywara
247a0597ba2SAndre Przywara if (pmic == UNKNOWN) {
248a0597ba2SAndre Przywara return;
249a0597ba2SAndre Przywara }
250a0597ba2SAndre Przywara
251a0597ba2SAndre Przywara /* Re-initialise after rich OS might have used it. */
252a0597ba2SAndre Przywara ret = pmic_bus_init(SUNXI_SOC_H616, pmic_bus_addr);
253a0597ba2SAndre Przywara if (ret) {
254a0597ba2SAndre Przywara return;
255a0597ba2SAndre Przywara }
256a0597ba2SAndre Przywara
25726123ca3SAndre Przywara switch (pmic) {
25826123ca3SAndre Przywara case AXP305:
259a0597ba2SAndre Przywara axp_setbits(0x32, BIT(7));
26026123ca3SAndre Przywara break;
26103851367SAndre Przywara case AXP313:
26203851367SAndre Przywara axp_setbits(0x1a, BIT(7));
26303851367SAndre Przywara break;
264646d06b2SAndre Przywara case AXP717:
265646d06b2SAndre Przywara axp_setbits(0x27, BIT(0));
266646d06b2SAndre Przywara break;
26726123ca3SAndre Przywara default:
26826123ca3SAndre Przywara break;
26926123ca3SAndre Przywara }
27026123ca3SAndre Przywara }
27126123ca3SAndre Przywara
sunxi_cpu_power_off_self(void)27226123ca3SAndre Przywara void sunxi_cpu_power_off_self(void)
27326123ca3SAndre Przywara {
27426123ca3SAndre Przywara u_register_t mpidr = read_mpidr();
27526123ca3SAndre Przywara unsigned int core = MPIDR_AFFLVL0_VAL(mpidr);
27626123ca3SAndre Przywara
27726123ca3SAndre Przywara /* Enable the CPUIDLE hardware (only really needs to be done once). */
27826123ca3SAndre Przywara mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0x16aa0000);
27926123ca3SAndre Przywara mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0xaa160001);
28026123ca3SAndre Przywara
28126123ca3SAndre Przywara /* Trigger power off for this core. */
28226123ca3SAndre Przywara mmio_write_32(SUNXI_CORE_CLOSE_REG, BIT_32(core));
28326123ca3SAndre Przywara }
284