linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] power: supply: Add driver for TI BQ2416X battery charger
@ 2017-02-07  1:09 Wojciech Ziemba
  2017-02-07  3:00 ` Liam Breck
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Wojciech Ziemba @ 2017-02-07  1:09 UTC (permalink / raw)
  To: sre
  Cc: robh+dt, mark.rutland, wojciech.ziemba, linux-pm, devicetree,
	linux-kernel

There is interest in adding a Linux driver for TI BQ2416X battery
charger. The driver supports BQ24160 chip, thus can be easily extended
for other BQ2416X family chargers.
Device exposes 'POWER_SUPPLY_PROP_*' properties and a number of knobs
for controlling the charging process as well as sends power supply change
notification via power-supply subsystem.

Signed-off-by: Wojciech Ziemba <wojciech.ziemba@verifone.com>
---
 .../devicetree/bindings/power/supply/bq2416x.txt   |   71 +
 drivers/power/supply/Kconfig                       |    7 +
 drivers/power/supply/Makefile                      |    1 +
 drivers/power/supply/bq2416x_charger.c             | 1871 ++++++++++++++++++++
 include/dt-bindings/power/bq2416x_charger.h        |   23 +
 include/linux/power/bq2416x_charger.h              |   80 +
 6 files changed, 2053 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/supply/bq2416x.txt
 create mode 100644 drivers/power/supply/bq2416x_charger.c
 create mode 100644 include/dt-bindings/power/bq2416x_charger.h
 create mode 100644 include/linux/power/bq2416x_charger.h

diff --git a/Documentation/devicetree/bindings/power/supply/bq2416x.txt b/Documentation/devicetree/bindings/power/supply/bq2416x.txt
new file mode 100644
index 0000000..8f4b814
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/bq2416x.txt
@@ -0,0 +1,71 @@
+Binding for TI bq2416x Li-Ion Charger
+
+Required properties:
+===================
+- compatible: one of the following:
+ * "ti,bq24160"
+ * "ti,bq24160a"
+ * "ti,bq24161"
+ * "ti,bq24161b"
+ * "ti,bq24163"
+ * "ti,bq24168"
+
+- reg:			I2c address of the device.
+- interrupt-parent:	The irq controller(phandle) connected to INT pin on BQ2416x
+- interrupts:		The irq number assigned for INT pin.
+
+Optional properties:
+===================
+- interrupt-names:		Meanigfull irq name.
+- ti,charge-voltage:		Charge volatge [mV].
+- ti,charge-current:		Charge current [mA].
+- ti,termination-current:	Termination current [mA}.
+- ti,in-current-limit:		IN source current limit. enum:
+				- IN_CURR_LIM_1500MA (0)
+				- IN_CURR_LIM_2500MA (1)
+
+- ti,usb-current-limit:		USB source current limit. enum:
+				- USB_CURR_LIM_100MA (0)
+				- USB_CURR_LIM_150MA (1)
+				- USB_CURR_LIM_500MA (2)
+				- USB_CURR_LIM_800MA (3)
+				- USB_CURR_LIM_900MA (4)
+				- USB_CURR_LIM_1500MA (5)
+
+- ti,status-pin-enable:		0 or 1. Enable charge status output STAT pin.
+- ti,current-termination-enable:0 or 1. Enable charge current termination.
+- ti,usb-dpm-voltage:		USB dpm voltage [mV]. Refer to datasheet.
+- ti,in-dpm-voltage:		IN dpm voltage [mV].
+- ti,safety-timer:		Safety timer. enum:
+			- TMR_27MIN (0)
+			- TMR_6H (1)
+			- TMR_9H (2)
+			- TMR_OFF (3)
+
+- ti,supplied-to:	string array: max 4. Names of devices to which
+			the charger supplies.
+
+Example:
+=======
+#include <dt-bindings/power/bq2416x_charger.h>
+
+bq24160@6b {
+	compatible = "ti,bq24160";
+	reg = <0x6b>;
+
+	interrupt-parent = <&gpio5>;
+	interrupts = <31 IRQ_TYPE_EDGE_RISING>;
+	interrupt-names = "bq24160-charge-status-change";
+
+	ti,charge-voltage = <4300>;
+	ti,charge-current = <1300>;
+	ti,termination-current = <100>;
+	ti,in-current-limit = <IN_CURR_LIM_1500MA>;
+	ti,usb-current-limit = <USB_CURR_LIM_1500MA>;
+	ti,status-pin-enable = <1>;
+	ti,current-termination-enable = <1>;
+	ti,usb-dpm-voltage = <4300>;
+	ti,in-dpm-voltage = <4300>;
+	ti,safety-timer = <TMR_6H>; /* charge max 6h */
+	ti,supplied-to = "bq27621-0";
+};
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 76806a0..575096e 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -413,6 +413,13 @@ config CHARGER_BQ2415X
 	  You'll need this driver to charge batteries on e.g. Nokia
 	  RX-51/N900.
 
+config CHARGER_BQ2416X
+	tristate "TI BQ2416x Dual-Input, Single Cell Switch-Mode Li-Ion charger"
+	depends on I2C
+	help
+	  Say Y here to enable support for the TI BQ2416x battery charger.
+	  The driver is configured to operate with a single lithium cell.
+
 config CHARGER_BQ24190
 	tristate "TI BQ24190 battery charger driver"
 	depends on I2C
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index 36c599d..73711e0 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_CHARGER_MAX8997)	+= max8997_charger.o
 obj-$(CONFIG_CHARGER_MAX8998)	+= max8998_charger.o
 obj-$(CONFIG_CHARGER_QCOM_SMBB)	+= qcom_smbb.o
 obj-$(CONFIG_CHARGER_BQ2415X)	+= bq2415x_charger.o
+obj-$(CONFIG_CHARGER_BQ2416X)	+= bq2416x_charger.o
 obj-$(CONFIG_CHARGER_BQ24190)	+= bq24190_charger.o
 obj-$(CONFIG_CHARGER_BQ24257)	+= bq24257_charger.o
 obj-$(CONFIG_CHARGER_BQ24735)	+= bq24735-charger.o
diff --git a/drivers/power/supply/bq2416x_charger.c b/drivers/power/supply/bq2416x_charger.c
new file mode 100644
index 0000000..fa13e55
--- /dev/null
+++ b/drivers/power/supply/bq2416x_charger.c
@@ -0,0 +1,1871 @@
+/*
+ * Driver for BQ2416X Li-Ion Battery Charger
+ *
+ * Copyright (C) 2015 Verifone, Inc.
+ *
+ * Author: Wojciech Ziemba <wojciech.ziemba@verifone.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * The bq2416x series is a 2.5A, Dual-Input, Single-Cell Switched-Mode
+ * Li-Ion Battery Charger with Power
+ * Path Management and I2C Interface
+ *
+ * This driver was tested on BQ24160.
+ *
+ * Datasheets:
+ * http://www.ti.com/product/bq24160
+ * http://www.ti.com/product/bq24160a
+ * http://www.ti.com/product/bq24161
+ * http://www.ti.com/product/bq24161b
+ * http://www.ti.com/product/bq24163
+ * http://www.ti.com/product/bq24168
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/regmap.h>
+#include <linux/power_supply.h>
+#include <linux/power/bq2416x_charger.h>
+
+/* Get the value of bitfield */
+#define BF_GET(_y, _mask) (((_y) & _mask) >> (__builtin_ffs((int) _mask) - 1))
+/* Shift the value of bitfield. Mask based */
+#define BF_SHIFT(_x, _mask) ((_x) << (__builtin_ffs((int) _mask) - 1))
+/* Watchdog timer. 3 second in reserve */
+#define BQ2416X_WATCHDOG_TIMER		(30 - 3)
+/* Register numbers */
+#define BQ2416X_REG_STATUS		0x00
+#define BQ2416X_REG_SUP_STATUS		0x01
+#define BQ2416X_REG_CONTROL		0x02
+#define BQ2416X_REG_BAT_VOLT		0x03
+#define BQ2416X_REG_VENDOR		0x04
+#define BQ2416X_REG_TERM		0x05
+#define BQ2416X_REG_DPM			0x06
+#define BQ2416X_REG_NTC			0x07
+#define BQ2416X_REG_MAX			0x08
+
+/* status/control register */
+#define BQ2416X_REG_STATUS_TMR_RST_MASK	BIT(7)
+#define BQ2416X_REG_STATUS_STAT_MASK		(BIT(6) | BIT(5) | BIT(4))
+#define BQ2416X_REG_STATUS_SUPPLY_SEL_MASK	BIT(3)
+#define BQ2416X_REG_STATUS_FAULT_MASK		(BIT(2) | BIT(1) | BIT(0))
+
+/* battery/supply status register */
+#define BQ2416X_REG_SUP_STATUS_INSTAT_MASK	(BIT(7) | BIT(6))
+#define BQ2416X_REG_SUP_STATUS_USBSTAT_MASK	(BIT(5) | BIT(4))
+#define BQ2416X_REG_SUP_STATUS_OTG_LOCK_MASK	BIT(3)
+#define BQ2416X_REG_SUP_STATUS_BATSTAT_MASK	(BIT(2) | BIT(1))
+#define BQ2416X_REG_SUP_STATUS_EN_NOBATOP_MASK	BIT(0)
+
+/* control register */
+#define BQ2416X_REG_CONTROL_RESET_MASK		BIT(7)
+#define BQ2416X_REG_CONTROL_USB_CURR_LIM_MASK	(BIT(6) | BIT(5) | BIT(4))
+#define BQ2416X_REG_CONTROL_EN_STAT_MASK	BIT(3)
+#define BQ2416X_REG_CONTROL_TE_MASK		BIT(2)
+#define BQ2416X_REG_CONTROL_CE_MASK		BIT(1)
+#define BQ2416X_REG_CONTROL_HZ_MODE_MASK	BIT(0)
+
+/* control/battery voltage register */
+#define BQ2416X_REG_BAT_VOLT_MASK		(BIT(7) | BIT(6) | BIT(5) | \
+						BIT(4) | BIT(3) | BIT(2))
+#define BQ2416X_REG_BAT_VOLT_IN_CURR_LIM_MASK	BIT(1)
+
+/* vendor/part/revision register */
+#define BQ2416X_REG_VENDOR_REV_MASK		(BIT(2) | BIT(1) | BIT(0))
+#define BQ2416X_REG_VENDOR_CODE_MASK		(BIT(7) | BIT(6) | BIT(5))
+
+/* battery termination fast charge current register */
+#define BQ2416X_REG_TERM_CHRG_CURR_MASK	(BIT(7) | BIT(6) | BIT(5) | \
+						BIT(4) | BIT(3))
+#define BQ2416X_REG_TERM_TERM_CURR_MASK	(BIT(2) | BIT(1) | BIT(0))
+
+/* VIN-DPM voltage/DPPM status register */
+#define BQ2416X_REG_DPM_MINSYS_STATUS_MASK	BIT(7)
+#define BQ2416X_REG_DPM_STATUS_MASK		BIT(6)
+#define BQ2416X_REG_DPM_USB_VOLT_MASK		(BIT(5) | BIT(4) | BIT(3))
+#define BQ2416X_REG_DPM_IN_VOLT_MASK		(BIT(2) | BIT(1) | BIT(0))
+
+/* Safety timer/NTC monitor register */
+#define BQ2416X_REG_NTC_TMRX2_MASK		BIT(7)
+#define BQ2416X_REG_NTC_TMR_MASK		(BIT(6) | BIT(5))
+#define BQ2416X_REG_NTC_TS_EN_MASK		BIT(3)
+#define BQ2416X_REG_NTC_TS_FAULT_MASK		(BIT(2) | BIT(1))
+#define BQ2416X_REG_NTC_LOW_CHARGE_MASK	BIT(0)
+
+/* Charge voltage [mV] */
+#define BQ2416X_CHARGE_VOLTAGE_MIN	3500
+#define BQ2416X_CHARGE_VOLTAGE_MAX	4440
+#define BQ2416X_CHARGE_VOLTAGE_STEP	20
+
+/* IN current limit */
+#define BQ2416X_IN_CURR_LIM_1500	0
+#define BQ2416X_IN_CURR_LIM_2500	1
+
+/* Charge current [mA] */
+#define BQ2416X_CHARGE_CURRENT_MIN	550
+#define BQ2416X_CHARGE_CURRENT_MAX	2500
+#define BQ2416X_CHARGE_CURRENT_STEP	75
+
+/* Charge termination current in mA */
+#define BQ2416X_CHARGE_TERM_CURRENT_MIN		50
+#define BQ2416X_CHARGE_TERM_CURRENT_MAX		400
+#define BQ2416X_CHARGE_TERM_CURRENT_STEP	50
+
+/* USB DPM voltage [mV] */
+#define BQ2416X_DPM_USB_VOLTAGE_MIN	4200
+#define BQ2416X_DPM_USB_VOLTAGE_MAX	4760
+#define BQ2416X_DPM_USB_VOLTAGE_STEP	80
+
+/* IN DPM voltage [mV] */
+#define BQ2416X_DPM_IN_VOLTAGE_MIN	4200
+#define BQ2416X_DPM_IN_VOLTAGE_MAX	4760
+#define BQ2416X_DPM_IN_VOLTAGE_STEP	80
+
+/* Supported chips */
+enum  bq2416x_type {
+	BQ24160,
+	BQ24160A,
+	BQ24161,
+	BQ24161B,
+	BQ24163,
+	BQ24168,
+};
+
+/* Charger status */
+enum {
+	STAT_NO_VALID_SOURCE,
+	STAT_IN_READY,
+	STAT_USB_READY,
+	STAT_CHARGING_FROM_IN,
+	STAT_CHARGING_FROM_USB,
+	STAT_CHARGE_DONE,
+	STAT_NA,
+	STAT_FAULT,
+};
+
+/* Charger status to string/power subsys status map */
+static const struct {
+	const char * const str;
+	const int id;
+} bq2416x_charge_status[] = {
+	[STAT_NO_VALID_SOURCE] = {"No valid source",
+				POWER_SUPPLY_STATUS_NOT_CHARGING},
+	[STAT_IN_READY] = {"IN ready", POWER_SUPPLY_STATUS_NOT_CHARGING},
+	[STAT_USB_READY] = {"USB ready", POWER_SUPPLY_STATUS_NOT_CHARGING},
+	[STAT_CHARGING_FROM_IN] = {"Charging from IN",
+				POWER_SUPPLY_STATUS_CHARGING},
+	[STAT_CHARGING_FROM_USB] = {"Charging from USB",
+				POWER_SUPPLY_STATUS_CHARGING},
+	[STAT_CHARGE_DONE] = {"Charge done", POWER_SUPPLY_STATUS_FULL},
+	[STAT_NA] = {"N/A", POWER_SUPPLY_STATUS_UNKNOWN},
+	[STAT_FAULT] = {"Fault", POWER_SUPPLY_STATUS_NOT_CHARGING},
+};
+
+/* Charger fault */
+enum {
+	FAULT_NORMAL,
+	FAULT_THERMAL_SHUTDOWN,
+	FAULT_BATT_TEMP_FAULT,
+	FAULT_WDOG_TIMER_EXPIRED,
+	FAULT_SAFETY_TIMER_EXPIRED,
+	FAULT_IN_SUPPLY_FAULT,
+	FAULT_USB_SUPPLY_FAULT,
+	FAULT_BATTERY_FAULT,
+};
+
+/* Charger fault to string/power subsys fault map */
+static const struct {
+	const char * const str;
+	const int id;
+} bq2416x_charge_fault[] = {
+	[FAULT_NORMAL] = {"Normal", POWER_SUPPLY_HEALTH_GOOD},
+	[FAULT_THERMAL_SHUTDOWN] = {"Thermal shutdown",
+				POWER_SUPPLY_HEALTH_OVERHEAT},
+	[FAULT_BATT_TEMP_FAULT] = {"Battery temp fault",
+				POWER_SUPPLY_HEALTH_OVERHEAT},
+	[FAULT_WDOG_TIMER_EXPIRED] = {"Watchdog timer expired",
+				POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE},
+	[FAULT_SAFETY_TIMER_EXPIRED] = {"Safety timer expired",
+				POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE},
+	[FAULT_IN_SUPPLY_FAULT] = {"IN Supply fault",
+				POWER_SUPPLY_HEALTH_UNSPEC_FAILURE},
+	[FAULT_USB_SUPPLY_FAULT] = {"USB Supply fault",
+				POWER_SUPPLY_HEALTH_UNSPEC_FAILURE},
+	[FAULT_BATTERY_FAULT] = {"Battery fault", POWER_SUPPLY_HEALTH_DEAD},
+};
+
+/* IN(Wall) source status */
+enum {
+	INSTAT_NORMAL,
+	INSTAT_SUPPLY_OVP,
+	INSTAT_WEAK_SOURCE_CONNECTED,
+	INSTAT_FAULTY_ADAPTER,
+};
+
+/* IN(Wall) source status to string map */
+static const char * const bq2416x_in_status[] = {
+	[INSTAT_NORMAL] = "Normal",
+	[INSTAT_SUPPLY_OVP] = "OVP",
+	[INSTAT_WEAK_SOURCE_CONNECTED] = "Weak source",
+	[INSTAT_FAULTY_ADAPTER] = "Faulty adapter",
+};
+
+/* Battery status */
+enum {
+	BATSTAT_BATTERY_PRESENT,
+	BATSTAT_BATTERY_OVP,
+	BATSTAT_BATTERY_NOT_PRESENT,
+	BATSTAT_BATTERY_NA,
+};
+
+/* Battery status to string map */
+static const char * const bq2416x_bat_status[] = {
+	[BATSTAT_BATTERY_PRESENT] = "present",
+	[BATSTAT_BATTERY_OVP] = "OVP",
+	[BATSTAT_BATTERY_NOT_PRESENT] = "not present",
+	[BATSTAT_BATTERY_NA] = "NA",
+};
+
+static const int bq2416x_usb_curr_lim[] = {
+	[USB_CURR_LIM_100MA] = 100,
+	[USB_CURR_LIM_150MA] = 150,
+	[USB_CURR_LIM_500MA] = 500,
+	[USB_CURR_LIM_800MA] = 800,
+	[USB_CURR_LIM_900MA] = 900,
+	[USB_CURR_LIM_1500MA] = 1500,
+};
+
+static const int const bq24160_in_lim[] = {
+	[IN_CURR_LIM_1500MA] = 1500,
+	[IN_CURR_LIM_2500MA] = 2500,
+};
+
+static const char * const bq2416x_tmr[] = {
+	[TMR_27MIN] = "27min",
+	[TMR_6H] = "6h",
+	[TMR_9H] = "9h",
+	[TMR_OFF] = "off",
+};
+
+/* External NTC Monitoring(TS) fault */
+enum {
+	TS_FAULT_NORMAL,
+	TS_FAULT_COLD_HOT,
+	TS_FAULT_COOL,
+	TS_FAULT_WARM,
+};
+
+static const char * const bq2416x_ts_fault[] = {
+	[TS_FAULT_NORMAL] = "normal",
+	[TS_FAULT_COLD_HOT] = "cold/hot(charge suspended)",
+	[TS_FAULT_COOL] = "cool(half current charge)",
+	[TS_FAULT_WARM] = "warm(voltage reduced)",
+};
+
+/* Firmware response: chip revision */
+enum {
+	VENDOR_REV_10,
+	VENDOR_REV_11,
+	VENDOR_REV_20,
+	VENDOR_REV_21,
+	VENDOR_REV_22,
+	VENDOR_REV_23,
+};
+
+static const char * const bq2416x_revision[] = {
+	[VENDOR_REV_10] = "1.0",
+	[VENDOR_REV_11] = "1.1",
+	[VENDOR_REV_20] = "2.0",
+	[VENDOR_REV_21] = "2.1",
+	[VENDOR_REV_22] = "2.2",
+	[VENDOR_REV_23] = "2.3",
+};
+
+/**
+ * struct bq2416x_priv - this device private data
+ * @dev: this device
+ * @regmap: register map for bq2416x
+ * @pdata: platform data
+ * @psy: power-supply-class for this device
+ * @watchdog: watchdog worker
+ * @model: model of this device
+ * @name: the name of this device instance
+ * @idr: the id of this chip
+ */
+struct bq2416x_priv {
+	struct device *dev;
+	struct regmap *regmap;
+	struct bq2416x_pdata pdata;
+	struct power_supply *psy;
+	struct power_supply_desc psy_desc;
+	struct delayed_work watchdog;
+	char *model;
+	char *name;
+	int idr;
+};
+
+/* each registered chip must have unique id */
+static DEFINE_IDR(bq2416x_idr);
+static DEFINE_MUTEX(bq2416x_idr_mutex);
+
+/**
+ * conv2bit_repr - converts value to its regulation binary representation
+ * @val: value to convert
+ * @min: offset - regulation minimum
+ * @max: regulation maximum
+ * @step: regulation step
+ */
+static inline unsigned int conv2bit_repr(unsigned int val, unsigned int min,
+					unsigned int max, unsigned int step)
+{
+	return (clamp_val(val, min, max) - min) / step;
+}
+
+/* regmap callbacks and configuration */
+static bool bq2416x_writeable(struct device *dev, unsigned int reg)
+{
+	return !(reg == BQ2416X_REG_VENDOR);
+}
+
+static bool bq2416x_volatile(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case BQ2416X_REG_BAT_VOLT:
+	case BQ2416X_REG_VENDOR:
+		return false;
+	}
+
+	return true;
+}
+
+static struct regmap_config bq2416x_i2c_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.writeable_reg = bq2416x_writeable,
+	.volatile_reg = bq2416x_volatile,
+	.cache_type = REGCACHE_RBTREE,
+	.max_register = BQ2416X_REG_MAX,
+};
+
+/* power-supply-class callbacks and configuration */
+static int bq2416x_property_is_writeable(struct power_supply *psy,
+		enum power_supply_property psp)
+{
+	int ret;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+		ret = 1;
+		break;
+	default:
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static enum power_supply_property bq2416x_power_supply_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+	POWER_SUPPLY_PROP_SCOPE
+};
+
+/**
+ * bq2416x_get_status - get charger status
+ * @bq2416x: the charger device
+ * @status: the pointer to the return value
+ *
+ * Returns 0 if there is no error or negative on error.
+ */
+static int bq2416x_get_status(struct bq2416x_priv *bq2416x, int *status)
+{
+	unsigned int reg_val;
+	int ret;
+
+	ret = regmap_read(bq2416x->regmap, BQ2416X_REG_STATUS, &reg_val);
+	if (unlikely(ret))
+		return ret;
+
+	reg_val = BF_GET(reg_val, BQ2416X_REG_STATUS_STAT_MASK);
+	*status = bq2416x_charge_status[reg_val].id;
+
+	return ret;
+}
+
+/**
+ * bq2416x_get_charge_type - Returns charge type
+ * @bq2416x: the charger device
+ * @charge_type: the pointer to the return value
+ *
+ * Returns 0 if there is no error or negative on error.
+ */
+static int bq2416x_get_charge_type(struct bq2416x_priv *bq2416x,
+					int *charge_type)
+{
+	unsigned int reg_val;
+	int ret;
+
+	ret = regmap_read(bq2416x->regmap, BQ2416X_REG_STATUS, &reg_val);
+	if (unlikely(ret))
+		return ret;
+
+	reg_val = BF_GET(reg_val, BQ2416X_REG_STATUS_STAT_MASK);
+	if (bq2416x_charge_status[reg_val].id != POWER_SUPPLY_STATUS_CHARGING)
+		*charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE;
+	else {
+		ret = regmap_read(bq2416x->regmap, BQ2416X_REG_NTC, &reg_val);
+		if (unlikely(ret))
+			return ret;
+
+		if (reg_val & BQ2416X_REG_NTC_LOW_CHARGE_MASK)
+			*charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+		else
+			*charge_type = POWER_SUPPLY_CHARGE_TYPE_FAST;
+	}
+
+	return ret;
+}
+
+/**
+ * bq2416x_set_charge_type - sets charge type
+ * @bq2416x: the charger device
+ * @type: new charge type
+ *
+ * Returns 0 if there is no error or negative on error.
+ */
+static int bq2416x_set_charge_type(struct bq2416x_priv *bq2416x,
+					int type)
+{
+	int ret;
+	unsigned int charge_disable;
+	unsigned int low_charge;
+
+	switch (type) {
+	case POWER_SUPPLY_CHARGE_TYPE_NONE:
+		charge_disable = BQ2416X_REG_CONTROL_CE_MASK;
+		low_charge = 0;
+		break;
+	case POWER_SUPPLY_CHARGE_TYPE_TRICKLE:
+		charge_disable = 0;
+		low_charge = BQ2416X_REG_NTC_LOW_CHARGE_MASK;
+		break;
+	case POWER_SUPPLY_CHARGE_TYPE_FAST:
+		charge_disable = 0;
+		low_charge = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = regmap_update_bits(bq2416x->regmap,
+				BQ2416X_REG_CONTROL,
+				BQ2416X_REG_CONTROL_RESET_MASK |
+				BQ2416X_REG_CONTROL_CE_MASK,
+				charge_disable);
+	if (unlikely(ret))
+		return ret;
+
+	ret = regmap_update_bits(bq2416x->regmap,
+				BQ2416X_REG_NTC,
+				BQ2416X_REG_NTC_LOW_CHARGE_MASK,
+				low_charge);
+
+	return ret;
+}
+
+/**
+ * bq2416x_get_health - returns charger health
+ * @bq2416x: this charger device
+ * @health: the pointer to the return value
+ *
+ * Returns 0 if there is no error or negative on error.
+ */
+static int bq2416x_get_health(struct bq2416x_priv *bq2416x, int *health)
+{
+	unsigned int reg_val;
+	int ret;
+
+	/* check supply status */
+	ret = regmap_read(bq2416x->regmap, BQ2416X_REG_STATUS, &reg_val);
+	if (unlikely(ret))
+		return ret;
+
+	reg_val = BF_GET(reg_val, BQ2416X_REG_STATUS_FAULT_MASK);
+	*health = bq2416x_charge_fault[reg_val].id;
+
+	return ret;
+}
+
+/**
+ * bq2416x_get_online - returns online status
+ * @bq2416x: this charger device
+ * @online: the pointer to the return value
+ *
+ * Returns 0 if there is no error or negative on error.
+ */
+static int bq2416x_get_online(struct bq2416x_priv *bq2416x, int *online)
+{
+	unsigned int reg_val;
+	int ret;
+
+	ret = regmap_read(bq2416x->regmap, BQ2416X_REG_STATUS, &reg_val);
+	if (unlikely(ret))
+		return ret;
+
+	reg_val = BF_GET(reg_val, BQ2416X_REG_STATUS_STAT_MASK);
+	*online = ((reg_val > STAT_NO_VALID_SOURCE) && (reg_val < STAT_NA));
+
+	return ret;
+}
+
+/**
+ * bq2416x_get_charge_current - returns charge current
+ * @bq2416x: the charger device
+ * @curr: the pointer to the return current value in [mA]
+ *
+ * Returns 0 if there is no error or negative on error.
+ */
+static int bq2416x_get_charge_current(struct bq2416x_priv *bq2416x,
+					int *curr)
+{
+	int ret;
+	unsigned int low_charge;
+
+	ret = regmap_read(bq2416x->regmap, BQ2416X_REG_TERM, curr);
+	if (unlikely(ret))
+		return ret;
+
+	*curr = BF_GET(*curr, BQ2416X_REG_TERM_CHRG_CURR_MASK) *
+		BQ2416X_CHARGE_CURRENT_STEP +
+		BQ2416X_CHARGE_CURRENT_MIN;
+
+	ret = regmap_read(bq2416x->regmap, BQ2416X_REG_NTC, &low_charge);
+	if (unlikely(ret))
+		return ret;
+
+	/* halve the current value if in low_charge state */
+	*curr >>= low_charge & BQ2416X_REG_NTC_LOW_CHARGE_MASK;
+
+	return ret;
+}
+
+/**
+ * bq2416x_set_charge_current - sets charge current
+ * @bq2416x: the charger device
+ * @curr: new current value in [mA]
+ *
+ * Returns 0 if there is no error or negative on error.
+ */
+static int bq2416x_set_charge_current(struct bq2416x_priv *bq2416x,
+					int curr)
+
+{
+	int ret;
+	unsigned int reg_bits;
+
+	reg_bits = conv2bit_repr(curr, BQ2416X_CHARGE_CURRENT_MIN,
+			BQ2416X_CHARGE_CURRENT_MAX,
+			BQ2416X_CHARGE_CURRENT_STEP);
+
+	ret = regmap_update_bits(bq2416x->regmap,
+				BQ2416X_REG_TERM,
+				BQ2416X_REG_TERM_CHRG_CURR_MASK,
+				BF_SHIFT(reg_bits,
+				BQ2416X_REG_TERM_CHRG_CURR_MASK));
+	if (unlikely(ret))
+		return ret;
+
+	/* unset low charge */
+	ret = regmap_update_bits(bq2416x->regmap,
+				BQ2416X_REG_NTC,
+				BQ2416X_REG_NTC_LOW_CHARGE_MASK,
+				0);
+	return ret;
+}
+
+/**
+ * bq2416x_get_charge_voltage - returns charge voltage
+ * @bq2416x: the charger device
+ * @voltage: the pointer to the return voltage value in [mV]
+ *
+ * Returns 0 if there is no error or negative on error.
+ */
+static int bq2416x_get_charge_voltage(struct bq2416x_priv *bq2416x,
+					int *voltage)
+{
+	int ret;
+
+	ret = regmap_read(bq2416x->regmap, BQ2416X_REG_BAT_VOLT, voltage);
+	if (unlikely(ret))
+		return ret;
+
+	*voltage = BF_GET(*voltage, BQ2416X_REG_BAT_VOLT_MASK) *
+		BQ2416X_CHARGE_VOLTAGE_STEP +
+		BQ2416X_CHARGE_VOLTAGE_MIN;
+
+	return ret;
+}
+
+/**
+ * bq2416x_set_charge_voltage - sets charge voltage
+ * @bq2416x: the charger device
+ * @voltage: new voltage value in [mV]
+ *
+ * Returns 0 if there is no error or negative on error.
+ */
+static int bq2416x_set_charge_voltage(struct bq2416x_priv *bq2416x,
+					int voltage)
+{
+	int ret;
+	unsigned int reg_bits;
+
+	reg_bits = conv2bit_repr(voltage, BQ2416X_CHARGE_VOLTAGE_MIN,
+			BQ2416X_CHARGE_VOLTAGE_MAX,
+			BQ2416X_CHARGE_VOLTAGE_STEP);
+
+	ret = regmap_update_bits(bq2416x->regmap,
+				BQ2416X_REG_BAT_VOLT,
+				BQ2416X_REG_BAT_VOLT_MASK,
+				BF_SHIFT(reg_bits, BQ2416X_REG_BAT_VOLT_MASK));
+	return ret;
+}
+
+/**
+ * bq2416x_set_term_current - sets charge termination current
+ * @bq2416x: the charger device
+ * @term_curr: new charge termination current value in [mA]
+ *
+ * Returns 0 if there is no error or negative on error.
+ */
+static int bq2416x_set_term_current(struct bq2416x_priv *bq2416x,
+					int term_curr)
+{
+	int ret;
+	unsigned int reg_bits;
+
+	reg_bits = conv2bit_repr(term_curr, BQ2416X_CHARGE_TERM_CURRENT_MIN,
+			BQ2416X_CHARGE_TERM_CURRENT_MAX,
+			BQ2416X_CHARGE_TERM_CURRENT_STEP);
+
+	ret = regmap_update_bits(bq2416x->regmap,
+				BQ2416X_REG_TERM,
+				BQ2416X_REG_TERM_TERM_CURR_MASK,
+				BF_SHIFT(reg_bits,
+				BQ2416X_REG_TERM_TERM_CURR_MASK));
+	if (unlikely(ret))
+		return ret;
+
+	ret = regmap_update_bits(bq2416x->regmap,
+				BQ2416X_REG_NTC,
+				BQ2416X_REG_NTC_LOW_CHARGE_MASK,
+				0);
+	return ret;
+}
+
+/**
+ * bq2416x_set_usb_dpm_voltage - sets USB DPM voltage
+ * @bq2416x: the charger device
+ * @dpm_volt: new USB DPM voltage in [mV]
+ *
+ * Returns 0 if there is no error or negative on error.
+ */
+static int bq2416x_set_usb_dpm_voltage(struct bq2416x_priv *bq2416x,
+					int dpm_volt)
+{
+	int ret;
+	unsigned int reg_bits;
+
+	reg_bits = conv2bit_repr(dpm_volt, BQ2416X_DPM_USB_VOLTAGE_MIN,
+			BQ2416X_DPM_USB_VOLTAGE_MAX,
+			BQ2416X_DPM_USB_VOLTAGE_STEP);
+
+	ret = regmap_update_bits(bq2416x->regmap,
+			BQ2416X_REG_DPM,
+			BQ2416X_REG_DPM_USB_VOLT_MASK,
+			BF_SHIFT(reg_bits, BQ2416X_REG_DPM_USB_VOLT_MASK));
+
+	return ret;
+}
+
+/**
+ * bq2416x_set_in_dpm_voltage - sets IN(Wall) DPM voltage
+ * @bq2416x: the charger device
+ * @dpm_volt: new IN DPM voltage in [mV]
+ *
+ * Returns 0 if there is no error or negative on error.
+ */
+static int bq2416x_set_in_dpm_voltage(struct bq2416x_priv *bq2416x,
+					int dpm_volt)
+{
+	int ret;
+	unsigned int reg_bits;
+
+	reg_bits = conv2bit_repr(dpm_volt, BQ2416X_DPM_IN_VOLTAGE_MIN,
+			BQ2416X_DPM_IN_VOLTAGE_MAX,
+			BQ2416X_DPM_IN_VOLTAGE_STEP);
+
+	ret = regmap_update_bits(bq2416x->regmap,
+			BQ2416X_REG_DPM,
+			BQ2416X_REG_DPM_IN_VOLT_MASK,
+			BF_SHIFT(reg_bits, BQ2416X_REG_DPM_IN_VOLT_MASK));
+	return ret;
+}
+
+/**
+ * bq2416x_reset_watchdog_tmr - resets watchdog timer
+ * @bq2416x: the charger device
+ *
+ * Returns 0 if there is no error or negative on error.
+ */
+static int bq2416x_reset_watchdog_tmr(struct bq2416x_priv *bq2416x)
+{
+	int ret;
+
+	ret = regmap_update_bits(bq2416x->regmap,
+			BQ2416X_REG_STATUS,
+			BQ2416X_REG_STATUS_TMR_RST_MASK,
+			BQ2416X_REG_STATUS_TMR_RST_MASK);
+	if (unlikely(ret))
+		dev_err(bq2416x->dev, "Can't reset watchdog timer\n");
+
+	return ret;
+}
+
+/**
+ * bq2416x_configure - configures charger per DT/platform data
+ * @bq2416x: the charger device
+ *
+ * Returns 0 if there is no error or negative on error.
+ */
+static int bq2416x_configure(struct bq2416x_priv *bq2416x)
+{
+	struct bq2416x_pdata *pdata = &bq2416x->pdata;
+	int ret;
+	unsigned int mask, bits;
+
+	ret = bq2416x_reset_watchdog_tmr(bq2416x);
+	if (unlikely(ret))
+		return ret;
+
+	ret = bq2416x_set_charge_voltage(bq2416x, pdata->charge_voltage);
+	if (unlikely(ret))
+		return ret;
+
+	ret = regmap_update_bits(bq2416x->regmap,
+			BQ2416X_REG_BAT_VOLT,
+			BQ2416X_REG_BAT_VOLT_IN_CURR_LIM_MASK,
+			BF_SHIFT(pdata->in_curr_limit,
+			BQ2416X_REG_BAT_VOLT_IN_CURR_LIM_MASK));
+	if (unlikely(ret))
+		return ret;
+
+	ret = regmap_update_bits(bq2416x->regmap,
+			BQ2416X_REG_CONTROL, BQ2416X_REG_CONTROL_RESET_MASK |
+			BQ2416X_REG_CONTROL_USB_CURR_LIM_MASK,
+			BF_SHIFT(pdata->usb_curr_limit,
+			BQ2416X_REG_CONTROL_USB_CURR_LIM_MASK));
+	if (unlikely(ret))
+		return ret;
+
+	mask =  BQ2416X_REG_CONTROL_RESET_MASK |
+		BQ2416X_REG_CONTROL_EN_STAT_MASK |
+		BQ2416X_REG_CONTROL_TE_MASK |
+		BQ2416X_REG_CONTROL_CE_MASK;
+
+	bits =  BF_SHIFT(pdata->stat_pin_en,
+			BQ2416X_REG_CONTROL_EN_STAT_MASK) |
+		BF_SHIFT(pdata->curr_term_en,
+			BQ2416X_REG_CONTROL_TE_MASK);
+
+	ret = regmap_update_bits(bq2416x->regmap,
+			BQ2416X_REG_CONTROL,
+			mask,
+			bits);
+	if (unlikely(ret))
+		return ret;
+
+	ret = bq2416x_set_charge_current(bq2416x, pdata->charge_current);
+	if (unlikely(ret))
+		return ret;
+
+	ret = bq2416x_set_term_current(bq2416x, pdata->term_current);
+	if (unlikely(ret))
+		return ret;
+
+	ret = bq2416x_set_usb_dpm_voltage(bq2416x, pdata->usb_dpm_voltage);
+	if (unlikely(ret))
+		return ret;
+
+	ret = bq2416x_set_in_dpm_voltage(bq2416x, pdata->in_dpm_voltage);
+	if (unlikely(ret))
+		return ret;
+
+	ret = regmap_update_bits(bq2416x->regmap,
+			BQ2416X_REG_NTC, BQ2416X_REG_NTC_TMR_MASK,
+			BF_SHIFT(pdata->safety_timer,
+			BQ2416X_REG_NTC_TMR_MASK));
+
+	return ret;
+}
+
+/**
+ * Status pin interrupt handler. It sends uevent upon charger status change
+ */
+static irqreturn_t bq2416x_thread_irq(int irq, void *priv)
+{
+	struct bq2416x_priv *bq2416x = priv;
+
+	/* Give registers some time */
+	msleep(300);
+
+	power_supply_changed(bq2416x->psy);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * Worker for watchdog timer reset.
+ */
+static void bq2416x_watchdog_work(struct work_struct *work)
+{
+	struct bq2416x_priv *bq2416x = container_of(work, struct bq2416x_priv,
+						 watchdog.work);
+
+	pm_runtime_get_sync(bq2416x->dev);
+	bq2416x_reset_watchdog_tmr(bq2416x);
+	pm_runtime_put_sync(bq2416x->dev);
+
+	schedule_delayed_work(&bq2416x->watchdog, BQ2416X_WATCHDOG_TIMER * HZ);
+}
+
+/**
+ * power-supply class get property callback
+ */
+static int bq2416x_psy_get_property(struct power_supply *psy,
+					enum power_supply_property psp,
+					union power_supply_propval *val)
+{
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret = 0;
+
+	val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+
+	pm_runtime_get_sync(bq2416x->dev);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		ret = bq2416x_get_status(bq2416x, &val->intval);
+		break;
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		val->strval = bq2416x->model;
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		val->strval = "Texas Instruments";
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		ret = bq2416x_get_charge_type(bq2416x, &val->intval);
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		ret = bq2416x_get_health(bq2416x, &val->intval);
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		ret = bq2416x_get_online(bq2416x, &val->intval);
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+		ret = bq2416x_get_charge_current(bq2416x, &val->intval);
+		val->intval *= 1000;
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+		val->intval = BQ2416X_CHARGE_CURRENT_MAX;
+		val->intval *= 1000;
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+		ret = bq2416x_get_charge_voltage(bq2416x, &val->intval);
+		val->intval *= 1000;
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+		val->intval = BQ2416X_CHARGE_VOLTAGE_MAX;
+		val->intval *= 1000;
+		break;
+	case POWER_SUPPLY_PROP_SCOPE:
+		val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	pm_runtime_put_sync(bq2416x->dev);
+	return ret;
+}
+
+/**
+ * power-supply class set property callback
+ */
+static int bq2416x_psy_set_property(struct power_supply *psy,
+					enum power_supply_property psp,
+					const union power_supply_propval *val)
+{
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret;
+
+	pm_runtime_get_sync(bq2416x->dev);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		ret = bq2416x_set_charge_type(bq2416x, val->intval);
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+		ret = bq2416x_set_charge_current(bq2416x, val->intval / 1000);
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+		ret = bq2416x_set_charge_voltage(bq2416x, val->intval / 1000);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	pm_runtime_put_sync(bq2416x->dev);
+	return ret;
+}
+
+/**
+ * device attributes callbacks
+ */
+static ssize_t bq2416x_sysfs_show_charge_status(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret;
+	unsigned int val;
+	const char *str;
+
+	ret = regmap_read(bq2416x->regmap, BQ2416X_REG_STATUS, &val);
+	if (unlikely(ret != 0))
+		return ret;
+
+	if (strcmp(attr->attr.name, "charge_status") == 0) {
+		val = BF_GET(val, BQ2416X_REG_STATUS_STAT_MASK);
+		str = bq2416x_charge_status[val].str;
+	} else if (strcmp(attr->attr.name, "charge_fault") == 0) {
+		val = BF_GET(val, BQ2416X_REG_STATUS_FAULT_MASK);
+		str = bq2416x_charge_fault[val].str;
+	} else if (strcmp(attr->attr.name, "supply_sel") == 0) {
+		if (val & BQ2416X_REG_STATUS_SUPPLY_SEL_MASK)
+			str = "usb";
+		else
+			str = "in";
+	} else
+		return -EINVAL;
+
+	return sprintf(buf, "%s\n", str);
+}
+
+static ssize_t bq2416x_sysfs_store_supply_sel(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret;
+
+	if (strncmp(buf, "usb", 3) == 0)
+		ret = regmap_update_bits(bq2416x->regmap,
+			BQ2416X_REG_STATUS,
+			BQ2416X_REG_STATUS_SUPPLY_SEL_MASK,
+			BQ2416X_REG_STATUS_SUPPLY_SEL_MASK);
+	else if (strncmp(buf, "in", 2) == 0)
+		ret = regmap_update_bits(bq2416x->regmap,
+			BQ2416X_REG_STATUS,
+			BQ2416X_REG_STATUS_SUPPLY_SEL_MASK,
+			0);
+	else
+		ret = -EINVAL;
+
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static ssize_t bq2416x_sysfs_show_supply_status(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret;
+	unsigned int val;
+	const char *str;
+
+	ret = regmap_read(bq2416x->regmap, BQ2416X_REG_SUP_STATUS, &val);
+	if (unlikely(ret != 0))
+		return ret;
+
+	if (strcmp(attr->attr.name, "in_status") == 0) {
+		val = BF_GET(val, BQ2416X_REG_SUP_STATUS_INSTAT_MASK);
+		str = bq2416x_in_status[val];
+	} else if (strcmp(attr->attr.name, "usb_status") == 0) {
+		val = BF_GET(val, BQ2416X_REG_SUP_STATUS_USBSTAT_MASK);
+		str = bq2416x_in_status[val];
+	} else if (strcmp(attr->attr.name, "bat_status") == 0) {
+		val = BF_GET(val, BQ2416X_REG_SUP_STATUS_BATSTAT_MASK);
+		str = bq2416x_bat_status[val];
+	} else
+		return -EINVAL;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", str);
+}
+
+static ssize_t bq2416x_sysfs_show_charge_voltage(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret;
+	unsigned int voltage;
+
+	ret = bq2416x_get_charge_voltage(bq2416x, &voltage);
+	if (unlikely(ret))
+		return ret;
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", voltage);
+}
+
+static ssize_t bq2416x_sysfs_store_charge_voltage(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret;
+	unsigned int voltage;
+
+	ret = kstrtouint(buf, 0, &voltage);
+	if (unlikely(ret))
+		return -EINVAL;
+
+	ret = bq2416x_set_charge_voltage(bq2416x, voltage);
+	if (unlikely(ret))
+		return ret;
+
+	return count;
+}
+
+static ssize_t bq2416x_sysfs_show_in_curr_limit(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret;
+	unsigned int limit;
+
+	ret = regmap_read(bq2416x->regmap, BQ2416X_REG_BAT_VOLT, &limit);
+	if (unlikely(ret))
+		return ret;
+
+	limit = BF_GET(limit, BQ2416X_REG_BAT_VOLT_IN_CURR_LIM_MASK);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", bq24160_in_lim[limit]);
+
+}
+
+static ssize_t bq2416x_sysfs_store_in_curr_limit(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret;
+	unsigned int reg_bits, limit;
+
+	ret = kstrtouint(buf, 0, &limit);
+	if (unlikely(ret))
+		return -EINVAL;
+
+	if (limit < 2500)
+		reg_bits = BQ2416X_IN_CURR_LIM_1500;
+	else
+		reg_bits = BQ2416X_IN_CURR_LIM_2500;
+
+	ret = regmap_update_bits(bq2416x->regmap,
+				BQ2416X_REG_BAT_VOLT,
+				BQ2416X_REG_BAT_VOLT_IN_CURR_LIM_MASK,
+				BF_SHIFT(reg_bits,
+				BQ2416X_REG_BAT_VOLT_IN_CURR_LIM_MASK));
+	if (unlikely(ret))
+		return ret;
+
+	return count;
+}
+
+static ssize_t bq2416x_sysfs_show_usb_curr_limit(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret;
+	unsigned int limit;
+
+	ret = regmap_read(bq2416x->regmap, BQ2416X_REG_CONTROL, &limit);
+	if (unlikely(ret != 0))
+		return ret;
+
+	limit = BF_GET(limit, BQ2416X_REG_CONTROL_USB_CURR_LIM_MASK);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", bq2416x_usb_curr_lim[limit]);
+}
+
+static ssize_t bq2416x_sysfs_store_usb_curr_limit(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret;
+	unsigned int curr, reg_bits;
+
+	ret = kstrtouint(buf, 0, &curr);
+	if (unlikely(ret))
+		return -EINVAL;
+
+	if (curr < 150)
+		reg_bits = USB_CURR_LIM_100MA;
+	else if (curr < 500)
+		reg_bits = USB_CURR_LIM_150MA;
+	else if (curr < 800)
+		reg_bits = USB_CURR_LIM_500MA;
+	else if (curr < 900)
+		reg_bits = USB_CURR_LIM_800MA;
+	else if (curr < 1500)
+		reg_bits = USB_CURR_LIM_900MA;
+	else
+		reg_bits = USB_CURR_LIM_1500MA;
+
+	ret = regmap_update_bits(bq2416x->regmap,
+			BQ2416X_REG_CONTROL, BQ2416X_REG_CONTROL_RESET_MASK |
+			BQ2416X_REG_CONTROL_USB_CURR_LIM_MASK,
+			BF_SHIFT(reg_bits,
+			BQ2416X_REG_CONTROL_USB_CURR_LIM_MASK));
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static ssize_t bq2416x_sysfs_show_charge_current(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret, curr;
+
+	ret = bq2416x_get_charge_current(bq2416x, &curr);
+	if (unlikely(ret))
+		return ret;
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", curr);
+}
+
+static ssize_t bq2416x_sysfs_store_charge_current(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret;
+	unsigned int curr;
+
+	ret = kstrtouint(buf, 0, &curr);
+	if (unlikely(ret))
+		return -EINVAL;
+
+	ret = bq2416x_set_charge_current(bq2416x, curr);
+	if (unlikely(ret))
+		return ret;
+
+	return count;
+}
+
+static ssize_t bq2416x_sysfs_show_term_current(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret;
+	unsigned int term_curr;
+
+	ret = regmap_read(bq2416x->regmap, BQ2416X_REG_TERM, &term_curr);
+	if (unlikely(ret))
+		return ret;
+
+	term_curr = BF_GET(term_curr, BQ2416X_REG_TERM_TERM_CURR_MASK) *
+			BQ2416X_CHARGE_TERM_CURRENT_STEP +
+			BQ2416X_CHARGE_TERM_CURRENT_MIN;
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", term_curr);
+}
+
+static ssize_t bq2416x_sysfs_store_term_current(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret;
+	unsigned int term_curr;
+
+	ret = kstrtouint(buf, 0, &term_curr);
+	if (unlikely(ret))
+		return -EINVAL;
+
+	ret = bq2416x_set_term_current(bq2416x, term_curr);
+	if (unlikely(ret))
+		return ret;
+
+	return count;
+}
+
+static ssize_t bq2416x_sysfs_show_dpm_voltage(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret;
+	unsigned int reg_val, dpm_volt;
+
+	ret = regmap_read(bq2416x->regmap, BQ2416X_REG_DPM, &reg_val);
+	if (unlikely(ret != 0))
+		return ret;
+
+	if (strcmp(attr->attr.name, "usb_dpm_voltage") == 0)
+		dpm_volt = BF_GET(reg_val, BQ2416X_REG_DPM_USB_VOLT_MASK);
+	else if (strcmp(attr->attr.name, "in_dpm_voltage") == 0)
+		dpm_volt = BF_GET(reg_val, BQ2416X_REG_DPM_IN_VOLT_MASK);
+	else
+		return -EINVAL;
+
+	dpm_volt = dpm_volt * BQ2416X_DPM_IN_VOLTAGE_STEP +
+		   BQ2416X_DPM_IN_VOLTAGE_MIN;
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", dpm_volt);
+}
+
+static ssize_t bq2416x_sysfs_store_dpm_voltage(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret;
+	unsigned int dpm_volt;
+
+	ret = kstrtouint(buf, 0, &dpm_volt);
+	if (unlikely(ret))
+		return -EINVAL;
+
+	if (strcmp(attr->attr.name, "usb_dpm_voltage") == 0)
+		ret = bq2416x_set_usb_dpm_voltage(bq2416x, dpm_volt);
+	else if (strcmp(attr->attr.name, "in_dpm_voltage") == 0)
+		ret = bq2416x_set_in_dpm_voltage(bq2416x, dpm_volt);
+	else
+		ret =  -EINVAL;
+
+	if (ret)
+		return ret;
+
+	return count;
+
+}
+
+static ssize_t bq2416x_sysfs_show_safety_timer(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret;
+	unsigned int val;
+	const char *str;
+
+	ret = regmap_read(bq2416x->regmap, BQ2416X_REG_NTC, &val);
+	if (unlikely(ret != 0))
+		return ret;
+
+	if (strcmp(attr->attr.name, "safety_timer") == 0) {
+		val = BF_GET(val, BQ2416X_REG_NTC_TMR_MASK);
+		str = bq2416x_tmr[val];
+	} else if (strcmp(attr->attr.name, "ts_fault") == 0) {
+		val = BF_GET(val, BQ2416X_REG_NTC_TS_FAULT_MASK);
+		str = bq2416x_ts_fault[val];
+	} else
+		return -EINVAL;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", str);
+}
+
+static ssize_t bq2416x_sysfs_store_safety_timer(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	int ret;
+	unsigned int tmr;
+	bool found = false;
+
+	for (tmr = 0; tmr <= TMR_OFF; tmr++) {
+		if (strncmp(buf, bq2416x_tmr[tmr],
+		    strlen(bq2416x_tmr[tmr])) == 0) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found)
+		return -EINVAL;
+
+	ret = regmap_update_bits(bq2416x->regmap,
+			BQ2416X_REG_NTC, BQ2416X_REG_NTC_TMR_MASK,
+			BF_SHIFT(tmr, BQ2416X_REG_NTC_TMR_MASK));
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static ssize_t bq2416x_sysfs_show_bit(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+	int ret;
+	unsigned int reg_val;
+	unsigned int reg = sattr->nr, mask = sattr->index;
+
+	ret = regmap_read(bq2416x->regmap, reg, &reg_val);
+	if (unlikely(ret != 0))
+		return ret;
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", !!(reg_val & mask));
+}
+
+static ssize_t bq2416x_sysfs_store_bit(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct power_supply *psy = dev_get_drvdata(dev);
+	struct bq2416x_priv *bq2416x = power_supply_get_drvdata(psy);
+	struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+	int ret;
+	unsigned int bits;
+	unsigned int reg = sattr->nr, mask = sattr->index;
+
+	if (strncmp(buf, "1", 1) == 0)
+		bits = mask;
+	else if (strncmp(buf, "0", 1) == 0)
+		bits = 0;
+	else
+		return -EINVAL;
+
+	/* clear reset bit before writeback */
+	if (reg == BQ2416X_REG_CONTROL)
+		mask |= BQ2416X_REG_CONTROL_RESET_MASK;
+
+	ret = regmap_update_bits(bq2416x->regmap,
+			reg,
+			mask,
+			bits);
+	if (unlikely(ret))
+		return ret;
+
+	return count;
+}
+
+#define BIT_DEVICE_ATTR(_name, _mode, _reg, _bit)			\
+	SENSOR_DEVICE_ATTR_2(_name, _mode, bq2416x_sysfs_show_bit,	\
+	bq2416x_sysfs_store_bit, _reg, _bit)
+
+static DEVICE_ATTR(charge_status, 0444,
+			bq2416x_sysfs_show_charge_status, NULL);
+static DEVICE_ATTR(charge_fault, 0444,
+			bq2416x_sysfs_show_charge_status, NULL);
+static DEVICE_ATTR(supply_sel, 0644,
+			bq2416x_sysfs_show_charge_status,
+			bq2416x_sysfs_store_supply_sel);
+static BIT_DEVICE_ATTR(timer_rst, 0200,
+			BQ2416X_REG_STATUS, BQ2416X_REG_STATUS_TMR_RST_MASK);
+static DEVICE_ATTR(in_status, 0444,
+			bq2416x_sysfs_show_supply_status, NULL);
+static DEVICE_ATTR(usb_status, 0444,
+			bq2416x_sysfs_show_supply_status, NULL);
+static BIT_DEVICE_ATTR(otg_lock, 0644,
+			BQ2416X_REG_SUP_STATUS,
+			BQ2416X_REG_SUP_STATUS_OTG_LOCK_MASK);
+static BIT_DEVICE_ATTR(nobatop_en, 0644,
+			BQ2416X_REG_SUP_STATUS,
+			BQ2416X_REG_SUP_STATUS_EN_NOBATOP_MASK);
+static DEVICE_ATTR(bat_status, 0444,
+			bq2416x_sysfs_show_supply_status, NULL);
+static DEVICE_ATTR(charge_voltage, 0644,
+			bq2416x_sysfs_show_charge_voltage,
+			bq2416x_sysfs_store_charge_voltage);
+static DEVICE_ATTR(in_curr_limit, 0644,
+			bq2416x_sysfs_show_in_curr_limit,
+			bq2416x_sysfs_store_in_curr_limit);
+static DEVICE_ATTR(usb_curr_limit, 0644,
+			bq2416x_sysfs_show_usb_curr_limit,
+			bq2416x_sysfs_store_usb_curr_limit);
+static BIT_DEVICE_ATTR(stat_pin_en, 0644,
+			BQ2416X_REG_CONTROL,
+			BQ2416X_REG_CONTROL_EN_STAT_MASK);
+static BIT_DEVICE_ATTR(curr_term_en, 0644,
+			BQ2416X_REG_CONTROL,
+			BQ2416X_REG_CONTROL_TE_MASK);
+static BIT_DEVICE_ATTR(charging_disable, 0644,
+			BQ2416X_REG_CONTROL,
+			BQ2416X_REG_CONTROL_CE_MASK);
+static BIT_DEVICE_ATTR(hz_mode, 0644,
+			BQ2416X_REG_CONTROL,
+			BQ2416X_REG_CONTROL_HZ_MODE_MASK);
+static DEVICE_ATTR(charge_current, 0644,
+			bq2416x_sysfs_show_charge_current,
+			bq2416x_sysfs_store_charge_current);
+static DEVICE_ATTR(term_current, 0644,
+			bq2416x_sysfs_show_term_current,
+			bq2416x_sysfs_store_term_current);
+static BIT_DEVICE_ATTR(min_sys_stat, 0444,
+			BQ2416X_REG_DPM,
+			BQ2416X_REG_DPM_MINSYS_STATUS_MASK);
+static BIT_DEVICE_ATTR(dpm_status, 0444,
+			BQ2416X_REG_DPM,
+			BQ2416X_REG_DPM_STATUS_MASK);
+static DEVICE_ATTR(usb_dpm_voltage, 0644,
+			bq2416x_sysfs_show_dpm_voltage,
+			bq2416x_sysfs_store_dpm_voltage);
+static DEVICE_ATTR(in_dpm_voltage, 0644,
+			bq2416x_sysfs_show_dpm_voltage,
+			bq2416x_sysfs_store_dpm_voltage);
+static BIT_DEVICE_ATTR(safety_timer_x2, 0644,
+			BQ2416X_REG_NTC,
+			BQ2416X_REG_NTC_TMRX2_MASK);
+static DEVICE_ATTR(safety_timer, 0644,
+			bq2416x_sysfs_show_safety_timer,
+			bq2416x_sysfs_store_safety_timer);
+static BIT_DEVICE_ATTR(ts_enable, 0644,
+			BQ2416X_REG_NTC,
+			BQ2416X_REG_NTC_TS_EN_MASK);
+static DEVICE_ATTR(ts_fault, 0444,
+			bq2416x_sysfs_show_safety_timer, NULL);
+static BIT_DEVICE_ATTR(low_charge, 0644,
+			BQ2416X_REG_NTC,
+			BQ2416X_REG_NTC_LOW_CHARGE_MASK);
+
+static struct attribute *bq2416x_sysfs_attributes[] = {
+	&dev_attr_charge_status.attr,
+	&dev_attr_charge_fault.attr,
+	&dev_attr_supply_sel.attr,
+	&sensor_dev_attr_timer_rst.dev_attr.attr,
+	&dev_attr_in_status.attr,
+	&dev_attr_usb_status.attr,
+	&sensor_dev_attr_otg_lock.dev_attr.attr,
+	&sensor_dev_attr_nobatop_en.dev_attr.attr,
+	&dev_attr_bat_status.attr,
+	&dev_attr_charge_voltage.attr,
+	&dev_attr_in_curr_limit.attr,
+	&dev_attr_usb_curr_limit.attr,
+	&sensor_dev_attr_stat_pin_en.dev_attr.attr,
+	&sensor_dev_attr_curr_term_en.dev_attr.attr,
+	&sensor_dev_attr_charging_disable.dev_attr.attr,
+	&sensor_dev_attr_hz_mode.dev_attr.attr,
+	&dev_attr_charge_current.attr,
+	&dev_attr_term_current.attr,
+	&sensor_dev_attr_min_sys_stat.dev_attr.attr,
+	&sensor_dev_attr_dpm_status.dev_attr.attr,
+	&dev_attr_usb_dpm_voltage.attr,
+	&dev_attr_in_dpm_voltage.attr,
+	&sensor_dev_attr_safety_timer_x2.dev_attr.attr,
+	&dev_attr_safety_timer.attr,
+	&sensor_dev_attr_ts_enable.dev_attr.attr,
+	&dev_attr_ts_fault.attr,
+	&sensor_dev_attr_low_charge.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group bq2416x_sysfs_attr_group = {
+	.attrs = bq2416x_sysfs_attributes,
+};
+
+#if defined(CONFIG_OF)
+static const struct of_device_id bq2416x_of_match[] = {
+	{ .compatible = "ti,bq24160" },
+	{ .compatible = "ti,bq24160a" },
+	{ .compatible = "ti,bq24161" },
+	{ .compatible = "ti,bq24161b" },
+	{ .compatible = "ti,bq24163" },
+	{ .compatible = "ti,bq24168" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, bq2416x_of_match);
+
+static void bq2416x_pdata_set_default(struct bq2416x_pdata *pdata)
+{
+	pdata->charge_voltage	= 4200;
+	pdata->in_curr_limit	= IN_CURR_LIM_1500MA;
+	pdata->usb_curr_limit	= USB_CURR_LIM_100MA;
+	pdata->stat_pin_en	= 1;
+	pdata->curr_term_en	= 1;
+	pdata->charge_current	= 1150;
+	pdata->term_current	= 100;
+	pdata->usb_dpm_voltage	= 4200;
+	pdata->in_dpm_voltage	= 4200;
+	pdata->safety_timer	= TMR_27MIN;
+	pdata->num_supplicants	= 1;
+	pdata->supplied_to[0]	= "main-battery";
+}
+
+static int bq2416x_pdata_from_of(struct bq2416x_priv *bq2416x)
+{
+	struct device_node *np = bq2416x->dev->of_node;
+	struct bq2416x_pdata *pdata = &bq2416x->pdata;
+	int ret, i, num_strings;
+	unsigned int prop;
+	const char *supplied_to[4];
+
+	bq2416x_pdata_set_default(pdata);
+
+	ret = of_property_read_u32(np, "ti,charge-voltage", &prop);
+	if (!ret)
+		pdata->charge_voltage = prop;
+
+	ret = of_property_read_u32(np, "ti,in-current-limit", &prop);
+	if (!ret)
+		pdata->in_curr_limit = prop;
+
+	ret = of_property_read_u32(np, "ti,usb-current-limit", &prop);
+	if (!ret)
+		pdata->usb_curr_limit = prop;
+
+	ret = of_property_read_u32(np, "ti,status-pin-enable", &prop);
+	if (!ret)
+		pdata->stat_pin_en = prop;
+
+	ret = of_property_read_u32(np, "ti,current-termination-enable", &prop);
+	if (!ret)
+		pdata->curr_term_en = prop;
+
+	ret = of_property_read_u32(np, "ti,charge-current", &prop);
+	if (!ret)
+		pdata->charge_current = prop;
+
+	ret = of_property_read_u32(np, "ti,termination-current", &prop);
+	if (!ret)
+		pdata->term_current = prop;
+
+	ret = of_property_read_u32(np, "ti,usb-dpm-voltage", &prop);
+	if (!ret)
+		pdata->usb_dpm_voltage = prop;
+
+	ret = of_property_read_u32(np, "ti,in-dpm-voltage", &prop);
+	if (!ret)
+		pdata->in_dpm_voltage = prop;
+
+	ret = of_property_read_u32(np, "ti,safety-timer", &prop);
+	if (!ret)
+		pdata->safety_timer = prop;
+
+	ret = of_property_read_string_array(np, "ti,supplied-to",
+				supplied_to, prop);
+	if (ret > 0) {
+		num_strings = ret;
+		if (num_strings > 4)
+			return -EINVAL;
+
+		pdata->num_supplicants = num_strings;
+		for (i = 0; i < num_strings; i++)
+			pdata->supplied_to[i] = supplied_to[i];
+	}
+
+	return ret;
+}
+#else /* CONFIG_OF */
+static int bq2416x_pdata_from_of(struct bq2416x_priv *bq2416x)
+{
+	return 0;
+}
+#endif /* CONFIG_OF */
+
+#ifdef CONFIG_PM_SLEEP
+static int bq2416x_suspend(struct device *dev)
+{
+	struct bq2416x_priv *bq2416x = dev_get_drvdata(dev);
+
+	cancel_delayed_work(&bq2416x->watchdog);
+
+	pm_runtime_get_sync(bq2416x->dev);
+	bq2416x_set_charge_type(bq2416x, POWER_SUPPLY_CHARGE_TYPE_NONE);
+	pm_runtime_put_sync(bq2416x->dev);
+
+	return 0;
+}
+
+static int bq2416x_resume(struct device *dev)
+{
+	struct bq2416x_priv *bq2416x = dev_get_drvdata(dev);
+
+	pm_runtime_get_sync(bq2416x->dev);
+	bq2416x_reset_watchdog_tmr(bq2416x);
+	bq2416x_set_charge_type(bq2416x, POWER_SUPPLY_CHARGE_TYPE_FAST);
+	pm_runtime_put_sync(bq2416x->dev);
+
+	schedule_delayed_work(&bq2416x->watchdog, BQ2416X_WATCHDOG_TIMER * HZ);
+
+	power_supply_changed(bq2416x->psy);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(bq2416x_pm_ops, bq2416x_suspend, bq2416x_resume);
+
+static int bq2416x_device_init(struct bq2416x_priv *bq2416x)
+{
+	int ret;
+	unsigned int vendor_reg, vendor_code, revision;
+	struct power_supply_config psy_cfg = { .drv_data = bq2416x };
+	struct bq2416x_pdata *pdata = &bq2416x->pdata;
+
+	dev_set_drvdata(bq2416x->dev, bq2416x);
+
+	if (dev_get_platdata(bq2416x->dev))
+		memcpy(pdata, dev_get_platdata(bq2416x->dev),
+			sizeof(*pdata));
+	else if (bq2416x->dev->of_node) {
+		ret = bq2416x_pdata_from_of(bq2416x);
+		if (ret < 0) {
+			dev_err(bq2416x->dev, "OF: not able to process DT\n");
+			return ret;
+		}
+	}
+
+	pm_runtime_get_sync(bq2416x->dev);
+	ret = regmap_read(bq2416x->regmap, BQ2416X_REG_VENDOR, &vendor_reg);
+	if (unlikely(ret)) {
+		dev_err(bq2416x->dev, "Can't read vendor code\n");
+		return ret;
+	}
+	pm_runtime_put_sync(bq2416x->dev);
+
+	vendor_code = BF_GET(vendor_reg, BQ2416X_REG_VENDOR_CODE_MASK);
+	revision = BF_GET(vendor_reg, BQ2416X_REG_VENDOR_REV_MASK);
+
+	dev_info(bq2416x->dev, "Found BQ2416X, code: 0x%02x rev: %s\n",
+			vendor_code, bq2416x_revision[revision]);
+
+	bq2416x->psy_desc.name = bq2416x->name;
+	bq2416x->psy_desc.type = POWER_SUPPLY_TYPE_USB;
+	bq2416x->psy_desc.properties = bq2416x_power_supply_props;
+	bq2416x->psy_desc.get_property = bq2416x_psy_get_property;
+	bq2416x->psy_desc.set_property = bq2416x_psy_set_property;
+	bq2416x->psy_desc.num_properties =
+					ARRAY_SIZE(bq2416x_power_supply_props);
+	psy_cfg.supplied_to = (char **) pdata->supplied_to;
+	psy_cfg.num_supplicants = pdata->num_supplicants;
+	bq2416x->psy_desc.property_is_writeable = bq2416x_property_is_writeable;
+
+	bq2416x->psy = power_supply_register(bq2416x->dev, &bq2416x->psy_desc,
+						&psy_cfg);
+	if (unlikely(IS_ERR(bq2416x->psy))) {
+		dev_err(bq2416x->dev, "Can't register power supply\n");
+		return PTR_ERR(bq2416x->psy);
+	}
+
+	return ret;
+}
+
+int bq2416x_i2c_probe(struct i2c_client *i2c,
+			     const struct i2c_device_id *id)
+{
+	struct i2c_adapter *adapter = to_i2c_adapter(i2c->dev.parent);
+	struct bq2416x_priv *bq2416x;
+	int ret, idr;
+	char *model, *name;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(&i2c->dev, "No support for SMBUS_BYTE_DATA\n");
+		return -ENODEV;
+	}
+
+	/* Get id for the new charger device */
+	mutex_lock(&bq2416x_idr_mutex);
+	idr = idr_alloc(&bq2416x_idr, i2c, 0, 0, GFP_KERNEL);
+	mutex_unlock(&bq2416x_idr_mutex);
+
+	if (IS_ERR_VALUE((unsigned long) idr))
+		return idr;
+
+	model = devm_kzalloc(&i2c->dev, strlen(id->name), GFP_KERNEL);
+	if (unlikely(!model)) {
+		dev_err(&i2c->dev, "Failed to allocate name\n");
+		ret = -ENOMEM;
+		goto err_rel_id;
+	}
+	strncpy(model, id->name, strlen(id->name));
+
+	bq2416x = devm_kzalloc(&i2c->dev, sizeof(*bq2416x), GFP_KERNEL);
+	if (unlikely(!bq2416x)) {
+		dev_err(&i2c->dev, "Failed to allocate private data\n");
+		ret = -ENOMEM;
+		goto err_rel_id;
+	}
+
+	bq2416x->regmap = devm_regmap_init_i2c(i2c, &bq2416x_i2c_regmap);
+	if (IS_ERR(bq2416x->regmap)) {
+		ret = PTR_ERR(bq2416x->regmap);
+		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+			ret);
+		goto err_rel_id;
+	}
+
+	name = kasprintf(GFP_KERNEL, "%s-%d", id->name, idr);
+	if (unlikely(!name)) {
+		dev_err(&i2c->dev, "Failed to allocate device name\n");
+		ret = -ENOMEM;
+		goto err_rel_id;
+	}
+
+	pm_runtime_enable(&i2c->dev);
+	pm_runtime_resume(&i2c->dev);
+
+	bq2416x->dev  = &i2c->dev;
+	bq2416x->idr  = idr;
+	bq2416x->model = model;
+	bq2416x->name  = name;
+
+	ret = bq2416x_device_init(bq2416x);
+	if (ret)
+		goto err_free_name;
+
+	ret = bq2416x_configure(bq2416x);
+	if (unlikely(ret)) {
+		dev_err(bq2416x->dev, "Inital configuration failed\n");
+		goto err_unregister_psy;
+	}
+
+	ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+				bq2416x_thread_irq, IRQF_TRIGGER_RISING |
+				IRQF_ONESHOT, "bq2416xinterrupt", bq2416x);
+	if (ret) {
+		dev_err(&i2c->dev, "Can't request IRQ\n");
+		goto err_unregister_psy;
+	}
+
+	ret = sysfs_create_group(&bq2416x->psy->dev.kobj,
+			&bq2416x_sysfs_attr_group);
+	if (unlikely(ret)) {
+		dev_err(bq2416x->dev, "Can't create sysfs entries\n");
+		goto err_unregister_psy;
+	}
+
+	INIT_DELAYED_WORK(&bq2416x->watchdog, bq2416x_watchdog_work);
+	schedule_delayed_work(&bq2416x->watchdog, BQ2416X_WATCHDOG_TIMER * HZ);
+
+	return 0;
+
+err_unregister_psy:
+	power_supply_unregister(bq2416x->psy);
+err_free_name:
+	pm_runtime_disable(&i2c->dev);
+	kfree(name);
+err_rel_id:
+	mutex_lock(&bq2416x_idr_mutex);
+	idr_remove(&bq2416x_idr, idr);
+	mutex_unlock(&bq2416x_idr_mutex);
+
+	return ret;
+}
+
+static int bq2416x_i2c_remove(struct i2c_client *i2c)
+{
+	struct bq2416x_priv *bq2416x = i2c_get_clientdata(i2c);
+
+	cancel_delayed_work_sync(&bq2416x->watchdog);
+	sysfs_remove_group(&bq2416x->psy->dev.kobj, &bq2416x_sysfs_attr_group);
+	power_supply_unregister(bq2416x->psy);
+	pm_runtime_disable(bq2416x->dev);
+
+	mutex_lock(&bq2416x_idr_mutex);
+	idr_remove(&bq2416x_idr, bq2416x->idr);
+	mutex_unlock(&bq2416x_idr_mutex);
+
+	kfree(bq2416x->name);
+
+	return 0;
+}
+
+static const struct i2c_device_id  bq2416x_i2c_id[] = {
+	{ "bq24160",  BQ24160 },
+	{ "bq24160a", BQ24160A },
+	{ "bq24161",  BQ24161 },
+	{ "bq24161b", BQ24161B },
+	{ "bq24163",  BQ24163 },
+	{ "bq24168",  BQ24168 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, bq2416x_i2c_id);
+
+static struct i2c_driver bq2416x_i2c_driver = {
+	.driver = {
+		.name	= "bq2416x-charger",
+		.of_match_table = of_match_ptr(bq2416x_of_match),
+		.pm     = &bq2416x_pm_ops,
+	},
+	.probe		= bq2416x_i2c_probe,
+	.remove		= bq2416x_i2c_remove,
+	.id_table	= bq2416x_i2c_id,
+};
+
+module_i2c_driver(bq2416x_i2c_driver);
+
+MODULE_DESCRIPTION("TI BQ2416x battery charger driver");
+MODULE_AUTHOR("Wojciech Ziemba <wojciech.ziemba@verifone.com>");
+MODULE_LICENSE("GPL");
diff --git a/include/dt-bindings/power/bq2416x_charger.h b/include/dt-bindings/power/bq2416x_charger.h
new file mode 100644
index 0000000..67363fd
--- /dev/null
+++ b/include/dt-bindings/power/bq2416x_charger.h
@@ -0,0 +1,23 @@
+/*
+ * This header provides constants for bq2416x bindings.
+ */
+
+#ifndef _DT_BINDINGS_POWER_H
+#define _DT_BINDINGS_POWER_H
+
+#define IN_CURR_LIM_1500MA	(0)
+#define IN_CURR_LIM_2500MA	(1)
+
+#define USB_CURR_LIM_100MA	(0)
+#define USB_CURR_LIM_150MA	(1)
+#define USB_CURR_LIM_500MA	(2)
+#define USB_CURR_LIM_800MA	(3)
+#define USB_CURR_LIM_900MA	(4)
+#define USB_CURR_LIM_1500MA	(5)
+
+#define TMR_27MIN	(0)
+#define TMR_6H		(1)
+#define TMR_9H		(2)
+#define TMR_OFF		(3)
+
+#endif /* _DT_BINDINGS_POWER_H */
diff --git a/include/linux/power/bq2416x_charger.h b/include/linux/power/bq2416x_charger.h
new file mode 100644
index 0000000..c561666
--- /dev/null
+++ b/include/linux/power/bq2416x_charger.h
@@ -0,0 +1,80 @@
+/*
+ * Driver for BQ2416X Li-Ion Battery Charger
+ *
+ * Copyright (C) 2015 Verifone, Inc.
+ *
+ * Author: Wojciech Ziemba <wojciech.ziemba@verifone.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * The bq2416x series is a 2.5A, Dual-Input, Single-Cell Switched-Mode
+ * Li-Ion Battery Charger with Power
+ * Path Management and I2C Interface
+ *
+ */
+
+#ifndef _BQ2416X_CHARGER_H
+#define _BQ2416X_CHARGER_H
+
+/* IN(Wall) source limit */
+enum in_curr_lim {
+	IN_CURR_LIM_1500MA,
+	IN_CURR_LIM_2500MA,
+};
+
+/* USB source current limit */
+enum usb_curr_lim {
+	USB_CURR_LIM_100MA,
+	USB_CURR_LIM_150MA,
+	USB_CURR_LIM_500MA,
+	USB_CURR_LIM_800MA,
+	USB_CURR_LIM_900MA,
+	USB_CURR_LIM_1500MA,
+};
+
+/* Safety timer settings */
+enum safe_tmr {
+	TMR_27MIN,
+	TMR_6H,
+	TMR_9H,
+	TMR_OFF,
+};
+
+/**
+ * struct bq2416x_pdata - Platform data for bq2416x chip. It contains default
+ *			  board voltages and currents.
+ * @charge_voltage: charge voltage in [mV]
+ * @charge_current: charge current in [mA]
+ * @in_curr_limit: Current limit for IN source . Enum 1.5A or 2.5A
+ * @usb_curr_limit: Current limit for USB source Enum 100mA - 1500mA
+ * @curr_term_en: enable charge terination by current
+ * @term_current: charge termination current in [mA]
+ * @usb_dpm_voltage: USB DPM voltage [mV]
+ * @in_dpm_voltage: IN DPM voltage [mV]
+ * @stat_pin_en: status pin enable
+ * @safety_timer: safety timer enum: 27min, 6h, 9h, off.
+ * @num_supplicants: number of notify devices. Max 4.
+ * @supplied_to: array of names of supplied to devices
+ */
+struct bq2416x_pdata {
+	int charge_voltage;
+	int charge_current;
+	enum in_curr_lim in_curr_limit;
+	enum usb_curr_lim usb_curr_limit;
+	int curr_term_en;
+	int term_current;
+	int usb_dpm_voltage;
+	int in_dpm_voltage;
+	int stat_pin_en;
+	enum safe_tmr safety_timer;
+	int num_supplicants;
+	const char *supplied_to[4];
+};
+
+#endif /* _BQ2416X_CHARGER_H */
-- 
1.9.1

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

* Re: [PATCH] power: supply: Add driver for TI BQ2416X battery charger
  2017-02-07  1:09 [PATCH] power: supply: Add driver for TI BQ2416X battery charger Wojciech Ziemba
@ 2017-02-07  3:00 ` Liam Breck
  2017-02-07 11:08 ` Andy Shevchenko
  2017-03-20  5:58 ` Sebastian Reichel
  2 siblings, 0 replies; 6+ messages in thread
