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> 12*06eb3e36SAndre Przywara #include <common/fdt_wrappers.h> 130bc752c9SSamuel Holland #include <drivers/allwinner/axp.h> 140bc752c9SSamuel Holland 150bc752c9SSamuel Holland int axp_check_id(void) 160bc752c9SSamuel Holland { 170bc752c9SSamuel Holland int ret; 180bc752c9SSamuel Holland 190bc752c9SSamuel Holland ret = axp_read(0x03); 200bc752c9SSamuel Holland if (ret < 0) 210bc752c9SSamuel Holland return ret; 220bc752c9SSamuel Holland 230bc752c9SSamuel Holland ret &= 0xcf; 240bc752c9SSamuel Holland if (ret != axp_chip_id) { 250bc752c9SSamuel Holland ERROR("PMIC: Found unknown PMIC %02x\n", ret); 260bc752c9SSamuel Holland return ret; 270bc752c9SSamuel Holland } 280bc752c9SSamuel Holland 290bc752c9SSamuel Holland return 0; 300bc752c9SSamuel Holland } 310bc752c9SSamuel Holland 320bc752c9SSamuel Holland int axp_clrsetbits(uint8_t reg, uint8_t clr_mask, uint8_t set_mask) 330bc752c9SSamuel Holland { 340bc752c9SSamuel Holland uint8_t val; 350bc752c9SSamuel Holland int ret; 360bc752c9SSamuel Holland 370bc752c9SSamuel Holland ret = axp_read(reg); 380bc752c9SSamuel Holland if (ret < 0) 390bc752c9SSamuel Holland return ret; 400bc752c9SSamuel Holland 410bc752c9SSamuel Holland val = (ret & ~clr_mask) | set_mask; 420bc752c9SSamuel Holland 430bc752c9SSamuel Holland return axp_write(reg, val); 440bc752c9SSamuel Holland } 450bc752c9SSamuel Holland 460bc752c9SSamuel Holland void axp_power_off(void) 470bc752c9SSamuel Holland { 480bc752c9SSamuel Holland /* Set "power disable control" bit */ 490bc752c9SSamuel Holland axp_setbits(0x32, BIT(7)); 500bc752c9SSamuel Holland } 510bc752c9SSamuel Holland 5267412e4dSAndre Przywara #if SUNXI_SETUP_REGULATORS == 1 530bc752c9SSamuel Holland /* 540bc752c9SSamuel Holland * Retrieve the voltage from a given regulator DTB node. 550bc752c9SSamuel Holland * Both the regulator-{min,max}-microvolt properties must be present and 560bc752c9SSamuel Holland * have the same value. Return that value in millivolts. 570bc752c9SSamuel Holland */ 580bc752c9SSamuel Holland static int fdt_get_regulator_millivolt(const void *fdt, int node) 590bc752c9SSamuel Holland { 600bc752c9SSamuel Holland const fdt32_t *prop; 610bc752c9SSamuel Holland uint32_t min_volt; 620bc752c9SSamuel Holland 630bc752c9SSamuel Holland prop = fdt_getprop(fdt, node, "regulator-min-microvolt", NULL); 640bc752c9SSamuel Holland if (prop == NULL) 650bc752c9SSamuel Holland return -EINVAL; 660bc752c9SSamuel Holland min_volt = fdt32_to_cpu(*prop); 670bc752c9SSamuel Holland 680bc752c9SSamuel Holland prop = fdt_getprop(fdt, node, "regulator-max-microvolt", NULL); 690bc752c9SSamuel Holland if (prop == NULL) 700bc752c9SSamuel Holland return -EINVAL; 710bc752c9SSamuel Holland 720bc752c9SSamuel Holland if (fdt32_to_cpu(*prop) != min_volt) 730bc752c9SSamuel Holland return -EINVAL; 740bc752c9SSamuel Holland 750bc752c9SSamuel Holland return min_volt / 1000; 760bc752c9SSamuel Holland } 770bc752c9SSamuel Holland 780bc752c9SSamuel Holland static int setup_regulator(const void *fdt, int node, 790bc752c9SSamuel Holland const struct axp_regulator *reg) 800bc752c9SSamuel Holland { 810bc752c9SSamuel Holland uint8_t val; 820bc752c9SSamuel Holland int mvolt; 830bc752c9SSamuel Holland 840bc752c9SSamuel Holland mvolt = fdt_get_regulator_millivolt(fdt, node); 850bc752c9SSamuel Holland if (mvolt < reg->min_volt || mvolt > reg->max_volt) 860bc752c9SSamuel Holland return -EINVAL; 870bc752c9SSamuel Holland 880bc752c9SSamuel Holland val = (mvolt / reg->step) - (reg->min_volt / reg->step); 890bc752c9SSamuel Holland if (val > reg->split) 900bc752c9SSamuel Holland val = ((val - reg->split) / 2) + reg->split; 910bc752c9SSamuel Holland 920bc752c9SSamuel Holland axp_write(reg->volt_reg, val); 930bc752c9SSamuel Holland axp_setbits(reg->switch_reg, BIT(reg->switch_bit)); 940bc752c9SSamuel Holland 950bc752c9SSamuel Holland INFO("PMIC: %s voltage: %d.%03dV\n", reg->dt_name, 960bc752c9SSamuel Holland mvolt / 1000, mvolt % 1000); 970bc752c9SSamuel Holland 980bc752c9SSamuel Holland return 0; 990bc752c9SSamuel Holland } 1000bc752c9SSamuel Holland 1010bc752c9SSamuel Holland static bool should_enable_regulator(const void *fdt, int node) 1020bc752c9SSamuel Holland { 103*06eb3e36SAndre Przywara if (!fdt_node_is_enabled(fdt, node)) { 1049655a1f5SRoman Beranek return false; 1059655a1f5SRoman Beranek } 1069655a1f5SRoman Beranek if (fdt_getprop(fdt, node, "phandle", NULL) != NULL) { 1070bc752c9SSamuel Holland return true; 1089655a1f5SRoman Beranek } 1099655a1f5SRoman Beranek if (fdt_getprop(fdt, node, "regulator-always-on", NULL) != NULL) { 1100bc752c9SSamuel Holland return true; 1119655a1f5SRoman Beranek } 1120bc752c9SSamuel Holland return false; 1130bc752c9SSamuel Holland } 1140bc752c9SSamuel Holland 11593fa305cSAndre Przywara static bool board_uses_usb0_host_mode(const void *fdt) 11693fa305cSAndre Przywara { 11793fa305cSAndre Przywara int node, length; 11893fa305cSAndre Przywara const char *prop; 11993fa305cSAndre Przywara 12093fa305cSAndre Przywara node = fdt_node_offset_by_compatible(fdt, -1, 12193fa305cSAndre Przywara "allwinner,sun8i-a33-musb"); 12293fa305cSAndre Przywara if (node < 0) { 12393fa305cSAndre Przywara return false; 12493fa305cSAndre Przywara } 12593fa305cSAndre Przywara 12693fa305cSAndre Przywara prop = fdt_getprop(fdt, node, "dr_mode", &length); 12793fa305cSAndre Przywara if (!prop) { 12893fa305cSAndre Przywara return false; 12993fa305cSAndre Przywara } 13093fa305cSAndre Przywara 13193fa305cSAndre Przywara return !strncmp(prop, "host", length); 13293fa305cSAndre Przywara } 13393fa305cSAndre Przywara 1340bc752c9SSamuel Holland void axp_setup_regulators(const void *fdt) 1350bc752c9SSamuel Holland { 1360bc752c9SSamuel Holland int node; 137f6d9c4caSSamuel Holland bool sw = false; 1380bc752c9SSamuel Holland 1390bc752c9SSamuel Holland if (fdt == NULL) 1400bc752c9SSamuel Holland return; 1410bc752c9SSamuel Holland 1420bc752c9SSamuel Holland /* locate the PMIC DT node, bail out if not found */ 1430bc752c9SSamuel Holland node = fdt_node_offset_by_compatible(fdt, -1, axp_compatible); 1440bc752c9SSamuel Holland if (node < 0) { 1450bc752c9SSamuel Holland WARN("PMIC: No PMIC DT node, skipping setup\n"); 1460bc752c9SSamuel Holland return; 1470bc752c9SSamuel Holland } 1480bc752c9SSamuel Holland 149f6d9c4caSSamuel Holland /* This applies to AXP803 only. */ 15093fa305cSAndre Przywara if (fdt_getprop(fdt, node, "x-powers,drive-vbus-en", NULL) && 15193fa305cSAndre Przywara board_uses_usb0_host_mode(fdt)) { 1520bc752c9SSamuel Holland axp_clrbits(0x8f, BIT(4)); 1530bc752c9SSamuel Holland axp_setbits(0x30, BIT(2)); 1540bc752c9SSamuel Holland INFO("PMIC: Enabling DRIVEVBUS\n"); 1550bc752c9SSamuel Holland } 1560bc752c9SSamuel Holland 1570bc752c9SSamuel Holland /* descend into the "regulators" subnode */ 1580bc752c9SSamuel Holland node = fdt_subnode_offset(fdt, node, "regulators"); 1590bc752c9SSamuel Holland if (node < 0) { 1600bc752c9SSamuel Holland WARN("PMIC: No regulators DT node, skipping setup\n"); 1610bc752c9SSamuel Holland return; 1620bc752c9SSamuel Holland } 1630bc752c9SSamuel Holland 1640bc752c9SSamuel Holland /* iterate over all regulators to find used ones */ 1650bc752c9SSamuel Holland fdt_for_each_subnode(node, fdt, node) { 1660bc752c9SSamuel Holland const struct axp_regulator *reg; 1670bc752c9SSamuel Holland const char *name; 1680bc752c9SSamuel Holland int length; 1690bc752c9SSamuel Holland 1700bc752c9SSamuel Holland /* We only care if it's always on or referenced. */ 1710bc752c9SSamuel Holland if (!should_enable_regulator(fdt, node)) 1720bc752c9SSamuel Holland continue; 1730bc752c9SSamuel Holland 1740bc752c9SSamuel Holland name = fdt_get_name(fdt, node, &length); 175f6d9c4caSSamuel Holland 176f6d9c4caSSamuel Holland /* Enable the switch last to avoid overheating. */ 177f6d9c4caSSamuel Holland if (!strncmp(name, "dc1sw", length) || 178f6d9c4caSSamuel Holland !strncmp(name, "sw", length)) { 179f6d9c4caSSamuel Holland sw = true; 180f6d9c4caSSamuel Holland continue; 181f6d9c4caSSamuel Holland } 182f6d9c4caSSamuel Holland 1830bc752c9SSamuel Holland for (reg = axp_regulators; reg->dt_name; reg++) { 1840bc752c9SSamuel Holland if (!strncmp(name, reg->dt_name, length)) { 1850bc752c9SSamuel Holland setup_regulator(fdt, node, reg); 1860bc752c9SSamuel Holland break; 1870bc752c9SSamuel Holland } 1880bc752c9SSamuel Holland } 1890bc752c9SSamuel Holland } 1900bc752c9SSamuel Holland 1910bc752c9SSamuel Holland /* 192f6d9c4caSSamuel Holland * On the AXP803, if DLDO2 is enabled after DC1SW, the PMIC overheats 193f6d9c4caSSamuel Holland * and shuts down. So always enable DC1SW as the very last regulator. 1940bc752c9SSamuel Holland */ 195f6d9c4caSSamuel Holland if (sw) { 196f6d9c4caSSamuel Holland INFO("PMIC: Enabling DC SW\n"); 197f6d9c4caSSamuel Holland if (axp_chip_id == AXP803_CHIP_ID) 1980bc752c9SSamuel Holland axp_setbits(0x12, BIT(7)); 199f6d9c4caSSamuel Holland if (axp_chip_id == AXP805_CHIP_ID) 200f6d9c4caSSamuel Holland axp_setbits(0x11, BIT(7)); 2010bc752c9SSamuel Holland } 2020bc752c9SSamuel Holland } 20367412e4dSAndre Przywara #endif /* SUNXI_SETUP_REGULATORS */ 204