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 /* 52 * Retrieve the voltage from a given regulator DTB node. 53 * Both the regulator-{min,max}-microvolt properties must be present and 54 * have the same value. Return that value in millivolts. 55 */ 56 static int fdt_get_regulator_millivolt(const void *fdt, int node) 57 { 58 const fdt32_t *prop; 59 uint32_t min_volt; 60 61 prop = fdt_getprop(fdt, node, "regulator-min-microvolt", NULL); 62 if (prop == NULL) 63 return -EINVAL; 64 min_volt = fdt32_to_cpu(*prop); 65 66 prop = fdt_getprop(fdt, node, "regulator-max-microvolt", NULL); 67 if (prop == NULL) 68 return -EINVAL; 69 70 if (fdt32_to_cpu(*prop) != min_volt) 71 return -EINVAL; 72 73 return min_volt / 1000; 74 } 75 76 static int setup_regulator(const void *fdt, int node, 77 const struct axp_regulator *reg) 78 { 79 uint8_t val; 80 int mvolt; 81 82 mvolt = fdt_get_regulator_millivolt(fdt, node); 83 if (mvolt < reg->min_volt || mvolt > reg->max_volt) 84 return -EINVAL; 85 86 val = (mvolt / reg->step) - (reg->min_volt / reg->step); 87 if (val > reg->split) 88 val = ((val - reg->split) / 2) + reg->split; 89 90 axp_write(reg->volt_reg, val); 91 axp_setbits(reg->switch_reg, BIT(reg->switch_bit)); 92 93 INFO("PMIC: %s voltage: %d.%03dV\n", reg->dt_name, 94 mvolt / 1000, mvolt % 1000); 95 96 return 0; 97 } 98 99 static bool is_node_disabled(const void *fdt, int node) 100 { 101 const char *cell; 102 cell = fdt_getprop(fdt, node, "status", NULL); 103 if (cell == NULL) { 104 return false; 105 } 106 return strcmp(cell, "okay") != 0; 107 } 108 109 static bool should_enable_regulator(const void *fdt, int node) 110 { 111 if (is_node_disabled(fdt, node)) { 112 return false; 113 } 114 if (fdt_getprop(fdt, node, "phandle", NULL) != NULL) { 115 return true; 116 } 117 if (fdt_getprop(fdt, node, "regulator-always-on", NULL) != NULL) { 118 return true; 119 } 120 return false; 121 } 122 123 static bool board_uses_usb0_host_mode(const void *fdt) 124 { 125 int node, length; 126 const char *prop; 127 128 node = fdt_node_offset_by_compatible(fdt, -1, 129 "allwinner,sun8i-a33-musb"); 130 if (node < 0) { 131 return false; 132 } 133 134 prop = fdt_getprop(fdt, node, "dr_mode", &length); 135 if (!prop) { 136 return false; 137 } 138 139 return !strncmp(prop, "host", length); 140 } 141 142 void axp_setup_regulators(const void *fdt) 143 { 144 int node; 145 bool sw = false; 146 147 if (fdt == NULL) 148 return; 149 150 /* locate the PMIC DT node, bail out if not found */ 151 node = fdt_node_offset_by_compatible(fdt, -1, axp_compatible); 152 if (node < 0) { 153 WARN("PMIC: No PMIC DT node, skipping setup\n"); 154 return; 155 } 156 157 /* This applies to AXP803 only. */ 158 if (fdt_getprop(fdt, node, "x-powers,drive-vbus-en", NULL) && 159 board_uses_usb0_host_mode(fdt)) { 160 axp_clrbits(0x8f, BIT(4)); 161 axp_setbits(0x30, BIT(2)); 162 INFO("PMIC: Enabling DRIVEVBUS\n"); 163 } 164 165 /* descend into the "regulators" subnode */ 166 node = fdt_subnode_offset(fdt, node, "regulators"); 167 if (node < 0) { 168 WARN("PMIC: No regulators DT node, skipping setup\n"); 169 return; 170 } 171 172 /* iterate over all regulators to find used ones */ 173 fdt_for_each_subnode(node, fdt, node) { 174 const struct axp_regulator *reg; 175 const char *name; 176 int length; 177 178 /* We only care if it's always on or referenced. */ 179 if (!should_enable_regulator(fdt, node)) 180 continue; 181 182 name = fdt_get_name(fdt, node, &length); 183 184 /* Enable the switch last to avoid overheating. */ 185 if (!strncmp(name, "dc1sw", length) || 186 !strncmp(name, "sw", length)) { 187 sw = true; 188 continue; 189 } 190 191 for (reg = axp_regulators; reg->dt_name; reg++) { 192 if (!strncmp(name, reg->dt_name, length)) { 193 setup_regulator(fdt, node, reg); 194 break; 195 } 196 } 197 } 198 199 /* 200 * On the AXP803, if DLDO2 is enabled after DC1SW, the PMIC overheats 201 * and shuts down. So always enable DC1SW as the very last regulator. 202 */ 203 if (sw) { 204 INFO("PMIC: Enabling DC SW\n"); 205 if (axp_chip_id == AXP803_CHIP_ID) 206 axp_setbits(0x12, BIT(7)); 207 if (axp_chip_id == AXP805_CHIP_ID) 208 axp_setbits(0x11, BIT(7)); 209 } 210 } 211