* [PATCH v5 0/3] BQ25150/155 Batter charger @ 2020-05-01 17:51 Dan Murphy 2020-05-01 17:51 ` [PATCH v5 1/3] power_supply: Add additional health properties to the header Dan Murphy ` (2 more replies) 0 siblings, 3 replies; 8+ messages in thread From: Dan Murphy @ 2020-05-01 17:51 UTC (permalink / raw) To: sre; +Cc: linux-pm, linux-kernel, devicetree, Dan Murphy Hello, Resending for Ricardo This v5 series picks up on the development that Dan Murphy <dmurphy@ti.com> began with the power_supply framework and bq2515x_charger driver. This series incorporates the changes suggested by Sebastien Reichel<sre@kernel.org> in v4. Datasheets for these devices can be found at: http://www.ti.com/lit/ds/symlink/bq25150.pdf http://www.ti.com/lit/ds/symlink/bq25155.pdf Thanks, Ricardo Dan Murphy (1): power_supply: Add additional health properties to the header Ricardo Rivera-Matos (2): Add the bindings for the bq25150 and bq25155 500mA charging ICs from Texas Instruments. power: supply: bq25150 introduce the bq25150 Documentation/ABI/testing/sysfs-class-power | 2 +- .../bindings/power/supply/bq2515x.yaml | 99 ++ drivers/power/supply/Kconfig | 8 + drivers/power/supply/Makefile | 1 + drivers/power/supply/bq2515x_charger.c | 1170 +++++++++++++++++ drivers/power/supply/power_supply_sysfs.c | 2 +- include/linux/power_supply.h | 3 + 7 files changed, 1283 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/power/supply/bq2515x.yaml create mode 100644 drivers/power/supply/bq2515x_charger.c -- 2.25.1 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v5 1/3] power_supply: Add additional health properties to the header 2020-05-01 17:51 [PATCH v5 0/3] BQ25150/155 Batter charger Dan Murphy @ 2020-05-01 17:51 ` Dan Murphy 2020-05-03 0:30 ` Sebastian Reichel 2020-05-01 17:51 ` [PATCH v5 2/3] Add the bindings for the bq25150 and bq25155 500mA charging ICs from Texas Instruments Dan Murphy 2020-05-01 17:51 ` [PATCH v5 3/3] power: supply: bq25150 introduce the bq25150 Dan Murphy 2 siblings, 1 reply; 8+ messages in thread From: Dan Murphy @ 2020-05-01 17:51 UTC (permalink / raw) To: sre; +Cc: linux-pm, linux-kernel, devicetree, Dan Murphy, Guru Das Srinagesh Add HEALTH_WARM, HEALTH_COOL and HEALTH_HOT to the health enum. Tested-by: Guru Das Srinagesh <gurus@codeaurora.org> Signed-off-by: Dan Murphy <dmurphy@ti.com> --- Documentation/ABI/testing/sysfs-class-power | 2 +- drivers/power/supply/power_supply_sysfs.c | 2 +- include/linux/power_supply.h | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power index bf3b48f022dc..9f3fd01a9373 100644 --- a/Documentation/ABI/testing/sysfs-class-power +++ b/Documentation/ABI/testing/sysfs-class-power @@ -190,7 +190,7 @@ Description: Valid values: "Unknown", "Good", "Overheat", "Dead", "Over voltage", "Unspecified failure", "Cold", "Watchdog timer expire", "Safety timer expire", - "Over current" + "Over current", "Warm", "Cool", "Hot" What: /sys/class/power_supply/<supply_name>/precharge_current Date: June 2017 diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index f37ad4eae60b..d0d549611794 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -61,7 +61,7 @@ static const char * const power_supply_charge_type_text[] = { static const char * const power_supply_health_text[] = { "Unknown", "Good", "Overheat", "Dead", "Over voltage", "Unspecified failure", "Cold", "Watchdog timer expire", - "Safety timer expire", "Over current" + "Safety timer expire", "Over current", "Warm", "Cool", "Hot" }; static const char * const power_supply_technology_text[] = { diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index dcd5a71e6c67..8670e90c1d51 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -61,6 +61,9 @@ enum { POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE, POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE, POWER_SUPPLY_HEALTH_OVERCURRENT, + POWER_SUPPLY_HEALTH_WARM, + POWER_SUPPLY_HEALTH_COOL, + POWER_SUPPLY_HEALTH_HOT, }; enum { -- 2.25.1 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v5 1/3] power_supply: Add additional health properties to the header 2020-05-01 17:51 ` [PATCH v5 1/3] power_supply: Add additional health properties to the header Dan Murphy @ 2020-05-03 0:30 ` Sebastian Reichel 0 siblings, 0 replies; 8+ messages in thread From: Sebastian Reichel @ 2020-05-03 0:30 UTC (permalink / raw) To: Dan Murphy Cc: linux-pm, linux-kernel, devicetree, Guru Das Srinagesh, Sandeep Patil [-- Attachment #1: Type: text/plain, Size: 2678 bytes --] Hi, On Fri, May 01, 2020 at 12:51:16PM -0500, Dan Murphy wrote: > Add HEALTH_WARM, HEALTH_COOL and HEALTH_HOT to the health enum. > > Tested-by: Guru Das Srinagesh <gurus@codeaurora.org> > Signed-off-by: Dan Murphy <dmurphy@ti.com> > --- What is going on? Ricardo wrote, that he added the JEITA spec reference to the commit message. I don't see it anywhere. Also Sandeep Patil asked to be Cc'd in new versions of the patchset. You do not need to list bq2515x as user, but patch will only be taken together with the driver (or any other driver using the properties in mainline kernel). -- Sebastian > Documentation/ABI/testing/sysfs-class-power | 2 +- > drivers/power/supply/power_supply_sysfs.c | 2 +- > include/linux/power_supply.h | 3 +++ > 3 files changed, 5 insertions(+), 2 deletions(-) > > diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power > index bf3b48f022dc..9f3fd01a9373 100644 > --- a/Documentation/ABI/testing/sysfs-class-power > +++ b/Documentation/ABI/testing/sysfs-class-power > @@ -190,7 +190,7 @@ Description: > Valid values: "Unknown", "Good", "Overheat", "Dead", > "Over voltage", "Unspecified failure", "Cold", > "Watchdog timer expire", "Safety timer expire", > - "Over current" > + "Over current", "Warm", "Cool", "Hot" > > What: /sys/class/power_supply/<supply_name>/precharge_current > Date: June 2017 > diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c > index f37ad4eae60b..d0d549611794 100644 > --- a/drivers/power/supply/power_supply_sysfs.c > +++ b/drivers/power/supply/power_supply_sysfs.c > @@ -61,7 +61,7 @@ static const char * const power_supply_charge_type_text[] = { > static const char * const power_supply_health_text[] = { > "Unknown", "Good", "Overheat", "Dead", "Over voltage", > "Unspecified failure", "Cold", "Watchdog timer expire", > - "Safety timer expire", "Over current" > + "Safety timer expire", "Over current", "Warm", "Cool", "Hot" > }; > > static const char * const power_supply_technology_text[] = { > diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h > index dcd5a71e6c67..8670e90c1d51 100644 > --- a/include/linux/power_supply.h > +++ b/include/linux/power_supply.h > @@ -61,6 +61,9 @@ enum { > POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE, > POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE, > POWER_SUPPLY_HEALTH_OVERCURRENT, > + POWER_SUPPLY_HEALTH_WARM, > + POWER_SUPPLY_HEALTH_COOL, > + POWER_SUPPLY_HEALTH_HOT, > }; > > enum { > -- > 2.25.1 > [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 833 bytes --] ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v5 2/3] Add the bindings for the bq25150 and bq25155 500mA charging ICs from Texas Instruments. 2020-05-01 17:51 [PATCH v5 0/3] BQ25150/155 Batter charger Dan Murphy 2020-05-01 17:51 ` [PATCH v5 1/3] power_supply: Add additional health properties to the header Dan Murphy @ 2020-05-01 17:51 ` Dan Murphy 2020-05-01 21:35 ` Rob Herring 2020-05-03 0:40 ` Sebastian Reichel 2020-05-01 17:51 ` [PATCH v5 3/3] power: supply: bq25150 introduce the bq25150 Dan Murphy 2 siblings, 2 replies; 8+ messages in thread From: Dan Murphy @ 2020-05-01 17:51 UTC (permalink / raw) To: sre; +Cc: linux-pm, linux-kernel, devicetree, Ricardo Rivera-Matos, Rob Herring From: Ricardo Rivera-Matos <r-rivera-matos@ti.com> Description: The BQ2515X family of devices are highly integrated battery management ICs that integrate the most common functions for wearbale devices namely a charger, an output voltage rail, ADC for battery and system monitoring, and a push-button controller. Datasheets: http://www.ti.com/lit/ds/symlink/bq25150.pdf http://www.ti.com/lit/ds/symlink/bq25155.pdf CC: Rob Herring <robh@kernel.org> Signed-off-by: Ricardo Rivera-Matos <r-rivera-matos@ti.com> --- .../bindings/power/supply/bq2515x.yaml | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/supply/bq2515x.yaml diff --git a/Documentation/devicetree/bindings/power/supply/bq2515x.yaml b/Documentation/devicetree/bindings/power/supply/bq2515x.yaml new file mode 100644 index 000000000000..673caf67a993 --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/bq2515x.yaml @@ -0,0 +1,99 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright (C) 2020 Texas Instruments Incorporated +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/power/supply/bq2515x.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: TI bq2515x 500-mA Linear charger family + +maintainers: + - Dan Murphy <dmurphy@ti.com> + +description: | + The BQ2515x family is a highly integrated battery charge management IC that + integrates the most common functions for wearable devices, namely a charger, + an output voltage rail, ADC for battery and system monitoring, and + push-button controller. + + Specifications about the charger can be found at: + http://www.ti.com/lit/ds/symlink/bq25150.pdf + http://www.ti.com/lit/ds/symlink/bq25155.pdf + +properties: + compatible: + enum: + - ti,bq25150 + - ti,bq25155 + + reg: + maxItems: 1 + + ac_detect-gpios: + description: | + GPIO used for connecting the bq2515x device PG (AC Detect) + pin. This pin should be used if possible as this is the + recommended way to obtain the charger's input PG state. + If this pin is not specified a software-based approach for PG + detection is used. + + reset-gpios: + description: | + GPIO used for hardware reset. + + low-power-gpios: + description: | + GPIO used for low power mode of IC. + + charge-enable-gpios: + description: | + GPIO used to turn on and off charging. + + constant-charge-current-max-microamp: + description: | + Maximum charging current in micro Amps. + minimum: 50000 + maximum: 600000 + + precharge-current-max-microamp: + description: | + Maximum precharging current in micro Amps. + minimum: 1250 + maximum: 77500 + + input-current-limit-microamp: + description: | + Maximum input current in micro Amps. + minimum: 50000 + maximum: 500000 + + constant-charge-voltage-max-microvolt: + description: | + Maximum charging voltage in micro volts. + minimum: 3600000 + maximum: 4600000 + +required: + - compatible + - reg + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + charger@6b { + compatible = "ti,bq25150"; + reg = <0x6b>; + + ac_detect-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>; + low-power-gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>; + charge-enable-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; + constant-charge-current-max-microamp = <300000>; + constant-charge-voltage-max-microvolt = <4200000>; + precharge-current-max-microamp = <2500>; + input-current-limit-microamp = <100000>; + }; + }; -- 2.25.1 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v5 2/3] Add the bindings for the bq25150 and bq25155 500mA charging ICs from Texas Instruments. 2020-05-01 17:51 ` [PATCH v5 2/3] Add the bindings for the bq25150 and bq25155 500mA charging ICs from Texas Instruments Dan Murphy @ 2020-05-01 21:35 ` Rob Herring 2020-05-03 0:40 ` Sebastian Reichel 1 sibling, 0 replies; 8+ messages in thread From: Rob Herring @ 2020-05-01 21:35 UTC (permalink / raw) To: Dan Murphy; +Cc: sre, linux-pm, linux-kernel, devicetree, Ricardo Rivera-Matos On Fri, 1 May 2020 12:51:17 -0500, Dan Murphy wrote: > From: Ricardo Rivera-Matos <r-rivera-matos@ti.com> > > Description: > The BQ2515X family of devices are highly integrated battery management > ICs that integrate the most common functions for wearbale devices > namely a charger, an output voltage rail, ADC for battery and system > monitoring, and a push-button controller. > > Datasheets: > http://www.ti.com/lit/ds/symlink/bq25150.pdf > http://www.ti.com/lit/ds/symlink/bq25155.pdf > > CC: Rob Herring <robh@kernel.org> > Signed-off-by: Ricardo Rivera-Matos <r-rivera-matos@ti.com> > --- > .../bindings/power/supply/bq2515x.yaml | 99 +++++++++++++++++++ > 1 file changed, 99 insertions(+) > create mode 100644 Documentation/devicetree/bindings/power/supply/bq2515x.yaml > My bot found errors running 'make dt_binding_check' on your patch: Documentation/devicetree/bindings/power/supply/bq2515x.yaml: while scanning a block scalar in "<unicode string>", line 81, column 5 found a tab character where an indentation space is expected in "<unicode string>", line 96, column 1 Documentation/devicetree/bindings/Makefile:11: recipe for target 'Documentation/devicetree/bindings/power/supply/bq2515x.example.dts' failed make[1]: *** [Documentation/devicetree/bindings/power/supply/bq2515x.example.dts] Error 1 make[1]: *** Waiting for unfinished jobs.... /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/power/supply/bq2515x.yaml: ignoring, error parsing file warning: no schema found in file: Documentation/devicetree/bindings/power/supply/bq2515x.yaml /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/power/supply/bq2515x.yaml: ignoring, error parsing file warning: no schema found in file: Documentation/devicetree/bindings/power/supply/bq2515x.yaml Makefile:1300: recipe for target 'dt_binding_check' failed make: *** [dt_binding_check] Error 2 See https://patchwork.ozlabs.org/patch/1281414 If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure dt-schema is up to date: pip3 install git+https://github.com/devicetree-org/dt-schema.git@master --upgrade Please check and re-submit. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v5 2/3] Add the bindings for the bq25150 and bq25155 500mA charging ICs from Texas Instruments. 2020-05-01 17:51 ` [PATCH v5 2/3] Add the bindings for the bq25150 and bq25155 500mA charging ICs from Texas Instruments Dan Murphy 2020-05-01 21:35 ` Rob Herring @ 2020-05-03 0:40 ` Sebastian Reichel 1 sibling, 0 replies; 8+ messages in thread From: Sebastian Reichel @ 2020-05-03 0:40 UTC (permalink / raw) To: Dan Murphy Cc: linux-pm, linux-kernel, devicetree, Ricardo Rivera-Matos, Rob Herring [-- Attachment #1: Type: text/plain, Size: 4807 bytes --] Hi, On Fri, May 01, 2020 at 12:51:17PM -0500, Dan Murphy wrote: > From: Ricardo Rivera-Matos <r-rivera-matos@ti.com> > > Description: > The BQ2515X family of devices are highly integrated battery management > ICs that integrate the most common functions for wearbale devices > namely a charger, an output voltage rail, ADC for battery and system > monitoring, and a push-button controller. > > Datasheets: > http://www.ti.com/lit/ds/symlink/bq25150.pdf > http://www.ti.com/lit/ds/symlink/bq25155.pdf > > CC: Rob Herring <robh@kernel.org> > Signed-off-by: Ricardo Rivera-Matos <r-rivera-matos@ti.com> > --- > .../bindings/power/supply/bq2515x.yaml | 99 +++++++++++++++++++ > 1 file changed, 99 insertions(+) > create mode 100644 Documentation/devicetree/bindings/power/supply/bq2515x.yaml > > diff --git a/Documentation/devicetree/bindings/power/supply/bq2515x.yaml b/Documentation/devicetree/bindings/power/supply/bq2515x.yaml > new file mode 100644 > index 000000000000..673caf67a993 > --- /dev/null > +++ b/Documentation/devicetree/bindings/power/supply/bq2515x.yaml > @@ -0,0 +1,99 @@ > +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) > +# Copyright (C) 2020 Texas Instruments Incorporated > +%YAML 1.2 > +--- > +$id: "http://devicetree.org/schemas/power/supply/bq2515x.yaml#" > +$schema: "http://devicetree.org/meta-schemas/core.yaml#" > + > +title: TI bq2515x 500-mA Linear charger family > + > +maintainers: > + - Dan Murphy <dmurphy@ti.com> > + > +description: | > + The BQ2515x family is a highly integrated battery charge management IC that > + integrates the most common functions for wearable devices, namely a charger, > + an output voltage rail, ADC for battery and system monitoring, and > + push-button controller. > + > + Specifications about the charger can be found at: > + http://www.ti.com/lit/ds/symlink/bq25150.pdf > + http://www.ti.com/lit/ds/symlink/bq25155.pdf > + > +properties: > + compatible: > + enum: > + - ti,bq25150 > + - ti,bq25155 > + > + reg: > + maxItems: 1 > + > + ac_detect-gpios: > + description: | > + GPIO used for connecting the bq2515x device PG (AC Detect) > + pin. This pin should be used if possible as this is the > + recommended way to obtain the charger's input PG state. > + If this pin is not specified a software-based approach for PG > + detection is used. No _ in DT properties, please use ac-detect-gpios as I wrote in PATCHv4. > + reset-gpios: > + description: | > + GPIO used for hardware reset. > + > + low-power-gpios: > + description: | > + GPIO used for low power mode of IC. > + > + charge-enable-gpios: > + description: | > + GPIO used to turn on and off charging. > + > + constant-charge-current-max-microamp: > + description: | > + Maximum charging current in micro Amps. > + minimum: 50000 > + maximum: 600000 > + > + precharge-current-max-microamp: > + description: | > + Maximum precharging current in micro Amps. > + minimum: 1250 > + maximum: 77500 > + > + input-current-limit-microamp: > + description: | > + Maximum input current in micro Amps. > + minimum: 50000 > + maximum: 500000 > + > + constant-charge-voltage-max-microvolt: > + description: | > + Maximum charging voltage in micro volts. > + minimum: 3600000 > + maximum: 4600000 The following properties describe battery properties and should go into a simple-battery node, which is referenced by the charger. Then you get the properties in the driver via power_supply_get_battery_info(): constant-charge-current-max-microamp constant-charge-voltage-max-microvolt precharge-current-max-microamp The binding for simple-battery can be found here: Documentation/devicetree/bindings/power/supply/battery.txt (still needs to be converted to YAML) > + > +required: > + - compatible > + - reg > + > +examples: > + - | > + #include <dt-bindings/gpio/gpio.h> > + i2c0 { > + #address-cells = <1>; > + #size-cells = <0>; > + charger@6b { > + compatible = "ti,bq25150"; > + reg = <0x6b>; > + > + ac_detect-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>; > + reset-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>; > + low-power-gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>; > + charge-enable-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; > + constant-charge-current-max-microamp = <300000>; > + constant-charge-voltage-max-microvolt = <4200000>; > + precharge-current-max-microamp = <2500>; > + input-current-limit-microamp = <100000>; bogus indention > + }; > + }; -- Sebastian [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 833 bytes --] ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v5 3/3] power: supply: bq25150 introduce the bq25150 2020-05-01 17:51 [PATCH v5 0/3] BQ25150/155 Batter charger Dan Murphy 2020-05-01 17:51 ` [PATCH v5 1/3] power_supply: Add additional health properties to the header Dan Murphy 2020-05-01 17:51 ` [PATCH v5 2/3] Add the bindings for the bq25150 and bq25155 500mA charging ICs from Texas Instruments Dan Murphy @ 2020-05-01 17:51 ` Dan Murphy 2020-05-03 1:16 ` Sebastian Reichel 2 siblings, 1 reply; 8+ messages in thread From: Dan Murphy @ 2020-05-01 17:51 UTC (permalink / raw) To: sre; +Cc: linux-pm, linux-kernel, devicetree, Ricardo Rivera-Matos From: Ricardo Rivera-Matos <r-rivera-matos@ti.com> Introduce the bq2515x family of chargers. Description: The BQ2515X family of devices are highly integrated battery management ICs that integrate the most common functions for wearbale devices namely a charger, an output voltage rail, ADC for battery and system monitoring, and a push-button controller. Datasheets: bq25150 - http://www.ti.com/lit/ds/symlink/bq25150.pdf bq25155 - http://www.ti.com/lit/ds/symlink/bq25155.pdf Signed-off-by: Ricardo Rivera-Matos <r-rivera-matos@ti.com> --- drivers/power/supply/Kconfig | 8 + drivers/power/supply/Makefile | 1 + drivers/power/supply/bq2515x_charger.c | 1170 ++++++++++++++++++++++++ 3 files changed, 1179 insertions(+) create mode 100644 drivers/power/supply/bq2515x_charger.c diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index f3424fdce341..7732c5586fc2 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -589,6 +589,14 @@ config CHARGER_BQ24735 help Say Y to enable support for the TI BQ24735 battery charger. +config CHARGER_BQ2515X + tristate "TI BQ2515X battery charger family" + depends on I2C + depends on GPIOLIB || COMPILE_TEST + select REGMAP_I2C + help + Say Y to enable support for the TI BQ2515X battery charger. + config CHARGER_BQ25890 tristate "TI BQ25890 battery charger driver" depends on I2C diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 6c7da920ea83..8fcc175a7e22 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o obj-$(CONFIG_CHARGER_BQ24257) += bq24257_charger.o obj-$(CONFIG_CHARGER_BQ24735) += bq24735-charger.o +obj-$(CONFIG_CHARGER_BQ2515X) += bq2515x_charger.o obj-$(CONFIG_CHARGER_BQ25890) += bq25890_charger.o obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o diff --git a/drivers/power/supply/bq2515x_charger.c b/drivers/power/supply/bq2515x_charger.c new file mode 100644 index 000000000000..c10789235982 --- /dev/null +++ b/drivers/power/supply/bq2515x_charger.c @@ -0,0 +1,1170 @@ +// SPDX-License-Identifier: GPL-2.0 +// BQ2515X Battery Charger Driver +// Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/gpio/consumer.h> +#include <linux/power_supply.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#define BQ2515X_MANUFACTURER "Texas Instruments" + +#define BQ2515X_STAT0 0x00 +#define BQ2515X_STAT1 0x01 +#define BQ2515X_STAT2 0x02 +#define BQ2515X_FLAG0 0x03 +#define BQ2515X_FLAG1 0x04 +#define BQ2515X_FLAG2 0x05 +#define BQ2515X_FLAG3 0x06 +#define BQ2515X_MASK0 0x07 +#define BQ2515X_MASK1 0x08 +#define BQ2515X_MASK2 0x09 +#define BQ2515X_MASK3 0x0a +#define BQ2515X_VBAT_CTRL 0x12 +#define BQ2515X_ICHG_CTRL 0x13 +#define BQ2515X_PCHRGCTRL 0x14 +#define BQ2515X_TERMCTRL 0x15 +#define BQ2515X_BUVLO 0x16 +#define BQ2515X_CHARGERCTRL0 0x17 +#define BQ2515X_CHARGERCTRL1 0x18 +#define BQ2515X_ILIMCTRL 0x19 +#define BQ2515X_LDOCTRL 0x1d +#define BQ2515X_MRCTRL 0x30 +#define BQ2515X_ICCTRL0 0x35 +#define BQ2515X_ICCTRL1 0x36 +#define BQ2515X_ICCTRL2 0x37 +#define BQ2515X_ADCCTRL0 0x40 +#define BQ2515X_ADCCTRL1 0x41 +#define BQ2515X_ADC_VBAT_M 0x42 +#define BQ2515X_ADC_VBAT_L 0x43 +#define BQ2515X_ADC_TS_M 0x44 +#define BQ2515X_ADC_TS_L 0x45 +#define BQ2515X_ADC_ICHG_M 0x46 +#define BQ2515X_ADC_ICHG_L 0x47 +#define BQ2515X_ADC_ADCIN_M 0x48 +#define BQ2515X_ADC_ADCIN_L 0x49 +#define BQ2515X_ADC_VIN_M 0x4a +#define BQ2515X_ADC_VIN_L 0x4b +#define BQ2515X_ADC_PMID_M 0x4c +#define BQ2515X_ADC_PMID_L 0x4d +#define BQ2515X_ADC_IIN_M 0x4e +#define BQ2515X_ADC_IIN_L 0x4f +#define BQ2515X_ADC_COMP1_M 0x52 +#define BQ2515X_ADC_COMP1_L 0X53 +#define BQ2515X_ADC_COMP2_M 0X54 +#define BQ2515X_ADC_COMP2_L 0x55 +#define BQ2515X_ADC_COMP3_M 0x56 +#define BQ2515X_ADC_COMP3_L 0x57 +#define BQ2515X_ADC_READ_EN 0x58 +#define BQ2515X_TS_FASTCHGCTRL 0x61 +#define BQ2515X_TS_COLD 0x62 +#define BQ2515X_TS_COOL 0x63 +#define BQ2515X_TS_WARM 0x64 +#define BQ2515X_TS_HOT 0x65 +#define BQ2515X_DEVICE_ID 0x6f + +#define BQ2515X_DEFAULT_ICHG_UA 10000 +#define BQ25150_DEFAULT_ILIM_UA 100000 +#define BQ25155_DEFAULT_ILIM_UA 500000 +#define BQ2515X_DEFAULT_VBAT_REG_UV 4200000 +#define BQ2515X_DEFAULT_IPRECHARGE_UA 2500 + +#define BQ2515X_DIVISOR 65536 +#define BQ2515X_VBAT_BASE_VOLT 3600000 +#define BQ2515X_VBAT_REG_MAX 4600000 +#define BQ2515X_VBAT_REG_MIN 3600000 +#define BQ2515X_VBAT_STEP_UV 10000 +#define BQ2515X_UV_FACTOR 1000000 +#define BQ2515X_ICHG_DIVISOR 52429 +#define BQ2515X_ICHG_CURR_STEP_THRESH_UA 318750 +#define BQ2515X_ICHG_MIN_UA 0 +#define BQ2515X_ICHG_MAX_UA 500000 +#define BQ2515X_ICHG_RNG_1B0_UA 1250 +#define BQ2515X_ICHG_RNG_1B1_UA 2500 +#define BQ2515X_VLOWV_SEL_1B0_UV 3000000 +#define BQ2515X_VLOWV_SEL_1B1_UV 2800000 +#define BQ2515X_PRECHRG_ICHRG_RNGE_1875_UA 18750 +#define BQ2515X_PRECHRG_ICHRG_RNGE_3750_UA 37500 +#define BQ2515X_TWAKE2_MIN_US 1700000 +#define BQ2515X_TWAKE2_MAX_US 2300000 + +#define BQ2515X_ILIM_150MA 0x2 +#define BQ2515X_ILIM_MASK 0x7 +#define BQ2515X_HEALTH_MASK 0xf +#define BQ2515X_ICHGRNG_MASK 0x80 +#define BQ2515X_STAT0_MASK 0x0f +#define BQ2515X_STAT1_MASK 0x1f +#define BQ2515X_PRECHARGE_MASK 0x1f + +#define BQ2515X_TS_HOT_STAT BIT(0) +#define BQ2515X_TS_WARM_STAT BIT(1) +#define BQ2515X_TS_COOL_STAT BIT(2) +#define BQ2515X_TS_COLD_STAT BIT(3) +#define BQ2515X_SAFETY_TIMER_EXP BIT(5) + +#define BQ2515X_VIN_GOOD BIT(0) +#define BQ2515X_CHRG_DONE BIT(5) +#define BQ2515X_CV_CHRG_MODE BIT(6) + +#define BQ2515X_VIN_OVP_FAULT_STAT BIT(7) + +#define BQ2515X_WATCHDOG_DISABLE BIT(4) + +#define BQ2515X_ICHARGE_RANGE BIT(7) + +#define BQ2515X_VLOWV_SEL BIT(5) + +#define BQ2515X_CHARGER_DISABLE BIT(0) + +#define BQ2515X_HWRESET_14S_WD BIT(1) + +static const int bq2515x_ilim_lvl_values[] = { + 50000, 100000, 150000, 200000, 300000, 400000, 500000, 600000 +}; + +/** + * struct bq2515x_init_data - + * @icgh: fast charge current + * @vreg: charge voltage + * @ilim: input current limit + * @iprecharge: precharge current + */ +struct bq2515x_init_data { + int ichg; + int vreg; + int ilim; + int iprecharge; +}; + +enum bq2515x_id { + BQ25150, + BQ25155, +}; + +/** + * struct bq2515x_device - + * @mains: mains properties + * @battery: battery properties + * @client: i2c client structure + * @regmap: register map structure + * @dev: device structure + * @lock: thread lock structure + * @id: enumeration of device ids + * + * @reset_gpio: manual reset (MR) pin + * @lp_gpio: low power mode pin + * @ac_detect_gpio: power good (PG) pin + * @ce_gpio: charge enable (CE) pin + * + * @model_name: string value describing device model + * @device_id: value of device_id + * @mains_online: boolean value indicating power supply online + * + * @bq2515x_init_data init_data: charger initialization data structure + */ +struct bq2515x_device { + struct power_supply *mains; + struct power_supply *battery; + struct i2c_client *client; + struct regmap *regmap; + struct device *dev; + struct mutex lock; + enum bq2515x_id id; + + struct gpio_desc *reset_gpio; + struct gpio_desc *lp_gpio; + struct gpio_desc *ac_detect_gpio; + struct gpio_desc *ce_gpio; + + char model_name[I2C_NAME_SIZE]; + int device_id; + bool mains_online; + + struct bq2515x_init_data init_data; +}; + +static struct reg_default bq25150_reg_defs[] = { + {BQ2515X_FLAG0, 0x0}, + {BQ2515X_FLAG1, 0x0}, + {BQ2515X_FLAG2, 0x0}, + {BQ2515X_FLAG3, 0x0}, + {BQ2515X_MASK0, 0x0}, + {BQ2515X_MASK1, 0x0}, + {BQ2515X_MASK2, 0x71}, + {BQ2515X_MASK3, 0x0}, + {BQ2515X_VBAT_CTRL, 0x3C}, + {BQ2515X_ICHG_CTRL, 0x8}, + {BQ2515X_PCHRGCTRL, 0x2}, + {BQ2515X_TERMCTRL, 0x14}, + {BQ2515X_BUVLO, 0x0}, + {BQ2515X_CHARGERCTRL0, 0x82}, + {BQ2515X_CHARGERCTRL1, 0x42}, + {BQ2515X_ILIMCTRL, 0x1}, + {BQ2515X_LDOCTRL, 0xB0}, + {BQ2515X_MRCTRL, 0x2A}, + {BQ2515X_ICCTRL0, 0x10}, + {BQ2515X_ICCTRL1, 0x0}, + {BQ2515X_ICCTRL2, 0x0}, + {BQ2515X_ADCCTRL0, 0x2}, + {BQ2515X_ADCCTRL1, 0x40}, + {BQ2515X_ADC_COMP1_M, 0x23}, + {BQ2515X_ADC_COMP1_L, 0x20}, + {BQ2515X_ADC_COMP2_M, 0x38}, + {BQ2515X_ADC_COMP2_L, 0x90}, + {BQ2515X_ADC_COMP3_M, 0x0}, + {BQ2515X_ADC_COMP3_L, 0x0}, + {BQ2515X_ADC_READ_EN, 0x0}, + {BQ2515X_TS_FASTCHGCTRL, 0x34}, + {BQ2515X_TS_COLD, 0x7C}, + {BQ2515X_TS_COOL, 0x6D}, + {BQ2515X_TS_WARM, 0x38}, + {BQ2515X_TS_HOT, 0x27}, + {BQ2515X_DEVICE_ID, 0x20}, +}; + +static struct reg_default bq25155_reg_defs[] = { + {BQ2515X_FLAG0, 0x0}, + {BQ2515X_FLAG1, 0x0}, + {BQ2515X_FLAG2, 0x0}, + {BQ2515X_FLAG3, 0x0}, + {BQ2515X_MASK0, 0x0}, + {BQ2515X_MASK1, 0x0}, + {BQ2515X_MASK2, 0x71}, + {BQ2515X_MASK3, 0x0}, + {BQ2515X_VBAT_CTRL, 0x3C}, + {BQ2515X_ICHG_CTRL, 0x8}, + {BQ2515X_PCHRGCTRL, 0x2}, + {BQ2515X_TERMCTRL, 0x14}, + {BQ2515X_BUVLO, 0x0}, + {BQ2515X_CHARGERCTRL0, 0x82}, + {BQ2515X_CHARGERCTRL1, 0xC2}, + {BQ2515X_ILIMCTRL, 0x6}, + {BQ2515X_LDOCTRL, 0xB0}, + {BQ2515X_MRCTRL, 0x2A}, + {BQ2515X_ICCTRL0, 0x10}, + {BQ2515X_ICCTRL1, 0x0}, + {BQ2515X_ICCTRL2, 0x40}, + {BQ2515X_ADCCTRL0, 0x2}, + {BQ2515X_ADCCTRL1, 0x40}, + {BQ2515X_ADC_COMP1_M, 0x23}, + {BQ2515X_ADC_COMP1_L, 0x20}, + {BQ2515X_ADC_COMP2_M, 0x38}, + {BQ2515X_ADC_COMP2_L, 0x90}, + {BQ2515X_ADC_COMP3_M, 0x0}, + {BQ2515X_ADC_COMP3_L, 0x0}, + {BQ2515X_ADC_READ_EN, 0x0}, + {BQ2515X_TS_FASTCHGCTRL, 0x34}, + {BQ2515X_TS_COLD, 0x7C}, + {BQ2515X_TS_COOL, 0x6D}, + {BQ2515X_TS_WARM, 0x38}, + {BQ2515X_TS_HOT, 0x27}, + {BQ2515X_DEVICE_ID, 0x35}, +}; + +static bool bq2515x_is_ps_online(struct bq2515x_device *bq2515x) +{ + return bq2515x->mains_online; +} + +static int bq2515x_wake_up(struct bq2515x_device *bq2515x) +{ + int ret; + int val; + + /* Read the STAT register if we can read it then the device is out + * of ship mode. If the register cannot be read then attempt to wake + * it up and enable the ADC. + */ + ret = regmap_read(bq2515x->regmap, BQ2515X_STAT0, &val); + if (!ret) + return ret; + + /* Need to toggle LP and bring device out of ship mode. The device + * will exit the ship mode when the MR pin is held low for at least + * t_WAKE2 as shown in section 8.3.7.1 of the datasheet. + */ + if (bq2515x->lp_gpio) + gpiod_set_value_cansleep(bq2515x->lp_gpio, 0); + + if (bq2515x->lp_gpio) { + gpiod_set_value_cansleep(bq2515x->reset_gpio, 0); + usleep_range(BQ2515X_TWAKE2_MIN_US, BQ2515X_TWAKE2_MAX_US); + gpiod_set_value_cansleep(bq2515x->reset_gpio, 1); + } + + return regmap_write(bq2515x->regmap, BQ2515X_ADC_READ_EN, BIT(3)); +} + +static int bq2515x_update_ps_status(struct bq2515x_device *bq2515x) +{ + bool dc = false; + unsigned int val; + int ret; + + if (bq2515x->ac_detect_gpio) + val = gpiod_get_value_cansleep(bq2515x->ac_detect_gpio); + + else { + ret = regmap_read(bq2515x->regmap, BQ2515X_STAT0, &val); + if (ret < 0) + return ret; + } + + dc = val & BQ2515X_VIN_GOOD; + + ret = bq2515x->mains_online != dc; + + bq2515x->mains_online = dc; + + return ret; +} + +static int bq2515x_disable_watchdog_timers(struct bq2515x_device *bq2515x) +{ + int ret; + + ret = regmap_update_bits(bq2515x->regmap, BQ2515X_CHARGERCTRL0, + BQ2515X_WATCHDOG_DISABLE, BQ2515X_WATCHDOG_DISABLE); + if (ret < 0) + return ret; + + return regmap_update_bits(bq2515x->regmap, BQ2515X_ICCTRL2, + BQ2515X_HWRESET_14S_WD, 0); +} + +static int bq2515x_get_battery_voltage_now(struct bq2515x_device *bq2515x) +{ + int ret; + int vbat_msb; + int vbat_lsb; + uint32_t vbat_measurement; + + if (!bq2515x_is_ps_online(bq2515x)) + bq2515x_wake_up(bq2515x); + + ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_VBAT_M, &vbat_msb); + if (ret) + return ret; + + ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_VBAT_L, &vbat_lsb); + if (ret) + return ret; + + vbat_measurement = (vbat_msb << 8) | vbat_lsb; + + return vbat_measurement * (BQ2515X_UV_FACTOR / BQ2515X_DIVISOR) * 6; +} + +static int bq2515x_get_battery_current_now(struct bq2515x_device *bq2515x) +{ + int ret; + int ichg_msb; + int ichg_lsb; + uint32_t ichg_measurement; + u16 ichg_multiplier = BQ2515X_ICHG_RNG_1B0_UA; + unsigned int ichg_reg_code, reg_code; + unsigned int icharge_range = 0, pchrgctrl; + unsigned int buvlo, vlowv_sel, vlowv = BQ2515X_VLOWV_SEL_1B0_UV; + + if (!bq2515x_is_ps_online(bq2515x)) + return -ENODATA; + + ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_ICHG_M, &ichg_msb); + if (ret < 0) + return ret; + + ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_ICHG_L, &ichg_lsb); + if (ret < 0) + return ret; + + ichg_measurement = (ichg_msb << 8) | ichg_lsb; + + ret = regmap_read(bq2515x->regmap, BQ2515X_BUVLO, &buvlo); + if (ret < 0) + return ret; + + vlowv_sel = buvlo & BQ2515X_VLOWV_SEL; + + if (vlowv_sel) + vlowv = BQ2515X_VLOWV_SEL_1B1_UV; + + if (bq2515x_get_battery_voltage_now(bq2515x) < vlowv) { + ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, + &pchrgctrl); + if (ret < 0) + return ret; + reg_code = pchrgctrl & BQ2515X_PRECHARGE_MASK; + } else { + ret = regmap_read(bq2515x->regmap, BQ2515X_ICHG_CTRL, + &ichg_reg_code); + if (ret < 0) + return ret; + reg_code = ichg_reg_code; + } + + ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl); + if (ret < 0) + return ret; + + icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE; + + if (icharge_range) + ichg_multiplier = BQ2515X_ICHG_RNG_1B1_UA; + + return reg_code * (ichg_multiplier * ichg_measurement + / BQ2515X_ICHG_DIVISOR); +} + +static bool bq2515x_get_charge_disable(struct bq2515x_device *bq2515x) +{ + int ret; + int ce_pin = 0; + int icctrl2; + int charger_disable; + + if (bq2515x->ce_gpio) + ce_pin = gpiod_get_value_cansleep(bq2515x->ce_gpio); + + ret = regmap_read(bq2515x->regmap, BQ2515X_ICCTRL2, &icctrl2); + if (ret < 0) + return ret; + + charger_disable = icctrl2 & BQ2515X_CHARGER_DISABLE; + + if (charger_disable || ce_pin) + return true; + + return false; +} + +static int bq2515x_set_charge_disable(struct bq2515x_device *bq2515x, int val) +{ + if (bq2515x->ce_gpio) + gpiod_set_value_cansleep(bq2515x->ce_gpio, val); + + return regmap_update_bits(bq2515x->regmap, BQ2515X_ICCTRL2, + BQ2515X_CHARGER_DISABLE, val); +} + +static int bq2515x_get_const_charge_current(struct bq2515x_device *bq2515x) +{ + int ret; + u16 ichg_multiplier = BQ2515X_ICHG_RNG_1B0_UA; + unsigned int ichg_reg_code; + unsigned int pchrgctrl; + unsigned int icharge_range; + + ret = regmap_read(bq2515x->regmap, BQ2515X_ICHG_CTRL, &ichg_reg_code); + if (ret < 0) + return ret; + + ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl); + if (ret < 0) + return ret; + + icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE; + + if (icharge_range) + ichg_multiplier = BQ2515X_ICHG_RNG_1B1_UA; + + return ichg_reg_code * ichg_multiplier; +} + +static int bq2515x_set_const_charge_current(struct bq2515x_device *bq2515x, + int val) +{ + int ret; + unsigned int ichg_reg_code; + u16 ichg_multiplier = BQ2515X_ICHG_RNG_1B0_UA; + unsigned int icharge_range = 0; + + if (val > BQ2515X_ICHG_MAX_UA || val < BQ2515X_ICHG_MIN_UA) + return -EINVAL; + + if (val > BQ2515X_ICHG_CURR_STEP_THRESH_UA) { + ichg_multiplier = BQ2515X_ICHG_RNG_1B1_UA; + icharge_range = BQ2515X_ICHARGE_RANGE; + } + + bq2515x_set_charge_disable(bq2515x, 1); + + ret = regmap_update_bits(bq2515x->regmap, BQ2515X_PCHRGCTRL, + BQ2515X_ICHARGE_RANGE, icharge_range); + if (ret < 0) + return ret; + + ichg_reg_code = val / ichg_multiplier; + + ret = regmap_write(bq2515x->regmap, BQ2515X_ICHG_CTRL, ichg_reg_code); + if (ret < 0) + return ret; + + return bq2515x_set_charge_disable(bq2515x, 0); +} + +static int bq2515x_get_precharge_current(struct bq2515x_device *bq2515x) +{ + int ret; + unsigned int pchrgctrl; + unsigned int icharge_range; + u16 precharge_multiplier = BQ2515X_ICHG_RNG_1B0_UA; + unsigned int precharge_reg_code; + + ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl); + if (ret < 0) + return ret; + + icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE; + + if (icharge_range) + precharge_multiplier = BQ2515X_ICHG_RNG_1B1_UA; + + precharge_reg_code = pchrgctrl & BQ2515X_PRECHARGE_MASK; + + return precharge_reg_code * precharge_multiplier; +} + +static int bq2515x_set_precharge_current(struct bq2515x_device *bq2515x, + int val) +{ + int ret; + unsigned int pchrgctrl; + unsigned int icharge_range; + unsigned int precharge_reg_code; + u16 precharge_multiplier = BQ2515X_ICHG_RNG_1B0_UA; + u16 precharge_max_ua = BQ2515X_PRECHRG_ICHRG_RNGE_1875_UA; + + ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl); + if (ret < 0) + return ret; + + icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE; + + if (icharge_range) { + precharge_max_ua = BQ2515X_PRECHRG_ICHRG_RNGE_3750_UA; + precharge_multiplier = BQ2515X_ICHG_RNG_1B1_UA; + } + if (val > precharge_max_ua || val < BQ2515X_ICHG_MIN_UA) + return -EINVAL; + + precharge_reg_code = val / precharge_multiplier; + + ret = bq2515x_set_charge_disable(bq2515x, 1); + if (ret < 0) + return ret; + + ret = regmap_update_bits(bq2515x->regmap, BQ2515X_PCHRGCTRL, + BQ2515X_PRECHARGE_MASK, precharge_reg_code); + if (ret < 0) + return ret; + + return bq2515x_set_charge_disable(bq2515x, 0); +} + +static int bq2515x_charging_status(struct bq2515x_device *bq2515x, + union power_supply_propval *val) +{ + bool status0_no_fault; + bool status1_no_fault; + bool ce_status; + bool charge_done; + unsigned int status; + int ret; + + if (!bq2515x_is_ps_online(bq2515x)) { + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + return 0; + } + + ret = regmap_read(bq2515x->regmap, BQ2515X_STAT0, &status); + if (ret) + return ret; + + /** + * The code block below is used to determine if any faults from the + * STAT0 register are disbaling charging or if the charge has completed + * according to the CHARGE_DONE_STAT bit. + */ + if (((status & BQ2515X_STAT0_MASK) == true) & + ((status & BQ2515X_CHRG_DONE) == false)) { + status0_no_fault = true; + charge_done = false; + } else if (status & BQ2515X_CHRG_DONE) { + charge_done = true; + status0_no_fault = false; + } else { + status0_no_fault = false; + charge_done = false; + } + + ret = regmap_read(bq2515x->regmap, BQ2515X_STAT1, &status); + if (ret) + return ret; + /** + *The code block below is used to determine if any faults from the + * STAT1 register are disbaling charging + */ + if ((status & BQ2515X_STAT1_MASK) == false) + status1_no_fault = true; + else + status1_no_fault = false; + + ce_status = (!bq2515x_get_charge_disable(bq2515x)); + + /** + *If there are no faults and charging is enabled, then status is + *charging. Otherwise, if charging is complete, then status is full. + *Otherwise, if a fault exists or charging is disbaled, then status is + *not charging + */ + if (status0_no_fault & status1_no_fault & ce_status) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (charge_done) + val->intval = POWER_SUPPLY_STATUS_FULL; + else if (!(status0_no_fault & status1_no_fault & ce_status)) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + + return ret; +} + +static int bq2515x_get_batt_reg(struct bq2515x_device *bq2515x) +{ + int vbat_reg_code; + int ret; + + ret = regmap_read(bq2515x->regmap, BQ2515X_VBAT_CTRL, &vbat_reg_code); + if (ret) + return ret; + + return BQ2515X_VBAT_BASE_VOLT + vbat_reg_code * BQ2515X_VBAT_STEP_UV; +} + +static int bq2515x_set_batt_reg(struct bq2515x_device *bq2515x, int val) +{ + int vbat_reg_code; + + if (val > BQ2515X_VBAT_REG_MAX || val < BQ2515X_VBAT_REG_MIN) + return -EINVAL; + + vbat_reg_code = (val - BQ2515X_VBAT_BASE_VOLT) / BQ2515X_VBAT_STEP_UV; + + return regmap_write(bq2515x->regmap, BQ2515X_VBAT_CTRL, vbat_reg_code); +} + +static int bq2515x_get_ilim_lvl(struct bq2515x_device *bq2515x) +{ + int ret; + int ilimctrl; + + ret = regmap_read(bq2515x->regmap, BQ2515X_ILIMCTRL, &ilimctrl); + if (ret) + return ret; + + return bq2515x_ilim_lvl_values[ilimctrl & BQ2515X_ILIM_MASK]; +} + +static int bq2515x_set_ilim_lvl(struct bq2515x_device *bq2515x, int val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bq2515x_ilim_lvl_values); i++) { + if (val == bq2515x_ilim_lvl_values[i]) + break; + + if (val > bq2515x_ilim_lvl_values[i - 1] && + val < bq2515x_ilim_lvl_values[i]) { + if (val - bq2515x_ilim_lvl_values[i - 1] < + bq2515x_ilim_lvl_values[i] - val) { + i = i - 1; + break; + } + } + } + + return regmap_write(bq2515x->regmap, BQ2515X_ILIMCTRL, i); +} + +static int bq2515x_power_supply_property_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + switch (prop) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + return true; + default: + return false; + } +} + +static int bq2515x_charger_get_health(struct bq2515x_device *bq2515x, + union power_supply_propval *val) +{ + int health = POWER_SUPPLY_HEALTH_GOOD; + int ret; + unsigned int stat1; + unsigned int flag3; + + if (!bq2515x_is_ps_online(bq2515x)) + bq2515x_wake_up(bq2515x); + + ret = regmap_read(bq2515x->regmap, BQ2515X_FLAG3, &flag3); + if (ret) + return ret; + + if (flag3 & BQ2515X_SAFETY_TIMER_EXP) { + health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; + goto ret_health; + } + + if (stat1 & BQ2515X_VIN_OVP_FAULT_STAT) { + health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + goto ret_health; + } + + ret = regmap_read(bq2515x->regmap, BQ2515X_STAT1, &stat1); + if (ret) + return ret; + + if (stat1 & BQ2515X_HEALTH_MASK) { + switch (stat1 & BQ2515X_HEALTH_MASK) { + case BQ2515X_TS_HOT_STAT: + health = POWER_SUPPLY_HEALTH_HOT; + break; + case BQ2515X_TS_WARM_STAT: + health = POWER_SUPPLY_HEALTH_WARM; + break; + case BQ2515X_TS_COOL_STAT: + health = POWER_SUPPLY_HEALTH_COOL; + break; + case BQ2515X_TS_COLD_STAT: + health = POWER_SUPPLY_HEALTH_COLD; + break; + default: + health = POWER_SUPPLY_HEALTH_UNKNOWN; + break; + } + } + + goto ret_health; + +ret_health: + val->intval = health; + return 0; +} + +static int bq2515x_mains_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy); + int ret; + + switch (prop) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = bq2515x_set_ilim_lvl(bq2515x, val->intval); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = bq2515x_set_batt_reg(bq2515x, val->intval); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = bq2515x_set_const_charge_current(bq2515x, val->intval); + break; + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + ret = bq2515x_set_precharge_current(bq2515x, val->intval); + break; + default: + return -EINVAL; + } + + return ret; +} + +static int bq2515x_mains_get_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy); + int ret = 0; + + switch (prop) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = bq2515x->mains_online; + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = bq2515x_get_const_charge_current(bq2515x); + if (ret < 0) + return ret; + + val->intval = ret; + break; + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + ret = bq2515x_get_precharge_current(bq2515x); + if (ret < 0) + return ret; + + val->intval = ret; + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = bq2515x_get_ilim_lvl(bq2515x); + if (ret < 0) + return ret; + + val->intval = ret; + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = bq2515x_get_batt_reg(bq2515x); + if (ret < 0) + return ret; + + val->intval = ret; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = bq2515x->model_name; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = BQ2515X_MANUFACTURER; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + val->intval = BQ2515X_VBAT_REG_MAX; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN: + val->intval = BQ2515X_VBAT_REG_MIN; + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + val->intval = BQ2515X_ICHG_MAX_UA; + break; + default: + return -EINVAL; + } + + return ret; +} + +static int bq2515x_battery_get_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy); + int ret; + + ret = bq2515x_update_ps_status(bq2515x); + if (ret < 0) + return ret; + + switch (prop) { + + case POWER_SUPPLY_PROP_STATUS: + if (!bq2515x_is_ps_online(bq2515x)) { + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + } + + ret = bq2515x_charging_status(bq2515x, val); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = bq2515x_get_battery_voltage_now(bq2515x); + if (ret < 0) + return ret; + + val->intval = ret; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = bq2515x_get_battery_current_now(bq2515x); + if (ret < 0) + return ret; + + val->intval = ret; + break; + case POWER_SUPPLY_PROP_HEALTH: + ret = bq2515x_charger_get_health(bq2515x, val); + if (ret) + val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; + break; + default: + return -EINVAL; + } + return 0; +} + +static enum power_supply_property bq2515x_battery_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, +}; + +static enum power_supply_property bq2515x_charger_properties[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_PRECHARGE_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_MIN, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static struct power_supply_desc bq2515x_mains_desc = { + .name = "bq2515x-mains", + .type = POWER_SUPPLY_TYPE_MAINS, + .get_property = bq2515x_mains_get_property, + .set_property = bq2515x_mains_set_property, + .properties = bq2515x_charger_properties, + .num_properties = ARRAY_SIZE(bq2515x_charger_properties), + .property_is_writeable = bq2515x_power_supply_property_is_writeable, + +}; + +static struct power_supply_desc bq2515x_battery_desc = { + .name = "bq2515x-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = bq2515x_battery_get_property, + .properties = bq2515x_battery_properties, + .num_properties = ARRAY_SIZE(bq2515x_battery_properties), +}; + +static int bq2515x_power_supply_register(struct bq2515x_device *bq2515x) +{ + struct power_supply_config psy_cfg = { .drv_data = bq2515x, }; + + bq2515x->mains = devm_power_supply_register(bq2515x->dev, + &bq2515x_mains_desc, + &psy_cfg); + if (IS_ERR(bq2515x->mains)) + return -EINVAL; + + bq2515x->battery = devm_power_supply_register(bq2515x->dev, + &bq2515x_battery_desc, + &psy_cfg); + if (IS_ERR(bq2515x->battery)) + return -EINVAL; + + return 0; +} + +static int bq2515x_hw_init(struct bq2515x_device *bq2515x) +{ + int ret = 0; + + if (bq2515x->init_data.ichg) + ret = bq2515x_set_const_charge_current(bq2515x, + bq2515x->init_data.ichg); + + if (ret) + goto err_out; + + if (bq2515x->init_data.ilim) + ret = bq2515x_set_ilim_lvl(bq2515x, bq2515x->init_data.ilim); + + if (ret) + goto err_out; + + if (bq2515x->init_data.vreg) + ret = bq2515x_set_batt_reg(bq2515x, bq2515x->init_data.vreg); + + if (ret) + goto err_out; + + ret = bq2515x_disable_watchdog_timers(bq2515x); + + if (ret) + goto err_out; + +err_out: + return ret; +} + +static int bq2515x_read_properties(struct bq2515x_device *bq2515x) +{ + int ret; + + ret = device_property_read_u32(bq2515x->dev, + "constant-charge-current-max-microamp", + &bq2515x->init_data.ichg); + if (ret) + bq2515x->init_data.ichg = BQ2515X_DEFAULT_ICHG_UA; + + ret = device_property_read_u32(bq2515x->dev, + "precharge-current-max-microamp", + &bq2515x->init_data.iprecharge); + if (ret) + bq2515x->init_data.iprecharge = BQ2515X_DEFAULT_IPRECHARGE_UA; + + ret = device_property_read_u32(bq2515x->dev, + "input-current-limit-microamp", + &bq2515x->init_data.ilim); + if (ret) + switch (bq2515x->device_id) { + case BQ25150: + bq2515x->init_data.ilim = BQ25150_DEFAULT_ILIM_UA; + break; + case BQ25155: + bq2515x->init_data.ilim = BQ25155_DEFAULT_ILIM_UA; + break; + } + + ret = device_property_read_u32(bq2515x->dev, + "constant-charge-voltage-max-microvolt", + &bq2515x->init_data.vreg); + if (ret) + bq2515x->init_data.vreg = BQ2515X_DEFAULT_VBAT_REG_UV; + + bq2515x->ac_detect_gpio = devm_gpiod_get_optional(bq2515x->dev, + "ac_detect", GPIOD_IN); + if (IS_ERR(bq2515x->ac_detect_gpio)) + dev_info(bq2515x->dev, "ac_detect GPIO not defined"); + + bq2515x->reset_gpio = devm_gpiod_get_optional(bq2515x->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(bq2515x->reset_gpio)) { + if (PTR_ERR(bq2515x->reset_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; + } + + bq2515x->lp_gpio = devm_gpiod_get_optional(bq2515x->dev, "low-power", + GPIOD_OUT_LOW); + if (IS_ERR(bq2515x->lp_gpio)) { + if (PTR_ERR(bq2515x->lp_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; + } + + bq2515x->ce_gpio = devm_gpiod_get_optional(bq2515x->dev, + "charge-enable", + GPIOD_OUT_LOW); + if (IS_ERR(bq2515x->ce_gpio)) { + if (PTR_ERR(bq2515x->ce_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; + } + + return 0; +} + +static bool bq2515x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BQ2515X_STAT0 ... BQ2515X_FLAG3: + case BQ2515X_ADC_VBAT_M ... BQ2515X_ADC_IIN_L: + return true; + default: + return false; + } +} + +static const struct regmap_config bq25150_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BQ2515X_DEVICE_ID, + .reg_defaults = bq25150_reg_defs, + .num_reg_defaults = ARRAY_SIZE(bq25150_reg_defs), + .cache_type = REGCACHE_RBTREE, + .volatile_reg = bq2515x_volatile_register, +}; + +static const struct regmap_config bq25155_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BQ2515X_DEVICE_ID, + .reg_defaults = bq25155_reg_defs, + .num_reg_defaults = ARRAY_SIZE(bq25155_reg_defs), + .cache_type = REGCACHE_RBTREE, + .volatile_reg = bq2515x_volatile_register, +}; + +static int bq2515x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct bq2515x_device *bq; + int ret; + + bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL); + if (!bq) + return -ENOMEM; + + bq->client = client; + bq->dev = dev; + + mutex_init(&bq->lock); + + strncpy(bq->model_name, id->name, I2C_NAME_SIZE); + + bq->device_id = (enum bq2515x_id)of_device_get_match_data(&client->dev); + + switch (bq->device_id) { + case BQ25150: + bq->regmap = devm_regmap_init_i2c(client, +&bq25150_regmap_config); + break; + case BQ25155: + bq->regmap = devm_regmap_init_i2c(client, +&bq25155_regmap_config); + break; + } + + if (IS_ERR(bq->regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(bq->regmap); + } + + i2c_set_clientdata(client, bq); + + ret = bq2515x_read_properties(bq); + if (ret) { + dev_err(dev, "Failed to read device tree properties %d\n", + ret); + goto err_out; + } + + ret = bq2515x_hw_init(bq); + if (ret) { + dev_err(dev, "Cannot initialize the chip.\n"); + goto err_out; + } + + return bq2515x_power_supply_register(bq); + +err_out: + mutex_destroy(&bq->lock); + return ret; +} + +static const struct i2c_device_id bq2515x_i2c_ids[] = { + { "bq25150", BQ25150, }, + { "bq25155", BQ25155, }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, bq2515x_i2c_ids); + +static const struct of_device_id bq2515x_of_match[] = { + { .compatible = "ti,bq25150", }, + { .compatible = "ti,bq25155", }, + { }, +}; +MODULE_DEVICE_TABLE(of, bq2515x_of_match); + +static struct i2c_driver bq2515x_driver = { + .driver = { + .name = "bq2515x-charger", + .of_match_table = bq2515x_of_match, + }, + .probe = bq2515x_probe, + .id_table = bq2515x_i2c_ids, +}; +module_i2c_driver(bq2515x_driver); + +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); +MODULE_AUTHOR("Ricardo Rivera-Matos <r-rivera-matos@ti.com>"); +MODULE_DESCRIPTION("BQ2515X charger driver"); +MODULE_LICENSE("GPL v2"); -- 2.25.1 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v5 3/3] power: supply: bq25150 introduce the bq25150 2020-05-01 17:51 ` [PATCH v5 3/3] power: supply: bq25150 introduce the bq25150 Dan Murphy @ 2020-05-03 1:16 ` Sebastian Reichel 0 siblings, 0 replies; 8+ messages in thread From: Sebastian Reichel @ 2020-05-03 1:16 UTC (permalink / raw) To: Dan Murphy; +Cc: linux-pm, linux-kernel, devicetree, Ricardo Rivera-Matos [-- Attachment #1: Type: text/plain, Size: 37640 bytes --] Hi, On Fri, May 01, 2020 at 12:51:18PM -0500, Dan Murphy wrote: > From: Ricardo Rivera-Matos <r-rivera-matos@ti.com> > > Introduce the bq2515x family of chargers. > > Description: > The BQ2515X family of devices are highly integrated battery management > ICs that integrate the most common functions for wearbale devices > namely a charger, an output voltage rail, ADC for battery and system > monitoring, and a push-button controller. > > Datasheets: > bq25150 - http://www.ti.com/lit/ds/symlink/bq25150.pdf > bq25155 - http://www.ti.com/lit/ds/symlink/bq25155.pdf > > Signed-off-by: Ricardo Rivera-Matos <r-rivera-matos@ti.com> > --- > drivers/power/supply/Kconfig | 8 + > drivers/power/supply/Makefile | 1 + > drivers/power/supply/bq2515x_charger.c | 1170 ++++++++++++++++++++++++ > 3 files changed, 1179 insertions(+) > create mode 100644 drivers/power/supply/bq2515x_charger.c > > diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig > index f3424fdce341..7732c5586fc2 100644 > --- a/drivers/power/supply/Kconfig > +++ b/drivers/power/supply/Kconfig > @@ -589,6 +589,14 @@ config CHARGER_BQ24735 > help > Say Y to enable support for the TI BQ24735 battery charger. > > +config CHARGER_BQ2515X > + tristate "TI BQ2515X battery charger family" > + depends on I2C > + depends on GPIOLIB || COMPILE_TEST > + select REGMAP_I2C > + help > + Say Y to enable support for the TI BQ2515X battery charger. > + > config CHARGER_BQ25890 > tristate "TI BQ25890 battery charger driver" > depends on I2C > diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile > index 6c7da920ea83..8fcc175a7e22 100644 > --- a/drivers/power/supply/Makefile > +++ b/drivers/power/supply/Makefile > @@ -80,6 +80,7 @@ obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o > obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o > obj-$(CONFIG_CHARGER_BQ24257) += bq24257_charger.o > obj-$(CONFIG_CHARGER_BQ24735) += bq24735-charger.o > +obj-$(CONFIG_CHARGER_BQ2515X) += bq2515x_charger.o > obj-$(CONFIG_CHARGER_BQ25890) += bq25890_charger.o > obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o > obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o > diff --git a/drivers/power/supply/bq2515x_charger.c b/drivers/power/supply/bq2515x_charger.c > new file mode 100644 > index 000000000000..c10789235982 > --- /dev/null > +++ b/drivers/power/supply/bq2515x_charger.c > @@ -0,0 +1,1170 @@ > +// SPDX-License-Identifier: GPL-2.0 > +// BQ2515X Battery Charger Driver > +// Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ > + > +#include <linux/err.h> > +#include <linux/i2c.h> > +#include <linux/init.h> > +#include <linux/interrupt.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/of_device.h> > +#include <linux/gpio/consumer.h> > +#include <linux/power_supply.h> > +#include <linux/regmap.h> > +#include <linux/types.h> > + > +#define BQ2515X_MANUFACTURER "Texas Instruments" > + > +#define BQ2515X_STAT0 0x00 > +#define BQ2515X_STAT1 0x01 > +#define BQ2515X_STAT2 0x02 > +#define BQ2515X_FLAG0 0x03 > +#define BQ2515X_FLAG1 0x04 > +#define BQ2515X_FLAG2 0x05 > +#define BQ2515X_FLAG3 0x06 > +#define BQ2515X_MASK0 0x07 > +#define BQ2515X_MASK1 0x08 > +#define BQ2515X_MASK2 0x09 > +#define BQ2515X_MASK3 0x0a > +#define BQ2515X_VBAT_CTRL 0x12 > +#define BQ2515X_ICHG_CTRL 0x13 > +#define BQ2515X_PCHRGCTRL 0x14 > +#define BQ2515X_TERMCTRL 0x15 > +#define BQ2515X_BUVLO 0x16 > +#define BQ2515X_CHARGERCTRL0 0x17 > +#define BQ2515X_CHARGERCTRL1 0x18 > +#define BQ2515X_ILIMCTRL 0x19 > +#define BQ2515X_LDOCTRL 0x1d > +#define BQ2515X_MRCTRL 0x30 > +#define BQ2515X_ICCTRL0 0x35 > +#define BQ2515X_ICCTRL1 0x36 > +#define BQ2515X_ICCTRL2 0x37 > +#define BQ2515X_ADCCTRL0 0x40 > +#define BQ2515X_ADCCTRL1 0x41 > +#define BQ2515X_ADC_VBAT_M 0x42 > +#define BQ2515X_ADC_VBAT_L 0x43 > +#define BQ2515X_ADC_TS_M 0x44 > +#define BQ2515X_ADC_TS_L 0x45 > +#define BQ2515X_ADC_ICHG_M 0x46 > +#define BQ2515X_ADC_ICHG_L 0x47 > +#define BQ2515X_ADC_ADCIN_M 0x48 > +#define BQ2515X_ADC_ADCIN_L 0x49 > +#define BQ2515X_ADC_VIN_M 0x4a > +#define BQ2515X_ADC_VIN_L 0x4b > +#define BQ2515X_ADC_PMID_M 0x4c > +#define BQ2515X_ADC_PMID_L 0x4d > +#define BQ2515X_ADC_IIN_M 0x4e > +#define BQ2515X_ADC_IIN_L 0x4f > +#define BQ2515X_ADC_COMP1_M 0x52 > +#define BQ2515X_ADC_COMP1_L 0X53 > +#define BQ2515X_ADC_COMP2_M 0X54 > +#define BQ2515X_ADC_COMP2_L 0x55 > +#define BQ2515X_ADC_COMP3_M 0x56 > +#define BQ2515X_ADC_COMP3_L 0x57 > +#define BQ2515X_ADC_READ_EN 0x58 > +#define BQ2515X_TS_FASTCHGCTRL 0x61 > +#define BQ2515X_TS_COLD 0x62 > +#define BQ2515X_TS_COOL 0x63 > +#define BQ2515X_TS_WARM 0x64 > +#define BQ2515X_TS_HOT 0x65 > +#define BQ2515X_DEVICE_ID 0x6f > + > +#define BQ2515X_DEFAULT_ICHG_UA 10000 > +#define BQ25150_DEFAULT_ILIM_UA 100000 > +#define BQ25155_DEFAULT_ILIM_UA 500000 > +#define BQ2515X_DEFAULT_VBAT_REG_UV 4200000 > +#define BQ2515X_DEFAULT_IPRECHARGE_UA 2500 > + > +#define BQ2515X_DIVISOR 65536 > +#define BQ2515X_VBAT_BASE_VOLT 3600000 > +#define BQ2515X_VBAT_REG_MAX 4600000 > +#define BQ2515X_VBAT_REG_MIN 3600000 > +#define BQ2515X_VBAT_STEP_UV 10000 > +#define BQ2515X_UV_FACTOR 1000000 > +#define BQ2515X_ICHG_DIVISOR 52429 > +#define BQ2515X_ICHG_CURR_STEP_THRESH_UA 318750 > +#define BQ2515X_ICHG_MIN_UA 0 > +#define BQ2515X_ICHG_MAX_UA 500000 > +#define BQ2515X_ICHG_RNG_1B0_UA 1250 > +#define BQ2515X_ICHG_RNG_1B1_UA 2500 > +#define BQ2515X_VLOWV_SEL_1B0_UV 3000000 > +#define BQ2515X_VLOWV_SEL_1B1_UV 2800000 > +#define BQ2515X_PRECHRG_ICHRG_RNGE_1875_UA 18750 > +#define BQ2515X_PRECHRG_ICHRG_RNGE_3750_UA 37500 > +#define BQ2515X_TWAKE2_MIN_US 1700000 > +#define BQ2515X_TWAKE2_MAX_US 2300000 > + > +#define BQ2515X_ILIM_150MA 0x2 > +#define BQ2515X_ILIM_MASK 0x7 > +#define BQ2515X_HEALTH_MASK 0xf > +#define BQ2515X_ICHGRNG_MASK 0x80 > +#define BQ2515X_STAT0_MASK 0x0f > +#define BQ2515X_STAT1_MASK 0x1f > +#define BQ2515X_PRECHARGE_MASK 0x1f > + > +#define BQ2515X_TS_HOT_STAT BIT(0) > +#define BQ2515X_TS_WARM_STAT BIT(1) > +#define BQ2515X_TS_COOL_STAT BIT(2) > +#define BQ2515X_TS_COLD_STAT BIT(3) > +#define BQ2515X_SAFETY_TIMER_EXP BIT(5) > + > +#define BQ2515X_VIN_GOOD BIT(0) > +#define BQ2515X_CHRG_DONE BIT(5) > +#define BQ2515X_CV_CHRG_MODE BIT(6) > + > +#define BQ2515X_VIN_OVP_FAULT_STAT BIT(7) > + > +#define BQ2515X_WATCHDOG_DISABLE BIT(4) > + > +#define BQ2515X_ICHARGE_RANGE BIT(7) > + > +#define BQ2515X_VLOWV_SEL BIT(5) > + > +#define BQ2515X_CHARGER_DISABLE BIT(0) > + > +#define BQ2515X_HWRESET_14S_WD BIT(1) > + > +static const int bq2515x_ilim_lvl_values[] = { > + 50000, 100000, 150000, 200000, 300000, 400000, 500000, 600000 > +}; > + > +/** > + * struct bq2515x_init_data - > + * @icgh: fast charge current > + * @vreg: charge voltage > + * @ilim: input current limit > + * @iprecharge: precharge current > + */ > +struct bq2515x_init_data { > + int ichg; > + int vreg; > + int ilim; > + int iprecharge; > +}; > + > +enum bq2515x_id { > + BQ25150, > + BQ25155, > +}; > + > +/** > + * struct bq2515x_device - > + * @mains: mains properties > + * @battery: battery properties > + * @client: i2c client structure > + * @regmap: register map structure > + * @dev: device structure > + * @lock: thread lock structure lock is unused and leaks (i.e. it is initialized, but not free'd in all error cases, also not free'd in device removal). Just remove it. > + * @id: enumeration of device ids > + * > + * @reset_gpio: manual reset (MR) pin > + * @lp_gpio: low power mode pin > + * @ac_detect_gpio: power good (PG) pin > + * @ce_gpio: charge enable (CE) pin > + * > + * @model_name: string value describing device model > + * @device_id: value of device_id > + * @mains_online: boolean value indicating power supply online > + * > + * @bq2515x_init_data init_data: charger initialization data structure > + */ > +struct bq2515x_device { > + struct power_supply *mains; > + struct power_supply *battery; > + struct i2c_client *client; > + struct regmap *regmap; > + struct device *dev; > + struct mutex lock; > + enum bq2515x_id id; > + > + struct gpio_desc *reset_gpio; > + struct gpio_desc *lp_gpio; > + struct gpio_desc *ac_detect_gpio; > + struct gpio_desc *ce_gpio; > + > + char model_name[I2C_NAME_SIZE]; > + int device_id; > + bool mains_online; > + > + struct bq2515x_init_data init_data; > +}; > + > +static struct reg_default bq25150_reg_defs[] = { > + {BQ2515X_FLAG0, 0x0}, > + {BQ2515X_FLAG1, 0x0}, > + {BQ2515X_FLAG2, 0x0}, > + {BQ2515X_FLAG3, 0x0}, > + {BQ2515X_MASK0, 0x0}, > + {BQ2515X_MASK1, 0x0}, > + {BQ2515X_MASK2, 0x71}, > + {BQ2515X_MASK3, 0x0}, > + {BQ2515X_VBAT_CTRL, 0x3C}, > + {BQ2515X_ICHG_CTRL, 0x8}, > + {BQ2515X_PCHRGCTRL, 0x2}, > + {BQ2515X_TERMCTRL, 0x14}, > + {BQ2515X_BUVLO, 0x0}, > + {BQ2515X_CHARGERCTRL0, 0x82}, > + {BQ2515X_CHARGERCTRL1, 0x42}, > + {BQ2515X_ILIMCTRL, 0x1}, > + {BQ2515X_LDOCTRL, 0xB0}, > + {BQ2515X_MRCTRL, 0x2A}, > + {BQ2515X_ICCTRL0, 0x10}, > + {BQ2515X_ICCTRL1, 0x0}, > + {BQ2515X_ICCTRL2, 0x0}, > + {BQ2515X_ADCCTRL0, 0x2}, > + {BQ2515X_ADCCTRL1, 0x40}, > + {BQ2515X_ADC_COMP1_M, 0x23}, > + {BQ2515X_ADC_COMP1_L, 0x20}, > + {BQ2515X_ADC_COMP2_M, 0x38}, > + {BQ2515X_ADC_COMP2_L, 0x90}, > + {BQ2515X_ADC_COMP3_M, 0x0}, > + {BQ2515X_ADC_COMP3_L, 0x0}, > + {BQ2515X_ADC_READ_EN, 0x0}, > + {BQ2515X_TS_FASTCHGCTRL, 0x34}, > + {BQ2515X_TS_COLD, 0x7C}, > + {BQ2515X_TS_COOL, 0x6D}, > + {BQ2515X_TS_WARM, 0x38}, > + {BQ2515X_TS_HOT, 0x27}, > + {BQ2515X_DEVICE_ID, 0x20}, > +}; > + > +static struct reg_default bq25155_reg_defs[] = { > + {BQ2515X_FLAG0, 0x0}, > + {BQ2515X_FLAG1, 0x0}, > + {BQ2515X_FLAG2, 0x0}, > + {BQ2515X_FLAG3, 0x0}, > + {BQ2515X_MASK0, 0x0}, > + {BQ2515X_MASK1, 0x0}, > + {BQ2515X_MASK2, 0x71}, > + {BQ2515X_MASK3, 0x0}, > + {BQ2515X_VBAT_CTRL, 0x3C}, > + {BQ2515X_ICHG_CTRL, 0x8}, > + {BQ2515X_PCHRGCTRL, 0x2}, > + {BQ2515X_TERMCTRL, 0x14}, > + {BQ2515X_BUVLO, 0x0}, > + {BQ2515X_CHARGERCTRL0, 0x82}, > + {BQ2515X_CHARGERCTRL1, 0xC2}, > + {BQ2515X_ILIMCTRL, 0x6}, > + {BQ2515X_LDOCTRL, 0xB0}, > + {BQ2515X_MRCTRL, 0x2A}, > + {BQ2515X_ICCTRL0, 0x10}, > + {BQ2515X_ICCTRL1, 0x0}, > + {BQ2515X_ICCTRL2, 0x40}, > + {BQ2515X_ADCCTRL0, 0x2}, > + {BQ2515X_ADCCTRL1, 0x40}, > + {BQ2515X_ADC_COMP1_M, 0x23}, > + {BQ2515X_ADC_COMP1_L, 0x20}, > + {BQ2515X_ADC_COMP2_M, 0x38}, > + {BQ2515X_ADC_COMP2_L, 0x90}, > + {BQ2515X_ADC_COMP3_M, 0x0}, > + {BQ2515X_ADC_COMP3_L, 0x0}, > + {BQ2515X_ADC_READ_EN, 0x0}, > + {BQ2515X_TS_FASTCHGCTRL, 0x34}, > + {BQ2515X_TS_COLD, 0x7C}, > + {BQ2515X_TS_COOL, 0x6D}, > + {BQ2515X_TS_WARM, 0x38}, > + {BQ2515X_TS_HOT, 0x27}, > + {BQ2515X_DEVICE_ID, 0x35}, > +}; > + > +static bool bq2515x_is_ps_online(struct bq2515x_device *bq2515x) > +{ > + return bq2515x->mains_online; > +} > + > +static int bq2515x_wake_up(struct bq2515x_device *bq2515x) > +{ > + int ret; > + int val; > + > + /* Read the STAT register if we can read it then the device is out > + * of ship mode. If the register cannot be read then attempt to wake > + * it up and enable the ADC. > + */ > + ret = regmap_read(bq2515x->regmap, BQ2515X_STAT0, &val); > + if (!ret) > + return ret; > + > + /* Need to toggle LP and bring device out of ship mode. The device > + * will exit the ship mode when the MR pin is held low for at least > + * t_WAKE2 as shown in section 8.3.7.1 of the datasheet. > + */ > + if (bq2515x->lp_gpio) > + gpiod_set_value_cansleep(bq2515x->lp_gpio, 0); gpiod_set_value_cansleep is nop when NULL is supplied as gpio, so can drop the if. > + if (bq2515x->lp_gpio) { > + gpiod_set_value_cansleep(bq2515x->reset_gpio, 0); > + usleep_range(BQ2515X_TWAKE2_MIN_US, BQ2515X_TWAKE2_MAX_US); > + gpiod_set_value_cansleep(bq2515x->reset_gpio, 1); > + } The if is wrong (lp_gpio instead of reset_gpio). Also you can just remove it. > + return regmap_write(bq2515x->regmap, BQ2515X_ADC_READ_EN, BIT(3)); > +} > + > +static int bq2515x_update_ps_status(struct bq2515x_device *bq2515x) > +{ > + bool dc = false; > + unsigned int val; > + int ret; > + > + if (bq2515x->ac_detect_gpio) > + val = gpiod_get_value_cansleep(bq2515x->ac_detect_gpio); > + empty line can be removed. > + else { > + ret = regmap_read(bq2515x->regmap, BQ2515X_STAT0, &val); > + if (ret < 0) > + return ret; > + } > + > + dc = val & BQ2515X_VIN_GOOD; > + > + ret = bq2515x->mains_online != dc; > + > + bq2515x->mains_online = dc; > + > + return ret; > +} > + > +static int bq2515x_disable_watchdog_timers(struct bq2515x_device *bq2515x) > +{ > + int ret; > + > + ret = regmap_update_bits(bq2515x->regmap, BQ2515X_CHARGERCTRL0, > + BQ2515X_WATCHDOG_DISABLE, BQ2515X_WATCHDOG_DISABLE); > + if (ret < 0) > + return ret; > + > + return regmap_update_bits(bq2515x->regmap, BQ2515X_ICCTRL2, > + BQ2515X_HWRESET_14S_WD, 0); > +} > + > +static int bq2515x_get_battery_voltage_now(struct bq2515x_device *bq2515x) > +{ > + int ret; > + int vbat_msb; > + int vbat_lsb; > + uint32_t vbat_measurement; > + > + if (!bq2515x_is_ps_online(bq2515x)) > + bq2515x_wake_up(bq2515x); > + > + ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_VBAT_M, &vbat_msb); > + if (ret) > + return ret; > + > + ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_VBAT_L, &vbat_lsb); > + if (ret) > + return ret; > + > + vbat_measurement = (vbat_msb << 8) | vbat_lsb; > + > + return vbat_measurement * (BQ2515X_UV_FACTOR / BQ2515X_DIVISOR) * 6; > +} > + > +static int bq2515x_get_battery_current_now(struct bq2515x_device *bq2515x) > +{ > + int ret; > + int ichg_msb; > + int ichg_lsb; > + uint32_t ichg_measurement; > + u16 ichg_multiplier = BQ2515X_ICHG_RNG_1B0_UA; > + unsigned int ichg_reg_code, reg_code; > + unsigned int icharge_range = 0, pchrgctrl; > + unsigned int buvlo, vlowv_sel, vlowv = BQ2515X_VLOWV_SEL_1B0_UV; > + > + if (!bq2515x_is_ps_online(bq2515x)) > + return -ENODATA; > + > + ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_ICHG_M, &ichg_msb); > + if (ret < 0) > + return ret; > + > + ret = regmap_read(bq2515x->regmap, BQ2515X_ADC_ICHG_L, &ichg_lsb); > + if (ret < 0) > + return ret; > + > + ichg_measurement = (ichg_msb << 8) | ichg_lsb; > + > + ret = regmap_read(bq2515x->regmap, BQ2515X_BUVLO, &buvlo); > + if (ret < 0) > + return ret; > + > + vlowv_sel = buvlo & BQ2515X_VLOWV_SEL; > + > + if (vlowv_sel) > + vlowv = BQ2515X_VLOWV_SEL_1B1_UV; > + > + if (bq2515x_get_battery_voltage_now(bq2515x) < vlowv) { > + ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, > + &pchrgctrl); > + if (ret < 0) > + return ret; > + reg_code = pchrgctrl & BQ2515X_PRECHARGE_MASK; > + } else { > + ret = regmap_read(bq2515x->regmap, BQ2515X_ICHG_CTRL, > + &ichg_reg_code); > + if (ret < 0) > + return ret; > + reg_code = ichg_reg_code; > + } > + > + ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl); > + if (ret < 0) > + return ret; > + > + icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE; > + > + if (icharge_range) > + ichg_multiplier = BQ2515X_ICHG_RNG_1B1_UA; > + > + return reg_code * (ichg_multiplier * ichg_measurement > + / BQ2515X_ICHG_DIVISOR); > +} > + > +static bool bq2515x_get_charge_disable(struct bq2515x_device *bq2515x) > +{ > + int ret; > + int ce_pin = 0; > + int icctrl2; > + int charger_disable; > + > + if (bq2515x->ce_gpio) > + ce_pin = gpiod_get_value_cansleep(bq2515x->ce_gpio); gpiod_get_value_cansleep will return 0 if NULL is supplied, so you can remove the if. > + > + ret = regmap_read(bq2515x->regmap, BQ2515X_ICCTRL2, &icctrl2); > + if (ret < 0) > + return ret; > + > + charger_disable = icctrl2 & BQ2515X_CHARGER_DISABLE; > + > + if (charger_disable || ce_pin) > + return true; > + > + return false; > +} > + > +static int bq2515x_set_charge_disable(struct bq2515x_device *bq2515x, int val) > +{ > + if (bq2515x->ce_gpio) > + gpiod_set_value_cansleep(bq2515x->ce_gpio, val); Here the if can also be removed. > + > + return regmap_update_bits(bq2515x->regmap, BQ2515X_ICCTRL2, > + BQ2515X_CHARGER_DISABLE, val); > +} > + > +static int bq2515x_get_const_charge_current(struct bq2515x_device *bq2515x) > +{ > + int ret; > + u16 ichg_multiplier = BQ2515X_ICHG_RNG_1B0_UA; > + unsigned int ichg_reg_code; > + unsigned int pchrgctrl; > + unsigned int icharge_range; > + > + ret = regmap_read(bq2515x->regmap, BQ2515X_ICHG_CTRL, &ichg_reg_code); > + if (ret < 0) > + return ret; > + > + ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl); > + if (ret < 0) > + return ret; > + > + icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE; > + > + if (icharge_range) > + ichg_multiplier = BQ2515X_ICHG_RNG_1B1_UA; > + > + return ichg_reg_code * ichg_multiplier; > +} > + > +static int bq2515x_set_const_charge_current(struct bq2515x_device *bq2515x, > + int val) > +{ > + int ret; > + unsigned int ichg_reg_code; > + u16 ichg_multiplier = BQ2515X_ICHG_RNG_1B0_UA; > + unsigned int icharge_range = 0; > + > + if (val > BQ2515X_ICHG_MAX_UA || val < BQ2515X_ICHG_MIN_UA) > + return -EINVAL; > + > + if (val > BQ2515X_ICHG_CURR_STEP_THRESH_UA) { > + ichg_multiplier = BQ2515X_ICHG_RNG_1B1_UA; > + icharge_range = BQ2515X_ICHARGE_RANGE; > + } > + > + bq2515x_set_charge_disable(bq2515x, 1); > + > + ret = regmap_update_bits(bq2515x->regmap, BQ2515X_PCHRGCTRL, > + BQ2515X_ICHARGE_RANGE, icharge_range); > + if (ret < 0) > + return ret; > + > + ichg_reg_code = val / ichg_multiplier; > + > + ret = regmap_write(bq2515x->regmap, BQ2515X_ICHG_CTRL, ichg_reg_code); > + if (ret < 0) > + return ret; > + > + return bq2515x_set_charge_disable(bq2515x, 0); > +} > + > +static int bq2515x_get_precharge_current(struct bq2515x_device *bq2515x) > +{ > + int ret; > + unsigned int pchrgctrl; > + unsigned int icharge_range; > + u16 precharge_multiplier = BQ2515X_ICHG_RNG_1B0_UA; > + unsigned int precharge_reg_code; > + > + ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl); > + if (ret < 0) > + return ret; > + > + icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE; > + > + if (icharge_range) > + precharge_multiplier = BQ2515X_ICHG_RNG_1B1_UA; > + > + precharge_reg_code = pchrgctrl & BQ2515X_PRECHARGE_MASK; > + > + return precharge_reg_code * precharge_multiplier; > +} > + > +static int bq2515x_set_precharge_current(struct bq2515x_device *bq2515x, > + int val) > +{ > + int ret; > + unsigned int pchrgctrl; > + unsigned int icharge_range; > + unsigned int precharge_reg_code; > + u16 precharge_multiplier = BQ2515X_ICHG_RNG_1B0_UA; > + u16 precharge_max_ua = BQ2515X_PRECHRG_ICHRG_RNGE_1875_UA; > + > + ret = regmap_read(bq2515x->regmap, BQ2515X_PCHRGCTRL, &pchrgctrl); > + if (ret < 0) > + return ret; > + > + icharge_range = pchrgctrl & BQ2515X_ICHARGE_RANGE; > + > + if (icharge_range) { > + precharge_max_ua = BQ2515X_PRECHRG_ICHRG_RNGE_3750_UA; > + precharge_multiplier = BQ2515X_ICHG_RNG_1B1_UA; > + } > + if (val > precharge_max_ua || val < BQ2515X_ICHG_MIN_UA) > + return -EINVAL; > + > + precharge_reg_code = val / precharge_multiplier; > + > + ret = bq2515x_set_charge_disable(bq2515x, 1); > + if (ret < 0) > + return ret; > + > + ret = regmap_update_bits(bq2515x->regmap, BQ2515X_PCHRGCTRL, > + BQ2515X_PRECHARGE_MASK, precharge_reg_code); > + if (ret < 0) > + return ret; > + > + return bq2515x_set_charge_disable(bq2515x, 0); > +} > + > +static int bq2515x_charging_status(struct bq2515x_device *bq2515x, > + union power_supply_propval *val) > +{ > + bool status0_no_fault; > + bool status1_no_fault; > + bool ce_status; > + bool charge_done; > + unsigned int status; > + int ret; > + > + if (!bq2515x_is_ps_online(bq2515x)) { > + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; > + return 0; > + } > + > + ret = regmap_read(bq2515x->regmap, BQ2515X_STAT0, &status); > + if (ret) > + return ret; > + > + /** > + * The code block below is used to determine if any faults from the > + * STAT0 register are disbaling charging or if the charge has completed > + * according to the CHARGE_DONE_STAT bit. > + */ > + if (((status & BQ2515X_STAT0_MASK) == true) & > + ((status & BQ2515X_CHRG_DONE) == false)) { > + status0_no_fault = true; > + charge_done = false; > + } else if (status & BQ2515X_CHRG_DONE) { > + charge_done = true; > + status0_no_fault = false; > + } else { > + status0_no_fault = false; > + charge_done = false; > + } > + > + ret = regmap_read(bq2515x->regmap, BQ2515X_STAT1, &status); > + if (ret) > + return ret; > + /** > + *The code block below is used to determine if any faults from the > + * STAT1 register are disbaling charging > + */ > + if ((status & BQ2515X_STAT1_MASK) == false) > + status1_no_fault = true; > + else > + status1_no_fault = false; > + > + ce_status = (!bq2515x_get_charge_disable(bq2515x)); > + > + /** > + *If there are no faults and charging is enabled, then status is > + *charging. Otherwise, if charging is complete, then status is full. > + *Otherwise, if a fault exists or charging is disbaled, then status is > + *not charging > + */ > + if (status0_no_fault & status1_no_fault & ce_status) > + val->intval = POWER_SUPPLY_STATUS_CHARGING; > + else if (charge_done) > + val->intval = POWER_SUPPLY_STATUS_FULL; > + else if (!(status0_no_fault & status1_no_fault & ce_status)) > + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; > + > + return ret; > +} > + > +static int bq2515x_get_batt_reg(struct bq2515x_device *bq2515x) > +{ > + int vbat_reg_code; > + int ret; > + > + ret = regmap_read(bq2515x->regmap, BQ2515X_VBAT_CTRL, &vbat_reg_code); > + if (ret) > + return ret; > + > + return BQ2515X_VBAT_BASE_VOLT + vbat_reg_code * BQ2515X_VBAT_STEP_UV; > +} > + > +static int bq2515x_set_batt_reg(struct bq2515x_device *bq2515x, int val) > +{ > + int vbat_reg_code; > + > + if (val > BQ2515X_VBAT_REG_MAX || val < BQ2515X_VBAT_REG_MIN) > + return -EINVAL; > + > + vbat_reg_code = (val - BQ2515X_VBAT_BASE_VOLT) / BQ2515X_VBAT_STEP_UV; > + > + return regmap_write(bq2515x->regmap, BQ2515X_VBAT_CTRL, vbat_reg_code); > +} > + > +static int bq2515x_get_ilim_lvl(struct bq2515x_device *bq2515x) > +{ > + int ret; > + int ilimctrl; > + > + ret = regmap_read(bq2515x->regmap, BQ2515X_ILIMCTRL, &ilimctrl); > + if (ret) > + return ret; > + > + return bq2515x_ilim_lvl_values[ilimctrl & BQ2515X_ILIM_MASK]; > +} > + > +static int bq2515x_set_ilim_lvl(struct bq2515x_device *bq2515x, int val) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(bq2515x_ilim_lvl_values); i++) { > + if (val == bq2515x_ilim_lvl_values[i]) > + break; > + > + if (val > bq2515x_ilim_lvl_values[i - 1] && > + val < bq2515x_ilim_lvl_values[i]) { > + if (val - bq2515x_ilim_lvl_values[i - 1] < > + bq2515x_ilim_lvl_values[i] - val) { > + i = i - 1; > + break; > + } > + } > + } > + > + return regmap_write(bq2515x->regmap, BQ2515X_ILIMCTRL, i); > +} > + > +static int bq2515x_power_supply_property_is_writeable(struct power_supply *psy, > + enum power_supply_property prop) > +{ > + switch (prop) { > + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: > + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: > + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: > + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: > + return true; > + default: > + return false; > + } > +} > + > +static int bq2515x_charger_get_health(struct bq2515x_device *bq2515x, > + union power_supply_propval *val) > +{ > + int health = POWER_SUPPLY_HEALTH_GOOD; > + int ret; > + unsigned int stat1; > + unsigned int flag3; > + > + if (!bq2515x_is_ps_online(bq2515x)) > + bq2515x_wake_up(bq2515x); > + > + ret = regmap_read(bq2515x->regmap, BQ2515X_FLAG3, &flag3); > + if (ret) > + return ret; > + > + if (flag3 & BQ2515X_SAFETY_TIMER_EXP) { > + health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; > + goto ret_health; > + } > + > + if (stat1 & BQ2515X_VIN_OVP_FAULT_STAT) { > + health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; > + goto ret_health; > + } > + > + ret = regmap_read(bq2515x->regmap, BQ2515X_STAT1, &stat1); > + if (ret) > + return ret; > + > + if (stat1 & BQ2515X_HEALTH_MASK) { > + switch (stat1 & BQ2515X_HEALTH_MASK) { > + case BQ2515X_TS_HOT_STAT: > + health = POWER_SUPPLY_HEALTH_HOT; > + break; > + case BQ2515X_TS_WARM_STAT: > + health = POWER_SUPPLY_HEALTH_WARM; > + break; > + case BQ2515X_TS_COOL_STAT: > + health = POWER_SUPPLY_HEALTH_COOL; > + break; > + case BQ2515X_TS_COLD_STAT: > + health = POWER_SUPPLY_HEALTH_COLD; > + break; > + default: > + health = POWER_SUPPLY_HEALTH_UNKNOWN; > + break; > + } > + } > + > + goto ret_health; > + > +ret_health: > + val->intval = health; > + return 0; > +} > + > +static int bq2515x_mains_set_property(struct power_supply *psy, > + enum power_supply_property prop, > + const union power_supply_propval *val) > +{ > + struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy); > + int ret; > + > + switch (prop) { > + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: > + ret = bq2515x_set_ilim_lvl(bq2515x, val->intval); > + break; > + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: > + ret = bq2515x_set_batt_reg(bq2515x, val->intval); > + break; > + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: > + ret = bq2515x_set_const_charge_current(bq2515x, val->intval); > + break; > + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: > + ret = bq2515x_set_precharge_current(bq2515x, val->intval); > + break; > + default: > + return -EINVAL; > + } > + > + return ret; > +} > + > +static int bq2515x_mains_get_property(struct power_supply *psy, > + enum power_supply_property prop, > + union power_supply_propval *val) > +{ > + struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy); > + int ret = 0; > + > + switch (prop) { > + case POWER_SUPPLY_PROP_ONLINE: > + val->intval = bq2515x->mains_online; > + break; > + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: > + ret = bq2515x_get_const_charge_current(bq2515x); > + if (ret < 0) > + return ret; > + > + val->intval = ret; > + break; > + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: > + ret = bq2515x_get_precharge_current(bq2515x); > + if (ret < 0) > + return ret; > + > + val->intval = ret; > + break; > + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: > + ret = bq2515x_get_ilim_lvl(bq2515x); > + if (ret < 0) > + return ret; > + > + val->intval = ret; > + break; > + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: > + ret = bq2515x_get_batt_reg(bq2515x); > + if (ret < 0) > + return ret; > + > + val->intval = ret; > + break; > + case POWER_SUPPLY_PROP_MODEL_NAME: > + val->strval = bq2515x->model_name; > + break; > + case POWER_SUPPLY_PROP_MANUFACTURER: > + val->strval = BQ2515X_MANUFACTURER; > + break; > + case POWER_SUPPLY_PROP_VOLTAGE_MAX: > + val->intval = BQ2515X_VBAT_REG_MAX; > + break; > + case POWER_SUPPLY_PROP_VOLTAGE_MIN: > + val->intval = BQ2515X_VBAT_REG_MIN; > + break; > + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: > + val->intval = BQ2515X_ICHG_MAX_UA; > + break; > + default: > + return -EINVAL; > + } > + > + return ret; > +} > + > +static int bq2515x_battery_get_property(struct power_supply *psy, > + enum power_supply_property prop, > + union power_supply_propval *val) > +{ > + struct bq2515x_device *bq2515x = power_supply_get_drvdata(psy); > + int ret; > + > + ret = bq2515x_update_ps_status(bq2515x); > + if (ret < 0) > + return ret; > + > + switch (prop) { > + > + case POWER_SUPPLY_PROP_STATUS: > + if (!bq2515x_is_ps_online(bq2515x)) { > + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; > + break; > + } > + > + ret = bq2515x_charging_status(bq2515x, val); > + break; > + case POWER_SUPPLY_PROP_VOLTAGE_NOW: > + ret = bq2515x_get_battery_voltage_now(bq2515x); > + if (ret < 0) > + return ret; > + > + val->intval = ret; > + break; > + case POWER_SUPPLY_PROP_CURRENT_NOW: > + ret = bq2515x_get_battery_current_now(bq2515x); > + if (ret < 0) > + return ret; > + > + val->intval = ret; > + break; > + case POWER_SUPPLY_PROP_HEALTH: > + ret = bq2515x_charger_get_health(bq2515x, val); > + if (ret) > + val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; > + break; > + default: > + return -EINVAL; > + } > + return 0; > +} > + > +static enum power_supply_property bq2515x_battery_properties[] = { > + POWER_SUPPLY_PROP_STATUS, > + POWER_SUPPLY_PROP_HEALTH, > + POWER_SUPPLY_PROP_VOLTAGE_NOW, > + POWER_SUPPLY_PROP_CURRENT_NOW, > +}; > + > +static enum power_supply_property bq2515x_charger_properties[] = { > + POWER_SUPPLY_PROP_ONLINE, > + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, > + POWER_SUPPLY_PROP_PRECHARGE_CURRENT, > + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, > + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, > + POWER_SUPPLY_PROP_VOLTAGE_MAX, > + POWER_SUPPLY_PROP_VOLTAGE_MIN, > + POWER_SUPPLY_PROP_MODEL_NAME, > + POWER_SUPPLY_PROP_MANUFACTURER, > +}; > + > +static struct power_supply_desc bq2515x_mains_desc = { > + .name = "bq2515x-mains", > + .type = POWER_SUPPLY_TYPE_MAINS, > + .get_property = bq2515x_mains_get_property, > + .set_property = bq2515x_mains_set_property, > + .properties = bq2515x_charger_properties, > + .num_properties = ARRAY_SIZE(bq2515x_charger_properties), > + .property_is_writeable = bq2515x_power_supply_property_is_writeable, > + > +}; > + > +static struct power_supply_desc bq2515x_battery_desc = { > + .name = "bq2515x-battery", > + .type = POWER_SUPPLY_TYPE_BATTERY, > + .get_property = bq2515x_battery_get_property, > + .properties = bq2515x_battery_properties, > + .num_properties = ARRAY_SIZE(bq2515x_battery_properties), > +}; > + > +static int bq2515x_power_supply_register(struct bq2515x_device *bq2515x) > +{ > + struct power_supply_config psy_cfg = { .drv_data = bq2515x, }; > + > + bq2515x->mains = devm_power_supply_register(bq2515x->dev, > + &bq2515x_mains_desc, > + &psy_cfg); > + if (IS_ERR(bq2515x->mains)) > + return -EINVAL; > + > + bq2515x->battery = devm_power_supply_register(bq2515x->dev, > + &bq2515x_battery_desc, > + &psy_cfg); > + if (IS_ERR(bq2515x->battery)) > + return -EINVAL; > + > + return 0; > +} > + > +static int bq2515x_hw_init(struct bq2515x_device *bq2515x) > +{ > + int ret = 0; > + > + if (bq2515x->init_data.ichg) > + ret = bq2515x_set_const_charge_current(bq2515x, > + bq2515x->init_data.ichg); > + > + if (ret) > + goto err_out; > + > + if (bq2515x->init_data.ilim) > + ret = bq2515x_set_ilim_lvl(bq2515x, bq2515x->init_data.ilim); > + > + if (ret) > + goto err_out; > + > + if (bq2515x->init_data.vreg) > + ret = bq2515x_set_batt_reg(bq2515x, bq2515x->init_data.vreg); > + > + if (ret) > + goto err_out; > + > + ret = bq2515x_disable_watchdog_timers(bq2515x); > + > + if (ret) > + goto err_out; > + > +err_out: > + return ret; > +} > + > +static int bq2515x_read_properties(struct bq2515x_device *bq2515x) > +{ > + int ret; > + > + ret = device_property_read_u32(bq2515x->dev, > + "constant-charge-current-max-microamp", > + &bq2515x->init_data.ichg); > + if (ret) > + bq2515x->init_data.ichg = BQ2515X_DEFAULT_ICHG_UA; > + > + ret = device_property_read_u32(bq2515x->dev, > + "precharge-current-max-microamp", > + &bq2515x->init_data.iprecharge); > + if (ret) > + bq2515x->init_data.iprecharge = BQ2515X_DEFAULT_IPRECHARGE_UA; > + > + ret = device_property_read_u32(bq2515x->dev, > + "input-current-limit-microamp", > + &bq2515x->init_data.ilim); > + if (ret) > + switch (bq2515x->device_id) { > + case BQ25150: > + bq2515x->init_data.ilim = BQ25150_DEFAULT_ILIM_UA; > + break; > + case BQ25155: > + bq2515x->init_data.ilim = BQ25155_DEFAULT_ILIM_UA; > + break; > + } > + > + ret = device_property_read_u32(bq2515x->dev, > + "constant-charge-voltage-max-microvolt", > + &bq2515x->init_data.vreg); > + if (ret) > + bq2515x->init_data.vreg = BQ2515X_DEFAULT_VBAT_REG_UV; As I wrote in the bindings patch, the *-charge-* properties should be in a simple-battery node and can be received via power_supply_get_battery_info(). There are a few example drivers in mainline tree. > + bq2515x->ac_detect_gpio = devm_gpiod_get_optional(bq2515x->dev, > + "ac_detect", GPIOD_IN); > + if (IS_ERR(bq2515x->ac_detect_gpio)) > + dev_info(bq2515x->dev, "ac_detect GPIO not defined"); This is incorrect. You are calling devm_gpiod_get_optional, which returns NULL if the gpio is not defined. If an error is returned you should return it, since it can be e.g. EPROBE_DEFER. In any case the system designer configured a GPIO and expects it to be used, so its better to just return an the error instead of ignoring it. > + bq2515x->reset_gpio = devm_gpiod_get_optional(bq2515x->dev, > + "reset", GPIOD_OUT_LOW); > + if (IS_ERR(bq2515x->reset_gpio)) { > + if (PTR_ERR(bq2515x->reset_gpio) == -EPROBE_DEFER) > + return -EPROBE_DEFER; > + } > + > + bq2515x->lp_gpio = devm_gpiod_get_optional(bq2515x->dev, "low-power", > + GPIOD_OUT_LOW); > + if (IS_ERR(bq2515x->lp_gpio)) { > + if (PTR_ERR(bq2515x->lp_gpio) == -EPROBE_DEFER) > + return -EPROBE_DEFER; > + } > + > + bq2515x->ce_gpio = devm_gpiod_get_optional(bq2515x->dev, > + "charge-enable", > + GPIOD_OUT_LOW); > + if (IS_ERR(bq2515x->ce_gpio)) { > + if (PTR_ERR(bq2515x->ce_gpio) == -EPROBE_DEFER) > + return -EPROBE_DEFER; > + } For all of the GPIOs you should also just return PTR_ERR(). > + return 0; > +} > + > +static bool bq2515x_volatile_register(struct device *dev, unsigned int reg) > +{ > + switch (reg) { > + case BQ2515X_STAT0 ... BQ2515X_FLAG3: > + case BQ2515X_ADC_VBAT_M ... BQ2515X_ADC_IIN_L: > + return true; > + default: > + return false; > + } > +} > + > +static const struct regmap_config bq25150_regmap_config = { > + .reg_bits = 8, > + .val_bits = 8, > + > + .max_register = BQ2515X_DEVICE_ID, > + .reg_defaults = bq25150_reg_defs, > + .num_reg_defaults = ARRAY_SIZE(bq25150_reg_defs), > + .cache_type = REGCACHE_RBTREE, > + .volatile_reg = bq2515x_volatile_register, > +}; > + > +static const struct regmap_config bq25155_regmap_config = { > + .reg_bits = 8, > + .val_bits = 8, > + > + .max_register = BQ2515X_DEVICE_ID, > + .reg_defaults = bq25155_reg_defs, > + .num_reg_defaults = ARRAY_SIZE(bq25155_reg_defs), > + .cache_type = REGCACHE_RBTREE, > + .volatile_reg = bq2515x_volatile_register, > +}; > + > +static int bq2515x_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct device *dev = &client->dev; > + struct bq2515x_device *bq; > + int ret; > + > + bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL); > + if (!bq) > + return -ENOMEM; > + > + bq->client = client; > + bq->dev = dev; > + > + mutex_init(&bq->lock); > + > + strncpy(bq->model_name, id->name, I2C_NAME_SIZE); > + > + bq->device_id = (enum bq2515x_id)of_device_get_match_data(&client->dev); > + > + switch (bq->device_id) { > + case BQ25150: > + bq->regmap = devm_regmap_init_i2c(client, > +&bq25150_regmap_config); > + break; > + case BQ25155: > + bq->regmap = devm_regmap_init_i2c(client, > +&bq25155_regmap_config); > + break; > + } > + > + if (IS_ERR(bq->regmap)) { > + dev_err(dev, "failed to allocate register map\n"); > + return PTR_ERR(bq->regmap); > + } > + > + i2c_set_clientdata(client, bq); > + > + ret = bq2515x_read_properties(bq); > + if (ret) { > + dev_err(dev, "Failed to read device tree properties %d\n", > + ret); > + goto err_out; > + } > + > + ret = bq2515x_hw_init(bq); > + if (ret) { > + dev_err(dev, "Cannot initialize the chip.\n"); > + goto err_out; > + } > + > + return bq2515x_power_supply_register(bq); > + > +err_out: > + mutex_destroy(&bq->lock); > + return ret; > +} > + > +static const struct i2c_device_id bq2515x_i2c_ids[] = { > + { "bq25150", BQ25150, }, > + { "bq25155", BQ25155, }, > + {}, > +}; > +MODULE_DEVICE_TABLE(i2c, bq2515x_i2c_ids); > + > +static const struct of_device_id bq2515x_of_match[] = { > + { .compatible = "ti,bq25150", }, > + { .compatible = "ti,bq25155", }, > + { }, > +}; > +MODULE_DEVICE_TABLE(of, bq2515x_of_match); > + > +static struct i2c_driver bq2515x_driver = { > + .driver = { > + .name = "bq2515x-charger", > + .of_match_table = bq2515x_of_match, > + }, > + .probe = bq2515x_probe, > + .id_table = bq2515x_i2c_ids, > +}; > +module_i2c_driver(bq2515x_driver); > + > +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); > +MODULE_AUTHOR("Ricardo Rivera-Matos <r-rivera-matos@ti.com>"); > +MODULE_DESCRIPTION("BQ2515X charger driver"); > +MODULE_LICENSE("GPL v2"); -- Sebastian [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 833 bytes --] ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2020-05-03 1:16 UTC | newest] Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2020-05-01 17:51 [PATCH v5 0/3] BQ25150/155 Batter charger Dan Murphy 2020-05-01 17:51 ` [PATCH v5 1/3] power_supply: Add additional health properties to the header Dan Murphy 2020-05-03 0:30 ` Sebastian Reichel 2020-05-01 17:51 ` [PATCH v5 2/3] Add the bindings for the bq25150 and bq25155 500mA charging ICs from Texas Instruments Dan Murphy 2020-05-01 21:35 ` Rob Herring 2020-05-03 0:40 ` Sebastian Reichel 2020-05-01 17:51 ` [PATCH v5 3/3] power: supply: bq25150 introduce the bq25150 Dan Murphy 2020-05-03 1:16 ` Sebastian Reichel
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).