mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-21 01:18:52 +09:00
![Olliver Schinagl](/assets/img/avatar_default.png)
Some boards feature a capacitance on LDO3's output that is too large,
causing inrush currents which as a result, shut down the AXP209. This
has been reported before, without knowing the actual cause.
A fix appeared to be done with
commit 0e6e34ac8d
("sunxi: Olimex A20 boards: Enable LDO3 and LDO4 regulators").
The description there is a bit misleading, the kernel does not hang
during AXP209 initialization, the PMIC shuts down, causing voltages to
drop and thus the whole system freezes.
While the AXP209 does have the ability to ramp up the voltage slowly, to
reduce these inrush currents, the voltage rate control (VRC) however is
not applicable when switching on the LDO3 output. Only when going from
an enabled lower voltage setting, to a higher voltage setting is the VRC
in effect.
To work around this problem, we set LDO3 to the lowest possible setting
of 0.7 V if it was not yet enabled, and then let the VRC (if enabled) do
its thing. It should be noted, that for some undocumented reason, there
is a short delay needed between setting the LDO3 voltage register and
enabling the power. One would expect that this delay ought to be just
after enabling the output power at 0.7 V, but this did not work.
Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
Signed-off-by: Priit Laes <plaes@plaes.org>
Acked-by: Maxime Ripard <maxime.ripard@bootlin.com>
242 lines
5.3 KiB
C
242 lines
5.3 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* (C) Copyright 2012
|
|
* Henrik Nordstrom <henrik@henriknordstrom.net>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <command.h>
|
|
#include <asm/arch/pmic_bus.h>
|
|
#include <axp_pmic.h>
|
|
|
|
#ifdef CONFIG_AXP_ALDO3_VOLT_SLOPE_08
|
|
# define AXP209_VRC_SLOPE AXP209_VRC_LDO3_800uV_uS
|
|
#endif
|
|
#ifdef CONFIG_AXP_ALDO3_VOLT_SLOPE_16
|
|
# define AXP209_VRC_SLOPE AXP209_VRC_LDO3_1600uV_uS
|
|
#endif
|
|
#if defined CONFIG_AXP_ALDO3_VOLT_SLOPE_NONE || !defined AXP209_VRC_SLOPE
|
|
# define AXP209_VRC_SLOPE 0x00
|
|
#endif
|
|
|
|
static u8 axp209_mvolt_to_cfg(int mvolt, int min, int max, int div)
|
|
{
|
|
if (mvolt < min)
|
|
mvolt = min;
|
|
else if (mvolt > max)
|
|
mvolt = max;
|
|
|
|
return (mvolt - min) / div;
|
|
}
|
|
|
|
int axp_set_dcdc2(unsigned int mvolt)
|
|
{
|
|
int rc;
|
|
u8 cfg, current;
|
|
|
|
if (mvolt == 0)
|
|
return pmic_bus_clrbits(AXP209_OUTPUT_CTRL,
|
|
AXP209_OUTPUT_CTRL_DCDC2);
|
|
|
|
rc = pmic_bus_setbits(AXP209_OUTPUT_CTRL, AXP209_OUTPUT_CTRL_DCDC2);
|
|
if (rc)
|
|
return rc;
|
|
|
|
cfg = axp209_mvolt_to_cfg(mvolt, 700, 2275, 25);
|
|
|
|
/* Do we really need to be this gentle? It has built-in voltage slope */
|
|
while ((rc = pmic_bus_read(AXP209_DCDC2_VOLTAGE, ¤t)) == 0 &&
|
|
current != cfg) {
|
|
if (current < cfg)
|
|
current++;
|
|
else
|
|
current--;
|
|
|
|
rc = pmic_bus_write(AXP209_DCDC2_VOLTAGE, current);
|
|
if (rc)
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int axp_set_dcdc3(unsigned int mvolt)
|
|
{
|
|
u8 cfg = axp209_mvolt_to_cfg(mvolt, 700, 3500, 25);
|
|
int rc;
|
|
|
|
if (mvolt == 0)
|
|
return pmic_bus_clrbits(AXP209_OUTPUT_CTRL,
|
|
AXP209_OUTPUT_CTRL_DCDC3);
|
|
|
|
rc = pmic_bus_write(AXP209_DCDC3_VOLTAGE, cfg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
return pmic_bus_setbits(AXP209_OUTPUT_CTRL, AXP209_OUTPUT_CTRL_DCDC3);
|
|
}
|
|
|
|
int axp_set_aldo2(unsigned int mvolt)
|
|
{
|
|
int rc;
|
|
u8 cfg, reg;
|
|
|
|
if (mvolt == 0)
|
|
return pmic_bus_clrbits(AXP209_OUTPUT_CTRL,
|
|
AXP209_OUTPUT_CTRL_LDO2);
|
|
|
|
cfg = axp209_mvolt_to_cfg(mvolt, 1800, 3300, 100);
|
|
|
|
rc = pmic_bus_read(AXP209_LDO24_VOLTAGE, ®);
|
|
if (rc)
|
|
return rc;
|
|
|
|
reg |= AXP209_LDO24_LDO2_SET(reg, cfg);
|
|
rc = pmic_bus_write(AXP209_LDO24_VOLTAGE, reg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
return pmic_bus_setbits(AXP209_OUTPUT_CTRL, AXP209_OUTPUT_CTRL_LDO2);
|
|
}
|
|
|
|
int axp_set_aldo3(unsigned int mvolt)
|
|
{
|
|
u8 cfg;
|
|
int rc;
|
|
|
|
if (mvolt == 0)
|
|
return pmic_bus_clrbits(AXP209_OUTPUT_CTRL,
|
|
AXP209_OUTPUT_CTRL_LDO3);
|
|
|
|
/*
|
|
* Some boards have trouble reaching the target voltage without causing
|
|
* great inrush currents. To prevent this, boards can enable a certain
|
|
* slope to ramp up voltage. Note, this only works when changing an
|
|
* already active power rail. When toggling power on, the AXP ramps up
|
|
* steeply at 0.0167 V/uS.
|
|
*/
|
|
rc = pmic_bus_read(AXP209_VRC_DCDC2_LDO3, &cfg);
|
|
cfg = AXP209_VRC_LDO3_SLOPE_SET(cfg, AXP209_VRC_SLOPE);
|
|
rc |= pmic_bus_write(AXP209_VRC_DCDC2_LDO3, cfg);
|
|
|
|
if (rc)
|
|
return rc;
|
|
|
|
#ifdef CONFIG_AXP_ALDO3_INRUSH_QUIRK
|
|
/*
|
|
* On some boards, LDO3 has a too big capacitor installed. When
|
|
* turning on LDO3, this causes the AXP209 to shutdown on
|
|
* voltages over 1.9 volt. As a workaround, we enable LDO3
|
|
* first with the lowest possible voltage. If this still causes
|
|
* high inrush currents, the voltage slope should be increased.
|
|
*/
|
|
rc = pmic_bus_read(AXP209_OUTPUT_CTRL, &cfg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
if (!(cfg & AXP209_OUTPUT_CTRL_LDO3)) {
|
|
rc = pmic_bus_write(AXP209_LDO3_VOLTAGE, 0x0); /* 0.7 Volt */
|
|
mdelay(1);
|
|
rc |= pmic_bus_setbits(AXP209_OUTPUT_CTRL,
|
|
AXP209_OUTPUT_CTRL_LDO3);
|
|
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
#endif
|
|
|
|
if (mvolt == -1) {
|
|
cfg = AXP209_LDO3_VOLTAGE_FROM_LDO3IN;
|
|
} else {
|
|
cfg = axp209_mvolt_to_cfg(mvolt, 700, 3500, 25);
|
|
cfg = AXP209_LDO3_VOLTAGE_SET(cfg);
|
|
}
|
|
|
|
rc = pmic_bus_write(AXP209_LDO3_VOLTAGE, cfg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
return pmic_bus_setbits(AXP209_OUTPUT_CTRL, AXP209_OUTPUT_CTRL_LDO3);
|
|
}
|
|
|
|
int axp_set_aldo4(unsigned int mvolt)
|
|
{
|
|
int rc;
|
|
static const unsigned int vindex[] = {
|
|
1250, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 2000, 2500,
|
|
2700, 2800, 3000, 3100, 3200, 3300
|
|
};
|
|
u8 cfg, reg;
|
|
|
|
if (mvolt == 0)
|
|
return pmic_bus_clrbits(AXP209_OUTPUT_CTRL,
|
|
AXP209_OUTPUT_CTRL_LDO4);
|
|
|
|
/* Translate mvolt to register cfg value, requested <= selected */
|
|
for (cfg = 15; vindex[cfg] > mvolt && cfg > 0; cfg--);
|
|
|
|
rc = pmic_bus_read(AXP209_LDO24_VOLTAGE, ®);
|
|
if (rc)
|
|
return rc;
|
|
|
|
reg |= AXP209_LDO24_LDO4_SET(reg, cfg);
|
|
rc = pmic_bus_write(AXP209_LDO24_VOLTAGE, reg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
return pmic_bus_setbits(AXP209_OUTPUT_CTRL, AXP209_OUTPUT_CTRL_LDO4);
|
|
}
|
|
|
|
int axp_init(void)
|
|
{
|
|
u8 ver;
|
|
int i, rc;
|
|
|
|
rc = pmic_bus_init();
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = pmic_bus_read(AXP209_CHIP_VERSION, &ver);
|
|
if (rc)
|
|
return rc;
|
|
|
|
if ((ver & AXP209_CHIP_VERSION_MASK) != 0x1)
|
|
return -EINVAL;
|
|
|
|
/* Mask all interrupts */
|
|
for (i = AXP209_IRQ_ENABLE1; i <= AXP209_IRQ_ENABLE5; i++) {
|
|
rc = pmic_bus_write(i, 0);
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Turn off LDOIO regulators / tri-state GPIO pins, when rebooting
|
|
* from android these are sometimes on.
|
|
*/
|
|
rc = pmic_bus_write(AXP_GPIO0_CTRL, AXP_GPIO_CTRL_INPUT);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = pmic_bus_write(AXP_GPIO1_CTRL, AXP_GPIO_CTRL_INPUT);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = pmic_bus_write(AXP_GPIO2_CTRL, AXP_GPIO_CTRL_INPUT);
|
|
if (rc)
|
|
return rc;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int do_poweroff(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
pmic_bus_write(AXP209_SHUTDOWN, AXP209_POWEROFF);
|
|
|
|
/* infinite loop during shutdown */
|
|
while (1) {}
|
|
|
|
/* not reached */
|
|
return 0;
|
|
}
|