1 /* 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> 13 #include <common/fdt_wrappers.h> 14 #include <drivers/allwinner/axp.h> 15 #include <drivers/allwinner/sunxi_rsb.h> 16 #include <drivers/mentor/mi2cv.h> 17 #include <lib/mmio.h> 18 #include <libfdt.h> 19 20 #include <sunxi_cpucfg.h> 21 #include <sunxi_def.h> 22 #include <sunxi_mmap.h> 23 #include <sunxi_private.h> 24 25 static uint16_t pmic_bus_addr; 26 static uint8_t rsb_rt_addr; 27 28 static bool is_using_rsb(void) 29 { 30 return rsb_rt_addr != 0; 31 } 32 33 static enum pmic_type { 34 UNKNOWN, 35 AXP305, 36 } pmic; 37 38 static uint8_t get_rsb_rt_address(uint16_t hw_addr) 39 { 40 switch (hw_addr) { 41 case 0x745: return 0x3a; 42 } 43 44 return 0; 45 } 46 47 int axp_read(uint8_t reg) 48 { 49 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, ®, 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; 66 } 67 68 int axp_write(uint8_t reg, uint8_t val) 69 { 70 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; 82 } 83 84 static 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 114 static int pmic_bus_init(uint16_t socid, uint16_t rsb_hw_addr) 115 { 116 int ret; 117 118 ret = sunxi_init_platform_r_twi(socid, is_using_rsb()); 119 if (ret) { 120 INFO("Could not init platform bus: %d\n", ret); 121 pmic = UNKNOWN; 122 return ret; 123 } 124 125 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); 134 } 135 136 return 0; 137 } 138 139 int sunxi_pmic_setup(uint16_t socid, const void *fdt) 140 { 141 int node, parent, ret; 142 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", ®)) { 155 ERROR("PMIC: PMIC DT node does not contain reg property.\n"); 156 return -EINVAL; 157 } 158 159 pmic_bus_addr = reg; 160 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 } 169 } 170 171 INFO("Probing for PMIC on %s:\n", is_using_rsb() ? "RSB" : "I2C"); 172 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 190 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; 201 } 202 203 void sunxi_power_down(void) 204 { 205 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 217 switch (pmic) { 218 case AXP305: 219 axp_setbits(0x32, BIT(7)); 220 break; 221 default: 222 break; 223 } 224 } 225 226 void 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 } 238