From: Liam Breck @ 2017-02-07  3:00 UTC (permalink / raw)
  To: Wojciech Ziemba
  Cc: sre, robh+dt, mark.rutland, wojciech.ziemba, linux-pm,
	devicetree, linux-kernel

G'day,

On 7 Feb 2017 at 01:09:09, Wojciech Ziemba wrote:
> diff --git a/Documentation/devicetree/bindings/power/supply/bq2416x.txt 
> b/Documentation/devicetree/bindings/power/supply/bq2416x.txt
> new file mode 100644
> index 0000000..8f4b814
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/supply/bq2416x.txt

DT docs are usually added/amended in a separate patch.

> @@ -0,0 +1,71 @@ 
> +Binding for TI bq2416x Li-Ion Charger
> ...
> +Optional properties:
> +===================
> +- interrupt-names:		Meanigfull irq name.
> +- ti,charge-voltage:		Charge volatge [mV].
> +- ti,charge-current:		Charge current [mA].
> +- ti,termination-current:	Termination current [mA}.
> +- ti,in-current-limit:		IN source current limit. enum:
> +				- IN_CURR_LIM_1500MA (0)
> +				- IN_CURR_LIM_2500MA (1)
> +
> +- ti,usb-current-limit:		USB source current limit. enum:
> +				- USB_CURR_LIM_100MA (0)
> +				- USB_CURR_LIM_150MA (1)
> +				- USB_CURR_LIM_500MA (2)
> +				- USB_CURR_LIM_800MA (3)
> +				- USB_CURR_LIM_900MA (4)
> +				- USB_CURR_LIM_1500MA (5)

