123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- /*
- * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
- #include <errno.h>
- #include <libfdt.h>
- #include <common/debug.h>
- #include <common/fdt_wrappers.h>
- #include <drivers/allwinner/axp.h>
- int axp_check_id(void)
- {
- int ret;
- ret = axp_read(0x03);
- if (ret < 0)
- return ret;
- ret &= 0xcf;
- if (ret != axp_chip_id) {
- ERROR("PMIC: Found unknown PMIC %02x\n", ret);
- return ret;
- }
- return 0;
- }
- int axp_clrsetbits(uint8_t reg, uint8_t clr_mask, uint8_t set_mask)
- {
- uint8_t val;
- int ret;
- ret = axp_read(reg);
- if (ret < 0)
- return ret;
- val = (ret & ~clr_mask) | set_mask;
- return axp_write(reg, val);
- }
- void axp_power_off(void)
- {
- /* Set "power disable control" bit */
- axp_setbits(0x32, BIT(7));
- }
- #if SUNXI_SETUP_REGULATORS == 1
- /*
- * 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;
- }
- static int setup_regulator(const void *fdt, int node,
- const struct axp_regulator *reg)
- {
- uint8_t val;
- int mvolt;
- mvolt = fdt_get_regulator_millivolt(fdt, node);
- if (mvolt < reg->min_volt || mvolt > reg->max_volt)
- return -EINVAL;
- val = (mvolt / reg->step) - (reg->min_volt / reg->step);
- if (val > reg->split)
- val = ((val - reg->split) / 2) + reg->split;
- axp_write(reg->volt_reg, val);
- axp_setbits(reg->switch_reg, BIT(reg->switch_bit));
- INFO("PMIC: %s voltage: %d.%03dV\n", reg->dt_name,
- mvolt / 1000, mvolt % 1000);
- return 0;
- }
- static bool should_enable_regulator(const void *fdt, int node)
- {
- if (!fdt_node_is_enabled(fdt, node)) {
- return false;
- }
- if (fdt_getprop(fdt, node, "phandle", NULL) != NULL) {
- return true;
- }
- if (fdt_getprop(fdt, node, "regulator-always-on", NULL) != NULL) {
- return true;
- }
- return false;
- }
- static bool board_uses_usb0_host_mode(const void *fdt)
- {
- int node, length;
- const char *prop;
- node = fdt_node_offset_by_compatible(fdt, -1,
- "allwinner,sun8i-a33-musb");
- if (node < 0) {
- return false;
- }
- prop = fdt_getprop(fdt, node, "dr_mode", &length);
- if (!prop) {
- return false;
- }
- return !strncmp(prop, "host", length);
- }
- void axp_setup_regulators(const void *fdt)
- {
- int node;
- bool sw = false;
- if (fdt == NULL)
- return;
- /* locate the PMIC DT node, bail out if not found */
- node = fdt_node_offset_by_compatible(fdt, -1, axp_compatible);
- if (node < 0) {
- WARN("PMIC: No PMIC DT node, skipping setup\n");
- return;
- }
- /* This applies to AXP803 only. */
- if (fdt_getprop(fdt, node, "x-powers,drive-vbus-en", NULL) &&
- board_uses_usb0_host_mode(fdt)) {
- axp_clrbits(0x8f, BIT(4));
- axp_setbits(0x30, BIT(2));
- INFO("PMIC: Enabling DRIVEVBUS\n");
- }
- /* descend into the "regulators" subnode */
- node = fdt_subnode_offset(fdt, node, "regulators");
- if (node < 0) {
- WARN("PMIC: No regulators DT node, skipping setup\n");
- return;
- }
- /* iterate over all regulators to find used ones */
- fdt_for_each_subnode(node, fdt, node) {
- const struct axp_regulator *reg;
- const char *name;
- int length;
- /* We only care if it's always on or referenced. */
- if (!should_enable_regulator(fdt, node))
- continue;
- name = fdt_get_name(fdt, node, &length);
- /* Enable the switch last to avoid overheating. */
- if (!strncmp(name, "dc1sw", length) ||
- !strncmp(name, "sw", length)) {
- sw = true;
- continue;
- }
- for (reg = axp_regulators; reg->dt_name; reg++) {
- if (!strncmp(name, reg->dt_name, length)) {
- setup_regulator(fdt, node, reg);
- break;
- }
- }
- }
- /*
- * On the AXP803, if DLDO2 is enabled after DC1SW, the PMIC overheats
- * and shuts down. So always enable DC1SW as the very last regulator.
- */
- if (sw) {
- INFO("PMIC: Enabling DC SW\n");
- if (axp_chip_id == AXP803_CHIP_ID)
- axp_setbits(0x12, BIT(7));
- if (axp_chip_id == AXP805_CHIP_ID)
- axp_setbits(0x11, BIT(7));
- }
- }
- #endif /* SUNXI_SETUP_REGULATORS */
|