devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 0/2] Introduce the BQ256XX family of chargers
@ 2020-10-01 21:40 Ricardo Rivera-Matos
  2020-10-01 21:40 ` [PATCH v5 1/2] dt-bindings: power: Add the bq256xx dt bindings Ricardo Rivera-Matos
  2020-10-01 21:40 ` [PATCH v5 2/2] power: supply: bq256xx: Introduce the BQ256XX charger driver Ricardo Rivera-Matos
  0 siblings, 2 replies; 5+ messages in thread
From: Ricardo Rivera-Matos @ 2020-10-01 21:40 UTC (permalink / raw)
  To: sre, robh+dt, linux-pm, devicetree, linux-kernel
  Cc: dmurphy, Ricardo Rivera-Matos

Hello,

This patchset introduces the bq256xx family of charging ICs. The bq256xx
ICs are highly integrated, buck, switching chargers intended for use in 
smartphones, tablets, and portable electronics.

Ricardo Rivera-Matos (2):
  dt-bindings: power: Add the bq256xx dt bindings
  power: supply: bq256xx: Introduce the BQ256XX charger driver

 .../bindings/power/supply/bq256xx.yaml        |  110 +
 drivers/power/supply/Kconfig                  |   11 +
 drivers/power/supply/Makefile                 |    1 +
 drivers/power/supply/bq256xx_charger.c        | 1785 +++++++++++++++++
 4 files changed, 1907 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/supply/bq256xx.yaml
 create mode 100644 drivers/power/supply/bq256xx_charger.c

-- 
2.28.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v5 1/2] dt-bindings: power: Add the bq256xx dt bindings
  2020-10-01 21:40 [PATCH v5 0/2] Introduce the BQ256XX family of chargers Ricardo Rivera-Matos
@ 2020-10-01 21:40 ` Ricardo Rivera-Matos
  2020-10-01 21:40 ` [PATCH v5 2/2] power: supply: bq256xx: Introduce the BQ256XX charger driver Ricardo Rivera-Matos
  1 sibling, 0 replies; 5+ messages in thread
From: Ricardo Rivera-Matos @ 2020-10-01 21:40 UTC (permalink / raw)
  To: sre, robh+dt, linux-pm, devicetree, linux-kernel
  Cc: dmurphy, Ricardo Rivera-Matos, Rob Herring

Add the bindings for the bq256xx series of battery charging ICs.

Datasheets:
- https://www.ti.com/lit/ds/symlink/bq25600.pdf
- https://www.ti.com/lit/ds/symlink/bq25601.pdf
- https://www.ti.com/lit/ds/symlink/bq25600d.pdf
- https://www.ti.com/lit/ds/symlink/bq25601d.pdf
- https://www.ti.com/lit/ds/symlink/bq25611d.pdf
- https://www.ti.com/lit/ds/symlink/bq25618.pdf
- https://www.ti.com/lit/ds/symlink/bq25619.pdf

Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Ricardo Rivera-Matos <r-rivera-matos@ti.com>

v4 - documents monitored-battery and interrupts, fixes example for

ti,watchdog-timeout-ms
---
 .../bindings/power/supply/bq256xx.yaml        | 110 ++++++++++++++++++
 1 file changed, 110 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/supply/bq256xx.yaml