DT values are usually uA, uV, uAh, etc.
DT properties usually get a unit suffix, e.g. -microamp.
See Documentation/devicetree/bindings/property-units.txt
and https://patchwork.kernel.org/patch/9555541/

> +- ti,status-pin-enable:		0 or 1. Enable charge status output STAT pin.
> +- ti,current-termination-enable:0 or 1. Enable charge current termination.
> +- ti,usb-dpm-voltage:		USB dpm voltage [mV]. Refer to datasheet.
> +- ti,in-dpm-voltage:		IN dpm voltage [mV].
> +- ti,safety-timer:		Safety timer. enum:
> +			- TMR_27MIN (0)
> +			- TMR_6H (1)
> +			- TMR_9H (2)
> +			- TMR_OFF (3)
> +
> +- ti,supplied-to:	string array: max 4. Names of devices to which
> +			the charger supplies.

You might want to use and/or add to power_supply_battery_info, which
Sebastian asked for, see:
https://patchwork.kernel.org/patch/9555539/
https://patchwork.kernel.org/patch/9555545/
https://patchwork.kernel.org/patch/9555547/
https://patchwork.kernel.org/patch/9555549/

I'm also at work on a patch for BQ24190 which adds:
  battery:precharge-current-microamp -> info.precharge_current_ua ->
    POWER_SUPPLY_PROP_PRECHARGE_CURRENT
  battery:endcharge-current-microamp -> info.endcharge_current_ua ->
    POWER_SUPPLY_PROP_ENDCHARGE_CURRENT

