diff mbox series

power: axp: sunxi: configure charge current

Message ID 20241127130419.50489-1-mslusarz@renau.com
State New
Delegated to: Andre Przywara
Headers show
Series power: axp: sunxi: configure charge current | expand

Commit Message

Marcin Ślusarz Nov. 27, 2024, 1:04 p.m. UTC
Allows decreasing initial charge current, e.g. when power
supply is too weak to support both charging and other operations.

Signed-off-by: Marcin Ślusarz <mslusarz@renau.com>
Cc: Jagan Teki <jagan@amarulasolutions.com>
Cc: Andre Przywara <andre.przywara@arm.com>
---
 board/sunxi/board.c    |  4 ++++
 drivers/power/Kconfig  |  7 +++++++
 drivers/power/axp221.c | 33 +++++++++++++++++++++++++++++++++
 include/axp221.h       |  3 +++
 include/axp_pmic.h     |  1 +
 5 files changed, 48 insertions(+)

Comments

Andre Przywara Dec. 16, 2024, 1:40 p.m. UTC | #1
On Wed, 27 Nov 2024 13:04:19 +0000
"Marcin Ślusarz" <marcin.slusarz@gmail.com> wrote:

Hi Marcin,

thanks for taking care of sending a mainline patch!

> Allows decreasing initial charge current, e.g. when power
> supply is too weak to support both charging and other operations.

Can you explain a bit more why and where you actually need that?

The problem I have with the code is that it's doing it in the SPL, where I
actually want to decrease any PMIC usage. We really only need the PMIC that
early to set the DRAM and CPU core voltage, everything else should be done
in either U-Boot proper or the kernel.

So how did this work before, and why it is a problem now? AXP221 points to
some older SoC, I guess?
Is it that you find yourself with a somewhat weak power supply, and if the
battery is discharged, there won't be enough power left for the system to
boot reliably?

Do you know of any existing DT property for instance that could limit the
charge current? Or is there a sysfs property only?

Cheers,
Andre