diff --git a/Documentation/devicetree/bindings/power/supply/bq256xx.yaml b/Documentation/devicetree/bindings/power/supply/bq256xx.yaml
new file mode 100644
index 000000000000..18b54783e11a
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/bq256xx.yaml
@@ -0,0 +1,110 @@
+# 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/bq256xx.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: TI bq256xx Switch Mode Buck Charger
+
+maintainers:
+  - Ricardo Rivera-Matos <r-rivera-matos@ti.com>
+
+description: |
+  The bq256xx devices are a family of highly-integrated battery charge
+  management and system power management ICs for single cell Li-ion and Li-
+  polymer batteries.
+
+  Datasheets:
+    - https://www.ti.com/lit/ds/symlink/bq25600.pdf
+    - https://www.ti.com/lit/ds/symlink/bq25601.pdf
+    - https://www.ti.com/lit/ds/symlink/bq25600d.pdf
+    - https://www.ti.com/lit/ds/symlink/bq25601d.pdf
+    - https://www.ti.com/lit/ds/symlink/bq25611d.pdf
+    - https://www.ti.com/lit/ds/symlink/bq25618.pdf
+    - https://www.ti.com/lit/ds/symlink/bq25619.pdf
+
+properties:
+  compatible:
+    enum:
+      - ti,bq25600
+      - ti,bq25601
+      - ti,bq25600d
+      - ti,bq25601d
+      - ti,bq25611d
+      - ti,bq25618
+      - ti,bq25619
+
+  reg:
+    maxItems: 1
+
+  ti,watchdog-timeout-ms:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    default: 0
+    description: |
+      Watchdog timer in ms. 0 (default) disables the watchdog
+    minimum: 0
+    maximum: 160000
+    enum: [ 0, 40000, 80000, 160000]
+
+  input-voltage-limit-microvolt:
+    description: |
+       Minimum input voltage limit in µV with a 100000 µV step
+    minimum: 3900000
+    maximum: 5400000
+
+  input-current-limit-microamp:
+    description: |
+       Maximum input current limit in µA with a 100000 µA step
+    minimum: 100000
+    maximum: 3200000
+
+  monitored-battery:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: phandle to the battery node being monitored
+
+  interrupts:
+    maxItems: 1
+    description: |
+      Interrupt sends an active low, 256 μs pulse to host to report the charger
+      device status and faults.
+
+required:
+  - compatible
+  - reg
+  - monitored-battery
+
+additionalProperties: false
+
+examples:
+  - |
+    bat: battery {
+      compatible = "simple-battery";
+      constant-charge-current-max-microamp = <2040000>;
+      constant-charge-voltage-max-microvolt = <4352000>;
+      precharge-current-microamp = <180000>;
+      charge-term-current-microamp = <180000>;
+    };
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+    i2c {
+
+      clock-frequency = <400000>;
+
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      charger@6b {
+        compatible = "ti,bq25601";
+        reg = <0x6b>;
+        monitored-battery = <&bat>;
+
+        interrupt-parent = <&gpio1>;
+        interrupts = <16 IRQ_TYPE_EDGE_FALLING>;
+        ti,watchdog-timeout-ms = <40000>;
+
+        input-voltage-limit-microvolt = <4500000>;
+        input-current-limit-microamp = <2400000>;
+       };
+    };
+...
-- 
2.28.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v5 2/2] power: supply: bq256xx: Introduce the BQ256XX charger driver
  2020-10-01 21:40 [PATCH v5 0/2] Introduce the BQ256XX family of chargers Ricardo Rivera-Matos
  2020-10-01 21:40 ` [PATCH v5 1/2] dt-bindings: power: Add the bq256xx dt bindings Ricardo Rivera-Matos
@ 2020-10-01 21:40 ` Ricardo Rivera-Matos
  2020-10-03 10:54   ` Sebastian Reichel
  1 sibling, 1 reply; 5+ messages in thread
From: Ricardo Rivera-Matos @ 2020-10-01 21:40 UTC (permalink / raw)
  To: sre, robh+dt, linux-pm, devicetree, linux-kernel
  Cc: dmurphy, Ricardo Rivera-Matos

The BQ256XX family of devices are highly integrated buck chargers
for single cell batteries.

Signed-off-by: Ricardo Rivera-Matos <r-rivera-matos@ti.com>

v5 - adds power_supply_put_battery_info() and devm_add_action_or_rest() calls
---
 drivers/power/supply/Kconfig           |   11 +
 drivers/power/supply/Makefile          |    1 +
 drivers/power/supply/bq256xx_charger.c | 1785 ++++++++++++++++++++++++
 3 files changed, 1797 insertions(+)
 create mode 100644 drivers/power/supply/bq256xx_charger.c

diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 44d3c8512fb8..87d852914bc2 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -618,6 +618,17 @@ config CHARGER_BQ25890
 	help
 	  Say Y to enable support for the TI BQ25890 battery charger.
 
+config CHARGER_BQ256XX
+	tristate "TI BQ256XX battery charger driver"
+	depends on I2C
+	depends on GPIOLIB || COMPILE_TEST
+	select REGMAP_I2C
+	help
+	  Say Y to enable support for the TI BQ256XX battery chargers. The
+	  BQ256XX family of devices are highly-integrated, switch-mode battery
+	  charge management and system power path management devices for single
+	  cell Li-ion and Li-polymer batteries.
+
 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 b9644663e435..e762442c7cc6 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -83,6 +83,7 @@ obj-$(CONFIG_CHARGER_BQ24190)	+= bq24190_charger.o
 obj-$(CONFIG_CHARGER_BQ24257)	+= bq24257_charger.o
 obj-$(CONFIG_CHARGER_BQ24735)	+= bq24735-charger.o
 obj-$(CONFIG_CHARGER_BQ25890)	+= bq25890_charger.o
+obj-$(CONFIG_CHARGER_BQ256XX)	+= bq256xx_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/bq256xx_charger.c b/drivers/power/supply/bq256xx_charger.c
new file mode 100644
index 000000000000..0226d8dc914e
--- /dev/null
+++ b/drivers/power/supply/bq256xx_charger.c
@@ -0,0 +1,1785 @@
+// SPDX-License-Identifier: GPL-2.0
+// BQ256XX 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/usb/phy.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+
+#define BQ256XX_MANUFACTURER "Texas Instruments"
+
+#define BQ256XX_INPUT_CURRENT_LIMIT		0x00
+#define BQ256XX_CHARGER_CONTROL_0		0x01
+#define BQ256XX_CHARGE_CURRENT_LIMIT		0x02
+#define BQ256XX_PRECHG_AND_TERM_CURR_LIM	0x03
+#define BQ256XX_BATTERY_VOLTAGE_LIMIT		0x04
+#define BQ256XX_CHARGER_CONTROL_1		0x05
+#define BQ256XX_CHARGER_CONTROL_2		0x06
+#define BQ256XX_CHARGER_CONTROL_3		0x07
+#define BQ256XX_CHARGER_STATUS_0		0x08
+#define BQ256XX_CHARGER_STATUS_1		0x09
+#define BQ256XX_CHARGER_STATUS_2		0x0a
+#define BQ256XX_PART_INFORMATION		0x0b
+#define BQ256XX_CHARGER_CONTROL_4		0x0c
+
+#define BQ256XX_IINDPM_MASK		GENMASK(4, 0)
+#define BQ256XX_IINDPM_STEP_uA		100000
+#define BQ256XX_IINDPM_OFFSET_uA	100000
+#define BQ256XX_IINDPM_MIN_uA		100000
+#define BQ256XX_IINDPM_MAX_uA		3200000
+#define BQ256XX_IINDPM_DEF_uA		2400000
+
+#define BQ256XX_VINDPM_MASK		GENMASK(3, 0)
+#define BQ256XX_VINDPM_STEP_uV		100000
+#define BQ256XX_VINDPM_OFFSET_uV	3900000
+#define BQ256XX_VINDPM_MIN_uV		3900000
+#define BQ256XX_VINDPM_MAX_uV		5400000
+#define BQ256XX_VINDPM_DEF_uV		4500000
+
+#define BQ256XX_VBATREG_MASK		GENMASK(7, 3)
+#define BQ2560X_VBATREG_STEP_uV		32000
+#define BQ2560X_VBATREG_OFFSET_uV	3856000
+#define BQ2560X_VBATREG_MIN_uV		3856000
+#define BQ2560X_VBATREG_MAX_uV		4624000
+#define BQ2560X_VBATREG_DEF_uV		4208000
+#define BQ25601D_VBATREG_OFFSET_uV	3847000
+#define BQ25601D_VBATREG_MIN_uV		3847000
+#define BQ25601D_VBATREG_MAX_uV		4615000
+#define BQ25601D_VBATREG_DEF_uV		4199000
+#define BQ2561X_VBATREG_STEP_uV		10000
+#define BQ25611D_VBATREG_MIN_uV		3494000
+#define BQ25611D_VBATREG_MAX_uV		4510000
+#define BQ25611D_VBATREG_DEF_uV		4190000
+#define BQ25618_VBATREG_MIN_uV		3504000
+#define BQ25618_VBATREG_MAX_uV		4500000
+#define BQ25618_VBATREG_DEF_uV		4200000
+#define BQ256XX_VBATREG_BIT_SHIFT	3
+#define BQ2561X_VBATREG_THRESH		0x8
+#define BQ25611D_VBATREG_THRESH_uV	4290000
+#define BQ25618_VBATREG_THRESH_uV	4300000
+
+#define BQ256XX_ITERM_MASK		GENMASK(3, 0)
+#define BQ256XX_ITERM_STEP_uA		60000
+#define BQ256XX_ITERM_OFFSET_uA		60000
+#define BQ256XX_ITERM_MIN_uA		60000
+#define BQ256XX_ITERM_MAX_uA		780000
+#define BQ256XX_ITERM_DEF_uA		180000
+#define BQ25618_ITERM_STEP_uA		20000
+#define BQ25618_ITERM_OFFSET_uA		20000
+#define BQ25618_ITERM_MIN_uA		20000
+#define BQ25618_ITERM_MAX_uA		260000
+#define BQ25618_ITERM_DEF_uA		60000
+
+#define BQ256XX_IPRECHG_MASK		GENMASK(7, 4)
+#define BQ256XX_IPRECHG_STEP_uA		60000
+#define BQ256XX_IPRECHG_OFFSET_uA	60000
+#define BQ256XX_IPRECHG_MIN_uA		60000
+#define BQ256XX_IPRECHG_MAX_uA		780000
+#define BQ256XX_IPRECHG_DEF_uA		180000
+#define BQ25618_IPRECHG_STEP_uA		20000
+#define BQ25618_IPRECHG_OFFSET_uA	20000
+#define BQ25618_IPRECHG_MIN_uA		20000
+#define BQ25618_IPRECHG_MAX_uA		260000
+#define BQ25618_IPRECHG_DEF_uA		40000
+#define BQ256XX_IPRECHG_BIT_SHIFT	4
+
+#define BQ256XX_ICHG_MASK		GENMASK(5, 0)
+#define BQ256XX_ICHG_STEP_uA		60000
+#define BQ256XX_ICHG_MIN_uA		0
+#define BQ256XX_ICHG_MAX_uA		3000000
+#define BQ2560X_ICHG_DEF_uA		2040000
+#define BQ25611D_ICHG_DEF_uA		1020000
+#define BQ25618_ICHG_STEP_uA		20000
+#define BQ25618_ICHG_MIN_uA		0
+#define BQ25618_ICHG_MAX_uA		1500000
+#define BQ25618_ICHG_DEF_uA		340000
+#define BQ25618_ICHG_THRESH		0x3c
+#define BQ25618_ICHG_THRESH_uA		1180000
+
+#define BQ256XX_VBUS_STAT_MASK		GENMASK(7, 5)
+#define BQ256XX_VBUS_STAT_NO_INPUT	0
+#define BQ256XX_VBUS_STAT_USB_SDP	BIT(5)
+#define BQ256XX_VBUS_STAT_USB_CDP	BIT(6)
+#define BQ256XX_VBUS_STAT_USB_DCP	(BIT(6) | BIT(5))
+#define BQ256XX_VBUS_STAT_USB_OTG	(BIT(7) | BIT(6) | BIT(5))
+
+#define BQ256XX_CHRG_STAT_MASK		GENMASK(4, 3)
+#define BQ256XX_CHRG_STAT_NOT_CHRGING	0
+#define BQ256XX_CHRG_STAT_PRECHRGING	BIT(3)
+#define BQ256XX_CHRG_STAT_FAST_CHRGING	BIT(4)
+#define BQ256XX_CHRG_STAT_CHRG_TERM	(BIT(4) | BIT(3))
+
+#define BQ256XX_PG_STAT_MASK		BIT(2)
+#define BQ256XX_WDT_FAULT_MASK		BIT(7)
+#define BQ256XX_CHRG_FAULT_MASK		GENMASK(5, 4)
+#define BQ256XX_CHRG_FAULT_NORMAL	0
+#define BQ256XX_CHRG_FAULT_INPUT	BIT(4)
+#define BQ256XX_CHRG_FAULT_THERM	BIT(5)
+#define BQ256XX_CHRG_FAULT_CST_EXPIRE	(BIT(5) | BIT(4))
+#define BQ256XX_BAT_FAULT_MASK		BIT(3)
+#define BQ256XX_NTC_FAULT_MASK		GENMASK(2, 0)
+#define BQ256XX_NTC_FAULT_WARM		BIT(1)
+#define BQ256XX_NTC_FAULT_COOL		(BIT(1) | BIT(0))
+#define BQ256XX_NTC_FAULT_COLD		(BIT(2) | BIT(0))
+#define BQ256XX_NTC_FAULT_HOT		(BIT(2) | BIT(1))
+
+#define BQ256XX_NUM_WD_VAL	8
+#define BQ256XX_WATCHDOG_MASK	GENMASK(5, 4)
+#define BQ256XX_WATCHDOG_MAX	1600000
+#define BQ256XX_WATCHDOG_DIS	0
+#define BQ256XX_WDT_BIT_SHIFT	4
+
+#define BQ256XX_REG_RST		BIT(7)
+
+/**
+ * struct bq256xx_init_data -
+ * @ichg: fast charge current
+ * @iindpm: input current limit
+ * @vbatreg: charge voltage
+ * @iterm: termination current
+ * @iprechg: precharge current
+ * @vindpm: input voltage limit
+ * @ichg_max: maximum fast charge current
+ * @vbatreg_max: maximum charge voltage
+ */
+struct bq256xx_init_data {
+	u32 ichg;
+	u32 iindpm;
+	u32 vbatreg;
+	u32 iterm;
+	u32 iprechg;
+	u32 vindpm;
+	u32 ichg_max;
+	u32 vbatreg_max;
+};
+
+/**
+ * struct bq256xx_state -
+ * @vbus_stat: VBUS status according to BQ256XX_CHARGER_STATUS_0
+ * @chrg_stat: charging status according to BQ256XX_CHARGER_STATUS_0
+ * @online: PG status according to BQ256XX_CHARGER_STATUS_0
+ *
+ * @wdt_fault: watchdog fault according to BQ256XX_CHARGER_STATUS_1
+ * @bat_fault: battery fault according to BQ256XX_CHARGER_STATUS_1
+ * @chrg_fault: charging fault according to BQ256XX_CHARGER_STATUS_1
+ * @ntc_fault: TS fault according to BQ256XX_CHARGER_STATUS_1
+ */
+struct bq256xx_state {
+	u8 vbus_stat;
+	u8 chrg_stat;
+	bool online;
+
+	u8 wdt_fault;
+	u8 bat_fault;
+	u8 chrg_fault;
+	u8 ntc_fault;
+};
+
+enum bq256xx_id {
+	BQ25600,
+	BQ25600D,
+	BQ25601,
+	BQ25601D,
+	BQ25618,
+	BQ25619,
+	BQ25611D,
+};
+
+/**
+ * struct bq256xx_device -
+ * @client: i2c client structure
+ * @regmap: register map structure
+ * @dev: device structure
+ * @lock: mutex lock structure
+ *
+ * @usb2_phy: usb_phy identifier
+ * @usb3_phy: usb_phy identifier
+ * @usb_nb: notifier block
+ * @usb_work: usb work queue
+ * @usb_event: usb_event code
+ *
+ * @model_name: i2c name string
+ *
+ * @init_data: initialization data
+ * @chip_info: device variant information
+ * @state: device status and faults
+ * @watchdog_timer: watchdog timer value in milliseconds
+ */
+struct bq256xx_device {
+	struct i2c_client *client;
+	struct device *dev;
+	struct power_supply *charger;
+	struct power_supply *battery;
+	struct mutex lock;
+	struct regmap *regmap;
+
+	struct usb_phy *usb2_phy;
+	struct usb_phy *usb3_phy;
+	struct notifier_block usb_nb;
+	struct work_struct usb_work;
+	unsigned long usb_event;
+
+	char model_name[I2C_NAME_SIZE];
+
+	struct bq256xx_init_data init_data;
+	const struct bq256xx_chip_info *chip_info;
+	struct bq256xx_state state;
+	int watchdog_timer;
+};
+
+/**
+ * struct bq256xx_chip_info -
+ * @model_id: device instance
+ *
+ * @bq256xx_regmap_config: regmap configuration struct
+ * @bq256xx_get_ichg: pointer to instance specific get_ichg function
+ * @bq256xx_get_iindpm: pointer to instance specific get_iindpm function
+ * @bq256xx_get_vbatreg: pointer to instance specific get_vbatreg function
+ * @bq256xx_get_iterm: pointer to instance specific get_iterm function
+ * @bq256xx_get_iprechg: pointer to instance specific get_iprechg function
+ * @bq256xx_get_vindpm: pointer to instance specific get_vindpm function
+ *
+ * @bq256xx_set_ichg: pointer to instance specific set_ichg function
+ * @bq256xx_set_iindpm: pointer to instance specific set_iindpm function
+ * @bq256xx_set_vbatreg: pointer to instance specific set_vbatreg function
+ * @bq256xx_set_iterm: pointer to instance specific set_iterm function
+ * @bq256xx_set_iprechg: pointer to instance specific set_iprechg function
+ * @bq256xx_set_vindpm: pointer to instance specific set_vindpm function
+ *
+ * @bq256xx_def_ichg: default ichg value in microamps
+ * @bq256xx_def_iindpm: default iindpm value in microamps
+ * @bq256xx_def_vbatreg: default vbatreg value in microvolts
+ * @bq256xx_def_iterm: default iterm value in microamps
+ * @bq256xx_def_iprechg: default iprechg value in microamps
+ * @bq256xx_def_vindpm: default vindpm value in microvolts
+ *
+ * @bq256xx_max_ichg: maximum charge current in microamps
+ * @bq256xx_max_vbatreg: maximum battery regulation voltage in microvolts
+ *
+ * @has_usb_detect: indicates whether device has BC1.2 detection
+ */
+struct bq256xx_chip_info {
+	int model_id;
+
+	const struct regmap_config *bq256xx_regmap_config;
+
+	int (*bq256xx_get_ichg)(struct bq256xx_device *bq);
+	int (*bq256xx_get_iindpm)(struct bq256xx_device *bq);
+	int (*bq256xx_get_vbatreg)(struct bq256xx_device *bq);
+	int (*bq256xx_get_iterm)(struct bq256xx_device *bq);
+	int (*bq256xx_get_iprechg)(struct bq256xx_device *bq);
+	int (*bq256xx_get_vindpm)(struct bq256xx_device *bq);
+
+	int (*bq256xx_set_ichg)(struct bq256xx_device *bq, int ichg);
+	int (*bq256xx_set_iindpm)(struct bq256xx_device *bq, int iindpm);
+	int (*bq256xx_set_vbatreg)(struct bq256xx_device *bq, int vbatreg);
+	int (*bq256xx_set_iterm)(struct bq256xx_device *bq, int iterm);
+	int (*bq256xx_set_iprechg)(struct bq256xx_device *bq, int iprechg);
+	int (*bq256xx_set_vindpm)(struct bq256xx_device *bq, int vindpm);
+
+	int bq256xx_def_ichg;
+	int bq256xx_def_iindpm;
+	int bq256xx_def_vbatreg;
+	int bq256xx_def_iterm;
+	int bq256xx_def_iprechg;
+	int bq256xx_def_vindpm;
+
+	int bq256xx_max_ichg;
+	int bq256xx_max_vbatreg;
+
+	bool has_usb_detect;
+};
+
+static int bq256xx_watchdog_time[BQ256XX_NUM_WD_VAL] = {
+	0, 40000, 80000, 1600000
+};
+
+static const int bq25611d_vbatreg_values[] = {
+	3494000, 3590000, 3686000, 3790000, 3894000, 3990000, 4090000, 4140000,
+	4190000
+};
+
+static const int bq25618_619_vbatreg_values[] = {
+	3504000, 3600000, 3696000, 3800000, 3904000, 4000000, 4100000, 4150000,
+	4200000
+};
+
+static const int bq25618_619_ichg_values[] = {
+	1290000, 1360000, 1430000, 1500000
+};
+
+static enum power_supply_usb_type bq256xx_usb_type[] = {
+	POWER_SUPPLY_USB_TYPE_SDP,
+	POWER_SUPPLY_USB_TYPE_CDP,
+	POWER_SUPPLY_USB_TYPE_DCP,
+	POWER_SUPPLY_USB_TYPE_UNKNOWN,
+	POWER_SUPPLY_USB_TYPE_ACA,
+};
+
+static int bq256xx_array_parse(int array_size, int val, const int array[])
+{
+	int i = 0;
+
+	if (val < array[i])
+		return i - 1;
+
+	if (val >= array[array_size - 1])
+		return array_size - 1;
+
+	for (i = 1; i < array_size; i++) {
+		if (val == array[i])
+			return i;
+
+		if (val > array[i - 1] && val < array[i]) {
+			if (val < array[i])
+				return i - 1;
+			else
+				return i;
+		}
+	}
+	return -EINVAL;
+}
+
+static int bq256xx_usb_notifier(struct notifier_block *nb, unsigned long val,
+				void *priv)
+{
+	struct bq256xx_device *bq =
+			container_of(nb, struct bq256xx_device, usb_nb);
+
+	bq->usb_event = val;
+	queue_work(system_power_efficient_wq, &bq->usb_work);
+
+	return NOTIFY_OK;
+}
+
+static void bq256xx_usb_work(struct work_struct *data)
+{
+	struct bq256xx_device *bq =
+			container_of(data, struct bq256xx_device, usb_work);
+
+	switch (bq->usb_event) {
+	case USB_EVENT_ID:
+		break;
+
+	case USB_EVENT_NONE:
+		power_supply_changed(bq->charger);
+		break;
+	default:
+		dev_err(bq->dev, "Error switching to charger mode.\n");
+		break;
+	}
+}
+
+static struct reg_default bq2560x_reg_defs[] = {
+	{BQ256XX_INPUT_CURRENT_LIMIT, 0x17},
+	{BQ256XX_CHARGER_CONTROL_0, 0x1a},
+	{BQ256XX_CHARGE_CURRENT_LIMIT, 0xa2},
+	{BQ256XX_PRECHG_AND_TERM_CURR_LIM, 0x22},
+	{BQ256XX_BATTERY_VOLTAGE_LIMIT, 0x58},
+	{BQ256XX_CHARGER_CONTROL_1, 0x9f},
+	{BQ256XX_CHARGER_CONTROL_2, 0x66},
+	{BQ256XX_CHARGER_CONTROL_3, 0x4c},
+};
+
+static struct reg_default bq25611d_reg_defs[] = {
+	{BQ256XX_INPUT_CURRENT_LIMIT, 0x17},
+	{BQ256XX_CHARGER_CONTROL_0, 0x1a},
+	{BQ256XX_CHARGE_CURRENT_LIMIT, 0x91},
+	{BQ256XX_PRECHG_AND_TERM_CURR_LIM, 0x12},
+	{BQ256XX_BATTERY_VOLTAGE_LIMIT, 0x40},
+	{BQ256XX_CHARGER_CONTROL_1, 0x9e},
+	{BQ256XX_CHARGER_CONTROL_2, 0xe6},
+	{BQ256XX_CHARGER_CONTROL_3, 0x4c},
+	{BQ256XX_PART_INFORMATION, 0x54},
+	{BQ256XX_CHARGER_CONTROL_4, 0x75},
+};
+
+static struct reg_default bq25618_619_reg_defs[] = {
+	{BQ256XX_INPUT_CURRENT_LIMIT, 0x17},
+	{BQ256XX_CHARGER_CONTROL_0, 0x1a},
+	{BQ256XX_CHARGE_CURRENT_LIMIT, 0x91},
+	{BQ256XX_PRECHG_AND_TERM_CURR_LIM, 0x12},
+	{BQ256XX_BATTERY_VOLTAGE_LIMIT, 0x40},
+	{BQ256XX_CHARGER_CONTROL_1, 0x9e},
+	{BQ256XX_CHARGER_CONTROL_2, 0xe6},
+	{BQ256XX_CHARGER_CONTROL_3, 0x4c},
+	{BQ256XX_PART_INFORMATION, 0x2c},
+	{BQ256XX_CHARGER_CONTROL_4, 0x75},
+};
+
+static int bq256xx_get_state(struct bq256xx_device *bq,
+				struct bq256xx_state *state)
+{
+	unsigned int charger_status_0;
+	unsigned int charger_status_1;
+	int ret;
+
+	ret = regmap_read(bq->regmap, BQ256XX_CHARGER_STATUS_0,
+						&charger_status_0);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(bq->regmap, BQ256XX_CHARGER_STATUS_1,
+						&charger_status_1);
+	if (ret)
+		return ret;
+
+	state->vbus_stat = charger_status_0 & BQ256XX_VBUS_STAT_MASK;
+	state->chrg_stat = charger_status_0 & BQ256XX_CHRG_STAT_MASK;
+	state->online = charger_status_0 & BQ256XX_PG_STAT_MASK;
+
+	state->wdt_fault = charger_status_1 & BQ256XX_WDT_FAULT_MASK;
+	state->bat_fault = charger_status_1 & BQ256XX_BAT_FAULT_MASK;
+	state->chrg_fault = charger_status_1 & BQ256XX_CHRG_FAULT_MASK;
+	state->ntc_fault = charger_status_1 & BQ256XX_NTC_FAULT_MASK;
+
+	return 0;
+}
+
+static int bq256xx_get_ichg_curr(struct bq256xx_device *bq)
+{
+	unsigned int charge_current_limit;
+	unsigned int ichg_reg_code;
+	int ret;
+
+	ret = regmap_read(bq->regmap, BQ256XX_CHARGE_CURRENT_LIMIT,
+						&charge_current_limit);
+	if (ret)
+		return ret;
+
+	ichg_reg_code = charge_current_limit & BQ256XX_ICHG_MASK;
+
+	return ichg_reg_code * BQ256XX_ICHG_STEP_uA;
+}
+
+static int bq25618_619_get_ichg_curr(struct bq256xx_device *bq)
+{
+	unsigned int charge_current_limit;
+	unsigned int ichg_reg_code;
+	int ret;
+
+	ret = regmap_read(bq->regmap, BQ256XX_CHARGE_CURRENT_LIMIT,
+						&charge_current_limit);
+	if (ret)
+		return ret;
+
+	ichg_reg_code = charge_current_limit & BQ256XX_ICHG_MASK;
+
+	if (ichg_reg_code < BQ25618_ICHG_THRESH)
+		return ichg_reg_code * BQ25618_ICHG_STEP_uA;
+
+	return bq25618_619_ichg_values[ichg_reg_code - BQ25618_ICHG_THRESH];
+}
+
+static int bq256xx_set_ichg_curr(struct bq256xx_device *bq, int ichg)
+{
+	unsigned int ichg_reg_code;
+
+	if (ichg < BQ256XX_ICHG_MIN_uA)
+		ichg = BQ256XX_ICHG_MIN_uA;
+	else if (ichg > bq->init_data.ichg_max)
+		ichg = bq->init_data.ichg_max;
+
+	ichg_reg_code = ichg / BQ256XX_ICHG_STEP_uA;
+
+	return regmap_update_bits(bq->regmap, BQ256XX_CHARGE_CURRENT_LIMIT,
+					BQ256XX_ICHG_MASK, ichg_reg_code);
+}
+
+static int bq25618_619_set_ichg_curr(struct bq256xx_device *bq, int ichg)
+{
+	int array_size = ARRAY_SIZE(bq25618_619_ichg_values);
+	unsigned int ichg_reg_code;
+
+	if (ichg < BQ25618_ICHG_MIN_uA)
+		ichg = BQ25618_ICHG_MIN_uA;
+	else if (ichg > bq->init_data.ichg_max)
+		ichg = bq->init_data.ichg_max;
+
+	if (ichg <= BQ25618_ICHG_THRESH_uA) {
+		ichg_reg_code = ichg / BQ25618_ICHG_STEP_uA;
+	} else {
+		ichg_reg_code = bq256xx_array_parse(array_size, ichg,
+			bq25618_619_ichg_values) + BQ25618_ICHG_THRESH;
+	}
+
+	return regmap_update_bits(bq->regmap, BQ256XX_CHARGE_CURRENT_LIMIT,
+					BQ256XX_ICHG_MASK, ichg_reg_code);
+}
+
+static int bq25618_619_get_chrg_volt(struct bq256xx_device *bq)
+{
+	unsigned int battery_volt_lim;
+	unsigned int vbatreg_reg_code;
+	int ret;
+
+	ret = regmap_read(bq->regmap, BQ256XX_BATTERY_VOLTAGE_LIMIT,
+							&battery_volt_lim);
+
+	if (ret)
+		return ret;
+
+	vbatreg_reg_code = (battery_volt_lim & BQ256XX_VBATREG_MASK) >>
+						BQ256XX_VBATREG_BIT_SHIFT;
+
+	if (vbatreg_reg_code > BQ2561X_VBATREG_THRESH)
+		return ((vbatreg_reg_code - BQ2561X_VBATREG_THRESH) *
+					BQ2561X_VBATREG_STEP_uV) +
+					BQ25618_VBATREG_THRESH_uV;
+
+	return bq25618_619_vbatreg_values[vbatreg_reg_code];
+}
+
+static int bq25611d_get_chrg_volt(struct bq256xx_device *bq)
+{
+	unsigned int battery_volt_lim;
+	unsigned int vbatreg_reg_code;
+	int ret;
+
+	ret = regmap_read(bq->regmap, BQ256XX_BATTERY_VOLTAGE_LIMIT,
+							&battery_volt_lim);
+	if (ret)
+		return ret;
+
+	vbatreg_reg_code = (battery_volt_lim & BQ256XX_VBATREG_MASK) >>
+						BQ256XX_VBATREG_BIT_SHIFT;
+
+	if (vbatreg_reg_code > BQ2561X_VBATREG_THRESH)
+		return ((vbatreg_reg_code - BQ2561X_VBATREG_THRESH) *
+					BQ2561X_VBATREG_STEP_uV) +
+					BQ25611D_VBATREG_THRESH_uV;
+
+	return bq25611d_vbatreg_values[vbatreg_reg_code];
+}
+
+static int bq2560x_get_chrg_volt(struct bq256xx_device *bq)
+{
+	unsigned int battery_volt_lim;
+	unsigned int vbatreg_reg_code;
+	int ret;
+
+	ret = regmap_read(bq->regmap, BQ256XX_BATTERY_VOLTAGE_LIMIT,
+							&battery_volt_lim);
+	if (ret)
+		return ret;
+
+	vbatreg_reg_code = (battery_volt_lim & BQ256XX_VBATREG_MASK) >>
+						BQ256XX_VBATREG_BIT_SHIFT;
+
+	return (vbatreg_reg_code * BQ2560X_VBATREG_STEP_uV)
+					+ BQ2560X_VBATREG_OFFSET_uV;
+}
+
+static int bq25601d_get_chrg_volt(struct bq256xx_device *bq)
+{
+	unsigned int battery_volt_lim;
+	unsigned int vbatreg_reg_code;
+	int ret;
+
+	ret = regmap_read(bq->regmap, BQ256XX_BATTERY_VOLTAGE_LIMIT,
+							&battery_volt_lim);
+	if (ret)
+		return ret;
+
+	vbatreg_reg_code = (battery_volt_lim & BQ256XX_VBATREG_MASK) >>
+						BQ256XX_VBATREG_BIT_SHIFT;
+
+	return (vbatreg_reg_code * BQ2560X_VBATREG_STEP_uV)
+					+ BQ25601D_VBATREG_OFFSET_uV;
+}
+
+static int bq25618_619_set_chrg_volt(struct bq256xx_device *bq, int vbatreg)
+{
+	int array_size = ARRAY_SIZE(bq25618_619_vbatreg_values);
+	unsigned int vbatreg_reg_code;
+
+	if (vbatreg < BQ25618_VBATREG_MIN_uV)
+		vbatreg = BQ25618_VBATREG_MIN_uV;
+	else if (vbatreg > bq->init_data.vbatreg_max)
+		vbatreg = bq->init_data.vbatreg_max;
+
+	if (vbatreg > BQ25618_VBATREG_THRESH_uV)
+		vbatreg_reg_code = ((vbatreg -
+		BQ25618_VBATREG_THRESH_uV) /
+		(BQ2561X_VBATREG_STEP_uV)) + BQ2561X_VBATREG_THRESH;
+	else {
+		vbatreg_reg_code = bq256xx_array_parse(array_size, vbatreg,
+						bq25618_619_vbatreg_values);
+	}
+
+	return regmap_update_bits(bq->regmap, BQ256XX_BATTERY_VOLTAGE_LIMIT,
+				BQ256XX_VBATREG_MASK, vbatreg_reg_code <<
+						BQ256XX_VBATREG_BIT_SHIFT);
+}
+
+static int bq25611d_set_chrg_volt(struct bq256xx_device *bq, int vbatreg)
+{
+	int array_size = ARRAY_SIZE(bq25611d_vbatreg_values);
+	unsigned int vbatreg_reg_code;
+
+	if (vbatreg < BQ25611D_VBATREG_MIN_uV)
+		vbatreg = BQ25611D_VBATREG_MIN_uV;
+	else if (vbatreg > bq->init_data.vbatreg_max)
+		vbatreg = bq->init_data.vbatreg_max;
+
+	if (vbatreg > BQ25611D_VBATREG_THRESH_uV)
+		vbatreg_reg_code = ((vbatreg -
+		BQ25611D_VBATREG_THRESH_uV) /
+		(BQ2561X_VBATREG_STEP_uV)) + BQ2561X_VBATREG_THRESH;
+	else {
+		vbatreg_reg_code = bq256xx_array_parse(array_size, vbatreg,
+						bq25611d_vbatreg_values);
+	}
+
+	return regmap_update_bits(bq->regmap, BQ256XX_BATTERY_VOLTAGE_LIMIT,
+				BQ256XX_VBATREG_MASK, vbatreg_reg_code <<
+						BQ256XX_VBATREG_BIT_SHIFT);
+}
+
+static int bq2560x_set_chrg_volt(struct bq256xx_device *bq, int vbatreg)
+{
+	unsigned int vbatreg_reg_code;
+
+	if (vbatreg < BQ2560X_VBATREG_MIN_uV)
+		vbatreg = BQ2560X_VBATREG_MIN_uV;
+	else if (vbatreg > bq->init_data.vbatreg_max)
+		vbatreg = bq->init_data.vbatreg_max;
+
+	vbatreg_reg_code = (vbatreg - BQ2560X_VBATREG_OFFSET_uV) /
+						BQ2560X_VBATREG_STEP_uV;
+
+	return regmap_update_bits(bq->regmap, BQ256XX_BATTERY_VOLTAGE_LIMIT,
+				BQ256XX_VBATREG_MASK, vbatreg_reg_code <<
+						BQ256XX_VBATREG_BIT_SHIFT);
+}
+
+static int bq25601d_set_chrg_volt(struct bq256xx_device *bq, int vbatreg)
+{
+	unsigned int vbatreg_reg_code;
+
+	if (vbatreg < BQ25601D_VBATREG_MIN_uV)
+		vbatreg = BQ25601D_VBATREG_MIN_uV;
+	else if (vbatreg > bq->init_data.vbatreg_max)
+		vbatreg = bq->init_data.vbatreg_max;
+
+	vbatreg_reg_code = (vbatreg - BQ25601D_VBATREG_OFFSET_uV) /
+						BQ2560X_VBATREG_STEP_uV;
+
+	return regmap_update_bits(bq->regmap, BQ256XX_BATTERY_VOLTAGE_LIMIT,
+				BQ256XX_VBATREG_MASK, vbatreg_reg_code <<
+						BQ256XX_VBATREG_BIT_SHIFT);
+}
+
+static int bq256xx_get_prechrg_curr(struct bq256xx_device *bq)
+{
+	unsigned int prechg_and_term_curr_lim;
+	unsigned int iprechg_reg_code;
+	int ret;
+
+	ret = regmap_read(bq->regmap, BQ256XX_PRECHG_AND_TERM_CURR_LIM,
+						&prechg_and_term_curr_lim);
+	if (ret)
+		return ret;
+
+	iprechg_reg_code = (prechg_and_term_curr_lim & BQ256XX_IPRECHG_MASK)
+						>> BQ256XX_IPRECHG_BIT_SHIFT;
+
+	return (iprechg_reg_code * BQ256XX_IPRECHG_STEP_uA) +
+						BQ256XX_IPRECHG_OFFSET_uA;
+}
+
+static int bq256xx_set_prechrg_curr(struct bq256xx_device *bq, int iprechg)
+{
+	unsigned int iprechg_reg_code;
+
+	if (iprechg < BQ256XX_IPRECHG_MIN_uA)
+		iprechg = BQ256XX_IPRECHG_MIN_uA;
+	else if (iprechg > BQ256XX_IPRECHG_MAX_uA)
+		iprechg = BQ256XX_IPRECHG_MAX_uA;
+
+	iprechg_reg_code = ((iprechg - BQ256XX_IPRECHG_OFFSET_uA) /
+			BQ256XX_IPRECHG_STEP_uA) << BQ256XX_IPRECHG_BIT_SHIFT;
+
+	return regmap_update_bits(bq->regmap, BQ256XX_PRECHG_AND_TERM_CURR_LIM,
+				BQ256XX_IPRECHG_MASK, iprechg_reg_code);
+}
+
+static int bq25618_619_get_prechrg_curr(struct bq256xx_device *bq)
+{
+	unsigned int prechg_and_term_curr_lim;
+	unsigned int iprechg_reg_code;
+	int ret;
+
+	ret = regmap_read(bq->regmap, BQ256XX_PRECHG_AND_TERM_CURR_LIM,
+						&prechg_and_term_curr_lim);
+	if (ret)
+		return ret;
+
+	iprechg_reg_code = (prechg_and_term_curr_lim & BQ256XX_IPRECHG_MASK)
+						>> BQ256XX_IPRECHG_BIT_SHIFT;
+
+	return (iprechg_reg_code * BQ25618_IPRECHG_STEP_uA) +
+						BQ25618_IPRECHG_OFFSET_uA;
+}
+
+static int bq25618_619_set_prechrg_curr(struct bq256xx_device *bq, int iprechg)
+{
+	unsigned int iprechg_reg_code;
+
+	if (iprechg < BQ25618_IPRECHG_MIN_uA)
+		iprechg = BQ25618_IPRECHG_MIN_uA;
+	else if (iprechg > BQ25618_IPRECHG_MAX_uA)
+		iprechg = BQ25618_IPRECHG_MAX_uA;
+
+	iprechg_reg_code = ((iprechg - BQ25618_IPRECHG_OFFSET_uA) /
+			BQ25618_IPRECHG_STEP_uA) << BQ256XX_IPRECHG_BIT_SHIFT;
+
+	return regmap_update_bits(bq->regmap, BQ256XX_PRECHG_AND_TERM_CURR_LIM,
+				BQ256XX_IPRECHG_MASK, iprechg_reg_code);
+}
+
+static int bq256xx_get_term_curr(struct bq256xx_device *bq)
+{
+	unsigned int prechg_and_term_curr_lim;
+	unsigned int iterm_reg_code;
+	int ret;
+
+	ret = regmap_read(bq->regmap, BQ256XX_PRECHG_AND_TERM_CURR_LIM,
+						&prechg_and_term_curr_lim);
+	if (ret)
+		return ret;
+
+	iterm_reg_code = prechg_and_term_curr_lim & BQ256XX_ITERM_MASK;
+
+	return (iterm_reg_code * BQ256XX_ITERM_STEP_uA) +
+						BQ256XX_ITERM_OFFSET_uA;
+}
+
+static int bq256xx_set_term_curr(struct bq256xx_device *bq, int iterm)
+{
+	unsigned int iterm_reg_code;
+
+	if (iterm < BQ256XX_ITERM_MIN_uA)
+		iterm = BQ256XX_ITERM_MIN_uA;
+	else if (iterm > BQ256XX_ITERM_MAX_uA)
+		iterm = BQ256XX_ITERM_MAX_uA;
+
+	iterm_reg_code = (iterm - BQ256XX_ITERM_OFFSET_uA) /
+							BQ256XX_ITERM_STEP_uA;
+
+	return regmap_update_bits(bq->regmap, BQ256XX_PRECHG_AND_TERM_CURR_LIM,
+				BQ256XX_ITERM_MASK, iterm_reg_code);
+}
+
+static int bq25618_619_get_term_curr(struct bq256xx_device *bq)
+{
+	unsigned int prechg_and_term_curr_lim;
+	unsigned int iterm_reg_code;
+	int ret;
+
+	ret = regmap_read(bq->regmap, BQ256XX_PRECHG_AND_TERM_CURR_LIM,
+						&prechg_and_term_curr_lim);
+	if (ret)
+		return ret;
+
+	iterm_reg_code = prechg_and_term_curr_lim & BQ256XX_ITERM_MASK;
+
+	return (iterm_reg_code * BQ25618_ITERM_STEP_uA) +
+						BQ25618_ITERM_OFFSET_uA;
+}
+
+static int bq25618_619_set_term_curr(struct bq256xx_device *bq, int iterm)
+{
+	unsigned int iterm_reg_code;
+
+	if (iterm < BQ25618_ITERM_MIN_uA)
+		iterm = BQ25618_ITERM_MIN_uA;
+	else if (iterm > BQ25618_ITERM_MAX_uA)
+		iterm = BQ25618_ITERM_MAX_uA;
+
+	iterm_reg_code = (iterm - BQ25618_ITERM_OFFSET_uA) /
+							BQ25618_ITERM_STEP_uA;
+
+	return regmap_update_bits(bq->regmap, BQ256XX_PRECHG_AND_TERM_CURR_LIM,
+				BQ256XX_ITERM_MASK, iterm_reg_code);
+}
+
+static int bq256xx_get_input_volt_lim(struct bq256xx_device *bq)
+{
+	unsigned int charger_control_2;
+	unsigned int vindpm_reg_code;
+	int ret;
+
+	ret = regmap_read(bq->regmap, BQ256XX_CHARGER_CONTROL_2,
+						&charger_control_2);
+	if (ret)
+		return ret;
+
+	vindpm_reg_code = charger_control_2 & BQ256XX_VINDPM_MASK;
+
+	return (vindpm_reg_code * BQ256XX_VINDPM_STEP_uV) +
+						BQ256XX_VINDPM_OFFSET_uV;
+}
+
+static int bq256xx_set_input_volt_lim(struct bq256xx_device *bq, int vindpm)
+{
+	unsigned int vindpm_reg_code;
+
+	if (vindpm < BQ256XX_VINDPM_MIN_uV)
+		vindpm = BQ256XX_VINDPM_MIN_uV;
+	else if (vindpm > BQ256XX_VINDPM_MAX_uV)
+		vindpm = BQ256XX_VINDPM_MAX_uV;
+
+	vindpm_reg_code = (vindpm - BQ256XX_VINDPM_OFFSET_uV) /
+							BQ256XX_VINDPM_STEP_uV;
+
+	return regmap_update_bits(bq->regmap, BQ256XX_CHARGER_CONTROL_2,
+					BQ256XX_VINDPM_MASK, vindpm_reg_code);
+}
+
+static int bq256xx_get_input_curr_lim(struct bq256xx_device *bq)
+{
+	unsigned int input_current_limit;
+	unsigned int iindpm_reg_code;
+	int ret;
+
+	ret = regmap_read(bq->regmap, BQ256XX_INPUT_CURRENT_LIMIT,
+						&input_current_limit);
+	if (ret)
+		return ret;
+
+	iindpm_reg_code = input_current_limit & BQ256XX_IINDPM_MASK;
+
+	return (iindpm_reg_code * BQ256XX_IINDPM_STEP_uA) +
+						BQ256XX_IINDPM_OFFSET_uA;
+}
+
+static int bq256xx_set_input_curr_lim(struct bq256xx_device *bq, int iindpm)
+{
+	unsigned int iindpm_reg_code;
+
+	if (iindpm < BQ256XX_IINDPM_MIN_uA)
+		iindpm = BQ256XX_IINDPM_MIN_uA;
+	else if (iindpm > BQ256XX_IINDPM_MAX_uA)
+		iindpm = BQ256XX_IINDPM_MAX_uA;
+
+	iindpm_reg_code = (iindpm - BQ256XX_IINDPM_OFFSET_uA) /
+							BQ256XX_IINDPM_STEP_uA;
+
+	return regmap_update_bits(bq->regmap, BQ256XX_INPUT_CURRENT_LIMIT,
+					BQ256XX_IINDPM_MASK, iindpm_reg_code);
+}
+
+static void bq256xx_charger_reset(void *data)
+{
+	struct bq256xx_device *bq = data;
+
+	regmap_update_bits(bq->regmap, BQ256XX_PART_INFORMATION,
+					BQ256XX_REG_RST, BQ256XX_REG_RST);
+}
+
+static int bq256xx_set_charger_property(struct power_supply *psy,
+		enum power_supply_property prop,
+		const union power_supply_propval *val)
+{
+	struct bq256xx_device *bq = power_supply_get_drvdata(psy);
+	int ret = -EINVAL;
+
+	switch (prop) {
+	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+		ret = bq->chip_info->bq256xx_set_iindpm(bq, val->intval);
+		if (ret)
+			return ret;
+		break;
+
+	case POWER_SUPPLY_PROP_STATUS:
+		break;
+
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+		ret = bq->chip_info->bq256xx_set_vbatreg(bq, val->intval);
+		if (ret)
+			return ret;
+		break;
+
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+		ret = bq->chip_info->bq256xx_set_ichg(bq, val->intval);
+		if (ret)
+			return ret;
+		break;
+
+	case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
+		ret = bq->chip_info->bq256xx_set_iprechg(bq, val->intval);
+		if (ret)
+			return ret;
+		break;
+
+	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+		ret = bq->chip_info->bq256xx_set_iterm(bq, val->intval);
+		if (ret)
+			return ret;
+		break;
+
+	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
+		ret = bq->chip_info->bq256xx_set_vindpm(bq, val->intval);
+		if (ret)
+			return ret;
+		break;
+
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+
+static int bq256xx_get_battery_property(struct power_supply *psy,
+				enum power_supply_property psp,
+				union power_supply_propval *val)
+{
+	struct bq256xx_device *bq = power_supply_get_drvdata(psy);
+
+	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.vbatreg_max;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int bq256xx_get_charger_property(struct power_supply *psy,
+				enum power_supply_property psp,
+				union power_supply_propval *val)
+{
+	struct bq256xx_device *bq = power_supply_get_drvdata(psy);
+	struct bq256xx_state state;
+	int ret = 0;
+
+	mutex_lock(&bq->lock);
+	ret = bq256xx_get_state(bq, &state);
+	mutex_unlock(&bq->lock);
+	if (ret)
+		return ret;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		if (state.vbus_stat == BQ256XX_VBUS_STAT_NO_INPUT ||
+		    state.vbus_stat == BQ256XX_VBUS_STAT_USB_OTG)
+			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+		else if (state.chrg_stat == BQ256XX_CHRG_STAT_NOT_CHRGING)
+			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+		else if (state.chrg_stat == BQ256XX_CHRG_STAT_CHRG_TERM)
+			val->intval = POWER_SUPPLY_STATUS_FULL;
+		else
+			val->intval = POWER_SUPPLY_STATUS_CHARGING;
+		break;
+
+	case POWER_SUPPLY_PROP_HEALTH:
+		val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
+		if (state.wdt_fault) {
+			val->intval =
+				POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE;
+		} else if (state.bat_fault) {
+			val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+		} else {
+			switch (state.chrg_stat) {
+			case BQ256XX_CHRG_FAULT_INPUT:
+				val->intval =
+					POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+				break;
+			case BQ256XX_CHRG_FAULT_THERM:
+				val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+				break;
+			case BQ256XX_CHRG_FAULT_CST_EXPIRE:
+				val->intval =
+				POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+				break;
+			default:
+				break;
+			}
+
+			switch (state.ntc_fault) {
+			case BQ256XX_NTC_FAULT_WARM:
+				val->intval = POWER_SUPPLY_HEALTH_WARM;
+				break;
+			case BQ256XX_NTC_FAULT_COOL:
+				val->intval = POWER_SUPPLY_HEALTH_COOL;
+				break;
+			case BQ256XX_NTC_FAULT_COLD:
+				val->intval = POWER_SUPPLY_HEALTH_COLD;
+				break;
+			case BQ256XX_NTC_FAULT_HOT:
+				val->intval = POWER_SUPPLY_HEALTH_HOT;
+				break;
+			default:
+				val->intval = POWER_SUPPLY_HEALTH_GOOD;
+				break;
+			}
+		}
+		break;
+
+	case POWER_SUPPLY_PROP_USB_TYPE:
+		if (bq->chip_info->has_usb_detect) {
+			switch (state.vbus_stat) {
+			case BQ256XX_VBUS_STAT_USB_SDP:
+				val->intval = POWER_SUPPLY_USB_TYPE_SDP;
+				break;
+			case BQ256XX_VBUS_STAT_USB_CDP:
+				val->intval = POWER_SUPPLY_USB_TYPE_CDP;
+				break;
+			case BQ256XX_VBUS_STAT_USB_DCP:
+				val->intval = POWER_SUPPLY_USB_TYPE_DCP;
+				break;
+			case BQ256XX_VBUS_STAT_USB_OTG:
+				val->intval = POWER_SUPPLY_USB_TYPE_ACA;
+				break;
+			default:
+				val->intval = POWER_SUPPLY_USB_TYPE_UNKNOWN;
+				break;
+			}
+		} else {
+			switch (state.vbus_stat) {
+			case BQ256XX_VBUS_STAT_USB_SDP:
+				val->intval = POWER_SUPPLY_USB_TYPE_SDP;
+				break;
+			case BQ256XX_VBUS_STAT_USB_OTG:
+				val->intval = POWER_SUPPLY_USB_TYPE_ACA;
+				break;
+			default:
+				val->intval = POWER_SUPPLY_USB_TYPE_UNKNOWN;
+				break;
+			}
+		}
+		break;
+
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		switch (state.chrg_stat) {
+		case BQ256XX_CHRG_STAT_NOT_CHRGING:
+			val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+			break;
+		case BQ256XX_CHRG_STAT_PRECHRGING:
+			val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+			break;
+		case BQ256XX_CHRG_STAT_FAST_CHRGING:
+			val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
+			break;
+		case BQ256XX_CHRG_STAT_CHRG_TERM:
+			val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+			break;
+		default:
+			val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+		}
+		break;
+
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		val->strval = BQ256XX_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 = bq->chip_info->bq256xx_get_vindpm(bq);
+		if (ret < 0)
+			return ret;
+		val->intval = ret;
+		break;
+
+	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+		ret = bq->chip_info->bq256xx_get_iindpm(bq);
+		if (ret < 0)
+			return ret;
+		val->intval = ret;
+		break;
+
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+		ret = bq->chip_info->bq256xx_get_vbatreg(bq);
+		if (ret < 0)
+			return ret;
+		val->intval = ret;
+		break;
+
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+		ret = bq->chip_info->bq256xx_get_ichg(bq);
+		if (ret < 0)
+			return ret;
+		val->intval = ret;
+		break;
+
+	case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
+		ret = bq->chip_info->bq256xx_get_iprechg(bq);
+		if (ret < 0)
+			return ret;
+		val->intval = ret;
+		break;
+
+	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+		ret = bq->chip_info->bq256xx_get_iterm(bq);
+		if (ret < 0)
+			return ret;
+		val->intval = ret;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static bool bq256xx_state_changed(struct bq256xx_device *bq,
+				  struct bq256xx_state *new_state)
+{
+	struct bq256xx_state old_state;
+
+	mutex_lock(&bq->lock);
+	old_state = bq->state;
+	mutex_unlock(&bq->lock);
+
+	return (old_state.vbus_stat != new_state->vbus_stat ||
+		old_state.chrg_stat != new_state->chrg_stat ||
+		old_state.online != new_state->online ||
+		old_state.wdt_fault != new_state->wdt_fault ||
+		old_state.bat_fault != new_state->bat_fault ||
+		old_state.chrg_fault != new_state->chrg_fault ||
+		old_state.ntc_fault != new_state->ntc_fault);
+}
+
+static irqreturn_t bq256xx_irq_handler_thread(int irq, void *private)
+{
+	struct bq256xx_device *bq = private;
+	struct bq256xx_state state;
+	int ret;
+
+	ret = bq256xx_get_state(bq, &state);
+	if (ret < 0)
+		goto irq_out;
+
+	if (!bq256xx_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 bq256xx_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_USB_TYPE,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+	POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
+	POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
+};
+
+static enum power_supply_property bq256xx_battery_props[] = {
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+};
+
+static char *bq256xx_charger_supplied_to[] = {
+	"main-battery",
+};
+
+static int bq256xx_property_is_writeable(struct power_supply *psy,
+					 enum power_supply_property prop)
+{
+	switch (prop) {
+	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+	case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
+	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+	case POWER_SUPPLY_PROP_STATUS:
+	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct power_supply_desc bq256xx_power_supply_desc = {
+	.name = "bq256xx-charger",
+	.type = POWER_SUPPLY_TYPE_USB,
+	.usb_types = bq256xx_usb_type,
+	.num_usb_types = ARRAY_SIZE(bq256xx_usb_type),
+	.properties = bq256xx_power_supply_props,
+	.num_properties = ARRAY_SIZE(bq256xx_power_supply_props),
+	.get_property = bq256xx_get_charger_property,
+	.set_property = bq256xx_set_charger_property,
+	.property_is_writeable = bq256xx_property_is_writeable,
+};
+
+static struct power_supply_desc bq256xx_battery_desc = {
+	.name			= "bq256xx-battery",
+	.type			= POWER_SUPPLY_TYPE_BATTERY,
+	.get_property		= bq256xx_get_battery_property,
+	.properties		= bq256xx_battery_props,
+	.num_properties		= ARRAY_SIZE(bq256xx_battery_props),
+	.property_is_writeable	= bq256xx_property_is_writeable,
+};
+
+
+static bool bq256xx_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case BQ256XX_INPUT_CURRENT_LIMIT:
+	case BQ256XX_CHARGER_STATUS_0...BQ256XX_CHARGER_STATUS_2:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config bq25600_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = BQ256XX_PART_INFORMATION,
+	.reg_defaults	= bq2560x_reg_defs,
+	.num_reg_defaults = ARRAY_SIZE(bq2560x_reg_defs),
+	.cache_type = REGCACHE_RBTREE,
+	.volatile_reg = bq256xx_is_volatile_reg,
+};
+
+static const struct regmap_config bq25611d_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = BQ256XX_CHARGER_CONTROL_4,
+	.reg_defaults	= bq25611d_reg_defs,
+	.num_reg_defaults = ARRAY_SIZE(bq25611d_reg_defs),
+	.cache_type = REGCACHE_RBTREE,
+	.volatile_reg = bq256xx_is_volatile_reg,
+};
+
+static const struct regmap_config bq25618_619_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = BQ256XX_CHARGER_CONTROL_4,
+	.reg_defaults	= bq25618_619_reg_defs,
+	.num_reg_defaults = ARRAY_SIZE(bq25618_619_reg_defs),
+	.cache_type = REGCACHE_RBTREE,
+	.volatile_reg = bq256xx_is_volatile_reg,
+};
+
+static const struct bq256xx_chip_info bq256xx_chip_info_tbl[] = {
+	[BQ25600] = {
+		.model_id = BQ25600,
+		.bq256xx_regmap_config = &bq25600_regmap_config,
+		.bq256xx_get_ichg = bq256xx_get_ichg_curr,
+		.bq256xx_get_iindpm = bq256xx_get_input_curr_lim,
+		.bq256xx_get_vbatreg = bq2560x_get_chrg_volt,
+		.bq256xx_get_iterm = bq256xx_get_term_curr,
+		.bq256xx_get_iprechg = bq256xx_get_prechrg_curr,
+		.bq256xx_get_vindpm = bq256xx_get_input_volt_lim,
+
+		.bq256xx_set_ichg = bq256xx_set_ichg_curr,
+		.bq256xx_set_iindpm = bq256xx_set_input_curr_lim,
+		.bq256xx_set_vbatreg = bq2560x_set_chrg_volt,
+		.bq256xx_set_iterm = bq256xx_set_term_curr,
+		.bq256xx_set_iprechg = bq256xx_set_prechrg_curr,
+		.bq256xx_set_vindpm = bq256xx_set_input_volt_lim,
+
+		.bq256xx_def_ichg = BQ2560X_ICHG_DEF_uA,
+		.bq256xx_def_iindpm = BQ256XX_IINDPM_DEF_uA,
+		.bq256xx_def_vbatreg = BQ2560X_VBATREG_DEF_uV,
+		.bq256xx_def_iterm = BQ256XX_ITERM_DEF_uA,
+		.bq256xx_def_iprechg = BQ256XX_IPRECHG_DEF_uA,
+		.bq256xx_def_vindpm = BQ256XX_VINDPM_DEF_uV,
+
+		.bq256xx_max_ichg = BQ256XX_ICHG_MAX_uA,
+		.bq256xx_max_vbatreg = BQ2560X_VBATREG_MAX_uV,
+
+		.has_usb_detect = false,
+	},
+
+	[BQ25600D] = {
+		.model_id = BQ25600D,
+		.bq256xx_regmap_config = &bq25600_regmap_config,
+		.bq256xx_get_ichg = bq256xx_get_ichg_curr,
+		.bq256xx_get_iindpm = bq256xx_get_input_curr_lim,
+		.bq256xx_get_vbatreg = bq2560x_get_chrg_volt,
+		.bq256xx_get_iterm = bq256xx_get_term_curr,
+		.bq256xx_get_iprechg = bq256xx_get_prechrg_curr,
+		.bq256xx_get_vindpm = bq256xx_get_input_volt_lim,
+
+		.bq256xx_set_ichg = bq256xx_set_ichg_curr,
+		.bq256xx_set_iindpm = bq256xx_set_input_curr_lim,
+		.bq256xx_set_vbatreg = bq2560x_set_chrg_volt,
+		.bq256xx_set_iterm = bq256xx_set_term_curr,
+		.bq256xx_set_iprechg = bq256xx_set_prechrg_curr,
+		.bq256xx_set_vindpm = bq256xx_set_input_volt_lim,
+
+		.bq256xx_def_ichg = BQ2560X_ICHG_DEF_uA,
+		.bq256xx_def_iindpm = BQ256XX_IINDPM_DEF_uA,
+		.bq256xx_def_vbatreg = BQ2560X_VBATREG_DEF_uV,
+		.bq256xx_def_iterm = BQ256XX_ITERM_DEF_uA,
+		.bq256xx_def_iprechg = BQ256XX_IPRECHG_DEF_uA,
+		.bq256xx_def_vindpm = BQ256XX_VINDPM_DEF_uV,
+
+		.bq256xx_max_ichg = BQ256XX_ICHG_MAX_uA,
+		.bq256xx_max_vbatreg = BQ2560X_VBATREG_MAX_uV,
+
+		.has_usb_detect = true,
+	},
+
+	[BQ25601] = {
+		.model_id = BQ25601,
+		.bq256xx_regmap_config = &bq25600_regmap_config,
+		.bq256xx_get_ichg = bq256xx_get_ichg_curr,
+		.bq256xx_get_iindpm = bq256xx_get_input_curr_lim,
+		.bq256xx_get_vbatreg = bq2560x_get_chrg_volt,
+		.bq256xx_get_iterm = bq256xx_get_term_curr,
+		.bq256xx_get_iprechg = bq256xx_get_prechrg_curr,
+		.bq256xx_get_vindpm = bq256xx_get_input_volt_lim,
+
+		.bq256xx_set_ichg = bq256xx_set_ichg_curr,
+		.bq256xx_set_iindpm = bq256xx_set_input_curr_lim,
+		.bq256xx_set_vbatreg = bq2560x_set_chrg_volt,
+		.bq256xx_set_iterm = bq256xx_set_term_curr,
+		.bq256xx_set_iprechg = bq256xx_set_prechrg_curr,
+		.bq256xx_set_vindpm = bq256xx_set_input_volt_lim,
+
+		.bq256xx_def_ichg = BQ2560X_ICHG_DEF_uA,
+		.bq256xx_def_iindpm = BQ256XX_IINDPM_DEF_uA,
+		.bq256xx_def_vbatreg = BQ2560X_VBATREG_DEF_uV,
+		.bq256xx_def_iterm = BQ256XX_ITERM_DEF_uA,
+		.bq256xx_def_iprechg = BQ256XX_IPRECHG_DEF_uA,
+		.bq256xx_def_vindpm = BQ256XX_VINDPM_DEF_uV,
+
+		.bq256xx_max_ichg = BQ256XX_ICHG_MAX_uA,
+		.bq256xx_max_vbatreg = BQ2560X_VBATREG_MAX_uV,
+
+		.has_usb_detect = false,
+	},
+
+	[BQ25601D] = {
+		.model_id = BQ25601D,
+		.bq256xx_regmap_config = &bq25600_regmap_config,
+		.bq256xx_get_ichg = bq256xx_get_ichg_curr,
+		.bq256xx_get_iindpm = bq256xx_get_input_curr_lim,
+		.bq256xx_get_vbatreg = bq25601d_get_chrg_volt,
+		.bq256xx_get_iterm = bq256xx_get_term_curr,
+		.bq256xx_get_iprechg = bq256xx_get_prechrg_curr,
+		.bq256xx_get_vindpm = bq256xx_get_input_volt_lim,
+
+		.bq256xx_set_ichg = bq256xx_set_ichg_curr,
+		.bq256xx_set_iindpm = bq256xx_set_input_curr_lim,
+		.bq256xx_set_vbatreg = bq25601d_set_chrg_volt,
+		.bq256xx_set_iterm = bq256xx_set_term_curr,
+		.bq256xx_set_iprechg = bq256xx_set_prechrg_curr,
+		.bq256xx_set_vindpm = bq256xx_set_input_volt_lim,
+
+		.bq256xx_def_ichg = BQ2560X_ICHG_DEF_uA,
+		.bq256xx_def_iindpm = BQ256XX_IINDPM_DEF_uA,
+		.bq256xx_def_vbatreg = BQ2560X_VBATREG_DEF_uV,
+		.bq256xx_def_iterm = BQ256XX_ITERM_DEF_uA,
+		.bq256xx_def_iprechg = BQ256XX_IPRECHG_DEF_uA,
+		.bq256xx_def_vindpm = BQ256XX_VINDPM_DEF_uV,
+
+		.bq256xx_max_ichg = BQ256XX_ICHG_MAX_uA,
+		.bq256xx_max_vbatreg = BQ2560X_VBATREG_MAX_uV,
+
+		.has_usb_detect = true,
+	},
+
+	[BQ25611D] = {
+		.model_id = BQ25611D,
+		.bq256xx_regmap_config = &bq25611d_regmap_config,
+		.bq256xx_get_ichg = bq256xx_get_ichg_curr,
+		.bq256xx_get_iindpm = bq256xx_get_input_curr_lim,
+		.bq256xx_get_vbatreg = bq25611d_get_chrg_volt,
+		.bq256xx_get_iterm = bq256xx_get_term_curr,
+		.bq256xx_get_iprechg = bq256xx_get_prechrg_curr,
+		.bq256xx_get_vindpm = bq256xx_get_input_volt_lim,
+
+		.bq256xx_set_ichg = bq256xx_set_ichg_curr,
+		.bq256xx_set_iindpm = bq256xx_set_input_curr_lim,
+		.bq256xx_set_vbatreg = bq25611d_set_chrg_volt,
+		.bq256xx_set_iterm = bq256xx_set_term_curr,
+		.bq256xx_set_iprechg = bq256xx_set_prechrg_curr,
+		.bq256xx_set_vindpm = bq256xx_set_input_volt_lim,
+
+		.bq256xx_def_ichg = BQ25611D_ICHG_DEF_uA,
+		.bq256xx_def_iindpm = BQ256XX_IINDPM_DEF_uA,
+		.bq256xx_def_vbatreg = BQ25611D_VBATREG_DEF_uV,
+		.bq256xx_def_iterm = BQ256XX_ITERM_DEF_uA,
+		.bq256xx_def_iprechg = BQ256XX_IPRECHG_DEF_uA,
+		.bq256xx_def_vindpm = BQ256XX_VINDPM_DEF_uV,
+
+		.bq256xx_max_ichg = BQ256XX_ICHG_MAX_uA,
+		.bq256xx_max_vbatreg = BQ25611D_VBATREG_MAX_uV,
+
+		.has_usb_detect = true,
+	},
+
+	[BQ25618] = {
+		.model_id = BQ25618,
+		.bq256xx_regmap_config = &bq25618_619_regmap_config,
+		.bq256xx_get_ichg = bq25618_619_get_ichg_curr,
+		.bq256xx_get_iindpm = bq256xx_get_input_curr_lim,
+		.bq256xx_get_vbatreg = bq25618_619_get_chrg_volt,
+		.bq256xx_get_iterm = bq25618_619_get_term_curr,
+		.bq256xx_get_iprechg = bq25618_619_get_prechrg_curr,
+		.bq256xx_get_vindpm = bq256xx_get_input_volt_lim,
+
+		.bq256xx_set_ichg = bq25618_619_set_ichg_curr,
+		.bq256xx_set_iindpm = bq256xx_set_input_curr_lim,
+		.bq256xx_set_vbatreg = bq25618_619_set_chrg_volt,
+		.bq256xx_set_iterm = bq25618_619_set_term_curr,
+		.bq256xx_set_iprechg = bq25618_619_set_prechrg_curr,
+		.bq256xx_set_vindpm = bq256xx_set_input_volt_lim,
+
+		.bq256xx_def_ichg = BQ25618_ICHG_DEF_uA,
+		.bq256xx_def_iindpm = BQ256XX_IINDPM_DEF_uA,
+		.bq256xx_def_vbatreg = BQ25618_VBATREG_DEF_uV,
+		.bq256xx_def_iterm = BQ25618_ITERM_DEF_uA,
+		.bq256xx_def_iprechg = BQ25618_IPRECHG_DEF_uA,
+		.bq256xx_def_vindpm = BQ256XX_VINDPM_DEF_uV,
+
+		.bq256xx_max_ichg = BQ25618_ICHG_MAX_uA,
+		.bq256xx_max_vbatreg = BQ25618_VBATREG_MAX_uV,
+
+		.has_usb_detect = false,
+	},
+
+	[BQ25619] = {
+		.model_id = BQ25619,
+		.bq256xx_regmap_config = &bq25618_619_regmap_config,
+		.bq256xx_get_ichg = bq25618_619_get_ichg_curr,
+		.bq256xx_get_iindpm = bq256xx_get_input_curr_lim,
+		.bq256xx_get_vbatreg = bq25618_619_get_chrg_volt,
+		.bq256xx_get_iterm = bq25618_619_get_term_curr,
+		.bq256xx_get_iprechg = bq25618_619_get_prechrg_curr,
+		.bq256xx_get_vindpm = bq256xx_get_input_volt_lim,
+
+		.bq256xx_set_ichg = bq25618_619_set_ichg_curr,
+		.bq256xx_set_iindpm = bq256xx_set_input_curr_lim,
+		.bq256xx_set_vbatreg = bq25618_619_set_chrg_volt,
+		.bq256xx_set_iterm = bq25618_619_set_term_curr,
+		.bq256xx_set_iprechg = bq25618_619_set_prechrg_curr,
+		.bq256xx_set_vindpm = bq256xx_set_input_volt_lim,
+
+		.bq256xx_def_ichg = BQ25618_ICHG_DEF_uA,
+		.bq256xx_def_iindpm = BQ256XX_IINDPM_DEF_uA,
+		.bq256xx_def_vbatreg = BQ25618_VBATREG_DEF_uV,
+		.bq256xx_def_iterm = BQ25618_ITERM_DEF_uA,
+		.bq256xx_def_iprechg = BQ25618_IPRECHG_DEF_uA,
+		.bq256xx_def_vindpm = BQ256XX_VINDPM_DEF_uV,
+
+		.bq256xx_max_ichg = BQ25618_ICHG_MAX_uA,
+		.bq256xx_max_vbatreg = BQ25618_VBATREG_MAX_uV,
+
+		.has_usb_detect = false,
+	},
+};
+
+static int bq256xx_power_supply_init(struct bq256xx_device *bq,
+							struct device *dev)
+{
+	struct power_supply_config psy_cfg = { .drv_data = bq,
+						.of_node = dev->of_node, };
+
+	psy_cfg.supplied_to = bq256xx_charger_supplied_to;
+	psy_cfg.num_supplicants = ARRAY_SIZE(bq256xx_charger_supplied_to);
+
+	bq->charger = devm_power_supply_register(bq->dev,
+						 &bq256xx_power_supply_desc,
+						 &psy_cfg);
+	if (IS_ERR(bq->charger))
+		return -EINVAL;
+
+	bq->battery = devm_power_supply_register(bq->dev,
+						      &bq256xx_battery_desc,
+						      &psy_cfg);
+	if (IS_ERR(bq->battery))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int bq256xx_hw_init(struct bq256xx_device *bq)
+{
+	struct power_supply_battery_info bat_info = { };
+	int wd_reg_val = BQ256XX_WATCHDOG_DIS;
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < BQ256XX_NUM_WD_VAL; i++) {
+		if (bq->watchdog_timer > bq256xx_watchdog_time[i] &&
+		    bq->watchdog_timer < bq256xx_watchdog_time[i + 1])
+			wd_reg_val = i;
+	}
+	ret = regmap_update_bits(bq->regmap, BQ256XX_CHARGER_CONTROL_1,
+				 BQ256XX_WATCHDOG_MASK, wd_reg_val <<
+						BQ256XX_WDT_BIT_SHIFT);
+
+	ret = power_supply_get_battery_info(bq->charger, &bat_info);
+	if (ret) {
+		dev_warn(bq->dev, "battery info missing, default values will be applied\n");
+
+		bat_info.constant_charge_current_max_ua =
+				bq->chip_info->bq256xx_def_ichg;
+
+		bat_info.constant_charge_voltage_max_uv =
+				bq->chip_info->bq256xx_def_vbatreg;
+
+		bat_info.precharge_current_ua =
+				bq->chip_info->bq256xx_def_iprechg;
+
+		bat_info.charge_term_current_ua =
+				bq->chip_info->bq256xx_def_iterm;
+
+		bq->init_data.ichg_max =
+				bq->chip_info->bq256xx_max_ichg;
+
+		bq->init_data.vbatreg_max =
+				bq->chip_info->bq256xx_max_vbatreg;
+	} else {
+		bq->init_data.ichg_max =
+			bat_info.constant_charge_current_max_ua;
+
+		bq->init_data.vbatreg_max =
+			bat_info.constant_charge_voltage_max_uv;
+	}
+
+	ret = bq->chip_info->bq256xx_set_vindpm(bq, bq->init_data.vindpm);
+	if (ret)
+		goto err_out;
+
+	ret = bq->chip_info->bq256xx_set_iindpm(bq, bq->init_data.iindpm);
+	if (ret)
+		goto err_out;
+
+	ret = bq->chip_info->bq256xx_set_ichg(bq,
+				bat_info.constant_charge_current_max_ua);
+	if (ret)
+		goto err_out;
+
+	ret = bq->chip_info->bq256xx_set_iprechg(bq,
+				bat_info.precharge_current_ua);
+	if (ret)
+		goto err_out;
+
+	ret = bq->chip_info->bq256xx_set_vbatreg(bq,
+				bat_info.constant_charge_voltage_max_uv);
+	if (ret)
+		goto err_out;
+
+	ret = bq->chip_info->bq256xx_set_iterm(bq,
+				bat_info.charge_term_current_ua);
+	if (ret)
+		goto err_out;
+
+	power_supply_put_battery_info(bq->charger, &bat_info);
+
+	return 0;
+
+err_out:
+	return ret;
+}
+
+static int bq256xx_parse_dt(struct bq256xx_device *bq)
+{
+	int ret = 0;
+
+	ret = device_property_read_u32(bq->dev, "ti,watchdog-timeout-ms",
+				       &bq->watchdog_timer);
+	if (ret)
+		bq->watchdog_timer = BQ256XX_WATCHDOG_DIS;
+
+	if (bq->watchdog_timer > BQ256XX_WATCHDOG_MAX ||
+	    bq->watchdog_timer < BQ256XX_WATCHDOG_DIS)
+		return -EINVAL;
+
+	ret = device_property_read_u32(bq->dev,
+				       "input-voltage-limit-microvolt",
+				       &bq->init_data.vindpm);
+	if (ret)
+		bq->init_data.vindpm = bq->chip_info->bq256xx_def_vindpm;
+
+	ret = device_property_read_u32(bq->dev,
+				       "input-current-limit-microamp",
+				       &bq->init_data.iindpm);
+	if (ret)
+		bq->init_data.iindpm = bq->chip_info->bq256xx_def_iindpm;
+
+	return 0;
+}
+
+static int bq256xx_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct bq256xx_device *bq;
+	int ret;
+
+	bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL);
+	if (!bq)
+		return -ENOMEM;
+
+	bq->client = client;
+	bq->dev = dev;
+	bq->chip_info = &bq256xx_chip_info_tbl[id->driver_data];
+
+	mutex_init(&bq->lock);
+
+	strncpy(bq->model_name, id->name, I2C_NAME_SIZE);
+
+	bq->regmap = devm_regmap_init_i2c(client,
+					bq->chip_info->bq256xx_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 = bq256xx_parse_dt(bq);
+	if (ret) {
+		dev_err(dev, "Failed to read device tree properties%d\n", ret);
+		return ret;
+	}
+
+	/* OTG reporting */
+	bq->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+	if (!IS_ERR_OR_NULL(bq->usb2_phy)) {
+		INIT_WORK(&bq->usb_work, bq256xx_usb_work);
+		bq->usb_nb.notifier_call = bq256xx_usb_notifier;
+		usb_register_notifier(bq->usb2_phy, &bq->usb_nb);
+	}
+
+	bq->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
+	if (!IS_ERR_OR_NULL(bq->usb3_phy)) {
+		INIT_WORK(&bq->usb_work, bq256xx_usb_work);
+		bq->usb_nb.notifier_call = bq256xx_usb_notifier;
+		usb_register_notifier(bq->usb3_phy, &bq->usb_nb);
+	}
+
+	if (client->irq) {
+		ret = devm_request_threaded_irq(dev, client->irq, NULL,
+						bq256xx_irq_handler_thread,
+						IRQF_TRIGGER_FALLING |
+						IRQF_ONESHOT,
+						dev_name(&client->dev), bq);
+		if (ret)
+			goto error_out;
+	}
+
+	ret = devm_add_action_or_reset(dev, bq256xx_charger_reset, bq);
+	if (ret)
+		goto error_out;
+
+	ret = bq256xx_power_supply_init(bq, dev);
+	if (ret) {
+		dev_err(dev, "Failed to register power supply\n");
+		goto error_out;
+	}
+
+	ret = bq256xx_hw_init(bq);
+	if (ret) {
+		dev_err(dev, "Cannot initialize the chip.\n");
+		goto error_out;
+	}
+
+	return ret;
+
+error_out:
+	if (!IS_ERR_OR_NULL(bq->usb2_phy))
+		usb_unregister_notifier(bq->usb2_phy, &bq->usb_nb);
+
+	if (!IS_ERR_OR_NULL(bq->usb3_phy))
+		usb_unregister_notifier(bq->usb3_phy, &bq->usb_nb);
+	return ret;
+}
+
+static const struct i2c_device_id bq256xx_i2c_ids[] = {
+	{ "bq25600", BQ25600 },
+	{ "bq25600d", BQ25600D },
+	{ "bq25601", BQ25601 },
+	{ "bq25601d", BQ25601D },
+	{ "bq25611d", BQ25611D },
+	{ "bq25618", BQ25618 },
+	{ "bq25619", BQ25619 },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, bq256xx_i2c_ids);
+
+static const struct of_device_id bq256xx_of_match[] = {
+	{ .compatible = "ti,bq25600", .data = (void *)BQ25600 },
+	{ .compatible = "ti,bq25600d", .data = (void *)BQ25600D },
+	{ .compatible = "ti,bq25601", .data = (void *)BQ25601 },
+	{ .compatible = "ti,bq25601d", .data = (void *)BQ25601D },
+	{ .compatible = "ti,bq25611d", .data = (void *)BQ25611D },
+	{ .compatible = "ti,bq25618", .data = (void *)BQ25618 },
+	{ .compatible = "ti,bq25619", .data = (void *)BQ25619 },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, bq256xx_of_match);
+
+static const struct acpi_device_id bq256xx_acpi_match[] = {
+	{ "bq25600", BQ25600 },
+	{ "bq25600d", BQ25600D },
+	{ "bq25601", BQ25601 },
+	{ "bq25601d", BQ25601D },
+	{ "bq25611d", BQ25611D },
+	{ "bq25618", BQ25618 },
+	{ "bq25619", BQ25619 },
+	{},
+};
+MODULE_DEVICE_TABLE(acpi, bq256xx_acpi_match);
+
+static struct i2c_driver bq256xx_driver = {
+	.driver = {
+		.name = "bq256xx-charger",
+		.of_match_table = bq256xx_of_match,
+		.acpi_match_table = ACPI_PTR(bq256xx_acpi_match),
+	},
+	.probe = bq256xx_probe,
+	.id_table = bq256xx_i2c_ids,
+};
+module_i2c_driver(bq256xx_driver);
+
+MODULE_AUTHOR("Ricardo Rivera-Matos <r-rivera-matos@ti.com>");
+MODULE_DESCRIPTION("bq256xx charger driver");
+MODULE_LICENSE("GPL v2");
-- 
2.28.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v5 2/2] power: supply: bq256xx: Introduce the BQ256XX charger driver
  2020-10-01 21:40 ` [PATCH v5 2/2] power: supply: bq256xx: Introduce the BQ256XX charger driver Ricardo Rivera-Matos