Hope that helps!

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

* Re: [PATCH] power: supply: Add driver for TI BQ2416X battery charger
  2017-02-07  1:09 [PATCH] power: supply: Add driver for TI BQ2416X battery charger Wojciech Ziemba
  2017-02-07  3:00 ` Liam Breck
@ 2017-02-07 11:08 ` Andy Shevchenko
  2017-03-20  5:58 ` Sebastian Reichel
  2 siblings, 0 replies; 6+ messages in thread
From: Andy Shevchenko @ 2017-02-07 11:08 UTC (permalink / raw)
  To: Wojciech Ziemba
  Cc: Sebastian Reichel, Rob Herring, Mark Rutland, wojciech.ziemba,
	linux-pm, devicetree, linux-kernel

On Tue, Feb 7, 2017 at 3:09 AM, Wojciech Ziemba <wo.ziemba@gmail.com> wrote:
> There is interest in adding a Linux driver for TI BQ2416X battery
> charger. The driver supports BQ24160 chip, thus can be easily extended
> for other BQ2416X family chargers.
> Device exposes 'POWER_SUPPLY_PROP_*' properties and a number of knobs
> for controlling the charging process as well as sends power supply change
> notification via power-supply subsystem.

Some style related comments

0. Less lines of code -> better! (but not be too eager)
1. Use GENMASK()
2.
int ret = -EINVAL;

if (a)
 ret = x;
else if (b)
 ret = y;

>> else
>> ret = -EINVAL;

Those are redundant.

if (ret)
 return ret;

3. #ifdef:s are ugly.
For CONFIG_PM_SLEEP functions just use
__maybe_unused attribute.

4. Try to avoid #ifdef CONFIG_OF (this will limit driver for OF case
when it might be used elsewhere, e.g. ACPI case)

5. Check headers and library for existing helpers. I believe some of
your bit operations and such already have nice helpers in kernel.

-- 
With Best Regards,
Andy Shevchenko

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

* Re: [PATCH] power: supply: Add driver for TI BQ2416X battery charger
  2017-02-07  1:09 [PATCH] power: supply: Add driver for TI BQ2416X battery charger Wojciech Ziemba
  2017-02-07  3:00 ` Liam Breck
  2017-02-07 11:08 ` Andy Shevchenko
@ 2017-03-20  5:58 ` Sebastian Reichel
  2017-03-22 13:53   ` Wojciech Ziemba
  2 siblings, 1 reply; 6+ messages in thread
From: Sebastian Reichel @ 2017-03-20  5:58 UTC (permalink / raw)
  To: Wojciech Ziemba
  Cc: robh+dt, mark.rutland, wojciech.ziemba, linux-pm, devicetree,
	linux-kernel

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

Hi,

On Tue, Feb 07, 2017 at 01:09:09AM +0000, Wojciech Ziemba wrote:
> There is interest in adding a Linux driver for TI BQ2416X battery
> charger.

This is a strange sentence to introduce a patch. If there wasn't
you wouldn't have sent a patch...

> The driver supports BQ24160 chip, thus can be easily extended
> for other BQ2416X family chargers.

Doesn't it already do? Do you mean "I only tested the driver using
bq24160"?

> Device exposes 'POWER_SUPPLY_PROP_*' properties

ok.

> and a number of knobs for controlling the charging process

missing sysfs ABI documentation. Most of them are probably either
not needed, or should become standard POWER_SUPPLY_PROP_ properties.

> as well as sends power supply change notification via power-supply
> subsystem.

ok.

> Signed-off-by: Wojciech Ziemba <wojciech.ziemba@verifone.com>
> ---
>  .../devicetree/bindings/power/supply/bq2416x.txt   |   71 +
>  drivers/power/supply/Kconfig                       |    7 +
>  drivers/power/supply/Makefile                      |    1 +
>  drivers/power/supply/bq2416x_charger.c             | 1871 ++++++++++++++++++++
>  include/dt-bindings/power/bq2416x_charger.h        |   23 +
>  include/linux/power/bq2416x_charger.h              |   80 +
>  6 files changed, 2053 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/power/supply/bq2416x.txt
>  create mode 100644 drivers/power/supply/bq2416x_charger.c
>  create mode 100644 include/dt-bindings/power/bq2416x_charger.h
>  create mode 100644 include/linux/power/bq2416x_charger.h
> 
> diff --git a/Documentation/devicetree/bindings/power/supply/bq2416x.txt b/Documentation/devicetree/bindings/power/supply/bq2416x.txt
> new file mode 100644
> index 0000000..8f4b814
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/supply/bq2416x.txt
> @@ -0,0 +1,71 @@
> +Binding for TI bq2416x Li-Ion Charger
> +
> +Required properties:
> +===================
> +- compatible: one of the following:
> + * "ti,bq24160"
> + * "ti,bq24160a"
> + * "ti,bq24161"
> + * "ti,bq24161b"
> + * "ti,bq24163"
> + * "ti,bq24168"
> +
> +- reg:			I2c address of the device.
> +- interrupt-parent:	The irq controller(phandle) connected to INT pin on BQ2416x
> +- interrupts:		The irq number assigned for INT pin.
> +
> +Optional properties:
> +===================
> +- interrupt-names:		Meanigfull irq name.

Drop this, it's not used.

> +- ti,charge-voltage:		Charge volatge [mV].
> +- ti,charge-current:		Charge current [mA].
> +- ti,termination-current:	Termination current [mA}.

These are battery dependent. You should get them from the
battery instead.

> +- ti,in-current-limit:		IN source current limit. enum:
> +				- IN_CURR_LIM_1500MA (0)
> +				- IN_CURR_LIM_2500MA (1)

This is probably needed. Just use an integer with the current
instead of the enum. The driver can just bail out if invalid
value was specified.

> +- ti,usb-current-limit:		USB source current limit. enum:
> +				- USB_CURR_LIM_100MA (0)
> +				- USB_CURR_LIM_150MA (1)
> +				- USB_CURR_LIM_500MA (2)
> +				- USB_CURR_LIM_800MA (3)
> +				- USB_CURR_LIM_900MA (4)
> +				- USB_CURR_LIM_1500MA (5)

Let's not add that to the DT binding. It should be auto-detected.
Additionally you can make the sysfs property writable.

> +- ti,status-pin-enable:		0 or 1. Enable charge status output STAT pin.
> +- ti,current-termination-enable:0 or 1. Enable charge current termination.

If termination current is specified -> enable, otherwise -> disable,
so not needed.

> +- ti,usb-dpm-voltage:		USB dpm voltage [mV]. Refer to datasheet.
> +- ti,in-dpm-voltage:		IN dpm voltage [mV].

I will have to check datasheet before commenting about this one.

> +- ti,safety-timer:		Safety timer. enum:
> +			- TMR_27MIN (0)
> +			- TMR_6H (1)
> +			- TMR_9H (2)
> +			- TMR_OFF (3)

This does not belong into DT. Just always set it to 27 minutes and
properly reset the timer in the driver. You will also need a suspend
handler, that disables the timer (or wakeup every now and then to
reset it).

> +- ti,supplied-to:	string array: max 4. Names of devices to which
> +			the charger supplies.

There is a standard binding for this, which is documented here:

Documentation/devicetree/bindings/power/supply/power_supply.txt

> +Example:
> +=======
> +#include <dt-bindings/power/bq2416x_charger.h>
> +
> +bq24160@6b {
> +	compatible = "ti,bq24160";
> +	reg = <0x6b>;
> +
> +	interrupt-parent = <&gpio5>;
> +	interrupts = <31 IRQ_TYPE_EDGE_RISING>;
> +	interrupt-names = "bq24160-charge-status-change";
> +
> +	ti,charge-voltage = <4300>;
> +	ti,charge-current = <1300>;
> +	ti,termination-current = <100>;
> +	ti,in-current-limit = <IN_CURR_LIM_1500MA>;
> +	ti,usb-current-limit = <USB_CURR_LIM_1500MA>;
> +	ti,status-pin-enable = <1>;
> +	ti,current-termination-enable = <1>;
> +	ti,usb-dpm-voltage = <4300>;
> +	ti,in-dpm-voltage = <4300>;
> +	ti,safety-timer = <TMR_6H>; /* charge max 6h */
> +	ti,supplied-to = "bq27621-0";
> +};
> diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
> index 76806a0..575096e 100644
> --- a/drivers/power/supply/Kconfig
> +++ b/drivers/power/supply/Kconfig
> @@ -413,6 +413,13 @@ config CHARGER_BQ2415X
>  	  You'll need this driver to charge batteries on e.g. Nokia
>  	  RX-51/N900.
>  
> +config CHARGER_BQ2416X
> +	tristate "TI BQ2416x Dual-Input, Single Cell Switch-Mode Li-Ion charger"
> +	depends on I2C
> +	help
> +	  Say Y here to enable support for the TI BQ2416x battery charger.
> +	  The driver is configured to operate with a single lithium cell.
> +
>  config CHARGER_BQ24190
>  	tristate "TI BQ24190 battery charger driver"
>  	depends on I2C
> diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
> index 36c599d..73711e0 100644
> --- a/drivers/power/supply/Makefile
> +++ b/drivers/power/supply/Makefile
> @@ -63,6 +63,7 @@ obj-$(CONFIG_CHARGER_MAX8997)	+= max8997_charger.o
>  obj-$(CONFIG_CHARGER_MAX8998)	+= max8998_charger.o
>  obj-$(CONFIG_CHARGER_QCOM_SMBB)	+= qcom_smbb.o
>  obj-$(CONFIG_CHARGER_BQ2415X)	+= bq2415x_charger.o
> +obj-$(CONFIG_CHARGER_BQ2416X)	+= bq2416x_charger.o
>  obj-$(CONFIG_CHARGER_BQ24190)	+= bq24190_charger.o
>  obj-$(CONFIG_CHARGER_BQ24257)	+= bq24257_charger.o
>  obj-$(CONFIG_CHARGER_BQ24735)	+= bq24735-charger.o
> diff --git a/drivers/power/supply/bq2416x_charger.c b/drivers/power/supply/bq2416x_charger.c
> new file mode 100644
> index 0000000..fa13e55
> --- /dev/null
> +++ b/drivers/power/supply/bq2416x_charger.c
> @@ -0,0 +1,1871 @@
> +/*
> + * Driver for BQ2416X Li-Ion Battery Charger
> + *
> + * Copyright (C) 2015 Verifone, Inc.
> + *
> + * Author: Wojciech Ziemba <wojciech.ziemba@verifone.com>
> + *
> + * This package is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * THIS PACKAGE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR
> + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
> + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
> + *
> + * The bq2416x series is a 2.5A, Dual-Input, Single-Cell Switched-Mode
> + * Li-Ion Battery Charger with Power
> + * Path Management and I2C Interface

strange line wrapping above.

> + * This driver was tested on BQ24160.
> + *
> + * Datasheets:
> + * http://www.ti.com/product/bq24160
> + * http://www.ti.com/product/bq24160a
> + * http://www.ti.com/product/bq24161
> + * http://www.ti.com/product/bq24161b
> + * http://www.ti.com/product/bq24163
> + * http://www.ti.com/product/bq24168
> + */
> +
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/regmap.h>
> +#include <linux/power_supply.h>
> +#include <linux/power/bq2416x_charger.h>

[...]

> diff --git a/include/linux/power/bq2416x_charger.h b/include/linux/power/bq2416x_charger.h
> new file mode 100644
> index 0000000..c561666
> --- /dev/null
> +++ b/include/linux/power/bq2416x_charger.h
> @@ -0,0 +1,80 @@
> +/*
> + * Driver for BQ2416X Li-Ion Battery Charger
> + *
> + * Copyright (C) 2015 Verifone, Inc.
> + *
> + * Author: Wojciech Ziemba <wojciech.ziemba@verifone.com>
> + *
> + * This package is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * THIS PACKAGE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR
> + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
> + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
> + *
> + * The bq2416x series is a 2.5A, Dual-Input, Single-Cell Switched-Mode
> + * Li-Ion Battery Charger with Power
> + * Path Management and I2C Interface
> + *
> + */
> +
> +#ifndef _BQ2416X_CHARGER_H
> +#define _BQ2416X_CHARGER_H
> +
> +/* IN(Wall) source limit */
> +enum in_curr_lim {
> +	IN_CURR_LIM_1500MA,
> +	IN_CURR_LIM_2500MA,
> +};
> +
> +/* USB source current limit */
> +enum usb_curr_lim {
> +	USB_CURR_LIM_100MA,
> +	USB_CURR_LIM_150MA,
> +	USB_CURR_LIM_500MA,
> +	USB_CURR_LIM_800MA,
> +	USB_CURR_LIM_900MA,
> +	USB_CURR_LIM_1500MA,
> +};
> +
> +/* Safety timer settings */
> +enum safe_tmr {
> +	TMR_27MIN,
> +	TMR_6H,
> +	TMR_9H,
> +	TMR_OFF,
> +};
> +
> +/**
> + * struct bq2416x_pdata - Platform data for bq2416x chip. It contains default
> + *			  board voltages and currents.
> + * @charge_voltage: charge voltage in [mV]
> + * @charge_current: charge current in [mA]
> + * @in_curr_limit: Current limit for IN source . Enum 1.5A or 2.5A
> + * @usb_curr_limit: Current limit for USB source Enum 100mA - 1500mA
> + * @curr_term_en: enable charge terination by current
> + * @term_current: charge termination current in [mA]
> + * @usb_dpm_voltage: USB DPM voltage [mV]
> + * @in_dpm_voltage: IN DPM voltage [mV]
> + * @stat_pin_en: status pin enable
> + * @safety_timer: safety timer enum: 27min, 6h, 9h, off.
> + * @num_supplicants: number of notify devices. Max 4.
> + * @supplied_to: array of names of supplied to devices
> + */
> +struct bq2416x_pdata {
> +	int charge_voltage;
> +	int charge_current;
> +	enum in_curr_lim in_curr_limit;
> +	enum usb_curr_lim usb_curr_limit;
> +	int curr_term_en;
> +	int term_current;
> +	int usb_dpm_voltage;
> +	int in_dpm_voltage;
> +	int stat_pin_en;
> +	enum safe_tmr safety_timer;
> +	int num_supplicants;
> +	const char *supplied_to[4];
> +};
> +
> +#endif /* _BQ2416X_CHARGER_H */

Please use device properties instead of platform_data (especially if
this is not yet used).

Apart from the comments I only skipped quickly over the driver. I
will have a more detailed look once the basic things are fixed.

-- Sebastian

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

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

* Re: [PATCH] power: supply: Add driver for TI BQ2416X battery charger
  2017-03-20  5:58 ` Sebastian Reichel
