aboutsummaryrefslogtreecommitdiff
path: root/plat/allwinner/sun50i_a64/sunxi_power.c
diff options
context:
space:
mode:
authorAndre Przywara <andre.przywara@arm.com>2018-09-16 11:24:34 +0100
committerAndre Przywara <andre.przywara@arm.com>2018-10-20 16:23:59 +0100
commitfb4e97868d996c6574147f318077bcba4f6c8e6b (patch)
treeb0d6eef338624d62f5c0dd43fa2d0e81056ef9fc /plat/allwinner/sun50i_a64/sunxi_power.c
parented80c1e2b306d347c25adfe350aa8100a89d88b6 (diff)
downloadtrusted-firmware-a-fb4e97868d996c6574147f318077bcba4f6c8e6b.tar.gz
allwinner: PMIC: AXP803: Setup basic voltage rails
Based on the just introduced PMIC FDT framework, we check the DT for more voltage rails that need to be setup early: - DCDC1 is typically the main board power rail, used for I/O pins, for instance. The PMIC's default is 3.0V, but 3.3V is what most boards use, so this needs to be adjusted as soon as possible. - DCDC5 is supposed to be connected to the DRAM. The AXP has some configurable reset voltage, but some boards get that wrong, so we better set up this here to avoid over- or under-volting. - DLDO1,2,3 and FLDO1 mostly drive some graphics related IP, some boards need this to be up to enable HDMI or the LCD screen, so we get screen output in U-Boot. To get the right setup, but still being flexible, we query the DT for the required voltage and whether that regulator is actually used. That gives us some robust default setup U-Boot is happy with. Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Diffstat (limited to 'plat/allwinner/sun50i_a64/sunxi_power.c')
-rw-r--r--plat/allwinner/sun50i_a64/sunxi_power.c86
1 files changed, 86 insertions, 0 deletions
diff --git a/plat/allwinner/sun50i_a64/sunxi_power.c b/plat/allwinner/sun50i_a64/sunxi_power.c
index 0d73371a89..9a2733996f 100644
--- a/plat/allwinner/sun50i_a64/sunxi_power.c
+++ b/plat/allwinner/sun50i_a64/sunxi_power.c
@@ -113,6 +113,11 @@ static int rsb_init(void)
AXP803_RT_ADDR);
}
+static int axp_write(uint8_t reg, uint8_t val)
+{
+ return rsb_write(AXP803_RT_ADDR, reg, val);
+}
+
static int axp_setbits(uint8_t reg, uint8_t set_mask)
{
uint8_t regval;
@@ -136,6 +141,79 @@ static bool should_enable_regulator(const void *fdt, int node)
return false;
}
+/*
+ * Retrieve the voltage from a given regulator DTB node.
+ * Both the regulator-{min,max}-microvolt properties must be present and
+ * have the same value. Return that value in millivolts.
+ */
+static int fdt_get_regulator_millivolt(const void *fdt, int node)
+{
+ const fdt32_t *prop;
+ uint32_t min_volt;
+
+ prop = fdt_getprop(fdt, node, "regulator-min-microvolt", NULL);
+ if (prop == NULL)
+ return -EINVAL;
+ min_volt = fdt32_to_cpu(*prop);
+
+ prop = fdt_getprop(fdt, node, "regulator-max-microvolt", NULL);
+ if (prop == NULL)
+ return -EINVAL;
+
+ if (fdt32_to_cpu(*prop) != min_volt)
+ return -EINVAL;
+
+ return min_volt / 1000;
+}
+
+#define NO_SPLIT 0xff
+
+struct axp_regulator {
+ char *dt_name;
+ uint16_t min_volt;
+ uint16_t max_volt;
+ uint16_t step;
+ unsigned char split;
+ unsigned char volt_reg;
+ unsigned char switch_reg;
+ unsigned char switch_bit;
+} regulators[] = {
+ {"dcdc1", 1600, 3400, 100, NO_SPLIT, 0x20, 0xff, 9},
+ {"dcdc5", 800, 1840, 10, 32, 0x24, 0xff, 9},
+ {"dldo1", 700, 3300, 100, NO_SPLIT, 0x15, 0x12, 3},
+ {"dldo2", 700, 4200, 100, 27, 0x16, 0x12, 4},
+ {"dldo3", 700, 3300, 100, NO_SPLIT, 0x17, 0x12, 5},
+ {"fldo1", 700, 1450, 50, NO_SPLIT, 0x1c, 0x13, 2},
+ {}
+};
+
+static int setup_regulator(const void *fdt, int node,
+ const struct axp_regulator *reg)
+{
+ int mvolt;
+ uint8_t regval;
+
+ if (!should_enable_regulator(fdt, node))
+ return -ENOENT;
+
+ mvolt = fdt_get_regulator_millivolt(fdt, node);
+ if (mvolt < reg->min_volt || mvolt > reg->max_volt)
+ return -EINVAL;
+
+ regval = (mvolt / reg->step) - (reg->min_volt / reg->step);
+ if (regval > reg->split)
+ regval = ((regval - reg->split) / 2) + reg->split;
+
+ axp_write(reg->volt_reg, regval);
+ if (reg->switch_reg < 0xff)
+ axp_setbits(reg->switch_reg, BIT(reg->switch_bit));
+
+ INFO("PMIC: AXP803: %s voltage: %d.%03dV\n", reg->dt_name,
+ mvolt / 1000, mvolt % 1000);
+
+ return 0;
+}
+
static void setup_axp803_rails(const void *fdt)
{
int node;
@@ -157,6 +235,7 @@ static void setup_axp803_rails(const void *fdt)
for (node = fdt_first_subnode(fdt, node);
node != -FDT_ERR_NOTFOUND;
node = fdt_next_subnode(fdt, node)) {
+ struct axp_regulator *reg;
const char *name;
int length;
@@ -165,6 +244,13 @@ static void setup_axp803_rails(const void *fdt)
continue;
name = fdt_get_name(fdt, node, &length);
+ for (reg = regulators; reg->dt_name; reg++) {
+ if (!strncmp(name, reg->dt_name, length)) {
+ setup_regulator(fdt, node, reg);
+ break;
+ }
+ }
+
if (!strncmp(name, "dc1sw", length)) {
INFO("PMIC: AXP803: Enabling DC1SW\n");
axp_setbits(0x12, BIT(7));