@ 2020-10-03 10:54   ` Sebastian Reichel
  2020-10-05 17:33     ` [EXTERNAL] " Ricardo Rivera-Matos
  0 siblings, 1 reply; 5+ messages in thread
From: Sebastian Reichel @ 2020-10-03 10:54 UTC (permalink / raw)
  To: Ricardo Rivera-Matos; +Cc: robh+dt, linux-pm, devicetree, linux-kernel, dmurphy

[-- Attachment #1: Type: text/plain, Size: 461 bytes --]

Hi Ricardo,

On Thu, Oct 01, 2020 at 04:40:34PM -0500, Ricardo Rivera-Matos wrote:
> The BQ256XX family of devices are highly integrated buck chargers
> for single cell batteries.
> 
> Signed-off-by: Ricardo Rivera-Matos <r-rivera-matos@ti.com>
> 
> v5 - adds power_supply_put_battery_info() and devm_add_action_or_reset() calls
> ---

changes look good, but you are still missing
usb_unregister_notifier() calls on drival removal.

-- Sebastian

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [EXTERNAL] Re: [PATCH v5 2/2] power: supply: bq256xx: Introduce the BQ256XX charger driver
  2020-10-03 10:54   ` Sebastian Reichel
@ 2020-10-05 17:33     ` Ricardo Rivera-Matos
  0 siblings, 0 replies; 5+ messages in thread
From: Ricardo Rivera-Matos @ 2020-10-05 17:33 UTC (permalink / raw)
  To: Sebastian Reichel; +Cc: robh+dt, linux-pm, devicetree, linux-kernel, dmurphy

Sebastian,

On 10/3/20 5:54 AM, Sebastian Reichel wrote:
> Hi Ricardo,
>
> On Thu, Oct 01, 2020 at 04:40:34PM -0500, Ricardo Rivera-Matos wrote:
>> The BQ256XX family of devices are highly integrated buck chargers
>> for single cell batteries.
>>
>> Signed-off-by: Ricardo Rivera-Matos <r-rivera-matos@ti.com>
>>
>> v5 - adds power_supply_put_battery_info() and devm_add_action_or_reset() calls
>> ---
> changes look good, but you are still missing
> usb_unregister_notifier() calls on drival removal.
ACK, I will add a driver removal function and make the appropriate 
usb_unregister_notifier() calls.
>
> -- Sebastian
Ricardo


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2020-10-05 17:33 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-01 21:40 [PATCH v5 0/2] Introduce the BQ256XX family of chargers Ricardo Rivera-Matos
2020-10-01 21:40 ` [PATCH v5 1/2] dt-bindings: power: Add the bq256xx dt bindings Ricardo Rivera-Matos
2020-10-01 21:40 ` [PATCH v5 2/2] power: supply: bq256xx: Introduce the BQ256XX charger driver Ricardo Rivera-Matos
2020-10-03 10:54   ` Sebastian Reichel
2020-10-05 17:33     ` [EXTERNAL] " Ricardo Rivera-Matos

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).