10bc752c9SSamuel Holland /* 20bc752c9SSamuel Holland * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. 30bc752c9SSamuel Holland * 40bc752c9SSamuel Holland * SPDX-License-Identifier: BSD-3-Clause 50bc752c9SSamuel Holland */ 60bc752c9SSamuel Holland 70bc752c9SSamuel Holland #include <errno.h> 80bc752c9SSamuel Holland 90bc752c9SSamuel Holland #include <libfdt.h> 100bc752c9SSamuel Holland 110bc752c9SSamuel Holland #include <common/debug.h> 120bc752c9SSamuel Holland #include <drivers/allwinner/axp.h> 130bc752c9SSamuel Holland 140bc752c9SSamuel Holland int axp_check_id(void) 150bc752c9SSamuel Holland { 160bc752c9SSamuel Holland int ret; 170bc752c9SSamuel Holland 180bc752c9SSamuel Holland ret = axp_read(0x03); 190bc752c9SSamuel Holland if (ret < 0) 200bc752c9SSamuel Holland return ret; 210bc752c9SSamuel Holland 220bc752c9SSamuel Holland ret &= 0xcf; 230bc752c9SSamuel Holland if (ret != axp_chip_id) { 240bc752c9SSamuel Holland ERROR("PMIC: Found unknown PMIC %02x\n", ret); 250bc752c9SSamuel Holland return ret; 260bc752c9SSamuel Holland } 270bc752c9SSamuel Holland 280bc752c9SSamuel Holland return 0; 290bc752c9SSamuel Holland } 300bc752c9SSamuel Holland 310bc752c9SSamuel Holland int axp_clrsetbits(uint8_t reg, uint8_t clr_mask, uint8_t set_mask) 320bc752c9SSamuel Holland { 330bc752c9SSamuel Holland uint8_t val; 340bc752c9SSamuel Holland int ret; 350bc752c9SSamuel Holland 360bc752c9SSamuel Holland ret = axp_read(reg); 370bc752c9SSamuel Holland if (ret < 0) 380bc752c9SSamuel Holland return ret; 390bc752c9SSamuel Holland 400bc752c9SSamuel Holland val = (ret & ~clr_mask) | set_mask; 410bc752c9SSamuel Holland 420bc752c9SSamuel Holland return axp_write(reg, val); 430bc752c9SSamuel Holland } 440bc752c9SSamuel Holland 450bc752c9SSamuel Holland void axp_power_off(void) 460bc752c9SSamuel Holland { 470bc752c9SSamuel Holland /* Set "power disable control" bit */ 480bc752c9SSamuel Holland axp_setbits(0x32, BIT(7)); 490bc752c9SSamuel Holland } 500bc752c9SSamuel Holland 51*67412e4dSAndre Przywara #if SUNXI_SETUP_REGULATORS == 1 520bc752c9SSamuel Holland /* 530bc752c9SSamuel Holland * Retrieve the voltage from a given regulator DTB node. 540bc752c9SSamuel Holland * Both the regulator-{min,max}-microvolt properties must be present and 550bc752c9SSamuel Holland * have the same value. Return that value in millivolts. 560bc752c9SSamuel Holland */ 570bc752c9SSamuel Holland static int fdt_get_regulator_millivolt(const void *fdt, int node) 580bc752c9SSamuel Holland { 590bc752c9SSamuel Holland const fdt32_t *prop; 600bc752c9SSamuel Holland uint32_t min_volt; 610bc752c9SSamuel Holland 620bc752c9SSamuel Holland prop = fdt_getprop(fdt, node, "regulator-min-microvolt", NULL); 630bc752c9SSamuel Holland if (prop == NULL) 640bc752c9SSamuel Holland return -EINVAL; 650bc752c9SSamuel Holland min_volt = fdt32_to_cpu(*prop); 660bc752c9SSamuel Holland 670bc752c9SSamuel Holland prop = fdt_getprop(fdt, node, "regulator-max-microvolt", NULL); 680bc752c9SSamuel Holland if (prop == NULL) 690bc752c9SSamuel Holland return -EINVAL; 700bc752c9SSamuel Holland 710bc752c9SSamuel Holland if (fdt32_to_cpu(*prop) != min_volt) 720bc752c9SSamuel Holland return -EINVAL; 730bc752c9SSamuel Holland 740bc752c9SSamuel Holland return min_volt / 1000; 750bc752c9SSamuel Holland } 760bc752c9SSamuel Holland 770bc752c9SSamuel Holland static int setup_regulator(const void *fdt, int node, 780bc752c9SSamuel Holland const struct axp_regulator *reg) 790bc752c9SSamuel Holland { 800bc752c9SSamuel Holland uint8_t val; 810bc752c9SSamuel Holland int mvolt; 820bc752c9SSamuel Holland 830bc752c9SSamuel Holland mvolt = fdt_get_regulator_millivolt(fdt, node); 840bc752c9SSamuel Holland if (mvolt < reg->min_volt || mvolt > reg->max_volt) 850bc752c9SSamuel Holland return -EINVAL; 860bc752c9SSamuel Holland 870bc752c9SSamuel Holland val = (mvolt / reg->step) - (reg->min_volt / reg->step); 880bc752c9SSamuel Holland if (val > reg->split) 890bc752c9SSamuel Holland val = ((val - reg->split) / 2) + reg->split; 900bc752c9SSamuel Holland 910bc752c9SSamuel Holland axp_write(reg->volt_reg, val); 920bc752c9SSamuel Holland axp_setbits(reg->switch_reg, BIT(reg->switch_bit)); 930bc752c9SSamuel Holland 940bc752c9SSamuel Holland INFO("PMIC: %s voltage: %d.%03dV\n", reg->dt_name, 950bc752c9SSamuel Holland mvolt / 1000, mvolt % 1000); 960bc752c9SSamuel Holland 970bc752c9SSamuel Holland return 0; 980bc752c9SSamuel Holland } 990bc752c9SSamuel Holland 1009655a1f5SRoman Beranek static bool is_node_disabled(const void *fdt, int node) 1019655a1f5SRoman Beranek { 1029655a1f5SRoman Beranek const char *cell; 1039655a1f5SRoman Beranek cell = fdt_getprop(fdt, node, "status", NULL); 1049655a1f5SRoman Beranek if (cell == NULL) { 1059655a1f5SRoman Beranek return false; 1069655a1f5SRoman Beranek } 1079655a1f5SRoman Beranek return strcmp(cell, "okay") != 0; 1089655a1f5SRoman Beranek } 1099655a1f5SRoman Beranek 1100bc752c9SSamuel Holland static bool should_enable_regulator(const void *fdt, int node) 1110bc752c9SSamuel Holland { 1129655a1f5SRoman Beranek if (is_node_disabled(fdt, node)) { 1139655a1f5SRoman Beranek return false; 1149655a1f5SRoman Beranek } 1159655a1f5SRoman Beranek if (fdt_getprop(fdt, node, "phandle", NULL) != NULL) { 1160bc752c9SSamuel Holland return true; 1179655a1f5SRoman Beranek } 1189655a1f5SRoman Beranek if (fdt_getprop(fdt, node, "regulator-always-on", NULL) != NULL) { 1190bc752c9SSamuel Holland return true; 1209655a1f5SRoman Beranek } 1210bc752c9SSamuel Holland return false; 1220bc752c9SSamuel Holland } 1230bc752c9SSamuel Holland 12493fa305cSAndre Przywara static bool board_uses_usb0_host_mode(const void *fdt) 12593fa305cSAndre Przywara { 12693fa305cSAndre Przywara int node, length; 12793fa305cSAndre Przywara const char *prop; 12893fa305cSAndre Przywara 12993fa305cSAndre Przywara node = fdt_node_offset_by_compatible(fdt, -1, 13093fa305cSAndre Przywara "allwinner,sun8i-a33-musb"); 13193fa305cSAndre Przywara if (node < 0) { 13293fa305cSAndre Przywara return false; 13393fa305cSAndre Przywara } 13493fa305cSAndre Przywara 13593fa305cSAndre Przywara prop = fdt_getprop(fdt, node, "dr_mode", &length); 13693fa305cSAndre Przywara if (!prop) { 13793fa305cSAndre Przywara return false; 13893fa305cSAndre Przywara } 13993fa305cSAndre Przywara 14093fa305cSAndre Przywara return !strncmp(prop, "host", length); 14193fa305cSAndre Przywara } 14293fa305cSAndre Przywara 1430bc752c9SSamuel Holland void axp_setup_regulators(const void *fdt) 1440bc752c9SSamuel Holland { 1450bc752c9SSamuel Holland int node; 146f6d9c4caSSamuel Holland bool sw = false; 1470bc752c9SSamuel Holland 1480bc752c9SSamuel Holland if (fdt == NULL) 1490bc752c9SSamuel Holland return; 1500bc752c9SSamuel Holland 1510bc752c9SSamuel Holland /* locate the PMIC DT node, bail out if not found */ 1520bc752c9SSamuel Holland node = fdt_node_offset_by_compatible(fdt, -1, axp_compatible); 1530bc752c9SSamuel Holland if (node < 0) { 1540bc752c9SSamuel Holland WARN("PMIC: No PMIC DT node, skipping setup\n"); 1550bc752c9SSamuel Holland return; 1560bc752c9SSamuel Holland } 1570bc752c9SSamuel Holland 158f6d9c4caSSamuel Holland /* This applies to AXP803 only. */ 15993fa305cSAndre Przywara if (fdt_getprop(fdt, node, "x-powers,drive-vbus-en", NULL) && 16093fa305cSAndre Przywara board_uses_usb0_host_mode(fdt)) { 1610bc752c9SSamuel Holland axp_clrbits(0x8f, BIT(4)); 1620bc752c9SSamuel Holland axp_setbits(0x30, BIT(2)); 1630bc752c9SSamuel Holland INFO("PMIC: Enabling DRIVEVBUS\n"); 1640bc752c9SSamuel Holland } 1650bc752c9SSamuel Holland 1660bc752c9SSamuel Holland /* descend into the "regulators" subnode */ 1670bc752c9SSamuel Holland node = fdt_subnode_offset(fdt, node, "regulators"); 1680bc752c9SSamuel Holland if (node < 0) { 1690bc752c9SSamuel Holland WARN("PMIC: No regulators DT node, skipping setup\n"); 1700bc752c9SSamuel Holland return; 1710bc752c9SSamuel Holland } 1720bc752c9SSamuel Holland 1730bc752c9SSamuel Holland /* iterate over all regulators to find used ones */ 1740bc752c9SSamuel Holland fdt_for_each_subnode(node, fdt, node) { 1750bc752c9SSamuel Holland const struct axp_regulator *reg; 1760bc752c9SSamuel Holland const char *name; 1770bc752c9SSamuel Holland int length; 1780bc752c9SSamuel Holland 1790bc752c9SSamuel Holland /* We only care if it's always on or referenced. */ 1800bc752c9SSamuel Holland if (!should_enable_regulator(fdt, node)) 1810bc752c9SSamuel Holland continue; 1820bc752c9SSamuel Holland 1830bc752c9SSamuel Holland name = fdt_get_name(fdt, node, &length); 184f6d9c4caSSamuel Holland 185f6d9c4caSSamuel Holland /* Enable the switch last to avoid overheating. */ 186f6d9c4caSSamuel Holland if (!strncmp(name, "dc1sw", length) || 187f6d9c4caSSamuel Holland !strncmp(name, "sw", length)) { 188f6d9c4caSSamuel Holland sw = true; 189f6d9c4caSSamuel Holland continue; 190f6d9c4caSSamuel Holland } 191f6d9c4caSSamuel Holland 1920bc752c9SSamuel Holland for (reg = axp_regulators; reg->dt_name; reg++) { 1930bc752c9SSamuel Holland if (!strncmp(name, reg->dt_name, length)) { 1940bc752c9SSamuel Holland setup_regulator(fdt, node, reg); 1950bc752c9SSamuel Holland break; 1960bc752c9SSamuel Holland } 1970bc752c9SSamuel Holland } 1980bc752c9SSamuel Holland } 1990bc752c9SSamuel Holland 2000bc752c9SSamuel Holland /* 201f6d9c4caSSamuel Holland * On the AXP803, if DLDO2 is enabled after DC1SW, the PMIC overheats 202f6d9c4caSSamuel Holland * and shuts down. So always enable DC1SW as the very last regulator. 2030bc752c9SSamuel Holland */ 204f6d9c4caSSamuel Holland if (sw) { 205f6d9c4caSSamuel Holland INFO("PMIC: Enabling DC SW\n"); 206f6d9c4caSSamuel Holland if (axp_chip_id == AXP803_CHIP_ID) 2070bc752c9SSamuel Holland axp_setbits(0x12, BIT(7)); 208f6d9c4caSSamuel Holland if (axp_chip_id == AXP805_CHIP_ID) 209f6d9c4caSSamuel Holland axp_setbits(0x11, BIT(7)); 2100bc752c9SSamuel Holland } 2110bc752c9SSamuel Holland } 212*67412e4dSAndre Przywara #endif /* SUNXI_SETUP_REGULATORS */ 213