@ 2017-03-22 13:53   ` Wojciech Ziemba
  2017-03-23 13:54     ` Sebastian Reichel
  0 siblings, 1 reply; 6+ messages in thread
From: Wojciech Ziemba @ 2017-03-22 13:53 UTC (permalink / raw)
  To: Sebastian Reichel, Wojciech Ziemba
  Cc: robh+dt, mark.rutland, linux-pm, devicetree, linux-kernel

Hi,
On 20/03/17 05:58, Sebastian Reichel wrote:
> Hi,
>
> On Tue, Feb 07, 2017 at 01:09:09AM +0000, Wojciech Ziemba wrote:
>> There is interest in adding a Linux driver for TI BQ2416X battery
>> charger.
> This is a strange sentence to introduce a patch. If there wasn't
> you wouldn't have sent a patch...

Will remove. Thanks.

>> The driver supports BQ24160 chip, thus can be easily extended
>> for other BQ2416X family chargers.
> Doesn't it already do? Do you mean "I only tested the driver using
> bq24160"?

The driver covers: bq24160, bq24160A, bq24161, bq24161B, bq24163, bq24168.
These have the same register set but are slightly different:
safety and watchdog timers, different USB source detection etc.

>> Device exposes 'POWER_SUPPLY_PROP_*' properties
> ok.
>
>> and a number of knobs for controlling the charging process
> missing sysfs ABI documentation. Most of them are probably either
> not needed, or should become standard POWER_SUPPLY_PROP_ properties.