> Signed-off-by: Marcin Ślusarz <mslusarz@renau.com>
> Cc: Jagan Teki <jagan@amarulasolutions.com>
> Cc: Andre Przywara <andre.przywara@arm.com>
> ---
>  board/sunxi/board.c    |  4 ++++
>  drivers/power/Kconfig  |  7 +++++++
>  drivers/power/axp221.c | 33 +++++++++++++++++++++++++++++++++
>  include/axp221.h       |  3 +++
>  include/axp_pmic.h     |  1 +
>  5 files changed, 48 insertions(+)
> 
> diff --git a/board/sunxi/board.c b/board/sunxi/board.c
> index 824c322a0d..07fccf972a 100644
> --- a/board/sunxi/board.c
> +++ b/board/sunxi/board.c
> @@ -623,6 +623,10 @@ void sunxi_board_init(void)
>  #if defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER
>  	power_failed |= axp_set_sw(IS_ENABLED(CONFIG_AXP_SW_ON));
>  #endif
> +
> +#ifdef CONFIG_AXP221_POWER
> +	power_failed |= axp221_set_charge_current(CONFIG_AXP221_CHARGE_CURRENT);
> +#endif
>  #endif	/* CONFIG_AXPxxx_POWER */
>  	printf("DRAM:");
>  	gd->ram_size = sunxi_dram_init();
> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
> index 4b81aeb749..06b509a383 100644
> --- a/drivers/power/Kconfig
> +++ b/drivers/power/Kconfig
> @@ -142,6 +142,13 @@ config SY8106A_POWER
>  
>  endchoice
>  
> +config AXP221_CHARGE_CURRENT
> +	int "axp221 constant charge current in mA"
> +	depends on AXP221_POWER
> +	default 1200
> +	---help---
> +	Any value between 300 and 2100 in 150 increments, or 0 to disable charging.
> +
>  config AXP_DCDC1_VOLT
>  	int "axp pmic dcdc1 voltage"
>  	depends on AXP221_POWER || AXP809_POWER || AXP818_POWER
> diff --git a/drivers/power/axp221.c b/drivers/power/axp221.c
> index c22ca03f46..bf48585da0 100644
> --- a/drivers/power/axp221.c
> +++ b/drivers/power/axp221.c
> @@ -205,6 +205,39 @@ int axp_set_eldo(int eldo_num, unsigned int mvolt)
>  				AXP221_OUTPUT_CTRL2_ELDO1_EN << (eldo_num - 1));
>  }
>  
> +static u8 axp221_mamp_to_cfg(int mamp, int min, int max, int div)
> +{
> +	if (mamp < min)
> +		mamp = min;
> +	else if (mamp > max)
> +		mamp = max;
> +
> +	return (mamp - min) / div;
> +}
> +
> +int axp221_set_charge_current(unsigned int mamp)
> +{
> +	int ret;
> +	u8 cfg, chrg_ctrl;
> +
> +	cfg = axp221_mamp_to_cfg(mamp, 300, 2100, 150);
> +	if (cfg > 0x0f)
> +		return -EINVAL;
> +
> +	ret = pmic_bus_read(AXP221_CHRG_CTRL1, &chrg_ctrl);
> +	if (ret)
> +		return ret;
> +
> +	chrg_ctrl = (chrg_ctrl & 0xf0) | cfg;
> +
> +	if (mamp == 0)
> +		chrg_ctrl &= 0x7f;
> +	else
> +		chrg_ctrl |= 0x80;
> +
> +	return pmic_bus_write(AXP221_CHRG_CTRL1, chrg_ctrl);
> +}
> +
>  int axp_init(void)
>  {
>  	u8 axp_chip_id;
> diff --git a/include/axp221.h b/include/axp221.h
> index 32b988f3a9..8eb1792435 100644
> --- a/include/axp221.h
> +++ b/include/axp221.h
> @@ -44,6 +44,9 @@
>  #define AXP221_ALDO3_CTRL	0x2a
>  #define AXP221_SHUTDOWN		0x32
>  #define AXP221_SHUTDOWN_POWEROFF	(1 << 7)
> +#define AXP221_CHRG_CTRL1	0x33
> +#define AXP221_CHRG_CTRL2	0x34
> +#define AXP221_CHRG_CTRL3	0x35
>  #define AXP221_PAGE		0xff
>  
>  /* Page 1 addresses */
> diff --git a/include/axp_pmic.h b/include/axp_pmic.h
> index ae62ef0d76..3bcd8f9ca9 100644
> --- a/include/axp_pmic.h
> +++ b/include/axp_pmic.h
> @@ -52,6 +52,7 @@ int axp_set_aldo4(unsigned int mvolt);
>  int axp_set_dldo(int dldo_num, unsigned int mvolt);
>  int axp_set_eldo(int eldo_num, unsigned int mvolt);
>  int axp_set_fldo(int fldo_num, unsigned int mvolt);
> +int axp221_set_charge_current(unsigned int mamp);
>  int axp_set_sw(bool on);
>  int axp_init(void);
>  int axp_get_sid(unsigned int *sid);
Marcin Ślusarz Dec. 16, 2024, 2:54 p.m. UTC | #2
pon., 16 gru 2024 o 14:40 Andre Przywara <andre.przywara@arm.com> napisał(a):
>
> On Wed, 27 Nov 2024 13:04:19 +0000
> "Marcin Ślusarz" <marcin.slusarz@gmail.com> wrote:
>
> Hi Marcin,
>
> thanks for taking care of sending a mainline patch!
>
> > Allows decreasing initial charge current, e.g. when power
> > supply is too weak to support both charging and other operations.
>
> Can you explain a bit more why and where you actually need that?

One of the boards I worked on had a design flaw where the power supply
was incorrectly sized. Booting that device on AC with an almost empty
battery led to weird behavior of some connected devices because
the board was starved by battery charging. The design bug was fixed, but
old boards exist and need to work...

> The problem I have with the code is that it's doing it in the SPL, where I
> actually want to decrease any PMIC usage. We really only need the PMIC that
> early to set the DRAM and CPU core voltage, everything else should be done
> in either U-Boot proper or the kernel.

The problem with disabling (or decreasing) charging later is that it
might be too late.
While I never tracked booting failures to that problem, it's
theoretically possible.

> So how did this work before, and why it is a problem now? AXP221 points to
> some older SoC, I guess?

It's an older SoC, but it's still used to develop new products :/.

> Is it that you find yourself with a somewhat weak power supply, and if the
> battery is discharged, there won't be enough power left for the system to
> boot reliably?
>
> Do you know of any existing DT property for instance that could limit the
> charge current? Or is there a sysfs property only?

It's possible to limit charging via DT and tweak it after booting with
sysfs, but
in the kernel version I had to deal with, it was not possible to disable
charging completely. And as I said, I fear it might be too late to do
it after bootup.

With all that said, there's no big need to merge this patch upstream. It would
lower the effort to update to a newer UBoot, and I thought it's generic enough
that someone else could use it.

Cheers,
Marcin
diff mbox series

Patch

diff --git a/board/sunxi/board.c b/board/sunxi/board.c
index 824c322a0d..07fccf972a 100644
--- a/board/sunxi/board.c
+++ b/board/sunxi/board.c
@@ -623,6 +623,10 @@  void sunxi_board_init(void)
 #if defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER
 	power_failed |= axp_set_sw(IS_ENABLED(CONFIG_AXP_SW_ON));
 #endif
+
+#ifdef CONFIG_AXP221_POWER
+	power_failed |= axp221_set_charge_current(CONFIG_AXP221_CHARGE_CURRENT);
+#endif
 #endif	/* CONFIG_AXPxxx_POWER */
 	printf("DRAM:");
 	gd->ram_size = sunxi_dram_init();
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 4b81aeb749..06b509a383 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -142,6 +142,13 @@  config SY8106A_POWER
 
 endchoice
 
+config AXP221_CHARGE_CURRENT
+	int "axp221 constant charge current in mA"
+	depends on AXP221_POWER
+	default 1200
+	---help---
+	Any value between 300 and 2100 in 150 increments, or 0 to disable charging.
+
 config AXP_DCDC1_VOLT
 	int "axp pmic dcdc1 voltage"
 	depends on AXP221_POWER || AXP809_POWER || AXP818_POWER
diff --git a/drivers/power/axp221.c b/drivers/power/axp221.c
index c22ca03f46..bf48585da0 100644
--- a/drivers/power/axp221.c
+++ b/drivers/power/axp221.c
@@ -205,6 +205,39 @@  int axp_set_eldo(int eldo_num, unsigned int mvolt)
 				AXP221_OUTPUT_CTRL2_ELDO1_EN << (eldo_num - 1));
 }
 
