1 /* 2 * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <errno.h> 8 9 #include <libfdt.h> 10 11 #include <common/debug.h> 12 #include <drivers/allwinner/axp.h> 13 14 int axp_check_id(void) 15 { 16 int ret; 17 18 ret = axp_read(0x03); 19 if (ret < 0) 20 return ret; 21 22 ret &= 0xcf; 23 if (ret != axp_chip_id) { 24 ERROR("PMIC: Found unknown PMIC %02x\n", ret); 25 return ret; 26 } 27 28 return 0; 29 } 30 31 int axp_clrsetbits(uint8_t reg, uint8_t clr_mask, uint8_t set_mask) 32 { 33 uint8_t val; 34 int ret; 35 36 ret = axp_read(reg); 37 if (ret < 0) 38 return ret; 39 40 val = (ret & ~clr_mask) | set_mask; 41 42 return axp_write(reg, val); 43 } 44 45 void axp_power_off(void) 46 { 47 /* Set "power disable control" bit */ 48 axp_setbits(0x32, BIT(7)); 49 } 50 51 #if SUNXI_SETUP_REGULATORS == 1 52 /* 53 * Retrieve the voltage from a given regulator DTB node. 54 * Both the regulator-{min,max}-microvolt properties must be present and 55 * have the same value. Return that value in millivolts. 56 */ 57 static int fdt_get_regulator_millivolt(const void *fdt, int node) 58 { 59 const fdt32_t *prop; 60 uint32_t min_volt; 61 62 prop = fdt_getprop(fdt, node, "regulator-min-microvolt", NULL); 63 if (prop == NULL) 64 return -EINVAL; 65 min_volt = fdt32_to_cpu(*prop); 66 67 prop = fdt_getprop(fdt, node, "regulator-max-microvolt", NULL); 68 if (prop == NULL) 69 return -EINVAL; 70 71 if (fdt32_to_cpu(*prop) != min_volt) 72 return -EINVAL; 73 74 return min_volt / 1000; 75 } 76 77 static int setup_regulator(const void *fdt, int node, 78 const struct axp_regulator *reg) 79 { 80 uint8_t val; 81 int mvolt; 82 83 mvolt = fdt_get_regulator_millivolt(fdt, node); 84 if (mvolt < reg->min_volt || mvolt > reg->max_volt) 85 return -EINVAL; 86 87 val = (mvolt / reg->step) - (reg->min_volt / reg->step); 88 if (val > reg->split) 89 val = ((val - reg->split) / 2) + reg->split; 90 91 axp_write(reg->volt_reg, val); 92 axp_setbits(reg->switch_reg, BIT(reg->switch_bit)); 93 94 INFO("PMIC: %s voltage: %d.%03dV\n", reg->dt_name, 95 mvolt / 1000, mvolt % 1000); 96 97 return 0; 98 } 99 100 static bool is_node_disabled(const void *fdt, int node) 101 { 102 const char *cell; 103 cell = fdt_getprop(fdt, node, "status", NULL); 104 if (cell == NULL) { 105 return false; 106 } 107 return strcmp(cell, "okay") != 0; 108 } 109 110 static bool should_enable_regulator(const void *fdt, int node) 111 { 112 if (is_node_disabled(fdt, node)) { 113 return false; 114 } 115 if (fdt_getprop(fdt, node, "phandle", NULL) != NULL) { 116 return true; 117 } 118 if (fdt_getprop(fdt, node, "regulator-always-on", NULL) != NULL) { 119 return true; 120 } 121 return false; 122 } 123 124 static bool board_uses_usb0_host_mode(const void *fdt) 125 { 126 int node, length; 127 const char *prop; 128 129 node = fdt_node_offset_by_compatible(fdt, -1, 130 "allwinner,sun8i-a33-musb"); 131 if (node < 0) { 132 return false; 133 } 134 135 prop = fdt_getprop(fdt, node, "dr_mode", &length); 136 if (!prop) { 137 return false; 138 } 139 140 return !strncmp(prop, "host", length); 141 } 142 143 void axp_setup_regulators(const void *fdt) 144 { 145 int node; 146 bool sw = false; 147 148 if (fdt == NULL) 149 return; 150 151 /* locate the PMIC DT node, bail out if not found */ 152 node = fdt_node_offset_by_compatible(fdt, -1, axp_compatible); 153 if (node < 0) { 154 WARN("PMIC: No PMIC DT node, skipping setup\n"); 155 return; 156 } 157 158 /* This applies to AXP803 only. */ 159 if (fdt_getprop(fdt, node, "x-powers,drive-vbus-en", NULL) && 160 board_uses_usb0_host_mode(fdt)) { 161 axp_clrbits(0x8f, BIT(4)); 162 axp_setbits(0x30, BIT(2)); 163 INFO("PMIC: Enabling DRIVEVBUS\n"); 164 } 165 166 /* descend into the "regulators" subnode */ 167 node = fdt_subnode_offset(fdt, node, "regulators"); 168 if (node < 0) { 169 WARN("PMIC: No regulators DT node, skipping setup\n"); 170 return; 171 } 172 173 /* iterate over all regulators to find used ones */ 174 fdt_for_each_subnode(node, fdt, node) { 175 const struct axp_regulator *reg; 176 const char *name; 177 int length; 178 179 /* We only care if it's always on or referenced. */ 180 if (!should_enable_regulator(fdt, node)) 181 continue; 182 183 name = fdt_get_name(fdt, node, &length); 184 185 /* Enable the switch last to avoid overheating. */ 186 if (!strncmp(name, "dc1sw", length) || 187 !strncmp(name, "sw", length)) { 188 sw = true; 189 continue; 190 } 191 192 for (reg = axp_regulators; reg->dt_name; reg++) { 193 if (!strncmp(name, reg->dt_name, length)) { 194 setup_regulator(fdt, node, reg); 195 break; 196 } 197 } 198 } 199 200 /* 201 * On the AXP803, if DLDO2 is enabled after DC1SW, the PMIC overheats 202 * and shuts down. So always enable DC1SW as the very last regulator. 203 */ 204 if (sw) { 205 INFO("PMIC: Enabling DC SW\n"); 206 if (axp_chip_id == AXP803_CHIP_ID) 207 axp_setbits(0x12, BIT(7)); 208 if (axp_chip_id == AXP805_CHIP_ID) 209 axp_setbits(0x11, BIT(7)); 210 } 211 } 212 #endif /* SUNXI_SETUP_REGULATORS */ 213