Well, it is up to you. POWER_SUPPLY props are already there.
If any new POWER_SUPPLY props should be introduced, it is an open question.
I agree sysfs knobs could be not needed, but many PSY drivers
still use them. Couldn't them still be useful even if don't fit the model directly?
Especially for more specialized embedded systems?

>> as well as sends power supply change notification via power-supply
>> subsystem.
> ok.
>
>> Signed-off-by: Wojciech Ziemba <wojciech.ziemba@verifone.com>
>> ---
>>   .../devicetree/bindings/power/supply/bq2416x.txt   |   71 +
>>   drivers/power/supply/Kconfig                       |    7 +
>>   drivers/power/supply/Makefile                      |    1 +
>>   drivers/power/supply/bq2416x_charger.c             | 1871 ++++++++++++++++++++
>>   include/dt-bindings/power/bq2416x_charger.h        |   23 +
>>   include/linux/power/bq2416x_charger.h              |   80 +
>>   6 files changed, 2053 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/power/supply/bq2416x.txt
>>   create mode 100644 drivers/power/supply/bq2416x_charger.c
>>   create mode 100644 include/dt-bindings/power/bq2416x_charger.h
>>   create mode 100644 include/linux/power/bq2416x_charger.h
>>
>> diff --git a/Documentation/devicetree/bindings/power/supply/bq2416x.txt b/Documentation/devicetree/bindings/power/supply/bq2416x.txt
>> new file mode 100644
>> index 0000000..8f4b814
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/power/supply/bq2416x.txt
>> @@ -0,0 +1,71 @@
>> +Binding for TI bq2416x Li-Ion Charger
>> +
>> +Required properties:
>> +===================
>> +- compatible: one of the following:
>> + * "ti,bq24160"
>> + * "ti,bq24160a"
>> + * "ti,bq24161"
>> + * "ti,bq24161b"
>> + * "ti,bq24163"
>> + * "ti,bq24168"
>> +
>> +- reg:                      I2c address of the device.
>> +- interrupt-parent: The irq controller(phandle) connected to INT pin on BQ2416x
>> +- interrupts:               The irq number assigned for INT pin.
>> +
>> +Optional properties:
>> +===================
>> +- interrupt-names:          Meanigfull irq name.
> Drop this, it's not used.

