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 <lib/mmio.h> 17 #include <libfdt.h> 18 19 #include <sunxi_cpucfg.h> 20 #include <sunxi_def.h> 21 #include <sunxi_mmap.h> 22 #include <sunxi_private.h> 23 24 static uint16_t pmic_bus_addr; 25 static uint8_t rsb_rt_addr; 26 27 static enum pmic_type { 28 UNKNOWN, 29 AXP305, 30 } pmic; 31 32 static uint8_t get_rsb_rt_address(uint16_t hw_addr) 33 { 34 switch (hw_addr) { 35 case 0x745: return 0x3a; 36 } 37 38 return 0; 39 } 40 41 int axp_read(uint8_t reg) 42 { 43 return rsb_read(rsb_rt_addr, reg); 44 } 45 46 int axp_write(uint8_t reg, uint8_t val) 47 { 48 return rsb_write(rsb_rt_addr, reg, val); 49 } 50 51 static int rsb_init(int rsb_hw_addr) 52 { 53 int ret; 54 55 ret = rsb_init_controller(); 56 if (ret) { 57 return ret; 58 } 59 60 /* Switch to the recommended 3 MHz bus clock. */ 61 ret = rsb_set_bus_speed(SUNXI_OSC24M_CLK_IN_HZ, 3000000); 62 if (ret) { 63 return ret; 64 } 65 66 /* Initiate an I2C transaction to switch the PMIC to RSB mode. */ 67 ret = rsb_set_device_mode(AXP20X_MODE_RSB << 16 | AXP20X_MODE_REG << 8); 68 if (ret) { 69 return ret; 70 } 71 72 /* Associate the 8-bit runtime address with the 12-bit bus address. */ 73 ret = rsb_assign_runtime_address(rsb_hw_addr, rsb_rt_addr); 74 if (ret) { 75 return ret; 76 } 77 78 return 0; 79 } 80 81 static int pmic_bus_init(uint16_t socid, uint16_t rsb_hw_addr) 82 { 83 int ret; 84 85 ret = sunxi_init_platform_r_twi(socid, true); 86 if (ret) { 87 INFO("Could not init platform bus: %d\n", ret); 88 pmic = UNKNOWN; 89 return ret; 90 } 91 92 ret = rsb_init(rsb_hw_addr); 93 if (ret) { 94 pmic = UNKNOWN; 95 return ret; 96 } 97 98 return 0; 99 } 100 101 int sunxi_pmic_setup(uint16_t socid, const void *fdt) 102 { 103 int node, ret; 104 uint32_t reg; 105 106 node = fdt_node_offset_by_compatible(fdt, 0, "x-powers,axp806"); 107 if (node >= 0) { 108 pmic = AXP305; 109 } 110 111 if (pmic == UNKNOWN) { 112 INFO("PMIC: No known PMIC in DT, skipping setup.\n"); 113 return -ENODEV; 114 } 115 116 if (fdt_read_uint32(fdt, node, "reg", ®)) { 117 ERROR("PMIC: PMIC DT node does not contain reg property.\n"); 118 return -EINVAL; 119 } 120 121 pmic_bus_addr = reg; 122 rsb_rt_addr = get_rsb_rt_address(pmic_bus_addr); 123 if (rsb_rt_addr == 0) { 124 ERROR("PMIC: no mapping for RSB address 0x%x\n", reg); 125 return -EINVAL; 126 } 127 128 INFO("Probing for PMIC on RSB:\n"); 129 130 ret = pmic_bus_init(socid, pmic_bus_addr); 131 if (ret) { 132 return ret; 133 } 134 135 ret = axp_read(0x03); 136 switch (ret & 0xcf) { 137 case 0x40: /* AXP305 */ 138 if (pmic == AXP305) { 139 INFO("PMIC: found AXP305, setting up regulators\n"); 140 axp_setup_regulators(fdt); 141 } else { 142 pmic = UNKNOWN; 143 } 144 break; 145 } 146 147 /* Switch the PMIC back to I2C mode. */ 148 return rsb_write(rsb_rt_addr, AXP20X_MODE_REG, AXP20X_MODE_I2C); 149 } 150 151 void sunxi_power_down(void) 152 { 153 int ret; 154 155 if (pmic == UNKNOWN) { 156 return; 157 } 158 159 /* Re-initialise after rich OS might have used it. */ 160 ret = pmic_bus_init(SUNXI_SOC_H616, pmic_bus_addr); 161 if (ret) { 162 return; 163 } 164 165 switch (pmic) { 166 case AXP305: 167 axp_setbits(0x32, BIT(7)); 168 break; 169 default: 170 break; 171 } 172 } 173 174 void sunxi_cpu_power_off_self(void) 175 { 176 u_register_t mpidr = read_mpidr(); 177 unsigned int core = MPIDR_AFFLVL0_VAL(mpidr); 178 179 /* Enable the CPUIDLE hardware (only really needs to be done once). */ 180 mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0x16aa0000); 181 mmio_write_32(SUNXI_CPUIDLE_EN_REG, 0xaa160001); 182 183 /* Trigger power off for this core. */ 184 mmio_write_32(SUNXI_CORE_CLOSE_REG, BIT_32(core)); 185 } 186