* [PATCH 1/2] dt-binding: bq25980: Add the bq25980 flash charger
@ 2020-08-31 16:48 Dan Murphy
2020-08-31 16:48 ` [PATCH 2/2] power: bq25980: Add support for the BQ259xx family Dan Murphy
2020-09-14 18:26 ` [PATCH 1/2] dt-binding: bq25980: Add the bq25980 flash charger Rob Herring
0 siblings, 2 replies; 5+ messages in thread
From: Dan Murphy @ 2020-08-31 16:48 UTC (permalink / raw)
To: sre, robh; +Cc: devicetree, r-rivera-matos, linux-pm, linux-kernel, Dan Murphy
Add yaml for the bq25980 flash charger.
Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
.../bindings/power/supply/bq25980.yaml | 114 ++++++++++++++++++
1 file changed, 114 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/supply/bq25980.yaml
diff --git a/Documentation/devicetree/bindings/power/supply/bq25980.yaml b/Documentation/devicetree/bindings/power/supply/bq25980.yaml
new file mode 100644
index 000000000000..f6b3dd4093ca
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/bq25980.yaml
@@ -0,0 +1,114 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2020 Texas Instruments Incorporated
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/power/supply/bq25980.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: TI BQ25980 Flash Charger
+
+maintainers:
+ - Dan Murphy <dmurphy@ti.com>
+ - Ricardo Rivera-Matos <r-rivera-matos@ti.com>
+
+description: |
+ The BQ25980, BQ25975, and BQ25960 are a series of flash chargers intended
+ for use in high-power density portable electronics. These inductorless
+ switching chargers can provide over 97% efficiency by making use of the
+ switched capacitor architecture.
+
+allOf:
+ - $ref: power-supply.yaml#
+
+properties:
+ compatible:
+ enum:
+ - ti,bq25980
+ - ti,bq25975
+ - ti,bq25960
+
+ reg:
+ maxItems: 1
+
+ ti,watchdog-timeout-ms:
+ description: |
+ Watchdog timer in milli seconds. 0 disables the watchdog.
+ default: 0
+ minimum: 0
+ maximum: 300000
+ enum: [ 0, 5000, 10000, 50000, 300000]
+
+ ti,sc-ovp-limit-microvolt:
+ description: |
+ Minimum input voltage limit in micro volts with a when the charger is in
+ switch cap mode. 100000 micro volt step.
+ default: 17800000
+ minimum: 14000000
+ maximum: 22000000
+
+ ti,sc-ocp-limit-microamp:
+ description: |
+ Maximum input current limit in micro amps with a 100000 micro amp step.
+ minimum: 100000
+ maximum: 3300000
+
+ ti,bypass-ovp-limit-microvolt:
+ description: |
+ Minimum input voltage limit in micro volts with a when the charger is in
+ switch cap mode. 50000 micro volt step.
+ minimum: 7000000
+ maximum: 12750000
+
+ ti,bypass-ocp-limit-microamp:
+ description: |
+ Maximum input current limit in micro amps with a 100000 micro amp step.
+ minimum: 100000
+ maximum: 3300000
+
+ ti,bypass-enable:
+ type: boolean
+ description: Enables bypass mode at boot time
+
+ interrupts:
+ description: |
+ Indicates that the device state has changed.
+
+ monitored-battery:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: phandle to the battery node being monitored
+
+required:
+ - compatible
+ - reg
+ - monitored-battery
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ bat: battery {
+ compatible = "simple-battery";
+ constant-charge-current-max-microamp = <4000000>;
+ constant-charge-voltage-max-microvolt = <8400000>;
+ precharge-current-microamp = <160000>;
+ charge-term-current-microamp = <160000>;
+ };
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ i2c0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ bq25980: charger@65 {
+ compatible = "ti,bq25980";
+ reg = <0x65>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <16 IRQ_TYPE_EDGE_FALLING>;
+ ti,watchdog-timer = <0>;
+ ti,sc-ocp-limit-microamp = <2000000>;
+ ti,sc-ovp-limit-microvolt = <17800000>;
+ monitored-battery = <&bat>;
+ };
+ };
+
+...
--
2.28.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/2] power: bq25980: Add support for the BQ259xx family
2020-08-31 16:48 [PATCH 1/2] dt-binding: bq25980: Add the bq25980 flash charger Dan Murphy
@ 2020-08-31 16:48 ` Dan Murphy
2020-09-22 16:06 ` Dan Murphy
2020-10-04 22:47 ` Sebastian Reichel
2020-09-14 18:26 ` [PATCH 1/2] dt-binding: bq25980: Add the bq25980 flash charger Rob Herring
1 sibling, 2 replies; 5+ messages in thread
From: Dan Murphy @ 2020-08-31 16:48 UTC (permalink / raw)
To: sre, robh; +Cc: devicetree, r-rivera-matos, linux-pm, linux-kernel, Dan Murphy
Add support for the BQ25980, BQ25975 and BQ25960 family of flash
chargers.
Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
drivers/power/supply/Kconfig | 9 +
drivers/power/supply/Makefile | 1 +
drivers/power/supply/bq25980_charger.c | 1316 ++++++++++++++++++++++++
drivers/power/supply/bq25980_charger.h | 178 ++++
4 files changed, 1504 insertions(+)
create mode 100644 drivers/power/supply/bq25980_charger.c
create mode 100644 drivers/power/supply/bq25980_charger.h
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index faf2830aa152..fbe974e4cff4 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -631,6 +631,15 @@ config CHARGER_BQ25890
help
Say Y to enable support for the TI BQ25890 battery charger.
+config CHARGER_BQ25980
+ tristate "TI BQ25980 battery charger driver"
+ depends on I2C
+ depends on GPIOLIB || COMPILE_TEST
+ select REGMAP_I2C
+ help
+ Say Y to enable support for the TI BQ25980, BQ25975 and BQ25960
+ series of fast battery chargers.
+
config CHARGER_SMB347
tristate "Summit Microelectronics SMB347 Battery Charger"
depends on I2C
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index b3c694a65114..9b2f21fa6f94 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -84,6 +84,7 @@ 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_BQ25980) += bq25980_charger.o
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o
diff --git a/drivers/power/supply/bq25980_charger.c b/drivers/power/supply/bq25980_charger.c
new file mode 100644
index 000000000000..3995fb7cf060
--- /dev/null
+++ b/drivers/power/supply/bq25980_charger.c
@@ -0,0 +1,1316 @@
+// SPDX-License-Identifier: GPL-2.0
+// BQ25980 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/gpio/consumer.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+
+#include "bq25980_charger.h"
+
+struct bq25980_state {
+ bool dischg;
+ bool ovp;
+ bool ocp;
+ bool wdt;
+ bool tflt;
+ bool online;
+ bool ce;
+ bool hiz;
+ bool bypass;
+
+ u32 vbat_adc;
+ u32 vsys_adc;
+ u32 ibat_adc;
+};
+
+enum bq25980_id {
+ BQ25980,
+ BQ25975,
+ BQ25960,
+};
+
+struct bq25980_chip_info {
+
+ int model_id;
+
+ const struct regmap_config *regmap_config;
+
+ int busocp_def;
+ int busocp_sc_max;
+ int busocp_byp_max;
+ int busocp_sc_min;
+ int busocp_byp_min;
+
+ int busovp_sc_def;
+ int busovp_byp_def;
+ int busovp_sc_step;
+
+ int busovp_sc_offset;
+ int busovp_byp_step;
+ int busovp_byp_offset;
+ int busovp_sc_min;
+ int busovp_sc_max;
+ int busovp_byp_min;
+ int busovp_byp_max;
+
+ int batovp_def;
+ int batovp_max;
+ int batovp_min;
+ int batovp_step;
+ int batovp_offset;
+
+ int batocp_def;
+ int batocp_max;
+};
+
+struct bq25980_init_data {
+ u32 ichg;
+ u32 bypass_ilim;
+ u32 sc_ilim;
+ u32 vreg;
+ u32 iterm;
+ u32 iprechg;
+ u32 bypass_vlim;
+ u32 sc_vlim;
+ u32 ichg_max;
+ u32 vreg_max;
+};
+
+struct bq25980_device {
+ struct i2c_client *client;
+ struct device *dev;
+ struct power_supply *charger;
+ struct power_supply *battery;
+ struct mutex lock;
+ struct regmap *regmap;
+
+ char model_name[I2C_NAME_SIZE];
+
+ struct bq25980_init_data init_data;
+ const struct bq25980_chip_info *chip_info;
+ struct bq25980_state state;
+ int watchdog_timer;
+};
+
+static struct reg_default bq25980_reg_defs[] = {
+ {BQ25980_BATOVP, 0x5A},
+ {BQ25980_BATOVP_ALM, 0x46},
+ {BQ25980_BATOCP, 0x51},
+ {BQ25980_BATOCP_ALM, 0x50},
+ {BQ25980_BATUCP_ALM, 0x28},
+ {BQ25980_CHRGR_CTRL_1, 0x0},
+ {BQ25980_BUSOVP, 0x26},
+ {BQ25980_BUSOVP_ALM, 0x22},
+ {BQ25980_BUSOCP, 0xD},
+ {BQ25980_BUSOCP_ALM, 0xC},
+ {BQ25980_TEMP_CONTROL, 0x30},
+ {BQ25980_TDIE_ALM, 0xC8},
+ {BQ25980_TSBUS_FLT, 0x15},
+ {BQ25980_TSBAT_FLG, 0x15},
+ {BQ25980_VAC_CONTROL, 0x0},
+ {BQ25980_CHRGR_CTRL_2, 0x0},
+ {BQ25980_CHRGR_CTRL_3, 0x20},
+ {BQ25980_CHRGR_CTRL_4, 0x1D},
+ {BQ25980_CHRGR_CTRL_5, 0x18},
+ {BQ25980_STAT1, 0x0},
+ {BQ25980_STAT2, 0x0},
+ {BQ25980_STAT3, 0x0},
+ {BQ25980_STAT4, 0x0},
+ {BQ25980_STAT5, 0x0},
+ {BQ25980_FLAG1, 0x0},
+ {BQ25980_FLAG2, 0x0},
+ {BQ25980_FLAG3, 0x0},
+ {BQ25980_FLAG4, 0x0},
+ {BQ25980_FLAG5, 0x0},
+ {BQ25980_MASK1, 0x0},
+ {BQ25980_MASK2, 0x0},
+ {BQ25980_MASK3, 0x0},
+ {BQ25980_MASK4, 0x0},
+ {BQ25980_MASK5, 0x0},
+ {BQ25980_DEVICE_INFO, 0x8},
+ {BQ25980_ADC_CONTROL1, 0x0},
+ {BQ25980_ADC_CONTROL2, 0x0},
+ {BQ25980_IBUS_ADC_LSB, 0x0},
+ {BQ25980_IBUS_ADC_MSB, 0x0},
+ {BQ25980_VBUS_ADC_LSB, 0x0},
+ {BQ25980_VBUS_ADC_MSB, 0x0},
+ {BQ25980_VAC1_ADC_LSB, 0x0},
+ {BQ25980_VAC2_ADC_LSB, 0x0},
+ {BQ25980_VOUT_ADC_LSB, 0x0},
+ {BQ25980_VBAT_ADC_LSB, 0x0},
+ {BQ25980_IBAT_ADC_MSB, 0x0},
+ {BQ25980_IBAT_ADC_LSB, 0x0},
+ {BQ25980_TSBUS_ADC_LSB, 0x0},
+ {BQ25980_TSBAT_ADC_LSB, 0x0},
+ {BQ25980_TDIE_ADC_LSB, 0x0},
+ {BQ25980_DEGLITCH_TIME, 0x0},
+ {BQ25980_CHRGR_CTRL_6, 0x0},
+};
+
+static struct reg_default bq25975_reg_defs[] = {
+ {BQ25980_BATOVP, 0x5A},
+ {BQ25980_BATOVP_ALM, 0x46},
+ {BQ25980_BATOCP, 0x51},
+ {BQ25980_BATOCP_ALM, 0x50},
+ {BQ25980_BATUCP_ALM, 0x28},
+ {BQ25980_CHRGR_CTRL_1, 0x0},
+ {BQ25980_BUSOVP, 0x26},
+ {BQ25980_BUSOVP_ALM, 0x22},
+ {BQ25980_BUSOCP, 0xD},
+ {BQ25980_BUSOCP_ALM, 0xC},
+ {BQ25980_TEMP_CONTROL, 0x30},
+ {BQ25980_TDIE_ALM, 0xC8},
+ {BQ25980_TSBUS_FLT, 0x15},
+ {BQ25980_TSBAT_FLG, 0x15},
+ {BQ25980_VAC_CONTROL, 0x0},
+ {BQ25980_CHRGR_CTRL_2, 0x0},
+ {BQ25980_CHRGR_CTRL_3, 0x20},
+ {BQ25980_CHRGR_CTRL_4, 0x1D},
+ {BQ25980_CHRGR_CTRL_5, 0x18},
+ {BQ25980_STAT1, 0x0},
+ {BQ25980_STAT2, 0x0},
+ {BQ25980_STAT3, 0x0},
+ {BQ25980_STAT4, 0x0},
+ {BQ25980_STAT5, 0x0},
+ {BQ25980_FLAG1, 0x0},
+ {BQ25980_FLAG2, 0x0},
+ {BQ25980_FLAG3, 0x0},
+ {BQ25980_FLAG4, 0x0},
+ {BQ25980_FLAG5, 0x0},
+ {BQ25980_MASK1, 0x0},
+ {BQ25980_MASK2, 0x0},
+ {BQ25980_MASK3, 0x0},
+ {BQ25980_MASK4, 0x0},
+ {BQ25980_MASK5, 0x0},
+ {BQ25980_DEVICE_INFO, 0x8},
+ {BQ25980_ADC_CONTROL1, 0x0},
+ {BQ25980_ADC_CONTROL2, 0x0},
+ {BQ25980_IBUS_ADC_LSB, 0x0},
+ {BQ25980_IBUS_ADC_MSB, 0x0},
+ {BQ25980_VBUS_ADC_LSB, 0x0},
+ {BQ25980_VBUS_ADC_MSB, 0x0},
+ {BQ25980_VAC1_ADC_LSB, 0x0},
+ {BQ25980_VAC2_ADC_LSB, 0x0},
+ {BQ25980_VOUT_ADC_LSB, 0x0},
+ {BQ25980_VBAT_ADC_LSB, 0x0},
+ {BQ25980_IBAT_ADC_MSB, 0x0},
+ {BQ25980_IBAT_ADC_LSB, 0x0},
+ {BQ25980_TSBUS_ADC_LSB, 0x0},
+ {BQ25980_TSBAT_ADC_LSB, 0x0},
+ {BQ25980_TDIE_ADC_LSB, 0x0},
+ {BQ25980_DEGLITCH_TIME, 0x0},
+ {BQ25980_CHRGR_CTRL_6, 0x0},
+};
+
+static struct reg_default bq25960_reg_defs[] = {
+ {BQ25980_BATOVP, 0x5A},
+ {BQ25980_BATOVP_ALM, 0x46},
+ {BQ25980_BATOCP, 0x51},
+ {BQ25980_BATOCP_ALM, 0x50},
+ {BQ25980_BATUCP_ALM, 0x28},
+ {BQ25980_CHRGR_CTRL_1, 0x0},
+ {BQ25980_BUSOVP, 0x26},
+ {BQ25980_BUSOVP_ALM, 0x22},
+ {BQ25980_BUSOCP, 0xD},
+ {BQ25980_BUSOCP_ALM, 0xC},
+ {BQ25980_TEMP_CONTROL, 0x30},
+ {BQ25980_TDIE_ALM, 0xC8},
+ {BQ25980_TSBUS_FLT, 0x15},
+ {BQ25980_TSBAT_FLG, 0x15},
+ {BQ25980_VAC_CONTROL, 0x0},
+ {BQ25980_CHRGR_CTRL_2, 0x0},
+ {BQ25980_CHRGR_CTRL_3, 0x20},
+ {BQ25980_CHRGR_CTRL_4, 0x1D},
+ {BQ25980_CHRGR_CTRL_5, 0x18},
+ {BQ25980_STAT1, 0x0},
+ {BQ25980_STAT2, 0x0},
+ {BQ25980_STAT3, 0x0},
+ {BQ25980_STAT4, 0x0},
+ {BQ25980_STAT5, 0x0},
+ {BQ25980_FLAG1, 0x0},
+ {BQ25980_FLAG2, 0x0},
+ {BQ25980_FLAG3, 0x0},
+ {BQ25980_FLAG4, 0x0},
+ {BQ25980_FLAG5, 0x0},
+ {BQ25980_MASK1, 0x0},
+ {BQ25980_MASK2, 0x0},
+ {BQ25980_MASK3, 0x0},
+ {BQ25980_MASK4, 0x0},
+ {BQ25980_MASK5, 0x0},
+ {BQ25980_DEVICE_INFO, 0x8},
+ {BQ25980_ADC_CONTROL1, 0x0},
+ {BQ25980_ADC_CONTROL2, 0x0},
+ {BQ25980_IBUS_ADC_LSB, 0x0},
+ {BQ25980_IBUS_ADC_MSB, 0x0},
+ {BQ25980_VBUS_ADC_LSB, 0x0},
+ {BQ25980_VBUS_ADC_MSB, 0x0},
+ {BQ25980_VAC1_ADC_LSB, 0x0},
+ {BQ25980_VAC2_ADC_LSB, 0x0},
+ {BQ25980_VOUT_ADC_LSB, 0x0},
+ {BQ25980_VBAT_ADC_LSB, 0x0},
+ {BQ25980_IBAT_ADC_MSB, 0x0},
+ {BQ25980_IBAT_ADC_LSB, 0x0},
+ {BQ25980_TSBUS_ADC_LSB, 0x0},
+ {BQ25980_TSBAT_ADC_LSB, 0x0},
+ {BQ25980_TDIE_ADC_LSB, 0x0},
+ {BQ25980_DEGLITCH_TIME, 0x0},
+ {BQ25980_CHRGR_CTRL_6, 0x0},
+};
+
+static int bq25980_watchdog_time[BQ25980_NUM_WD_VAL] = {5000, 10000, 50000,
+ 300000};
+
+static int bq25980_get_input_curr_lim(struct bq25980_device *bq)
+{
+ unsigned int busocp_reg_code;
+ int ret;
+
+ ret = regmap_read(bq->regmap, BQ25980_BUSOCP, &busocp_reg_code);
+ if (ret)
+ return ret;
+
+ return (busocp_reg_code * BQ25980_BUSOCP_STEP_uA) + BQ25980_BUSOCP_OFFSET_uA;
+}
+
+static int bq25980_set_hiz(struct bq25980_device *bq, int setting)
+{
+ return regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2,
+ BQ25980_EN_HIZ, setting);
+}
+
+static int bq25980_set_input_curr_lim(struct bq25980_device *bq, int busocp)
+{
+ unsigned int busocp_reg_code;
+ int ret;
+
+ if (!busocp)
+ return bq25980_set_hiz(bq, BQ25980_ENABLE_HIZ);
+
+ bq25980_set_hiz(bq, BQ25980_DISABLE_HIZ);
+
+ if (busocp < BQ25980_BUSOCP_MIN_uA)
+ busocp = BQ25980_BUSOCP_MIN_uA;
+
+ if (bq->state.bypass)
+ busocp = min(busocp, bq->chip_info->busocp_sc_max);
+ else
+ busocp = min(busocp, bq->chip_info->busocp_byp_max);
+
+ busocp_reg_code = (busocp - BQ25980_BUSOCP_OFFSET_uA)
+ / BQ25980_BUSOCP_STEP_uA;
+
+ ret = regmap_write(bq->regmap, BQ25980_BUSOCP, busocp_reg_code);
+ if (ret)
+ return ret;
+
+ return regmap_write(bq->regmap, BQ25980_BUSOCP_ALM, busocp_reg_code);
+}
+
+static int bq25980_get_input_volt_lim(struct bq25980_device *bq)
+{
+ unsigned int busovp_reg_code;
+ unsigned int busovp_offset;
+ unsigned int busovp_step;
+ int ret;
+
+ if (bq->state.bypass) {
+ busovp_step = bq->chip_info->busovp_byp_step;
+ busovp_offset = bq->chip_info->busovp_byp_offset;
+ } else {
+ busovp_step = bq->chip_info->busovp_sc_step;
+ busovp_offset = bq->chip_info->busovp_sc_offset;
+ }
+
+ ret = regmap_read(bq->regmap, BQ25980_BUSOVP, &busovp_reg_code);
+ if (ret)
+ return ret;
+
+ return (busovp_reg_code * busovp_step) + busovp_offset;
+}
+
+static int bq25980_set_input_volt_lim(struct bq25980_device *bq, int busovp)
+{
+ unsigned int busovp_reg_code;
+ unsigned int busovp_step;
+ unsigned int busovp_offset;
+ int ret;
+
+ if (bq->state.bypass) {
+ busovp_step = bq->chip_info->busovp_byp_step;
+ busovp_offset = bq->chip_info->busovp_byp_offset;
+ if (busovp > bq->chip_info->busovp_byp_max)
+ busovp = bq->chip_info->busovp_byp_max;
+ else if (busovp < bq->chip_info->busovp_byp_min)
+ busovp = bq->chip_info->busovp_byp_min;
+ } else {
+ busovp_step = bq->chip_info->busovp_sc_step;
+ busovp_offset = bq->chip_info->busovp_sc_offset;
+ if (busovp > bq->chip_info->busovp_sc_max)
+ busovp = bq->chip_info->busovp_sc_max;
+ else if (busovp < bq->chip_info->busovp_sc_min)
+ busovp = bq->chip_info->busovp_sc_min;
+ }
+
+ busovp_reg_code = (busovp - busovp_offset) / busovp_step;
+
+ ret = regmap_write(bq->regmap, BQ25980_BUSOVP, busovp_reg_code);
+ if (ret)
+ return ret;
+
+ return regmap_write(bq->regmap, BQ25980_BUSOVP_ALM, busovp_reg_code);
+}
+
+static int bq25980_get_const_charge_curr(struct bq25980_device *bq)
+{
+ unsigned int batocp_reg_code;
+ int ret;
+
+ ret = regmap_read(bq->regmap, BQ25980_BATOCP, &batocp_reg_code);
+ if (ret)
+ return ret;
+
+ return (batocp_reg_code & BQ25980_BATOCP_MASK) *
+ BQ25980_BATOCP_STEP_uA;
+}
+
+static int bq25980_set_const_charge_curr(struct bq25980_device *bq, int batocp)
+{
+ unsigned int batocp_reg_code;
+ int ret;
+
+ batocp = max(batocp, BQ25980_BATOCP_MIN_uA);
+ batocp = min(batocp, bq->chip_info->batocp_max);
+
+ batocp_reg_code = batocp / BQ25980_BATOCP_STEP_uA;
+
+ ret = regmap_update_bits(bq->regmap, BQ25980_BATOCP,
+ BQ25980_BATOCP_MASK, batocp_reg_code);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(bq->regmap, BQ25980_BATOCP_ALM,
+ BQ25980_BATOCP_MASK, batocp_reg_code);
+}
+
+static int bq25980_get_const_charge_volt(struct bq25980_device *bq)
+{
+ unsigned int batovp_reg_code;
+ int ret;
+
+ ret = regmap_read(bq->regmap, BQ25980_BATOVP, &batovp_reg_code);
+ if (ret)
+ return ret;
+
+ return ((batovp_reg_code * bq->chip_info->batovp_step) +
+ bq->chip_info->batovp_offset);
+}
+
+static int bq25980_set_const_charge_volt(struct bq25980_device *bq, int batovp)
+{
+ unsigned int batovp_reg_code;
+ int ret;
+
+ if (batovp < bq->chip_info->batovp_min)
+ batovp = bq->chip_info->batovp_min;
+
+ if (batovp > bq->chip_info->batovp_max)
+ batovp = bq->chip_info->batovp_max;
+
+ batovp_reg_code = (batovp - bq->chip_info->batovp_offset) /
+ bq->chip_info->batovp_step;
+
+ ret = regmap_write(bq->regmap, BQ25980_BATOVP, batovp_reg_code);
+ if (ret)
+ return ret;
+
+ return regmap_write(bq->regmap, BQ25980_BATOVP_ALM, batovp_reg_code);
+}
+
+static int bq25980_set_bypass(struct bq25980_device *bq, bool en_bypass)
+{
+ int ret;
+
+ if (en_bypass)
+ ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2,
+ BQ25980_EN_BYPASS, BQ25980_EN_BYPASS);
+ else
+ ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2,
+ BQ25980_EN_BYPASS, en_bypass);
+ if (ret)
+ return ret;
+
+ bq->state.bypass = en_bypass;
+
+ return bq->state.bypass;
+}
+
+static int bq25980_set_chg_en(struct bq25980_device *bq, bool en_chg)
+{
+ int ret;
+
+ if (en_chg)
+ ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2,
+ BQ25980_CHG_EN, BQ25980_CHG_EN);
+ else
+ ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2,
+ BQ25980_CHG_EN, en_chg);
+ if (ret)
+ return ret;
+
+ bq->state.ce = en_chg;
+
+ return 0;
+}
+
+static int bq25980_get_adc_ibus(struct bq25980_device *bq)
+{
+ int ibus_adc_lsb, ibus_adc_msb;
+ u16 ibus_adc;
+ int ret;
+
+ ret = regmap_read(bq->regmap, BQ25980_IBUS_ADC_MSB, &ibus_adc_msb);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(bq->regmap, BQ25980_IBUS_ADC_LSB, &ibus_adc_lsb);
+ if (ret)
+ return ret;
+
+ ibus_adc = (ibus_adc_msb << 8) | ibus_adc_lsb;
+
+ if (ibus_adc_msb & BQ25980_ADC_POLARITY_BIT)
+ return ((ibus_adc ^ 0xffff) + 1) * BQ25980_ADC_CURR_STEP_uA;
+
+ return ibus_adc * BQ25980_ADC_CURR_STEP_uA;
+}
+
+static int bq25980_get_adc_vbus(struct bq25980_device *bq)
+{
+ int vbus_adc_lsb, vbus_adc_msb;
+ u16 vbus_adc;
+ int ret;
+
+ ret = regmap_read(bq->regmap, BQ25980_VBUS_ADC_MSB, &vbus_adc_msb);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(bq->regmap, BQ25980_VBUS_ADC_LSB, &vbus_adc_lsb);
+ if (ret)
+ return ret;
+
+ vbus_adc = (vbus_adc_msb << 8) | vbus_adc_lsb;
+
+ return vbus_adc * BQ25980_ADC_VOLT_STEP_uV;
+}
+
+static int bq25980_get_ibat_adc(struct bq25980_device *bq)
+{
+ int ret;
+ int ibat_adc_lsb, ibat_adc_msb;
+ int ibat_adc;
+
+ ret = regmap_read(bq->regmap, BQ25980_IBAT_ADC_MSB, &ibat_adc_msb);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(bq->regmap, BQ25980_IBAT_ADC_LSB, &ibat_adc_lsb);
+ if (ret)
+ return ret;
+
+ ibat_adc = (ibat_adc_msb << 8) | ibat_adc_lsb;
+
+ if (ibat_adc_msb & BQ25980_ADC_POLARITY_BIT)
+ return ((ibat_adc ^ 0xffff) + 1) * BQ25980_ADC_CURR_STEP_uA;
+
+ return ibat_adc * BQ25980_ADC_CURR_STEP_uA;
+}
+
+static int bq25980_get_adc_vbat(struct bq25980_device *bq)
+{
+ int vsys_adc_lsb, vsys_adc_msb;
+ u16 vsys_adc;
+ int ret;
+
+ ret = regmap_read(bq->regmap, BQ25980_VBAT_ADC_MSB, &vsys_adc_msb);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(bq->regmap, BQ25980_VBAT_ADC_LSB, &vsys_adc_lsb);
+ if (ret)
+ return ret;
+
+ vsys_adc = (vsys_adc_msb << 8) | vsys_adc_lsb;
+
+ return vsys_adc * BQ25980_ADC_VOLT_STEP_uV;
+}
+
+static int bq25980_get_state(struct bq25980_device *bq,
+ struct bq25980_state *state)
+{
+ unsigned int chg_ctrl_2;
+ unsigned int stat1;
+ unsigned int stat2;
+ unsigned int stat3;
+ unsigned int stat4;
+ unsigned int ibat_adc_msb;
+ int ret;
+
+ ret = regmap_read(bq->regmap, BQ25980_STAT1, &stat1);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(bq->regmap, BQ25980_STAT2, &stat2);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(bq->regmap, BQ25980_STAT3, &stat3);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(bq->regmap, BQ25980_STAT4, &stat4);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(bq->regmap, BQ25980_CHRGR_CTRL_2, &chg_ctrl_2);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(bq->regmap, BQ25980_IBAT_ADC_MSB, &ibat_adc_msb);
+ if (ret)
+ return ret;
+
+ state->dischg = ibat_adc_msb & BQ25980_ADC_POLARITY_BIT;
+ state->ovp = (stat1 & BQ25980_STAT1_OVP_MASK) |
+ (stat3 & BQ25980_STAT3_OVP_MASK);
+ state->ocp = (stat1 & BQ25980_STAT1_OCP_MASK) |
+ (stat2 & BQ25980_STAT2_OCP_MASK);
+ state->tflt = stat4 & BQ25980_STAT4_TFLT_MASK;
+ state->wdt = stat4 & BQ25980_WD_STAT;
+ state->online = stat3 & BQ25980_PRESENT_MASK;
+ state->ce = chg_ctrl_2 & BQ25980_CHG_EN;
+ state->hiz = chg_ctrl_2 & BQ25980_EN_HIZ;
+ state->bypass = chg_ctrl_2 & BQ25980_EN_BYPASS;
+
+ return 0;
+}
+
+static int bq25980_set_battery_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct bq25980_device *bq = power_supply_get_drvdata(psy);
+ int ret = 0;
+
+ if (ret)
+ return ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ ret = bq25980_set_const_charge_curr(bq, val->intval);
+ if (ret)
+ return ret;
+ break;
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ ret = bq25980_set_const_charge_volt(bq, val->intval);
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int bq25980_get_battery_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct bq25980_device *bq = power_supply_get_drvdata(psy);
+ int ret = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+ val->intval = bq->init_data.ichg_max;
+ break;
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+ val->intval = bq->init_data.vreg_max;
+ break;
+
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ ret = bq25980_get_ibat_adc(bq);
+ val->intval = ret;
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ ret = bq25980_get_adc_vbat(bq);
+ if (ret < 0)
+ return ret;
+
+ val->intval = ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int bq25980_set_charger_property(struct power_supply *psy,
+ enum power_supply_property prop,
+ const union power_supply_propval *val)
+{
+ struct bq25980_device *bq = power_supply_get_drvdata(psy);
+ int ret = -EINVAL;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ ret = bq25980_set_input_curr_lim(bq, val->intval);
+ if (ret)
+ return ret;
+ break;
+
+ case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
+ ret = bq25980_set_input_volt_lim(bq, val->intval);
+ if (ret)
+ return ret;
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ ret = bq25980_set_bypass(bq, val->intval);
+ if (ret)
+ return ret;
+ break;
+
+ case POWER_SUPPLY_PROP_STATUS:
+ ret = bq25980_set_chg_en(bq, val->intval);
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int bq25980_get_charger_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct bq25980_device *bq = power_supply_get_drvdata(psy);
+ struct bq25980_state state;
+ int ret = 0;
+
+ mutex_lock(&bq->lock);
+ ret = bq25980_get_state(bq, &state);
+ mutex_unlock(&bq->lock);
+ if (ret)
+ return ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = BQ25980_MANUFACTURER;
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = bq->model_name;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = state.online;
+ break;
+
+ case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
+ ret = bq25980_get_input_volt_lim(bq);
+ if (ret < 0)
+ return ret;
+ val->intval = ret;
+ break;
+
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ ret = bq25980_get_input_curr_lim(bq);
+ if (ret < 0)
+ return ret;
+
+ val->intval = ret;
+ break;
+
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+
+ if (state.tflt)
+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ else if (state.ovp)
+ val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ else if (state.ocp)
+ val->intval = POWER_SUPPLY_HEALTH_OVERCURRENT;
+ else if (state.wdt)
+ val->intval =
+ POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE;
+ break;
+
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+
+ if ((state.ce) && (!state.hiz))
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else if (state.dischg)
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ else if (!state.ce)
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+
+ if (!state.ce)
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ else if (state.bypass)
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
+ else if (!state.bypass)
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
+ break;
+
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ ret = bq25980_get_adc_ibus(bq);
+ if (ret < 0)
+ return ret;
+
+ val->intval = ret;
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ ret = bq25980_get_adc_vbus(bq);
+ if (ret < 0)
+ return ret;
+
+ val->intval = ret;
+ break;
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+ ret = bq25980_get_const_charge_curr(bq);
+ if (ret < 0)
+ return ret;
+
+ val->intval = ret;
+ break;
+
+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+ ret = bq25980_get_const_charge_volt(bq);
+ if (ret < 0)
+ return ret;
+
+ val->intval = ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static bool bq25980_state_changed(struct bq25980_device *bq,
+ struct bq25980_state *new_state)
+{
+ struct bq25980_state old_state;
+
+ mutex_lock(&bq->lock);
+ old_state = bq->state;
+ mutex_unlock(&bq->lock);
+
+ return (old_state.dischg != new_state->dischg ||
+ old_state.ovp != new_state->ovp ||
+ old_state.ocp != new_state->ocp ||
+ old_state.online != new_state->online ||
+ old_state.wdt != new_state->wdt ||
+ old_state.tflt != new_state->tflt ||
+ old_state.ce != new_state->ce ||
+ old_state.hiz != new_state->hiz ||
+ old_state.bypass != new_state->bypass);
+}
+
+static irqreturn_t bq25980_irq_handler_thread(int irq, void *private)
+{
+ struct bq25980_device *bq = private;
+ struct bq25980_state state;
+ int ret;
+
+ ret = bq25980_get_state(bq, &state);
+ if (ret < 0)
+ goto irq_out;
+
+ if (!bq25980_state_changed(bq, &state))
+ goto irq_out;
+
+ mutex_lock(&bq->lock);
+ bq->state = state;
+ mutex_unlock(&bq->lock);
+
+ power_supply_changed(bq->charger);
+
+irq_out:
+ return IRQ_HANDLED;
+}
+
+static enum power_supply_property bq25980_power_supply_props[] = {
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+static enum power_supply_property bq25980_battery_props[] = {
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+static char *bq25980_charger_supplied_to[] = {
+ "main-battery",
+};
+
+static int bq25980_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_CHARGE_TYPE:
+ case POWER_SUPPLY_PROP_STATUS:
+ case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct power_supply_desc bq25980_power_supply_desc = {
+ .name = "bq25980-charger",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .properties = bq25980_power_supply_props,
+ .num_properties = ARRAY_SIZE(bq25980_power_supply_props),
+ .get_property = bq25980_get_charger_property,
+ .set_property = bq25980_set_charger_property,
+ .property_is_writeable = bq25980_property_is_writeable,
+};
+
+static struct power_supply_desc bq25980_battery_desc = {
+ .name = "bq25980-battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .get_property = bq25980_get_battery_property,
+ .set_property = bq25980_set_battery_property,
+ .properties = bq25980_battery_props,
+ .num_properties = ARRAY_SIZE(bq25980_battery_props),
+ .property_is_writeable = bq25980_property_is_writeable,
+};
+
+
+static bool bq25980_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BQ25980_CHRGR_CTRL_2:
+ case BQ25980_STAT1...BQ25980_FLAG5:
+ case BQ25980_ADC_CONTROL1...BQ25980_TDIE_ADC_LSB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config bq25980_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = BQ25980_CHRGR_CTRL_6,
+ .reg_defaults = bq25980_reg_defs,
+ .num_reg_defaults = ARRAY_SIZE(bq25980_reg_defs),
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = bq25980_is_volatile_reg,
+};
+
+static const struct regmap_config bq25975_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = BQ25980_CHRGR_CTRL_6,
+ .reg_defaults = bq25975_reg_defs,
+ .num_reg_defaults = ARRAY_SIZE(bq25975_reg_defs),
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = bq25980_is_volatile_reg,
+};
+
+static const struct regmap_config bq25960_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = BQ25980_CHRGR_CTRL_6,
+ .reg_defaults = bq25960_reg_defs,
+ .num_reg_defaults = ARRAY_SIZE(bq25960_reg_defs),
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = bq25980_is_volatile_reg,
+};
+
+static const struct bq25980_chip_info bq25980_chip_info_tbl[] = {
+ [BQ25980] = {
+ .model_id = BQ25980,
+ .regmap_config = &bq25980_regmap_config,
+
+ .busocp_def = BQ25980_BUSOCP_DFLT_uA,
+ .busocp_sc_min = BQ25960_BUSOCP_SC_MAX_uA,
+ .busocp_sc_max = BQ25980_BUSOCP_SC_MAX_uA,
+ .busocp_byp_max = BQ25980_BUSOCP_BYP_MAX_uA,
+ .busocp_byp_min = BQ25980_BUSOCP_MIN_uA,
+
+ .busovp_sc_def = BQ25980_BUSOVP_DFLT_uV,
+ .busovp_byp_def = BQ25980_BUSOVP_BYPASS_DFLT_uV,
+ .busovp_sc_step = BQ25980_BUSOVP_SC_STEP_uV,
+ .busovp_sc_offset = BQ25980_BUSOVP_SC_OFFSET_uV,
+ .busovp_byp_step = BQ25980_BUSOVP_BYP_STEP_uV,
+ .busovp_byp_offset = BQ25980_BUSOVP_BYP_OFFSET_uV,
+ .busovp_sc_min = BQ25980_BUSOVP_SC_MIN_uV,
+ .busovp_sc_max = BQ25980_BUSOVP_SC_MAX_uV,
+ .busovp_byp_min = BQ25980_BUSOVP_BYP_MIN_uV,
+ .busovp_byp_max = BQ25980_BUSOVP_BYP_MAX_uV,
+
+ .batovp_def = BQ25980_BATOVP_DFLT_uV,
+ .batovp_max = BQ25980_BATOVP_MAX_uV,
+ .batovp_min = BQ25980_BATOVP_MIN_uV,
+ .batovp_step = BQ25980_BATOVP_STEP_uV,
+ .batovp_offset = BQ25980_BATOVP_OFFSET_uV,
+
+ .batocp_def = BQ25980_BATOCP_DFLT_uA,
+ .batocp_max = BQ25980_BATOCP_MAX_uA,
+ },
+
+ [BQ25975] = {
+ .model_id = BQ25975,
+ .regmap_config = &bq25975_regmap_config,
+
+ .busocp_def = BQ25975_BUSOCP_DFLT_uA,
+ .busocp_sc_min = BQ25975_BUSOCP_SC_MAX_uA,
+ .busocp_sc_max = BQ25975_BUSOCP_SC_MAX_uA,
+ .busocp_byp_min = BQ25980_BUSOCP_MIN_uA,
+ .busocp_byp_max = BQ25975_BUSOCP_BYP_MAX_uA,
+
+ .busovp_sc_def = BQ25975_BUSOVP_DFLT_uV,
+ .busovp_byp_def = BQ25975_BUSOVP_BYPASS_DFLT_uV,
+ .busovp_sc_step = BQ25975_BUSOVP_SC_STEP_uV,
+ .busovp_sc_offset = BQ25975_BUSOVP_SC_OFFSET_uV,
+ .busovp_byp_step = BQ25975_BUSOVP_BYP_STEP_uV,
+ .busovp_byp_offset = BQ25975_BUSOVP_BYP_OFFSET_uV,
+ .busovp_sc_min = BQ25975_BUSOVP_SC_MIN_uV,
+ .busovp_sc_max = BQ25975_BUSOVP_SC_MAX_uV,
+ .busovp_byp_min = BQ25975_BUSOVP_BYP_MIN_uV,
+ .busovp_byp_max = BQ25975_BUSOVP_BYP_MAX_uV,
+
+ .batovp_def = BQ25975_BATOVP_DFLT_uV,
+ .batovp_max = BQ25975_BATOVP_MAX_uV,
+ .batovp_min = BQ25975_BATOVP_MIN_uV,
+ .batovp_step = BQ25975_BATOVP_STEP_uV,
+ .batovp_offset = BQ25975_BATOVP_OFFSET_uV,
+
+ .batocp_def = BQ25980_BATOCP_DFLT_uA,
+ .batocp_max = BQ25980_BATOCP_MAX_uA,
+ },
+
+ [BQ25960] = {
+ .model_id = BQ25960,
+ .regmap_config = &bq25960_regmap_config,
+
+ .busocp_def = BQ25960_BUSOCP_DFLT_uA,
+ .busocp_sc_min = BQ25960_BUSOCP_SC_MAX_uA,
+ .busocp_sc_max = BQ25960_BUSOCP_SC_MAX_uA,
+ .busocp_byp_min = BQ25960_BUSOCP_SC_MAX_uA,
+ .busocp_byp_max = BQ25960_BUSOCP_BYP_MAX_uA,
+
+ .busovp_sc_def = BQ25975_BUSOVP_DFLT_uV,
+ .busovp_byp_def = BQ25975_BUSOVP_BYPASS_DFLT_uV,
+ .busovp_sc_step = BQ25960_BUSOVP_SC_STEP_uV,
+ .busovp_sc_offset = BQ25960_BUSOVP_SC_OFFSET_uV,
+ .busovp_byp_step = BQ25960_BUSOVP_BYP_STEP_uV,
+ .busovp_byp_offset = BQ25960_BUSOVP_BYP_OFFSET_uV,
+ .busovp_sc_min = BQ25960_BUSOVP_SC_MIN_uV,
+ .busovp_sc_max = BQ25960_BUSOVP_SC_MAX_uV,
+ .busovp_byp_min = BQ25960_BUSOVP_BYP_MIN_uV,
+ .busovp_byp_max = BQ25960_BUSOVP_BYP_MAX_uV,
+
+ .batovp_def = BQ25960_BATOVP_DFLT_uV,
+ .batovp_max = BQ25960_BATOVP_MAX_uV,
+ .batovp_min = BQ25960_BATOVP_MIN_uV,
+ .batovp_step = BQ25960_BATOVP_STEP_uV,
+ .batovp_offset = BQ25960_BATOVP_OFFSET_uV,
+
+ .batocp_def = BQ25960_BATOCP_DFLT_uA,
+ .batocp_max = BQ25960_BATOCP_MAX_uA,
+ },
+};
+
+static int bq25980_power_supply_init(struct bq25980_device *bq,
+ struct device *dev)
+{
+ struct power_supply_config psy_cfg = { .drv_data = bq,
+ .of_node = dev->of_node, };
+
+ psy_cfg.supplied_to = bq25980_charger_supplied_to;
+ psy_cfg.num_supplicants = ARRAY_SIZE(bq25980_charger_supplied_to);
+
+ bq->charger = devm_power_supply_register(bq->dev,
+ &bq25980_power_supply_desc,
+ &psy_cfg);
+ if (IS_ERR(bq->charger))
+ return -EINVAL;
+
+ bq->battery = devm_power_supply_register(bq->dev,
+ &bq25980_battery_desc,
+ &psy_cfg);
+ if (IS_ERR(bq->battery))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int bq25980_hw_init(struct bq25980_device *bq)
+{
+ struct power_supply_battery_info bat_info = { };
+ int wd_reg_val;
+ int ret = 0;
+ int curr_val;
+ int volt_val;
+ int i;
+
+ if (!bq->watchdog_timer) {
+ ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_3,
+ BQ25980_WATCHDOG_DIS,
+ BQ25980_WATCHDOG_DIS);
+ } else {
+ for (i = 0; i < BQ25980_NUM_WD_VAL; i++) {
+ if (bq->watchdog_timer > bq25980_watchdog_time[i] &&
+ bq->watchdog_timer < bq25980_watchdog_time[i + 1]) {
+ wd_reg_val = i;
+ break;
+ }
+ }
+
+ ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_3,
+ BQ25980_WATCHDOG_MASK, wd_reg_val);
+ }
+ if (ret)
+ return ret;
+
+ ret = power_supply_get_battery_info(bq->charger, &bat_info);
+ if (ret) {
+ dev_warn(bq->dev, "battery info missing\n");
+ return -EINVAL;
+ }
+
+ bq->init_data.ichg_max = bat_info.constant_charge_current_max_ua;
+ bq->init_data.vreg_max = bat_info.constant_charge_voltage_max_uv;
+
+ if (bq->state.bypass) {
+ ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2,
+ BQ25980_EN_BYPASS, BQ25980_EN_BYPASS);
+ if (ret)
+ return ret;
+
+ curr_val = bq->init_data.bypass_ilim;
+ volt_val = bq->init_data.bypass_vlim;
+ } else {
+ curr_val = bq->init_data.sc_ilim;
+ volt_val = bq->init_data.sc_vlim;
+ }
+
+ ret = bq25980_set_input_curr_lim(bq, curr_val);
+ if (ret)
+ return ret;
+
+ ret = bq25980_set_input_volt_lim(bq, volt_val);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(bq->regmap, BQ25980_ADC_CONTROL1,
+ BQ25980_ADC_EN, BQ25980_ADC_EN);
+}
+
+static int bq25980_parse_dt(struct bq25980_device *bq)
+{
+ int ret;
+
+ ret = device_property_read_u32(bq->dev, "ti,watchdog-timeout-ms",
+ &bq->watchdog_timer);
+ if (ret)
+ bq->watchdog_timer = BQ25980_WATCHDOG_MIN;
+
+ if (bq->watchdog_timer > BQ25980_WATCHDOG_MAX ||
+ bq->watchdog_timer < BQ25980_WATCHDOG_MIN)
+ return -EINVAL;
+
+ ret = device_property_read_u32(bq->dev,
+ "ti,sc-ovp-limit-microvolt",
+ &bq->init_data.sc_vlim);
+ if (ret)
+ bq->init_data.sc_vlim = bq->chip_info->busovp_sc_def;
+
+ if (bq->init_data.sc_vlim > bq->chip_info->busovp_sc_max ||
+ bq->init_data.sc_vlim < bq->chip_info->busovp_sc_min) {
+ dev_err(bq->dev, "SC ovp limit is out of range\n");
+ return -EINVAL;
+ }
+
+ ret = device_property_read_u32(bq->dev,
+ "ti,sc-ocp-limit-microamp",
+ &bq->init_data.sc_ilim);
+ if (ret)
+ bq->init_data.sc_ilim = bq->chip_info->busocp_def;
+
+ if (bq->init_data.sc_ilim > bq->chip_info->busocp_sc_max ||
+ bq->init_data.sc_ilim < bq->chip_info->busocp_sc_min) {
+ dev_err(bq->dev, "SC ocp limit is out of range\n");
+ return -EINVAL;
+ }
+
+ ret = device_property_read_u32(bq->dev,
+ "ti,bypass-ovp-limit-microvolt",
+ &bq->init_data.bypass_vlim);
+ if (ret)
+ bq->init_data.bypass_vlim = bq->chip_info->busovp_byp_def;
+
+ if (bq->init_data.bypass_vlim > bq->chip_info->busovp_byp_max ||
+ bq->init_data.bypass_vlim < bq->chip_info->busovp_byp_min) {
+ dev_err(bq->dev, "Bypass ovp limit is out of range\n");
+ return -EINVAL;
+ }
+
+ ret = device_property_read_u32(bq->dev,
+ "ti,bypass-ocp-limit-microamp",
+ &bq->init_data.bypass_ilim);
+ if (ret)
+ bq->init_data.bypass_ilim = bq->chip_info->busocp_def;
+
+ if (bq->init_data.bypass_ilim > bq->chip_info->busocp_byp_max ||
+ bq->init_data.bypass_ilim < bq->chip_info->busocp_byp_min) {
+ dev_err(bq->dev, "Bypass ocp limit is out of range\n");
+ return -EINVAL;
+ }
+
+
+ bq->state.bypass = device_property_read_bool(bq->dev,
+ "ti,bypass-enable");
+ return 0;
+}
+
+static int bq25980_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct bq25980_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->chip_info = &bq25980_chip_info_tbl[id->driver_data];
+
+ bq->regmap = devm_regmap_init_i2c(client,
+ bq->chip_info->regmap_config);
+ 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 = bq25980_parse_dt(bq);
+ if (ret) {
+ dev_err(dev, "Failed to read device tree properties%d\n", ret);
+ return ret;
+ }
+
+ if (client->irq) {
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ bq25980_irq_handler_thread,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ dev_name(&client->dev), bq);
+ if (ret)
+ return ret;
+ }
+
+ ret = bq25980_power_supply_init(bq, dev);
+ if (ret) {
+ dev_err(dev, "Failed to register power supply\n");
+ return ret;
+ }
+
+ ret = bq25980_hw_init(bq);
+ if (ret) {
+ dev_err(dev, "Cannot initialize the chip.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id bq25980_i2c_ids[] = {
+ { "bq25980", BQ25980 },
+ { "bq25975", BQ25975 },
+ { "bq25975", BQ25975 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, bq25980_i2c_ids);
+
+static const struct of_device_id bq25980_of_match[] = {
+ { .compatible = "ti,bq25980", .data = (void *)BQ25980 },
+ { .compatible = "ti,bq25975", .data = (void *)BQ25975 },
+ { .compatible = "ti,bq25960", .data = (void *)BQ25960 },
+ { },
+};
+MODULE_DEVICE_TABLE(of, bq25980_of_match);
+
+static struct i2c_driver bq25980_driver = {
+ .driver = {
+ .name = "bq25980-charger",
+ .of_match_table = bq25980_of_match,
+ },
+ .probe = bq25980_probe,
+ .id_table = bq25980_i2c_ids,
+};
+module_i2c_driver(bq25980_driver);
+
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_AUTHOR("Ricardo Rivera-Matos <r-rivera-matos@ti.com>");
+MODULE_DESCRIPTION("bq25980 charger driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/supply/bq25980_charger.h b/drivers/power/supply/bq25980_charger.h
new file mode 100644
index 000000000000..39f94eba5f6c
--- /dev/null
+++ b/drivers/power/supply/bq25980_charger.h
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ */
+
+#ifndef BQ25980_CHARGER_H
+#define BQ25980_CHARGER_H
+
+#define BQ25980_MANUFACTURER "Texas Instruments"
+
+#define BQ25980_BATOVP 0x0
+#define BQ25980_BATOVP_ALM 0x1
+#define BQ25980_BATOCP 0x2
+#define BQ25980_BATOCP_ALM 0x3
+#define BQ25980_BATUCP_ALM 0x4
+#define BQ25980_CHRGR_CTRL_1 0x5
+#define BQ25980_BUSOVP 0x6
+#define BQ25980_BUSOVP_ALM 0x7
+#define BQ25980_BUSOCP 0x8
+#define BQ25980_BUSOCP_ALM 0x9
+#define BQ25980_TEMP_CONTROL 0xA
+#define BQ25980_TDIE_ALM 0xB
+#define BQ25980_TSBUS_FLT 0xC
+#define BQ25980_TSBAT_FLG 0xD
+#define BQ25980_VAC_CONTROL 0xE
+#define BQ25980_CHRGR_CTRL_2 0xF
+#define BQ25980_CHRGR_CTRL_3 0x10
+#define BQ25980_CHRGR_CTRL_4 0x11
+#define BQ25980_CHRGR_CTRL_5 0x12
+#define BQ25980_STAT1 0x13
+#define BQ25980_STAT2 0x14
+#define BQ25980_STAT3 0x15
+#define BQ25980_STAT4 0x16
+#define BQ25980_STAT5 0x17
+#define BQ25980_FLAG1 0x18
+#define BQ25980_FLAG2 0x19
+#define BQ25980_FLAG3 0x1A
+#define BQ25980_FLAG4 0x1B
+#define BQ25980_FLAG5 0x1C
+#define BQ25980_MASK1 0x1D
+#define BQ25980_MASK2 0x1E
+#define BQ25980_MASK3 0x1F
+#define BQ25980_MASK4 0x20
+#define BQ25980_MASK5 0x21
+#define BQ25980_DEVICE_INFO 0x22
+#define BQ25980_ADC_CONTROL1 0x23
+#define BQ25980_ADC_CONTROL2 0x24
+#define BQ25980_IBUS_ADC_MSB 0x25
+#define BQ25980_IBUS_ADC_LSB 0x26
+#define BQ25980_VBUS_ADC_MSB 0x27
+#define BQ25980_VBUS_ADC_LSB 0x28
+#define BQ25980_VAC1_ADC_MSB 0x29
+#define BQ25980_VAC1_ADC_LSB 0x2A
+#define BQ25980_VAC2_ADC_MSB 0x2B
+#define BQ25980_VAC2_ADC_LSB 0x2C
+#define BQ25980_VOUT_ADC_MSB 0x2D
+#define BQ25980_VOUT_ADC_LSB 0x2E
+#define BQ25980_VBAT_ADC_MSB 0x2F
+#define BQ25980_VBAT_ADC_LSB 0x30
+#define BQ25980_IBAT_ADC_MSB 0x31
+#define BQ25980_IBAT_ADC_LSB 0x32
+#define BQ25980_TSBUS_ADC_MSB 0x33
+#define BQ25980_TSBUS_ADC_LSB 0x34
+#define BQ25980_TSBAT_ADC_MSB 0x35
+#define BQ25980_TSBAT_ADC_LSB 0x36
+#define BQ25980_TDIE_ADC_MSB 0x37
+#define BQ25980_TDIE_ADC_LSB 0x38
+#define BQ25980_DEGLITCH_TIME 0x39
+#define BQ25980_CHRGR_CTRL_6 0x3A
+
+#define BQ25980_BUSOCP_STEP_uA 250000
+#define BQ25980_BUSOCP_OFFSET_uA 1000000
+
+#define BQ25980_BUSOCP_DFLT_uA 4250000
+#define BQ25975_BUSOCP_DFLT_uA 4250000
+#define BQ25960_BUSOCP_DFLT_uA 3250000
+
+#define BQ25980_BUSOCP_MIN_uA 1000000
+
+#define BQ25980_BUSOCP_SC_MAX_uA 5750000
+#define BQ25975_BUSOCP_SC_MAX_uA 5750000
+#define BQ25960_BUSOCP_SC_MAX_uA 3750000
+
+#define BQ25980_BUSOCP_BYP_MAX_uA 8500000
+#define BQ25975_BUSOCP_BYP_MAX_uA 8500000
+#define BQ25960_BUSOCP_BYP_MAX_uA 5750000
+
+#define BQ25980_BUSOVP_SC_STEP_uV 100000
+#define BQ25975_BUSOVP_SC_STEP_uV 50000
+#define BQ25960_BUSOVP_SC_STEP_uV 50000
+#define BQ25980_BUSOVP_SC_OFFSET_uV 14000000
+#define BQ25975_BUSOVP_SC_OFFSET_uV 7000000
+#define BQ25960_BUSOVP_SC_OFFSET_uV 7000000
+
+#define BQ25980_BUSOVP_BYP_STEP_uV 50000
+#define BQ25975_BUSOVP_BYP_STEP_uV 25000
+#define BQ25960_BUSOVP_BYP_STEP_uV 25000
+#define BQ25980_BUSOVP_BYP_OFFSET_uV 7000000
+#define BQ25975_BUSOVP_BYP_OFFSET_uV 3500000
+#define BQ25960_BUSOVP_BYP_OFFSET_uV 3500000
+
+#define BQ25980_BUSOVP_DFLT_uV 17800000
+#define BQ25980_BUSOVP_BYPASS_DFLT_uV 8900000
+#define BQ25975_BUSOVP_DFLT_uV 8900000
+#define BQ25975_BUSOVP_BYPASS_DFLT_uV 4450000
+#define BQ25960_BUSOVP_DFLT_uV 8900000
+
+#define BQ25980_BUSOVP_SC_MIN_uV 14000000
+#define BQ25975_BUSOVP_SC_MIN_uV 7000000
+#define BQ25960_BUSOVP_SC_MIN_uV 7000000
+#define BQ25980_BUSOVP_BYP_MIN_uV 7000000
+#define BQ25975_BUSOVP_BYP_MIN_uV 3500000
+#define BQ25960_BUSOVP_BYP_MIN_uV 3500000
+
+#define BQ25980_BUSOVP_SC_MAX_uV 22000000
+#define BQ25975_BUSOVP_SC_MAX_uV 12750000
+#define BQ25960_BUSOVP_SC_MAX_uV 12750000
+
+#define BQ25980_BUSOVP_BYP_MAX_uV 12750000
+#define BQ25975_BUSOVP_BYP_MAX_uV 6500000
+#define BQ25960_BUSOVP_BYP_MAX_uV 6500000
+
+#define BQ25980_BATOVP_STEP_uV 20000
+#define BQ25975_BATOVP_STEP_uV 10000
+#define BQ25960_BATOVP_STEP_uV 10000
+
+#define BQ25980_BATOVP_OFFSET_uV 7000000
+#define BQ25975_BATOVP_OFFSET_uV 3500000
+#define BQ25960_BATOVP_OFFSET_uV 3500000
+
+#define BQ25980_BATOVP_DFLT_uV 14000000
+#define BQ25975_BATOVP_DFLT_uV 8900000
+#define BQ25960_BATOVP_DFLT_uV 8900000
+
+#define BQ25980_BATOVP_MIN_uV 7000000
+#define BQ25975_BATOVP_MIN_uV 3500000
+#define BQ25960_BATOVP_MIN_uV 3500000
+
+#define BQ25980_BATOVP_MAX_uV 9540000
+#define BQ25975_BATOVP_MAX_uV 4770000
+#define BQ25960_BATOVP_MAX_uV 4770000
+
+#define BQ25980_BATOCP_STEP_uA 100000
+
+#define BQ25980_BATOCP_MASK GENMASK(6, 0)
+
+#define BQ25980_BATOCP_DFLT_uA 8100000
+#define BQ25960_BATOCP_DFLT_uA 6100000
+
+#define BQ25980_BATOCP_MIN_uA 2000000
+
+#define BQ25980_BATOCP_MAX_uA 11000000
+#define BQ25975_BATOCP_MAX_uA 11000000
+#define BQ25960_BATOCP_MAX_uA 7000000
+
+#define BQ25980_ENABLE_HIZ 0xff
+#define BQ25980_DISABLE_HIZ 0x0
+#define BQ25980_EN_BYPASS BIT(3)
+#define BQ25980_STAT1_OVP_MASK (BIT(6) | BIT(5) | BIT(0))
+#define BQ25980_STAT3_OVP_MASK (BIT(7) | BIT(6))
+#define BQ25980_STAT1_OCP_MASK BIT(3)
+#define BQ25980_STAT2_OCP_MASK (BIT(6) | BIT(1))
+#define BQ25980_STAT4_TFLT_MASK GENMASK(5, 1)
+#define BQ25980_WD_STAT BIT(0)
+#define BQ25980_PRESENT_MASK GENMASK(4, 2)
+#define BQ25980_CHG_EN BIT(4)
+#define BQ25980_EN_HIZ BIT(6)
+#define BQ25980_ADC_EN BIT(7)
+
+#define BQ25980_ADC_VOLT_STEP_uV 1000
+#define BQ25980_ADC_CURR_STEP_uA 1000
+#define BQ25980_ADC_POLARITY_BIT BIT(7)
+
+#define BQ25980_WATCHDOG_MASK GENMASK(4, 3)
+#define BQ25980_WATCHDOG_DIS BIT(2)
+#define BQ25980_WATCHDOG_MAX 300000
+#define BQ25980_WATCHDOG_MIN 0
+#define BQ25980_NUM_WD_VAL 4
+
+#endif /* BQ25980_CHARGER_H */
--
2.28.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 1/2] dt-binding: bq25980: Add the bq25980 flash charger
2020-08-31 16:48 [PATCH 1/2] dt-binding: bq25980: Add the bq25980 flash charger Dan Murphy
2020-08-31 16:48 ` [PATCH 2/2] power: bq25980: Add support for the BQ259xx family Dan Murphy
@ 2020-09-14 18:26 ` Rob Herring
1 sibling, 0 replies; 5+ messages in thread
From: Rob Herring @ 2020-09-14 18:26 UTC (permalink / raw)
To: Dan Murphy; +Cc: sre, r-rivera-matos, devicetree, linux-kernel, linux-pm
On Mon, 31 Aug 2020 11:48:48 -0500, Dan Murphy wrote:
> Add yaml for the bq25980 flash charger.
>
> Signed-off-by: Dan Murphy <dmurphy@ti.com>
> ---
> .../bindings/power/supply/bq25980.yaml | 114 ++++++++++++++++++
> 1 file changed, 114 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/power/supply/bq25980.yaml
>
Reviewed-by: Rob Herring <robh@kernel.org>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] power: bq25980: Add support for the BQ259xx family
2020-08-31 16:48 ` [PATCH 2/2] power: bq25980: Add support for the BQ259xx family Dan Murphy
@ 2020-09-22 16:06 ` Dan Murphy
2020-10-04 22:47 ` Sebastian Reichel
1 sibling, 0 replies; 5+ messages in thread
From: Dan Murphy @ 2020-09-22 16:06 UTC (permalink / raw)
To: sre, robh; +Cc: devicetree, r-rivera-matos, linux-pm, linux-kernel
Hello
On 8/31/20 11:48 AM, Dan Murphy wrote:
> Add support for the BQ25980, BQ25975 and BQ25960 family of flash
> chargers.
Gentle Bump
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] power: bq25980: Add support for the BQ259xx family
2020-08-31 16:48 ` [PATCH 2/2] power: bq25980: Add support for the BQ259xx family Dan Murphy
2020-09-22 16:06 ` Dan Murphy
@ 2020-10-04 22:47 ` Sebastian Reichel
1 sibling, 0 replies; 5+ messages in thread
From: Sebastian Reichel @ 2020-10-04 22:47 UTC (permalink / raw)
To: Dan Murphy; +Cc: robh, devicetree, r-rivera-matos, linux-pm, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 47006 bytes --]
Hi,
On Mon, Aug 31, 2020 at 11:48:49AM -0500, Dan Murphy wrote:
> Add support for the BQ25980, BQ25975 and BQ25960 family of flash
> chargers.
>
> Signed-off-by: Dan Murphy <dmurphy@ti.com>
> ---
Thanks, queued (together with the binding)
-- Sebastian
> drivers/power/supply/Kconfig | 9 +
> drivers/power/supply/Makefile | 1 +
> drivers/power/supply/bq25980_charger.c | 1316 ++++++++++++++++++++++++
> drivers/power/supply/bq25980_charger.h | 178 ++++
> 4 files changed, 1504 insertions(+)
> create mode 100644 drivers/power/supply/bq25980_charger.c
> create mode 100644 drivers/power/supply/bq25980_charger.h
>
> diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
> index faf2830aa152..fbe974e4cff4 100644
> --- a/drivers/power/supply/Kconfig
> +++ b/drivers/power/supply/Kconfig
> @@ -631,6 +631,15 @@ config CHARGER_BQ25890
> help
> Say Y to enable support for the TI BQ25890 battery charger.
>
> +config CHARGER_BQ25980
> + tristate "TI BQ25980 battery charger driver"
> + depends on I2C
> + depends on GPIOLIB || COMPILE_TEST
> + select REGMAP_I2C
> + help
> + Say Y to enable support for the TI BQ25980, BQ25975 and BQ25960
> + series of fast battery chargers.
> +
> config CHARGER_SMB347
> tristate "Summit Microelectronics SMB347 Battery Charger"
> depends on I2C
> diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
> index b3c694a65114..9b2f21fa6f94 100644
> --- a/drivers/power/supply/Makefile
> +++ b/drivers/power/supply/Makefile
> @@ -84,6 +84,7 @@ 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_BQ25980) += bq25980_charger.o
> obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
> obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
> obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o
> diff --git a/drivers/power/supply/bq25980_charger.c b/drivers/power/supply/bq25980_charger.c
> new file mode 100644
> index 000000000000..3995fb7cf060
> --- /dev/null
> +++ b/drivers/power/supply/bq25980_charger.c
> @@ -0,0 +1,1316 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// BQ25980 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/gpio/consumer.h>
> +#include <linux/power_supply.h>
> +#include <linux/regmap.h>
> +#include <linux/types.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/moduleparam.h>
> +#include <linux/slab.h>
> +
> +#include "bq25980_charger.h"
> +
> +struct bq25980_state {
> + bool dischg;
> + bool ovp;
> + bool ocp;
> + bool wdt;
> + bool tflt;
> + bool online;
> + bool ce;
> + bool hiz;
> + bool bypass;
> +
> + u32 vbat_adc;
> + u32 vsys_adc;
> + u32 ibat_adc;
> +};
> +
> +enum bq25980_id {
> + BQ25980,
> + BQ25975,
> + BQ25960,
> +};
> +
> +struct bq25980_chip_info {
> +
> + int model_id;
> +
> + const struct regmap_config *regmap_config;
> +
> + int busocp_def;
> + int busocp_sc_max;
> + int busocp_byp_max;
> + int busocp_sc_min;
> + int busocp_byp_min;
> +
> + int busovp_sc_def;
> + int busovp_byp_def;
> + int busovp_sc_step;
> +
> + int busovp_sc_offset;
> + int busovp_byp_step;
> + int busovp_byp_offset;
> + int busovp_sc_min;
> + int busovp_sc_max;
> + int busovp_byp_min;
> + int busovp_byp_max;
> +
> + int batovp_def;
> + int batovp_max;
> + int batovp_min;
> + int batovp_step;
> + int batovp_offset;
> +
> + int batocp_def;
> + int batocp_max;
> +};
> +
> +struct bq25980_init_data {
> + u32 ichg;
> + u32 bypass_ilim;
> + u32 sc_ilim;
> + u32 vreg;
> + u32 iterm;
> + u32 iprechg;
> + u32 bypass_vlim;
> + u32 sc_vlim;
> + u32 ichg_max;
> + u32 vreg_max;
> +};
> +
> +struct bq25980_device {
> + struct i2c_client *client;
> + struct device *dev;
> + struct power_supply *charger;
> + struct power_supply *battery;
> + struct mutex lock;
> + struct regmap *regmap;
> +
> + char model_name[I2C_NAME_SIZE];
> +
> + struct bq25980_init_data init_data;
> + const struct bq25980_chip_info *chip_info;
> + struct bq25980_state state;
> + int watchdog_timer;
> +};
> +
> +static struct reg_default bq25980_reg_defs[] = {
> + {BQ25980_BATOVP, 0x5A},
> + {BQ25980_BATOVP_ALM, 0x46},
> + {BQ25980_BATOCP, 0x51},
> + {BQ25980_BATOCP_ALM, 0x50},
> + {BQ25980_BATUCP_ALM, 0x28},
> + {BQ25980_CHRGR_CTRL_1, 0x0},
> + {BQ25980_BUSOVP, 0x26},
> + {BQ25980_BUSOVP_ALM, 0x22},
> + {BQ25980_BUSOCP, 0xD},
> + {BQ25980_BUSOCP_ALM, 0xC},
> + {BQ25980_TEMP_CONTROL, 0x30},
> + {BQ25980_TDIE_ALM, 0xC8},
> + {BQ25980_TSBUS_FLT, 0x15},
> + {BQ25980_TSBAT_FLG, 0x15},
> + {BQ25980_VAC_CONTROL, 0x0},
> + {BQ25980_CHRGR_CTRL_2, 0x0},
> + {BQ25980_CHRGR_CTRL_3, 0x20},
> + {BQ25980_CHRGR_CTRL_4, 0x1D},
> + {BQ25980_CHRGR_CTRL_5, 0x18},
> + {BQ25980_STAT1, 0x0},
> + {BQ25980_STAT2, 0x0},
> + {BQ25980_STAT3, 0x0},
> + {BQ25980_STAT4, 0x0},
> + {BQ25980_STAT5, 0x0},
> + {BQ25980_FLAG1, 0x0},
> + {BQ25980_FLAG2, 0x0},
> + {BQ25980_FLAG3, 0x0},
> + {BQ25980_FLAG4, 0x0},
> + {BQ25980_FLAG5, 0x0},
> + {BQ25980_MASK1, 0x0},
> + {BQ25980_MASK2, 0x0},
> + {BQ25980_MASK3, 0x0},
> + {BQ25980_MASK4, 0x0},
> + {BQ25980_MASK5, 0x0},
> + {BQ25980_DEVICE_INFO, 0x8},
> + {BQ25980_ADC_CONTROL1, 0x0},
> + {BQ25980_ADC_CONTROL2, 0x0},
> + {BQ25980_IBUS_ADC_LSB, 0x0},
> + {BQ25980_IBUS_ADC_MSB, 0x0},
> + {BQ25980_VBUS_ADC_LSB, 0x0},
> + {BQ25980_VBUS_ADC_MSB, 0x0},
> + {BQ25980_VAC1_ADC_LSB, 0x0},
> + {BQ25980_VAC2_ADC_LSB, 0x0},
> + {BQ25980_VOUT_ADC_LSB, 0x0},
> + {BQ25980_VBAT_ADC_LSB, 0x0},
> + {BQ25980_IBAT_ADC_MSB, 0x0},
> + {BQ25980_IBAT_ADC_LSB, 0x0},
> + {BQ25980_TSBUS_ADC_LSB, 0x0},
> + {BQ25980_TSBAT_ADC_LSB, 0x0},
> + {BQ25980_TDIE_ADC_LSB, 0x0},
> + {BQ25980_DEGLITCH_TIME, 0x0},
> + {BQ25980_CHRGR_CTRL_6, 0x0},
> +};
> +
> +static struct reg_default bq25975_reg_defs[] = {
> + {BQ25980_BATOVP, 0x5A},
> + {BQ25980_BATOVP_ALM, 0x46},
> + {BQ25980_BATOCP, 0x51},
> + {BQ25980_BATOCP_ALM, 0x50},
> + {BQ25980_BATUCP_ALM, 0x28},
> + {BQ25980_CHRGR_CTRL_1, 0x0},
> + {BQ25980_BUSOVP, 0x26},
> + {BQ25980_BUSOVP_ALM, 0x22},
> + {BQ25980_BUSOCP, 0xD},
> + {BQ25980_BUSOCP_ALM, 0xC},
> + {BQ25980_TEMP_CONTROL, 0x30},
> + {BQ25980_TDIE_ALM, 0xC8},
> + {BQ25980_TSBUS_FLT, 0x15},
> + {BQ25980_TSBAT_FLG, 0x15},
> + {BQ25980_VAC_CONTROL, 0x0},
> + {BQ25980_CHRGR_CTRL_2, 0x0},
> + {BQ25980_CHRGR_CTRL_3, 0x20},
> + {BQ25980_CHRGR_CTRL_4, 0x1D},
> + {BQ25980_CHRGR_CTRL_5, 0x18},
> + {BQ25980_STAT1, 0x0},
> + {BQ25980_STAT2, 0x0},
> + {BQ25980_STAT3, 0x0},
> + {BQ25980_STAT4, 0x0},
> + {BQ25980_STAT5, 0x0},
> + {BQ25980_FLAG1, 0x0},
> + {BQ25980_FLAG2, 0x0},
> + {BQ25980_FLAG3, 0x0},
> + {BQ25980_FLAG4, 0x0},
> + {BQ25980_FLAG5, 0x0},
> + {BQ25980_MASK1, 0x0},
> + {BQ25980_MASK2, 0x0},
> + {BQ25980_MASK3, 0x0},
> + {BQ25980_MASK4, 0x0},
> + {BQ25980_MASK5, 0x0},
> + {BQ25980_DEVICE_INFO, 0x8},
> + {BQ25980_ADC_CONTROL1, 0x0},
> + {BQ25980_ADC_CONTROL2, 0x0},
> + {BQ25980_IBUS_ADC_LSB, 0x0},
> + {BQ25980_IBUS_ADC_MSB, 0x0},
> + {BQ25980_VBUS_ADC_LSB, 0x0},
> + {BQ25980_VBUS_ADC_MSB, 0x0},
> + {BQ25980_VAC1_ADC_LSB, 0x0},
> + {BQ25980_VAC2_ADC_LSB, 0x0},
> + {BQ25980_VOUT_ADC_LSB, 0x0},
> + {BQ25980_VBAT_ADC_LSB, 0x0},
> + {BQ25980_IBAT_ADC_MSB, 0x0},
> + {BQ25980_IBAT_ADC_LSB, 0x0},
> + {BQ25980_TSBUS_ADC_LSB, 0x0},
> + {BQ25980_TSBAT_ADC_LSB, 0x0},
> + {BQ25980_TDIE_ADC_LSB, 0x0},
> + {BQ25980_DEGLITCH_TIME, 0x0},
> + {BQ25980_CHRGR_CTRL_6, 0x0},
> +};
> +
> +static struct reg_default bq25960_reg_defs[] = {
> + {BQ25980_BATOVP, 0x5A},
> + {BQ25980_BATOVP_ALM, 0x46},
> + {BQ25980_BATOCP, 0x51},
> + {BQ25980_BATOCP_ALM, 0x50},
> + {BQ25980_BATUCP_ALM, 0x28},
> + {BQ25980_CHRGR_CTRL_1, 0x0},
> + {BQ25980_BUSOVP, 0x26},
> + {BQ25980_BUSOVP_ALM, 0x22},
> + {BQ25980_BUSOCP, 0xD},
> + {BQ25980_BUSOCP_ALM, 0xC},
> + {BQ25980_TEMP_CONTROL, 0x30},
> + {BQ25980_TDIE_ALM, 0xC8},
> + {BQ25980_TSBUS_FLT, 0x15},
> + {BQ25980_TSBAT_FLG, 0x15},
> + {BQ25980_VAC_CONTROL, 0x0},
> + {BQ25980_CHRGR_CTRL_2, 0x0},
> + {BQ25980_CHRGR_CTRL_3, 0x20},
> + {BQ25980_CHRGR_CTRL_4, 0x1D},
> + {BQ25980_CHRGR_CTRL_5, 0x18},
> + {BQ25980_STAT1, 0x0},
> + {BQ25980_STAT2, 0x0},
> + {BQ25980_STAT3, 0x0},
> + {BQ25980_STAT4, 0x0},
> + {BQ25980_STAT5, 0x0},
> + {BQ25980_FLAG1, 0x0},
> + {BQ25980_FLAG2, 0x0},
> + {BQ25980_FLAG3, 0x0},
> + {BQ25980_FLAG4, 0x0},
> + {BQ25980_FLAG5, 0x0},
> + {BQ25980_MASK1, 0x0},
> + {BQ25980_MASK2, 0x0},
> + {BQ25980_MASK3, 0x0},
> + {BQ25980_MASK4, 0x0},
> + {BQ25980_MASK5, 0x0},
> + {BQ25980_DEVICE_INFO, 0x8},
> + {BQ25980_ADC_CONTROL1, 0x0},
> + {BQ25980_ADC_CONTROL2, 0x0},
> + {BQ25980_IBUS_ADC_LSB, 0x0},
> + {BQ25980_IBUS_ADC_MSB, 0x0},
> + {BQ25980_VBUS_ADC_LSB, 0x0},
> + {BQ25980_VBUS_ADC_MSB, 0x0},
> + {BQ25980_VAC1_ADC_LSB, 0x0},
> + {BQ25980_VAC2_ADC_LSB, 0x0},
> + {BQ25980_VOUT_ADC_LSB, 0x0},
> + {BQ25980_VBAT_ADC_LSB, 0x0},
> + {BQ25980_IBAT_ADC_MSB, 0x0},
> + {BQ25980_IBAT_ADC_LSB, 0x0},
> + {BQ25980_TSBUS_ADC_LSB, 0x0},
> + {BQ25980_TSBAT_ADC_LSB, 0x0},
> + {BQ25980_TDIE_ADC_LSB, 0x0},
> + {BQ25980_DEGLITCH_TIME, 0x0},
> + {BQ25980_CHRGR_CTRL_6, 0x0},
> +};
> +
> +static int bq25980_watchdog_time[BQ25980_NUM_WD_VAL] = {5000, 10000, 50000,
> + 300000};
> +
> +static int bq25980_get_input_curr_lim(struct bq25980_device *bq)
> +{
> + unsigned int busocp_reg_code;
> + int ret;
> +
> + ret = regmap_read(bq->regmap, BQ25980_BUSOCP, &busocp_reg_code);
> + if (ret)
> + return ret;
> +
> + return (busocp_reg_code * BQ25980_BUSOCP_STEP_uA) + BQ25980_BUSOCP_OFFSET_uA;
> +}
> +
> +static int bq25980_set_hiz(struct bq25980_device *bq, int setting)
> +{
> + return regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2,
> + BQ25980_EN_HIZ, setting);
> +}
> +
> +static int bq25980_set_input_curr_lim(struct bq25980_device *bq, int busocp)
> +{
> + unsigned int busocp_reg_code;
> + int ret;
> +
> + if (!busocp)
> + return bq25980_set_hiz(bq, BQ25980_ENABLE_HIZ);
> +
> + bq25980_set_hiz(bq, BQ25980_DISABLE_HIZ);
> +
> + if (busocp < BQ25980_BUSOCP_MIN_uA)
> + busocp = BQ25980_BUSOCP_MIN_uA;
> +
> + if (bq->state.bypass)
> + busocp = min(busocp, bq->chip_info->busocp_sc_max);
> + else
> + busocp = min(busocp, bq->chip_info->busocp_byp_max);
> +
> + busocp_reg_code = (busocp - BQ25980_BUSOCP_OFFSET_uA)
> + / BQ25980_BUSOCP_STEP_uA;
> +
> + ret = regmap_write(bq->regmap, BQ25980_BUSOCP, busocp_reg_code);
> + if (ret)
> + return ret;
> +
> + return regmap_write(bq->regmap, BQ25980_BUSOCP_ALM, busocp_reg_code);
> +}
> +
> +static int bq25980_get_input_volt_lim(struct bq25980_device *bq)
> +{
> + unsigned int busovp_reg_code;
> + unsigned int busovp_offset;
> + unsigned int busovp_step;
> + int ret;
> +
> + if (bq->state.bypass) {
> + busovp_step = bq->chip_info->busovp_byp_step;
> + busovp_offset = bq->chip_info->busovp_byp_offset;
> + } else {
> + busovp_step = bq->chip_info->busovp_sc_step;
> + busovp_offset = bq->chip_info->busovp_sc_offset;
> + }
> +
> + ret = regmap_read(bq->regmap, BQ25980_BUSOVP, &busovp_reg_code);
> + if (ret)
> + return ret;
> +
> + return (busovp_reg_code * busovp_step) + busovp_offset;
> +}
> +
> +static int bq25980_set_input_volt_lim(struct bq25980_device *bq, int busovp)
> +{
> + unsigned int busovp_reg_code;
> + unsigned int busovp_step;
> + unsigned int busovp_offset;
> + int ret;
> +
> + if (bq->state.bypass) {
> + busovp_step = bq->chip_info->busovp_byp_step;
> + busovp_offset = bq->chip_info->busovp_byp_offset;
> + if (busovp > bq->chip_info->busovp_byp_max)
> + busovp = bq->chip_info->busovp_byp_max;
> + else if (busovp < bq->chip_info->busovp_byp_min)
> + busovp = bq->chip_info->busovp_byp_min;
> + } else {
> + busovp_step = bq->chip_info->busovp_sc_step;
> + busovp_offset = bq->chip_info->busovp_sc_offset;
> + if (busovp > bq->chip_info->busovp_sc_max)
> + busovp = bq->chip_info->busovp_sc_max;
> + else if (busovp < bq->chip_info->busovp_sc_min)
> + busovp = bq->chip_info->busovp_sc_min;
> + }
> +
> + busovp_reg_code = (busovp - busovp_offset) / busovp_step;
> +
> + ret = regmap_write(bq->regmap, BQ25980_BUSOVP, busovp_reg_code);
> + if (ret)
> + return ret;
> +
> + return regmap_write(bq->regmap, BQ25980_BUSOVP_ALM, busovp_reg_code);
> +}
> +
> +static int bq25980_get_const_charge_curr(struct bq25980_device *bq)
> +{
> + unsigned int batocp_reg_code;
> + int ret;
> +
> + ret = regmap_read(bq->regmap, BQ25980_BATOCP, &batocp_reg_code);
> + if (ret)
> + return ret;
> +
> + return (batocp_reg_code & BQ25980_BATOCP_MASK) *
> + BQ25980_BATOCP_STEP_uA;
> +}
> +
> +static int bq25980_set_const_charge_curr(struct bq25980_device *bq, int batocp)
> +{
> + unsigned int batocp_reg_code;
> + int ret;
> +
> + batocp = max(batocp, BQ25980_BATOCP_MIN_uA);
> + batocp = min(batocp, bq->chip_info->batocp_max);
> +
> + batocp_reg_code = batocp / BQ25980_BATOCP_STEP_uA;
> +
> + ret = regmap_update_bits(bq->regmap, BQ25980_BATOCP,
> + BQ25980_BATOCP_MASK, batocp_reg_code);
> + if (ret)
> + return ret;
> +
> + return regmap_update_bits(bq->regmap, BQ25980_BATOCP_ALM,
> + BQ25980_BATOCP_MASK, batocp_reg_code);
> +}
> +
> +static int bq25980_get_const_charge_volt(struct bq25980_device *bq)
> +{
> + unsigned int batovp_reg_code;
> + int ret;
> +
> + ret = regmap_read(bq->regmap, BQ25980_BATOVP, &batovp_reg_code);
> + if (ret)
> + return ret;
> +
> + return ((batovp_reg_code * bq->chip_info->batovp_step) +
> + bq->chip_info->batovp_offset);
> +}
> +
> +static int bq25980_set_const_charge_volt(struct bq25980_device *bq, int batovp)
> +{
> + unsigned int batovp_reg_code;
> + int ret;
> +
> + if (batovp < bq->chip_info->batovp_min)
> + batovp = bq->chip_info->batovp_min;
> +
> + if (batovp > bq->chip_info->batovp_max)
> + batovp = bq->chip_info->batovp_max;
> +
> + batovp_reg_code = (batovp - bq->chip_info->batovp_offset) /
> + bq->chip_info->batovp_step;
> +
> + ret = regmap_write(bq->regmap, BQ25980_BATOVP, batovp_reg_code);
> + if (ret)
> + return ret;
> +
> + return regmap_write(bq->regmap, BQ25980_BATOVP_ALM, batovp_reg_code);
> +}
> +
> +static int bq25980_set_bypass(struct bq25980_device *bq, bool en_bypass)
> +{
> + int ret;
> +
> + if (en_bypass)
> + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2,
> + BQ25980_EN_BYPASS, BQ25980_EN_BYPASS);
> + else
> + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2,
> + BQ25980_EN_BYPASS, en_bypass);
> + if (ret)
> + return ret;
> +
> + bq->state.bypass = en_bypass;
> +
> + return bq->state.bypass;
> +}
> +
> +static int bq25980_set_chg_en(struct bq25980_device *bq, bool en_chg)
> +{
> + int ret;
> +
> + if (en_chg)
> + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2,
> + BQ25980_CHG_EN, BQ25980_CHG_EN);
> + else
> + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2,
> + BQ25980_CHG_EN, en_chg);
> + if (ret)
> + return ret;
> +
> + bq->state.ce = en_chg;
> +
> + return 0;
> +}
> +
> +static int bq25980_get_adc_ibus(struct bq25980_device *bq)
> +{
> + int ibus_adc_lsb, ibus_adc_msb;
> + u16 ibus_adc;
> + int ret;
> +
> + ret = regmap_read(bq->regmap, BQ25980_IBUS_ADC_MSB, &ibus_adc_msb);
> + if (ret)
> + return ret;
> +
> + ret = regmap_read(bq->regmap, BQ25980_IBUS_ADC_LSB, &ibus_adc_lsb);
> + if (ret)
> + return ret;
> +
> + ibus_adc = (ibus_adc_msb << 8) | ibus_adc_lsb;
> +
> + if (ibus_adc_msb & BQ25980_ADC_POLARITY_BIT)
> + return ((ibus_adc ^ 0xffff) + 1) * BQ25980_ADC_CURR_STEP_uA;
> +
> + return ibus_adc * BQ25980_ADC_CURR_STEP_uA;
> +}
> +
> +static int bq25980_get_adc_vbus(struct bq25980_device *bq)
> +{
> + int vbus_adc_lsb, vbus_adc_msb;
> + u16 vbus_adc;
> + int ret;
> +
> + ret = regmap_read(bq->regmap, BQ25980_VBUS_ADC_MSB, &vbus_adc_msb);
> + if (ret)
> + return ret;
> +
> + ret = regmap_read(bq->regmap, BQ25980_VBUS_ADC_LSB, &vbus_adc_lsb);
> + if (ret)
> + return ret;
> +
> + vbus_adc = (vbus_adc_msb << 8) | vbus_adc_lsb;
> +
> + return vbus_adc * BQ25980_ADC_VOLT_STEP_uV;
> +}
> +
> +static int bq25980_get_ibat_adc(struct bq25980_device *bq)
> +{
> + int ret;
> + int ibat_adc_lsb, ibat_adc_msb;
> + int ibat_adc;
> +
> + ret = regmap_read(bq->regmap, BQ25980_IBAT_ADC_MSB, &ibat_adc_msb);
> + if (ret)
> + return ret;
> +
> + ret = regmap_read(bq->regmap, BQ25980_IBAT_ADC_LSB, &ibat_adc_lsb);
> + if (ret)
> + return ret;
> +
> + ibat_adc = (ibat_adc_msb << 8) | ibat_adc_lsb;
> +
> + if (ibat_adc_msb & BQ25980_ADC_POLARITY_BIT)
> + return ((ibat_adc ^ 0xffff) + 1) * BQ25980_ADC_CURR_STEP_uA;
> +
> + return ibat_adc * BQ25980_ADC_CURR_STEP_uA;
> +}
> +
> +static int bq25980_get_adc_vbat(struct bq25980_device *bq)
> +{
> + int vsys_adc_lsb, vsys_adc_msb;
> + u16 vsys_adc;
> + int ret;
> +
> + ret = regmap_read(bq->regmap, BQ25980_VBAT_ADC_MSB, &vsys_adc_msb);
> + if (ret)
> + return ret;
> +
> + ret = regmap_read(bq->regmap, BQ25980_VBAT_ADC_LSB, &vsys_adc_lsb);
> + if (ret)
> + return ret;
> +
> + vsys_adc = (vsys_adc_msb << 8) | vsys_adc_lsb;
> +
> + return vsys_adc * BQ25980_ADC_VOLT_STEP_uV;
> +}
> +
> +static int bq25980_get_state(struct bq25980_device *bq,
> + struct bq25980_state *state)
> +{
> + unsigned int chg_ctrl_2;
> + unsigned int stat1;
> + unsigned int stat2;
> + unsigned int stat3;
> + unsigned int stat4;
> + unsigned int ibat_adc_msb;
> + int ret;
> +
> + ret = regmap_read(bq->regmap, BQ25980_STAT1, &stat1);
> + if (ret)
> + return ret;
> +
> + ret = regmap_read(bq->regmap, BQ25980_STAT2, &stat2);
> + if (ret)
> + return ret;
> +
> + ret = regmap_read(bq->regmap, BQ25980_STAT3, &stat3);
> + if (ret)
> + return ret;
> +
> + ret = regmap_read(bq->regmap, BQ25980_STAT4, &stat4);
> + if (ret)
> + return ret;
> +
> + ret = regmap_read(bq->regmap, BQ25980_CHRGR_CTRL_2, &chg_ctrl_2);
> + if (ret)
> + return ret;
> +
> + ret = regmap_read(bq->regmap, BQ25980_IBAT_ADC_MSB, &ibat_adc_msb);
> + if (ret)
> + return ret;
> +
> + state->dischg = ibat_adc_msb & BQ25980_ADC_POLARITY_BIT;
> + state->ovp = (stat1 & BQ25980_STAT1_OVP_MASK) |
> + (stat3 & BQ25980_STAT3_OVP_MASK);
> + state->ocp = (stat1 & BQ25980_STAT1_OCP_MASK) |
> + (stat2 & BQ25980_STAT2_OCP_MASK);
> + state->tflt = stat4 & BQ25980_STAT4_TFLT_MASK;
> + state->wdt = stat4 & BQ25980_WD_STAT;
> + state->online = stat3 & BQ25980_PRESENT_MASK;
> + state->ce = chg_ctrl_2 & BQ25980_CHG_EN;
> + state->hiz = chg_ctrl_2 & BQ25980_EN_HIZ;
> + state->bypass = chg_ctrl_2 & BQ25980_EN_BYPASS;
> +
> + return 0;
> +}
> +
> +static int bq25980_set_battery_property(struct power_supply *psy,
> + enum power_supply_property psp,
> + const union power_supply_propval *val)
> +{
> + struct bq25980_device *bq = power_supply_get_drvdata(psy);
> + int ret = 0;
> +
> + if (ret)
> + return ret;
> +
> + switch (psp) {
> + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
> + ret = bq25980_set_const_charge_curr(bq, val->intval);
> + if (ret)
> + return ret;
> + break;
> +
> + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
> + ret = bq25980_set_const_charge_volt(bq, val->intval);
> + if (ret)
> + return ret;
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return ret;
> +}
> +
> +static int bq25980_get_battery_property(struct power_supply *psy,
> + enum power_supply_property psp,
> + union power_supply_propval *val)
> +{
> + struct bq25980_device *bq = power_supply_get_drvdata(psy);
> + int ret = 0;
> +
> + switch (psp) {
> + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
> + val->intval = bq->init_data.ichg_max;
> + break;
> +
> + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
> + val->intval = bq->init_data.vreg_max;
> + break;
> +
> + case POWER_SUPPLY_PROP_CURRENT_NOW:
> + ret = bq25980_get_ibat_adc(bq);
> + val->intval = ret;
> + break;
> +
> + case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> + ret = bq25980_get_adc_vbat(bq);
> + if (ret < 0)
> + return ret;
> +
> + val->intval = ret;
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return ret;
> +}
> +
> +static int bq25980_set_charger_property(struct power_supply *psy,
> + enum power_supply_property prop,
> + const union power_supply_propval *val)
> +{
> + struct bq25980_device *bq = power_supply_get_drvdata(psy);
> + int ret = -EINVAL;
> +
> + switch (prop) {
> + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
> + ret = bq25980_set_input_curr_lim(bq, val->intval);
> + if (ret)
> + return ret;
> + break;
> +
> + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
> + ret = bq25980_set_input_volt_lim(bq, val->intval);
> + if (ret)
> + return ret;
> + break;
> +
> + case POWER_SUPPLY_PROP_CHARGE_TYPE:
> + ret = bq25980_set_bypass(bq, val->intval);
> + if (ret)
> + return ret;
> + break;
> +
> + case POWER_SUPPLY_PROP_STATUS:
> + ret = bq25980_set_chg_en(bq, val->intval);
> + if (ret)
> + return ret;
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return ret;
> +}
> +
> +static int bq25980_get_charger_property(struct power_supply *psy,
> + enum power_supply_property psp,
> + union power_supply_propval *val)
> +{
> + struct bq25980_device *bq = power_supply_get_drvdata(psy);
> + struct bq25980_state state;
> + int ret = 0;
> +
> + mutex_lock(&bq->lock);
> + ret = bq25980_get_state(bq, &state);
> + mutex_unlock(&bq->lock);
> + if (ret)
> + return ret;
> +
> + switch (psp) {
> + case POWER_SUPPLY_PROP_MANUFACTURER:
> + val->strval = BQ25980_MANUFACTURER;
> + break;
> + case POWER_SUPPLY_PROP_MODEL_NAME:
> + val->strval = bq->model_name;
> + break;
> + case POWER_SUPPLY_PROP_ONLINE:
> + val->intval = state.online;
> + break;
> +
> + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
> + ret = bq25980_get_input_volt_lim(bq);
> + if (ret < 0)
> + return ret;
> + val->intval = ret;
> + break;
> +
> + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
> + ret = bq25980_get_input_curr_lim(bq);
> + if (ret < 0)
> + return ret;
> +
> + val->intval = ret;
> + break;
> +
> + case POWER_SUPPLY_PROP_HEALTH:
> + val->intval = POWER_SUPPLY_HEALTH_GOOD;
> +
> + if (state.tflt)
> + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
> + else if (state.ovp)
> + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
> + else if (state.ocp)
> + val->intval = POWER_SUPPLY_HEALTH_OVERCURRENT;
> + else if (state.wdt)
> + val->intval =
> + POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE;
> + break;
> +
> + case POWER_SUPPLY_PROP_STATUS:
> + val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
> +
> + if ((state.ce) && (!state.hiz))
> + val->intval = POWER_SUPPLY_STATUS_CHARGING;
> + else if (state.dischg)
> + val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
> + else if (!state.ce)
> + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
> + break;
> +
> + case POWER_SUPPLY_PROP_CHARGE_TYPE:
> + val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
> +
> + if (!state.ce)
> + val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
> + else if (state.bypass)
> + val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
> + else if (!state.bypass)
> + val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
> + break;
> +
> + case POWER_SUPPLY_PROP_CURRENT_NOW:
> + ret = bq25980_get_adc_ibus(bq);
> + if (ret < 0)
> + return ret;
> +
> + val->intval = ret;
> + break;
> +
> + case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> + ret = bq25980_get_adc_vbus(bq);
> + if (ret < 0)
> + return ret;
> +
> + val->intval = ret;
> + break;
> +
> + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
> + ret = bq25980_get_const_charge_curr(bq);
> + if (ret < 0)
> + return ret;
> +
> + val->intval = ret;
> + break;
> +
> + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
> + ret = bq25980_get_const_charge_volt(bq);
> + if (ret < 0)
> + return ret;
> +
> + val->intval = ret;
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return ret;
> +}
> +
> +static bool bq25980_state_changed(struct bq25980_device *bq,
> + struct bq25980_state *new_state)
> +{
> + struct bq25980_state old_state;
> +
> + mutex_lock(&bq->lock);
> + old_state = bq->state;
> + mutex_unlock(&bq->lock);
> +
> + return (old_state.dischg != new_state->dischg ||
> + old_state.ovp != new_state->ovp ||
> + old_state.ocp != new_state->ocp ||
> + old_state.online != new_state->online ||
> + old_state.wdt != new_state->wdt ||
> + old_state.tflt != new_state->tflt ||
> + old_state.ce != new_state->ce ||
> + old_state.hiz != new_state->hiz ||
> + old_state.bypass != new_state->bypass);
> +}
> +
> +static irqreturn_t bq25980_irq_handler_thread(int irq, void *private)
> +{
> + struct bq25980_device *bq = private;
> + struct bq25980_state state;
> + int ret;
> +
> + ret = bq25980_get_state(bq, &state);
> + if (ret < 0)
> + goto irq_out;
> +
> + if (!bq25980_state_changed(bq, &state))
> + goto irq_out;
> +
> + mutex_lock(&bq->lock);
> + bq->state = state;
> + mutex_unlock(&bq->lock);
> +
> + power_supply_changed(bq->charger);
> +
> +irq_out:
> + return IRQ_HANDLED;
> +}
> +
> +static enum power_supply_property bq25980_power_supply_props[] = {
> + POWER_SUPPLY_PROP_MANUFACTURER,
> + POWER_SUPPLY_PROP_MODEL_NAME,
> + POWER_SUPPLY_PROP_STATUS,
> + POWER_SUPPLY_PROP_ONLINE,
> + POWER_SUPPLY_PROP_HEALTH,
> + POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
> + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
> + POWER_SUPPLY_PROP_CHARGE_TYPE,
> + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
> + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
> + POWER_SUPPLY_PROP_CURRENT_NOW,
> + POWER_SUPPLY_PROP_VOLTAGE_NOW,
> +};
> +
> +static enum power_supply_property bq25980_battery_props[] = {
> + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
> + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
> + POWER_SUPPLY_PROP_CURRENT_NOW,
> + POWER_SUPPLY_PROP_VOLTAGE_NOW,
> +};
> +
> +static char *bq25980_charger_supplied_to[] = {
> + "main-battery",
> +};
> +
> +static int bq25980_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_CHARGE_TYPE:
> + case POWER_SUPPLY_PROP_STATUS:
> + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +static const struct power_supply_desc bq25980_power_supply_desc = {
> + .name = "bq25980-charger",
> + .type = POWER_SUPPLY_TYPE_MAINS,
> + .properties = bq25980_power_supply_props,
> + .num_properties = ARRAY_SIZE(bq25980_power_supply_props),
> + .get_property = bq25980_get_charger_property,
> + .set_property = bq25980_set_charger_property,
> + .property_is_writeable = bq25980_property_is_writeable,
> +};
> +
> +static struct power_supply_desc bq25980_battery_desc = {
> + .name = "bq25980-battery",
> + .type = POWER_SUPPLY_TYPE_BATTERY,
> + .get_property = bq25980_get_battery_property,
> + .set_property = bq25980_set_battery_property,
> + .properties = bq25980_battery_props,
> + .num_properties = ARRAY_SIZE(bq25980_battery_props),
> + .property_is_writeable = bq25980_property_is_writeable,
> +};
> +
> +
> +static bool bq25980_is_volatile_reg(struct device *dev, unsigned int reg)
> +{
> + switch (reg) {
> + case BQ25980_CHRGR_CTRL_2:
> + case BQ25980_STAT1...BQ25980_FLAG5:
> + case BQ25980_ADC_CONTROL1...BQ25980_TDIE_ADC_LSB:
> + return true;
> + default:
> + return false;
> + }
> +}
> +
> +static const struct regmap_config bq25980_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> +
> + .max_register = BQ25980_CHRGR_CTRL_6,
> + .reg_defaults = bq25980_reg_defs,
> + .num_reg_defaults = ARRAY_SIZE(bq25980_reg_defs),
> + .cache_type = REGCACHE_RBTREE,
> + .volatile_reg = bq25980_is_volatile_reg,
> +};
> +
> +static const struct regmap_config bq25975_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> +
> + .max_register = BQ25980_CHRGR_CTRL_6,
> + .reg_defaults = bq25975_reg_defs,
> + .num_reg_defaults = ARRAY_SIZE(bq25975_reg_defs),
> + .cache_type = REGCACHE_RBTREE,
> + .volatile_reg = bq25980_is_volatile_reg,
> +};
> +
> +static const struct regmap_config bq25960_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> +
> + .max_register = BQ25980_CHRGR_CTRL_6,
> + .reg_defaults = bq25960_reg_defs,
> + .num_reg_defaults = ARRAY_SIZE(bq25960_reg_defs),
> + .cache_type = REGCACHE_RBTREE,
> + .volatile_reg = bq25980_is_volatile_reg,
> +};
> +
> +static const struct bq25980_chip_info bq25980_chip_info_tbl[] = {
> + [BQ25980] = {
> + .model_id = BQ25980,
> + .regmap_config = &bq25980_regmap_config,
> +
> + .busocp_def = BQ25980_BUSOCP_DFLT_uA,
> + .busocp_sc_min = BQ25960_BUSOCP_SC_MAX_uA,
> + .busocp_sc_max = BQ25980_BUSOCP_SC_MAX_uA,
> + .busocp_byp_max = BQ25980_BUSOCP_BYP_MAX_uA,
> + .busocp_byp_min = BQ25980_BUSOCP_MIN_uA,
> +
> + .busovp_sc_def = BQ25980_BUSOVP_DFLT_uV,
> + .busovp_byp_def = BQ25980_BUSOVP_BYPASS_DFLT_uV,
> + .busovp_sc_step = BQ25980_BUSOVP_SC_STEP_uV,
> + .busovp_sc_offset = BQ25980_BUSOVP_SC_OFFSET_uV,
> + .busovp_byp_step = BQ25980_BUSOVP_BYP_STEP_uV,
> + .busovp_byp_offset = BQ25980_BUSOVP_BYP_OFFSET_uV,
> + .busovp_sc_min = BQ25980_BUSOVP_SC_MIN_uV,
> + .busovp_sc_max = BQ25980_BUSOVP_SC_MAX_uV,
> + .busovp_byp_min = BQ25980_BUSOVP_BYP_MIN_uV,
> + .busovp_byp_max = BQ25980_BUSOVP_BYP_MAX_uV,
> +
> + .batovp_def = BQ25980_BATOVP_DFLT_uV,
> + .batovp_max = BQ25980_BATOVP_MAX_uV,
> + .batovp_min = BQ25980_BATOVP_MIN_uV,
> + .batovp_step = BQ25980_BATOVP_STEP_uV,
> + .batovp_offset = BQ25980_BATOVP_OFFSET_uV,
> +
> + .batocp_def = BQ25980_BATOCP_DFLT_uA,
> + .batocp_max = BQ25980_BATOCP_MAX_uA,
> + },
> +
> + [BQ25975] = {
> + .model_id = BQ25975,
> + .regmap_config = &bq25975_regmap_config,
> +
> + .busocp_def = BQ25975_BUSOCP_DFLT_uA,
> + .busocp_sc_min = BQ25975_BUSOCP_SC_MAX_uA,
> + .busocp_sc_max = BQ25975_BUSOCP_SC_MAX_uA,
> + .busocp_byp_min = BQ25980_BUSOCP_MIN_uA,
> + .busocp_byp_max = BQ25975_BUSOCP_BYP_MAX_uA,
> +
> + .busovp_sc_def = BQ25975_BUSOVP_DFLT_uV,
> + .busovp_byp_def = BQ25975_BUSOVP_BYPASS_DFLT_uV,
> + .busovp_sc_step = BQ25975_BUSOVP_SC_STEP_uV,
> + .busovp_sc_offset = BQ25975_BUSOVP_SC_OFFSET_uV,
> + .busovp_byp_step = BQ25975_BUSOVP_BYP_STEP_uV,
> + .busovp_byp_offset = BQ25975_BUSOVP_BYP_OFFSET_uV,
> + .busovp_sc_min = BQ25975_BUSOVP_SC_MIN_uV,
> + .busovp_sc_max = BQ25975_BUSOVP_SC_MAX_uV,
> + .busovp_byp_min = BQ25975_BUSOVP_BYP_MIN_uV,
> + .busovp_byp_max = BQ25975_BUSOVP_BYP_MAX_uV,
> +
> + .batovp_def = BQ25975_BATOVP_DFLT_uV,
> + .batovp_max = BQ25975_BATOVP_MAX_uV,
> + .batovp_min = BQ25975_BATOVP_MIN_uV,
> + .batovp_step = BQ25975_BATOVP_STEP_uV,
> + .batovp_offset = BQ25975_BATOVP_OFFSET_uV,
> +
> + .batocp_def = BQ25980_BATOCP_DFLT_uA,
> + .batocp_max = BQ25980_BATOCP_MAX_uA,
> + },
> +
> + [BQ25960] = {
> + .model_id = BQ25960,
> + .regmap_config = &bq25960_regmap_config,
> +
> + .busocp_def = BQ25960_BUSOCP_DFLT_uA,
> + .busocp_sc_min = BQ25960_BUSOCP_SC_MAX_uA,
> + .busocp_sc_max = BQ25960_BUSOCP_SC_MAX_uA,
> + .busocp_byp_min = BQ25960_BUSOCP_SC_MAX_uA,
> + .busocp_byp_max = BQ25960_BUSOCP_BYP_MAX_uA,
> +
> + .busovp_sc_def = BQ25975_BUSOVP_DFLT_uV,
> + .busovp_byp_def = BQ25975_BUSOVP_BYPASS_DFLT_uV,
> + .busovp_sc_step = BQ25960_BUSOVP_SC_STEP_uV,
> + .busovp_sc_offset = BQ25960_BUSOVP_SC_OFFSET_uV,
> + .busovp_byp_step = BQ25960_BUSOVP_BYP_STEP_uV,
> + .busovp_byp_offset = BQ25960_BUSOVP_BYP_OFFSET_uV,
> + .busovp_sc_min = BQ25960_BUSOVP_SC_MIN_uV,
> + .busovp_sc_max = BQ25960_BUSOVP_SC_MAX_uV,
> + .busovp_byp_min = BQ25960_BUSOVP_BYP_MIN_uV,
> + .busovp_byp_max = BQ25960_BUSOVP_BYP_MAX_uV,
> +
> + .batovp_def = BQ25960_BATOVP_DFLT_uV,
> + .batovp_max = BQ25960_BATOVP_MAX_uV,
> + .batovp_min = BQ25960_BATOVP_MIN_uV,
> + .batovp_step = BQ25960_BATOVP_STEP_uV,
> + .batovp_offset = BQ25960_BATOVP_OFFSET_uV,
> +
> + .batocp_def = BQ25960_BATOCP_DFLT_uA,
> + .batocp_max = BQ25960_BATOCP_MAX_uA,
> + },
> +};
> +
> +static int bq25980_power_supply_init(struct bq25980_device *bq,
> + struct device *dev)
> +{
> + struct power_supply_config psy_cfg = { .drv_data = bq,
> + .of_node = dev->of_node, };
> +
> + psy_cfg.supplied_to = bq25980_charger_supplied_to;
> + psy_cfg.num_supplicants = ARRAY_SIZE(bq25980_charger_supplied_to);
> +
> + bq->charger = devm_power_supply_register(bq->dev,
> + &bq25980_power_supply_desc,
> + &psy_cfg);
> + if (IS_ERR(bq->charger))
> + return -EINVAL;
> +
> + bq->battery = devm_power_supply_register(bq->dev,
> + &bq25980_battery_desc,
> + &psy_cfg);
> + if (IS_ERR(bq->battery))
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int bq25980_hw_init(struct bq25980_device *bq)
> +{
> + struct power_supply_battery_info bat_info = { };
> + int wd_reg_val;
> + int ret = 0;
> + int curr_val;
> + int volt_val;
> + int i;
> +
> + if (!bq->watchdog_timer) {
> + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_3,
> + BQ25980_WATCHDOG_DIS,
> + BQ25980_WATCHDOG_DIS);
> + } else {
> + for (i = 0; i < BQ25980_NUM_WD_VAL; i++) {
> + if (bq->watchdog_timer > bq25980_watchdog_time[i] &&
> + bq->watchdog_timer < bq25980_watchdog_time[i + 1]) {
> + wd_reg_val = i;
> + break;
> + }
> + }
> +
> + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_3,
> + BQ25980_WATCHDOG_MASK, wd_reg_val);
> + }
> + if (ret)
> + return ret;
> +
> + ret = power_supply_get_battery_info(bq->charger, &bat_info);
> + if (ret) {
> + dev_warn(bq->dev, "battery info missing\n");
> + return -EINVAL;
> + }
> +
> + bq->init_data.ichg_max = bat_info.constant_charge_current_max_ua;
> + bq->init_data.vreg_max = bat_info.constant_charge_voltage_max_uv;
> +
> + if (bq->state.bypass) {
> + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2,
> + BQ25980_EN_BYPASS, BQ25980_EN_BYPASS);
> + if (ret)
> + return ret;
> +
> + curr_val = bq->init_data.bypass_ilim;
> + volt_val = bq->init_data.bypass_vlim;
> + } else {
> + curr_val = bq->init_data.sc_ilim;
> + volt_val = bq->init_data.sc_vlim;
> + }
> +
> + ret = bq25980_set_input_curr_lim(bq, curr_val);
> + if (ret)
> + return ret;
> +
> + ret = bq25980_set_input_volt_lim(bq, volt_val);
> + if (ret)
> + return ret;
> +
> + return regmap_update_bits(bq->regmap, BQ25980_ADC_CONTROL1,
> + BQ25980_ADC_EN, BQ25980_ADC_EN);
> +}
> +
> +static int bq25980_parse_dt(struct bq25980_device *bq)
> +{
> + int ret;
> +
> + ret = device_property_read_u32(bq->dev, "ti,watchdog-timeout-ms",
> + &bq->watchdog_timer);
> + if (ret)
> + bq->watchdog_timer = BQ25980_WATCHDOG_MIN;
> +
> + if (bq->watchdog_timer > BQ25980_WATCHDOG_MAX ||
> + bq->watchdog_timer < BQ25980_WATCHDOG_MIN)
> + return -EINVAL;
> +
> + ret = device_property_read_u32(bq->dev,
> + "ti,sc-ovp-limit-microvolt",
> + &bq->init_data.sc_vlim);
> + if (ret)
> + bq->init_data.sc_vlim = bq->chip_info->busovp_sc_def;
> +
> + if (bq->init_data.sc_vlim > bq->chip_info->busovp_sc_max ||
> + bq->init_data.sc_vlim < bq->chip_info->busovp_sc_min) {
> + dev_err(bq->dev, "SC ovp limit is out of range\n");
> + return -EINVAL;
> + }
> +
> + ret = device_property_read_u32(bq->dev,
> + "ti,sc-ocp-limit-microamp",
> + &bq->init_data.sc_ilim);
> + if (ret)
> + bq->init_data.sc_ilim = bq->chip_info->busocp_def;
> +
> + if (bq->init_data.sc_ilim > bq->chip_info->busocp_sc_max ||
> + bq->init_data.sc_ilim < bq->chip_info->busocp_sc_min) {
> + dev_err(bq->dev, "SC ocp limit is out of range\n");
> + return -EINVAL;
> + }
> +
> + ret = device_property_read_u32(bq->dev,
> + "ti,bypass-ovp-limit-microvolt",
> + &bq->init_data.bypass_vlim);
> + if (ret)
> + bq->init_data.bypass_vlim = bq->chip_info->busovp_byp_def;
> +
> + if (bq->init_data.bypass_vlim > bq->chip_info->busovp_byp_max ||
> + bq->init_data.bypass_vlim < bq->chip_info->busovp_byp_min) {
> + dev_err(bq->dev, "Bypass ovp limit is out of range\n");
> + return -EINVAL;
> + }
> +
> + ret = device_property_read_u32(bq->dev,
> + "ti,bypass-ocp-limit-microamp",
> + &bq->init_data.bypass_ilim);
> + if (ret)
> + bq->init_data.bypass_ilim = bq->chip_info->busocp_def;
> +
> + if (bq->init_data.bypass_ilim > bq->chip_info->busocp_byp_max ||
> + bq->init_data.bypass_ilim < bq->chip_info->busocp_byp_min) {
> + dev_err(bq->dev, "Bypass ocp limit is out of range\n");
> + return -EINVAL;
> + }
> +
> +
> + bq->state.bypass = device_property_read_bool(bq->dev,
> + "ti,bypass-enable");
> + return 0;
> +}
> +
> +static int bq25980_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct device *dev = &client->dev;
> + struct bq25980_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->chip_info = &bq25980_chip_info_tbl[id->driver_data];
> +
> + bq->regmap = devm_regmap_init_i2c(client,
> + bq->chip_info->regmap_config);
> + 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 = bq25980_parse_dt(bq);
> + if (ret) {
> + dev_err(dev, "Failed to read device tree properties%d\n", ret);
> + return ret;
> + }
> +
> + if (client->irq) {
> + ret = devm_request_threaded_irq(dev, client->irq, NULL,
> + bq25980_irq_handler_thread,
> + IRQF_TRIGGER_FALLING |
> + IRQF_ONESHOT,
> + dev_name(&client->dev), bq);
> + if (ret)
> + return ret;
> + }
> +
> + ret = bq25980_power_supply_init(bq, dev);
> + if (ret) {
> + dev_err(dev, "Failed to register power supply\n");
> + return ret;
> + }
> +
> + ret = bq25980_hw_init(bq);
> + if (ret) {
> + dev_err(dev, "Cannot initialize the chip.\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static const struct i2c_device_id bq25980_i2c_ids[] = {
> + { "bq25980", BQ25980 },
> + { "bq25975", BQ25975 },
> + { "bq25975", BQ25975 },
> + {},
> +};
> +MODULE_DEVICE_TABLE(i2c, bq25980_i2c_ids);
> +
> +static const struct of_device_id bq25980_of_match[] = {
> + { .compatible = "ti,bq25980", .data = (void *)BQ25980 },
> + { .compatible = "ti,bq25975", .data = (void *)BQ25975 },
> + { .compatible = "ti,bq25960", .data = (void *)BQ25960 },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, bq25980_of_match);
> +
> +static struct i2c_driver bq25980_driver = {
> + .driver = {
> + .name = "bq25980-charger",
> + .of_match_table = bq25980_of_match,
> + },
> + .probe = bq25980_probe,
> + .id_table = bq25980_i2c_ids,
> +};
> +module_i2c_driver(bq25980_driver);
> +
> +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
> +MODULE_AUTHOR("Ricardo Rivera-Matos <r-rivera-matos@ti.com>");
> +MODULE_DESCRIPTION("bq25980 charger driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/power/supply/bq25980_charger.h b/drivers/power/supply/bq25980_charger.h
> new file mode 100644
> index 000000000000..39f94eba5f6c
> --- /dev/null
> +++ b/drivers/power/supply/bq25980_charger.h
> @@ -0,0 +1,178 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/* Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ */
> +
> +#ifndef BQ25980_CHARGER_H
> +#define BQ25980_CHARGER_H
> +
> +#define BQ25980_MANUFACTURER "Texas Instruments"
> +
> +#define BQ25980_BATOVP 0x0
> +#define BQ25980_BATOVP_ALM 0x1
> +#define BQ25980_BATOCP 0x2
> +#define BQ25980_BATOCP_ALM 0x3
> +#define BQ25980_BATUCP_ALM 0x4
> +#define BQ25980_CHRGR_CTRL_1 0x5
> +#define BQ25980_BUSOVP 0x6
> +#define BQ25980_BUSOVP_ALM 0x7
> +#define BQ25980_BUSOCP 0x8
> +#define BQ25980_BUSOCP_ALM 0x9
> +#define BQ25980_TEMP_CONTROL 0xA
> +#define BQ25980_TDIE_ALM 0xB
> +#define BQ25980_TSBUS_FLT 0xC
> +#define BQ25980_TSBAT_FLG 0xD
> +#define BQ25980_VAC_CONTROL 0xE
> +#define BQ25980_CHRGR_CTRL_2 0xF
> +#define BQ25980_CHRGR_CTRL_3 0x10
> +#define BQ25980_CHRGR_CTRL_4 0x11
> +#define BQ25980_CHRGR_CTRL_5 0x12
> +#define BQ25980_STAT1 0x13
> +#define BQ25980_STAT2 0x14
> +#define BQ25980_STAT3 0x15
> +#define BQ25980_STAT4 0x16
> +#define BQ25980_STAT5 0x17
> +#define BQ25980_FLAG1 0x18
> +#define BQ25980_FLAG2 0x19
> +#define BQ25980_FLAG3 0x1A
> +#define BQ25980_FLAG4 0x1B
> +#define BQ25980_FLAG5 0x1C
> +#define BQ25980_MASK1 0x1D
> +#define BQ25980_MASK2 0x1E
> +#define BQ25980_MASK3 0x1F
> +#define BQ25980_MASK4 0x20
> +#define BQ25980_MASK5 0x21
> +#define BQ25980_DEVICE_INFO 0x22
> +#define BQ25980_ADC_CONTROL1 0x23
> +#define BQ25980_ADC_CONTROL2 0x24
> +#define BQ25980_IBUS_ADC_MSB 0x25
> +#define BQ25980_IBUS_ADC_LSB 0x26
> +#define BQ25980_VBUS_ADC_MSB 0x27
> +#define BQ25980_VBUS_ADC_LSB 0x28
> +#define BQ25980_VAC1_ADC_MSB 0x29
> +#define BQ25980_VAC1_ADC_LSB 0x2A
> +#define BQ25980_VAC2_ADC_MSB 0x2B
> +#define BQ25980_VAC2_ADC_LSB 0x2C
> +#define BQ25980_VOUT_ADC_MSB 0x2D
> +#define BQ25980_VOUT_ADC_LSB 0x2E
> +#define BQ25980_VBAT_ADC_MSB 0x2F
> +#define BQ25980_VBAT_ADC_LSB 0x30
> +#define BQ25980_IBAT_ADC_MSB 0x31
> +#define BQ25980_IBAT_ADC_LSB 0x32
> +#define BQ25980_TSBUS_ADC_MSB 0x33
> +#define BQ25980_TSBUS_ADC_LSB 0x34
> +#define BQ25980_TSBAT_ADC_MSB 0x35
> +#define BQ25980_TSBAT_ADC_LSB 0x36
> +#define BQ25980_TDIE_ADC_MSB 0x37
> +#define BQ25980_TDIE_ADC_LSB 0x38
> +#define BQ25980_DEGLITCH_TIME 0x39
> +#define BQ25980_CHRGR_CTRL_6 0x3A
> +
> +#define BQ25980_BUSOCP_STEP_uA 250000
> +#define BQ25980_BUSOCP_OFFSET_uA 1000000
> +
> +#define BQ25980_BUSOCP_DFLT_uA 4250000
> +#define BQ25975_BUSOCP_DFLT_uA 4250000
> +#define BQ25960_BUSOCP_DFLT_uA 3250000
> +
> +#define BQ25980_BUSOCP_MIN_uA 1000000
> +
> +#define BQ25980_BUSOCP_SC_MAX_uA 5750000
> +#define BQ25975_BUSOCP_SC_MAX_uA 5750000
> +#define BQ25960_BUSOCP_SC_MAX_uA 3750000
> +
> +#define BQ25980_BUSOCP_BYP_MAX_uA 8500000
> +#define BQ25975_BUSOCP_BYP_MAX_uA 8500000
> +#define BQ25960_BUSOCP_BYP_MAX_uA 5750000
> +
> +#define BQ25980_BUSOVP_SC_STEP_uV 100000
> +#define BQ25975_BUSOVP_SC_STEP_uV 50000
> +#define BQ25960_BUSOVP_SC_STEP_uV 50000
> +#define BQ25980_BUSOVP_SC_OFFSET_uV 14000000
> +#define BQ25975_BUSOVP_SC_OFFSET_uV 7000000
> +#define BQ25960_BUSOVP_SC_OFFSET_uV 7000000
> +
> +#define BQ25980_BUSOVP_BYP_STEP_uV 50000
> +#define BQ25975_BUSOVP_BYP_STEP_uV 25000
> +#define BQ25960_BUSOVP_BYP_STEP_uV 25000
> +#define BQ25980_BUSOVP_BYP_OFFSET_uV 7000000
> +#define BQ25975_BUSOVP_BYP_OFFSET_uV 3500000
> +#define BQ25960_BUSOVP_BYP_OFFSET_uV 3500000
> +
> +#define BQ25980_BUSOVP_DFLT_uV 17800000
> +#define BQ25980_BUSOVP_BYPASS_DFLT_uV 8900000
> +#define BQ25975_BUSOVP_DFLT_uV 8900000
> +#define BQ25975_BUSOVP_BYPASS_DFLT_uV 4450000
> +#define BQ25960_BUSOVP_DFLT_uV 8900000
> +
> +#define BQ25980_BUSOVP_SC_MIN_uV 14000000
> +#define BQ25975_BUSOVP_SC_MIN_uV 7000000
> +#define BQ25960_BUSOVP_SC_MIN_uV 7000000
> +#define BQ25980_BUSOVP_BYP_MIN_uV 7000000
> +#define BQ25975_BUSOVP_BYP_MIN_uV 3500000
> +#define BQ25960_BUSOVP_BYP_MIN_uV 3500000
> +
> +#define BQ25980_BUSOVP_SC_MAX_uV 22000000
> +#define BQ25975_BUSOVP_SC_MAX_uV 12750000
> +#define BQ25960_BUSOVP_SC_MAX_uV 12750000
> +
> +#define BQ25980_BUSOVP_BYP_MAX_uV 12750000
> +#define BQ25975_BUSOVP_BYP_MAX_uV 6500000
> +#define BQ25960_BUSOVP_BYP_MAX_uV 6500000
> +
> +#define BQ25980_BATOVP_STEP_uV 20000
> +#define BQ25975_BATOVP_STEP_uV 10000
> +#define BQ25960_BATOVP_STEP_uV 10000
> +
> +#define BQ25980_BATOVP_OFFSET_uV 7000000
> +#define BQ25975_BATOVP_OFFSET_uV 3500000
> +#define BQ25960_BATOVP_OFFSET_uV 3500000
> +
> +#define BQ25980_BATOVP_DFLT_uV 14000000
> +#define BQ25975_BATOVP_DFLT_uV 8900000
> +#define BQ25960_BATOVP_DFLT_uV 8900000
> +
> +#define BQ25980_BATOVP_MIN_uV 7000000
> +#define BQ25975_BATOVP_MIN_uV 3500000
> +#define BQ25960_BATOVP_MIN_uV 3500000
> +
> +#define BQ25980_BATOVP_MAX_uV 9540000
> +#define BQ25975_BATOVP_MAX_uV 4770000
> +#define BQ25960_BATOVP_MAX_uV 4770000
> +
> +#define BQ25980_BATOCP_STEP_uA 100000
> +
> +#define BQ25980_BATOCP_MASK GENMASK(6, 0)
> +
> +#define BQ25980_BATOCP_DFLT_uA 8100000
> +#define BQ25960_BATOCP_DFLT_uA 6100000
> +
> +#define BQ25980_BATOCP_MIN_uA 2000000
> +
> +#define BQ25980_BATOCP_MAX_uA 11000000
> +#define BQ25975_BATOCP_MAX_uA 11000000
> +#define BQ25960_BATOCP_MAX_uA 7000000
> +
> +#define BQ25980_ENABLE_HIZ 0xff
> +#define BQ25980_DISABLE_HIZ 0x0
> +#define BQ25980_EN_BYPASS BIT(3)
> +#define BQ25980_STAT1_OVP_MASK (BIT(6) | BIT(5) | BIT(0))
> +#define BQ25980_STAT3_OVP_MASK (BIT(7) | BIT(6))
> +#define BQ25980_STAT1_OCP_MASK BIT(3)
> +#define BQ25980_STAT2_OCP_MASK (BIT(6) | BIT(1))
> +#define BQ25980_STAT4_TFLT_MASK GENMASK(5, 1)
> +#define BQ25980_WD_STAT BIT(0)
> +#define BQ25980_PRESENT_MASK GENMASK(4, 2)
> +#define BQ25980_CHG_EN BIT(4)
> +#define BQ25980_EN_HIZ BIT(6)
> +#define BQ25980_ADC_EN BIT(7)
> +
> +#define BQ25980_ADC_VOLT_STEP_uV 1000
> +#define BQ25980_ADC_CURR_STEP_uA 1000
> +#define BQ25980_ADC_POLARITY_BIT BIT(7)
> +
> +#define BQ25980_WATCHDOG_MASK GENMASK(4, 3)
> +#define BQ25980_WATCHDOG_DIS BIT(2)
> +#define BQ25980_WATCHDOG_MAX 300000
> +#define BQ25980_WATCHDOG_MIN 0
> +#define BQ25980_NUM_WD_VAL 4
> +
> +#endif /* BQ25980_CHARGER_H */
> --
> 2.28.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2020-10-04 22:47 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-31 16:48 [PATCH 1/2] dt-binding: bq25980: Add the bq25980 flash charger Dan Murphy
2020-08-31 16:48 ` [PATCH 2/2] power: bq25980: Add support for the BQ259xx family Dan Murphy
2020-09-22 16:06 ` Dan Murphy
2020-10-04 22:47 ` Sebastian Reichel
2020-09-14 18:26 ` [PATCH 1/2] dt-binding: bq25980: Add the bq25980 flash charger Rob Herring
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.