1*0bc752c9SSamuel Holland /* 2*0bc752c9SSamuel Holland * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. 3*0bc752c9SSamuel Holland * 4*0bc752c9SSamuel Holland * SPDX-License-Identifier: BSD-3-Clause 5*0bc752c9SSamuel Holland */ 6*0bc752c9SSamuel Holland 7*0bc752c9SSamuel Holland #include <errno.h> 8*0bc752c9SSamuel Holland 9*0bc752c9SSamuel Holland #include <libfdt.h> 10*0bc752c9SSamuel Holland 11*0bc752c9SSamuel Holland #include <common/debug.h> 12*0bc752c9SSamuel Holland #include <drivers/allwinner/axp.h> 13*0bc752c9SSamuel Holland 14*0bc752c9SSamuel Holland int axp_check_id(void) 15*0bc752c9SSamuel Holland { 16*0bc752c9SSamuel Holland int ret; 17*0bc752c9SSamuel Holland 18*0bc752c9SSamuel Holland ret = axp_read(0x03); 19*0bc752c9SSamuel Holland if (ret < 0) 20*0bc752c9SSamuel Holland return ret; 21*0bc752c9SSamuel Holland 22*0bc752c9SSamuel Holland ret &= 0xcf; 23*0bc752c9SSamuel Holland if (ret != axp_chip_id) { 24*0bc752c9SSamuel Holland ERROR("PMIC: Found unknown PMIC %02x\n", ret); 25*0bc752c9SSamuel Holland return ret; 26*0bc752c9SSamuel Holland } 27*0bc752c9SSamuel Holland 28*0bc752c9SSamuel Holland return 0; 29*0bc752c9SSamuel Holland } 30*0bc752c9SSamuel Holland 31*0bc752c9SSamuel Holland int axp_clrsetbits(uint8_t reg, uint8_t clr_mask, uint8_t set_mask) 32*0bc752c9SSamuel Holland { 33*0bc752c9SSamuel Holland uint8_t val; 34*0bc752c9SSamuel Holland int ret; 35*0bc752c9SSamuel Holland 36*0bc752c9SSamuel Holland ret = axp_read(reg); 37*0bc752c9SSamuel Holland if (ret < 0) 38*0bc752c9SSamuel Holland return ret; 39*0bc752c9SSamuel Holland 40*0bc752c9SSamuel Holland val = (ret & ~clr_mask) | set_mask; 41*0bc752c9SSamuel Holland 42*0bc752c9SSamuel Holland return axp_write(reg, val); 43*0bc752c9SSamuel Holland } 44*0bc752c9SSamuel Holland 45*0bc752c9SSamuel Holland void axp_power_off(void) 46*0bc752c9SSamuel Holland { 47*0bc752c9SSamuel Holland /* Set "power disable control" bit */ 48*0bc752c9SSamuel Holland axp_setbits(0x32, BIT(7)); 49*0bc752c9SSamuel Holland } 50*0bc752c9SSamuel Holland 51*0bc752c9SSamuel Holland /* 52*0bc752c9SSamuel Holland * Retrieve the voltage from a given regulator DTB node. 53*0bc752c9SSamuel Holland * Both the regulator-{min,max}-microvolt properties must be present and 54*0bc752c9SSamuel Holland * have the same value. Return that value in millivolts. 55*0bc752c9SSamuel Holland */ 56*0bc752c9SSamuel Holland static int fdt_get_regulator_millivolt(const void *fdt, int node) 57*0bc752c9SSamuel Holland { 58*0bc752c9SSamuel Holland const fdt32_t *prop; 59*0bc752c9SSamuel Holland uint32_t min_volt; 60*0bc752c9SSamuel Holland 61*0bc752c9SSamuel Holland prop = fdt_getprop(fdt, node, "regulator-min-microvolt", NULL); 62*0bc752c9SSamuel Holland if (prop == NULL) 63*0bc752c9SSamuel Holland return -EINVAL; 64*0bc752c9SSamuel Holland min_volt = fdt32_to_cpu(*prop); 65*0bc752c9SSamuel Holland 66*0bc752c9SSamuel Holland prop = fdt_getprop(fdt, node, "regulator-max-microvolt", NULL); 67*0bc752c9SSamuel Holland if (prop == NULL) 68*0bc752c9SSamuel Holland return -EINVAL; 69*0bc752c9SSamuel Holland 70*0bc752c9SSamuel Holland if (fdt32_to_cpu(*prop) != min_volt) 71*0bc752c9SSamuel Holland return -EINVAL; 72*0bc752c9SSamuel Holland 73*0bc752c9SSamuel Holland return min_volt / 1000; 74*0bc752c9SSamuel Holland } 75*0bc752c9SSamuel Holland 76*0bc752c9SSamuel Holland static int setup_regulator(const void *fdt, int node, 77*0bc752c9SSamuel Holland const struct axp_regulator *reg) 78*0bc752c9SSamuel Holland { 79*0bc752c9SSamuel Holland uint8_t val; 80*0bc752c9SSamuel Holland int mvolt; 81*0bc752c9SSamuel Holland 82*0bc752c9SSamuel Holland mvolt = fdt_get_regulator_millivolt(fdt, node); 83*0bc752c9SSamuel Holland if (mvolt < reg->min_volt || mvolt > reg->max_volt) 84*0bc752c9SSamuel Holland return -EINVAL; 85*0bc752c9SSamuel Holland 86*0bc752c9SSamuel Holland val = (mvolt / reg->step) - (reg->min_volt / reg->step); 87*0bc752c9SSamuel Holland if (val > reg->split) 88*0bc752c9SSamuel Holland val = ((val - reg->split) / 2) + reg->split; 89*0bc752c9SSamuel Holland 90*0bc752c9SSamuel Holland axp_write(reg->volt_reg, val); 91*0bc752c9SSamuel Holland axp_setbits(reg->switch_reg, BIT(reg->switch_bit)); 92*0bc752c9SSamuel Holland 93*0bc752c9SSamuel Holland INFO("PMIC: %s voltage: %d.%03dV\n", reg->dt_name, 94*0bc752c9SSamuel Holland mvolt / 1000, mvolt % 1000); 95*0bc752c9SSamuel Holland 96*0bc752c9SSamuel Holland return 0; 97*0bc752c9SSamuel Holland } 98*0bc752c9SSamuel Holland 99*0bc752c9SSamuel Holland static bool should_enable_regulator(const void *fdt, int node) 100*0bc752c9SSamuel Holland { 101*0bc752c9SSamuel Holland if (fdt_getprop(fdt, node, "phandle", NULL) != NULL) 102*0bc752c9SSamuel Holland return true; 103*0bc752c9SSamuel Holland if (fdt_getprop(fdt, node, "regulator-always-on", NULL) != NULL) 104*0bc752c9SSamuel Holland return true; 105*0bc752c9SSamuel Holland return false; 106*0bc752c9SSamuel Holland } 107*0bc752c9SSamuel Holland 108*0bc752c9SSamuel Holland void axp_setup_regulators(const void *fdt) 109*0bc752c9SSamuel Holland { 110*0bc752c9SSamuel Holland int node; 111*0bc752c9SSamuel Holland bool dc1sw = false; 112*0bc752c9SSamuel Holland 113*0bc752c9SSamuel Holland if (fdt == NULL) 114*0bc752c9SSamuel Holland return; 115*0bc752c9SSamuel Holland 116*0bc752c9SSamuel Holland /* locate the PMIC DT node, bail out if not found */ 117*0bc752c9SSamuel Holland node = fdt_node_offset_by_compatible(fdt, -1, axp_compatible); 118*0bc752c9SSamuel Holland if (node < 0) { 119*0bc752c9SSamuel Holland WARN("PMIC: No PMIC DT node, skipping setup\n"); 120*0bc752c9SSamuel Holland return; 121*0bc752c9SSamuel Holland } 122*0bc752c9SSamuel Holland 123*0bc752c9SSamuel Holland if (fdt_getprop(fdt, node, "x-powers,drive-vbus-en", NULL)) { 124*0bc752c9SSamuel Holland axp_clrbits(0x8f, BIT(4)); 125*0bc752c9SSamuel Holland axp_setbits(0x30, BIT(2)); 126*0bc752c9SSamuel Holland INFO("PMIC: Enabling DRIVEVBUS\n"); 127*0bc752c9SSamuel Holland } 128*0bc752c9SSamuel Holland 129*0bc752c9SSamuel Holland /* descend into the "regulators" subnode */ 130*0bc752c9SSamuel Holland node = fdt_subnode_offset(fdt, node, "regulators"); 131*0bc752c9SSamuel Holland if (node < 0) { 132*0bc752c9SSamuel Holland WARN("PMIC: No regulators DT node, skipping setup\n"); 133*0bc752c9SSamuel Holland return; 134*0bc752c9SSamuel Holland } 135*0bc752c9SSamuel Holland 136*0bc752c9SSamuel Holland /* iterate over all regulators to find used ones */ 137*0bc752c9SSamuel Holland fdt_for_each_subnode(node, fdt, node) { 138*0bc752c9SSamuel Holland const struct axp_regulator *reg; 139*0bc752c9SSamuel Holland const char *name; 140*0bc752c9SSamuel Holland int length; 141*0bc752c9SSamuel Holland 142*0bc752c9SSamuel Holland /* We only care if it's always on or referenced. */ 143*0bc752c9SSamuel Holland if (!should_enable_regulator(fdt, node)) 144*0bc752c9SSamuel Holland continue; 145*0bc752c9SSamuel Holland 146*0bc752c9SSamuel Holland name = fdt_get_name(fdt, node, &length); 147*0bc752c9SSamuel Holland for (reg = axp_regulators; reg->dt_name; reg++) { 148*0bc752c9SSamuel Holland if (!strncmp(name, reg->dt_name, length)) { 149*0bc752c9SSamuel Holland setup_regulator(fdt, node, reg); 150*0bc752c9SSamuel Holland break; 151*0bc752c9SSamuel Holland } 152*0bc752c9SSamuel Holland } 153*0bc752c9SSamuel Holland 154*0bc752c9SSamuel Holland if (!strncmp(name, "dc1sw", length)) { 155*0bc752c9SSamuel Holland /* Delay DC1SW enablement to avoid overheating. */ 156*0bc752c9SSamuel Holland dc1sw = true; 157*0bc752c9SSamuel Holland continue; 158*0bc752c9SSamuel Holland } 159*0bc752c9SSamuel Holland } 160*0bc752c9SSamuel Holland 161*0bc752c9SSamuel Holland /* 162*0bc752c9SSamuel Holland * If DLDO2 is enabled after DC1SW, the PMIC overheats and shuts 163*0bc752c9SSamuel Holland * down. So always enable DC1SW as the very last regulator. 164*0bc752c9SSamuel Holland */ 165*0bc752c9SSamuel Holland if (dc1sw) { 166*0bc752c9SSamuel Holland INFO("PMIC: Enabling DC1SW\n"); 167*0bc752c9SSamuel Holland axp_setbits(0x12, BIT(7)); 168*0bc752c9SSamuel Holland } 169*0bc752c9SSamuel Holland } 170