Ok. Thanks.

>> +- ti,charge-voltage:                Charge volatge [mV].
>> +- ti,charge-current:                Charge current [mA].
>> +- ti,termination-current:   Termination current [mA}.
> These are battery dependent. You should get them from the
> battery instead.

Ok.

>> +- ti,in-current-limit:              IN source current limit. enum:
>> +                            - IN_CURR_LIM_1500MA (0)
>> +                            - IN_CURR_LIM_2500MA (1)
> This is probably needed. Just use an integer with the current
> instead of the enum. The driver can just bail out if invalid
> value was specified.
>
>> +- ti,usb-current-limit:             USB source current limit. enum:
>> +                            - USB_CURR_LIM_100MA (0)
>> +                            - USB_CURR_LIM_150MA (1)
>> +                            - USB_CURR_LIM_500MA (2)
>> +                            - USB_CURR_LIM_800MA (3)
>> +                            - USB_CURR_LIM_900MA (4)
>> +                            - USB_CURR_LIM_1500MA (5)
> Let's not add that to the DT binding. It should be auto-detected.
> Additionally you can make the sysfs property writable.

I see. That would be perfect.

>> +- ti,status-pin-enable:             0 or 1. Enable charge status output STAT pin.
>> +- ti,current-termination-enable:0 or 1. Enable charge current termination.
> If termination current is specified -> enable, otherwise -> disable,
> so not needed.