+static u8 axp221_mamp_to_cfg(int mamp, int min, int max, int div)
+{
+	if (mamp < min)
+		mamp = min;
+	else if (mamp > max)
+		mamp = max;
+
+	return (mamp - min) / div;
+}
+
+int axp221_set_charge_current(unsigned int mamp)
+{
+	int ret;
+	u8 cfg, chrg_ctrl;
+
+	cfg = axp221_mamp_to_cfg(mamp, 300, 2100, 150);
+	if (cfg > 0x0f)
+		return -EINVAL;
+
+	ret = pmic_bus_read(AXP221_CHRG_CTRL1, &chrg_ctrl);
+	if (ret)
+		return ret;
+
+	chrg_ctrl = (chrg_ctrl & 0xf0) | cfg;
+
+	if (mamp == 0)
+		chrg_ctrl &= 0x7f;
+	else
+		chrg_ctrl |= 0x80;
+
+	return pmic_bus_write(AXP221_CHRG_CTRL1, chrg_ctrl);
+}
+
 int axp_init(void)
 {
 	u8 axp_chip_id;
diff --git a/include/axp221.h b/include/axp221.h
index 32b988f3a9..8eb1792435 100644
--- a/include/axp221.h
+++ b/include/axp221.h
@@ -44,6 +44,9 @@ 
 #define AXP221_ALDO3_CTRL	0x2a
 #define AXP221_SHUTDOWN		0x32
 #define AXP221_SHUTDOWN_POWEROFF	(1 << 7)
+#define AXP221_CHRG_CTRL1	0x33
+#define AXP221_CHRG_CTRL2	0x34
+#define AXP221_CHRG_CTRL3	0x35
 #define AXP221_PAGE		0xff
 
 /* Page 1 addresses */
diff --git a/include/axp_pmic.h b/include/axp_pmic.h
index ae62ef0d76..3bcd8f9ca9 100644
--- a/include/axp_pmic.h
+++ b/include/axp_pmic.h
@@ -52,6 +52,7 @@  int axp_set_aldo4(unsigned int mvolt);
 int axp_set_dldo(int dldo_num, unsigned int mvolt);
 int axp_set_eldo(int eldo_num, unsigned int mvolt);
 int axp_set_fldo(int fldo_num, unsigned int mvolt);
+int axp221_set_charge_current(unsigned int mamp);
 int axp_set_sw(bool on);
 int axp_init(void);
 int axp_get_sid(unsigned int *sid);