* [PATCH 1/8] dt-bindings: iio: adc: Add AXP717 compatible
2024-06-17 22:05 [PATCH 0/8] Add Battery and USB Supply for AXP717 Chris Morgan
@ 2024-06-17 22:05 ` Chris Morgan
2024-06-18 6:41 ` Krzysztof Kozlowski
2024-06-23 5:17 ` Chen-Yu Tsai
2024-06-17 22:05 ` [PATCH 2/8] power: supply: axp20x_usb_power: Add support for AXP717 Chris Morgan
` (6 subsequent siblings)
7 siblings, 2 replies; 28+ messages in thread
From: Chris Morgan @ 2024-06-17 22:05 UTC (permalink / raw)
To: linux-sunxi
Cc: linux-pm, devicetree, linux-iio, broonie, lee, samuel,
jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add compatible binding for the axp717.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
.../bindings/iio/adc/x-powers,axp209-adc.yaml | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml b/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml
index d40689f233f2..267c74fa2d0c 100644
--- a/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml
@@ -37,6 +37,15 @@ description: |
3 | batt_dischrg_i
4 | ts_v
+ AXP717
+ ------
+ 0 | batt_v
+ 1 | ts_v
+ 2 | vbus_v
+ 3 | vsys_v
+ 4 | pmic_temp
+ 5 | batt_chrg_i
+
AXP813
------
0 | pmic_temp
@@ -52,6 +61,7 @@ properties:
oneOf:
- const: x-powers,axp209-adc
- const: x-powers,axp221-adc
+ - const: x-powers,axp717-adc
- const: x-powers,axp813-adc
- items:
--
2.34.1
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH 1/8] dt-bindings: iio: adc: Add AXP717 compatible
2024-06-17 22:05 ` [PATCH 1/8] dt-bindings: iio: adc: Add AXP717 compatible Chris Morgan
@ 2024-06-18 6:41 ` Krzysztof Kozlowski
2024-06-23 5:17 ` Chen-Yu Tsai
1 sibling, 0 replies; 28+ messages in thread
From: Krzysztof Kozlowski @ 2024-06-18 6:41 UTC (permalink / raw)
To: Chris Morgan, linux-sunxi
Cc: linux-pm, devicetree, linux-iio, broonie, lee, samuel,
jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
On 18/06/2024 00:05, Chris Morgan wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add compatible binding for the axp717.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 1/8] dt-bindings: iio: adc: Add AXP717 compatible
2024-06-17 22:05 ` [PATCH 1/8] dt-bindings: iio: adc: Add AXP717 compatible Chris Morgan
2024-06-18 6:41 ` Krzysztof Kozlowski
@ 2024-06-23 5:17 ` Chen-Yu Tsai
1 sibling, 0 replies; 28+ messages in thread
From: Chen-Yu Tsai @ 2024-06-23 5:17 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-sunxi, linux-pm, devicetree, linux-iio, broonie, lee,
samuel, jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
On Mon, Jun 17, 2024 at 05:05:28PM -0500, Chris Morgan wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add compatible binding for the axp717.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
> ---
> .../bindings/iio/adc/x-powers,axp209-adc.yaml | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml b/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml
> index d40689f233f2..267c74fa2d0c 100644
> --- a/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml
> +++ b/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml
> @@ -37,6 +37,15 @@ description: |
> 3 | batt_dischrg_i
> 4 | ts_v
>
> + AXP717
> + ------
> + 0 | batt_v
> + 1 | ts_v
> + 2 | vbus_v
> + 3 | vsys_v
> + 4 | pmic_temp
> + 5 | batt_chrg_i
You are missing channels for VMID voltage and backup battery voltage.
ChenYu
> +
> AXP813
> ------
> 0 | pmic_temp
> @@ -52,6 +61,7 @@ properties:
> oneOf:
> - const: x-powers,axp209-adc
> - const: x-powers,axp221-adc
> + - const: x-powers,axp717-adc
> - const: x-powers,axp813-adc
>
> - items:
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 2/8] power: supply: axp20x_usb_power: Add support for AXP717
2024-06-17 22:05 [PATCH 0/8] Add Battery and USB Supply for AXP717 Chris Morgan
2024-06-17 22:05 ` [PATCH 1/8] dt-bindings: iio: adc: Add AXP717 compatible Chris Morgan
@ 2024-06-17 22:05 ` Chris Morgan
2024-06-18 6:40 ` Krzysztof Kozlowski
2024-06-23 5:02 ` Chen-Yu Tsai
2024-06-17 22:05 ` [PATCH 3/8] power: supply: axp20x_battery: add " Chris Morgan
` (5 subsequent siblings)
7 siblings, 2 replies; 28+ messages in thread
From: Chris Morgan @ 2024-06-17 22:05 UTC (permalink / raw)
To: linux-sunxi
Cc: linux-pm, devicetree, linux-iio, broonie, lee, samuel,
jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add support for the AXP717. It has BC 1.2 detection like the AXP813
and uses ADC channels like all other AXP devices, but otherwise is
very different requiring new registers for most functions.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
.../power/supply/x-powers,axp20x-usb-power-supply.yaml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml
index 34b7959d6772..e5879c85c9a3 100644
--- a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml
+++ b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml
@@ -26,11 +26,17 @@ properties:
- x-powers,axp202-usb-power-supply
- x-powers,axp221-usb-power-supply
- x-powers,axp223-usb-power-supply
+ - x-powers,axp717-usb-power-supply
- x-powers,axp813-usb-power-supply
- items:
- const: x-powers,axp803-usb-power-supply
- const: x-powers,axp813-usb-power-supply
+ input-current-limit-microamp:
+ description:
+ Optional value to clamp the maximum input current limit to for
+ the device. The supported min and max values will vary based on
+ the PMIC revision, consult the datasheet for supported values.
required:
- compatible
--
2.34.1
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH 2/8] power: supply: axp20x_usb_power: Add support for AXP717
2024-06-17 22:05 ` [PATCH 2/8] power: supply: axp20x_usb_power: Add support for AXP717 Chris Morgan
@ 2024-06-18 6:40 ` Krzysztof Kozlowski
2024-06-23 5:02 ` Chen-Yu Tsai
1 sibling, 0 replies; 28+ messages in thread
From: Krzysztof Kozlowski @ 2024-06-18 6:40 UTC (permalink / raw)
To: Chris Morgan, linux-sunxi
Cc: linux-pm, devicetree, linux-iio, broonie, lee, samuel,
jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
On 18/06/2024 00:05, Chris Morgan wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add support for the AXP717. It has BC 1.2 detection like the AXP813
> and uses ADC channels like all other AXP devices, but otherwise is
> very different requiring new registers for most functions.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Please use subject prefixes matching the subsystem. You can get them for
example with `git log --oneline -- DIRECTORY_OR_FILE` on the directory
your patch is touching. For bindings, the preferred subjects are
explained here:
https://www.kernel.org/doc/html/latest/devicetree/bindings/submitting-patches.html#i-for-patch-submitters
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 2/8] power: supply: axp20x_usb_power: Add support for AXP717
2024-06-17 22:05 ` [PATCH 2/8] power: supply: axp20x_usb_power: Add support for AXP717 Chris Morgan
2024-06-18 6:40 ` Krzysztof Kozlowski
@ 2024-06-23 5:02 ` Chen-Yu Tsai
1 sibling, 0 replies; 28+ messages in thread
From: Chen-Yu Tsai @ 2024-06-23 5:02 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-sunxi, linux-pm, devicetree, linux-iio, broonie, lee,
samuel, jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
On Mon, Jun 17, 2024 at 05:05:29PM -0500, Chris Morgan wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add support for the AXP717. It has BC 1.2 detection like the AXP813
> and uses ADC channels like all other AXP devices, but otherwise is
> very different requiring new registers for most functions.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Acked-by: Chen-Yu Tsai <wens@csie.org>
> ---
> .../power/supply/x-powers,axp20x-usb-power-supply.yaml | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml
> index 34b7959d6772..e5879c85c9a3 100644
> --- a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml
> +++ b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml
> @@ -26,11 +26,17 @@ properties:
> - x-powers,axp202-usb-power-supply
> - x-powers,axp221-usb-power-supply
> - x-powers,axp223-usb-power-supply
> + - x-powers,axp717-usb-power-supply
> - x-powers,axp813-usb-power-supply
> - items:
> - const: x-powers,axp803-usb-power-supply
> - const: x-powers,axp813-usb-power-supply
>
> + input-current-limit-microamp:
> + description:
> + Optional value to clamp the maximum input current limit to for
> + the device. The supported min and max values will vary based on
> + the PMIC revision, consult the datasheet for supported values.
>
> required:
> - compatible
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 3/8] power: supply: axp20x_battery: add support for AXP717
2024-06-17 22:05 [PATCH 0/8] Add Battery and USB Supply for AXP717 Chris Morgan
2024-06-17 22:05 ` [PATCH 1/8] dt-bindings: iio: adc: Add AXP717 compatible Chris Morgan
2024-06-17 22:05 ` [PATCH 2/8] power: supply: axp20x_usb_power: Add support for AXP717 Chris Morgan
@ 2024-06-17 22:05 ` Chris Morgan
2024-06-18 6:41 ` Krzysztof Kozlowski
2024-06-23 5:05 ` Chen-Yu Tsai
2024-06-17 22:05 ` [PATCH 4/8] mfd: axp20x: Add ADC, BAT, and USB cells " Chris Morgan
` (4 subsequent siblings)
7 siblings, 2 replies; 28+ messages in thread
From: Chris Morgan @ 2024-06-17 22:05 UTC (permalink / raw)
To: linux-sunxi
Cc: linux-pm, devicetree, linux-iio, broonie, lee, samuel,
jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add binding information for AXP717. Also, as the driver can read
simple-battery parameters for the AXP717 and other batteries, define
the simple-battery parameter.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
.../power/supply/x-powers,axp20x-battery-power-supply.yaml | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml
index e0b95ecbbebd..8d6b06117f6d 100644
--- a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml
+++ b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml
@@ -23,11 +23,18 @@ properties:
- const: x-powers,axp202-battery-power-supply
- const: x-powers,axp209-battery-power-supply
- const: x-powers,axp221-battery-power-supply
+ - const: x-powers,axp717-battery-power-supply
- items:
- const: x-powers,axp803-battery-power-supply
- const: x-powers,axp813-battery-power-supply
- const: x-powers,axp813-battery-power-supply
+ monitored-battery:
+ description: |
+ Specifies the phandle of an optional simple-battery connected to
+ this gauge.
+ $ref: /schemas/types.yaml#/definitions/phandle
+
required:
- compatible
--
2.34.1
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH 3/8] power: supply: axp20x_battery: add support for AXP717
2024-06-17 22:05 ` [PATCH 3/8] power: supply: axp20x_battery: add " Chris Morgan
@ 2024-06-18 6:41 ` Krzysztof Kozlowski
2024-06-23 5:05 ` Chen-Yu Tsai
1 sibling, 0 replies; 28+ messages in thread
From: Krzysztof Kozlowski @ 2024-06-18 6:41 UTC (permalink / raw)
To: Chris Morgan, linux-sunxi
Cc: linux-pm, devicetree, linux-iio, broonie, lee, samuel,
jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
On 18/06/2024 00:05, Chris Morgan wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add binding information for AXP717. Also, as the driver can read
> simple-battery parameters for the AXP717 and other batteries, define
> the simple-battery parameter.
>
> Signed-off-by: Chris Morgan <m
Please use subject prefixes matching the subsystem. You can get them for
example with `git log --oneline -- DIRECTORY_OR_FILE` on the directory
your patch is touching. For bindings, the preferred subjects are
explained here:
https://www.kernel.org/doc/html/latest/devicetree/bindings/submitting-patches.html#i-for-patch-submitters
acromorgan@hotmail.com>
> ---
> .../power/supply/x-powers,axp20x-battery-power-supply.yaml | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml
> index e0b95ecbbebd..8d6b06117f6d 100644
> --- a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml
> +++ b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml
> @@ -23,11 +23,18 @@ properties:
> - const: x-powers,axp202-battery-power-supply
> - const: x-powers,axp209-battery-power-supply
> - const: x-powers,axp221-battery-power-supply
> + - const: x-powers,axp717-battery-power-supply
> - items:
> - const: x-powers,axp803-battery-power-supply
> - const: x-powers,axp813-battery-power-supply
> - const: x-powers,axp813-battery-power-supply
>
> + monitored-battery:
> + description: |
Do not need '|' unless you need to preserve formatting.
> + Specifies the phandle of an optional simple-battery connected to
> + this gauge.
> + $ref: /schemas/types.yaml#/definitions/phandle
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 3/8] power: supply: axp20x_battery: add support for AXP717
2024-06-17 22:05 ` [PATCH 3/8] power: supply: axp20x_battery: add " Chris Morgan
2024-06-18 6:41 ` Krzysztof Kozlowski
@ 2024-06-23 5:05 ` Chen-Yu Tsai
1 sibling, 0 replies; 28+ messages in thread
From: Chen-Yu Tsai @ 2024-06-23 5:05 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-sunxi, linux-pm, devicetree, linux-iio, broonie, lee,
samuel, jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
On Mon, Jun 17, 2024 at 05:05:30PM -0500, Chris Morgan wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add binding information for AXP717. Also, as the driver can read
> simple-battery parameters for the AXP717 and other batteries, define
> the simple-battery parameter.
The binding should not care about whether the implementation (driver)
can or cannot do something.
Probably reword it like "the PMIC can be programmed with specific
battery parameters".
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
> ---
> .../power/supply/x-powers,axp20x-battery-power-supply.yaml | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml
> index e0b95ecbbebd..8d6b06117f6d 100644
> --- a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml
> +++ b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml
> @@ -23,11 +23,18 @@ properties:
> - const: x-powers,axp202-battery-power-supply
> - const: x-powers,axp209-battery-power-supply
> - const: x-powers,axp221-battery-power-supply
> + - const: x-powers,axp717-battery-power-supply
> - items:
> - const: x-powers,axp803-battery-power-supply
> - const: x-powers,axp813-battery-power-supply
> - const: x-powers,axp813-battery-power-supply
>
> + monitored-battery:
> + description: |
> + Specifies the phandle of an optional simple-battery connected to
> + this gauge.
> + $ref: /schemas/types.yaml#/definitions/phandle
> +
> required:
> - compatible
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 4/8] mfd: axp20x: Add ADC, BAT, and USB cells for AXP717
2024-06-17 22:05 [PATCH 0/8] Add Battery and USB Supply for AXP717 Chris Morgan
` (2 preceding siblings ...)
2024-06-17 22:05 ` [PATCH 3/8] power: supply: axp20x_battery: add " Chris Morgan
@ 2024-06-17 22:05 ` Chris Morgan
2024-06-20 17:25 ` Lee Jones
2024-06-23 5:55 ` Chen-Yu Tsai
2024-06-17 22:05 ` [PATCH 5/8] iio: adc: axp20x_adc: add support for AXP717 ADC Chris Morgan
` (3 subsequent siblings)
7 siblings, 2 replies; 28+ messages in thread
From: Chris Morgan @ 2024-06-17 22:05 UTC (permalink / raw)
To: linux-sunxi
Cc: linux-pm, devicetree, linux-iio, broonie, lee, samuel,
jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add support for the AXP717 PMIC to utilize the ADC (for reading
voltage, current, and temperature information from the PMIC) as well
as the USB charger and battery.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/mfd/axp20x.c | 30 +++++++++++++++++++++++++---
drivers/regulator/axp20x-regulator.c | 2 +-
include/linux/mfd/axp20x.h | 26 +++++++++++++++++++++++-
3 files changed, 53 insertions(+), 5 deletions(-)
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index 609e7e149681..07db4a11acbc 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -209,15 +209,23 @@ static const struct regmap_access_table axp313a_volatile_table = {
};
static const struct regmap_range axp717_writeable_ranges[] = {
- regmap_reg_range(AXP717_MODULE_EN_CONTROL, AXP717_MODULE_EN_CONTROL),
- regmap_reg_range(AXP717_BOOST_CONTROL, AXP717_BOOST_CONTROL),
+ regmap_reg_range(AXP717_PMU_FAULT, AXP717_MODULE_EN_CONTROL_1),
+ regmap_reg_range(AXP717_MIN_SYS_V_CONTROL, AXP717_BOOST_CONTROL),
+ regmap_reg_range(AXP717_VSYS_V_POWEROFF, AXP717_VSYS_V_POWEROFF),
regmap_reg_range(AXP717_IRQ0_EN, AXP717_IRQ4_EN),
regmap_reg_range(AXP717_IRQ0_STATE, AXP717_IRQ4_STATE),
+ regmap_reg_range(AXP717_ICC_CHG_SET, AXP717_CV_CHG_SET),
regmap_reg_range(AXP717_DCDC_OUTPUT_CONTROL, AXP717_CPUSLDO_CONTROL),
+ regmap_reg_range(AXP717_ADC_CH_EN_CONTROL, AXP717_ADC_CH_EN_CONTROL),
+ regmap_reg_range(AXP717_ADC_DATA_SEL, AXP717_ADC_DATA_SEL),
};
static const struct regmap_range axp717_volatile_ranges[] = {
+ regmap_reg_range(AXP717_ON_INDICATE, AXP717_PMU_FAULT),
regmap_reg_range(AXP717_IRQ0_STATE, AXP717_IRQ4_STATE),
+ regmap_reg_range(AXP717_BATT_PERCENT_DATA, AXP717_BATT_PERCENT_DATA),
+ regmap_reg_range(AXP717_BATT_V_H, AXP717_BATT_CHRG_I_L),
+ regmap_reg_range(AXP717_ADC_DATA_H, AXP717_ADC_DATA_L),
};
static const struct regmap_access_table axp717_writeable_table = {
@@ -310,6 +318,11 @@ static const struct resource axp22x_usb_power_supply_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"),
};
+static const struct resource axp717_usb_power_supply_resources[] = {
+ DEFINE_RES_IRQ_NAMED(AXP717_IRQ_VBUS_PLUGIN, "VBUS_PLUGIN"),
+ DEFINE_RES_IRQ_NAMED(AXP717_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"),
+};
+
/* AXP803 and AXP813/AXP818 share the same interrupts */
static const struct resource axp803_usb_power_supply_resources[] = {
DEFINE_RES_IRQ_NAMED(AXP803_IRQ_VBUS_PLUGIN, "VBUS_PLUGIN"),
@@ -424,7 +437,7 @@ static const struct regmap_config axp717_regmap_config = {
.val_bits = 8,
.wr_table = &axp717_writeable_table,
.volatile_table = &axp717_volatile_table,
- .max_register = AXP717_CPUSLDO_CONTROL,
+ .max_register = AXP717_ADC_DATA_L,
.cache_type = REGCACHE_MAPLE,
};
@@ -1026,6 +1039,17 @@ static struct mfd_cell axp313a_cells[] = {
static struct mfd_cell axp717_cells[] = {
MFD_CELL_NAME("axp20x-regulator"),
MFD_CELL_RES("axp20x-pek", axp717_pek_resources),
+ {
+ .name = "axp717-adc",
+ .of_compatible = "x-powers,axp717-adc",
+ },
+ MFD_CELL_OF("axp20x-usb-power-supply",
+ axp717_usb_power_supply_resources, NULL, 0, 0,
+ "x-powers,axp717-usb-power-supply"),
+ {
+ .name = "axp20x-battery-power-supply",
+ .of_compatible = "x-powers,axp717-battery-power-supply",
+ },
};
static const struct resource axp288_adc_resources[] = {
diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c
index 20bef3971fec..a8e91d9d028b 100644
--- a/drivers/regulator/axp20x-regulator.c
+++ b/drivers/regulator/axp20x-regulator.c
@@ -837,7 +837,7 @@ static const struct regulator_desc axp717_regulators[] = {
AXP717_LDO1_OUTPUT_CONTROL, BIT(4)),
AXP_DESC(AXP717, BOOST, "boost", "vin1", 4550, 5510, 64,
AXP717_BOOST_CONTROL, AXP717_BOOST_V_OUT_MASK,
- AXP717_MODULE_EN_CONTROL, BIT(4)),
+ AXP717_MODULE_EN_CONTROL_2, BIT(4)),
};
/* DCDC ranges shared with AXP813 */
diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
index 5e86b976c4ca..f4dfc1871a95 100644
--- a/include/linux/mfd/axp20x.h
+++ b/include/linux/mfd/axp20x.h
@@ -115,8 +115,16 @@ enum axp20x_variants {
#define AXP313A_IRQ_STATE 0x21
#define AXP717_ON_INDICATE 0x00
-#define AXP717_MODULE_EN_CONTROL 0x19
+#define AXP717_PMU_STATUS_2 0x01
+#define AXP717_BC_DETECT 0x05
+#define AXP717_PMU_FAULT 0x08
+#define AXP717_MODULE_EN_CONTROL_1 0x0b
+#define AXP717_MIN_SYS_V_CONTROL 0x15
+#define AXP717_INPUT_VOL_LIMIT_CTRL 0x16
+#define AXP717_INPUT_CUR_LIMIT_CTRL 0x17
+#define AXP717_MODULE_EN_CONTROL_2 0x19
#define AXP717_BOOST_CONTROL 0x1e
+#define AXP717_VSYS_V_POWEROFF 0x24
#define AXP717_IRQ0_EN 0x40
#define AXP717_IRQ1_EN 0x41
#define AXP717_IRQ2_EN 0x42
@@ -127,6 +135,9 @@ enum axp20x_variants {
#define AXP717_IRQ2_STATE 0x4a
#define AXP717_IRQ3_STATE 0x4b
#define AXP717_IRQ4_STATE 0x4c
+#define AXP717_ICC_CHG_SET 0x62
+#define AXP717_ITERM_CHG_SET 0x63
+#define AXP717_CV_CHG_SET 0x64
#define AXP717_DCDC_OUTPUT_CONTROL 0x80
#define AXP717_DCDC1_CONTROL 0x83
#define AXP717_DCDC2_CONTROL 0x84
@@ -147,6 +158,19 @@ enum axp20x_variants {
#define AXP717_CLDO3_CONTROL 0x9d
#define AXP717_CLDO4_CONTROL 0x9e
#define AXP717_CPUSLDO_CONTROL 0x9f
+#define AXP717_BATT_PERCENT_DATA 0xa4
+#define AXP717_ADC_CH_EN_CONTROL 0xc0
+#define AXP717_BATT_V_H 0xc4
+#define AXP717_BATT_V_L 0xc5
+#define AXP717_VBUS_V_H 0xc6
+#define AXP717_VBUS_V_L 0xc7
+#define AXP717_VSYS_V_H 0xc8
+#define AXP717_VSYS_V_L 0xc9
+#define AXP717_BATT_CHRG_I_H 0xca
+#define AXP717_BATT_CHRG_I_L 0xcb
+#define AXP717_ADC_DATA_SEL 0xcd
+#define AXP717_ADC_DATA_H 0xce
+#define AXP717_ADC_DATA_L 0xcf
#define AXP806_STARTUP_SRC 0x00
#define AXP806_CHIP_ID 0x03
--
2.34.1
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH 4/8] mfd: axp20x: Add ADC, BAT, and USB cells for AXP717
2024-06-17 22:05 ` [PATCH 4/8] mfd: axp20x: Add ADC, BAT, and USB cells " Chris Morgan
@ 2024-06-20 17:25 ` Lee Jones
2024-06-23 5:55 ` Chen-Yu Tsai
1 sibling, 0 replies; 28+ messages in thread
From: Lee Jones @ 2024-06-20 17:25 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-sunxi, linux-pm, devicetree, linux-iio, broonie, samuel,
jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
On Mon, 17 Jun 2024, Chris Morgan wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add support for the AXP717 PMIC to utilize the ADC (for reading
> voltage, current, and temperature information from the PMIC) as well
> as the USB charger and battery.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
> ---
> drivers/mfd/axp20x.c | 30 +++++++++++++++++++++++++---
> drivers/regulator/axp20x-regulator.c | 2 +-
> include/linux/mfd/axp20x.h | 26 +++++++++++++++++++++++-
> 3 files changed, 53 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
> index 609e7e149681..07db4a11acbc 100644
> --- a/drivers/mfd/axp20x.c
> +++ b/drivers/mfd/axp20x.c
> @@ -209,15 +209,23 @@ static const struct regmap_access_table axp313a_volatile_table = {
> };
>
> static const struct regmap_range axp717_writeable_ranges[] = {
> - regmap_reg_range(AXP717_MODULE_EN_CONTROL, AXP717_MODULE_EN_CONTROL),
> - regmap_reg_range(AXP717_BOOST_CONTROL, AXP717_BOOST_CONTROL),
> + regmap_reg_range(AXP717_PMU_FAULT, AXP717_MODULE_EN_CONTROL_1),
> + regmap_reg_range(AXP717_MIN_SYS_V_CONTROL, AXP717_BOOST_CONTROL),
> + regmap_reg_range(AXP717_VSYS_V_POWEROFF, AXP717_VSYS_V_POWEROFF),
> regmap_reg_range(AXP717_IRQ0_EN, AXP717_IRQ4_EN),
> regmap_reg_range(AXP717_IRQ0_STATE, AXP717_IRQ4_STATE),
> + regmap_reg_range(AXP717_ICC_CHG_SET, AXP717_CV_CHG_SET),
> regmap_reg_range(AXP717_DCDC_OUTPUT_CONTROL, AXP717_CPUSLDO_CONTROL),
> + regmap_reg_range(AXP717_ADC_CH_EN_CONTROL, AXP717_ADC_CH_EN_CONTROL),
> + regmap_reg_range(AXP717_ADC_DATA_SEL, AXP717_ADC_DATA_SEL),
> };
>
> static const struct regmap_range axp717_volatile_ranges[] = {
> + regmap_reg_range(AXP717_ON_INDICATE, AXP717_PMU_FAULT),
> regmap_reg_range(AXP717_IRQ0_STATE, AXP717_IRQ4_STATE),
> + regmap_reg_range(AXP717_BATT_PERCENT_DATA, AXP717_BATT_PERCENT_DATA),
> + regmap_reg_range(AXP717_BATT_V_H, AXP717_BATT_CHRG_I_L),
> + regmap_reg_range(AXP717_ADC_DATA_H, AXP717_ADC_DATA_L),
> };
>
> static const struct regmap_access_table axp717_writeable_table = {
> @@ -310,6 +318,11 @@ static const struct resource axp22x_usb_power_supply_resources[] = {
> DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"),
> };
>
> +static const struct resource axp717_usb_power_supply_resources[] = {
> + DEFINE_RES_IRQ_NAMED(AXP717_IRQ_VBUS_PLUGIN, "VBUS_PLUGIN"),
> + DEFINE_RES_IRQ_NAMED(AXP717_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"),
> +};
> +
> /* AXP803 and AXP813/AXP818 share the same interrupts */
> static const struct resource axp803_usb_power_supply_resources[] = {
> DEFINE_RES_IRQ_NAMED(AXP803_IRQ_VBUS_PLUGIN, "VBUS_PLUGIN"),
> @@ -424,7 +437,7 @@ static const struct regmap_config axp717_regmap_config = {
> .val_bits = 8,
> .wr_table = &axp717_writeable_table,
> .volatile_table = &axp717_volatile_table,
> - .max_register = AXP717_CPUSLDO_CONTROL,
> + .max_register = AXP717_ADC_DATA_L,
> .cache_type = REGCACHE_MAPLE,
> };
>
> @@ -1026,6 +1039,17 @@ static struct mfd_cell axp313a_cells[] = {
> static struct mfd_cell axp717_cells[] = {
> MFD_CELL_NAME("axp20x-regulator"),
> MFD_CELL_RES("axp20x-pek", axp717_pek_resources),
> + {
> + .name = "axp717-adc",
> + .of_compatible = "x-powers,axp717-adc",
> + },
> + MFD_CELL_OF("axp20x-usb-power-supply",
> + axp717_usb_power_supply_resources, NULL, 0, 0,
> + "x-powers,axp717-usb-power-supply"),
> + {
> + .name = "axp20x-battery-power-supply",
> + .of_compatible = "x-powers,axp717-battery-power-supply",
> + },
> };
Please refrain from mixing and matching:
MFD_CELL_OF() for both.
--
Lee Jones [李琼斯]
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [PATCH 4/8] mfd: axp20x: Add ADC, BAT, and USB cells for AXP717
2024-06-17 22:05 ` [PATCH 4/8] mfd: axp20x: Add ADC, BAT, and USB cells " Chris Morgan
2024-06-20 17:25 ` Lee Jones
@ 2024-06-23 5:55 ` Chen-Yu Tsai
1 sibling, 0 replies; 28+ messages in thread
From: Chen-Yu Tsai @ 2024-06-23 5:55 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-sunxi, linux-pm, devicetree, linux-iio, broonie, lee,
samuel, jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
On Mon, Jun 17, 2024 at 05:05:31PM -0500, Chris Morgan wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add support for the AXP717 PMIC to utilize the ADC (for reading
> voltage, current, and temperature information from the PMIC) as well
> as the USB charger and battery.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
> ---
> drivers/mfd/axp20x.c | 30 +++++++++++++++++++++++++---
> drivers/regulator/axp20x-regulator.c | 2 +-
I suggest you rebase your patches on to linux-next. The boost regulator
isn't merged yet, and it will probably hold back your patch series.
> include/linux/mfd/axp20x.h | 26 +++++++++++++++++++++++-
> 3 files changed, 53 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
> index 609e7e149681..07db4a11acbc 100644
> --- a/drivers/mfd/axp20x.c
> +++ b/drivers/mfd/axp20x.c
> @@ -209,15 +209,23 @@ static const struct regmap_access_table axp313a_volatile_table = {
> };
>
> static const struct regmap_range axp717_writeable_ranges[] = {
> - regmap_reg_range(AXP717_MODULE_EN_CONTROL, AXP717_MODULE_EN_CONTROL),
> - regmap_reg_range(AXP717_BOOST_CONTROL, AXP717_BOOST_CONTROL),
> + regmap_reg_range(AXP717_PMU_FAULT, AXP717_MODULE_EN_CONTROL_1),
> + regmap_reg_range(AXP717_MIN_SYS_V_CONTROL, AXP717_BOOST_CONTROL),
> + regmap_reg_range(AXP717_VSYS_V_POWEROFF, AXP717_VSYS_V_POWEROFF),
> regmap_reg_range(AXP717_IRQ0_EN, AXP717_IRQ4_EN),
> regmap_reg_range(AXP717_IRQ0_STATE, AXP717_IRQ4_STATE),
> + regmap_reg_range(AXP717_ICC_CHG_SET, AXP717_CV_CHG_SET),
> regmap_reg_range(AXP717_DCDC_OUTPUT_CONTROL, AXP717_CPUSLDO_CONTROL),
> + regmap_reg_range(AXP717_ADC_CH_EN_CONTROL, AXP717_ADC_CH_EN_CONTROL),
> + regmap_reg_range(AXP717_ADC_DATA_SEL, AXP717_ADC_DATA_SEL),
> };
>
> static const struct regmap_range axp717_volatile_ranges[] = {
> + regmap_reg_range(AXP717_ON_INDICATE, AXP717_PMU_FAULT),
> regmap_reg_range(AXP717_IRQ0_STATE, AXP717_IRQ4_STATE),
> + regmap_reg_range(AXP717_BATT_PERCENT_DATA, AXP717_BATT_PERCENT_DATA),
> + regmap_reg_range(AXP717_BATT_V_H, AXP717_BATT_CHRG_I_L),
> + regmap_reg_range(AXP717_ADC_DATA_H, AXP717_ADC_DATA_L),
> };
>
> static const struct regmap_access_table axp717_writeable_table = {
> @@ -310,6 +318,11 @@ static const struct resource axp22x_usb_power_supply_resources[] = {
> DEFINE_RES_IRQ_NAMED(AXP22X_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"),
> };
>
> +static const struct resource axp717_usb_power_supply_resources[] = {
> + DEFINE_RES_IRQ_NAMED(AXP717_IRQ_VBUS_PLUGIN, "VBUS_PLUGIN"),
> + DEFINE_RES_IRQ_NAMED(AXP717_IRQ_VBUS_REMOVAL, "VBUS_REMOVAL"),
Perhaps it would make sense to add VBUS fault and VBUS overvoltage?
> +};
> +
> /* AXP803 and AXP813/AXP818 share the same interrupts */
> static const struct resource axp803_usb_power_supply_resources[] = {
> DEFINE_RES_IRQ_NAMED(AXP803_IRQ_VBUS_PLUGIN, "VBUS_PLUGIN"),
> @@ -424,7 +437,7 @@ static const struct regmap_config axp717_regmap_config = {
> .val_bits = 8,
> .wr_table = &axp717_writeable_table,
> .volatile_table = &axp717_volatile_table,
> - .max_register = AXP717_CPUSLDO_CONTROL,
> + .max_register = AXP717_ADC_DATA_L,
> .cache_type = REGCACHE_MAPLE,
> };
>
> @@ -1026,6 +1039,17 @@ static struct mfd_cell axp313a_cells[] = {
> static struct mfd_cell axp717_cells[] = {
> MFD_CELL_NAME("axp20x-regulator"),
> MFD_CELL_RES("axp20x-pek", axp717_pek_resources),
> + {
> + .name = "axp717-adc",
> + .of_compatible = "x-powers,axp717-adc",
> + },
> + MFD_CELL_OF("axp20x-usb-power-supply",
> + axp717_usb_power_supply_resources, NULL, 0, 0,
> + "x-powers,axp717-usb-power-supply"),
> + {
> + .name = "axp20x-battery-power-supply",
> + .of_compatible = "x-powers,axp717-battery-power-supply",
> + },
> };
>
> static const struct resource axp288_adc_resources[] = {
> diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c
> index 20bef3971fec..a8e91d9d028b 100644
> --- a/drivers/regulator/axp20x-regulator.c
> +++ b/drivers/regulator/axp20x-regulator.c
> @@ -837,7 +837,7 @@ static const struct regulator_desc axp717_regulators[] = {
> AXP717_LDO1_OUTPUT_CONTROL, BIT(4)),
> AXP_DESC(AXP717, BOOST, "boost", "vin1", 4550, 5510, 64,
> AXP717_BOOST_CONTROL, AXP717_BOOST_V_OUT_MASK,
> - AXP717_MODULE_EN_CONTROL, BIT(4)),
> + AXP717_MODULE_EN_CONTROL_2, BIT(4)),
> };
>
> /* DCDC ranges shared with AXP813 */
As mentioned above, please rebase onto linux-next and drop this hunk.
The rest look correct.
Thanks
ChenYu
> diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
> index 5e86b976c4ca..f4dfc1871a95 100644
> --- a/include/linux/mfd/axp20x.h
> +++ b/include/linux/mfd/axp20x.h
> @@ -115,8 +115,16 @@ enum axp20x_variants {
> #define AXP313A_IRQ_STATE 0x21
>
> #define AXP717_ON_INDICATE 0x00
> -#define AXP717_MODULE_EN_CONTROL 0x19
> +#define AXP717_PMU_STATUS_2 0x01
> +#define AXP717_BC_DETECT 0x05
> +#define AXP717_PMU_FAULT 0x08
> +#define AXP717_MODULE_EN_CONTROL_1 0x0b
> +#define AXP717_MIN_SYS_V_CONTROL 0x15
> +#define AXP717_INPUT_VOL_LIMIT_CTRL 0x16
> +#define AXP717_INPUT_CUR_LIMIT_CTRL 0x17
> +#define AXP717_MODULE_EN_CONTROL_2 0x19
> #define AXP717_BOOST_CONTROL 0x1e
> +#define AXP717_VSYS_V_POWEROFF 0x24
> #define AXP717_IRQ0_EN 0x40
> #define AXP717_IRQ1_EN 0x41
> #define AXP717_IRQ2_EN 0x42
> @@ -127,6 +135,9 @@ enum axp20x_variants {
> #define AXP717_IRQ2_STATE 0x4a
> #define AXP717_IRQ3_STATE 0x4b
> #define AXP717_IRQ4_STATE 0x4c
> +#define AXP717_ICC_CHG_SET 0x62
> +#define AXP717_ITERM_CHG_SET 0x63
> +#define AXP717_CV_CHG_SET 0x64
> #define AXP717_DCDC_OUTPUT_CONTROL 0x80
> #define AXP717_DCDC1_CONTROL 0x83
> #define AXP717_DCDC2_CONTROL 0x84
> @@ -147,6 +158,19 @@ enum axp20x_variants {
> #define AXP717_CLDO3_CONTROL 0x9d
> #define AXP717_CLDO4_CONTROL 0x9e
> #define AXP717_CPUSLDO_CONTROL 0x9f
> +#define AXP717_BATT_PERCENT_DATA 0xa4
> +#define AXP717_ADC_CH_EN_CONTROL 0xc0
> +#define AXP717_BATT_V_H 0xc4
> +#define AXP717_BATT_V_L 0xc5
> +#define AXP717_VBUS_V_H 0xc6
> +#define AXP717_VBUS_V_L 0xc7
> +#define AXP717_VSYS_V_H 0xc8
> +#define AXP717_VSYS_V_L 0xc9
> +#define AXP717_BATT_CHRG_I_H 0xca
> +#define AXP717_BATT_CHRG_I_L 0xcb
> +#define AXP717_ADC_DATA_SEL 0xcd
> +#define AXP717_ADC_DATA_H 0xce
> +#define AXP717_ADC_DATA_L 0xcf
>
> #define AXP806_STARTUP_SRC 0x00
> #define AXP806_CHIP_ID 0x03
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 5/8] iio: adc: axp20x_adc: add support for AXP717 ADC
2024-06-17 22:05 [PATCH 0/8] Add Battery and USB Supply for AXP717 Chris Morgan
` (3 preceding siblings ...)
2024-06-17 22:05 ` [PATCH 4/8] mfd: axp20x: Add ADC, BAT, and USB cells " Chris Morgan
@ 2024-06-17 22:05 ` Chris Morgan
2024-06-23 5:27 ` Chen-Yu Tsai
2024-06-23 16:30 ` Jonathan Cameron
2024-06-17 22:05 ` [PATCH 6/8] power: supply: axp20x_usb_power: Add support for AXP717 Chris Morgan
` (2 subsequent siblings)
7 siblings, 2 replies; 28+ messages in thread
From: Chris Morgan @ 2024-06-17 22:05 UTC (permalink / raw)
To: linux-sunxi
Cc: linux-pm, devicetree, linux-iio, broonie, lee, samuel,
jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add support for the AXP717 ADC. The AXP717 differs from other ADCs
in this series by utilizing a 14 bit ADC for all channels (a full 16
bits with the first 2 digits reserved). It also differs by lacking a
battery discharge current channel.
Note that while the current charge channel itself is included in this
driver for the AXP717 and listed in the datasheet, no scale or offset
was given for this channel. For now no scale or offset is provided in
this driver.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/adc/axp20x_adc.c | 167 +++++++++++++++++++++++++++++++++--
1 file changed, 160 insertions(+), 7 deletions(-)
diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
index d6c51b0f48e3..f35ba2c11e1b 100644
--- a/drivers/iio/adc/axp20x_adc.c
+++ b/drivers/iio/adc/axp20x_adc.c
@@ -5,6 +5,7 @@
* Quentin Schulz <quentin.schulz@free-electrons.com>
*/
+#include <asm/unaligned.h>
#include <linux/bitfield.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
@@ -27,6 +28,8 @@
#define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0))
+#define AXP717_ADC_EN1_MASK GENMASK(5, 0)
+
#define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0)
#define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1)
@@ -35,6 +38,11 @@
#define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK)
+#define AXP717_ADC_DATA_TS 0x00
+#define AXP717_ADC_DATA_TEMP 0x01
+
+#define AXP717_ADC_DATA_MASK 0x3fff
+
#define AXP813_V_I_ADC_RATE_MASK GENMASK(5, 4)
#define AXP813_ADC_RATE_MASK (AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK)
#define AXP813_TS_GPIO0_ADC_RATE_HZ(x) AXP20X_ADC_RATE_HZ(x)
@@ -98,6 +106,18 @@ enum axp22x_adc_channel_i {
AXP22X_BATT_DISCHRG_I,
};
+enum axp717_adc_channel_v {
+ AXP717_BATT_V = 0,
+ AXP717_TS_IN,
+ AXP717_VBUS_V,
+ AXP717_VSYS_V,
+ AXP717_DIE_TEMP_V,
+};
+
+enum axp717_adc_channel_i {
+ AXP717_BATT_CHRG_I = 5,
+};
+
enum axp813_adc_channel_v {
AXP813_TS_IN = 0,
AXP813_GPIO0_V,
@@ -152,6 +172,22 @@ static struct iio_map axp22x_maps[] = {
}, { /* sentinel */ }
};
+static struct iio_map axp717_maps[] = {
+ {
+ .consumer_dev_name = "axp20x-usb-power-supply",
+ .consumer_channel = "vbus_v",
+ .adc_channel_label = "vbus_v",
+ }, {
+ .consumer_dev_name = "axp20x-battery-power-supply",
+ .consumer_channel = "batt_v",
+ .adc_channel_label = "batt_v",
+ }, {
+ .consumer_dev_name = "axp20x-battery-power-supply",
+ .consumer_channel = "batt_chrg_i",
+ .adc_channel_label = "batt_chrg_i",
+ },
+};
+
/*
* Channels are mapped by physical system. Their channels share the same index.
* i.e. acin_i is in_current0_raw and acin_v is in_voltage0_raw.
@@ -210,6 +246,25 @@ static const struct iio_chan_spec axp22x_adc_channels[] = {
AXP22X_TS_ADC_H),
};
+/*
+ * Scale and offset is unknown for temp, ts, and batt_chrg_i channels
+ * for now. Leaving scale and offset undefined for now.
+ */
+static const struct iio_chan_spec axp717_adc_channels[] = {
+ AXP20X_ADC_CHANNEL(AXP717_BATT_V, "batt_v", IIO_VOLTAGE,
+ AXP717_BATT_V_H),
+ AXP20X_ADC_CHANNEL(AXP717_TS_IN, "ts_v", IIO_VOLTAGE,
+ AXP717_ADC_DATA_H),
+ AXP20X_ADC_CHANNEL(AXP717_VBUS_V, "vbus_v", IIO_VOLTAGE,
+ AXP717_VBUS_V_H),
+ AXP20X_ADC_CHANNEL(AXP717_VSYS_V, "vsys_v", IIO_VOLTAGE,
+ AXP717_VSYS_V_H),
+ AXP20X_ADC_CHANNEL(AXP717_DIE_TEMP_V, "pmic_temp", IIO_TEMP,
+ AXP717_ADC_DATA_H),
+ AXP20X_ADC_CHANNEL(AXP717_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
+ AXP717_BATT_CHRG_I_H),
+};
+
static const struct iio_chan_spec axp813_adc_channels[] = {
{
.type = IIO_TEMP,
@@ -269,6 +324,44 @@ static int axp22x_adc_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
}
+static int axp717_adc_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val)
+{
+ struct axp20x_adc_iio *info = iio_priv(indio_dev);
+ u8 bulk_reg[2];
+ int ret;
+
+ /*
+ * A generic "ADC data" channel is used for TS, tdie, vmid,
+ * and vbackup. This channel must both first be enabled and
+ * also selected before it can be read.
+ */
+ switch (chan->channel) {
+ case AXP717_TS_IN:
+ regmap_write(info->regmap, AXP717_ADC_DATA_SEL,
+ AXP717_ADC_DATA_TS);
+ break;
+ case AXP717_DIE_TEMP_V:
+ regmap_write(info->regmap, AXP717_ADC_DATA_SEL,
+ AXP717_ADC_DATA_TEMP);
+ break;
+
+ default:
+ break;
+ }
+
+ /*
+ * All channels are 14 bits, with the first 2 bits on the high
+ * register reserved and the remaining bits as the ADC value.
+ */
+ ret = regmap_bulk_read(info->regmap, chan->address, bulk_reg, 2);
+ if (ret < 0)
+ return ret;
+
+ *val = get_unaligned_be16(bulk_reg) & AXP717_ADC_DATA_MASK;
+ return IIO_VAL_INT;
+}
+
static int axp813_adc_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val)
{
@@ -425,6 +518,27 @@ static int axp22x_adc_scale(struct iio_chan_spec const *chan, int *val,
}
}
+static int axp717_adc_scale(struct iio_chan_spec const *chan, int *val,
+ int *val2)
+{
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *val = 1;
+ return IIO_VAL_INT;
+
+ case IIO_CURRENT:
+ *val = 1;
+ return IIO_VAL_INT;
+
+ case IIO_TEMP:
+ *val = 100;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
static int axp813_adc_scale(struct iio_chan_spec const *chan, int *val,
int *val2)
{
@@ -529,6 +643,22 @@ static int axp22x_read_raw(struct iio_dev *indio_dev,
}
}
+static int axp717_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return axp717_adc_scale(chan, val, val2);
+
+ case IIO_CHAN_INFO_RAW:
+ return axp717_adc_raw(indio_dev, chan, val);
+
+ default:
+ return -EINVAL;
+ }
+}
+
static int axp813_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
@@ -593,6 +723,10 @@ static const struct iio_info axp22x_adc_iio_info = {
.read_raw = axp22x_read_raw,
};
+static const struct iio_info axp717_adc_iio_info = {
+ .read_raw = axp717_read_raw,
+};
+
static const struct iio_info axp813_adc_iio_info = {
.read_raw = axp813_read_raw,
};
@@ -622,7 +756,9 @@ struct axp_data {
const struct iio_info *iio_info;
int num_channels;
struct iio_chan_spec const *channels;
+ unsigned long adc_en1;
unsigned long adc_en1_mask;
+ unsigned long adc_en2;
unsigned long adc_en2_mask;
int (*adc_rate)(struct axp20x_adc_iio *info,
int rate);
@@ -633,7 +769,9 @@ static const struct axp_data axp20x_data = {
.iio_info = &axp20x_adc_iio_info,
.num_channels = ARRAY_SIZE(axp20x_adc_channels),
.channels = axp20x_adc_channels,
+ .adc_en1 = AXP20X_ADC_EN1,
.adc_en1_mask = AXP20X_ADC_EN1_MASK,
+ .adc_en2 = AXP20X_ADC_EN2,
.adc_en2_mask = AXP20X_ADC_EN2_MASK,
.adc_rate = axp20x_adc_rate,
.maps = axp20x_maps,
@@ -643,15 +781,26 @@ static const struct axp_data axp22x_data = {
.iio_info = &axp22x_adc_iio_info,
.num_channels = ARRAY_SIZE(axp22x_adc_channels),
.channels = axp22x_adc_channels,
+ .adc_en1 = AXP20X_ADC_EN1,
.adc_en1_mask = AXP22X_ADC_EN1_MASK,
.adc_rate = axp22x_adc_rate,
.maps = axp22x_maps,
};
+static const struct axp_data axp717_data = {
+ .iio_info = &axp717_adc_iio_info,
+ .num_channels = ARRAY_SIZE(axp717_adc_channels),
+ .channels = axp717_adc_channels,
+ .adc_en1 = AXP717_ADC_CH_EN_CONTROL,
+ .adc_en1_mask = AXP717_ADC_EN1_MASK,
+ .maps = axp717_maps,
+};
+
static const struct axp_data axp813_data = {
.iio_info = &axp813_adc_iio_info,
.num_channels = ARRAY_SIZE(axp813_adc_channels),
.channels = axp813_adc_channels,
+ .adc_en1 = AXP20X_ADC_EN1,
.adc_en1_mask = AXP22X_ADC_EN1_MASK,
.adc_rate = axp813_adc_rate,
.maps = axp22x_maps,
@@ -660,6 +809,7 @@ static const struct axp_data axp813_data = {
static const struct of_device_id axp20x_adc_of_match[] = {
{ .compatible = "x-powers,axp209-adc", .data = (void *)&axp20x_data, },
{ .compatible = "x-powers,axp221-adc", .data = (void *)&axp22x_data, },
+ { .compatible = "x-powers,axp717-adc", .data = (void *)&axp717_data, },
{ .compatible = "x-powers,axp813-adc", .data = (void *)&axp813_data, },
{ /* sentinel */ }
};
@@ -668,6 +818,7 @@ MODULE_DEVICE_TABLE(of, axp20x_adc_of_match);
static const struct platform_device_id axp20x_adc_id_match[] = {
{ .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, },
{ .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, },
+ { .name = "axp717-adc", .driver_data = (kernel_ulong_t)&axp717_data, },
{ .name = "axp813-adc", .driver_data = (kernel_ulong_t)&axp813_data, },
{ /* sentinel */ },
};
@@ -709,15 +860,17 @@ static int axp20x_probe(struct platform_device *pdev)
indio_dev->channels = info->data->channels;
/* Enable the ADCs on IP */
- regmap_write(info->regmap, AXP20X_ADC_EN1, info->data->adc_en1_mask);
+ regmap_write(info->regmap, info->data->adc_en1,
+ info->data->adc_en1_mask);
if (info->data->adc_en2_mask)
- regmap_update_bits(info->regmap, AXP20X_ADC_EN2,
+ regmap_update_bits(info->regmap, info->data->adc_en2,
info->data->adc_en2_mask,
info->data->adc_en2_mask);
/* Configure ADCs rate */
- info->data->adc_rate(info, 100);
+ if (info->data->adc_rate)
+ info->data->adc_rate(info, 100);
ret = iio_map_array_register(indio_dev, info->data->maps);
if (ret < 0) {
@@ -737,10 +890,10 @@ static int axp20x_probe(struct platform_device *pdev)
iio_map_array_unregister(indio_dev);
fail_map:
- regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
+ regmap_write(info->regmap, info->data->adc_en1, 0);
if (info->data->adc_en2_mask)
- regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
+ regmap_write(info->regmap, info->data->adc_en2, 0);
return ret;
}
@@ -753,10 +906,10 @@ static void axp20x_remove(struct platform_device *pdev)
iio_device_unregister(indio_dev);
iio_map_array_unregister(indio_dev);
- regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
+ regmap_write(info->regmap, info->data->adc_en1, 0);
if (info->data->adc_en2_mask)
- regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
+ regmap_write(info->regmap, info->data->adc_en2, 0);
}
static struct platform_driver axp20x_adc_driver = {
--
2.34.1
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH 5/8] iio: adc: axp20x_adc: add support for AXP717 ADC
2024-06-17 22:05 ` [PATCH 5/8] iio: adc: axp20x_adc: add support for AXP717 ADC Chris Morgan
@ 2024-06-23 5:27 ` Chen-Yu Tsai
2024-07-30 19:11 ` Chris Morgan
2024-06-23 16:30 ` Jonathan Cameron
1 sibling, 1 reply; 28+ messages in thread
From: Chen-Yu Tsai @ 2024-06-23 5:27 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-sunxi, linux-pm, devicetree, linux-iio, broonie, lee,
samuel, jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
On Mon, Jun 17, 2024 at 05:05:32PM -0500, Chris Morgan wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add support for the AXP717 ADC. The AXP717 differs from other ADCs
> in this series by utilizing a 14 bit ADC for all channels (a full 16
> bits with the first 2 digits reserved). It also differs by lacking a
> battery discharge current channel.
Maybe charge and discharge share the same channel?
> Note that while the current charge channel itself is included in this
> driver for the AXP717 and listed in the datasheet, no scale or offset
> was given for this channel. For now no scale or offset is provided in
> this driver.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
> ---
> drivers/iio/adc/axp20x_adc.c | 167 +++++++++++++++++++++++++++++++++--
> 1 file changed, 160 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
> index d6c51b0f48e3..f35ba2c11e1b 100644
> --- a/drivers/iio/adc/axp20x_adc.c
> +++ b/drivers/iio/adc/axp20x_adc.c
> @@ -5,6 +5,7 @@
> * Quentin Schulz <quentin.schulz@free-electrons.com>
> */
>
> +#include <asm/unaligned.h>
> #include <linux/bitfield.h>
> #include <linux/completion.h>
> #include <linux/interrupt.h>
> @@ -27,6 +28,8 @@
>
> #define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0))
>
> +#define AXP717_ADC_EN1_MASK GENMASK(5, 0)
> +
> #define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0)
> #define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1)
>
> @@ -35,6 +38,11 @@
>
> #define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK)
>
> +#define AXP717_ADC_DATA_TS 0x00
> +#define AXP717_ADC_DATA_TEMP 0x01
> +
> +#define AXP717_ADC_DATA_MASK 0x3fff
> +
> #define AXP813_V_I_ADC_RATE_MASK GENMASK(5, 4)
> #define AXP813_ADC_RATE_MASK (AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK)
> #define AXP813_TS_GPIO0_ADC_RATE_HZ(x) AXP20X_ADC_RATE_HZ(x)
> @@ -98,6 +106,18 @@ enum axp22x_adc_channel_i {
> AXP22X_BATT_DISCHRG_I,
> };
>
> +enum axp717_adc_channel_v {
> + AXP717_BATT_V = 0,
> + AXP717_TS_IN,
> + AXP717_VBUS_V,
> + AXP717_VSYS_V,
> + AXP717_DIE_TEMP_V,
Please also add VMID and backup battery voltage.
> +};
> +
> +enum axp717_adc_channel_i {
> + AXP717_BATT_CHRG_I = 5,
> +};
> +
> enum axp813_adc_channel_v {
> AXP813_TS_IN = 0,
> AXP813_GPIO0_V,
> @@ -152,6 +172,22 @@ static struct iio_map axp22x_maps[] = {
> }, { /* sentinel */ }
> };
>
> +static struct iio_map axp717_maps[] = {
> + {
> + .consumer_dev_name = "axp20x-usb-power-supply",
> + .consumer_channel = "vbus_v",
> + .adc_channel_label = "vbus_v",
> + }, {
> + .consumer_dev_name = "axp20x-battery-power-supply",
> + .consumer_channel = "batt_v",
> + .adc_channel_label = "batt_v",
> + }, {
> + .consumer_dev_name = "axp20x-battery-power-supply",
> + .consumer_channel = "batt_chrg_i",
> + .adc_channel_label = "batt_chrg_i",
> + },
> +};
> +
> /*
> * Channels are mapped by physical system. Their channels share the same index.
> * i.e. acin_i is in_current0_raw and acin_v is in_voltage0_raw.
> @@ -210,6 +246,25 @@ static const struct iio_chan_spec axp22x_adc_channels[] = {
> AXP22X_TS_ADC_H),
> };
>
> +/*
> + * Scale and offset is unknown for temp, ts, and batt_chrg_i channels
> + * for now. Leaving scale and offset undefined for now.
> + */
> +static const struct iio_chan_spec axp717_adc_channels[] = {
> + AXP20X_ADC_CHANNEL(AXP717_BATT_V, "batt_v", IIO_VOLTAGE,
> + AXP717_BATT_V_H),
> + AXP20X_ADC_CHANNEL(AXP717_TS_IN, "ts_v", IIO_VOLTAGE,
> + AXP717_ADC_DATA_H),
> + AXP20X_ADC_CHANNEL(AXP717_VBUS_V, "vbus_v", IIO_VOLTAGE,
> + AXP717_VBUS_V_H),
> + AXP20X_ADC_CHANNEL(AXP717_VSYS_V, "vsys_v", IIO_VOLTAGE,
> + AXP717_VSYS_V_H),
> + AXP20X_ADC_CHANNEL(AXP717_DIE_TEMP_V, "pmic_temp", IIO_TEMP,
> + AXP717_ADC_DATA_H),
> + AXP20X_ADC_CHANNEL(AXP717_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
> + AXP717_BATT_CHRG_I_H),
> +};
> +
> static const struct iio_chan_spec axp813_adc_channels[] = {
> {
> .type = IIO_TEMP,
> @@ -269,6 +324,44 @@ static int axp22x_adc_raw(struct iio_dev *indio_dev,
> return IIO_VAL_INT;
> }
>
> +static int axp717_adc_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val)
> +{
> + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> + u8 bulk_reg[2];
> + int ret;
> +
> + /*
> + * A generic "ADC data" channel is used for TS, tdie, vmid,
> + * and vbackup. This channel must both first be enabled and
> + * also selected before it can be read.
> + */
> + switch (chan->channel) {
> + case AXP717_TS_IN:
> + regmap_write(info->regmap, AXP717_ADC_DATA_SEL,
> + AXP717_ADC_DATA_TS);
> + break;
> + case AXP717_DIE_TEMP_V:
> + regmap_write(info->regmap, AXP717_ADC_DATA_SEL,
> + AXP717_ADC_DATA_TEMP);
> + break;
> +
> + default:
> + break;
> + }
> +
> + /*
> + * All channels are 14 bits, with the first 2 bits on the high
> + * register reserved and the remaining bits as the ADC value.
> + */
> + ret = regmap_bulk_read(info->regmap, chan->address, bulk_reg, 2);
> + if (ret < 0)
> + return ret;
> +
> + *val = get_unaligned_be16(bulk_reg) & AXP717_ADC_DATA_MASK;
> + return IIO_VAL_INT;
> +}
> +
> static int axp813_adc_raw(struct iio_dev *indio_dev,
> struct iio_chan_spec const *chan, int *val)
> {
> @@ -425,6 +518,27 @@ static int axp22x_adc_scale(struct iio_chan_spec const *chan, int *val,
> }
> }
>
> +static int axp717_adc_scale(struct iio_chan_spec const *chan, int *val,
> + int *val2)
> +{
> + switch (chan->type) {
> + case IIO_VOLTAGE:
> + *val = 1;
> + return IIO_VAL_INT;
> +
> + case IIO_CURRENT:
> + *val = 1;
> + return IIO_VAL_INT;
> +
> + case IIO_TEMP:
> + *val = 100;
> + return IIO_VAL_INT;
> +
> + default:
> + return -EINVAL;
> + }
> +}
> +
> static int axp813_adc_scale(struct iio_chan_spec const *chan, int *val,
> int *val2)
> {
> @@ -529,6 +643,22 @@ static int axp22x_read_raw(struct iio_dev *indio_dev,
> }
> }
>
> +static int axp717_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val,
> + int *val2, long mask)
> +{
> + switch (mask) {
> + case IIO_CHAN_INFO_SCALE:
> + return axp717_adc_scale(chan, val, val2);
> +
> + case IIO_CHAN_INFO_RAW:
> + return axp717_adc_raw(indio_dev, chan, val);
> +
> + default:
> + return -EINVAL;
> + }
> +}
> +
> static int axp813_read_raw(struct iio_dev *indio_dev,
> struct iio_chan_spec const *chan, int *val,
> int *val2, long mask)
> @@ -593,6 +723,10 @@ static const struct iio_info axp22x_adc_iio_info = {
> .read_raw = axp22x_read_raw,
> };
>
> +static const struct iio_info axp717_adc_iio_info = {
> + .read_raw = axp717_read_raw,
> +};
> +
> static const struct iio_info axp813_adc_iio_info = {
> .read_raw = axp813_read_raw,
> };
> @@ -622,7 +756,9 @@ struct axp_data {
> const struct iio_info *iio_info;
> int num_channels;
> struct iio_chan_spec const *channels;
> + unsigned long adc_en1;
> unsigned long adc_en1_mask;
> + unsigned long adc_en2;
> unsigned long adc_en2_mask;
> int (*adc_rate)(struct axp20x_adc_iio *info,
> int rate);
> @@ -633,7 +769,9 @@ static const struct axp_data axp20x_data = {
> .iio_info = &axp20x_adc_iio_info,
> .num_channels = ARRAY_SIZE(axp20x_adc_channels),
> .channels = axp20x_adc_channels,
> + .adc_en1 = AXP20X_ADC_EN1,
> .adc_en1_mask = AXP20X_ADC_EN1_MASK,
> + .adc_en2 = AXP20X_ADC_EN2,
> .adc_en2_mask = AXP20X_ADC_EN2_MASK,
> .adc_rate = axp20x_adc_rate,
> .maps = axp20x_maps,
> @@ -643,15 +781,26 @@ static const struct axp_data axp22x_data = {
> .iio_info = &axp22x_adc_iio_info,
> .num_channels = ARRAY_SIZE(axp22x_adc_channels),
> .channels = axp22x_adc_channels,
> + .adc_en1 = AXP20X_ADC_EN1,
> .adc_en1_mask = AXP22X_ADC_EN1_MASK,
> .adc_rate = axp22x_adc_rate,
> .maps = axp22x_maps,
> };
Please split out the changes generalizing the driver into a separate
patch. That makes it easier to follow.
ChenYu
> +static const struct axp_data axp717_data = {
> + .iio_info = &axp717_adc_iio_info,
> + .num_channels = ARRAY_SIZE(axp717_adc_channels),
> + .channels = axp717_adc_channels,
> + .adc_en1 = AXP717_ADC_CH_EN_CONTROL,
> + .adc_en1_mask = AXP717_ADC_EN1_MASK,
> + .maps = axp717_maps,
> +};
> +
> static const struct axp_data axp813_data = {
> .iio_info = &axp813_adc_iio_info,
> .num_channels = ARRAY_SIZE(axp813_adc_channels),
> .channels = axp813_adc_channels,
> + .adc_en1 = AXP20X_ADC_EN1,
> .adc_en1_mask = AXP22X_ADC_EN1_MASK,
> .adc_rate = axp813_adc_rate,
> .maps = axp22x_maps,
> @@ -660,6 +809,7 @@ static const struct axp_data axp813_data = {
> static const struct of_device_id axp20x_adc_of_match[] = {
> { .compatible = "x-powers,axp209-adc", .data = (void *)&axp20x_data, },
> { .compatible = "x-powers,axp221-adc", .data = (void *)&axp22x_data, },
> + { .compatible = "x-powers,axp717-adc", .data = (void *)&axp717_data, },
> { .compatible = "x-powers,axp813-adc", .data = (void *)&axp813_data, },
> { /* sentinel */ }
> };
> @@ -668,6 +818,7 @@ MODULE_DEVICE_TABLE(of, axp20x_adc_of_match);
> static const struct platform_device_id axp20x_adc_id_match[] = {
> { .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, },
> { .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, },
> + { .name = "axp717-adc", .driver_data = (kernel_ulong_t)&axp717_data, },
> { .name = "axp813-adc", .driver_data = (kernel_ulong_t)&axp813_data, },
> { /* sentinel */ },
> };
> @@ -709,15 +860,17 @@ static int axp20x_probe(struct platform_device *pdev)
> indio_dev->channels = info->data->channels;
>
> /* Enable the ADCs on IP */
> - regmap_write(info->regmap, AXP20X_ADC_EN1, info->data->adc_en1_mask);
> + regmap_write(info->regmap, info->data->adc_en1,
> + info->data->adc_en1_mask);
>
> if (info->data->adc_en2_mask)
> - regmap_update_bits(info->regmap, AXP20X_ADC_EN2,
> + regmap_update_bits(info->regmap, info->data->adc_en2,
> info->data->adc_en2_mask,
> info->data->adc_en2_mask);
>
> /* Configure ADCs rate */
> - info->data->adc_rate(info, 100);
> + if (info->data->adc_rate)
> + info->data->adc_rate(info, 100);
>
> ret = iio_map_array_register(indio_dev, info->data->maps);
> if (ret < 0) {
> @@ -737,10 +890,10 @@ static int axp20x_probe(struct platform_device *pdev)
> iio_map_array_unregister(indio_dev);
>
> fail_map:
> - regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
> + regmap_write(info->regmap, info->data->adc_en1, 0);
>
> if (info->data->adc_en2_mask)
> - regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
> + regmap_write(info->regmap, info->data->adc_en2, 0);
>
> return ret;
> }
> @@ -753,10 +906,10 @@ static void axp20x_remove(struct platform_device *pdev)
> iio_device_unregister(indio_dev);
> iio_map_array_unregister(indio_dev);
>
> - regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
> + regmap_write(info->regmap, info->data->adc_en1, 0);
>
> if (info->data->adc_en2_mask)
> - regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
> + regmap_write(info->regmap, info->data->adc_en2, 0);
> }
>
> static struct platform_driver axp20x_adc_driver = {
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [PATCH 5/8] iio: adc: axp20x_adc: add support for AXP717 ADC
2024-06-23 5:27 ` Chen-Yu Tsai
@ 2024-07-30 19:11 ` Chris Morgan
0 siblings, 0 replies; 28+ messages in thread
From: Chris Morgan @ 2024-07-30 19:11 UTC (permalink / raw)
To: Chen-Yu Tsai
Cc: Chris Morgan, linux-sunxi, linux-pm, devicetree, linux-iio,
broonie, lee, samuel, jernej.skrabec, sre, wens, conor+dt,
krzk+dt, robh, lars
On Sun, Jun 23, 2024 at 01:27:25PM +0800, Chen-Yu Tsai wrote:
> On Mon, Jun 17, 2024 at 05:05:32PM -0500, Chris Morgan wrote:
> > From: Chris Morgan <macromorgan@hotmail.com>
> >
> > Add support for the AXP717 ADC. The AXP717 differs from other ADCs
> > in this series by utilizing a 14 bit ADC for all channels (a full 16
> > bits with the first 2 digits reserved). It also differs by lacking a
> > battery discharge current channel.
>
> Maybe charge and discharge share the same channel?
I can't confirm that. I've enabled multiple power draws and don't
notice a change on this channel consistent with such a draw. Power
draws include backlight, GPIO vibrators, and turning on/off regulators.
>
> > Note that while the current charge channel itself is included in this
> > driver for the AXP717 and listed in the datasheet, no scale or offset
> > was given for this channel. For now no scale or offset is provided in
> > this driver.
> >
> > Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
> > ---
> > drivers/iio/adc/axp20x_adc.c | 167 +++++++++++++++++++++++++++++++++--
> > 1 file changed, 160 insertions(+), 7 deletions(-)
> >
> > diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
> > index d6c51b0f48e3..f35ba2c11e1b 100644
> > --- a/drivers/iio/adc/axp20x_adc.c
> > +++ b/drivers/iio/adc/axp20x_adc.c
> > @@ -5,6 +5,7 @@
> > * Quentin Schulz <quentin.schulz@free-electrons.com>
> > */
> >
> > +#include <asm/unaligned.h>
> > #include <linux/bitfield.h>
> > #include <linux/completion.h>
> > #include <linux/interrupt.h>
> > @@ -27,6 +28,8 @@
> >
> > #define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0))
> >
> > +#define AXP717_ADC_EN1_MASK GENMASK(5, 0)
> > +
> > #define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0)
> > #define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1)
> >
> > @@ -35,6 +38,11 @@
> >
> > #define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK)
> >
> > +#define AXP717_ADC_DATA_TS 0x00
> > +#define AXP717_ADC_DATA_TEMP 0x01
> > +
> > +#define AXP717_ADC_DATA_MASK 0x3fff
> > +
> > #define AXP813_V_I_ADC_RATE_MASK GENMASK(5, 4)
> > #define AXP813_ADC_RATE_MASK (AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK)
> > #define AXP813_TS_GPIO0_ADC_RATE_HZ(x) AXP20X_ADC_RATE_HZ(x)
> > @@ -98,6 +106,18 @@ enum axp22x_adc_channel_i {
> > AXP22X_BATT_DISCHRG_I,
> > };
> >
> > +enum axp717_adc_channel_v {
> > + AXP717_BATT_V = 0,
> > + AXP717_TS_IN,
> > + AXP717_VBUS_V,
> > + AXP717_VSYS_V,
> > + AXP717_DIE_TEMP_V,
>
> Please also add VMID and backup battery voltage.
Will do, I'll make sure all ADC channels are here, even if they aren't
used.
>
> > +};
> > +
> > +enum axp717_adc_channel_i {
> > + AXP717_BATT_CHRG_I = 5,
> > +};
> > +
> > enum axp813_adc_channel_v {
> > AXP813_TS_IN = 0,
> > AXP813_GPIO0_V,
> > @@ -152,6 +172,22 @@ static struct iio_map axp22x_maps[] = {
> > }, { /* sentinel */ }
> > };
> >
> > +static struct iio_map axp717_maps[] = {
> > + {
> > + .consumer_dev_name = "axp20x-usb-power-supply",
> > + .consumer_channel = "vbus_v",
> > + .adc_channel_label = "vbus_v",
> > + }, {
> > + .consumer_dev_name = "axp20x-battery-power-supply",
> > + .consumer_channel = "batt_v",
> > + .adc_channel_label = "batt_v",
> > + }, {
> > + .consumer_dev_name = "axp20x-battery-power-supply",
> > + .consumer_channel = "batt_chrg_i",
> > + .adc_channel_label = "batt_chrg_i",
> > + },
> > +};
> > +
> > /*
> > * Channels are mapped by physical system. Their channels share the same index.
> > * i.e. acin_i is in_current0_raw and acin_v is in_voltage0_raw.
> > @@ -210,6 +246,25 @@ static const struct iio_chan_spec axp22x_adc_channels[] = {
> > AXP22X_TS_ADC_H),
> > };
> >
> > +/*
> > + * Scale and offset is unknown for temp, ts, and batt_chrg_i channels
> > + * for now. Leaving scale and offset undefined for now.
> > + */
> > +static const struct iio_chan_spec axp717_adc_channels[] = {
> > + AXP20X_ADC_CHANNEL(AXP717_BATT_V, "batt_v", IIO_VOLTAGE,
> > + AXP717_BATT_V_H),
> > + AXP20X_ADC_CHANNEL(AXP717_TS_IN, "ts_v", IIO_VOLTAGE,
> > + AXP717_ADC_DATA_H),
> > + AXP20X_ADC_CHANNEL(AXP717_VBUS_V, "vbus_v", IIO_VOLTAGE,
> > + AXP717_VBUS_V_H),
> > + AXP20X_ADC_CHANNEL(AXP717_VSYS_V, "vsys_v", IIO_VOLTAGE,
> > + AXP717_VSYS_V_H),
> > + AXP20X_ADC_CHANNEL(AXP717_DIE_TEMP_V, "pmic_temp", IIO_TEMP,
> > + AXP717_ADC_DATA_H),
> > + AXP20X_ADC_CHANNEL(AXP717_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
> > + AXP717_BATT_CHRG_I_H),
> > +};
> > +
> > static const struct iio_chan_spec axp813_adc_channels[] = {
> > {
> > .type = IIO_TEMP,
> > @@ -269,6 +324,44 @@ static int axp22x_adc_raw(struct iio_dev *indio_dev,
> > return IIO_VAL_INT;
> > }
> >
> > +static int axp717_adc_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan, int *val)
> > +{
> > + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> > + u8 bulk_reg[2];
> > + int ret;
> > +
> > + /*
> > + * A generic "ADC data" channel is used for TS, tdie, vmid,
> > + * and vbackup. This channel must both first be enabled and
> > + * also selected before it can be read.
> > + */
> > + switch (chan->channel) {
> > + case AXP717_TS_IN:
> > + regmap_write(info->regmap, AXP717_ADC_DATA_SEL,
> > + AXP717_ADC_DATA_TS);
> > + break;
> > + case AXP717_DIE_TEMP_V:
> > + regmap_write(info->regmap, AXP717_ADC_DATA_SEL,
> > + AXP717_ADC_DATA_TEMP);
> > + break;
> > +
> > + default:
> > + break;
> > + }
> > +
> > + /*
> > + * All channels are 14 bits, with the first 2 bits on the high
> > + * register reserved and the remaining bits as the ADC value.
> > + */
> > + ret = regmap_bulk_read(info->regmap, chan->address, bulk_reg, 2);
> > + if (ret < 0)
> > + return ret;
> > +
> > + *val = get_unaligned_be16(bulk_reg) & AXP717_ADC_DATA_MASK;
> > + return IIO_VAL_INT;
> > +}
> > +
> > static int axp813_adc_raw(struct iio_dev *indio_dev,
> > struct iio_chan_spec const *chan, int *val)
> > {
> > @@ -425,6 +518,27 @@ static int axp22x_adc_scale(struct iio_chan_spec const *chan, int *val,
> > }
> > }
> >
> > +static int axp717_adc_scale(struct iio_chan_spec const *chan, int *val,
> > + int *val2)
> > +{
> > + switch (chan->type) {
> > + case IIO_VOLTAGE:
> > + *val = 1;
> > + return IIO_VAL_INT;
> > +
> > + case IIO_CURRENT:
> > + *val = 1;
> > + return IIO_VAL_INT;
> > +
> > + case IIO_TEMP:
> > + *val = 100;
> > + return IIO_VAL_INT;
> > +
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > static int axp813_adc_scale(struct iio_chan_spec const *chan, int *val,
> > int *val2)
> > {
> > @@ -529,6 +643,22 @@ static int axp22x_read_raw(struct iio_dev *indio_dev,
> > }
> > }
> >
> > +static int axp717_read_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan, int *val,
> > + int *val2, long mask)
> > +{
> > + switch (mask) {
> > + case IIO_CHAN_INFO_SCALE:
> > + return axp717_adc_scale(chan, val, val2);
> > +
> > + case IIO_CHAN_INFO_RAW:
> > + return axp717_adc_raw(indio_dev, chan, val);
> > +
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > static int axp813_read_raw(struct iio_dev *indio_dev,
> > struct iio_chan_spec const *chan, int *val,
> > int *val2, long mask)
> > @@ -593,6 +723,10 @@ static const struct iio_info axp22x_adc_iio_info = {
> > .read_raw = axp22x_read_raw,
> > };
> >
> > +static const struct iio_info axp717_adc_iio_info = {
> > + .read_raw = axp717_read_raw,
> > +};
> > +
> > static const struct iio_info axp813_adc_iio_info = {
> > .read_raw = axp813_read_raw,
> > };
> > @@ -622,7 +756,9 @@ struct axp_data {
> > const struct iio_info *iio_info;
> > int num_channels;
> > struct iio_chan_spec const *channels;
> > + unsigned long adc_en1;
> > unsigned long adc_en1_mask;
> > + unsigned long adc_en2;
> > unsigned long adc_en2_mask;
> > int (*adc_rate)(struct axp20x_adc_iio *info,
> > int rate);
> > @@ -633,7 +769,9 @@ static const struct axp_data axp20x_data = {
> > .iio_info = &axp20x_adc_iio_info,
> > .num_channels = ARRAY_SIZE(axp20x_adc_channels),
> > .channels = axp20x_adc_channels,
> > + .adc_en1 = AXP20X_ADC_EN1,
> > .adc_en1_mask = AXP20X_ADC_EN1_MASK,
> > + .adc_en2 = AXP20X_ADC_EN2,
> > .adc_en2_mask = AXP20X_ADC_EN2_MASK,
> > .adc_rate = axp20x_adc_rate,
> > .maps = axp20x_maps,
> > @@ -643,15 +781,26 @@ static const struct axp_data axp22x_data = {
> > .iio_info = &axp22x_adc_iio_info,
> > .num_channels = ARRAY_SIZE(axp22x_adc_channels),
> > .channels = axp22x_adc_channels,
> > + .adc_en1 = AXP20X_ADC_EN1,
> > .adc_en1_mask = AXP22X_ADC_EN1_MASK,
> > .adc_rate = axp22x_adc_rate,
> > .maps = axp22x_maps,
> > };
>
> Please split out the changes generalizing the driver into a separate
> patch. That makes it easier to follow.
>
Acknowledged.
>
> ChenYu
>
> > +static const struct axp_data axp717_data = {
> > + .iio_info = &axp717_adc_iio_info,
> > + .num_channels = ARRAY_SIZE(axp717_adc_channels),
> > + .channels = axp717_adc_channels,
> > + .adc_en1 = AXP717_ADC_CH_EN_CONTROL,
> > + .adc_en1_mask = AXP717_ADC_EN1_MASK,
> > + .maps = axp717_maps,
> > +};
> > +
> > static const struct axp_data axp813_data = {
> > .iio_info = &axp813_adc_iio_info,
> > .num_channels = ARRAY_SIZE(axp813_adc_channels),
> > .channels = axp813_adc_channels,
> > + .adc_en1 = AXP20X_ADC_EN1,
> > .adc_en1_mask = AXP22X_ADC_EN1_MASK,
> > .adc_rate = axp813_adc_rate,
> > .maps = axp22x_maps,
> > @@ -660,6 +809,7 @@ static const struct axp_data axp813_data = {
> > static const struct of_device_id axp20x_adc_of_match[] = {
> > { .compatible = "x-powers,axp209-adc", .data = (void *)&axp20x_data, },
> > { .compatible = "x-powers,axp221-adc", .data = (void *)&axp22x_data, },
> > + { .compatible = "x-powers,axp717-adc", .data = (void *)&axp717_data, },
> > { .compatible = "x-powers,axp813-adc", .data = (void *)&axp813_data, },
> > { /* sentinel */ }
> > };
> > @@ -668,6 +818,7 @@ MODULE_DEVICE_TABLE(of, axp20x_adc_of_match);
> > static const struct platform_device_id axp20x_adc_id_match[] = {
> > { .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, },
> > { .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, },
> > + { .name = "axp717-adc", .driver_data = (kernel_ulong_t)&axp717_data, },
> > { .name = "axp813-adc", .driver_data = (kernel_ulong_t)&axp813_data, },
> > { /* sentinel */ },
> > };
> > @@ -709,15 +860,17 @@ static int axp20x_probe(struct platform_device *pdev)
> > indio_dev->channels = info->data->channels;
> >
> > /* Enable the ADCs on IP */
> > - regmap_write(info->regmap, AXP20X_ADC_EN1, info->data->adc_en1_mask);
> > + regmap_write(info->regmap, info->data->adc_en1,
> > + info->data->adc_en1_mask);
> >
> > if (info->data->adc_en2_mask)
> > - regmap_update_bits(info->regmap, AXP20X_ADC_EN2,
> > + regmap_update_bits(info->regmap, info->data->adc_en2,
> > info->data->adc_en2_mask,
> > info->data->adc_en2_mask);
> >
> > /* Configure ADCs rate */
> > - info->data->adc_rate(info, 100);
> > + if (info->data->adc_rate)
> > + info->data->adc_rate(info, 100);
> >
> > ret = iio_map_array_register(indio_dev, info->data->maps);
> > if (ret < 0) {
> > @@ -737,10 +890,10 @@ static int axp20x_probe(struct platform_device *pdev)
> > iio_map_array_unregister(indio_dev);
> >
> > fail_map:
> > - regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
> > + regmap_write(info->regmap, info->data->adc_en1, 0);
> >
> > if (info->data->adc_en2_mask)
> > - regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
> > + regmap_write(info->regmap, info->data->adc_en2, 0);
> >
> > return ret;
> > }
> > @@ -753,10 +906,10 @@ static void axp20x_remove(struct platform_device *pdev)
> > iio_device_unregister(indio_dev);
> > iio_map_array_unregister(indio_dev);
> >
> > - regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
> > + regmap_write(info->regmap, info->data->adc_en1, 0);
> >
> > if (info->data->adc_en2_mask)
> > - regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
> > + regmap_write(info->regmap, info->data->adc_en2, 0);
> > }
> >
> > static struct platform_driver axp20x_adc_driver = {
> > --
> > 2.34.1
> >
Thank you for the feedback,
Chris
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 5/8] iio: adc: axp20x_adc: add support for AXP717 ADC
2024-06-17 22:05 ` [PATCH 5/8] iio: adc: axp20x_adc: add support for AXP717 ADC Chris Morgan
2024-06-23 5:27 ` Chen-Yu Tsai
@ 2024-06-23 16:30 ` Jonathan Cameron
1 sibling, 0 replies; 28+ messages in thread
From: Jonathan Cameron @ 2024-06-23 16:30 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-sunxi, linux-pm, devicetree, linux-iio, broonie, lee,
samuel, jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
On Mon, 17 Jun 2024 17:05:32 -0500
Chris Morgan <macroalpha82@gmail.com> wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add support for the AXP717 ADC. The AXP717 differs from other ADCs
> in this series by utilizing a 14 bit ADC for all channels (a full 16
> bits with the first 2 digits reserved). It also differs by lacking a
> battery discharge current channel.
>
> Note that while the current charge channel itself is included in this
> driver for the AXP717 and listed in the datasheet, no scale or offset
> was given for this channel. For now no scale or offset is provided in
> this driver.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Hi Chris
A few minor comments inline,
Thanks,
Jonathan
> ---
> drivers/iio/adc/axp20x_adc.c | 167 +++++++++++++++++++++++++++++++++--
> 1 file changed, 160 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
> index d6c51b0f48e3..f35ba2c11e1b 100644
> --- a/drivers/iio/adc/axp20x_adc.c
> +++ b/drivers/iio/adc/axp20x_adc.c
> @@ -5,6 +5,7 @@
> * Quentin Schulz <quentin.schulz@free-electrons.com>
> */
>
> +#include <asm/unaligned.h>
> #include <linux/bitfield.h>
> #include <linux/completion.h>
> #include <linux/interrupt.h>
> @@ -27,6 +28,8 @@
>
> #define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0))
>
> +#define AXP717_ADC_EN1_MASK GENMASK(5, 0)
> +
> #define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0)
> #define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1)
>
> @@ -35,6 +38,11 @@
>
> #define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK)
>
> +#define AXP717_ADC_DATA_TS 0x00
> +#define AXP717_ADC_DATA_TEMP 0x01
> +
> +#define AXP717_ADC_DATA_MASK 0x3fff
GENMASK()
> +static int axp717_adc_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val)
> +{
> + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> + u8 bulk_reg[2];
> + int ret;
> +
> + /*
> + * A generic "ADC data" channel is used for TS, tdie, vmid,
> + * and vbackup. This channel must both first be enabled and
> + * also selected before it can be read.
> + */
> + switch (chan->channel) {
> + case AXP717_TS_IN:
> + regmap_write(info->regmap, AXP717_ADC_DATA_SEL,
> + AXP717_ADC_DATA_TS);
> + break;
> + case AXP717_DIE_TEMP_V:
> + regmap_write(info->regmap, AXP717_ADC_DATA_SEL,
> + AXP717_ADC_DATA_TEMP);
> + break;
> +
> + default:
> + break;
> + }
> +
> + /*
> + * All channels are 14 bits, with the first 2 bits on the high
> + * register reserved and the remaining bits as the ADC value.
> + */
> + ret = regmap_bulk_read(info->regmap, chan->address, bulk_reg, 2);
> + if (ret < 0)
> + return ret;
> +
> + *val = get_unaligned_be16(bulk_reg) & AXP717_ADC_DATA_MASK;
FIELD_GET() preferred as then I don't have to check if DATA_MASK includes
the 0th bit.
> + return IIO_VAL_INT;
> +}
> +
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 6/8] power: supply: axp20x_usb_power: Add support for AXP717
2024-06-17 22:05 [PATCH 0/8] Add Battery and USB Supply for AXP717 Chris Morgan
` (4 preceding siblings ...)
2024-06-17 22:05 ` [PATCH 5/8] iio: adc: axp20x_adc: add support for AXP717 ADC Chris Morgan
@ 2024-06-17 22:05 ` Chris Morgan
2024-06-22 23:49 ` Ryan Walklin
` (2 more replies)
2024-06-17 22:05 ` [PATCH 7/8] power: supply: axp20x_battery: add " Chris Morgan
2024-06-17 22:05 ` [PATCH 8/8] arm64: dts: allwinner: h700: Add charger for Anbernic RG35XX Chris Morgan
7 siblings, 3 replies; 28+ messages in thread
From: Chris Morgan @ 2024-06-17 22:05 UTC (permalink / raw)
To: linux-sunxi
Cc: linux-pm, devicetree, linux-iio, broonie, lee, samuel,
jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add support for the AXP717 PMIC. The AXP717 PMIC allows for detection
of USB type like the AXP813, but has little in common otherwise with
the other AXP PMICs. The USB charger is able to provide between
100000uA and 3250000uA of power, and can measure the VBUS input in mV
with up to 14 bits of precision.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/power/supply/axp20x_usb_power.c | 350 +++++++++++++++++++++---
1 file changed, 314 insertions(+), 36 deletions(-)
diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
index dae7e5cfc54e..c442dd005831 100644
--- a/drivers/power/supply/axp20x_usb_power.c
+++ b/drivers/power/supply/axp20x_usb_power.c
@@ -30,8 +30,13 @@
#define AXP20X_PWR_STATUS_VBUS_PRESENT BIT(5)
#define AXP20X_PWR_STATUS_VBUS_USED BIT(4)
+#define AXP717_PWR_STATUS_VBUS_GOOD BIT(5)
+
#define AXP20X_USB_STATUS_VBUS_VALID BIT(2)
+#define AXP717_PMU_FAULT_VBUS BIT(5)
+#define AXP717_PMU_FAULT_VSYS BIT(3)
+
#define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000)
#define AXP20X_VBUS_VHOLD_MASK GENMASK(5, 3)
#define AXP20X_VBUS_VHOLD_OFFSET 3
@@ -39,12 +44,20 @@
#define AXP20X_ADC_EN1_VBUS_CURR BIT(2)
#define AXP20X_ADC_EN1_VBUS_VOLT BIT(3)
+#define AXP717_INPUT_VOL_LIMIT_MASK GENMASK(3, 0)
+#define AXP717_INPUT_CUR_LIMIT_MASK GENMASK(5, 0)
+#define AXP717_ADC_DATA_MASK GENMASK(14, 0)
+
+#define AXP717_ADC_EN_VBUS_VOLT BIT(2)
+
/*
* Note do not raise the debounce time, we must report Vusb high within
* 100ms otherwise we get Vbus errors in musb.
*/
#define DEBOUNCE_TIME msecs_to_jiffies(50)
+struct axp20x_usb_power;
+
struct axp_data {
const struct power_supply_desc *power_desc;
const char * const *irq_names;
@@ -58,6 +71,10 @@ struct axp_data {
struct reg_field usb_bc_det_fld;
struct reg_field vbus_disable_bit;
bool vbus_needs_polling: 1;
+ void (*axp20x_read_vbus)(struct work_struct *work);
+ int (*axp20x_cfg_iio_chan)(struct platform_device *pdev,
+ struct axp20x_usb_power *power);
+ int (*axp20x_cfg_adc_reg)(struct axp20x_usb_power *power);
};
struct axp20x_usb_power {
@@ -74,6 +91,7 @@ struct axp20x_usb_power {
struct iio_channel *vbus_v;
struct iio_channel *vbus_i;
struct delayed_work vbus_detect;
+ int max_input_cur;
unsigned int old_status;
unsigned int online;
unsigned int num_irqs;
@@ -136,6 +154,24 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work)
mod_delayed_work(system_power_efficient_wq, &power->vbus_detect, DEBOUNCE_TIME);
}
+static void axp717_usb_power_poll_vbus(struct work_struct *work)
+{
+ struct axp20x_usb_power *power =
+ container_of(work, struct axp20x_usb_power, vbus_detect.work);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(power->regmap, AXP717_ON_INDICATE, &val);
+ if (ret)
+ return;
+
+ val &= AXP717_PWR_STATUS_VBUS_GOOD;
+ if (val != power->old_status)
+ power_supply_changed(power->supply);
+
+ power->old_status = val;
+}
+
static int axp20x_get_usb_type(struct axp20x_usb_power *power,
union power_supply_propval *val)
{
@@ -281,6 +317,84 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
return 0;
}
+static int axp717_usb_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp, union power_supply_propval *val)
+{
+ struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
+ unsigned int v;
+ int ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ ret = regmap_read(power->regmap, AXP717_PMU_FAULT_VBUS, &v);
+ if (ret)
+ return ret;
+
+ v &= (AXP717_PMU_FAULT_VBUS | AXP717_PMU_FAULT_VSYS);
+ if (v) {
+ val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ regmap_write(power->regmap, AXP717_PMU_FAULT_VBUS, v);
+ }
+
+ break;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ ret = regmap_read(power->regmap, AXP717_INPUT_CUR_LIMIT_CTRL, &v);
+ if (ret)
+ return ret;
+
+ /* 50ma step size with 100ma offset. */
+ v &= AXP717_INPUT_CUR_LIMIT_MASK;
+ val->intval = (v * 50000) + 100000;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ case POWER_SUPPLY_PROP_PRESENT:
+ ret = regmap_read(power->regmap, AXP717_ON_INDICATE, &v);
+ if (ret)
+ return ret;
+ val->intval = !!(v & AXP717_PWR_STATUS_VBUS_GOOD);
+ break;
+ case POWER_SUPPLY_PROP_USB_TYPE:
+ return axp20x_get_usb_type(power, val);
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ ret = regmap_read(power->regmap, AXP717_INPUT_VOL_LIMIT_CTRL, &v);
+ if (ret)
+ return ret;
+
+ /* 80mv step size with 3.88v offset. */
+ v &= AXP717_INPUT_VOL_LIMIT_MASK;
+ val->intval = (v * 80000) + 3880000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ if (IS_ENABLED(CONFIG_AXP20X_ADC)) {
+ ret = iio_read_channel_processed(power->vbus_v,
+ &val->intval);
+ if (ret)
+ return ret;
+
+ /*
+ * IIO framework gives mV but Power Supply framework
+ * gives uV.
+ */
+ val->intval *= 1000;
+ return 0;
+ }
+
+ ret = axp20x_read_variable_width(power->regmap,
+ AXP717_VBUS_V_H, 16);
+ if (ret < 0)
+ return ret;
+
+ val->intval = (ret % AXP717_ADC_DATA_MASK) * 1000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+
+}
+
static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power,
int intval)
{
@@ -307,16 +421,40 @@ static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power,
return -EINVAL;
}
+static int axp717_usb_power_set_voltage_min(struct axp20x_usb_power *power,
+ int intval)
+{
+ int val;
+
+ /* Minimum value of 3.88v and maximum of 5.08v. */
+ if (intval < 3880000 || intval > 5080000)
+ return -EINVAL;
+
+ /* step size of 80ma with 3.88v offset. */
+ val = (intval - 3880000) / 80000;
+ return regmap_update_bits(power->regmap,
+ AXP717_INPUT_VOL_LIMIT_CTRL,
+ AXP717_INPUT_VOL_LIMIT_MASK, val);
+}
+
static int axp20x_usb_power_set_input_current_limit(struct axp20x_usb_power *power,
int intval)
{
- int ret;
+ int ret, val;
unsigned int reg;
const unsigned int max = power->axp_data->curr_lim_table_size;
if (intval == -1)
return -EINVAL;
+ if (power->max_input_cur && (intval > power->max_input_cur)) {
+ val = power->max_input_cur;
+ dev_warn(power->dev,
+ "reqested current %d clamped to max current %d\n",
+ intval, power->max_input_cur);
+ } else
+ val = intval;
+
/*
* BC1.2 detection can cause a race condition if we try to set a current
* limit while it's in progress. When it finishes it will overwrite the
@@ -331,15 +469,39 @@ static int axp20x_usb_power_set_input_current_limit(struct axp20x_usb_power *pow
}
for (reg = max - 1; reg > 0; reg--)
- if (power->axp_data->curr_lim_table[reg] <= intval)
+ if (power->axp_data->curr_lim_table[reg] <= val)
break;
dev_dbg(power->dev, "setting input current limit reg to %d (%d uA), requested %d uA",
- reg, power->axp_data->curr_lim_table[reg], intval);
+ reg, power->axp_data->curr_lim_table[reg], val);
return regmap_field_write(power->curr_lim_fld, reg);
}
+static int axp717_usb_power_set_input_current_limit(struct axp20x_usb_power *power,
+ int intval)
+{
+ int val, tmp;
+
+ /* Minimum value of 100mA and maximum value of 3.25A*/
+ if (intval < 100000 || intval > 3250000)
+ return -EINVAL;
+
+ if (power->max_input_cur && (intval > power->max_input_cur)) {
+ val = power->max_input_cur;
+ dev_warn(power->dev,
+ "reqested current %d clamped to max current %d\n",
+ intval, power->max_input_cur);
+ } else
+ val = intval;
+
+ /* Minimum value of 100mA with step size of 50mA. */
+ tmp = (val - 100000) / 50000;
+ return regmap_update_bits(power->regmap,
+ AXP717_INPUT_CUR_LIMIT_CTRL,
+ AXP717_INPUT_CUR_LIMIT_MASK, tmp);
+}
+
static int axp20x_usb_power_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
@@ -366,6 +528,26 @@ static int axp20x_usb_power_set_property(struct power_supply *psy,
return -EINVAL;
}
+static int axp717_usb_power_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ return axp717_usb_power_set_input_current_limit(power, val->intval);
+
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+ return axp717_usb_power_set_voltage_min(power, val->intval);
+
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
static int axp20x_usb_power_prop_writeable(struct power_supply *psy,
enum power_supply_property psp)
{
@@ -385,6 +567,64 @@ static int axp20x_usb_power_prop_writeable(struct power_supply *psy,
psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
}
+static int axp717_usb_power_prop_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN ||
+ psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
+}
+
+static int axp20x_configure_iio_channels(struct platform_device *pdev,
+ struct axp20x_usb_power *power)
+{
+ power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v");
+ if (IS_ERR(power->vbus_v)) {
+ if (PTR_ERR(power->vbus_v) == -ENODEV)
+ return -EPROBE_DEFER;
+ return PTR_ERR(power->vbus_v);
+ }
+
+ power->vbus_i = devm_iio_channel_get(&pdev->dev, "vbus_i");
+ if (IS_ERR(power->vbus_i)) {
+ if (PTR_ERR(power->vbus_i) == -ENODEV)
+ return -EPROBE_DEFER;
+ return PTR_ERR(power->vbus_i);
+ }
+
+ return 0;
+}
+
+static int axp717_configure_iio_channels(struct platform_device *pdev,
+ struct axp20x_usb_power *power)
+{
+ power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v");
+ if (IS_ERR(power->vbus_v)) {
+ if (PTR_ERR(power->vbus_v) == -ENODEV)
+ return -EPROBE_DEFER;
+ return PTR_ERR(power->vbus_v);
+ }
+
+ return 0;
+}
+
+static int axp20x_configure_adc_registers(struct axp20x_usb_power *power)
+{
+ /* Enable vbus voltage and current measurement */
+ return regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
+ AXP20X_ADC_EN1_VBUS_CURR |
+ AXP20X_ADC_EN1_VBUS_VOLT,
+ AXP20X_ADC_EN1_VBUS_CURR |
+ AXP20X_ADC_EN1_VBUS_VOLT);
+}
+
+static int axp717_configure_adc_registers(struct axp20x_usb_power *power)
+{
+ /* Enable vbus voltage measurement */
+ return regmap_update_bits(power->regmap, AXP717_ADC_CH_EN_CONTROL,
+ AXP717_ADC_EN_VBUS_VOLT,
+ AXP717_ADC_EN_VBUS_VOLT);
+}
+
static enum power_supply_property axp20x_usb_power_properties[] = {
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
@@ -403,6 +643,16 @@ static enum power_supply_property axp22x_usb_power_properties[] = {
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
};
+static enum power_supply_property axp717_usb_power_properties[] = {
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_USB_TYPE,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
static enum power_supply_property axp813_usb_power_properties[] = {
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
@@ -439,6 +689,18 @@ static const struct power_supply_desc axp22x_usb_power_desc = {
.set_property = axp20x_usb_power_set_property,
};
+static const struct power_supply_desc axp717_usb_power_desc = {
+ .name = "axp20x-usb",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .properties = axp717_usb_power_properties,
+ .num_properties = ARRAY_SIZE(axp717_usb_power_properties),
+ .property_is_writeable = axp717_usb_power_prop_writeable,
+ .get_property = axp717_usb_power_get_property,
+ .set_property = axp717_usb_power_set_property,
+ .usb_types = axp813_usb_types,
+ .num_usb_types = ARRAY_SIZE(axp813_usb_types),
+};
+
static const struct power_supply_desc axp813_usb_power_desc = {
.name = "axp20x-usb",
.type = POWER_SUPPLY_TYPE_USB,
@@ -505,6 +767,9 @@ static const struct axp_data axp192_data = {
.curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
.vbus_valid_bit = REG_FIELD(AXP192_USB_OTG_STATUS, 2, 2),
.vbus_mon_bit = REG_FIELD(AXP20X_VBUS_MON, 3, 3),
+ .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
+ .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
+ .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
};
static const struct axp_data axp202_data = {
@@ -516,6 +781,9 @@ static const struct axp_data axp202_data = {
.curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
.vbus_valid_bit = REG_FIELD(AXP20X_USB_OTG_STATUS, 2, 2),
.vbus_mon_bit = REG_FIELD(AXP20X_VBUS_MON, 3, 3),
+ .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
+ .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
+ .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
};
static const struct axp_data axp221_data = {
@@ -526,6 +794,9 @@ static const struct axp_data axp221_data = {
.curr_lim_table_size = ARRAY_SIZE(axp221_usb_curr_lim_table),
.curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
.vbus_needs_polling = true,
+ .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
+ .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
+ .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
};
static const struct axp_data axp223_data = {
@@ -536,6 +807,23 @@ static const struct axp_data axp223_data = {
.curr_lim_table_size = ARRAY_SIZE(axp20x_usb_curr_lim_table),
.curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
.vbus_needs_polling = true,
+ .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
+ .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
+ .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
+};
+
+static const struct axp_data axp717_data = {
+ .power_desc = &axp717_usb_power_desc,
+ .irq_names = axp22x_irq_names,
+ .num_irq_names = ARRAY_SIZE(axp22x_irq_names),
+ .curr_lim_fld = REG_FIELD(AXP717_INPUT_CUR_LIMIT_CTRL, 0, 5),
+ .usb_bc_en_bit = REG_FIELD(AXP717_MODULE_EN_CONTROL_1, 4, 4),
+ .usb_bc_det_fld = REG_FIELD(AXP717_BC_DETECT, 5, 7),
+ .vbus_mon_bit = REG_FIELD(AXP717_ADC_CH_EN_CONTROL, 2, 2),
+ .vbus_needs_polling = false,
+ .axp20x_read_vbus = &axp717_usb_power_poll_vbus,
+ .axp20x_cfg_iio_chan = axp717_configure_iio_channels,
+ .axp20x_cfg_adc_reg = axp717_configure_adc_registers,
};
static const struct axp_data axp813_data = {
@@ -549,6 +837,9 @@ static const struct axp_data axp813_data = {
.usb_bc_det_fld = REG_FIELD(AXP288_BC_DET_STAT, 5, 7),
.vbus_disable_bit = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 7, 7),
.vbus_needs_polling = true,
+ .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
+ .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
+ .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
};
#ifdef CONFIG_PM_SLEEP
@@ -590,36 +881,6 @@ static int axp20x_usb_power_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(axp20x_usb_power_pm_ops, axp20x_usb_power_suspend,
axp20x_usb_power_resume);
-static int configure_iio_channels(struct platform_device *pdev,
- struct axp20x_usb_power *power)
-{
- power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v");
- if (IS_ERR(power->vbus_v)) {
- if (PTR_ERR(power->vbus_v) == -ENODEV)
- return -EPROBE_DEFER;
- return PTR_ERR(power->vbus_v);
- }
-
- power->vbus_i = devm_iio_channel_get(&pdev->dev, "vbus_i");
- if (IS_ERR(power->vbus_i)) {
- if (PTR_ERR(power->vbus_i) == -ENODEV)
- return -EPROBE_DEFER;
- return PTR_ERR(power->vbus_i);
- }
-
- return 0;
-}
-
-static int configure_adc_registers(struct axp20x_usb_power *power)
-{
- /* Enable vbus voltage and current measurement */
- return regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
- AXP20X_ADC_EN1_VBUS_CURR |
- AXP20X_ADC_EN1_VBUS_VOLT,
- AXP20X_ADC_EN1_VBUS_CURR |
- AXP20X_ADC_EN1_VBUS_VOLT);
-}
-
static int axp20x_regmap_field_alloc_optional(struct device *dev,
struct regmap *regmap,
struct reg_field fdesc,
@@ -640,6 +901,18 @@ static int axp20x_regmap_field_alloc_optional(struct device *dev,
return 0;
}
+/* Optionally allow users to specify a maximum charging current. */
+static void axp20x_charger_parse_dt_property(struct device *dev,
+ struct axp20x_usb_power *power)
+{
+ int ret;
+
+ ret = device_property_read_u32(dev, "input-current-limit-microamp",
+ &power->max_input_cur);
+ if (ret)
+ dev_dbg(dev, "%s() no input-current-limit specified\n", __func__);
+}
+
static int axp20x_usb_power_probe(struct platform_device *pdev)
{
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
@@ -676,6 +949,8 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
if (IS_ERR(power->curr_lim_fld))
return PTR_ERR(power->curr_lim_fld);
+ axp20x_charger_parse_dt_property(&pdev->dev, power);
+
ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
axp_data->vbus_valid_bit,
&power->vbus_valid_bit);
@@ -707,7 +982,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
return ret;
ret = devm_delayed_work_autocancel(&pdev->dev, &power->vbus_detect,
- axp20x_usb_power_poll_vbus);
+ axp_data->axp20x_read_vbus);
if (ret)
return ret;
@@ -718,9 +993,9 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
return ret;
if (IS_ENABLED(CONFIG_AXP20X_ADC))
- ret = configure_iio_channels(pdev, power);
+ ret = axp_data->axp20x_cfg_iio_chan(pdev, power);
else
- ret = configure_adc_registers(power);
+ ret = axp_data->axp20x_cfg_adc_reg(power);
if (ret)
return ret;
@@ -778,6 +1053,9 @@ static const struct of_device_id axp20x_usb_power_match[] = {
}, {
.compatible = "x-powers,axp223-usb-power-supply",
.data = &axp223_data,
+ }, {
+ .compatible = "x-powers,axp717-usb-power-supply",
+ .data = &axp717_data,
}, {
.compatible = "x-powers,axp813-usb-power-supply",
.data = &axp813_data,
--
2.34.1
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH 6/8] power: supply: axp20x_usb_power: Add support for AXP717
2024-06-17 22:05 ` [PATCH 6/8] power: supply: axp20x_usb_power: Add support for AXP717 Chris Morgan
@ 2024-06-22 23:49 ` Ryan Walklin
2024-06-23 5:42 ` Chen-Yu Tsai
2024-06-24 18:00 ` Philippe Simons
2 siblings, 0 replies; 28+ messages in thread
From: Ryan Walklin @ 2024-06-22 23:49 UTC (permalink / raw)
To: Chris Morgan, linux-sunxi
Cc: linux-pm, devicetree, linux-iio, Mark Brown, Lee Jones,
Samuel Holland, Jernej Skrabec, sre, Chen-Yu Tsai, Conor Dooley,
Krzysztof Kozlowski, Rob Herring, lars, Chris Morgan
On Tue, 18 Jun 2024, at 10:05 AM, Chris Morgan wrote:
Thanks Chris!
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add support for the AXP717 PMIC. The AXP717 PMIC allows for detection
> of USB type like the AXP813, but has little in common otherwise with
> the other AXP PMICs. The USB charger is able to provide between
> 100000uA and 3250000uA of power, and can measure the VBUS input in mV
> with up to 14 bits of precision.
>
Can also confirm working on an H700 device with the AXP717, with detection of supplied current, voltage and presence.
Tested-by: Ryan Walklin <ryan@testtoast.com>
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 6/8] power: supply: axp20x_usb_power: Add support for AXP717
2024-06-17 22:05 ` [PATCH 6/8] power: supply: axp20x_usb_power: Add support for AXP717 Chris Morgan
2024-06-22 23:49 ` Ryan Walklin
@ 2024-06-23 5:42 ` Chen-Yu Tsai
2024-07-30 19:14 ` Chris Morgan
2024-06-24 18:00 ` Philippe Simons
2 siblings, 1 reply; 28+ messages in thread
From: Chen-Yu Tsai @ 2024-06-23 5:42 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-sunxi, linux-pm, devicetree, linux-iio, broonie, lee,
samuel, jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
On Mon, Jun 17, 2024 at 05:05:33PM -0500, Chris Morgan wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add support for the AXP717 PMIC. The AXP717 PMIC allows for detection
> of USB type like the AXP813, but has little in common otherwise with
> the other AXP PMICs. The USB charger is able to provide between
> 100000uA and 3250000uA of power, and can measure the VBUS input in mV
> with up to 14 bits of precision.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
> ---
> drivers/power/supply/axp20x_usb_power.c | 350 +++++++++++++++++++++---
> 1 file changed, 314 insertions(+), 36 deletions(-)
>
> diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
> index dae7e5cfc54e..c442dd005831 100644
> --- a/drivers/power/supply/axp20x_usb_power.c
> +++ b/drivers/power/supply/axp20x_usb_power.c
> @@ -30,8 +30,13 @@
> #define AXP20X_PWR_STATUS_VBUS_PRESENT BIT(5)
> #define AXP20X_PWR_STATUS_VBUS_USED BIT(4)
>
> +#define AXP717_PWR_STATUS_VBUS_GOOD BIT(5)
> +
> #define AXP20X_USB_STATUS_VBUS_VALID BIT(2)
>
> +#define AXP717_PMU_FAULT_VBUS BIT(5)
> +#define AXP717_PMU_FAULT_VSYS BIT(3)
> +
> #define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000)
> #define AXP20X_VBUS_VHOLD_MASK GENMASK(5, 3)
> #define AXP20X_VBUS_VHOLD_OFFSET 3
> @@ -39,12 +44,20 @@
> #define AXP20X_ADC_EN1_VBUS_CURR BIT(2)
> #define AXP20X_ADC_EN1_VBUS_VOLT BIT(3)
>
> +#define AXP717_INPUT_VOL_LIMIT_MASK GENMASK(3, 0)
> +#define AXP717_INPUT_CUR_LIMIT_MASK GENMASK(5, 0)
> +#define AXP717_ADC_DATA_MASK GENMASK(14, 0)
> +
> +#define AXP717_ADC_EN_VBUS_VOLT BIT(2)
> +
> /*
> * Note do not raise the debounce time, we must report Vusb high within
> * 100ms otherwise we get Vbus errors in musb.
> */
> #define DEBOUNCE_TIME msecs_to_jiffies(50)
>
> +struct axp20x_usb_power;
> +
> struct axp_data {
> const struct power_supply_desc *power_desc;
> const char * const *irq_names;
> @@ -58,6 +71,10 @@ struct axp_data {
> struct reg_field usb_bc_det_fld;
> struct reg_field vbus_disable_bit;
> bool vbus_needs_polling: 1;
> + void (*axp20x_read_vbus)(struct work_struct *work);
> + int (*axp20x_cfg_iio_chan)(struct platform_device *pdev,
> + struct axp20x_usb_power *power);
> + int (*axp20x_cfg_adc_reg)(struct axp20x_usb_power *power);
Please split out changes generalizing the driver into a separate patch.
> };
>
> struct axp20x_usb_power {
> @@ -74,6 +91,7 @@ struct axp20x_usb_power {
> struct iio_channel *vbus_v;
> struct iio_channel *vbus_i;
> struct delayed_work vbus_detect;
> + int max_input_cur;
> unsigned int old_status;
> unsigned int online;
> unsigned int num_irqs;
> @@ -136,6 +154,24 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work)
> mod_delayed_work(system_power_efficient_wq, &power->vbus_detect, DEBOUNCE_TIME);
> }
>
> +static void axp717_usb_power_poll_vbus(struct work_struct *work)
> +{
> + struct axp20x_usb_power *power =
> + container_of(work, struct axp20x_usb_power, vbus_detect.work);
> + unsigned int val;
> + int ret;
> +
> + ret = regmap_read(power->regmap, AXP717_ON_INDICATE, &val);
> + if (ret)
> + return;
> +
> + val &= AXP717_PWR_STATUS_VBUS_GOOD;
> + if (val != power->old_status)
> + power_supply_changed(power->supply);
> +
> + power->old_status = val;
> +}
> +
> static int axp20x_get_usb_type(struct axp20x_usb_power *power,
> union power_supply_propval *val)
> {
> @@ -281,6 +317,84 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
> return 0;
> }
>
> +static int axp717_usb_power_get_property(struct power_supply *psy,
> + enum power_supply_property psp, union power_supply_propval *val)
> +{
> + struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
> + unsigned int v;
> + int ret;
> +
> + switch (psp) {
> + case POWER_SUPPLY_PROP_HEALTH:
> + val->intval = POWER_SUPPLY_HEALTH_GOOD;
> + ret = regmap_read(power->regmap, AXP717_PMU_FAULT_VBUS, &v);
> + if (ret)
> + return ret;
> +
> + v &= (AXP717_PMU_FAULT_VBUS | AXP717_PMU_FAULT_VSYS);
> + if (v) {
> + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
> + regmap_write(power->regmap, AXP717_PMU_FAULT_VBUS, v);
> + }
> +
Return POWER_SUPPLY_HEALTH_UNKNOWN if vbus is not present?
> + break;
> + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
> + ret = regmap_read(power->regmap, AXP717_INPUT_CUR_LIMIT_CTRL, &v);
> + if (ret)
> + return ret;
> +
> + /* 50ma step size with 100ma offset. */
> + v &= AXP717_INPUT_CUR_LIMIT_MASK;
> + val->intval = (v * 50000) + 100000;
> + break;
> + case POWER_SUPPLY_PROP_ONLINE:
Probably use VINDPM status for this?
> + case POWER_SUPPLY_PROP_PRESENT:
> + ret = regmap_read(power->regmap, AXP717_ON_INDICATE, &v);
> + if (ret)
> + return ret;
> + val->intval = !!(v & AXP717_PWR_STATUS_VBUS_GOOD);
> + break;
> + case POWER_SUPPLY_PROP_USB_TYPE:
> + return axp20x_get_usb_type(power, val);
> + case POWER_SUPPLY_PROP_VOLTAGE_MIN:
> + ret = regmap_read(power->regmap, AXP717_INPUT_VOL_LIMIT_CTRL, &v);
> + if (ret)
> + return ret;
> +
> + /* 80mv step size with 3.88v offset. */
> + v &= AXP717_INPUT_VOL_LIMIT_MASK;
> + val->intval = (v * 80000) + 3880000;
> + break;
> + case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> + if (IS_ENABLED(CONFIG_AXP20X_ADC)) {
> + ret = iio_read_channel_processed(power->vbus_v,
> + &val->intval);
> + if (ret)
> + return ret;
> +
> + /*
> + * IIO framework gives mV but Power Supply framework
> + * gives uV.
> + */
> + val->intval *= 1000;
> + return 0;
> + }
> +
> + ret = axp20x_read_variable_width(power->regmap,
> + AXP717_VBUS_V_H, 16);
> + if (ret < 0)
> + return ret;
> +
> + val->intval = (ret % AXP717_ADC_DATA_MASK) * 1000;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +
> +}
> +
> static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power,
> int intval)
> {
> @@ -307,16 +421,40 @@ static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power,
> return -EINVAL;
> }
>
> +static int axp717_usb_power_set_voltage_min(struct axp20x_usb_power *power,
> + int intval)
> +{
> + int val;
> +
> + /* Minimum value of 3.88v and maximum of 5.08v. */
> + if (intval < 3880000 || intval > 5080000)
> + return -EINVAL;
> +
> + /* step size of 80ma with 3.88v offset. */
> + val = (intval - 3880000) / 80000;
> + return regmap_update_bits(power->regmap,
> + AXP717_INPUT_VOL_LIMIT_CTRL,
> + AXP717_INPUT_VOL_LIMIT_MASK, val);
> +}
> +
> static int axp20x_usb_power_set_input_current_limit(struct axp20x_usb_power *power,
> int intval)
> {
> - int ret;
> + int ret, val;
> unsigned int reg;
> const unsigned int max = power->axp_data->curr_lim_table_size;
>
> if (intval == -1)
> return -EINVAL;
>
> + if (power->max_input_cur && (intval > power->max_input_cur)) {
> + val = power->max_input_cur;
> + dev_warn(power->dev,
> + "reqested current %d clamped to max current %d\n",
> + intval, power->max_input_cur);
> + } else
> + val = intval;
> +
Just overwrite intval. One less variable and less changes below.
> /*
> * BC1.2 detection can cause a race condition if we try to set a current
> * limit while it's in progress. When it finishes it will overwrite the
> @@ -331,15 +469,39 @@ static int axp20x_usb_power_set_input_current_limit(struct axp20x_usb_power *pow
> }
>
> for (reg = max - 1; reg > 0; reg--)
> - if (power->axp_data->curr_lim_table[reg] <= intval)
> + if (power->axp_data->curr_lim_table[reg] <= val)
> break;
>
> dev_dbg(power->dev, "setting input current limit reg to %d (%d uA), requested %d uA",
> - reg, power->axp_data->curr_lim_table[reg], intval);
> + reg, power->axp_data->curr_lim_table[reg], val);
>
> return regmap_field_write(power->curr_lim_fld, reg);
> }
>
> +static int axp717_usb_power_set_input_current_limit(struct axp20x_usb_power *power,
> + int intval)
> +{
> + int val, tmp;
> +
> + /* Minimum value of 100mA and maximum value of 3.25A*/
> + if (intval < 100000 || intval > 3250000)
> + return -EINVAL;
> +
> + if (power->max_input_cur && (intval > power->max_input_cur)) {
> + val = power->max_input_cur;
> + dev_warn(power->dev,
> + "reqested current %d clamped to max current %d\n",
> + intval, power->max_input_cur);
> + } else
> + val = intval;
Same comment: just overwrite intval.
> +
> + /* Minimum value of 100mA with step size of 50mA. */
> + tmp = (val - 100000) / 50000;
> + return regmap_update_bits(power->regmap,
> + AXP717_INPUT_CUR_LIMIT_CTRL,
> + AXP717_INPUT_CUR_LIMIT_MASK, tmp);
> +}
> +
> static int axp20x_usb_power_set_property(struct power_supply *psy,
> enum power_supply_property psp,
> const union power_supply_propval *val)
> @@ -366,6 +528,26 @@ static int axp20x_usb_power_set_property(struct power_supply *psy,
> return -EINVAL;
> }
>
> +static int axp717_usb_power_set_property(struct power_supply *psy,
> + enum power_supply_property psp,
> + const union power_supply_propval *val)
> +{
> + struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
> +
> + switch (psp) {
> + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
> + return axp717_usb_power_set_input_current_limit(power, val->intval);
> +
> + case POWER_SUPPLY_PROP_VOLTAGE_MIN:
> + return axp717_usb_power_set_voltage_min(power, val->intval);
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
This is redundant.
> +}
> +
> static int axp20x_usb_power_prop_writeable(struct power_supply *psy,
> enum power_supply_property psp)
> {
> @@ -385,6 +567,64 @@ static int axp20x_usb_power_prop_writeable(struct power_supply *psy,
> psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
> }
>
> +static int axp717_usb_power_prop_writeable(struct power_supply *psy,
> + enum power_supply_property psp)
> +{
> + return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN ||
> + psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
> +}
> +
> +static int axp20x_configure_iio_channels(struct platform_device *pdev,
> + struct axp20x_usb_power *power)
> +{
> + power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v");
> + if (IS_ERR(power->vbus_v)) {
> + if (PTR_ERR(power->vbus_v) == -ENODEV)
> + return -EPROBE_DEFER;
> + return PTR_ERR(power->vbus_v);
> + }
> +
> + power->vbus_i = devm_iio_channel_get(&pdev->dev, "vbus_i");
> + if (IS_ERR(power->vbus_i)) {
> + if (PTR_ERR(power->vbus_i) == -ENODEV)
> + return -EPROBE_DEFER;
> + return PTR_ERR(power->vbus_i);
> + }
> +
> + return 0;
> +}
> +
> +static int axp717_configure_iio_channels(struct platform_device *pdev,
> + struct axp20x_usb_power *power)
> +{
> + power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v");
> + if (IS_ERR(power->vbus_v)) {
> + if (PTR_ERR(power->vbus_v) == -ENODEV)
> + return -EPROBE_DEFER;
> + return PTR_ERR(power->vbus_v);
> + }
> +
> + return 0;
> +}
> +
> +static int axp20x_configure_adc_registers(struct axp20x_usb_power *power)
> +{
> + /* Enable vbus voltage and current measurement */
> + return regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
> + AXP20X_ADC_EN1_VBUS_CURR |
> + AXP20X_ADC_EN1_VBUS_VOLT,
> + AXP20X_ADC_EN1_VBUS_CURR |
> + AXP20X_ADC_EN1_VBUS_VOLT);
> +}
> +
> +static int axp717_configure_adc_registers(struct axp20x_usb_power *power)
> +{
> + /* Enable vbus voltage measurement */
> + return regmap_update_bits(power->regmap, AXP717_ADC_CH_EN_CONTROL,
> + AXP717_ADC_EN_VBUS_VOLT,
> + AXP717_ADC_EN_VBUS_VOLT);
> +}
> +
> static enum power_supply_property axp20x_usb_power_properties[] = {
> POWER_SUPPLY_PROP_HEALTH,
> POWER_SUPPLY_PROP_PRESENT,
> @@ -403,6 +643,16 @@ static enum power_supply_property axp22x_usb_power_properties[] = {
> POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
> };
>
> +static enum power_supply_property axp717_usb_power_properties[] = {
> + POWER_SUPPLY_PROP_HEALTH,
> + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
> + POWER_SUPPLY_PROP_ONLINE,
> + POWER_SUPPLY_PROP_PRESENT,
> + POWER_SUPPLY_PROP_USB_TYPE,
> + POWER_SUPPLY_PROP_VOLTAGE_MIN,
> + POWER_SUPPLY_PROP_VOLTAGE_NOW,
> +};
> +
> static enum power_supply_property axp813_usb_power_properties[] = {
> POWER_SUPPLY_PROP_HEALTH,
> POWER_SUPPLY_PROP_PRESENT,
> @@ -439,6 +689,18 @@ static const struct power_supply_desc axp22x_usb_power_desc = {
> .set_property = axp20x_usb_power_set_property,
> };
>
> +static const struct power_supply_desc axp717_usb_power_desc = {
> + .name = "axp20x-usb",
> + .type = POWER_SUPPLY_TYPE_USB,
> + .properties = axp717_usb_power_properties,
> + .num_properties = ARRAY_SIZE(axp717_usb_power_properties),
> + .property_is_writeable = axp717_usb_power_prop_writeable,
> + .get_property = axp717_usb_power_get_property,
> + .set_property = axp717_usb_power_set_property,
> + .usb_types = axp813_usb_types,
> + .num_usb_types = ARRAY_SIZE(axp813_usb_types),
> +};
> +
> static const struct power_supply_desc axp813_usb_power_desc = {
> .name = "axp20x-usb",
> .type = POWER_SUPPLY_TYPE_USB,
> @@ -505,6 +767,9 @@ static const struct axp_data axp192_data = {
> .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
> .vbus_valid_bit = REG_FIELD(AXP192_USB_OTG_STATUS, 2, 2),
> .vbus_mon_bit = REG_FIELD(AXP20X_VBUS_MON, 3, 3),
> + .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
> + .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
> + .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
> };
>
> static const struct axp_data axp202_data = {
> @@ -516,6 +781,9 @@ static const struct axp_data axp202_data = {
> .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
> .vbus_valid_bit = REG_FIELD(AXP20X_USB_OTG_STATUS, 2, 2),
> .vbus_mon_bit = REG_FIELD(AXP20X_VBUS_MON, 3, 3),
> + .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
> + .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
> + .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
> };
>
> static const struct axp_data axp221_data = {
> @@ -526,6 +794,9 @@ static const struct axp_data axp221_data = {
> .curr_lim_table_size = ARRAY_SIZE(axp221_usb_curr_lim_table),
> .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
> .vbus_needs_polling = true,
> + .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
> + .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
> + .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
> };
>
> static const struct axp_data axp223_data = {
> @@ -536,6 +807,23 @@ static const struct axp_data axp223_data = {
> .curr_lim_table_size = ARRAY_SIZE(axp20x_usb_curr_lim_table),
> .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
> .vbus_needs_polling = true,
> + .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
> + .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
> + .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
> +};
> +
> +static const struct axp_data axp717_data = {
> + .power_desc = &axp717_usb_power_desc,
> + .irq_names = axp22x_irq_names,
> + .num_irq_names = ARRAY_SIZE(axp22x_irq_names),
> + .curr_lim_fld = REG_FIELD(AXP717_INPUT_CUR_LIMIT_CTRL, 0, 5),
> + .usb_bc_en_bit = REG_FIELD(AXP717_MODULE_EN_CONTROL_1, 4, 4),
> + .usb_bc_det_fld = REG_FIELD(AXP717_BC_DETECT, 5, 7),
> + .vbus_mon_bit = REG_FIELD(AXP717_ADC_CH_EN_CONTROL, 2, 2),
> + .vbus_needs_polling = false,
> + .axp20x_read_vbus = &axp717_usb_power_poll_vbus,
> + .axp20x_cfg_iio_chan = axp717_configure_iio_channels,
> + .axp20x_cfg_adc_reg = axp717_configure_adc_registers,
> };
>
> static const struct axp_data axp813_data = {
> @@ -549,6 +837,9 @@ static const struct axp_data axp813_data = {
> .usb_bc_det_fld = REG_FIELD(AXP288_BC_DET_STAT, 5, 7),
> .vbus_disable_bit = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 7, 7),
> .vbus_needs_polling = true,
> + .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
> + .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
> + .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
> };
>
> #ifdef CONFIG_PM_SLEEP
> @@ -590,36 +881,6 @@ static int axp20x_usb_power_resume(struct device *dev)
> static SIMPLE_DEV_PM_OPS(axp20x_usb_power_pm_ops, axp20x_usb_power_suspend,
> axp20x_usb_power_resume);
>
> -static int configure_iio_channels(struct platform_device *pdev,
> - struct axp20x_usb_power *power)
> -{
> - power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v");
> - if (IS_ERR(power->vbus_v)) {
> - if (PTR_ERR(power->vbus_v) == -ENODEV)
> - return -EPROBE_DEFER;
> - return PTR_ERR(power->vbus_v);
> - }
> -
> - power->vbus_i = devm_iio_channel_get(&pdev->dev, "vbus_i");
> - if (IS_ERR(power->vbus_i)) {
> - if (PTR_ERR(power->vbus_i) == -ENODEV)
> - return -EPROBE_DEFER;
> - return PTR_ERR(power->vbus_i);
> - }
> -
> - return 0;
> -}
> -
> -static int configure_adc_registers(struct axp20x_usb_power *power)
> -{
> - /* Enable vbus voltage and current measurement */
> - return regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
> - AXP20X_ADC_EN1_VBUS_CURR |
> - AXP20X_ADC_EN1_VBUS_VOLT,
> - AXP20X_ADC_EN1_VBUS_CURR |
> - AXP20X_ADC_EN1_VBUS_VOLT);
> -}
> -
> static int axp20x_regmap_field_alloc_optional(struct device *dev,
> struct regmap *regmap,
> struct reg_field fdesc,
> @@ -640,6 +901,18 @@ static int axp20x_regmap_field_alloc_optional(struct device *dev,
> return 0;
> }
>
> +/* Optionally allow users to specify a maximum charging current. */
> +static void axp20x_charger_parse_dt_property(struct device *dev,
axp20x_usb_power_parse_dt().
Regards
ChenYu
> + struct axp20x_usb_power *power)
> +{
> + int ret;
> +
> + ret = device_property_read_u32(dev, "input-current-limit-microamp",
> + &power->max_input_cur);
> + if (ret)
> + dev_dbg(dev, "%s() no input-current-limit specified\n", __func__);
> +}
> +
> static int axp20x_usb_power_probe(struct platform_device *pdev)
> {
> struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
> @@ -676,6 +949,8 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
> if (IS_ERR(power->curr_lim_fld))
> return PTR_ERR(power->curr_lim_fld);
>
> + axp20x_charger_parse_dt_property(&pdev->dev, power);
> +
> ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
> axp_data->vbus_valid_bit,
> &power->vbus_valid_bit);
> @@ -707,7 +982,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
> return ret;
>
> ret = devm_delayed_work_autocancel(&pdev->dev, &power->vbus_detect,
> - axp20x_usb_power_poll_vbus);
> + axp_data->axp20x_read_vbus);
> if (ret)
> return ret;
>
> @@ -718,9 +993,9 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
> return ret;
>
> if (IS_ENABLED(CONFIG_AXP20X_ADC))
> - ret = configure_iio_channels(pdev, power);
> + ret = axp_data->axp20x_cfg_iio_chan(pdev, power);
> else
> - ret = configure_adc_registers(power);
> + ret = axp_data->axp20x_cfg_adc_reg(power);
>
> if (ret)
> return ret;
> @@ -778,6 +1053,9 @@ static const struct of_device_id axp20x_usb_power_match[] = {
> }, {
> .compatible = "x-powers,axp223-usb-power-supply",
> .data = &axp223_data,
> + }, {
> + .compatible = "x-powers,axp717-usb-power-supply",
> + .data = &axp717_data,
> }, {
> .compatible = "x-powers,axp813-usb-power-supply",
> .data = &axp813_data,
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [PATCH 6/8] power: supply: axp20x_usb_power: Add support for AXP717
2024-06-23 5:42 ` Chen-Yu Tsai
@ 2024-07-30 19:14 ` Chris Morgan
0 siblings, 0 replies; 28+ messages in thread
From: Chris Morgan @ 2024-07-30 19:14 UTC (permalink / raw)
To: Chen-Yu Tsai
Cc: Chris Morgan, linux-sunxi, linux-pm, devicetree, linux-iio,
broonie, lee, samuel, jernej.skrabec, sre, wens, conor+dt,
krzk+dt, robh, lars
On Sun, Jun 23, 2024 at 01:42:16PM +0800, Chen-Yu Tsai wrote:
> On Mon, Jun 17, 2024 at 05:05:33PM -0500, Chris Morgan wrote:
> > From: Chris Morgan <macromorgan@hotmail.com>
> >
> > Add support for the AXP717 PMIC. The AXP717 PMIC allows for detection
> > of USB type like the AXP813, but has little in common otherwise with
> > the other AXP PMICs. The USB charger is able to provide between
> > 100000uA and 3250000uA of power, and can measure the VBUS input in mV
> > with up to 14 bits of precision.
> >
> > Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
> > ---
> > drivers/power/supply/axp20x_usb_power.c | 350 +++++++++++++++++++++---
> > 1 file changed, 314 insertions(+), 36 deletions(-)
> >
> > diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
> > index dae7e5cfc54e..c442dd005831 100644
> > --- a/drivers/power/supply/axp20x_usb_power.c
> > +++ b/drivers/power/supply/axp20x_usb_power.c
> > @@ -30,8 +30,13 @@
> > #define AXP20X_PWR_STATUS_VBUS_PRESENT BIT(5)
> > #define AXP20X_PWR_STATUS_VBUS_USED BIT(4)
> >
> > +#define AXP717_PWR_STATUS_VBUS_GOOD BIT(5)
> > +
> > #define AXP20X_USB_STATUS_VBUS_VALID BIT(2)
> >
> > +#define AXP717_PMU_FAULT_VBUS BIT(5)
> > +#define AXP717_PMU_FAULT_VSYS BIT(3)
> > +
> > #define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000)
> > #define AXP20X_VBUS_VHOLD_MASK GENMASK(5, 3)
> > #define AXP20X_VBUS_VHOLD_OFFSET 3
> > @@ -39,12 +44,20 @@
> > #define AXP20X_ADC_EN1_VBUS_CURR BIT(2)
> > #define AXP20X_ADC_EN1_VBUS_VOLT BIT(3)
> >
> > +#define AXP717_INPUT_VOL_LIMIT_MASK GENMASK(3, 0)
> > +#define AXP717_INPUT_CUR_LIMIT_MASK GENMASK(5, 0)
> > +#define AXP717_ADC_DATA_MASK GENMASK(14, 0)
> > +
> > +#define AXP717_ADC_EN_VBUS_VOLT BIT(2)
> > +
> > /*
> > * Note do not raise the debounce time, we must report Vusb high within
> > * 100ms otherwise we get Vbus errors in musb.
> > */
> > #define DEBOUNCE_TIME msecs_to_jiffies(50)
> >
> > +struct axp20x_usb_power;
> > +
> > struct axp_data {
> > const struct power_supply_desc *power_desc;
> > const char * const *irq_names;
> > @@ -58,6 +71,10 @@ struct axp_data {
> > struct reg_field usb_bc_det_fld;
> > struct reg_field vbus_disable_bit;
> > bool vbus_needs_polling: 1;
> > + void (*axp20x_read_vbus)(struct work_struct *work);
> > + int (*axp20x_cfg_iio_chan)(struct platform_device *pdev,
> > + struct axp20x_usb_power *power);
> > + int (*axp20x_cfg_adc_reg)(struct axp20x_usb_power *power);
>
> Please split out changes generalizing the driver into a separate patch.
Acknowledged.
>
> > };
> >
> > struct axp20x_usb_power {
> > @@ -74,6 +91,7 @@ struct axp20x_usb_power {
> > struct iio_channel *vbus_v;
> > struct iio_channel *vbus_i;
> > struct delayed_work vbus_detect;
> > + int max_input_cur;
> > unsigned int old_status;
> > unsigned int online;
> > unsigned int num_irqs;
> > @@ -136,6 +154,24 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work)
> > mod_delayed_work(system_power_efficient_wq, &power->vbus_detect, DEBOUNCE_TIME);
> > }
> >
> > +static void axp717_usb_power_poll_vbus(struct work_struct *work)
> > +{
> > + struct axp20x_usb_power *power =
> > + container_of(work, struct axp20x_usb_power, vbus_detect.work);
> > + unsigned int val;
> > + int ret;
> > +
> > + ret = regmap_read(power->regmap, AXP717_ON_INDICATE, &val);
> > + if (ret)
> > + return;
> > +
> > + val &= AXP717_PWR_STATUS_VBUS_GOOD;
> > + if (val != power->old_status)
> > + power_supply_changed(power->supply);
> > +
> > + power->old_status = val;
> > +}
> > +
> > static int axp20x_get_usb_type(struct axp20x_usb_power *power,
> > union power_supply_propval *val)
> > {
> > @@ -281,6 +317,84 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
> > return 0;
> > }
> >
> > +static int axp717_usb_power_get_property(struct power_supply *psy,
> > + enum power_supply_property psp, union power_supply_propval *val)
> > +{
> > + struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
> > + unsigned int v;
> > + int ret;
> > +
> > + switch (psp) {
> > + case POWER_SUPPLY_PROP_HEALTH:
> > + val->intval = POWER_SUPPLY_HEALTH_GOOD;
> > + ret = regmap_read(power->regmap, AXP717_PMU_FAULT_VBUS, &v);
> > + if (ret)
> > + return ret;
> > +
> > + v &= (AXP717_PMU_FAULT_VBUS | AXP717_PMU_FAULT_VSYS);
> > + if (v) {
> > + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
> > + regmap_write(power->regmap, AXP717_PMU_FAULT_VBUS, v);
> > + }
> > +
>
> Return POWER_SUPPLY_HEALTH_UNKNOWN if vbus is not present?
>
Will do.
> > + break;
> > + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
> > + ret = regmap_read(power->regmap, AXP717_INPUT_CUR_LIMIT_CTRL, &v);
> > + if (ret)
> > + return ret;
> > +
> > + /* 50ma step size with 100ma offset. */
> > + v &= AXP717_INPUT_CUR_LIMIT_MASK;
> > + val->intval = (v * 50000) + 100000;
> > + break;
> > + case POWER_SUPPLY_PROP_ONLINE:
>
> Probably use VINDPM status for this?
>
For online? Unfortunately the VINDPM bit doesn't change whether or not a
charger is present for me.
> > + case POWER_SUPPLY_PROP_PRESENT:
> > + ret = regmap_read(power->regmap, AXP717_ON_INDICATE, &v);
> > + if (ret)
> > + return ret;
> > + val->intval = !!(v & AXP717_PWR_STATUS_VBUS_GOOD);
> > + break;
> > + case POWER_SUPPLY_PROP_USB_TYPE:
> > + return axp20x_get_usb_type(power, val);
> > + case POWER_SUPPLY_PROP_VOLTAGE_MIN:
> > + ret = regmap_read(power->regmap, AXP717_INPUT_VOL_LIMIT_CTRL, &v);
> > + if (ret)
> > + return ret;
> > +
> > + /* 80mv step size with 3.88v offset. */
> > + v &= AXP717_INPUT_VOL_LIMIT_MASK;
> > + val->intval = (v * 80000) + 3880000;
> > + break;
> > + case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> > + if (IS_ENABLED(CONFIG_AXP20X_ADC)) {
> > + ret = iio_read_channel_processed(power->vbus_v,
> > + &val->intval);
> > + if (ret)
> > + return ret;
> > +
> > + /*
> > + * IIO framework gives mV but Power Supply framework
> > + * gives uV.
> > + */
> > + val->intval *= 1000;
> > + return 0;
> > + }
> > +
> > + ret = axp20x_read_variable_width(power->regmap,
> > + AXP717_VBUS_V_H, 16);
> > + if (ret < 0)
> > + return ret;
> > +
> > + val->intval = (ret % AXP717_ADC_DATA_MASK) * 1000;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + return 0;
> > +
> > +}
> > +
> > static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power,
> > int intval)
> > {
> > @@ -307,16 +421,40 @@ static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power,
> > return -EINVAL;
> > }
> >
> > +static int axp717_usb_power_set_voltage_min(struct axp20x_usb_power *power,
> > + int intval)
> > +{
> > + int val;
> > +
> > + /* Minimum value of 3.88v and maximum of 5.08v. */
> > + if (intval < 3880000 || intval > 5080000)
> > + return -EINVAL;
> > +
> > + /* step size of 80ma with 3.88v offset. */
> > + val = (intval - 3880000) / 80000;
> > + return regmap_update_bits(power->regmap,
> > + AXP717_INPUT_VOL_LIMIT_CTRL,
> > + AXP717_INPUT_VOL_LIMIT_MASK, val);
> > +}
> > +
> > static int axp20x_usb_power_set_input_current_limit(struct axp20x_usb_power *power,
> > int intval)
> > {
> > - int ret;
> > + int ret, val;
> > unsigned int reg;
> > const unsigned int max = power->axp_data->curr_lim_table_size;
> >
> > if (intval == -1)
> > return -EINVAL;
> >
> > + if (power->max_input_cur && (intval > power->max_input_cur)) {
> > + val = power->max_input_cur;
> > + dev_warn(power->dev,
> > + "reqested current %d clamped to max current %d\n",
> > + intval, power->max_input_cur);
> > + } else
> > + val = intval;
> > +
>
> Just overwrite intval. One less variable and less changes below.
Done.
>
> > /*
> > * BC1.2 detection can cause a race condition if we try to set a current
> > * limit while it's in progress. When it finishes it will overwrite the
> > @@ -331,15 +469,39 @@ static int axp20x_usb_power_set_input_current_limit(struct axp20x_usb_power *pow
> > }
> >
> > for (reg = max - 1; reg > 0; reg--)
> > - if (power->axp_data->curr_lim_table[reg] <= intval)
> > + if (power->axp_data->curr_lim_table[reg] <= val)
> > break;
> >
> > dev_dbg(power->dev, "setting input current limit reg to %d (%d uA), requested %d uA",
> > - reg, power->axp_data->curr_lim_table[reg], intval);
> > + reg, power->axp_data->curr_lim_table[reg], val);
> >
> > return regmap_field_write(power->curr_lim_fld, reg);
> > }
> >
> > +static int axp717_usb_power_set_input_current_limit(struct axp20x_usb_power *power,
> > + int intval)
> > +{
> > + int val, tmp;
> > +
> > + /* Minimum value of 100mA and maximum value of 3.25A*/
> > + if (intval < 100000 || intval > 3250000)
> > + return -EINVAL;
> > +
> > + if (power->max_input_cur && (intval > power->max_input_cur)) {
> > + val = power->max_input_cur;
> > + dev_warn(power->dev,
> > + "reqested current %d clamped to max current %d\n",
> > + intval, power->max_input_cur);
> > + } else
> > + val = intval;
>
> Same comment: just overwrite intval.
>
Done.
> > +
> > + /* Minimum value of 100mA with step size of 50mA. */
> > + tmp = (val - 100000) / 50000;
> > + return regmap_update_bits(power->regmap,
> > + AXP717_INPUT_CUR_LIMIT_CTRL,
> > + AXP717_INPUT_CUR_LIMIT_MASK, tmp);
> > +}
> > +
> > static int axp20x_usb_power_set_property(struct power_supply *psy,
> > enum power_supply_property psp,
> > const union power_supply_propval *val)
> > @@ -366,6 +528,26 @@ static int axp20x_usb_power_set_property(struct power_supply *psy,
> > return -EINVAL;
> > }
> >
> > +static int axp717_usb_power_set_property(struct power_supply *psy,
> > + enum power_supply_property psp,
> > + const union power_supply_propval *val)
> > +{
> > + struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
> > +
> > + switch (psp) {
> > + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
> > + return axp717_usb_power_set_input_current_limit(power, val->intval);
> > +
> > + case POWER_SUPPLY_PROP_VOLTAGE_MIN:
> > + return axp717_usb_power_set_voltage_min(power, val->intval);
> > +
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + return -EINVAL;
>
> This is redundant.
>
Dropping.
> > +}
> > +
> > static int axp20x_usb_power_prop_writeable(struct power_supply *psy,
> > enum power_supply_property psp)
> > {
> > @@ -385,6 +567,64 @@ static int axp20x_usb_power_prop_writeable(struct power_supply *psy,
> > psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
> > }
> >
> > +static int axp717_usb_power_prop_writeable(struct power_supply *psy,
> > + enum power_supply_property psp)
> > +{
> > + return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN ||
> > + psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
> > +}
> > +
> > +static int axp20x_configure_iio_channels(struct platform_device *pdev,
> > + struct axp20x_usb_power *power)
> > +{
> > + power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v");
> > + if (IS_ERR(power->vbus_v)) {
> > + if (PTR_ERR(power->vbus_v) == -ENODEV)
> > + return -EPROBE_DEFER;
> > + return PTR_ERR(power->vbus_v);
> > + }
> > +
> > + power->vbus_i = devm_iio_channel_get(&pdev->dev, "vbus_i");
> > + if (IS_ERR(power->vbus_i)) {
> > + if (PTR_ERR(power->vbus_i) == -ENODEV)
> > + return -EPROBE_DEFER;
> > + return PTR_ERR(power->vbus_i);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int axp717_configure_iio_channels(struct platform_device *pdev,
> > + struct axp20x_usb_power *power)
> > +{
> > + power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v");
> > + if (IS_ERR(power->vbus_v)) {
> > + if (PTR_ERR(power->vbus_v) == -ENODEV)
> > + return -EPROBE_DEFER;
> > + return PTR_ERR(power->vbus_v);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int axp20x_configure_adc_registers(struct axp20x_usb_power *power)
> > +{
> > + /* Enable vbus voltage and current measurement */
> > + return regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
> > + AXP20X_ADC_EN1_VBUS_CURR |
> > + AXP20X_ADC_EN1_VBUS_VOLT,
> > + AXP20X_ADC_EN1_VBUS_CURR |
> > + AXP20X_ADC_EN1_VBUS_VOLT);
> > +}
> > +
> > +static int axp717_configure_adc_registers(struct axp20x_usb_power *power)
> > +{
> > + /* Enable vbus voltage measurement */
> > + return regmap_update_bits(power->regmap, AXP717_ADC_CH_EN_CONTROL,
> > + AXP717_ADC_EN_VBUS_VOLT,
> > + AXP717_ADC_EN_VBUS_VOLT);
> > +}
> > +
> > static enum power_supply_property axp20x_usb_power_properties[] = {
> > POWER_SUPPLY_PROP_HEALTH,
> > POWER_SUPPLY_PROP_PRESENT,
> > @@ -403,6 +643,16 @@ static enum power_supply_property axp22x_usb_power_properties[] = {
> > POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
> > };
> >
> > +static enum power_supply_property axp717_usb_power_properties[] = {
> > + POWER_SUPPLY_PROP_HEALTH,
> > + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
> > + POWER_SUPPLY_PROP_ONLINE,
> > + POWER_SUPPLY_PROP_PRESENT,
> > + POWER_SUPPLY_PROP_USB_TYPE,
> > + POWER_SUPPLY_PROP_VOLTAGE_MIN,
> > + POWER_SUPPLY_PROP_VOLTAGE_NOW,
> > +};
> > +
> > static enum power_supply_property axp813_usb_power_properties[] = {
> > POWER_SUPPLY_PROP_HEALTH,
> > POWER_SUPPLY_PROP_PRESENT,
> > @@ -439,6 +689,18 @@ static const struct power_supply_desc axp22x_usb_power_desc = {
> > .set_property = axp20x_usb_power_set_property,
> > };
> >
> > +static const struct power_supply_desc axp717_usb_power_desc = {
> > + .name = "axp20x-usb",
> > + .type = POWER_SUPPLY_TYPE_USB,
> > + .properties = axp717_usb_power_properties,
> > + .num_properties = ARRAY_SIZE(axp717_usb_power_properties),
> > + .property_is_writeable = axp717_usb_power_prop_writeable,
> > + .get_property = axp717_usb_power_get_property,
> > + .set_property = axp717_usb_power_set_property,
> > + .usb_types = axp813_usb_types,
> > + .num_usb_types = ARRAY_SIZE(axp813_usb_types),
> > +};
> > +
> > static const struct power_supply_desc axp813_usb_power_desc = {
> > .name = "axp20x-usb",
> > .type = POWER_SUPPLY_TYPE_USB,
> > @@ -505,6 +767,9 @@ static const struct axp_data axp192_data = {
> > .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
> > .vbus_valid_bit = REG_FIELD(AXP192_USB_OTG_STATUS, 2, 2),
> > .vbus_mon_bit = REG_FIELD(AXP20X_VBUS_MON, 3, 3),
> > + .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
> > + .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
> > + .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
> > };
> >
> > static const struct axp_data axp202_data = {
> > @@ -516,6 +781,9 @@ static const struct axp_data axp202_data = {
> > .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
> > .vbus_valid_bit = REG_FIELD(AXP20X_USB_OTG_STATUS, 2, 2),
> > .vbus_mon_bit = REG_FIELD(AXP20X_VBUS_MON, 3, 3),
> > + .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
> > + .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
> > + .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
> > };
> >
> > static const struct axp_data axp221_data = {
> > @@ -526,6 +794,9 @@ static const struct axp_data axp221_data = {
> > .curr_lim_table_size = ARRAY_SIZE(axp221_usb_curr_lim_table),
> > .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
> > .vbus_needs_polling = true,
> > + .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
> > + .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
> > + .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
> > };
> >
> > static const struct axp_data axp223_data = {
> > @@ -536,6 +807,23 @@ static const struct axp_data axp223_data = {
> > .curr_lim_table_size = ARRAY_SIZE(axp20x_usb_curr_lim_table),
> > .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
> > .vbus_needs_polling = true,
> > + .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
> > + .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
> > + .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
> > +};
> > +
> > +static const struct axp_data axp717_data = {
> > + .power_desc = &axp717_usb_power_desc,
> > + .irq_names = axp22x_irq_names,
> > + .num_irq_names = ARRAY_SIZE(axp22x_irq_names),
> > + .curr_lim_fld = REG_FIELD(AXP717_INPUT_CUR_LIMIT_CTRL, 0, 5),
> > + .usb_bc_en_bit = REG_FIELD(AXP717_MODULE_EN_CONTROL_1, 4, 4),
> > + .usb_bc_det_fld = REG_FIELD(AXP717_BC_DETECT, 5, 7),
> > + .vbus_mon_bit = REG_FIELD(AXP717_ADC_CH_EN_CONTROL, 2, 2),
> > + .vbus_needs_polling = false,
> > + .axp20x_read_vbus = &axp717_usb_power_poll_vbus,
> > + .axp20x_cfg_iio_chan = axp717_configure_iio_channels,
> > + .axp20x_cfg_adc_reg = axp717_configure_adc_registers,
> > };
> >
> > static const struct axp_data axp813_data = {
> > @@ -549,6 +837,9 @@ static const struct axp_data axp813_data = {
> > .usb_bc_det_fld = REG_FIELD(AXP288_BC_DET_STAT, 5, 7),
> > .vbus_disable_bit = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 7, 7),
> > .vbus_needs_polling = true,
> > + .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
> > + .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
> > + .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
> > };
> >
> > #ifdef CONFIG_PM_SLEEP
> > @@ -590,36 +881,6 @@ static int axp20x_usb_power_resume(struct device *dev)
> > static SIMPLE_DEV_PM_OPS(axp20x_usb_power_pm_ops, axp20x_usb_power_suspend,
> > axp20x_usb_power_resume);
> >
> > -static int configure_iio_channels(struct platform_device *pdev,
> > - struct axp20x_usb_power *power)
> > -{
> > - power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v");
> > - if (IS_ERR(power->vbus_v)) {
> > - if (PTR_ERR(power->vbus_v) == -ENODEV)
> > - return -EPROBE_DEFER;
> > - return PTR_ERR(power->vbus_v);
> > - }
> > -
> > - power->vbus_i = devm_iio_channel_get(&pdev->dev, "vbus_i");
> > - if (IS_ERR(power->vbus_i)) {
> > - if (PTR_ERR(power->vbus_i) == -ENODEV)
> > - return -EPROBE_DEFER;
> > - return PTR_ERR(power->vbus_i);
> > - }
> > -
> > - return 0;
> > -}
> > -
> > -static int configure_adc_registers(struct axp20x_usb_power *power)
> > -{
> > - /* Enable vbus voltage and current measurement */
> > - return regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
> > - AXP20X_ADC_EN1_VBUS_CURR |
> > - AXP20X_ADC_EN1_VBUS_VOLT,
> > - AXP20X_ADC_EN1_VBUS_CURR |
> > - AXP20X_ADC_EN1_VBUS_VOLT);
> > -}
> > -
> > static int axp20x_regmap_field_alloc_optional(struct device *dev,
> > struct regmap *regmap,
> > struct reg_field fdesc,
> > @@ -640,6 +901,18 @@ static int axp20x_regmap_field_alloc_optional(struct device *dev,
> > return 0;
> > }
> >
> > +/* Optionally allow users to specify a maximum charging current. */
> > +static void axp20x_charger_parse_dt_property(struct device *dev,
>
> axp20x_usb_power_parse_dt().
>
Thank you, I'll rename the function.
>
> Regards
> ChenYu
>
> > + struct axp20x_usb_power *power)
> > +{
> > + int ret;
> > +
> > + ret = device_property_read_u32(dev, "input-current-limit-microamp",
> > + &power->max_input_cur);
> > + if (ret)
> > + dev_dbg(dev, "%s() no input-current-limit specified\n", __func__);
> > +}
> > +
> > static int axp20x_usb_power_probe(struct platform_device *pdev)
> > {
> > struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
> > @@ -676,6 +949,8 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
> > if (IS_ERR(power->curr_lim_fld))
> > return PTR_ERR(power->curr_lim_fld);
> >
> > + axp20x_charger_parse_dt_property(&pdev->dev, power);
> > +
> > ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
> > axp_data->vbus_valid_bit,
> > &power->vbus_valid_bit);
> > @@ -707,7 +982,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
> > return ret;
> >
> > ret = devm_delayed_work_autocancel(&pdev->dev, &power->vbus_detect,
> > - axp20x_usb_power_poll_vbus);
> > + axp_data->axp20x_read_vbus);
> > if (ret)
> > return ret;
> >
> > @@ -718,9 +993,9 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
> > return ret;
> >
> > if (IS_ENABLED(CONFIG_AXP20X_ADC))
> > - ret = configure_iio_channels(pdev, power);
> > + ret = axp_data->axp20x_cfg_iio_chan(pdev, power);
> > else
> > - ret = configure_adc_registers(power);
> > + ret = axp_data->axp20x_cfg_adc_reg(power);
> >
> > if (ret)
> > return ret;
> > @@ -778,6 +1053,9 @@ static const struct of_device_id axp20x_usb_power_match[] = {
> > }, {
> > .compatible = "x-powers,axp223-usb-power-supply",
> > .data = &axp223_data,
> > + }, {
> > + .compatible = "x-powers,axp717-usb-power-supply",
> > + .data = &axp717_data,
> > }, {
> > .compatible = "x-powers,axp813-usb-power-supply",
> > .data = &axp813_data,
> > --
> > 2.34.1
> >
Thank you for your feedback,
Chris
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 6/8] power: supply: axp20x_usb_power: Add support for AXP717
2024-06-17 22:05 ` [PATCH 6/8] power: supply: axp20x_usb_power: Add support for AXP717 Chris Morgan
2024-06-22 23:49 ` Ryan Walklin
2024-06-23 5:42 ` Chen-Yu Tsai
@ 2024-06-24 18:00 ` Philippe Simons
2 siblings, 0 replies; 28+ messages in thread
From: Philippe Simons @ 2024-06-24 18:00 UTC (permalink / raw)
To: Chris Morgan, linux-sunxi
Cc: linux-pm, devicetree, linux-iio, broonie, lee, samuel,
jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
sysfs correctly reports presence of USB power and voltages, tested on
RG35XX-H (H700)
Tested-by: Philippe Simons <simons.philippe@gmail.com>
On 18/06/24 00:05, Chris Morgan wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add support for the AXP717 PMIC. The AXP717 PMIC allows for detection
> of USB type like the AXP813, but has little in common otherwise with
> the other AXP PMICs. The USB charger is able to provide between
> 100000uA and 3250000uA of power, and can measure the VBUS input in mV
> with up to 14 bits of precision.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
> ---
> drivers/power/supply/axp20x_usb_power.c | 350 +++++++++++++++++++++---
> 1 file changed, 314 insertions(+), 36 deletions(-)
>
> diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
> index dae7e5cfc54e..c442dd005831 100644
> --- a/drivers/power/supply/axp20x_usb_power.c
> +++ b/drivers/power/supply/axp20x_usb_power.c
> @@ -30,8 +30,13 @@
> #define AXP20X_PWR_STATUS_VBUS_PRESENT BIT(5)
> #define AXP20X_PWR_STATUS_VBUS_USED BIT(4)
>
> +#define AXP717_PWR_STATUS_VBUS_GOOD BIT(5)
> +
> #define AXP20X_USB_STATUS_VBUS_VALID BIT(2)
>
> +#define AXP717_PMU_FAULT_VBUS BIT(5)
> +#define AXP717_PMU_FAULT_VSYS BIT(3)
> +
> #define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000)
> #define AXP20X_VBUS_VHOLD_MASK GENMASK(5, 3)
> #define AXP20X_VBUS_VHOLD_OFFSET 3
> @@ -39,12 +44,20 @@
> #define AXP20X_ADC_EN1_VBUS_CURR BIT(2)
> #define AXP20X_ADC_EN1_VBUS_VOLT BIT(3)
>
> +#define AXP717_INPUT_VOL_LIMIT_MASK GENMASK(3, 0)
> +#define AXP717_INPUT_CUR_LIMIT_MASK GENMASK(5, 0)
> +#define AXP717_ADC_DATA_MASK GENMASK(14, 0)
> +
> +#define AXP717_ADC_EN_VBUS_VOLT BIT(2)
> +
> /*
> * Note do not raise the debounce time, we must report Vusb high within
> * 100ms otherwise we get Vbus errors in musb.
> */
> #define DEBOUNCE_TIME msecs_to_jiffies(50)
>
> +struct axp20x_usb_power;
> +
> struct axp_data {
> const struct power_supply_desc *power_desc;
> const char * const *irq_names;
> @@ -58,6 +71,10 @@ struct axp_data {
> struct reg_field usb_bc_det_fld;
> struct reg_field vbus_disable_bit;
> bool vbus_needs_polling: 1;
> + void (*axp20x_read_vbus)(struct work_struct *work);
> + int (*axp20x_cfg_iio_chan)(struct platform_device *pdev,
> + struct axp20x_usb_power *power);
> + int (*axp20x_cfg_adc_reg)(struct axp20x_usb_power *power);
> };
>
> struct axp20x_usb_power {
> @@ -74,6 +91,7 @@ struct axp20x_usb_power {
> struct iio_channel *vbus_v;
> struct iio_channel *vbus_i;
> struct delayed_work vbus_detect;
> + int max_input_cur;
> unsigned int old_status;
> unsigned int online;
> unsigned int num_irqs;
> @@ -136,6 +154,24 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work)
> mod_delayed_work(system_power_efficient_wq, &power->vbus_detect, DEBOUNCE_TIME);
> }
>
> +static void axp717_usb_power_poll_vbus(struct work_struct *work)
> +{
> + struct axp20x_usb_power *power =
> + container_of(work, struct axp20x_usb_power, vbus_detect.work);
> + unsigned int val;
> + int ret;
> +
> + ret = regmap_read(power->regmap, AXP717_ON_INDICATE, &val);
> + if (ret)
> + return;
> +
> + val &= AXP717_PWR_STATUS_VBUS_GOOD;
> + if (val != power->old_status)
> + power_supply_changed(power->supply);
> +
> + power->old_status = val;
> +}
> +
> static int axp20x_get_usb_type(struct axp20x_usb_power *power,
> union power_supply_propval *val)
> {
> @@ -281,6 +317,84 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
> return 0;
> }
>
> +static int axp717_usb_power_get_property(struct power_supply *psy,
> + enum power_supply_property psp, union power_supply_propval *val)
> +{
> + struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
> + unsigned int v;
> + int ret;
> +
> + switch (psp) {
> + case POWER_SUPPLY_PROP_HEALTH:
> + val->intval = POWER_SUPPLY_HEALTH_GOOD;
> + ret = regmap_read(power->regmap, AXP717_PMU_FAULT_VBUS, &v);
> + if (ret)
> + return ret;
> +
> + v &= (AXP717_PMU_FAULT_VBUS | AXP717_PMU_FAULT_VSYS);
> + if (v) {
> + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
> + regmap_write(power->regmap, AXP717_PMU_FAULT_VBUS, v);
> + }
> +
> + break;
> + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
> + ret = regmap_read(power->regmap, AXP717_INPUT_CUR_LIMIT_CTRL, &v);
> + if (ret)
> + return ret;
> +
> + /* 50ma step size with 100ma offset. */
> + v &= AXP717_INPUT_CUR_LIMIT_MASK;
> + val->intval = (v * 50000) + 100000;
> + break;
> + case POWER_SUPPLY_PROP_ONLINE:
> + case POWER_SUPPLY_PROP_PRESENT:
> + ret = regmap_read(power->regmap, AXP717_ON_INDICATE, &v);
> + if (ret)
> + return ret;
> + val->intval = !!(v & AXP717_PWR_STATUS_VBUS_GOOD);
> + break;
> + case POWER_SUPPLY_PROP_USB_TYPE:
> + return axp20x_get_usb_type(power, val);
> + case POWER_SUPPLY_PROP_VOLTAGE_MIN:
> + ret = regmap_read(power->regmap, AXP717_INPUT_VOL_LIMIT_CTRL, &v);
> + if (ret)
> + return ret;
> +
> + /* 80mv step size with 3.88v offset. */
> + v &= AXP717_INPUT_VOL_LIMIT_MASK;
> + val->intval = (v * 80000) + 3880000;
> + break;
> + case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> + if (IS_ENABLED(CONFIG_AXP20X_ADC)) {
> + ret = iio_read_channel_processed(power->vbus_v,
> + &val->intval);
> + if (ret)
> + return ret;
> +
> + /*
> + * IIO framework gives mV but Power Supply framework
> + * gives uV.
> + */
> + val->intval *= 1000;
> + return 0;
> + }
> +
> + ret = axp20x_read_variable_width(power->regmap,
> + AXP717_VBUS_V_H, 16);
> + if (ret < 0)
> + return ret;
> +
> + val->intval = (ret % AXP717_ADC_DATA_MASK) * 1000;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +
> +}
> +
> static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power,
> int intval)
> {
> @@ -307,16 +421,40 @@ static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power,
> return -EINVAL;
> }
>
> +static int axp717_usb_power_set_voltage_min(struct axp20x_usb_power *power,
> + int intval)
> +{
> + int val;
> +
> + /* Minimum value of 3.88v and maximum of 5.08v. */
> + if (intval < 3880000 || intval > 5080000)
> + return -EINVAL;
> +
> + /* step size of 80ma with 3.88v offset. */
> + val = (intval - 3880000) / 80000;
> + return regmap_update_bits(power->regmap,
> + AXP717_INPUT_VOL_LIMIT_CTRL,
> + AXP717_INPUT_VOL_LIMIT_MASK, val);
> +}
> +
> static int axp20x_usb_power_set_input_current_limit(struct axp20x_usb_power *power,
> int intval)
> {
> - int ret;
> + int ret, val;
> unsigned int reg;
> const unsigned int max = power->axp_data->curr_lim_table_size;
>
> if (intval == -1)
> return -EINVAL;
>
> + if (power->max_input_cur && (intval > power->max_input_cur)) {
> + val = power->max_input_cur;
> + dev_warn(power->dev,
> + "reqested current %d clamped to max current %d\n",
> + intval, power->max_input_cur);
> + } else
> + val = intval;
> +
> /*
> * BC1.2 detection can cause a race condition if we try to set a current
> * limit while it's in progress. When it finishes it will overwrite the
> @@ -331,15 +469,39 @@ static int axp20x_usb_power_set_input_current_limit(struct axp20x_usb_power *pow
> }
>
> for (reg = max - 1; reg > 0; reg--)
> - if (power->axp_data->curr_lim_table[reg] <= intval)
> + if (power->axp_data->curr_lim_table[reg] <= val)
> break;
>
> dev_dbg(power->dev, "setting input current limit reg to %d (%d uA), requested %d uA",
> - reg, power->axp_data->curr_lim_table[reg], intval);
> + reg, power->axp_data->curr_lim_table[reg], val);
>
> return regmap_field_write(power->curr_lim_fld, reg);
> }
>
> +static int axp717_usb_power_set_input_current_limit(struct axp20x_usb_power *power,
> + int intval)
> +{
> + int val, tmp;
> +
> + /* Minimum value of 100mA and maximum value of 3.25A*/
> + if (intval < 100000 || intval > 3250000)
> + return -EINVAL;
> +
> + if (power->max_input_cur && (intval > power->max_input_cur)) {
> + val = power->max_input_cur;
> + dev_warn(power->dev,
> + "reqested current %d clamped to max current %d\n",
> + intval, power->max_input_cur);
> + } else
> + val = intval;
> +
> + /* Minimum value of 100mA with step size of 50mA. */
> + tmp = (val - 100000) / 50000;
> + return regmap_update_bits(power->regmap,
> + AXP717_INPUT_CUR_LIMIT_CTRL,
> + AXP717_INPUT_CUR_LIMIT_MASK, tmp);
> +}
> +
> static int axp20x_usb_power_set_property(struct power_supply *psy,
> enum power_supply_property psp,
> const union power_supply_propval *val)
> @@ -366,6 +528,26 @@ static int axp20x_usb_power_set_property(struct power_supply *psy,
> return -EINVAL;
> }
>
> +static int axp717_usb_power_set_property(struct power_supply *psy,
> + enum power_supply_property psp,
> + const union power_supply_propval *val)
> +{
> + struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
> +
> + switch (psp) {
> + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
> + return axp717_usb_power_set_input_current_limit(power, val->intval);
> +
> + case POWER_SUPPLY_PROP_VOLTAGE_MIN:
> + return axp717_usb_power_set_voltage_min(power, val->intval);
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> static int axp20x_usb_power_prop_writeable(struct power_supply *psy,
> enum power_supply_property psp)
> {
> @@ -385,6 +567,64 @@ static int axp20x_usb_power_prop_writeable(struct power_supply *psy,
> psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
> }
>
> +static int axp717_usb_power_prop_writeable(struct power_supply *psy,
> + enum power_supply_property psp)
> +{
> + return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN ||
> + psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
> +}
> +
> +static int axp20x_configure_iio_channels(struct platform_device *pdev,
> + struct axp20x_usb_power *power)
> +{
> + power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v");
> + if (IS_ERR(power->vbus_v)) {
> + if (PTR_ERR(power->vbus_v) == -ENODEV)
> + return -EPROBE_DEFER;
> + return PTR_ERR(power->vbus_v);
> + }
> +
> + power->vbus_i = devm_iio_channel_get(&pdev->dev, "vbus_i");
> + if (IS_ERR(power->vbus_i)) {
> + if (PTR_ERR(power->vbus_i) == -ENODEV)
> + return -EPROBE_DEFER;
> + return PTR_ERR(power->vbus_i);
> + }
> +
> + return 0;
> +}
> +
> +static int axp717_configure_iio_channels(struct platform_device *pdev,
> + struct axp20x_usb_power *power)
> +{
> + power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v");
> + if (IS_ERR(power->vbus_v)) {
> + if (PTR_ERR(power->vbus_v) == -ENODEV)
> + return -EPROBE_DEFER;
> + return PTR_ERR(power->vbus_v);
> + }
> +
> + return 0;
> +}
> +
> +static int axp20x_configure_adc_registers(struct axp20x_usb_power *power)
> +{
> + /* Enable vbus voltage and current measurement */
> + return regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
> + AXP20X_ADC_EN1_VBUS_CURR |
> + AXP20X_ADC_EN1_VBUS_VOLT,
> + AXP20X_ADC_EN1_VBUS_CURR |
> + AXP20X_ADC_EN1_VBUS_VOLT);
> +}
> +
> +static int axp717_configure_adc_registers(struct axp20x_usb_power *power)
> +{
> + /* Enable vbus voltage measurement */
> + return regmap_update_bits(power->regmap, AXP717_ADC_CH_EN_CONTROL,
> + AXP717_ADC_EN_VBUS_VOLT,
> + AXP717_ADC_EN_VBUS_VOLT);
> +}
> +
> static enum power_supply_property axp20x_usb_power_properties[] = {
> POWER_SUPPLY_PROP_HEALTH,
> POWER_SUPPLY_PROP_PRESENT,
> @@ -403,6 +643,16 @@ static enum power_supply_property axp22x_usb_power_properties[] = {
> POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
> };
>
> +static enum power_supply_property axp717_usb_power_properties[] = {
> + POWER_SUPPLY_PROP_HEALTH,
> + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
> + POWER_SUPPLY_PROP_ONLINE,
> + POWER_SUPPLY_PROP_PRESENT,
> + POWER_SUPPLY_PROP_USB_TYPE,
> + POWER_SUPPLY_PROP_VOLTAGE_MIN,
> + POWER_SUPPLY_PROP_VOLTAGE_NOW,
> +};
> +
> static enum power_supply_property axp813_usb_power_properties[] = {
> POWER_SUPPLY_PROP_HEALTH,
> POWER_SUPPLY_PROP_PRESENT,
> @@ -439,6 +689,18 @@ static const struct power_supply_desc axp22x_usb_power_desc = {
> .set_property = axp20x_usb_power_set_property,
> };
>
> +static const struct power_supply_desc axp717_usb_power_desc = {
> + .name = "axp20x-usb",
> + .type = POWER_SUPPLY_TYPE_USB,
> + .properties = axp717_usb_power_properties,
> + .num_properties = ARRAY_SIZE(axp717_usb_power_properties),
> + .property_is_writeable = axp717_usb_power_prop_writeable,
> + .get_property = axp717_usb_power_get_property,
> + .set_property = axp717_usb_power_set_property,
> + .usb_types = axp813_usb_types,
> + .num_usb_types = ARRAY_SIZE(axp813_usb_types),
> +};
> +
> static const struct power_supply_desc axp813_usb_power_desc = {
> .name = "axp20x-usb",
> .type = POWER_SUPPLY_TYPE_USB,
> @@ -505,6 +767,9 @@ static const struct axp_data axp192_data = {
> .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
> .vbus_valid_bit = REG_FIELD(AXP192_USB_OTG_STATUS, 2, 2),
> .vbus_mon_bit = REG_FIELD(AXP20X_VBUS_MON, 3, 3),
> + .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
> + .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
> + .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
> };
>
> static const struct axp_data axp202_data = {
> @@ -516,6 +781,9 @@ static const struct axp_data axp202_data = {
> .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
> .vbus_valid_bit = REG_FIELD(AXP20X_USB_OTG_STATUS, 2, 2),
> .vbus_mon_bit = REG_FIELD(AXP20X_VBUS_MON, 3, 3),
> + .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
> + .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
> + .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
> };
>
> static const struct axp_data axp221_data = {
> @@ -526,6 +794,9 @@ static const struct axp_data axp221_data = {
> .curr_lim_table_size = ARRAY_SIZE(axp221_usb_curr_lim_table),
> .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
> .vbus_needs_polling = true,
> + .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
> + .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
> + .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
> };
>
> static const struct axp_data axp223_data = {
> @@ -536,6 +807,23 @@ static const struct axp_data axp223_data = {
> .curr_lim_table_size = ARRAY_SIZE(axp20x_usb_curr_lim_table),
> .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
> .vbus_needs_polling = true,
> + .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
> + .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
> + .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
> +};
> +
> +static const struct axp_data axp717_data = {
> + .power_desc = &axp717_usb_power_desc,
> + .irq_names = axp22x_irq_names,
> + .num_irq_names = ARRAY_SIZE(axp22x_irq_names),
> + .curr_lim_fld = REG_FIELD(AXP717_INPUT_CUR_LIMIT_CTRL, 0, 5),
> + .usb_bc_en_bit = REG_FIELD(AXP717_MODULE_EN_CONTROL_1, 4, 4),
> + .usb_bc_det_fld = REG_FIELD(AXP717_BC_DETECT, 5, 7),
> + .vbus_mon_bit = REG_FIELD(AXP717_ADC_CH_EN_CONTROL, 2, 2),
> + .vbus_needs_polling = false,
> + .axp20x_read_vbus = &axp717_usb_power_poll_vbus,
> + .axp20x_cfg_iio_chan = axp717_configure_iio_channels,
> + .axp20x_cfg_adc_reg = axp717_configure_adc_registers,
> };
>
> static const struct axp_data axp813_data = {
> @@ -549,6 +837,9 @@ static const struct axp_data axp813_data = {
> .usb_bc_det_fld = REG_FIELD(AXP288_BC_DET_STAT, 5, 7),
> .vbus_disable_bit = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 7, 7),
> .vbus_needs_polling = true,
> + .axp20x_read_vbus = &axp20x_usb_power_poll_vbus,
> + .axp20x_cfg_iio_chan = axp20x_configure_iio_channels,
> + .axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
> };
>
> #ifdef CONFIG_PM_SLEEP
> @@ -590,36 +881,6 @@ static int axp20x_usb_power_resume(struct device *dev)
> static SIMPLE_DEV_PM_OPS(axp20x_usb_power_pm_ops, axp20x_usb_power_suspend,
> axp20x_usb_power_resume);
>
> -static int configure_iio_channels(struct platform_device *pdev,
> - struct axp20x_usb_power *power)
> -{
> - power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v");
> - if (IS_ERR(power->vbus_v)) {
> - if (PTR_ERR(power->vbus_v) == -ENODEV)
> - return -EPROBE_DEFER;
> - return PTR_ERR(power->vbus_v);
> - }
> -
> - power->vbus_i = devm_iio_channel_get(&pdev->dev, "vbus_i");
> - if (IS_ERR(power->vbus_i)) {
> - if (PTR_ERR(power->vbus_i) == -ENODEV)
> - return -EPROBE_DEFER;
> - return PTR_ERR(power->vbus_i);
> - }
> -
> - return 0;
> -}
> -
> -static int configure_adc_registers(struct axp20x_usb_power *power)
> -{
> - /* Enable vbus voltage and current measurement */
> - return regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
> - AXP20X_ADC_EN1_VBUS_CURR |
> - AXP20X_ADC_EN1_VBUS_VOLT,
> - AXP20X_ADC_EN1_VBUS_CURR |
> - AXP20X_ADC_EN1_VBUS_VOLT);
> -}
> -
> static int axp20x_regmap_field_alloc_optional(struct device *dev,
> struct regmap *regmap,
> struct reg_field fdesc,
> @@ -640,6 +901,18 @@ static int axp20x_regmap_field_alloc_optional(struct device *dev,
> return 0;
> }
>
> +/* Optionally allow users to specify a maximum charging current. */
> +static void axp20x_charger_parse_dt_property(struct device *dev,
> + struct axp20x_usb_power *power)
> +{
> + int ret;
> +
> + ret = device_property_read_u32(dev, "input-current-limit-microamp",
> + &power->max_input_cur);
> + if (ret)
> + dev_dbg(dev, "%s() no input-current-limit specified\n", __func__);
> +}
> +
> static int axp20x_usb_power_probe(struct platform_device *pdev)
> {
> struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
> @@ -676,6 +949,8 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
> if (IS_ERR(power->curr_lim_fld))
> return PTR_ERR(power->curr_lim_fld);
>
> + axp20x_charger_parse_dt_property(&pdev->dev, power);
> +
> ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
> axp_data->vbus_valid_bit,
> &power->vbus_valid_bit);
> @@ -707,7 +982,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
> return ret;
>
> ret = devm_delayed_work_autocancel(&pdev->dev, &power->vbus_detect,
> - axp20x_usb_power_poll_vbus);
> + axp_data->axp20x_read_vbus);
> if (ret)
> return ret;
>
> @@ -718,9 +993,9 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
> return ret;
>
> if (IS_ENABLED(CONFIG_AXP20X_ADC))
> - ret = configure_iio_channels(pdev, power);
> + ret = axp_data->axp20x_cfg_iio_chan(pdev, power);
> else
> - ret = configure_adc_registers(power);
> + ret = axp_data->axp20x_cfg_adc_reg(power);
>
> if (ret)
> return ret;
> @@ -778,6 +1053,9 @@ static const struct of_device_id axp20x_usb_power_match[] = {
> }, {
> .compatible = "x-powers,axp223-usb-power-supply",
> .data = &axp223_data,
> + }, {
> + .compatible = "x-powers,axp717-usb-power-supply",
> + .data = &axp717_data,
> }, {
> .compatible = "x-powers,axp813-usb-power-supply",
> .data = &axp813_data,
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 7/8] power: supply: axp20x_battery: add support for AXP717
2024-06-17 22:05 [PATCH 0/8] Add Battery and USB Supply for AXP717 Chris Morgan
` (5 preceding siblings ...)
2024-06-17 22:05 ` [PATCH 6/8] power: supply: axp20x_usb_power: Add support for AXP717 Chris Morgan
@ 2024-06-17 22:05 ` Chris Morgan
2024-06-22 23:51 ` Ryan Walklin
` (2 more replies)
2024-06-17 22:05 ` [PATCH 8/8] arm64: dts: allwinner: h700: Add charger for Anbernic RG35XX Chris Morgan
7 siblings, 3 replies; 28+ messages in thread
From: Chris Morgan @ 2024-06-17 22:05 UTC (permalink / raw)
To: linux-sunxi
Cc: linux-pm, devicetree, linux-iio, broonie, lee, samuel,
jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add support for the AXP717 PMIC battery charger. The AXP717 differs
greatly from existing AXP battery chargers in that it cannot measure
the discharge current. The datasheet does not document the current
value's offset or scale, so the POWER_SUPPLY_PROP_CURRENT_NOW is left
unscaled.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/power/supply/axp20x_battery.c | 580 +++++++++++++++++++++++---
1 file changed, 531 insertions(+), 49 deletions(-)
diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c
index 6ac5c80cfda2..f09a56948642 100644
--- a/drivers/power/supply/axp20x_battery.c
+++ b/drivers/power/supply/axp20x_battery.c
@@ -32,9 +32,19 @@
#include <linux/mfd/axp20x.h>
#define AXP20X_PWR_STATUS_BAT_CHARGING BIT(2)
+#define AXP717_PWR_STATUS_MASK GENMASK(6, 5)
+#define AXP717_PWR_STATUS_BAT_STANDBY (0 << 5)
+#define AXP717_PWR_STATUS_BAT_CHRG (1 << 5)
+#define AXP717_PWR_STATUS_BAT_DISCHRG (2 << 5)
#define AXP20X_PWR_OP_BATT_PRESENT BIT(5)
#define AXP20X_PWR_OP_BATT_ACTIVATED BIT(3)
+#define AXP717_PWR_OP_BATT_PRESENT BIT(3)
+
+#define AXP717_BATT_PMU_FAULT_MASK GENMASK(2, 0)
+#define AXP717_BATT_UVLO_2_5V (1 << 2)
+#define AXP717_BATT_OVER_TEMP (1 << 1)
+#define AXP717_BATT_UNDER_TEMP (1 << 0)
#define AXP209_FG_PERCENT GENMASK(6, 0)
#define AXP22X_FG_VALID BIT(7)
@@ -49,20 +59,50 @@
#define AXP22X_CHRG_CTRL1_TGT_4_22V (1 << 5)
#define AXP22X_CHRG_CTRL1_TGT_4_24V (3 << 5)
+#define AXP717_CHRG_ENABLE BIT(1)
+#define AXP717_CHRG_CV_VOLT_MASK GENMASK(2, 0)
+#define AXP717_CHRG_CV_4_0V 0
+#define AXP717_CHRG_CV_4_1V 1
+#define AXP717_CHRG_CV_4_2V 2
+#define AXP717_CHRG_CV_4_35V 3
+#define AXP717_CHRG_CV_4_4V 4
+/* Values 5 and 6 reserved. */
+#define AXP717_CHRG_CV_5_0V 7
+
#define AXP813_CHRG_CTRL1_TGT_4_35V (3 << 5)
#define AXP20X_CHRG_CTRL1_TGT_CURR GENMASK(3, 0)
+#define AXP717_ICC_CHARGER_LIM GENMASK(5, 0)
+
+#define AXP717_ITERM_CHG_LIM GENMASK(3, 0)
+#define AXP717_ITERM_CC_STEP 64000
#define AXP20X_V_OFF_MASK GENMASK(2, 0)
+#define AXP717_V_OFF_MASK GENMASK(6, 4)
+
+#define AXP717_BAT_VMIN_MIN_UV 2600000
+#define AXP717_BAT_VMIN_MAX_UV 3300000
+#define AXP717_BAT_VMIN_STEP 100000
+#define AXP717_BAT_CV_MIN_UV 4000000
+#define AXP717_BAT_CV_MAX_UV 5000000
+#define AXP717_BAT_CC_MIN_UA 0
+#define AXP717_BAT_CC_MAX_UA 3008000
struct axp20x_batt_ps;
struct axp_data {
- int ccc_scale;
- int ccc_offset;
- bool has_fg_valid;
+ int ccc_scale;
+ int ccc_offset;
+ bool has_fg_valid;
+ const struct power_supply_desc *bat_ps_desc;
int (*get_max_voltage)(struct axp20x_batt_ps *batt, int *val);
int (*set_max_voltage)(struct axp20x_batt_ps *batt, int val);
+ int (*cfg_iio_chan)(struct platform_device *pdev,
+ struct axp20x_batt_ps *axp_batt);
+ void (*set_bat_info)(struct platform_device *pdev,
+ struct axp20x_batt_ps *axp_batt,
+ struct power_supply_battery_info *info);
+ int (*get_constant_chg_cur)(struct axp20x_batt_ps *axp, int *val);
};
struct axp20x_batt_ps {
@@ -135,6 +175,41 @@ static int axp22x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
return 0;
}
+static int axp717_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
+ int *val)
+{
+ int ret, reg;
+
+ ret = regmap_read(axp20x_batt->regmap, AXP717_CV_CHG_SET, ®);
+ if (ret)
+ return ret;
+
+ switch (reg & AXP717_CHRG_CV_VOLT_MASK) {
+ case AXP717_CHRG_CV_4_0V:
+ *val = 4000000;
+ break;
+ case AXP717_CHRG_CV_4_1V:
+ *val = 4100000;
+ break;
+ case AXP717_CHRG_CV_4_2V:
+ *val = 4200000;
+ break;
+ case AXP717_CHRG_CV_4_35V:
+ *val = 4350000;
+ break;
+ case AXP717_CHRG_CV_4_4V:
+ *val = 4400000;
+ break;
+ case AXP717_CHRG_CV_5_0V:
+ *val = 5000000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int axp813_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
int *val)
{
@@ -180,6 +255,22 @@ static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp,
return 0;
}
+static int axp717_get_constant_charge_current(struct axp20x_batt_ps *axp,
+ int *val)
+{
+ int ret;
+
+ ret = regmap_read(axp->regmap, AXP717_ICC_CHG_SET, val);
+ if (ret)
+ return ret;
+
+ *val &= AXP717_ICC_CHARGER_LIM;
+
+ *val = *val * axp->data->ccc_scale;
+
+ return 0;
+}
+
static int axp20x_battery_get_prop(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@@ -332,6 +423,178 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
return 0;
}
+static int axp717_battery_get_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy);
+ int ret = 0, reg;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ case POWER_SUPPLY_PROP_ONLINE:
+ ret = regmap_read(axp20x_batt->regmap, AXP717_ON_INDICATE,
+ ®);
+ if (ret)
+ return ret;
+
+ val->intval = !!(reg & AXP717_PWR_OP_BATT_PRESENT);
+ break;
+
+ case POWER_SUPPLY_PROP_STATUS:
+ ret = regmap_read(axp20x_batt->regmap, AXP717_PMU_STATUS_2,
+ ®);
+ if (ret)
+ return ret;
+
+ switch (reg & AXP717_PWR_STATUS_MASK) {
+ case AXP717_PWR_STATUS_BAT_STANDBY:
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+
+ case AXP717_PWR_STATUS_BAT_CHRG:
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ break;
+
+ case AXP717_PWR_STATUS_BAT_DISCHRG:
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ break;
+
+ default:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ }
+
+ break;
+
+ /*
+ * If a fault is detected it must also be cleared; if the
+ * condition persists it should reappear (This is an
+ * assumption, it's actually not documented). A restart was
+ * not sufficient to clear the bit in testing despite the
+ * register listed as POR.
+ */
+ case POWER_SUPPLY_PROP_HEALTH:
+ ret = regmap_read(axp20x_batt->regmap, AXP717_PMU_FAULT,
+ ®);
+ if (ret)
+ return ret;
+
+ switch (reg & AXP717_BATT_PMU_FAULT_MASK) {
+ case AXP717_BATT_UVLO_2_5V:
+ val->intval = POWER_SUPPLY_HEALTH_DEAD;
+ regmap_update_bits(axp20x_batt->regmap,
+ AXP717_PMU_FAULT,
+ AXP717_BATT_UVLO_2_5V,
+ AXP717_BATT_UVLO_2_5V);
+ break;
+
+ case AXP717_BATT_OVER_TEMP:
+ val->intval = POWER_SUPPLY_HEALTH_HOT;
+ regmap_update_bits(axp20x_batt->regmap,
+ AXP717_PMU_FAULT,
+ AXP717_BATT_OVER_TEMP,
+ AXP717_BATT_OVER_TEMP);
+ break;
+
+ case AXP717_BATT_UNDER_TEMP:
+ val->intval = POWER_SUPPLY_HEALTH_COLD;
+ regmap_update_bits(axp20x_batt->regmap,
+ AXP717_PMU_FAULT,
+ AXP717_BATT_UNDER_TEMP,
+ AXP717_BATT_UNDER_TEMP);
+ break;
+
+ default:
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ }
+
+ break;
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ ret = axp717_get_constant_charge_current(axp20x_batt,
+ &val->intval);
+ if (ret)
+ return ret;
+ break;
+
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ /*
+ * The offset of this value is currently unknown and is
+ * not documented in the datasheet. Based on
+ * observation it's assumed to be somewhere around
+ * 450ma. I will leave the value raw for now.
+ */
+ ret = iio_read_channel_processed(axp20x_batt->batt_chrg_i, &val->intval);
+ if (ret)
+ return ret;
+ /* IIO framework gives mA but Power Supply framework gives uA */
+ val->intval *= 1000;
+ break;
+
+ case POWER_SUPPLY_PROP_CAPACITY:
+ /* When no battery is present, return capacity is 100% */
+ ret = regmap_read(axp20x_batt->regmap, AXP717_ON_INDICATE,
+ ®);
+ if (ret)
+ return ret;
+
+ if (!(reg & AXP717_PWR_OP_BATT_PRESENT)) {
+ val->intval = 100;
+ return 0;
+ }
+
+ ret = regmap_read(axp20x_batt->regmap,
+ AXP717_BATT_PERCENT_DATA, ®);
+ if (ret)
+ return ret;
+
+ /*
+ * Fuel Gauge data takes 7 bits but the stored value seems to be
+ * directly the raw percentage without any scaling to 7 bits.
+ */
+ val->intval = reg & AXP209_FG_PERCENT;
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ return axp20x_batt->data->get_max_voltage(axp20x_batt,
+ &val->intval);
+
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ ret = regmap_read(axp20x_batt->regmap,
+ AXP717_VSYS_V_POWEROFF, ®);
+ if (ret)
+ return ret;
+
+ val->intval = AXP717_BAT_VMIN_MIN_UV + AXP717_BAT_VMIN_STEP *
+ (reg & AXP717_V_OFF_MASK);
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ ret = iio_read_channel_processed(axp20x_batt->batt_v,
+ &val->intval);
+ if (ret)
+ return ret;
+
+ /* IIO framework gives mV but Power Supply framework gives uV */
+ val->intval *= 1000;
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+ ret = regmap_read(axp20x_batt->regmap,
+ AXP717_ITERM_CHG_SET, ®);
+ if (ret)
+ return ret;
+
+ val->intval = (reg & AXP717_ITERM_CHG_LIM) * AXP717_ITERM_CC_STEP;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int axp22x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
int val)
{
@@ -388,6 +651,35 @@ static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
AXP20X_CHRG_CTRL1_TGT_VOLT, val);
}
+static int axp717_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
+ int val)
+{
+ switch (val) {
+ case 4000000:
+ val = AXP717_CHRG_CV_4_0V;
+ break;
+
+ case 4100000:
+ val = AXP717_CHRG_CV_4_1V;
+ break;
+
+ case 4200000:
+ val = AXP717_CHRG_CV_4_2V;
+ break;
+
+ default:
+ /*
+ * AXP717 can go up to 4.35, 4.4, and 5.0 volts which
+ * seem too high for lithium batteries, so do not allow.
+ */
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(axp20x_batt->regmap,
+ AXP717_CV_CHG_SET,
+ AXP717_CHRG_CV_VOLT_MASK, val);
+}
+
static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt,
int charge_current)
{
@@ -404,6 +696,24 @@ static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt,
AXP20X_CHRG_CTRL1_TGT_CURR, charge_current);
}
+static int axp717_set_constant_charge_current(struct axp20x_batt_ps *axp,
+ int charge_current)
+{
+ int val;
+
+ if (charge_current > axp->max_ccc)
+ return -EINVAL;
+
+ if (charge_current > AXP717_BAT_CC_MAX_UA || charge_current < 0)
+ return -EINVAL;
+
+ val = (charge_current - axp->data->ccc_offset) /
+ axp->data->ccc_scale;
+
+ return regmap_update_bits(axp->regmap, AXP717_ICC_CHG_SET,
+ AXP717_ICC_CHARGER_LIM, val);
+}
+
static int axp20x_set_max_constant_charge_current(struct axp20x_batt_ps *axp,
int charge_current)
{
@@ -448,6 +758,19 @@ static int axp20x_set_voltage_min_design(struct axp20x_batt_ps *axp_batt,
AXP20X_V_OFF_MASK, val1);
}
+static int axp717_set_voltage_min_design(struct axp20x_batt_ps *axp_batt,
+ int min_voltage)
+{
+ int val1 = (min_voltage - AXP717_BAT_VMIN_MIN_UV) / AXP717_BAT_VMIN_STEP;
+
+ if (val1 < 0 || val1 > AXP717_V_OFF_MASK)
+ return -EINVAL;
+
+ return regmap_update_bits(axp_batt->regmap,
+ AXP717_VSYS_V_POWEROFF,
+ AXP717_V_OFF_MASK, val1);
+}
+
static int axp20x_battery_set_prop(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
@@ -484,6 +807,42 @@ static int axp20x_battery_set_prop(struct power_supply *psy,
}
}
+static int axp717_battery_set_prop(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ return axp717_set_voltage_min_design(axp20x_batt, val->intval);
+
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ return axp20x_batt->data->set_max_voltage(axp20x_batt, val->intval);
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ return axp717_set_constant_charge_current(axp20x_batt,
+ val->intval);
+ case POWER_SUPPLY_PROP_STATUS:
+ switch (val->intval) {
+ case POWER_SUPPLY_STATUS_CHARGING:
+ return regmap_update_bits(axp20x_batt->regmap,
+ AXP717_MODULE_EN_CONTROL_2,
+ AXP717_CHRG_ENABLE,
+ AXP717_CHRG_ENABLE);
+
+ case POWER_SUPPLY_STATUS_DISCHARGING:
+ case POWER_SUPPLY_STATUS_NOT_CHARGING:
+ return regmap_update_bits(axp20x_batt->regmap,
+ AXP717_MODULE_EN_CONTROL_2,
+ AXP717_CHRG_ENABLE, 0);
+ }
+ fallthrough;
+ default:
+ return -EINVAL;
+ }
+}
+
static enum power_supply_property axp20x_battery_props[] = {
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
@@ -498,6 +857,20 @@ static enum power_supply_property axp20x_battery_props[] = {
POWER_SUPPLY_PROP_CAPACITY,
};
+static enum power_supply_property axp717_battery_props[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
+};
+
static int axp20x_battery_prop_writeable(struct power_supply *psy,
enum power_supply_property psp)
{
@@ -508,7 +881,16 @@ static int axp20x_battery_prop_writeable(struct power_supply *psy,
psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
}
-static const struct power_supply_desc axp20x_batt_ps_desc = {
+static int axp717_battery_prop_writeable(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ return psp == POWER_SUPPLY_PROP_STATUS ||
+ psp == POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN ||
+ psp == POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN ||
+ psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
+}
+
+static const struct power_supply_desc axp209_batt_ps_desc = {
.name = "axp20x-battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = axp20x_battery_props,
@@ -518,27 +900,159 @@ static const struct power_supply_desc axp20x_batt_ps_desc = {
.set_property = axp20x_battery_set_prop,
};
+static const struct power_supply_desc axp717_batt_ps_desc = {
+ .name = "axp20x-battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = axp717_battery_props,
+ .num_properties = ARRAY_SIZE(axp717_battery_props),
+ .property_is_writeable = axp717_battery_prop_writeable,
+ .get_property = axp717_battery_get_prop,
+ .set_property = axp717_battery_set_prop,
+};
+
+static int axp209_bat_cfg_iio_channels(struct platform_device *pdev,
+ struct axp20x_batt_ps *axp_batt)
+{
+ axp_batt->batt_v = devm_iio_channel_get(&pdev->dev, "batt_v");
+ if (IS_ERR(axp_batt->batt_v)) {
+ if (PTR_ERR(axp_batt->batt_v) == -ENODEV)
+ return -EPROBE_DEFER;
+ return PTR_ERR(axp_batt->batt_v);
+ }
+
+ axp_batt->batt_chrg_i = devm_iio_channel_get(&pdev->dev,
+ "batt_chrg_i");
+ if (IS_ERR(axp_batt->batt_chrg_i)) {
+ if (PTR_ERR(axp_batt->batt_chrg_i) == -ENODEV)
+ return -EPROBE_DEFER;
+ return PTR_ERR(axp_batt->batt_chrg_i);
+ }
+
+ axp_batt->batt_dischrg_i = devm_iio_channel_get(&pdev->dev,
+ "batt_dischrg_i");
+ if (IS_ERR(axp_batt->batt_dischrg_i)) {
+ if (PTR_ERR(axp_batt->batt_dischrg_i) == -ENODEV)
+ return -EPROBE_DEFER;
+ return PTR_ERR(axp_batt->batt_dischrg_i);
+ }
+
+ return 0;
+}
+
+static int axp717_bat_cfg_iio_channels(struct platform_device *pdev,
+ struct axp20x_batt_ps *axp_batt)
+{
+ axp_batt->batt_v = devm_iio_channel_get(&pdev->dev, "batt_v");
+ if (IS_ERR(axp_batt->batt_v)) {
+ if (PTR_ERR(axp_batt->batt_v) == -ENODEV)
+ return -EPROBE_DEFER;
+ return PTR_ERR(axp_batt->batt_v);
+ }
+
+ axp_batt->batt_chrg_i = devm_iio_channel_get(&pdev->dev,
+ "batt_chrg_i");
+ if (IS_ERR(axp_batt->batt_chrg_i)) {
+ if (PTR_ERR(axp_batt->batt_chrg_i) == -ENODEV)
+ return -EPROBE_DEFER;
+ return PTR_ERR(axp_batt->batt_chrg_i);
+ }
+
+ return 0;
+}
+
+static void axp209_set_battery_info(struct platform_device *pdev,
+ struct axp20x_batt_ps *axp_batt,
+ struct power_supply_battery_info *info)
+{
+ int vmin = info->voltage_min_design_uv;
+ int ccc = info->constant_charge_current_max_ua;
+
+ if (vmin > 0 && axp20x_set_voltage_min_design(axp_batt, vmin))
+ dev_err(&pdev->dev,
+ "couldn't set voltage_min_design\n");
+
+ /* Set max to unverified value to be able to set CCC */
+ axp_batt->max_ccc = ccc;
+
+ if (ccc <= 0 || axp20x_set_constant_charge_current(axp_batt, ccc)) {
+ dev_err(&pdev->dev,
+ "couldn't set ccc from DT: fallback to min value\n");
+ ccc = 300000;
+ axp_batt->max_ccc = ccc;
+ axp20x_set_constant_charge_current(axp_batt, ccc);
+ }
+}
+
+static void axp717_set_battery_info(struct platform_device *pdev,
+ struct axp20x_batt_ps *axp_batt,
+ struct power_supply_battery_info *info)
+{
+ int vmin = info->voltage_min_design_uv;
+ int vmax = info->voltage_max_design_uv;
+ int ccc = info->constant_charge_current_max_ua;
+ int val;
+
+ if (vmin > 0 && axp717_set_voltage_min_design(axp_batt, vmin))
+ dev_err(&pdev->dev,
+ "couldn't set voltage_min_design\n");
+
+ if (vmax > 0 && axp717_battery_set_max_voltage(axp_batt, vmax))
+ dev_err(&pdev->dev,
+ "couldn't set voltage_max_design\n");
+
+ axp717_get_constant_charge_current(axp_batt, &val);
+ axp_batt->max_ccc = ccc;
+ if (ccc <= 0 || axp717_set_constant_charge_current(axp_batt, ccc)) {
+ dev_err(&pdev->dev,
+ "couldn't set ccc from DT: current ccc is %d\n",
+ val);
+ }
+}
+
static const struct axp_data axp209_data = {
.ccc_scale = 100000,
.ccc_offset = 300000,
+ .bat_ps_desc = &axp209_batt_ps_desc,
.get_max_voltage = axp20x_battery_get_max_voltage,
.set_max_voltage = axp20x_battery_set_max_voltage,
+ .cfg_iio_chan = axp209_bat_cfg_iio_channels,
+ .set_bat_info = axp209_set_battery_info,
+ .get_constant_chg_cur = axp20x_get_constant_charge_current,
};
static const struct axp_data axp221_data = {
.ccc_scale = 150000,
.ccc_offset = 300000,
.has_fg_valid = true,
+ .bat_ps_desc = &axp209_batt_ps_desc,
.get_max_voltage = axp22x_battery_get_max_voltage,
.set_max_voltage = axp22x_battery_set_max_voltage,
+ .cfg_iio_chan = axp209_bat_cfg_iio_channels,
+ .set_bat_info = axp209_set_battery_info,
+ .get_constant_chg_cur = axp20x_get_constant_charge_current,
+};
+
+static const struct axp_data axp717_data = {
+ .ccc_scale = 64000,
+ .ccc_offset = 0,
+ .bat_ps_desc = &axp717_batt_ps_desc,
+ .get_max_voltage = axp717_battery_get_max_voltage,
+ .set_max_voltage = axp717_battery_set_max_voltage,
+ .cfg_iio_chan = axp717_bat_cfg_iio_channels,
+ .set_bat_info = axp717_set_battery_info,
+ .get_constant_chg_cur = axp717_get_constant_charge_current,
};
static const struct axp_data axp813_data = {
.ccc_scale = 200000,
.ccc_offset = 200000,
.has_fg_valid = true,
+ .bat_ps_desc = &axp209_batt_ps_desc,
.get_max_voltage = axp813_battery_get_max_voltage,
.set_max_voltage = axp20x_battery_set_max_voltage,
+ .cfg_iio_chan = axp209_bat_cfg_iio_channels,
+ .set_bat_info = axp209_set_battery_info,
+ .get_constant_chg_cur = axp20x_get_constant_charge_current,
};
static const struct of_device_id axp20x_battery_ps_id[] = {
@@ -548,6 +1062,9 @@ static const struct of_device_id axp20x_battery_ps_id[] = {
}, {
.compatible = "x-powers,axp221-battery-power-supply",
.data = (void *)&axp221_data,
+ }, {
+ .compatible = "x-powers,axp717-battery-power-supply",
+ .data = (void *)&axp717_data,
}, {
.compatible = "x-powers,axp813-battery-power-supply",
.data = (void *)&axp813_data,
@@ -561,6 +1078,7 @@ static int axp20x_power_probe(struct platform_device *pdev)
struct power_supply_config psy_cfg = {};
struct power_supply_battery_info *info;
struct device *dev = &pdev->dev;
+ int ret;
if (!of_device_is_available(pdev->dev.of_node))
return -ENODEV;
@@ -572,29 +1090,6 @@ static int axp20x_power_probe(struct platform_device *pdev)
axp20x_batt->dev = &pdev->dev;
- axp20x_batt->batt_v = devm_iio_channel_get(&pdev->dev, "batt_v");
- if (IS_ERR(axp20x_batt->batt_v)) {
- if (PTR_ERR(axp20x_batt->batt_v) == -ENODEV)
- return -EPROBE_DEFER;
- return PTR_ERR(axp20x_batt->batt_v);
- }
-
- axp20x_batt->batt_chrg_i = devm_iio_channel_get(&pdev->dev,
- "batt_chrg_i");
- if (IS_ERR(axp20x_batt->batt_chrg_i)) {
- if (PTR_ERR(axp20x_batt->batt_chrg_i) == -ENODEV)
- return -EPROBE_DEFER;
- return PTR_ERR(axp20x_batt->batt_chrg_i);
- }
-
- axp20x_batt->batt_dischrg_i = devm_iio_channel_get(&pdev->dev,
- "batt_dischrg_i");
- if (IS_ERR(axp20x_batt->batt_dischrg_i)) {
- if (PTR_ERR(axp20x_batt->batt_dischrg_i) == -ENODEV)
- return -EPROBE_DEFER;
- return PTR_ERR(axp20x_batt->batt_dischrg_i);
- }
-
axp20x_batt->regmap = dev_get_regmap(pdev->dev.parent, NULL);
platform_set_drvdata(pdev, axp20x_batt);
@@ -603,8 +1098,12 @@ static int axp20x_power_probe(struct platform_device *pdev)
axp20x_batt->data = (struct axp_data *)of_device_get_match_data(dev);
+ ret = axp20x_batt->data->cfg_iio_chan(pdev, axp20x_batt);
+ if (ret)
+ return ret;
+
axp20x_batt->batt = devm_power_supply_register(&pdev->dev,
- &axp20x_batt_ps_desc,
+ axp20x_batt->data->bat_ps_desc,
&psy_cfg);
if (IS_ERR(axp20x_batt->batt)) {
dev_err(&pdev->dev, "failed to register power supply: %ld\n",
@@ -613,33 +1112,16 @@ static int axp20x_power_probe(struct platform_device *pdev)
}
if (!power_supply_get_battery_info(axp20x_batt->batt, &info)) {
- int vmin = info->voltage_min_design_uv;
- int ccc = info->constant_charge_current_max_ua;
-
- if (vmin > 0 && axp20x_set_voltage_min_design(axp20x_batt,
- vmin))
- dev_err(&pdev->dev,
- "couldn't set voltage_min_design\n");
-
- /* Set max to unverified value to be able to set CCC */
- axp20x_batt->max_ccc = ccc;
-
- if (ccc <= 0 || axp20x_set_constant_charge_current(axp20x_batt,
- ccc)) {
- dev_err(&pdev->dev,
- "couldn't set constant charge current from DT: fallback to minimum value\n");
- ccc = 300000;
- axp20x_batt->max_ccc = ccc;
- axp20x_set_constant_charge_current(axp20x_batt, ccc);
- }
+ axp20x_batt->data->set_bat_info(pdev, axp20x_batt, info);
+ power_supply_put_battery_info(axp20x_batt->batt, info);
}
/*
* Update max CCC to a valid value if battery info is present or set it
* to current register value by default.
*/
- axp20x_get_constant_charge_current(axp20x_batt,
- &axp20x_batt->max_ccc);
+ axp20x_batt->data->get_constant_chg_cur(axp20x_batt,
+ &axp20x_batt->max_ccc);
return 0;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 28+ messages in thread* Re: [PATCH 7/8] power: supply: axp20x_battery: add support for AXP717
2024-06-17 22:05 ` [PATCH 7/8] power: supply: axp20x_battery: add " Chris Morgan
@ 2024-06-22 23:51 ` Ryan Walklin
2024-06-23 6:19 ` Chen-Yu Tsai
2024-06-24 17:56 ` Philippe Simons
2 siblings, 0 replies; 28+ messages in thread
From: Ryan Walklin @ 2024-06-22 23:51 UTC (permalink / raw)
To: Chris Morgan, linux-sunxi
Cc: linux-pm, devicetree, linux-iio, Mark Brown, Lee Jones,
Samuel Holland, Jernej Skrabec, sre, Chen-Yu Tsai, Conor Dooley,
Krzysztof Kozlowski, Rob Herring, lars, Chris Morgan
On Tue, 18 Jun 2024, at 10:05 AM, Chris Morgan wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
Thanks Chris!
> Add support for the AXP717 PMIC battery charger. The AXP717 differs
> greatly from existing AXP battery chargers in that it cannot measure
> the discharge current.
The battery does seem to report a discharge current with no USB power connected, do you think this is not accurate?
localhost:/sys/class/power_supply/axp20x-battery# cat current_now
433000
Otherwise Working charge state, percentage and current/voltage. Thanks!
Tested-by: Ryan Walklin <ryan@testtoast.com>
Regards,
Ryan
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 7/8] power: supply: axp20x_battery: add support for AXP717
2024-06-17 22:05 ` [PATCH 7/8] power: supply: axp20x_battery: add " Chris Morgan
2024-06-22 23:51 ` Ryan Walklin
@ 2024-06-23 6:19 ` Chen-Yu Tsai
2024-07-30 19:38 ` Chris Morgan
2024-06-24 17:56 ` Philippe Simons
2 siblings, 1 reply; 28+ messages in thread
From: Chen-Yu Tsai @ 2024-06-23 6:19 UTC (permalink / raw)
To: Chris Morgan
Cc: linux-sunxi, linux-pm, devicetree, linux-iio, broonie, lee,
samuel, jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
On Mon, Jun 17, 2024 at 05:05:34PM -0500, Chris Morgan wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add support for the AXP717 PMIC battery charger. The AXP717 differs
> greatly from existing AXP battery chargers in that it cannot measure
> the discharge current. The datasheet does not document the current
> value's offset or scale, so the POWER_SUPPLY_PROP_CURRENT_NOW is left
> unscaled.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
> ---
> drivers/power/supply/axp20x_battery.c | 580 +++++++++++++++++++++++---
> 1 file changed, 531 insertions(+), 49 deletions(-)
>
> diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c
> index 6ac5c80cfda2..f09a56948642 100644
> --- a/drivers/power/supply/axp20x_battery.c
> +++ b/drivers/power/supply/axp20x_battery.c
> @@ -32,9 +32,19 @@
> #include <linux/mfd/axp20x.h>
>
> #define AXP20X_PWR_STATUS_BAT_CHARGING BIT(2)
> +#define AXP717_PWR_STATUS_MASK GENMASK(6, 5)
> +#define AXP717_PWR_STATUS_BAT_STANDBY (0 << 5)
> +#define AXP717_PWR_STATUS_BAT_CHRG (1 << 5)
> +#define AXP717_PWR_STATUS_BAT_DISCHRG (2 << 5)
>
> #define AXP20X_PWR_OP_BATT_PRESENT BIT(5)
> #define AXP20X_PWR_OP_BATT_ACTIVATED BIT(3)
> +#define AXP717_PWR_OP_BATT_PRESENT BIT(3)
> +
> +#define AXP717_BATT_PMU_FAULT_MASK GENMASK(2, 0)
> +#define AXP717_BATT_UVLO_2_5V (1 << 2)
> +#define AXP717_BATT_OVER_TEMP (1 << 1)
> +#define AXP717_BATT_UNDER_TEMP (1 << 0)
>
> #define AXP209_FG_PERCENT GENMASK(6, 0)
> #define AXP22X_FG_VALID BIT(7)
> @@ -49,20 +59,50 @@
> #define AXP22X_CHRG_CTRL1_TGT_4_22V (1 << 5)
> #define AXP22X_CHRG_CTRL1_TGT_4_24V (3 << 5)
>
> +#define AXP717_CHRG_ENABLE BIT(1)
> +#define AXP717_CHRG_CV_VOLT_MASK GENMASK(2, 0)
> +#define AXP717_CHRG_CV_4_0V 0
> +#define AXP717_CHRG_CV_4_1V 1
> +#define AXP717_CHRG_CV_4_2V 2
> +#define AXP717_CHRG_CV_4_35V 3
> +#define AXP717_CHRG_CV_4_4V 4
> +/* Values 5 and 6 reserved. */
> +#define AXP717_CHRG_CV_5_0V 7
> +
> #define AXP813_CHRG_CTRL1_TGT_4_35V (3 << 5)
>
> #define AXP20X_CHRG_CTRL1_TGT_CURR GENMASK(3, 0)
> +#define AXP717_ICC_CHARGER_LIM GENMASK(5, 0)
AXP717_ICC_CHARGER_LIM_MASK
> +
> +#define AXP717_ITERM_CHG_LIM GENMASK(3, 0)
AXP717_ITERM_CHG_LIM_MASK
> +#define AXP717_ITERM_CC_STEP 64000
>
> #define AXP20X_V_OFF_MASK GENMASK(2, 0)
> +#define AXP717_V_OFF_MASK GENMASK(6, 4)
> +
> +#define AXP717_BAT_VMIN_MIN_UV 2600000
> +#define AXP717_BAT_VMIN_MAX_UV 3300000
> +#define AXP717_BAT_VMIN_STEP 100000
> +#define AXP717_BAT_CV_MIN_UV 4000000
> +#define AXP717_BAT_CV_MAX_UV 5000000
> +#define AXP717_BAT_CC_MIN_UA 0
> +#define AXP717_BAT_CC_MAX_UA 3008000
>
> struct axp20x_batt_ps;
>
> struct axp_data {
> - int ccc_scale;
> - int ccc_offset;
> - bool has_fg_valid;
> + int ccc_scale;
> + int ccc_offset;
> + bool has_fg_valid;
> + const struct power_supply_desc *bat_ps_desc;
> int (*get_max_voltage)(struct axp20x_batt_ps *batt, int *val);
> int (*set_max_voltage)(struct axp20x_batt_ps *batt, int val);
> + int (*cfg_iio_chan)(struct platform_device *pdev,
> + struct axp20x_batt_ps *axp_batt);
> + void (*set_bat_info)(struct platform_device *pdev,
> + struct axp20x_batt_ps *axp_batt,
> + struct power_supply_battery_info *info);
> + int (*get_constant_chg_cur)(struct axp20x_batt_ps *axp, int *val);
Instead of adding a callback that is half different, probably just add
fields for the CCC register offset and mask values to generalize the
existing function.
Please split out changes that generalize the driver from the axp717
specific changes.
> };
>
> struct axp20x_batt_ps {
> @@ -135,6 +175,41 @@ static int axp22x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> return 0;
> }
>
> +static int axp717_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> + int *val)
> +{
> + int ret, reg;
> +
> + ret = regmap_read(axp20x_batt->regmap, AXP717_CV_CHG_SET, ®);
> + if (ret)
> + return ret;
> +
> + switch (reg & AXP717_CHRG_CV_VOLT_MASK) {
> + case AXP717_CHRG_CV_4_0V:
> + *val = 4000000;
> + break;
> + case AXP717_CHRG_CV_4_1V:
> + *val = 4100000;
> + break;
> + case AXP717_CHRG_CV_4_2V:
> + *val = 4200000;
> + break;
> + case AXP717_CHRG_CV_4_35V:
> + *val = 4350000;
> + break;
> + case AXP717_CHRG_CV_4_4V:
> + *val = 4400000;
> + break;
> + case AXP717_CHRG_CV_5_0V:
> + *val = 5000000;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> static int axp813_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> int *val)
> {
> @@ -180,6 +255,22 @@ static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp,
> return 0;
> }
>
> +static int axp717_get_constant_charge_current(struct axp20x_batt_ps *axp,
> + int *val)
> +{
> + int ret;
> +
> + ret = regmap_read(axp->regmap, AXP717_ICC_CHG_SET, val);
> + if (ret)
> + return ret;
> +
> + *val &= AXP717_ICC_CHARGER_LIM;
> +
> + *val = *val * axp->data->ccc_scale;
> +
> + return 0;
> +}
> +
> static int axp20x_battery_get_prop(struct power_supply *psy,
> enum power_supply_property psp,
> union power_supply_propval *val)
> @@ -332,6 +423,178 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
> return 0;
> }
>
> +static int axp717_battery_get_prop(struct power_supply *psy,
> + enum power_supply_property psp,
> + union power_supply_propval *val)
> +{
> + struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy);
> + int ret = 0, reg;
> +
> + switch (psp) {
> + case POWER_SUPPLY_PROP_PRESENT:
> + case POWER_SUPPLY_PROP_ONLINE:
> + ret = regmap_read(axp20x_batt->regmap, AXP717_ON_INDICATE,
> + ®);
> + if (ret)
> + return ret;
> +
> + val->intval = !!(reg & AXP717_PWR_OP_BATT_PRESENT);
> + break;
The battery could be present but not online if some fault were to
happen. Please differentiate the two.
> +
> + case POWER_SUPPLY_PROP_STATUS:
> + ret = regmap_read(axp20x_batt->regmap, AXP717_PMU_STATUS_2,
> + ®);
> + if (ret)
> + return ret;
> +
> + switch (reg & AXP717_PWR_STATUS_MASK) {
> + case AXP717_PWR_STATUS_BAT_STANDBY:
> + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
> + break;
> +
> + case AXP717_PWR_STATUS_BAT_CHRG:
> + val->intval = POWER_SUPPLY_STATUS_CHARGING;
> + break;
> +
> + case AXP717_PWR_STATUS_BAT_DISCHRG:
> + val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
> + break;
> +
> + default:
> + val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
> + }
> +
> + break;
> +
> + /*
> + * If a fault is detected it must also be cleared; if the
> + * condition persists it should reappear (This is an
> + * assumption, it's actually not documented). A restart was
Can you test and confirm your assumption?
Thanks
ChenYu
> + * not sufficient to clear the bit in testing despite the
> + * register listed as POR.
> + */
> + case POWER_SUPPLY_PROP_HEALTH:
> + ret = regmap_read(axp20x_batt->regmap, AXP717_PMU_FAULT,
> + ®);
> + if (ret)
> + return ret;
> +
> + switch (reg & AXP717_BATT_PMU_FAULT_MASK) {
> + case AXP717_BATT_UVLO_2_5V:
> + val->intval = POWER_SUPPLY_HEALTH_DEAD;
> + regmap_update_bits(axp20x_batt->regmap,
> + AXP717_PMU_FAULT,
> + AXP717_BATT_UVLO_2_5V,
> + AXP717_BATT_UVLO_2_5V);
> + break;
> +
> + case AXP717_BATT_OVER_TEMP:
> + val->intval = POWER_SUPPLY_HEALTH_HOT;
> + regmap_update_bits(axp20x_batt->regmap,
> + AXP717_PMU_FAULT,
> + AXP717_BATT_OVER_TEMP,
> + AXP717_BATT_OVER_TEMP);
> + break;
> +
> + case AXP717_BATT_UNDER_TEMP:
> + val->intval = POWER_SUPPLY_HEALTH_COLD;
> + regmap_update_bits(axp20x_batt->regmap,
> + AXP717_PMU_FAULT,
> + AXP717_BATT_UNDER_TEMP,
> + AXP717_BATT_UNDER_TEMP);
> + break;
> +
> + default:
> + val->intval = POWER_SUPPLY_HEALTH_GOOD;
> + }
> +
> + break;
> +
> + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
> + ret = axp717_get_constant_charge_current(axp20x_batt,
> + &val->intval);
> + if (ret)
> + return ret;
> + break;
> +
> + case POWER_SUPPLY_PROP_CURRENT_NOW:
> + /*
> + * The offset of this value is currently unknown and is
> + * not documented in the datasheet. Based on
> + * observation it's assumed to be somewhere around
> + * 450ma. I will leave the value raw for now.
> + */
> + ret = iio_read_channel_processed(axp20x_batt->batt_chrg_i, &val->intval);
> + if (ret)
> + return ret;
> + /* IIO framework gives mA but Power Supply framework gives uA */
> + val->intval *= 1000;
> + break;
> +
> + case POWER_SUPPLY_PROP_CAPACITY:
> + /* When no battery is present, return capacity is 100% */
> + ret = regmap_read(axp20x_batt->regmap, AXP717_ON_INDICATE,
> + ®);
> + if (ret)
> + return ret;
> +
> + if (!(reg & AXP717_PWR_OP_BATT_PRESENT)) {
> + val->intval = 100;
> + return 0;
Maybe return an error instead? 100 percent on an imaginary battery
doesn't make sense.
> + }
> +
> + ret = regmap_read(axp20x_batt->regmap,
> + AXP717_BATT_PERCENT_DATA, ®);
> + if (ret)
> + return ret;
> +
> + /*
> + * Fuel Gauge data takes 7 bits but the stored value seems to be
> + * directly the raw percentage without any scaling to 7 bits.
> + */
> + val->intval = reg & AXP209_FG_PERCENT;
> + break;
> +
> + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
> + return axp20x_batt->data->get_max_voltage(axp20x_batt,
> + &val->intval);
> +
> + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
> + ret = regmap_read(axp20x_batt->regmap,
> + AXP717_VSYS_V_POWEROFF, ®);
> + if (ret)
> + return ret;
> +
> + val->intval = AXP717_BAT_VMIN_MIN_UV + AXP717_BAT_VMIN_STEP *
> + (reg & AXP717_V_OFF_MASK);
> + break;
These should probably be POWER_SUPPLY_PROP_VOLTAGE_{MIN,MAX}.
POWER_SUPPLY_PROP_VOLTAGE_{MIN,MAX}_DESIGN should be the values
specified in the battery device node with the
voltage-{min,max}-design-microvolt properties.
The former are configurable limits for the charger, while the latter
are physical limits of the battery.
Could you also fix the existing values?
> +
> + case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> + ret = iio_read_channel_processed(axp20x_batt->batt_v,
> + &val->intval);
> + if (ret)
> + return ret;
> +
> + /* IIO framework gives mV but Power Supply framework gives uV */
> + val->intval *= 1000;
> + break;
> +
> + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
> + ret = regmap_read(axp20x_batt->regmap,
> + AXP717_ITERM_CHG_SET, ®);
> + if (ret)
> + return ret;
> +
> + val->intval = (reg & AXP717_ITERM_CHG_LIM) * AXP717_ITERM_CC_STEP;
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> static int axp22x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> int val)
> {
> @@ -388,6 +651,35 @@ static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> AXP20X_CHRG_CTRL1_TGT_VOLT, val);
> }
>
> +static int axp717_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> + int val)
> +{
> + switch (val) {
> + case 4000000:
> + val = AXP717_CHRG_CV_4_0V;
> + break;
> +
> + case 4100000:
> + val = AXP717_CHRG_CV_4_1V;
> + break;
> +
> + case 4200000:
> + val = AXP717_CHRG_CV_4_2V;
> + break;
> +
> + default:
> + /*
> + * AXP717 can go up to 4.35, 4.4, and 5.0 volts which
> + * seem too high for lithium batteries, so do not allow.
> + */
> + return -EINVAL;
> + }
> +
> + return regmap_update_bits(axp20x_batt->regmap,
> + AXP717_CV_CHG_SET,
> + AXP717_CHRG_CV_VOLT_MASK, val);
> +}
> +
> static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt,
> int charge_current)
> {
> @@ -404,6 +696,24 @@ static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt,
> AXP20X_CHRG_CTRL1_TGT_CURR, charge_current);
> }
>
> +static int axp717_set_constant_charge_current(struct axp20x_batt_ps *axp,
> + int charge_current)
> +{
> + int val;
> +
> + if (charge_current > axp->max_ccc)
> + return -EINVAL;
> +
> + if (charge_current > AXP717_BAT_CC_MAX_UA || charge_current < 0)
> + return -EINVAL;
> +
> + val = (charge_current - axp->data->ccc_offset) /
> + axp->data->ccc_scale;
> +
> + return regmap_update_bits(axp->regmap, AXP717_ICC_CHG_SET,
> + AXP717_ICC_CHARGER_LIM, val);
> +}
> +
> static int axp20x_set_max_constant_charge_current(struct axp20x_batt_ps *axp,
> int charge_current)
> {
> @@ -448,6 +758,19 @@ static int axp20x_set_voltage_min_design(struct axp20x_batt_ps *axp_batt,
> AXP20X_V_OFF_MASK, val1);
> }
>
> +static int axp717_set_voltage_min_design(struct axp20x_batt_ps *axp_batt,
> + int min_voltage)
> +{
> + int val1 = (min_voltage - AXP717_BAT_VMIN_MIN_UV) / AXP717_BAT_VMIN_STEP;
> +
> + if (val1 < 0 || val1 > AXP717_V_OFF_MASK)
> + return -EINVAL;
> +
> + return regmap_update_bits(axp_batt->regmap,
> + AXP717_VSYS_V_POWEROFF,
> + AXP717_V_OFF_MASK, val1);
> +}
> +
> static int axp20x_battery_set_prop(struct power_supply *psy,
> enum power_supply_property psp,
> const union power_supply_propval *val)
> @@ -484,6 +807,42 @@ static int axp20x_battery_set_prop(struct power_supply *psy,
> }
> }
>
> +static int axp717_battery_set_prop(struct power_supply *psy,
> + enum power_supply_property psp,
> + const union power_supply_propval *val)
> +{
> + struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy);
> +
> + switch (psp) {
> + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
> + return axp717_set_voltage_min_design(axp20x_batt, val->intval);
> +
> + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
> + return axp20x_batt->data->set_max_voltage(axp20x_batt, val->intval);
> +
> + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
> + return axp717_set_constant_charge_current(axp20x_batt,
> + val->intval);
> + case POWER_SUPPLY_PROP_STATUS:
> + switch (val->intval) {
> + case POWER_SUPPLY_STATUS_CHARGING:
> + return regmap_update_bits(axp20x_batt->regmap,
> + AXP717_MODULE_EN_CONTROL_2,
> + AXP717_CHRG_ENABLE,
> + AXP717_CHRG_ENABLE);
> +
> + case POWER_SUPPLY_STATUS_DISCHARGING:
> + case POWER_SUPPLY_STATUS_NOT_CHARGING:
> + return regmap_update_bits(axp20x_batt->regmap,
> + AXP717_MODULE_EN_CONTROL_2,
> + AXP717_CHRG_ENABLE, 0);
> + }
> + fallthrough;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> static enum power_supply_property axp20x_battery_props[] = {
> POWER_SUPPLY_PROP_PRESENT,
> POWER_SUPPLY_PROP_ONLINE,
> @@ -498,6 +857,20 @@ static enum power_supply_property axp20x_battery_props[] = {
> POWER_SUPPLY_PROP_CAPACITY,
> };
>
> +static enum power_supply_property axp717_battery_props[] = {
> + POWER_SUPPLY_PROP_PRESENT,
> + POWER_SUPPLY_PROP_ONLINE,
> + POWER_SUPPLY_PROP_STATUS,
> + POWER_SUPPLY_PROP_VOLTAGE_NOW,
> + POWER_SUPPLY_PROP_CURRENT_NOW,
> + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
> + POWER_SUPPLY_PROP_HEALTH,
> + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
> + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
> + POWER_SUPPLY_PROP_CAPACITY,
> + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
> +};
> +
> static int axp20x_battery_prop_writeable(struct power_supply *psy,
> enum power_supply_property psp)
> {
> @@ -508,7 +881,16 @@ static int axp20x_battery_prop_writeable(struct power_supply *psy,
> psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
> }
>
> -static const struct power_supply_desc axp20x_batt_ps_desc = {
> +static int axp717_battery_prop_writeable(struct power_supply *psy,
> + enum power_supply_property psp)
> +{
> + return psp == POWER_SUPPLY_PROP_STATUS ||
> + psp == POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN ||
> + psp == POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN ||
> + psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
> +}
> +
> +static const struct power_supply_desc axp209_batt_ps_desc = {
> .name = "axp20x-battery",
> .type = POWER_SUPPLY_TYPE_BATTERY,
> .properties = axp20x_battery_props,
> @@ -518,27 +900,159 @@ static const struct power_supply_desc axp20x_batt_ps_desc = {
> .set_property = axp20x_battery_set_prop,
> };
>
> +static const struct power_supply_desc axp717_batt_ps_desc = {
> + .name = "axp20x-battery",
> + .type = POWER_SUPPLY_TYPE_BATTERY,
> + .properties = axp717_battery_props,
> + .num_properties = ARRAY_SIZE(axp717_battery_props),
> + .property_is_writeable = axp717_battery_prop_writeable,
> + .get_property = axp717_battery_get_prop,
> + .set_property = axp717_battery_set_prop,
> +};
> +
> +static int axp209_bat_cfg_iio_channels(struct platform_device *pdev,
> + struct axp20x_batt_ps *axp_batt)
> +{
> + axp_batt->batt_v = devm_iio_channel_get(&pdev->dev, "batt_v");
> + if (IS_ERR(axp_batt->batt_v)) {
> + if (PTR_ERR(axp_batt->batt_v) == -ENODEV)
> + return -EPROBE_DEFER;
> + return PTR_ERR(axp_batt->batt_v);
> + }
> +
> + axp_batt->batt_chrg_i = devm_iio_channel_get(&pdev->dev,
> + "batt_chrg_i");
> + if (IS_ERR(axp_batt->batt_chrg_i)) {
> + if (PTR_ERR(axp_batt->batt_chrg_i) == -ENODEV)
> + return -EPROBE_DEFER;
> + return PTR_ERR(axp_batt->batt_chrg_i);
> + }
> +
> + axp_batt->batt_dischrg_i = devm_iio_channel_get(&pdev->dev,
> + "batt_dischrg_i");
> + if (IS_ERR(axp_batt->batt_dischrg_i)) {
> + if (PTR_ERR(axp_batt->batt_dischrg_i) == -ENODEV)
> + return -EPROBE_DEFER;
> + return PTR_ERR(axp_batt->batt_dischrg_i);
> + }
> +
> + return 0;
> +}
> +
> +static int axp717_bat_cfg_iio_channels(struct platform_device *pdev,
> + struct axp20x_batt_ps *axp_batt)
> +{
> + axp_batt->batt_v = devm_iio_channel_get(&pdev->dev, "batt_v");
> + if (IS_ERR(axp_batt->batt_v)) {
> + if (PTR_ERR(axp_batt->batt_v) == -ENODEV)
> + return -EPROBE_DEFER;
> + return PTR_ERR(axp_batt->batt_v);
> + }
> +
> + axp_batt->batt_chrg_i = devm_iio_channel_get(&pdev->dev,
> + "batt_chrg_i");
> + if (IS_ERR(axp_batt->batt_chrg_i)) {
> + if (PTR_ERR(axp_batt->batt_chrg_i) == -ENODEV)
> + return -EPROBE_DEFER;
> + return PTR_ERR(axp_batt->batt_chrg_i);
> + }
> +
> + return 0;
> +}
> +
> +static void axp209_set_battery_info(struct platform_device *pdev,
> + struct axp20x_batt_ps *axp_batt,
> + struct power_supply_battery_info *info)
> +{
> + int vmin = info->voltage_min_design_uv;
> + int ccc = info->constant_charge_current_max_ua;
> +
> + if (vmin > 0 && axp20x_set_voltage_min_design(axp_batt, vmin))
> + dev_err(&pdev->dev,
> + "couldn't set voltage_min_design\n");
> +
> + /* Set max to unverified value to be able to set CCC */
> + axp_batt->max_ccc = ccc;
> +
> + if (ccc <= 0 || axp20x_set_constant_charge_current(axp_batt, ccc)) {
> + dev_err(&pdev->dev,
> + "couldn't set ccc from DT: fallback to min value\n");
> + ccc = 300000;
> + axp_batt->max_ccc = ccc;
> + axp20x_set_constant_charge_current(axp_batt, ccc);
> + }
> +}
> +
> +static void axp717_set_battery_info(struct platform_device *pdev,
> + struct axp20x_batt_ps *axp_batt,
> + struct power_supply_battery_info *info)
> +{
> + int vmin = info->voltage_min_design_uv;
> + int vmax = info->voltage_max_design_uv;
> + int ccc = info->constant_charge_current_max_ua;
> + int val;
> +
> + if (vmin > 0 && axp717_set_voltage_min_design(axp_batt, vmin))
> + dev_err(&pdev->dev,
> + "couldn't set voltage_min_design\n");
> +
> + if (vmax > 0 && axp717_battery_set_max_voltage(axp_batt, vmax))
> + dev_err(&pdev->dev,
> + "couldn't set voltage_max_design\n");
> +
> + axp717_get_constant_charge_current(axp_batt, &val);
> + axp_batt->max_ccc = ccc;
> + if (ccc <= 0 || axp717_set_constant_charge_current(axp_batt, ccc)) {
> + dev_err(&pdev->dev,
> + "couldn't set ccc from DT: current ccc is %d\n",
> + val);
> + }
> +}
> +
> static const struct axp_data axp209_data = {
> .ccc_scale = 100000,
> .ccc_offset = 300000,
> + .bat_ps_desc = &axp209_batt_ps_desc,
> .get_max_voltage = axp20x_battery_get_max_voltage,
> .set_max_voltage = axp20x_battery_set_max_voltage,
> + .cfg_iio_chan = axp209_bat_cfg_iio_channels,
> + .set_bat_info = axp209_set_battery_info,
> + .get_constant_chg_cur = axp20x_get_constant_charge_current,
> };
>
> static const struct axp_data axp221_data = {
> .ccc_scale = 150000,
> .ccc_offset = 300000,
> .has_fg_valid = true,
> + .bat_ps_desc = &axp209_batt_ps_desc,
> .get_max_voltage = axp22x_battery_get_max_voltage,
> .set_max_voltage = axp22x_battery_set_max_voltage,
> + .cfg_iio_chan = axp209_bat_cfg_iio_channels,
> + .set_bat_info = axp209_set_battery_info,
> + .get_constant_chg_cur = axp20x_get_constant_charge_current,
> +};
> +
> +static const struct axp_data axp717_data = {
> + .ccc_scale = 64000,
> + .ccc_offset = 0,
> + .bat_ps_desc = &axp717_batt_ps_desc,
> + .get_max_voltage = axp717_battery_get_max_voltage,
> + .set_max_voltage = axp717_battery_set_max_voltage,
> + .cfg_iio_chan = axp717_bat_cfg_iio_channels,
> + .set_bat_info = axp717_set_battery_info,
> + .get_constant_chg_cur = axp717_get_constant_charge_current,
> };
>
> static const struct axp_data axp813_data = {
> .ccc_scale = 200000,
> .ccc_offset = 200000,
> .has_fg_valid = true,
> + .bat_ps_desc = &axp209_batt_ps_desc,
> .get_max_voltage = axp813_battery_get_max_voltage,
> .set_max_voltage = axp20x_battery_set_max_voltage,
> + .cfg_iio_chan = axp209_bat_cfg_iio_channels,
> + .set_bat_info = axp209_set_battery_info,
> + .get_constant_chg_cur = axp20x_get_constant_charge_current,
> };
>
> static const struct of_device_id axp20x_battery_ps_id[] = {
> @@ -548,6 +1062,9 @@ static const struct of_device_id axp20x_battery_ps_id[] = {
> }, {
> .compatible = "x-powers,axp221-battery-power-supply",
> .data = (void *)&axp221_data,
> + }, {
> + .compatible = "x-powers,axp717-battery-power-supply",
> + .data = (void *)&axp717_data,
> }, {
> .compatible = "x-powers,axp813-battery-power-supply",
> .data = (void *)&axp813_data,
> @@ -561,6 +1078,7 @@ static int axp20x_power_probe(struct platform_device *pdev)
> struct power_supply_config psy_cfg = {};
> struct power_supply_battery_info *info;
> struct device *dev = &pdev->dev;
> + int ret;
>
> if (!of_device_is_available(pdev->dev.of_node))
> return -ENODEV;
> @@ -572,29 +1090,6 @@ static int axp20x_power_probe(struct platform_device *pdev)
>
> axp20x_batt->dev = &pdev->dev;
>
> - axp20x_batt->batt_v = devm_iio_channel_get(&pdev->dev, "batt_v");
> - if (IS_ERR(axp20x_batt->batt_v)) {
> - if (PTR_ERR(axp20x_batt->batt_v) == -ENODEV)
> - return -EPROBE_DEFER;
> - return PTR_ERR(axp20x_batt->batt_v);
> - }
> -
> - axp20x_batt->batt_chrg_i = devm_iio_channel_get(&pdev->dev,
> - "batt_chrg_i");
> - if (IS_ERR(axp20x_batt->batt_chrg_i)) {
> - if (PTR_ERR(axp20x_batt->batt_chrg_i) == -ENODEV)
> - return -EPROBE_DEFER;
> - return PTR_ERR(axp20x_batt->batt_chrg_i);
> - }
> -
> - axp20x_batt->batt_dischrg_i = devm_iio_channel_get(&pdev->dev,
> - "batt_dischrg_i");
> - if (IS_ERR(axp20x_batt->batt_dischrg_i)) {
> - if (PTR_ERR(axp20x_batt->batt_dischrg_i) == -ENODEV)
> - return -EPROBE_DEFER;
> - return PTR_ERR(axp20x_batt->batt_dischrg_i);
> - }
> -
> axp20x_batt->regmap = dev_get_regmap(pdev->dev.parent, NULL);
> platform_set_drvdata(pdev, axp20x_batt);
>
> @@ -603,8 +1098,12 @@ static int axp20x_power_probe(struct platform_device *pdev)
>
> axp20x_batt->data = (struct axp_data *)of_device_get_match_data(dev);
>
> + ret = axp20x_batt->data->cfg_iio_chan(pdev, axp20x_batt);
> + if (ret)
> + return ret;
> +
> axp20x_batt->batt = devm_power_supply_register(&pdev->dev,
> - &axp20x_batt_ps_desc,
> + axp20x_batt->data->bat_ps_desc,
> &psy_cfg);
> if (IS_ERR(axp20x_batt->batt)) {
> dev_err(&pdev->dev, "failed to register power supply: %ld\n",
> @@ -613,33 +1112,16 @@ static int axp20x_power_probe(struct platform_device *pdev)
> }
>
> if (!power_supply_get_battery_info(axp20x_batt->batt, &info)) {
> - int vmin = info->voltage_min_design_uv;
> - int ccc = info->constant_charge_current_max_ua;
> -
> - if (vmin > 0 && axp20x_set_voltage_min_design(axp20x_batt,
> - vmin))
> - dev_err(&pdev->dev,
> - "couldn't set voltage_min_design\n");
> -
> - /* Set max to unverified value to be able to set CCC */
> - axp20x_batt->max_ccc = ccc;
> -
> - if (ccc <= 0 || axp20x_set_constant_charge_current(axp20x_batt,
> - ccc)) {
> - dev_err(&pdev->dev,
> - "couldn't set constant charge current from DT: fallback to minimum value\n");
> - ccc = 300000;
> - axp20x_batt->max_ccc = ccc;
> - axp20x_set_constant_charge_current(axp20x_batt, ccc);
> - }
> + axp20x_batt->data->set_bat_info(pdev, axp20x_batt, info);
> + power_supply_put_battery_info(axp20x_batt->batt, info);
> }
>
> /*
> * Update max CCC to a valid value if battery info is present or set it
> * to current register value by default.
> */
> - axp20x_get_constant_charge_current(axp20x_batt,
> - &axp20x_batt->max_ccc);
> + axp20x_batt->data->get_constant_chg_cur(axp20x_batt,
> + &axp20x_batt->max_ccc);
>
> return 0;
> }
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [PATCH 7/8] power: supply: axp20x_battery: add support for AXP717
2024-06-23 6:19 ` Chen-Yu Tsai
@ 2024-07-30 19:38 ` Chris Morgan
0 siblings, 0 replies; 28+ messages in thread
From: Chris Morgan @ 2024-07-30 19:38 UTC (permalink / raw)
To: Chen-Yu Tsai
Cc: Chris Morgan, linux-sunxi, linux-pm, devicetree, linux-iio,
broonie, lee, samuel, jernej.skrabec, sre, wens, conor+dt,
krzk+dt, robh, lars
On Sun, Jun 23, 2024 at 02:19:32PM +0800, Chen-Yu Tsai wrote:
> On Mon, Jun 17, 2024 at 05:05:34PM -0500, Chris Morgan wrote:
> > From: Chris Morgan <macromorgan@hotmail.com>
> >
> > Add support for the AXP717 PMIC battery charger. The AXP717 differs
> > greatly from existing AXP battery chargers in that it cannot measure
> > the discharge current. The datasheet does not document the current
> > value's offset or scale, so the POWER_SUPPLY_PROP_CURRENT_NOW is left
> > unscaled.
> >
> > Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
> > ---
> > drivers/power/supply/axp20x_battery.c | 580 +++++++++++++++++++++++---
> > 1 file changed, 531 insertions(+), 49 deletions(-)
> >
> > diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c
> > index 6ac5c80cfda2..f09a56948642 100644
> > --- a/drivers/power/supply/axp20x_battery.c
> > +++ b/drivers/power/supply/axp20x_battery.c
> > @@ -32,9 +32,19 @@
> > #include <linux/mfd/axp20x.h>
> >
> > #define AXP20X_PWR_STATUS_BAT_CHARGING BIT(2)
> > +#define AXP717_PWR_STATUS_MASK GENMASK(6, 5)
> > +#define AXP717_PWR_STATUS_BAT_STANDBY (0 << 5)
> > +#define AXP717_PWR_STATUS_BAT_CHRG (1 << 5)
> > +#define AXP717_PWR_STATUS_BAT_DISCHRG (2 << 5)
> >
> > #define AXP20X_PWR_OP_BATT_PRESENT BIT(5)
> > #define AXP20X_PWR_OP_BATT_ACTIVATED BIT(3)
> > +#define AXP717_PWR_OP_BATT_PRESENT BIT(3)
> > +
> > +#define AXP717_BATT_PMU_FAULT_MASK GENMASK(2, 0)
> > +#define AXP717_BATT_UVLO_2_5V (1 << 2)
> > +#define AXP717_BATT_OVER_TEMP (1 << 1)
> > +#define AXP717_BATT_UNDER_TEMP (1 << 0)
> >
> > #define AXP209_FG_PERCENT GENMASK(6, 0)
> > #define AXP22X_FG_VALID BIT(7)
> > @@ -49,20 +59,50 @@
> > #define AXP22X_CHRG_CTRL1_TGT_4_22V (1 << 5)
> > #define AXP22X_CHRG_CTRL1_TGT_4_24V (3 << 5)
> >
> > +#define AXP717_CHRG_ENABLE BIT(1)
> > +#define AXP717_CHRG_CV_VOLT_MASK GENMASK(2, 0)
> > +#define AXP717_CHRG_CV_4_0V 0
> > +#define AXP717_CHRG_CV_4_1V 1
> > +#define AXP717_CHRG_CV_4_2V 2
> > +#define AXP717_CHRG_CV_4_35V 3
> > +#define AXP717_CHRG_CV_4_4V 4
> > +/* Values 5 and 6 reserved. */
> > +#define AXP717_CHRG_CV_5_0V 7
> > +
> > #define AXP813_CHRG_CTRL1_TGT_4_35V (3 << 5)
> >
> > #define AXP20X_CHRG_CTRL1_TGT_CURR GENMASK(3, 0)
> > +#define AXP717_ICC_CHARGER_LIM GENMASK(5, 0)
>
> AXP717_ICC_CHARGER_LIM_MASK
Acknowledged.
>
> > +
> > +#define AXP717_ITERM_CHG_LIM GENMASK(3, 0)
>
> AXP717_ITERM_CHG_LIM_MASK
>
Acknowledged.
> > +#define AXP717_ITERM_CC_STEP 64000
> >
> > #define AXP20X_V_OFF_MASK GENMASK(2, 0)
> > +#define AXP717_V_OFF_MASK GENMASK(6, 4)
> > +
> > +#define AXP717_BAT_VMIN_MIN_UV 2600000
> > +#define AXP717_BAT_VMIN_MAX_UV 3300000
> > +#define AXP717_BAT_VMIN_STEP 100000
> > +#define AXP717_BAT_CV_MIN_UV 4000000
> > +#define AXP717_BAT_CV_MAX_UV 5000000
> > +#define AXP717_BAT_CC_MIN_UA 0
> > +#define AXP717_BAT_CC_MAX_UA 3008000
> >
> > struct axp20x_batt_ps;
> >
> > struct axp_data {
> > - int ccc_scale;
> > - int ccc_offset;
> > - bool has_fg_valid;
> > + int ccc_scale;
> > + int ccc_offset;
> > + bool has_fg_valid;
> > + const struct power_supply_desc *bat_ps_desc;
> > int (*get_max_voltage)(struct axp20x_batt_ps *batt, int *val);
> > int (*set_max_voltage)(struct axp20x_batt_ps *batt, int val);
> > + int (*cfg_iio_chan)(struct platform_device *pdev,
> > + struct axp20x_batt_ps *axp_batt);
> > + void (*set_bat_info)(struct platform_device *pdev,
> > + struct axp20x_batt_ps *axp_batt,
> > + struct power_supply_battery_info *info);
> > + int (*get_constant_chg_cur)(struct axp20x_batt_ps *axp, int *val);
>
> Instead of adding a callback that is half different, probably just add
> fields for the CCC register offset and mask values to generalize the
> existing function.
>
> Please split out changes that generalize the driver from the axp717
> specific changes.
>
Will do.
> > };
> >
> > struct axp20x_batt_ps {
> > @@ -135,6 +175,41 @@ static int axp22x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> > return 0;
> > }
> >
> > +static int axp717_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> > + int *val)
> > +{
> > + int ret, reg;
> > +
> > + ret = regmap_read(axp20x_batt->regmap, AXP717_CV_CHG_SET, ®);
> > + if (ret)
> > + return ret;
> > +
> > + switch (reg & AXP717_CHRG_CV_VOLT_MASK) {
> > + case AXP717_CHRG_CV_4_0V:
> > + *val = 4000000;
> > + break;
> > + case AXP717_CHRG_CV_4_1V:
> > + *val = 4100000;
> > + break;
> > + case AXP717_CHRG_CV_4_2V:
> > + *val = 4200000;
> > + break;
> > + case AXP717_CHRG_CV_4_35V:
> > + *val = 4350000;
> > + break;
> > + case AXP717_CHRG_CV_4_4V:
> > + *val = 4400000;
> > + break;
> > + case AXP717_CHRG_CV_5_0V:
> > + *val = 5000000;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > static int axp813_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> > int *val)
> > {
> > @@ -180,6 +255,22 @@ static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp,
> > return 0;
> > }
> >
> > +static int axp717_get_constant_charge_current(struct axp20x_batt_ps *axp,
> > + int *val)
> > +{
> > + int ret;
> > +
> > + ret = regmap_read(axp->regmap, AXP717_ICC_CHG_SET, val);
> > + if (ret)
> > + return ret;
> > +
> > + *val &= AXP717_ICC_CHARGER_LIM;
> > +
> > + *val = *val * axp->data->ccc_scale;
> > +
> > + return 0;
> > +}
> > +
> > static int axp20x_battery_get_prop(struct power_supply *psy,
> > enum power_supply_property psp,
> > union power_supply_propval *val)
> > @@ -332,6 +423,178 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
> > return 0;
> > }
> >
> > +static int axp717_battery_get_prop(struct power_supply *psy,
> > + enum power_supply_property psp,
> > + union power_supply_propval *val)
> > +{
> > + struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy);
> > + int ret = 0, reg;
> > +
> > + switch (psp) {
> > + case POWER_SUPPLY_PROP_PRESENT:
> > + case POWER_SUPPLY_PROP_ONLINE:
> > + ret = regmap_read(axp20x_batt->regmap, AXP717_ON_INDICATE,
> > + ®);
> > + if (ret)
> > + return ret;
> > +
> > + val->intval = !!(reg & AXP717_PWR_OP_BATT_PRESENT);
> > + break;
>
> The battery could be present but not online if some fault were to
> happen. Please differentiate the two.
>
I'm afraid I don't know how to differentiate this. I can show the
battery as online if it's discharging. Otherwise I can just check
for a battery fault and report offline if one is found.
> > +
> > + case POWER_SUPPLY_PROP_STATUS:
> > + ret = regmap_read(axp20x_batt->regmap, AXP717_PMU_STATUS_2,
> > + ®);
> > + if (ret)
> > + return ret;
> > +
> > + switch (reg & AXP717_PWR_STATUS_MASK) {
> > + case AXP717_PWR_STATUS_BAT_STANDBY:
> > + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
> > + break;
> > +
> > + case AXP717_PWR_STATUS_BAT_CHRG:
> > + val->intval = POWER_SUPPLY_STATUS_CHARGING;
> > + break;
> > +
> > + case AXP717_PWR_STATUS_BAT_DISCHRG:
> > + val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
> > + break;
> > +
> > + default:
> > + val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
> > + }
> > +
> > + break;
> > +
> > + /*
> > + * If a fault is detected it must also be cleared; if the
> > + * condition persists it should reappear (This is an
> > + * assumption, it's actually not documented). A restart was
>
> Can you test and confirm your assumption?
>
I'm afraid I cannot as I'm not sure how to trigger another fault (or
even what fault I saw previously). I do know that POR registers in the
data sheet are not getting reset except when pulling the battery, and
I do know that the datasheet says these registers are RW1C. I can
update the comment at least to state that information.
>
> Thanks
> ChenYu
>
>
> > + * not sufficient to clear the bit in testing despite the
> > + * register listed as POR.
> > + */
> > + case POWER_SUPPLY_PROP_HEALTH:
> > + ret = regmap_read(axp20x_batt->regmap, AXP717_PMU_FAULT,
> > + ®);
> > + if (ret)
> > + return ret;
> > +
> > + switch (reg & AXP717_BATT_PMU_FAULT_MASK) {
> > + case AXP717_BATT_UVLO_2_5V:
> > + val->intval = POWER_SUPPLY_HEALTH_DEAD;
> > + regmap_update_bits(axp20x_batt->regmap,
> > + AXP717_PMU_FAULT,
> > + AXP717_BATT_UVLO_2_5V,
> > + AXP717_BATT_UVLO_2_5V);
> > + break;
> > +
> > + case AXP717_BATT_OVER_TEMP:
> > + val->intval = POWER_SUPPLY_HEALTH_HOT;
> > + regmap_update_bits(axp20x_batt->regmap,
> > + AXP717_PMU_FAULT,
> > + AXP717_BATT_OVER_TEMP,
> > + AXP717_BATT_OVER_TEMP);
> > + break;
> > +
> > + case AXP717_BATT_UNDER_TEMP:
> > + val->intval = POWER_SUPPLY_HEALTH_COLD;
> > + regmap_update_bits(axp20x_batt->regmap,
> > + AXP717_PMU_FAULT,
> > + AXP717_BATT_UNDER_TEMP,
> > + AXP717_BATT_UNDER_TEMP);
> > + break;
> > +
> > + default:
> > + val->intval = POWER_SUPPLY_HEALTH_GOOD;
> > + }
> > +
> > + break;
> > +
> > + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
> > + ret = axp717_get_constant_charge_current(axp20x_batt,
> > + &val->intval);
> > + if (ret)
> > + return ret;
> > + break;
> > +
> > + case POWER_SUPPLY_PROP_CURRENT_NOW:
> > + /*
> > + * The offset of this value is currently unknown and is
> > + * not documented in the datasheet. Based on
> > + * observation it's assumed to be somewhere around
> > + * 450ma. I will leave the value raw for now.
> > + */
> > + ret = iio_read_channel_processed(axp20x_batt->batt_chrg_i, &val->intval);
> > + if (ret)
> > + return ret;
> > + /* IIO framework gives mA but Power Supply framework gives uA */
> > + val->intval *= 1000;
> > + break;
> > +
> > + case POWER_SUPPLY_PROP_CAPACITY:
> > + /* When no battery is present, return capacity is 100% */
> > + ret = regmap_read(axp20x_batt->regmap, AXP717_ON_INDICATE,
> > + ®);
> > + if (ret)
> > + return ret;
> > +
> > + if (!(reg & AXP717_PWR_OP_BATT_PRESENT)) {
> > + val->intval = 100;
> > + return 0;
>
> Maybe return an error instead? 100 percent on an imaginary battery
> doesn't make sense.
I was just copying the above behavior, but I'll change it for the 717.
>
> > + }
> > +
> > + ret = regmap_read(axp20x_batt->regmap,
> > + AXP717_BATT_PERCENT_DATA, ®);
> > + if (ret)
> > + return ret;
> > +
> > + /*
> > + * Fuel Gauge data takes 7 bits but the stored value seems to be
> > + * directly the raw percentage without any scaling to 7 bits.
> > + */
> > + val->intval = reg & AXP209_FG_PERCENT;
> > + break;
> > +
> > + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
> > + return axp20x_batt->data->get_max_voltage(axp20x_batt,
> > + &val->intval);
> > +
> > + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
> > + ret = regmap_read(axp20x_batt->regmap,
> > + AXP717_VSYS_V_POWEROFF, ®);
> > + if (ret)
> > + return ret;
> > +
> > + val->intval = AXP717_BAT_VMIN_MIN_UV + AXP717_BAT_VMIN_STEP *
> > + (reg & AXP717_V_OFF_MASK);
> > + break;
>
> These should probably be POWER_SUPPLY_PROP_VOLTAGE_{MIN,MAX}.
> POWER_SUPPLY_PROP_VOLTAGE_{MIN,MAX}_DESIGN should be the values
> specified in the battery device node with the
> voltage-{min,max}-design-microvolt properties.
>
> The former are configurable limits for the charger, while the latter
> are physical limits of the battery.
>
> Could you also fix the existing values?
Will do, thank you.
>
> > +
> > + case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> > + ret = iio_read_channel_processed(axp20x_batt->batt_v,
> > + &val->intval);
> > + if (ret)
> > + return ret;
> > +
> > + /* IIO framework gives mV but Power Supply framework gives uV */
> > + val->intval *= 1000;
> > + break;
> > +
> > + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
> > + ret = regmap_read(axp20x_batt->regmap,
> > + AXP717_ITERM_CHG_SET, ®);
> > + if (ret)
> > + return ret;
> > +
> > + val->intval = (reg & AXP717_ITERM_CHG_LIM) * AXP717_ITERM_CC_STEP;
> > + break;
> > +
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > static int axp22x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> > int val)
> > {
> > @@ -388,6 +651,35 @@ static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> > AXP20X_CHRG_CTRL1_TGT_VOLT, val);
> > }
> >
> > +static int axp717_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> > + int val)
> > +{
> > + switch (val) {
> > + case 4000000:
> > + val = AXP717_CHRG_CV_4_0V;
> > + break;
> > +
> > + case 4100000:
> > + val = AXP717_CHRG_CV_4_1V;
> > + break;
> > +
> > + case 4200000:
> > + val = AXP717_CHRG_CV_4_2V;
> > + break;
> > +
> > + default:
> > + /*
> > + * AXP717 can go up to 4.35, 4.4, and 5.0 volts which
> > + * seem too high for lithium batteries, so do not allow.
> > + */
> > + return -EINVAL;
> > + }
> > +
> > + return regmap_update_bits(axp20x_batt->regmap,
> > + AXP717_CV_CHG_SET,
> > + AXP717_CHRG_CV_VOLT_MASK, val);
> > +}
> > +
> > static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt,
> > int charge_current)
> > {
> > @@ -404,6 +696,24 @@ static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt,
> > AXP20X_CHRG_CTRL1_TGT_CURR, charge_current);
> > }
> >
> > +static int axp717_set_constant_charge_current(struct axp20x_batt_ps *axp,
> > + int charge_current)
> > +{
> > + int val;
> > +
> > + if (charge_current > axp->max_ccc)
> > + return -EINVAL;
> > +
> > + if (charge_current > AXP717_BAT_CC_MAX_UA || charge_current < 0)
> > + return -EINVAL;
> > +
> > + val = (charge_current - axp->data->ccc_offset) /
> > + axp->data->ccc_scale;
> > +
> > + return regmap_update_bits(axp->regmap, AXP717_ICC_CHG_SET,
> > + AXP717_ICC_CHARGER_LIM, val);
> > +}
> > +
> > static int axp20x_set_max_constant_charge_current(struct axp20x_batt_ps *axp,
> > int charge_current)
> > {
> > @@ -448,6 +758,19 @@ static int axp20x_set_voltage_min_design(struct axp20x_batt_ps *axp_batt,
> > AXP20X_V_OFF_MASK, val1);
> > }
> >
> > +static int axp717_set_voltage_min_design(struct axp20x_batt_ps *axp_batt,
> > + int min_voltage)
> > +{
> > + int val1 = (min_voltage - AXP717_BAT_VMIN_MIN_UV) / AXP717_BAT_VMIN_STEP;
> > +
> > + if (val1 < 0 || val1 > AXP717_V_OFF_MASK)
> > + return -EINVAL;
> > +
> > + return regmap_update_bits(axp_batt->regmap,
> > + AXP717_VSYS_V_POWEROFF,
> > + AXP717_V_OFF_MASK, val1);
> > +}
> > +
> > static int axp20x_battery_set_prop(struct power_supply *psy,
> > enum power_supply_property psp,
> > const union power_supply_propval *val)
> > @@ -484,6 +807,42 @@ static int axp20x_battery_set_prop(struct power_supply *psy,
> > }
> > }
> >
> > +static int axp717_battery_set_prop(struct power_supply *psy,
> > + enum power_supply_property psp,
> > + const union power_supply_propval *val)
> > +{
> > + struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy);
> > +
> > + switch (psp) {
> > + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
> > + return axp717_set_voltage_min_design(axp20x_batt, val->intval);
> > +
> > + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
> > + return axp20x_batt->data->set_max_voltage(axp20x_batt, val->intval);
> > +
> > + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
> > + return axp717_set_constant_charge_current(axp20x_batt,
> > + val->intval);
> > + case POWER_SUPPLY_PROP_STATUS:
> > + switch (val->intval) {
> > + case POWER_SUPPLY_STATUS_CHARGING:
> > + return regmap_update_bits(axp20x_batt->regmap,
> > + AXP717_MODULE_EN_CONTROL_2,
> > + AXP717_CHRG_ENABLE,
> > + AXP717_CHRG_ENABLE);
> > +
> > + case POWER_SUPPLY_STATUS_DISCHARGING:
> > + case POWER_SUPPLY_STATUS_NOT_CHARGING:
> > + return regmap_update_bits(axp20x_batt->regmap,
> > + AXP717_MODULE_EN_CONTROL_2,
> > + AXP717_CHRG_ENABLE, 0);
> > + }
> > + fallthrough;
> > + default:
> > + return -EINVAL;
> > + }
> > +}
> > +
> > static enum power_supply_property axp20x_battery_props[] = {
> > POWER_SUPPLY_PROP_PRESENT,
> > POWER_SUPPLY_PROP_ONLINE,
> > @@ -498,6 +857,20 @@ static enum power_supply_property axp20x_battery_props[] = {
> > POWER_SUPPLY_PROP_CAPACITY,
> > };
> >
> > +static enum power_supply_property axp717_battery_props[] = {
> > + POWER_SUPPLY_PROP_PRESENT,
> > + POWER_SUPPLY_PROP_ONLINE,
> > + POWER_SUPPLY_PROP_STATUS,
> > + POWER_SUPPLY_PROP_VOLTAGE_NOW,
> > + POWER_SUPPLY_PROP_CURRENT_NOW,
> > + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
> > + POWER_SUPPLY_PROP_HEALTH,
> > + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
> > + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
> > + POWER_SUPPLY_PROP_CAPACITY,
> > + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
> > +};
> > +
> > static int axp20x_battery_prop_writeable(struct power_supply *psy,
> > enum power_supply_property psp)
> > {
> > @@ -508,7 +881,16 @@ static int axp20x_battery_prop_writeable(struct power_supply *psy,
> > psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
> > }
> >
> > -static const struct power_supply_desc axp20x_batt_ps_desc = {
> > +static int axp717_battery_prop_writeable(struct power_supply *psy,
> > + enum power_supply_property psp)
> > +{
> > + return psp == POWER_SUPPLY_PROP_STATUS ||
> > + psp == POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN ||
> > + psp == POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN ||
> > + psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
> > +}
> > +
> > +static const struct power_supply_desc axp209_batt_ps_desc = {
> > .name = "axp20x-battery",
> > .type = POWER_SUPPLY_TYPE_BATTERY,
> > .properties = axp20x_battery_props,
> > @@ -518,27 +900,159 @@ static const struct power_supply_desc axp20x_batt_ps_desc = {
> > .set_property = axp20x_battery_set_prop,
> > };
> >
> > +static const struct power_supply_desc axp717_batt_ps_desc = {
> > + .name = "axp20x-battery",
> > + .type = POWER_SUPPLY_TYPE_BATTERY,
> > + .properties = axp717_battery_props,
> > + .num_properties = ARRAY_SIZE(axp717_battery_props),
> > + .property_is_writeable = axp717_battery_prop_writeable,
> > + .get_property = axp717_battery_get_prop,
> > + .set_property = axp717_battery_set_prop,
> > +};
> > +
> > +static int axp209_bat_cfg_iio_channels(struct platform_device *pdev,
> > + struct axp20x_batt_ps *axp_batt)
> > +{
> > + axp_batt->batt_v = devm_iio_channel_get(&pdev->dev, "batt_v");
> > + if (IS_ERR(axp_batt->batt_v)) {
> > + if (PTR_ERR(axp_batt->batt_v) == -ENODEV)
> > + return -EPROBE_DEFER;
> > + return PTR_ERR(axp_batt->batt_v);
> > + }
> > +
> > + axp_batt->batt_chrg_i = devm_iio_channel_get(&pdev->dev,
> > + "batt_chrg_i");
> > + if (IS_ERR(axp_batt->batt_chrg_i)) {
> > + if (PTR_ERR(axp_batt->batt_chrg_i) == -ENODEV)
> > + return -EPROBE_DEFER;
> > + return PTR_ERR(axp_batt->batt_chrg_i);
> > + }
> > +
> > + axp_batt->batt_dischrg_i = devm_iio_channel_get(&pdev->dev,
> > + "batt_dischrg_i");
> > + if (IS_ERR(axp_batt->batt_dischrg_i)) {
> > + if (PTR_ERR(axp_batt->batt_dischrg_i) == -ENODEV)
> > + return -EPROBE_DEFER;
> > + return PTR_ERR(axp_batt->batt_dischrg_i);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int axp717_bat_cfg_iio_channels(struct platform_device *pdev,
> > + struct axp20x_batt_ps *axp_batt)
> > +{
> > + axp_batt->batt_v = devm_iio_channel_get(&pdev->dev, "batt_v");
> > + if (IS_ERR(axp_batt->batt_v)) {
> > + if (PTR_ERR(axp_batt->batt_v) == -ENODEV)
> > + return -EPROBE_DEFER;
> > + return PTR_ERR(axp_batt->batt_v);
> > + }
> > +
> > + axp_batt->batt_chrg_i = devm_iio_channel_get(&pdev->dev,
> > + "batt_chrg_i");
> > + if (IS_ERR(axp_batt->batt_chrg_i)) {
> > + if (PTR_ERR(axp_batt->batt_chrg_i) == -ENODEV)
> > + return -EPROBE_DEFER;
> > + return PTR_ERR(axp_batt->batt_chrg_i);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void axp209_set_battery_info(struct platform_device *pdev,
> > + struct axp20x_batt_ps *axp_batt,
> > + struct power_supply_battery_info *info)
> > +{
> > + int vmin = info->voltage_min_design_uv;
> > + int ccc = info->constant_charge_current_max_ua;
> > +
> > + if (vmin > 0 && axp20x_set_voltage_min_design(axp_batt, vmin))
> > + dev_err(&pdev->dev,
> > + "couldn't set voltage_min_design\n");
> > +
> > + /* Set max to unverified value to be able to set CCC */
> > + axp_batt->max_ccc = ccc;
> > +
> > + if (ccc <= 0 || axp20x_set_constant_charge_current(axp_batt, ccc)) {
> > + dev_err(&pdev->dev,
> > + "couldn't set ccc from DT: fallback to min value\n");
> > + ccc = 300000;
> > + axp_batt->max_ccc = ccc;
> > + axp20x_set_constant_charge_current(axp_batt, ccc);
> > + }
> > +}
> > +
> > +static void axp717_set_battery_info(struct platform_device *pdev,
> > + struct axp20x_batt_ps *axp_batt,
> > + struct power_supply_battery_info *info)
> > +{
> > + int vmin = info->voltage_min_design_uv;
> > + int vmax = info->voltage_max_design_uv;
> > + int ccc = info->constant_charge_current_max_ua;
> > + int val;
> > +
> > + if (vmin > 0 && axp717_set_voltage_min_design(axp_batt, vmin))
> > + dev_err(&pdev->dev,
> > + "couldn't set voltage_min_design\n");
> > +
> > + if (vmax > 0 && axp717_battery_set_max_voltage(axp_batt, vmax))
> > + dev_err(&pdev->dev,
> > + "couldn't set voltage_max_design\n");
> > +
> > + axp717_get_constant_charge_current(axp_batt, &val);
> > + axp_batt->max_ccc = ccc;
> > + if (ccc <= 0 || axp717_set_constant_charge_current(axp_batt, ccc)) {
> > + dev_err(&pdev->dev,
> > + "couldn't set ccc from DT: current ccc is %d\n",
> > + val);
> > + }
> > +}
> > +
> > static const struct axp_data axp209_data = {
> > .ccc_scale = 100000,
> > .ccc_offset = 300000,
> > + .bat_ps_desc = &axp209_batt_ps_desc,
> > .get_max_voltage = axp20x_battery_get_max_voltage,
> > .set_max_voltage = axp20x_battery_set_max_voltage,
> > + .cfg_iio_chan = axp209_bat_cfg_iio_channels,
> > + .set_bat_info = axp209_set_battery_info,
> > + .get_constant_chg_cur = axp20x_get_constant_charge_current,
> > };
> >
> > static const struct axp_data axp221_data = {
> > .ccc_scale = 150000,
> > .ccc_offset = 300000,
> > .has_fg_valid = true,
> > + .bat_ps_desc = &axp209_batt_ps_desc,
> > .get_max_voltage = axp22x_battery_get_max_voltage,
> > .set_max_voltage = axp22x_battery_set_max_voltage,
> > + .cfg_iio_chan = axp209_bat_cfg_iio_channels,
> > + .set_bat_info = axp209_set_battery_info,
> > + .get_constant_chg_cur = axp20x_get_constant_charge_current,
> > +};
> > +
> > +static const struct axp_data axp717_data = {
> > + .ccc_scale = 64000,
> > + .ccc_offset = 0,
> > + .bat_ps_desc = &axp717_batt_ps_desc,
> > + .get_max_voltage = axp717_battery_get_max_voltage,
> > + .set_max_voltage = axp717_battery_set_max_voltage,
> > + .cfg_iio_chan = axp717_bat_cfg_iio_channels,
> > + .set_bat_info = axp717_set_battery_info,
> > + .get_constant_chg_cur = axp717_get_constant_charge_current,
> > };
> >
> > static const struct axp_data axp813_data = {
> > .ccc_scale = 200000,
> > .ccc_offset = 200000,
> > .has_fg_valid = true,
> > + .bat_ps_desc = &axp209_batt_ps_desc,
> > .get_max_voltage = axp813_battery_get_max_voltage,
> > .set_max_voltage = axp20x_battery_set_max_voltage,
> > + .cfg_iio_chan = axp209_bat_cfg_iio_channels,
> > + .set_bat_info = axp209_set_battery_info,
> > + .get_constant_chg_cur = axp20x_get_constant_charge_current,
> > };
> >
> > static const struct of_device_id axp20x_battery_ps_id[] = {
> > @@ -548,6 +1062,9 @@ static const struct of_device_id axp20x_battery_ps_id[] = {
> > }, {
> > .compatible = "x-powers,axp221-battery-power-supply",
> > .data = (void *)&axp221_data,
> > + }, {
> > + .compatible = "x-powers,axp717-battery-power-supply",
> > + .data = (void *)&axp717_data,
> > }, {
> > .compatible = "x-powers,axp813-battery-power-supply",
> > .data = (void *)&axp813_data,
> > @@ -561,6 +1078,7 @@ static int axp20x_power_probe(struct platform_device *pdev)
> > struct power_supply_config psy_cfg = {};
> > struct power_supply_battery_info *info;
> > struct device *dev = &pdev->dev;
> > + int ret;
> >
> > if (!of_device_is_available(pdev->dev.of_node))
> > return -ENODEV;
> > @@ -572,29 +1090,6 @@ static int axp20x_power_probe(struct platform_device *pdev)
> >
> > axp20x_batt->dev = &pdev->dev;
> >
> > - axp20x_batt->batt_v = devm_iio_channel_get(&pdev->dev, "batt_v");
> > - if (IS_ERR(axp20x_batt->batt_v)) {
> > - if (PTR_ERR(axp20x_batt->batt_v) == -ENODEV)
> > - return -EPROBE_DEFER;
> > - return PTR_ERR(axp20x_batt->batt_v);
> > - }
> > -
> > - axp20x_batt->batt_chrg_i = devm_iio_channel_get(&pdev->dev,
> > - "batt_chrg_i");
> > - if (IS_ERR(axp20x_batt->batt_chrg_i)) {
> > - if (PTR_ERR(axp20x_batt->batt_chrg_i) == -ENODEV)
> > - return -EPROBE_DEFER;
> > - return PTR_ERR(axp20x_batt->batt_chrg_i);
> > - }
> > -
> > - axp20x_batt->batt_dischrg_i = devm_iio_channel_get(&pdev->dev,
> > - "batt_dischrg_i");
> > - if (IS_ERR(axp20x_batt->batt_dischrg_i)) {
> > - if (PTR_ERR(axp20x_batt->batt_dischrg_i) == -ENODEV)
> > - return -EPROBE_DEFER;
> > - return PTR_ERR(axp20x_batt->batt_dischrg_i);
> > - }
> > -
> > axp20x_batt->regmap = dev_get_regmap(pdev->dev.parent, NULL);
> > platform_set_drvdata(pdev, axp20x_batt);
> >
> > @@ -603,8 +1098,12 @@ static int axp20x_power_probe(struct platform_device *pdev)
> >
> > axp20x_batt->data = (struct axp_data *)of_device_get_match_data(dev);
> >
> > + ret = axp20x_batt->data->cfg_iio_chan(pdev, axp20x_batt);
> > + if (ret)
> > + return ret;
> > +
> > axp20x_batt->batt = devm_power_supply_register(&pdev->dev,
> > - &axp20x_batt_ps_desc,
> > + axp20x_batt->data->bat_ps_desc,
> > &psy_cfg);
> > if (IS_ERR(axp20x_batt->batt)) {
> > dev_err(&pdev->dev, "failed to register power supply: %ld\n",
> > @@ -613,33 +1112,16 @@ static int axp20x_power_probe(struct platform_device *pdev)
> > }
> >
> > if (!power_supply_get_battery_info(axp20x_batt->batt, &info)) {
> > - int vmin = info->voltage_min_design_uv;
> > - int ccc = info->constant_charge_current_max_ua;
> > -
> > - if (vmin > 0 && axp20x_set_voltage_min_design(axp20x_batt,
> > - vmin))
> > - dev_err(&pdev->dev,
> > - "couldn't set voltage_min_design\n");
> > -
> > - /* Set max to unverified value to be able to set CCC */
> > - axp20x_batt->max_ccc = ccc;
> > -
> > - if (ccc <= 0 || axp20x_set_constant_charge_current(axp20x_batt,
> > - ccc)) {
> > - dev_err(&pdev->dev,
> > - "couldn't set constant charge current from DT: fallback to minimum value\n");
> > - ccc = 300000;
> > - axp20x_batt->max_ccc = ccc;
> > - axp20x_set_constant_charge_current(axp20x_batt, ccc);
> > - }
> > + axp20x_batt->data->set_bat_info(pdev, axp20x_batt, info);
> > + power_supply_put_battery_info(axp20x_batt->batt, info);
> > }
> >
> > /*
> > * Update max CCC to a valid value if battery info is present or set it
> > * to current register value by default.
> > */
> > - axp20x_get_constant_charge_current(axp20x_batt,
> > - &axp20x_batt->max_ccc);
> > + axp20x_batt->data->get_constant_chg_cur(axp20x_batt,
> > + &axp20x_batt->max_ccc);
> >
> > return 0;
> > }
> > --
> > 2.34.1
> >
Thank you,
Chris
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH 7/8] power: supply: axp20x_battery: add support for AXP717
2024-06-17 22:05 ` [PATCH 7/8] power: supply: axp20x_battery: add " Chris Morgan
2024-06-22 23:51 ` Ryan Walklin
2024-06-23 6:19 ` Chen-Yu Tsai
@ 2024-06-24 17:56 ` Philippe Simons
2 siblings, 0 replies; 28+ messages in thread
From: Philippe Simons @ 2024-06-24 17:56 UTC (permalink / raw)
To: Chris Morgan, linux-sunxi
Cc: linux-pm, devicetree, linux-iio, broonie, lee, samuel,
jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
sysfs correctly reports the presence, voltage and current of the
battery. Tested on RG35XX-H (H700)
Tested-by: Philippe Simons <simons.philippe@gmail.com>
On 18/06/24 00:05, Chris Morgan wrote:
> From: Chris Morgan <macromorgan@hotmail.com>
>
> Add support for the AXP717 PMIC battery charger. The AXP717 differs
> greatly from existing AXP battery chargers in that it cannot measure
> the discharge current. The datasheet does not document the current
> value's offset or scale, so the POWER_SUPPLY_PROP_CURRENT_NOW is left
> unscaled.
>
> Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
> ---
> drivers/power/supply/axp20x_battery.c | 580 +++++++++++++++++++++++---
> 1 file changed, 531 insertions(+), 49 deletions(-)
>
> diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c
> index 6ac5c80cfda2..f09a56948642 100644
> --- a/drivers/power/supply/axp20x_battery.c
> +++ b/drivers/power/supply/axp20x_battery.c
> @@ -32,9 +32,19 @@
> #include <linux/mfd/axp20x.h>
>
> #define AXP20X_PWR_STATUS_BAT_CHARGING BIT(2)
> +#define AXP717_PWR_STATUS_MASK GENMASK(6, 5)
> +#define AXP717_PWR_STATUS_BAT_STANDBY (0 << 5)
> +#define AXP717_PWR_STATUS_BAT_CHRG (1 << 5)
> +#define AXP717_PWR_STATUS_BAT_DISCHRG (2 << 5)
>
> #define AXP20X_PWR_OP_BATT_PRESENT BIT(5)
> #define AXP20X_PWR_OP_BATT_ACTIVATED BIT(3)
> +#define AXP717_PWR_OP_BATT_PRESENT BIT(3)
> +
> +#define AXP717_BATT_PMU_FAULT_MASK GENMASK(2, 0)
> +#define AXP717_BATT_UVLO_2_5V (1 << 2)
> +#define AXP717_BATT_OVER_TEMP (1 << 1)
> +#define AXP717_BATT_UNDER_TEMP (1 << 0)
>
> #define AXP209_FG_PERCENT GENMASK(6, 0)
> #define AXP22X_FG_VALID BIT(7)
> @@ -49,20 +59,50 @@
> #define AXP22X_CHRG_CTRL1_TGT_4_22V (1 << 5)
> #define AXP22X_CHRG_CTRL1_TGT_4_24V (3 << 5)
>
> +#define AXP717_CHRG_ENABLE BIT(1)
> +#define AXP717_CHRG_CV_VOLT_MASK GENMASK(2, 0)
> +#define AXP717_CHRG_CV_4_0V 0
> +#define AXP717_CHRG_CV_4_1V 1
> +#define AXP717_CHRG_CV_4_2V 2
> +#define AXP717_CHRG_CV_4_35V 3
> +#define AXP717_CHRG_CV_4_4V 4
> +/* Values 5 and 6 reserved. */
> +#define AXP717_CHRG_CV_5_0V 7
> +
> #define AXP813_CHRG_CTRL1_TGT_4_35V (3 << 5)
>
> #define AXP20X_CHRG_CTRL1_TGT_CURR GENMASK(3, 0)
> +#define AXP717_ICC_CHARGER_LIM GENMASK(5, 0)
> +
> +#define AXP717_ITERM_CHG_LIM GENMASK(3, 0)
> +#define AXP717_ITERM_CC_STEP 64000
>
> #define AXP20X_V_OFF_MASK GENMASK(2, 0)
> +#define AXP717_V_OFF_MASK GENMASK(6, 4)
> +
> +#define AXP717_BAT_VMIN_MIN_UV 2600000
> +#define AXP717_BAT_VMIN_MAX_UV 3300000
> +#define AXP717_BAT_VMIN_STEP 100000
> +#define AXP717_BAT_CV_MIN_UV 4000000
> +#define AXP717_BAT_CV_MAX_UV 5000000
> +#define AXP717_BAT_CC_MIN_UA 0
> +#define AXP717_BAT_CC_MAX_UA 3008000
>
> struct axp20x_batt_ps;
>
> struct axp_data {
> - int ccc_scale;
> - int ccc_offset;
> - bool has_fg_valid;
> + int ccc_scale;
> + int ccc_offset;
> + bool has_fg_valid;
> + const struct power_supply_desc *bat_ps_desc;
> int (*get_max_voltage)(struct axp20x_batt_ps *batt, int *val);
> int (*set_max_voltage)(struct axp20x_batt_ps *batt, int val);
> + int (*cfg_iio_chan)(struct platform_device *pdev,
> + struct axp20x_batt_ps *axp_batt);
> + void (*set_bat_info)(struct platform_device *pdev,
> + struct axp20x_batt_ps *axp_batt,
> + struct power_supply_battery_info *info);
> + int (*get_constant_chg_cur)(struct axp20x_batt_ps *axp, int *val);
> };
>
> struct axp20x_batt_ps {
> @@ -135,6 +175,41 @@ static int axp22x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> return 0;
> }
>
> +static int axp717_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> + int *val)
> +{
> + int ret, reg;
> +
> + ret = regmap_read(axp20x_batt->regmap, AXP717_CV_CHG_SET, ®);
> + if (ret)
> + return ret;
> +
> + switch (reg & AXP717_CHRG_CV_VOLT_MASK) {
> + case AXP717_CHRG_CV_4_0V:
> + *val = 4000000;
> + break;
> + case AXP717_CHRG_CV_4_1V:
> + *val = 4100000;
> + break;
> + case AXP717_CHRG_CV_4_2V:
> + *val = 4200000;
> + break;
> + case AXP717_CHRG_CV_4_35V:
> + *val = 4350000;
> + break;
> + case AXP717_CHRG_CV_4_4V:
> + *val = 4400000;
> + break;
> + case AXP717_CHRG_CV_5_0V:
> + *val = 5000000;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> static int axp813_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> int *val)
> {
> @@ -180,6 +255,22 @@ static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp,
> return 0;
> }
>
> +static int axp717_get_constant_charge_current(struct axp20x_batt_ps *axp,
> + int *val)
> +{
> + int ret;
> +
> + ret = regmap_read(axp->regmap, AXP717_ICC_CHG_SET, val);
> + if (ret)
> + return ret;
> +
> + *val &= AXP717_ICC_CHARGER_LIM;
> +
> + *val = *val * axp->data->ccc_scale;
> +
> + return 0;
> +}
> +
> static int axp20x_battery_get_prop(struct power_supply *psy,
> enum power_supply_property psp,
> union power_supply_propval *val)
> @@ -332,6 +423,178 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
> return 0;
> }
>
> +static int axp717_battery_get_prop(struct power_supply *psy,
> + enum power_supply_property psp,
> + union power_supply_propval *val)
> +{
> + struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy);
> + int ret = 0, reg;
> +
> + switch (psp) {
> + case POWER_SUPPLY_PROP_PRESENT:
> + case POWER_SUPPLY_PROP_ONLINE:
> + ret = regmap_read(axp20x_batt->regmap, AXP717_ON_INDICATE,
> + ®);
> + if (ret)
> + return ret;
> +
> + val->intval = !!(reg & AXP717_PWR_OP_BATT_PRESENT);
> + break;
> +
> + case POWER_SUPPLY_PROP_STATUS:
> + ret = regmap_read(axp20x_batt->regmap, AXP717_PMU_STATUS_2,
> + ®);
> + if (ret)
> + return ret;
> +
> + switch (reg & AXP717_PWR_STATUS_MASK) {
> + case AXP717_PWR_STATUS_BAT_STANDBY:
> + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
> + break;
> +
> + case AXP717_PWR_STATUS_BAT_CHRG:
> + val->intval = POWER_SUPPLY_STATUS_CHARGING;
> + break;
> +
> + case AXP717_PWR_STATUS_BAT_DISCHRG:
> + val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
> + break;
> +
> + default:
> + val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
> + }
> +
> + break;
> +
> + /*
> + * If a fault is detected it must also be cleared; if the
> + * condition persists it should reappear (This is an
> + * assumption, it's actually not documented). A restart was
> + * not sufficient to clear the bit in testing despite the
> + * register listed as POR.
> + */
> + case POWER_SUPPLY_PROP_HEALTH:
> + ret = regmap_read(axp20x_batt->regmap, AXP717_PMU_FAULT,
> + ®);
> + if (ret)
> + return ret;
> +
> + switch (reg & AXP717_BATT_PMU_FAULT_MASK) {
> + case AXP717_BATT_UVLO_2_5V:
> + val->intval = POWER_SUPPLY_HEALTH_DEAD;
> + regmap_update_bits(axp20x_batt->regmap,
> + AXP717_PMU_FAULT,
> + AXP717_BATT_UVLO_2_5V,
> + AXP717_BATT_UVLO_2_5V);
> + break;
> +
> + case AXP717_BATT_OVER_TEMP:
> + val->intval = POWER_SUPPLY_HEALTH_HOT;
> + regmap_update_bits(axp20x_batt->regmap,
> + AXP717_PMU_FAULT,
> + AXP717_BATT_OVER_TEMP,
> + AXP717_BATT_OVER_TEMP);
> + break;
> +
> + case AXP717_BATT_UNDER_TEMP:
> + val->intval = POWER_SUPPLY_HEALTH_COLD;
> + regmap_update_bits(axp20x_batt->regmap,
> + AXP717_PMU_FAULT,
> + AXP717_BATT_UNDER_TEMP,
> + AXP717_BATT_UNDER_TEMP);
> + break;
> +
> + default:
> + val->intval = POWER_SUPPLY_HEALTH_GOOD;
> + }
> +
> + break;
> +
> + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
> + ret = axp717_get_constant_charge_current(axp20x_batt,
> + &val->intval);
> + if (ret)
> + return ret;
> + break;
> +
> + case POWER_SUPPLY_PROP_CURRENT_NOW:
> + /*
> + * The offset of this value is currently unknown and is
> + * not documented in the datasheet. Based on
> + * observation it's assumed to be somewhere around
> + * 450ma. I will leave the value raw for now.
> + */
> + ret = iio_read_channel_processed(axp20x_batt->batt_chrg_i, &val->intval);
> + if (ret)
> + return ret;
> + /* IIO framework gives mA but Power Supply framework gives uA */
> + val->intval *= 1000;
> + break;
> +
> + case POWER_SUPPLY_PROP_CAPACITY:
> + /* When no battery is present, return capacity is 100% */
> + ret = regmap_read(axp20x_batt->regmap, AXP717_ON_INDICATE,
> + ®);
> + if (ret)
> + return ret;
> +
> + if (!(reg & AXP717_PWR_OP_BATT_PRESENT)) {
> + val->intval = 100;
> + return 0;
> + }
> +
> + ret = regmap_read(axp20x_batt->regmap,
> + AXP717_BATT_PERCENT_DATA, ®);
> + if (ret)
> + return ret;
> +
> + /*
> + * Fuel Gauge data takes 7 bits but the stored value seems to be
> + * directly the raw percentage without any scaling to 7 bits.
> + */
> + val->intval = reg & AXP209_FG_PERCENT;
> + break;
> +
> + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
> + return axp20x_batt->data->get_max_voltage(axp20x_batt,
> + &val->intval);
> +
> + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
> + ret = regmap_read(axp20x_batt->regmap,
> + AXP717_VSYS_V_POWEROFF, ®);
> + if (ret)
> + return ret;
> +
> + val->intval = AXP717_BAT_VMIN_MIN_UV + AXP717_BAT_VMIN_STEP *
> + (reg & AXP717_V_OFF_MASK);
> + break;
> +
> + case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> + ret = iio_read_channel_processed(axp20x_batt->batt_v,
> + &val->intval);
> + if (ret)
> + return ret;
> +
> + /* IIO framework gives mV but Power Supply framework gives uV */
> + val->intval *= 1000;
> + break;
> +
> + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
> + ret = regmap_read(axp20x_batt->regmap,
> + AXP717_ITERM_CHG_SET, ®);
> + if (ret)
> + return ret;
> +
> + val->intval = (reg & AXP717_ITERM_CHG_LIM) * AXP717_ITERM_CC_STEP;
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> static int axp22x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> int val)
> {
> @@ -388,6 +651,35 @@ static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> AXP20X_CHRG_CTRL1_TGT_VOLT, val);
> }
>
> +static int axp717_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
> + int val)
> +{
> + switch (val) {
> + case 4000000:
> + val = AXP717_CHRG_CV_4_0V;
> + break;
> +
> + case 4100000:
> + val = AXP717_CHRG_CV_4_1V;
> + break;
> +
> + case 4200000:
> + val = AXP717_CHRG_CV_4_2V;
> + break;
> +
> + default:
> + /*
> + * AXP717 can go up to 4.35, 4.4, and 5.0 volts which
> + * seem too high for lithium batteries, so do not allow.
> + */
> + return -EINVAL;
> + }
> +
> + return regmap_update_bits(axp20x_batt->regmap,
> + AXP717_CV_CHG_SET,
> + AXP717_CHRG_CV_VOLT_MASK, val);
> +}
> +
> static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt,
> int charge_current)
> {
> @@ -404,6 +696,24 @@ static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt,
> AXP20X_CHRG_CTRL1_TGT_CURR, charge_current);
> }
>
> +static int axp717_set_constant_charge_current(struct axp20x_batt_ps *axp,
> + int charge_current)
> +{
> + int val;
> +
> + if (charge_current > axp->max_ccc)
> + return -EINVAL;
> +
> + if (charge_current > AXP717_BAT_CC_MAX_UA || charge_current < 0)
> + return -EINVAL;
> +
> + val = (charge_current - axp->data->ccc_offset) /
> + axp->data->ccc_scale;
> +
> + return regmap_update_bits(axp->regmap, AXP717_ICC_CHG_SET,
> + AXP717_ICC_CHARGER_LIM, val);
> +}
> +
> static int axp20x_set_max_constant_charge_current(struct axp20x_batt_ps *axp,
> int charge_current)
> {
> @@ -448,6 +758,19 @@ static int axp20x_set_voltage_min_design(struct axp20x_batt_ps *axp_batt,
> AXP20X_V_OFF_MASK, val1);
> }
>
> +static int axp717_set_voltage_min_design(struct axp20x_batt_ps *axp_batt,
> + int min_voltage)
> +{
> + int val1 = (min_voltage - AXP717_BAT_VMIN_MIN_UV) / AXP717_BAT_VMIN_STEP;
> +
> + if (val1 < 0 || val1 > AXP717_V_OFF_MASK)
> + return -EINVAL;
> +
> + return regmap_update_bits(axp_batt->regmap,
> + AXP717_VSYS_V_POWEROFF,
> + AXP717_V_OFF_MASK, val1);
> +}
> +
> static int axp20x_battery_set_prop(struct power_supply *psy,
> enum power_supply_property psp,
> const union power_supply_propval *val)
> @@ -484,6 +807,42 @@ static int axp20x_battery_set_prop(struct power_supply *psy,
> }
> }
>
> +static int axp717_battery_set_prop(struct power_supply *psy,
> + enum power_supply_property psp,
> + const union power_supply_propval *val)
> +{
> + struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy);
> +
> + switch (psp) {
> + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
> + return axp717_set_voltage_min_design(axp20x_batt, val->intval);
> +
> + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
> + return axp20x_batt->data->set_max_voltage(axp20x_batt, val->intval);
> +
> + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
> + return axp717_set_constant_charge_current(axp20x_batt,
> + val->intval);
> + case POWER_SUPPLY_PROP_STATUS:
> + switch (val->intval) {
> + case POWER_SUPPLY_STATUS_CHARGING:
> + return regmap_update_bits(axp20x_batt->regmap,
> + AXP717_MODULE_EN_CONTROL_2,
> + AXP717_CHRG_ENABLE,
> + AXP717_CHRG_ENABLE);
> +
> + case POWER_SUPPLY_STATUS_DISCHARGING:
> + case POWER_SUPPLY_STATUS_NOT_CHARGING:
> + return regmap_update_bits(axp20x_batt->regmap,
> + AXP717_MODULE_EN_CONTROL_2,
> + AXP717_CHRG_ENABLE, 0);
> + }
> + fallthrough;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> static enum power_supply_property axp20x_battery_props[] = {
> POWER_SUPPLY_PROP_PRESENT,
> POWER_SUPPLY_PROP_ONLINE,
> @@ -498,6 +857,20 @@ static enum power_supply_property axp20x_battery_props[] = {
> POWER_SUPPLY_PROP_CAPACITY,
> };
>
> +static enum power_supply_property axp717_battery_props[] = {
> + POWER_SUPPLY_PROP_PRESENT,
> + POWER_SUPPLY_PROP_ONLINE,
> + POWER_SUPPLY_PROP_STATUS,
> + POWER_SUPPLY_PROP_VOLTAGE_NOW,
> + POWER_SUPPLY_PROP_CURRENT_NOW,
> + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
> + POWER_SUPPLY_PROP_HEALTH,
> + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
> + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
> + POWER_SUPPLY_PROP_CAPACITY,
> + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
> +};
> +
> static int axp20x_battery_prop_writeable(struct power_supply *psy,
> enum power_supply_property psp)
> {
> @@ -508,7 +881,16 @@ static int axp20x_battery_prop_writeable(struct power_supply *psy,
> psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
> }
>
> -static const struct power_supply_desc axp20x_batt_ps_desc = {
> +static int axp717_battery_prop_writeable(struct power_supply *psy,
> + enum power_supply_property psp)
> +{
> + return psp == POWER_SUPPLY_PROP_STATUS ||
> + psp == POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN ||
> + psp == POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN ||
> + psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX;
> +}
> +
> +static const struct power_supply_desc axp209_batt_ps_desc = {
> .name = "axp20x-battery",
> .type = POWER_SUPPLY_TYPE_BATTERY,
> .properties = axp20x_battery_props,
> @@ -518,27 +900,159 @@ static const struct power_supply_desc axp20x_batt_ps_desc = {
> .set_property = axp20x_battery_set_prop,
> };
>
> +static const struct power_supply_desc axp717_batt_ps_desc = {
> + .name = "axp20x-battery",
> + .type = POWER_SUPPLY_TYPE_BATTERY,
> + .properties = axp717_battery_props,
> + .num_properties = ARRAY_SIZE(axp717_battery_props),
> + .property_is_writeable = axp717_battery_prop_writeable,
> + .get_property = axp717_battery_get_prop,
> + .set_property = axp717_battery_set_prop,
> +};
> +
> +static int axp209_bat_cfg_iio_channels(struct platform_device *pdev,
> + struct axp20x_batt_ps *axp_batt)
> +{
> + axp_batt->batt_v = devm_iio_channel_get(&pdev->dev, "batt_v");
> + if (IS_ERR(axp_batt->batt_v)) {
> + if (PTR_ERR(axp_batt->batt_v) == -ENODEV)
> + return -EPROBE_DEFER;
> + return PTR_ERR(axp_batt->batt_v);
> + }
> +
> + axp_batt->batt_chrg_i = devm_iio_channel_get(&pdev->dev,
> + "batt_chrg_i");
> + if (IS_ERR(axp_batt->batt_chrg_i)) {
> + if (PTR_ERR(axp_batt->batt_chrg_i) == -ENODEV)
> + return -EPROBE_DEFER;
> + return PTR_ERR(axp_batt->batt_chrg_i);
> + }
> +
> + axp_batt->batt_dischrg_i = devm_iio_channel_get(&pdev->dev,
> + "batt_dischrg_i");
> + if (IS_ERR(axp_batt->batt_dischrg_i)) {
> + if (PTR_ERR(axp_batt->batt_dischrg_i) == -ENODEV)
> + return -EPROBE_DEFER;
> + return PTR_ERR(axp_batt->batt_dischrg_i);
> + }
> +
> + return 0;
> +}
> +
> +static int axp717_bat_cfg_iio_channels(struct platform_device *pdev,
> + struct axp20x_batt_ps *axp_batt)
> +{
> + axp_batt->batt_v = devm_iio_channel_get(&pdev->dev, "batt_v");
> + if (IS_ERR(axp_batt->batt_v)) {
> + if (PTR_ERR(axp_batt->batt_v) == -ENODEV)
> + return -EPROBE_DEFER;
> + return PTR_ERR(axp_batt->batt_v);
> + }
> +
> + axp_batt->batt_chrg_i = devm_iio_channel_get(&pdev->dev,
> + "batt_chrg_i");
> + if (IS_ERR(axp_batt->batt_chrg_i)) {
> + if (PTR_ERR(axp_batt->batt_chrg_i) == -ENODEV)
> + return -EPROBE_DEFER;
> + return PTR_ERR(axp_batt->batt_chrg_i);
> + }
> +
> + return 0;
> +}
> +
> +static void axp209_set_battery_info(struct platform_device *pdev,
> + struct axp20x_batt_ps *axp_batt,
> + struct power_supply_battery_info *info)
> +{
> + int vmin = info->voltage_min_design_uv;
> + int ccc = info->constant_charge_current_max_ua;
> +
> + if (vmin > 0 && axp20x_set_voltage_min_design(axp_batt, vmin))
> + dev_err(&pdev->dev,
> + "couldn't set voltage_min_design\n");
> +
> + /* Set max to unverified value to be able to set CCC */
> + axp_batt->max_ccc = ccc;
> +
> + if (ccc <= 0 || axp20x_set_constant_charge_current(axp_batt, ccc)) {
> + dev_err(&pdev->dev,
> + "couldn't set ccc from DT: fallback to min value\n");
> + ccc = 300000;
> + axp_batt->max_ccc = ccc;
> + axp20x_set_constant_charge_current(axp_batt, ccc);
> + }
> +}
> +
> +static void axp717_set_battery_info(struct platform_device *pdev,
> + struct axp20x_batt_ps *axp_batt,
> + struct power_supply_battery_info *info)
> +{
> + int vmin = info->voltage_min_design_uv;
> + int vmax = info->voltage_max_design_uv;
> + int ccc = info->constant_charge_current_max_ua;
> + int val;
> +
> + if (vmin > 0 && axp717_set_voltage_min_design(axp_batt, vmin))
> + dev_err(&pdev->dev,
> + "couldn't set voltage_min_design\n");
> +
> + if (vmax > 0 && axp717_battery_set_max_voltage(axp_batt, vmax))
> + dev_err(&pdev->dev,
> + "couldn't set voltage_max_design\n");
> +
> + axp717_get_constant_charge_current(axp_batt, &val);
> + axp_batt->max_ccc = ccc;
> + if (ccc <= 0 || axp717_set_constant_charge_current(axp_batt, ccc)) {
> + dev_err(&pdev->dev,
> + "couldn't set ccc from DT: current ccc is %d\n",
> + val);
> + }
> +}
> +
> static const struct axp_data axp209_data = {
> .ccc_scale = 100000,
> .ccc_offset = 300000,
> + .bat_ps_desc = &axp209_batt_ps_desc,
> .get_max_voltage = axp20x_battery_get_max_voltage,
> .set_max_voltage = axp20x_battery_set_max_voltage,
> + .cfg_iio_chan = axp209_bat_cfg_iio_channels,
> + .set_bat_info = axp209_set_battery_info,
> + .get_constant_chg_cur = axp20x_get_constant_charge_current,
> };
>
> static const struct axp_data axp221_data = {
> .ccc_scale = 150000,
> .ccc_offset = 300000,
> .has_fg_valid = true,
> + .bat_ps_desc = &axp209_batt_ps_desc,
> .get_max_voltage = axp22x_battery_get_max_voltage,
> .set_max_voltage = axp22x_battery_set_max_voltage,
> + .cfg_iio_chan = axp209_bat_cfg_iio_channels,
> + .set_bat_info = axp209_set_battery_info,
> + .get_constant_chg_cur = axp20x_get_constant_charge_current,
> +};
> +
> +static const struct axp_data axp717_data = {
> + .ccc_scale = 64000,
> + .ccc_offset = 0,
> + .bat_ps_desc = &axp717_batt_ps_desc,
> + .get_max_voltage = axp717_battery_get_max_voltage,
> + .set_max_voltage = axp717_battery_set_max_voltage,
> + .cfg_iio_chan = axp717_bat_cfg_iio_channels,
> + .set_bat_info = axp717_set_battery_info,
> + .get_constant_chg_cur = axp717_get_constant_charge_current,
> };
>
> static const struct axp_data axp813_data = {
> .ccc_scale = 200000,
> .ccc_offset = 200000,
> .has_fg_valid = true,
> + .bat_ps_desc = &axp209_batt_ps_desc,
> .get_max_voltage = axp813_battery_get_max_voltage,
> .set_max_voltage = axp20x_battery_set_max_voltage,
> + .cfg_iio_chan = axp209_bat_cfg_iio_channels,
> + .set_bat_info = axp209_set_battery_info,
> + .get_constant_chg_cur = axp20x_get_constant_charge_current,
> };
>
> static const struct of_device_id axp20x_battery_ps_id[] = {
> @@ -548,6 +1062,9 @@ static const struct of_device_id axp20x_battery_ps_id[] = {
> }, {
> .compatible = "x-powers,axp221-battery-power-supply",
> .data = (void *)&axp221_data,
> + }, {
> + .compatible = "x-powers,axp717-battery-power-supply",
> + .data = (void *)&axp717_data,
> }, {
> .compatible = "x-powers,axp813-battery-power-supply",
> .data = (void *)&axp813_data,
> @@ -561,6 +1078,7 @@ static int axp20x_power_probe(struct platform_device *pdev)
> struct power_supply_config psy_cfg = {};
> struct power_supply_battery_info *info;
> struct device *dev = &pdev->dev;
> + int ret;
>
> if (!of_device_is_available(pdev->dev.of_node))
> return -ENODEV;
> @@ -572,29 +1090,6 @@ static int axp20x_power_probe(struct platform_device *pdev)
>
> axp20x_batt->dev = &pdev->dev;
>
> - axp20x_batt->batt_v = devm_iio_channel_get(&pdev->dev, "batt_v");
> - if (IS_ERR(axp20x_batt->batt_v)) {
> - if (PTR_ERR(axp20x_batt->batt_v) == -ENODEV)
> - return -EPROBE_DEFER;
> - return PTR_ERR(axp20x_batt->batt_v);
> - }
> -
> - axp20x_batt->batt_chrg_i = devm_iio_channel_get(&pdev->dev,
> - "batt_chrg_i");
> - if (IS_ERR(axp20x_batt->batt_chrg_i)) {
> - if (PTR_ERR(axp20x_batt->batt_chrg_i) == -ENODEV)
> - return -EPROBE_DEFER;
> - return PTR_ERR(axp20x_batt->batt_chrg_i);
> - }
> -
> - axp20x_batt->batt_dischrg_i = devm_iio_channel_get(&pdev->dev,
> - "batt_dischrg_i");
> - if (IS_ERR(axp20x_batt->batt_dischrg_i)) {
> - if (PTR_ERR(axp20x_batt->batt_dischrg_i) == -ENODEV)
> - return -EPROBE_DEFER;
> - return PTR_ERR(axp20x_batt->batt_dischrg_i);
> - }
> -
> axp20x_batt->regmap = dev_get_regmap(pdev->dev.parent, NULL);
> platform_set_drvdata(pdev, axp20x_batt);
>
> @@ -603,8 +1098,12 @@ static int axp20x_power_probe(struct platform_device *pdev)
>
> axp20x_batt->data = (struct axp_data *)of_device_get_match_data(dev);
>
> + ret = axp20x_batt->data->cfg_iio_chan(pdev, axp20x_batt);
> + if (ret)
> + return ret;
> +
> axp20x_batt->batt = devm_power_supply_register(&pdev->dev,
> - &axp20x_batt_ps_desc,
> + axp20x_batt->data->bat_ps_desc,
> &psy_cfg);
> if (IS_ERR(axp20x_batt->batt)) {
> dev_err(&pdev->dev, "failed to register power supply: %ld\n",
> @@ -613,33 +1112,16 @@ static int axp20x_power_probe(struct platform_device *pdev)
> }
>
> if (!power_supply_get_battery_info(axp20x_batt->batt, &info)) {
> - int vmin = info->voltage_min_design_uv;
> - int ccc = info->constant_charge_current_max_ua;
> -
> - if (vmin > 0 && axp20x_set_voltage_min_design(axp20x_batt,
> - vmin))
> - dev_err(&pdev->dev,
> - "couldn't set voltage_min_design\n");
> -
> - /* Set max to unverified value to be able to set CCC */
> - axp20x_batt->max_ccc = ccc;
> -
> - if (ccc <= 0 || axp20x_set_constant_charge_current(axp20x_batt,
> - ccc)) {
> - dev_err(&pdev->dev,
> - "couldn't set constant charge current from DT: fallback to minimum value\n");
> - ccc = 300000;
> - axp20x_batt->max_ccc = ccc;
> - axp20x_set_constant_charge_current(axp20x_batt, ccc);
> - }
> + axp20x_batt->data->set_bat_info(pdev, axp20x_batt, info);
> + power_supply_put_battery_info(axp20x_batt->batt, info);
> }
>
> /*
> * Update max CCC to a valid value if battery info is present or set it
> * to current register value by default.
> */
> - axp20x_get_constant_charge_current(axp20x_batt,
> - &axp20x_batt->max_ccc);
> + axp20x_batt->data->get_constant_chg_cur(axp20x_batt,
> + &axp20x_batt->max_ccc);
>
> return 0;
> }
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH 8/8] arm64: dts: allwinner: h700: Add charger for Anbernic RG35XX
2024-06-17 22:05 [PATCH 0/8] Add Battery and USB Supply for AXP717 Chris Morgan
` (6 preceding siblings ...)
2024-06-17 22:05 ` [PATCH 7/8] power: supply: axp20x_battery: add " Chris Morgan
@ 2024-06-17 22:05 ` Chris Morgan
7 siblings, 0 replies; 28+ messages in thread
From: Chris Morgan @ 2024-06-17 22:05 UTC (permalink / raw)
To: linux-sunxi
Cc: linux-pm, devicetree, linux-iio, broonie, lee, samuel,
jernej.skrabec, sre, wens, conor+dt, krzk+dt, robh, lars,
Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add the necessary nodes to the AXP717 to allow for monitoring the USB
and battery charger.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
.../sun50i-h700-anbernic-rg35xx-2024.dts | 21 +++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h700-anbernic-rg35xx-2024.dts b/arch/arm64/boot/dts/allwinner/sun50i-h700-anbernic-rg35xx-2024.dts
index ee30584b6ad7..f15a5d193f7c 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h700-anbernic-rg35xx-2024.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h700-anbernic-rg35xx-2024.dts
@@ -21,6 +21,12 @@ aliases {
serial0 = &uart0;
};
+ battery: battery {
+ compatible = "simple-battery";
+ constant-charge-current-max-microamp = <1024000>;
+ voltage-max-design-microvolt = <4200000>;
+ };
+
chosen {
stdout-path = "serial0:115200n8";
};
@@ -217,6 +223,16 @@ axp717: pmic@3a3 {
vin3-supply = <®_vcc5v>;
vin4-supply = <®_vcc5v>;
+ axp_adc: adc {
+ compatible = "x-powers,axp717-adc";
+ #io-channel-cells = <1>;
+ };
+
+ battery_power: battery-power {
+ compatible = "x-powers,axp717-battery-power-supply";
+ monitored-battery = <&battery>;
+ };
+
regulators {
reg_dcdc1: dcdc1 {
regulator-always-on;
@@ -307,6 +323,11 @@ reg_cpusldo: cpusldo {
/* unused */
};
};
+
+ usb_power: usb-power {
+ compatible = "x-powers,axp717-usb-power-supply";
+ input-current-limit-microamp = <1500000>;
+ };
};
};
--
2.34.1
^ permalink raw reply related [flat|nested] 28+ messages in thread