Ok.

>> +- ti,usb-dpm-voltage:               USB dpm voltage [mV]. Refer to datasheet.
>> +- ti,in-dpm-voltage:                IN dpm voltage [mV].
> I will have to check datasheet before commenting about this one.
>
>> +- ti,safety-timer:          Safety timer. enum:
>> +                    - TMR_27MIN (0)
>> +                    - TMR_6H (1)
>> +                    - TMR_9H (2)
>> +                    - TMR_OFF (3)
> This does not belong into DT. Just always set it to 27 minutes and
> properly reset the timer in the driver. You will also need a suspend
> handler, that disables the timer (or wakeup every now and then to
> reset it).

Do you mean, more flexible safety timer based on minimal 27min reset?
Will address wakeup in suspend, separately in this driver.

>> +- ti,supplied-to:   string array: max 4. Names of devices to which
>> +                    the charger supplies.
> There is a standard binding for this, which is documented here:
>
> Documentation/devicetree/bindings/power/supply/power_supply.txt

Ok. Thanks.

>> +Example:
>> +=======
>> +#include <dt-bindings/power/bq2416x_charger.h>
>> +
>> +bq24160@6b {
>> +    compatible = "ti,bq24160";
>> +    reg = <0x6b>;
>> +
>> +    interrupt-parent = <&gpio5>;
>> +    interrupts = <31 IRQ_TYPE_EDGE_RISING>;
>> +    interrupt-names = "bq24160-charge-status-change";
>> +
>> +    ti,charge-voltage = <4300>;
>> +    ti,charge-current = <1300>;
>> +    ti,termination-current = <100>;
>> +    ti,in-current-limit = <IN_CURR_LIM_1500MA>;
>> +    ti,usb-current-limit = <USB_CURR_LIM_1500MA>;
>> +    ti,status-pin-enable = <1>;
>> +    ti,current-termination-enable = <1>;
>> +    ti,usb-dpm-voltage = <4300>;
>> +    ti,in-dpm-voltage = <4300>;
>> +    ti,safety-timer = <TMR_6H>; /* charge max 6h */
>> +    ti,supplied-to = "bq27621-0";
>> +};
>> diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
>> index 76806a0..575096e 100644
>> --- a/drivers/power/supply/Kconfig
>> +++ b/drivers/power/supply/Kconfig
>> @@ -413,6 +413,13 @@ config CHARGER_BQ2415X
>>         You'll need this driver to charge batteries on e.g. Nokia
>>         RX-51/N900.
>>   
>> +config CHARGER_BQ2416X
>> +    tristate "TI BQ2416x Dual-Input, Single Cell Switch-Mode Li-Ion charger"
>> +    depends on I2C
>> +    help
>> +      Say Y here to enable support for the TI BQ2416x battery charger.
>> +      The driver is configured to operate with a single lithium cell.
>> +
>>   config CHARGER_BQ24190
>>       tristate "TI BQ24190 battery charger driver"
>>       depends on I2C
>> diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
>> index 36c599d..73711e0 100644
>> --- a/drivers/power/supply/Makefile
>> +++ b/drivers/power/supply/Makefile
>> @@ -63,6 +63,7 @@ obj-$(CONFIG_CHARGER_MAX8997)      += max8997_charger.o
>>   obj-$(CONFIG_CHARGER_MAX8998)      += max8998_charger.o
>>   obj-$(CONFIG_CHARGER_QCOM_SMBB)    += qcom_smbb.o
>>   obj-$(CONFIG_CHARGER_BQ2415X)      += bq2415x_charger.o
>> +obj-$(CONFIG_CHARGER_BQ2416X)       += bq2416x_charger.o
>>   obj-$(CONFIG_CHARGER_BQ24190)      += bq24190_charger.o
>>   obj-$(CONFIG_CHARGER_BQ24257)      += bq24257_charger.o
>>   obj-$(CONFIG_CHARGER_BQ24735)      += bq24735-charger.o
>> diff --git a/drivers/power/supply/bq2416x_charger.c b/drivers/power/supply/bq2416x_charger.c
>> new file mode 100644
>> index 0000000..fa13e55
>> --- /dev/null
>> +++ b/drivers/power/supply/bq2416x_charger.c
>> @@ -0,0 +1,1871 @@
>> +/*
>> + * Driver for BQ2416X Li-Ion Battery Charger
>> + *
>> + * Copyright (C) 2015 Verifone, Inc.
>> + *
>> + * Author: Wojciech Ziemba <wojciech.ziemba@verifone.com>
>> + *
>> + * This package is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * THIS PACKAGE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR
>> + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
>> + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
>> + *
>> + * The bq2416x series is a 2.5A, Dual-Input, Single-Cell Switched-Mode
>> + * Li-Ion Battery Charger with Power
>> + * Path Management and I2C Interface
> strange line wrapping above.

ok.

>> + * This driver was tested on BQ24160.
>> + *
>> + * Datasheets:
>> + * http://www.ti.com/product/bq24160
>> + * http://www.ti.com/product/bq24160a
>> + * http://www.ti.com/product/bq24161
>> + * http://www.ti.com/product/bq24161b
>> + * http://www.ti.com/product/bq24163
>> + * http://www.ti.com/product/bq24168
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/slab.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/delay.h>
>> +#include <linux/i2c.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/hwmon-sysfs.h>
>> +#include <linux/regmap.h>
>> +#include <linux/power_supply.h>
>> +#include <linux/power/bq2416x_charger.h>
> [...]
>
>> diff --git a/include/linux/power/bq2416x_charger.h b/include/linux/power/bq2416x_charger.h
>> new file mode 100644
>> index 0000000..c561666
>> --- /dev/null
>> +++ b/include/linux/power/bq2416x_charger.h
>> @@ -0,0 +1,80 @@
>> +/*
>> + * Driver for BQ2416X Li-Ion Battery Charger
>> + *
>> + * Copyright (C) 2015 Verifone, Inc.
>> + *
>> + * Author: Wojciech Ziemba <wojciech.ziemba@verifone.com>
>> + *
>> + * This package is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * THIS PACKAGE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR
>> + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
>> + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
>> + *
>> + * The bq2416x series is a 2.5A, Dual-Input, Single-Cell Switched-Mode
>> + * Li-Ion Battery Charger with Power
>> + * Path Management and I2C Interface
>> + *
>> + */
>> +
>> +#ifndef _BQ2416X_CHARGER_H
>> +#define _BQ2416X_CHARGER_H
>> +
>> +/* IN(Wall) source limit */
>> +enum in_curr_lim {
>> +    IN_CURR_LIM_1500MA,
>> +    IN_CURR_LIM_2500MA,
>> +};
>> +
>> +/* USB source current limit */
>> +enum usb_curr_lim {
>> +    USB_CURR_LIM_100MA,
>> +    USB_CURR_LIM_150MA,
>> +    USB_CURR_LIM_500MA,
>> +    USB_CURR_LIM_800MA,
>> +    USB_CURR_LIM_900MA,
>> +    USB_CURR_LIM_1500MA,
>> +};
>> +
>> +/* Safety timer settings */
>> +enum safe_tmr {
>> +    TMR_27MIN,
>> +    TMR_6H,
>> +    TMR_9H,
>> +    TMR_OFF,
>> +};
>> +
>> +/**
>> + * struct bq2416x_pdata - Platform data for bq2416x chip. It contains default
>> + *                    board voltages and currents.
>> + * @charge_voltage: charge voltage in [mV]
>> + * @charge_current: charge current in [mA]
>> + * @in_curr_limit: Current limit for IN source . Enum 1.5A or 2.5A
>> + * @usb_curr_limit: Current limit for USB source Enum 100mA - 1500mA
>> + * @curr_term_en: enable charge terination by current
>> + * @term_current: charge termination current in [mA]
>> + * @usb_dpm_voltage: USB DPM voltage [mV]
>> + * @in_dpm_voltage: IN DPM voltage [mV]
>> + * @stat_pin_en: status pin enable
>> + * @safety_timer: safety timer enum: 27min, 6h, 9h, off.
>> + * @num_supplicants: number of notify devices. Max 4.
>> + * @supplied_to: array of names of supplied to devices
>> + */
>> +struct bq2416x_pdata {
>> +    int charge_voltage;
>> +    int charge_current;
>> +    enum in_curr_lim in_curr_limit;
>> +    enum usb_curr_lim usb_curr_limit;
>> +    int curr_term_en;
>> +    int term_current;
>> +    int usb_dpm_voltage;
>> +    int in_dpm_voltage;
>> +    int stat_pin_en;
>> +    enum safe_tmr safety_timer;
>> +    int num_supplicants;
>> +    const char *supplied_to[4];
>> +};
>> +
>> +#endif /* _BQ2416X_CHARGER_H */
> Please use device properties instead of platform_data (especially if
> this is not yet used).

ok.

> Apart from the comments I only skipped quickly over the driver. I
> will have a more detailed look once the basic things are fixed.

Thank you. I Will prepare [RFC v2] addressing your and others comments.

> -- Sebastian

Thanks,
Wojciech

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

* Re: [PATCH] power: supply: Add driver for TI BQ2416X battery charger
  2017-03-22 13:53   ` Wojciech Ziemba
@ 2017-03-23 13:54     ` Sebastian Reichel
  0 siblings, 0 replies; 6+ messages in thread
From: Sebastian Reichel @ 2017-03-23 13:54 UTC (permalink / raw)
  To: Wojciech Ziemba
  Cc: Wojciech Ziemba, robh+dt, mark.rutland, linux-pm, devicetree,
	linux-kernel

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

Hi,

On Wed, Mar 22, 2017 at 01:53:19PM +0000, Wojciech Ziemba wrote:
> [...]
>
> >> and a number of knobs for controlling the charging process
> > missing sysfs ABI documentation. Most of them are probably either
> > not needed, or should become standard POWER_SUPPLY_PROP_ properties.
> 
> Well, it is up to you. POWER_SUPPLY props are already there.
> If any new POWER_SUPPLY props should be introduced, it is an open question.
> I agree sysfs knobs could be not needed, but many PSY drivers
> still use them. Couldn't them still be useful even if don't fit the model directly?
> Especially for more specialized embedded systems?

Yes they may, they also may not. Let's add them once they are
needed, because adding them means, that they become ABI.

> [...]
>
> >> +- ti,safety-timer:          Safety timer. enum:
> >> +                    - TMR_27MIN (0)
> >> +                    - TMR_6H (1)
> >> +                    - TMR_9H (2)
> >> +                    - TMR_OFF (3)
> > This does not belong into DT. Just always set it to 27 minutes and
> > properly reset the timer in the driver. You will also need a suspend
> > handler, that disables the timer (or wakeup every now and then to
> > reset it).
> 
> Do you mean, more flexible safety timer based on minimal 27min reset?
> Will address wakeup in suspend, separately in this driver.

I mean just always use TMR_27MIN in the driver + watchdog to keep
the system charging. You can change the timer to something bigger
in the suspend routine.

-- Sebastian

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

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

end of thread, other threads:[~2017-03-23 13:54 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-07  1:09 [PATCH] power: supply: Add driver for TI BQ2416X battery charger Wojciech Ziemba
2017-02-07  3:00 ` Liam Breck
2017-02-07 11:08 ` Andy Shevchenko
2017-03-20  5:58 ` Sebastian Reichel
2017-03-22 13:53   ` Wojciech Ziemba
2017-03-23 13:54     ` Sebastian Reichel

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