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 510bc752c9SSamuel Holland /* 520bc752c9SSamuel Holland * Retrieve the voltage from a given regulator DTB node. 530bc752c9SSamuel Holland * Both the regulator-{min,max}-microvolt properties must be present and 540bc752c9SSamuel Holland * have the same value. Return that value in millivolts. 550bc752c9SSamuel Holland */ 560bc752c9SSamuel Holland static int fdt_get_regulator_millivolt(const void *fdt, int node) 570bc752c9SSamuel Holland { 580bc752c9SSamuel Holland const fdt32_t *prop; 590bc752c9SSamuel Holland uint32_t min_volt; 600bc752c9SSamuel Holland 610bc752c9SSamuel Holland prop = fdt_getprop(fdt, node, "regulator-min-microvolt", NULL); 620bc752c9SSamuel Holland if (prop == NULL) 630bc752c9SSamuel Holland return -EINVAL; 640bc752c9SSamuel Holland min_volt = fdt32_to_cpu(*prop); 650bc752c9SSamuel Holland 660bc752c9SSamuel Holland prop = fdt_getprop(fdt, node, "regulator-max-microvolt", NULL); 670bc752c9SSamuel Holland if (prop == NULL) 680bc752c9SSamuel Holland return -EINVAL; 690bc752c9SSamuel Holland 700bc752c9SSamuel Holland if (fdt32_to_cpu(*prop) != min_volt) 710bc752c9SSamuel Holland return -EINVAL; 720bc752c9SSamuel Holland 730bc752c9SSamuel Holland return min_volt / 1000; 740bc752c9SSamuel Holland } 750bc752c9SSamuel Holland 760bc752c9SSamuel Holland static int setup_regulator(const void *fdt, int node, 770bc752c9SSamuel Holland const struct axp_regulator *reg) 780bc752c9SSamuel Holland { 790bc752c9SSamuel Holland uint8_t val; 800bc752c9SSamuel Holland int mvolt; 810bc752c9SSamuel Holland 820bc752c9SSamuel Holland mvolt = fdt_get_regulator_millivolt(fdt, node); 830bc752c9SSamuel Holland if (mvolt < reg->min_volt || mvolt > reg->max_volt) 840bc752c9SSamuel Holland return -EINVAL; 850bc752c9SSamuel Holland 860bc752c9SSamuel Holland val = (mvolt / reg->step) - (reg->min_volt / reg->step); 870bc752c9SSamuel Holland if (val > reg->split) 880bc752c9SSamuel Holland val = ((val - reg->split) / 2) + reg->split; 890bc752c9SSamuel Holland 900bc752c9SSamuel Holland axp_write(reg->volt_reg, val); 910bc752c9SSamuel Holland axp_setbits(reg->switch_reg, BIT(reg->switch_bit)); 920bc752c9SSamuel Holland 930bc752c9SSamuel Holland INFO("PMIC: %s voltage: %d.%03dV\n", reg->dt_name, 940bc752c9SSamuel Holland mvolt / 1000, mvolt % 1000); 950bc752c9SSamuel Holland 960bc752c9SSamuel Holland return 0; 970bc752c9SSamuel Holland } 980bc752c9SSamuel Holland 990bc752c9SSamuel Holland static bool should_enable_regulator(const void *fdt, int node) 1000bc752c9SSamuel Holland { 1010bc752c9SSamuel Holland if (fdt_getprop(fdt, node, "phandle", NULL) != NULL) 1020bc752c9SSamuel Holland return true; 1030bc752c9SSamuel Holland if (fdt_getprop(fdt, node, "regulator-always-on", NULL) != NULL) 1040bc752c9SSamuel Holland return true; 1050bc752c9SSamuel Holland return false; 1060bc752c9SSamuel Holland } 1070bc752c9SSamuel Holland 108*93fa305cSAndre Przywara static bool board_uses_usb0_host_mode(const void *fdt) 109*93fa305cSAndre Przywara { 110*93fa305cSAndre Przywara int node, length; 111*93fa305cSAndre Przywara const char *prop; 112*93fa305cSAndre Przywara 113*93fa305cSAndre Przywara node = fdt_node_offset_by_compatible(fdt, -1, 114*93fa305cSAndre Przywara "allwinner,sun8i-a33-musb"); 115*93fa305cSAndre Przywara if (node < 0) { 116*93fa305cSAndre Przywara return false; 117*93fa305cSAndre Przywara } 118*93fa305cSAndre Przywara 119*93fa305cSAndre Przywara prop = fdt_getprop(fdt, node, "dr_mode", &length); 120*93fa305cSAndre Przywara if (!prop) { 121*93fa305cSAndre Przywara return false; 122*93fa305cSAndre Przywara } 123*93fa305cSAndre Przywara 124*93fa305cSAndre Przywara return !strncmp(prop, "host", length); 125*93fa305cSAndre Przywara } 126*93fa305cSAndre Przywara 1270bc752c9SSamuel Holland void axp_setup_regulators(const void *fdt) 1280bc752c9SSamuel Holland { 1290bc752c9SSamuel Holland int node; 130f6d9c4caSSamuel Holland bool sw = false; 1310bc752c9SSamuel Holland 1320bc752c9SSamuel Holland if (fdt == NULL) 1330bc752c9SSamuel Holland return; 1340bc752c9SSamuel Holland 1350bc752c9SSamuel Holland /* locate the PMIC DT node, bail out if not found */ 1360bc752c9SSamuel Holland node = fdt_node_offset_by_compatible(fdt, -1, axp_compatible); 1370bc752c9SSamuel Holland if (node < 0) { 1380bc752c9SSamuel Holland WARN("PMIC: No PMIC DT node, skipping setup\n"); 1390bc752c9SSamuel Holland return; 1400bc752c9SSamuel Holland } 1410bc752c9SSamuel Holland 142f6d9c4caSSamuel Holland /* This applies to AXP803 only. */ 143*93fa305cSAndre Przywara if (fdt_getprop(fdt, node, "x-powers,drive-vbus-en", NULL) && 144*93fa305cSAndre Przywara board_uses_usb0_host_mode(fdt)) { 1450bc752c9SSamuel Holland axp_clrbits(0x8f, BIT(4)); 1460bc752c9SSamuel Holland axp_setbits(0x30, BIT(2)); 1470bc752c9SSamuel Holland INFO("PMIC: Enabling DRIVEVBUS\n"); 1480bc752c9SSamuel Holland } 1490bc752c9SSamuel Holland 1500bc752c9SSamuel Holland /* descend into the "regulators" subnode */ 1510bc752c9SSamuel Holland node = fdt_subnode_offset(fdt, node, "regulators"); 1520bc752c9SSamuel Holland if (node < 0) { 1530bc752c9SSamuel Holland WARN("PMIC: No regulators DT node, skipping setup\n"); 1540bc752c9SSamuel Holland return; 1550bc752c9SSamuel Holland } 1560bc752c9SSamuel Holland 1570bc752c9SSamuel Holland /* iterate over all regulators to find used ones */ 1580bc752c9SSamuel Holland fdt_for_each_subnode(node, fdt, node) { 1590bc752c9SSamuel Holland const struct axp_regulator *reg; 1600bc752c9SSamuel Holland const char *name; 1610bc752c9SSamuel Holland int length; 1620bc752c9SSamuel Holland 1630bc752c9SSamuel Holland /* We only care if it's always on or referenced. */ 1640bc752c9SSamuel Holland if (!should_enable_regulator(fdt, node)) 1650bc752c9SSamuel Holland continue; 1660bc752c9SSamuel Holland 1670bc752c9SSamuel Holland name = fdt_get_name(fdt, node, &length); 168f6d9c4caSSamuel Holland 169f6d9c4caSSamuel Holland /* Enable the switch last to avoid overheating. */ 170f6d9c4caSSamuel Holland if (!strncmp(name, "dc1sw", length) || 171f6d9c4caSSamuel Holland !strncmp(name, "sw", length)) { 172f6d9c4caSSamuel Holland sw = true; 173f6d9c4caSSamuel Holland continue; 174f6d9c4caSSamuel Holland } 175f6d9c4caSSamuel Holland 1760bc752c9SSamuel Holland for (reg = axp_regulators; reg->dt_name; reg++) { 1770bc752c9SSamuel Holland if (!strncmp(name, reg->dt_name, length)) { 1780bc752c9SSamuel Holland setup_regulator(fdt, node, reg); 1790bc752c9SSamuel Holland break; 1800bc752c9SSamuel Holland } 1810bc752c9SSamuel Holland } 1820bc752c9SSamuel Holland } 1830bc752c9SSamuel Holland 1840bc752c9SSamuel Holland /* 185f6d9c4caSSamuel Holland * On the AXP803, if DLDO2 is enabled after DC1SW, the PMIC overheats 186f6d9c4caSSamuel Holland * and shuts down. So always enable DC1SW as the very last regulator. 1870bc752c9SSamuel Holland */ 188f6d9c4caSSamuel Holland if (sw) { 189f6d9c4caSSamuel Holland INFO("PMIC: Enabling DC SW\n"); 190f6d9c4caSSamuel Holland if (axp_chip_id == AXP803_CHIP_ID) 1910bc752c9SSamuel Holland axp_setbits(0x12, BIT(7)); 192f6d9c4caSSamuel Holland if (axp_chip_id == AXP805_CHIP_ID) 193f6d9c4caSSamuel Holland axp_setbits(0x11, BIT(7)); 1940bc752c9SSamuel Holland } 1950bc752c9SSamuel Holland } 196