All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/3] power: supply: Add driver for TI BQ2416X battery charger
@ 2018-09-17 12:37 Karsten Mueller
  2018-09-17 12:37 ` [PATCH 2/3] dt-bindings: power: supply: bq2416x driver Karsten Mueller
  2018-09-17 12:37 ` [PATCH 3/3] Add sysfs ABI for bq2416x Karsten Mueller
  0 siblings, 2 replies; 4+ messages in thread
From: Karsten Mueller @ 2018-09-17 12:37 UTC (permalink / raw)
  To: mueller.k
  Cc: renner, Sebastian Reichel, Rob Herring, Mark Rutland, linux-pm,
	linux-kernel, devicetree

This patch set is to add power driver for TI BQ2416X.

Driver is tested with BQ24160 and is based on:
 https://lore.kernel.org/patchwork/patch/758105/

Signed-off-by: Karsten Mueller <mueller.k@efe-gmbh.de>
---
 drivers/power/supply/Kconfig           |    6 +
 drivers/power/supply/Makefile          |    1 +
 drivers/power/supply/bq2416x_charger.c | 1669 ++++++++++++++++++++++++
 3 files changed, 1676 insertions(+)
 create mode 100644 drivers/power/supply/bq2416x_charger.c

diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index ff6dab0bf..6e3604458 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -543,6 +543,12 @@ 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.
+
 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 a26b402c4..f44b24e41 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -75,6 +75,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 000000000..88ddbde5a
--- /dev/null
+++ b/drivers/power/supply/bq2416x_charger.c
@@ -0,0 +1,1669 @@
+// SPDX-License-Identifier: GPL-1.0+
+/*
+ * Driver for BQ2416X Li-Ion Battery Charger
+ *
+ * Copyright (C) 2015 Verifone, Inc.
+ * Wojciech Ziemba <wojciech.ziemba@verifone.com>
+ *
+ * Copyright (C) 2018 EFE GmbH
+ * Karsten Müller <mueller.k@efe-gmbh.de>
+ * Jens Renner <renner@efe-gmbh.de>
+ *
+ * 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>
+
+/* 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. 10 seconds in reserve */
+#define BQ2416X_WATCHDOG_TIMER	(30 - 10)
+/* 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)
+#define BQ2416X_REG_BAT_VOLT_USB_DETECT_MASK	BIT(0)
+
+/* 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 [uV] */
+#define BQ2416X_CHARGE_VOLTAGE_MIN		3500000
+#define BQ2416X_CHARGE_VOLTAGE_MAX		4440000
+#define BQ2416X_CHARGE_VOLTAGE_STEP		20000
+
+/* IN current limit */
+#define BQ2416X_IN_CURR_LIM_1500		0
+#define BQ2416X_IN_CURR_LIM_2500		1
+
+/* Charge current [uA] */
+#define BQ2416X_CHARGE_CURRENT_MIN		550000
+#define BQ2416X_CHARGE_CURRENT_MAX		2500000
+#define BQ2416X_CHARGE_CURRENT_STEP		75000
+
+/* Charge termination current in uA */
+#define BQ2416X_CHARGE_TERM_CURRENT_MIN		50000
+#define BQ2416X_CHARGE_TERM_CURRENT_MAX		400000
+#define BQ2416X_CHARGE_TERM_CURRENT_STEP	50000
+
+/* USB DPM voltage [uV] */
+#define BQ2416X_DPM_USB_VOLTAGE_MIN		4200000
+#define BQ2416X_DPM_USB_VOLTAGE_MAX		4760000
+#define BQ2416X_DPM_USB_VOLTAGE_STEP		80000
+
+/* IN DPM voltage [uV] */
+#define BQ2416X_DPM_IN_VOLTAGE_MIN		4200000
+#define BQ2416X_DPM_IN_VOLTAGE_MAX		4760000
+#define BQ2416X_DPM_IN_VOLTAGE_STEP		80000
+
+/* Supported chips */
+enum  bq2416x_type {
+	BQ24160 = 0,
+	BQ24160A,
+	BQ24161,
+	BQ24161B,
+	BQ24163,
+	BQ24168,
+};
+
+/* Charger status */
+enum {
+	STAT_NO_VALID_SOURCE = 0,
+	STAT_IN_READY,
+	STAT_USB_READY,
+	STAT_CHARGING_FROM_IN,
+	STAT_CHARGING_FROM_USB,
+	STAT_CHARGE_DONE,
+	STAT_NA,
+	STAT_FAULT,
+};
+
+/* Charger status to power subsys status map */
+static const int bq2416x_charge_status[] = {
+	[STAT_NO_VALID_SOURCE] = POWER_SUPPLY_STATUS_NOT_CHARGING,
+	[STAT_IN_READY] = POWER_SUPPLY_STATUS_NOT_CHARGING,
+	[STAT_USB_READY] = POWER_SUPPLY_STATUS_NOT_CHARGING,
+	[STAT_CHARGING_FROM_IN] = POWER_SUPPLY_STATUS_CHARGING,
+	[STAT_CHARGING_FROM_USB] = POWER_SUPPLY_STATUS_CHARGING,
+	[STAT_CHARGE_DONE] = POWER_SUPPLY_STATUS_FULL,
+	[STAT_NA] = POWER_SUPPLY_STATUS_UNKNOWN,
+	[STAT_FAULT] = POWER_SUPPLY_STATUS_NOT_CHARGING,
+};
+
+/* Charger fault */
+enum {
+	FAULT_NORMAL = 0,
+	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 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 = 0,
+	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 = 0,
+	BATSTAT_BATTERY_OVP,
+	BATSTAT_BATTERY_NOT_PRESENT,
+	BATSTAT_BATTERY_NA,
+};
+
+/* IN(Wall) source limit */
+enum in_curr_lim {
+	IN_CURR_LIM_1500MA = 0,
+	IN_CURR_LIM_2500MA,
+};
+
+/*
+ * USB source current limit
+ * AUTO as auto detection (BQ24160/0A/3) or limit of default mode
+ */
+enum usb_curr_lim {
+	USB_CURR_LIM_AUTO   = -EINVAL,
+	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,
+};
+
+/* Safety timer settings */
+enum safe_tmr {
+	TMR_27MIN = 0,
+	TMR_6H,
+	TMR_9H,
+	TMR_OFF,
+};
+
+static const int bq2416x_usb_curr_lim[] = {
+	[USB_CURR_LIM_100MA]	= 100000,
+	[USB_CURR_LIM_150MA]	= 150000,
+	[USB_CURR_LIM_500MA]	= 500000,
+	[USB_CURR_LIM_800MA]	= 800000,
+	[USB_CURR_LIM_900MA]	= 900000,
+	[USB_CURR_LIM_1500MA]	= 1500000,
+};
+
+static const int bq24160_in_lim[] = {
+	[IN_CURR_LIM_1500MA] = 1500000,
+	[IN_CURR_LIM_2500MA] = 2500000,
+};
+
+/* 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 = 0,
+	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_initdata - Config data for bq2416x chip.
+ * It contains default board voltages and currents.
+ * @charge_voltage: charge voltage in [uV]
+ * @charge_current: charge current in [uA]
+ * @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 or AUTO for using auto detection
+ * @curr_term_en: enable charge termination by current
+ * @nobat_mode: enable no battery operation mode
+ *  (charge disable, curr_term = off, nobat_op = 1)
+ * @term_current: charge termination current in [uA]
+ * @usb_dpm_voltage: USB DPM voltage [uV]
+ * @in_dpm_voltage: IN DPM voltage [uV]
+ * @stat_pin_en: status pin enable
+ * @safety_timer: safety timer enum: 27min, 6h, 9h, off.
+ */
+struct bq2416x_initdata {
+	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 nobat_mode;
+	int term_current;
+	int usb_dpm_voltage;
+	int in_dpm_voltage;
+	int stat_pin_en;
+	enum safe_tmr safety_timer;
+};
+
+/* Default: no battery mode, battery can be specified in DT */
+const struct bq2416x_initdata initdefault = {
+	.charge_voltage		= 4200000,
+	.charge_current		= 0,
+	.in_curr_limit		= IN_CURR_LIM_1500MA,
+	.usb_curr_limit		= USB_CURR_LIM_AUTO,
+	.curr_term_en		= 0,
+	.nobat_mode		= 1,
+	.term_current		= 0,
+	.usb_dpm_voltage	= 4200000,
+	.in_dpm_voltage		= 4200000,
+	.stat_pin_en		= 1,
+	.safety_timer		= TMR_9H
+};
+
+/**
+ * struct bq2416x_priv - this device's 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_initdata idata;
+	struct power_supply *psy;
+	struct power_supply_desc psy_desc;
+	struct delayed_work watchdog;
+	u8 fault_reg;
+	u8 stat_reg;
+	char *model;
+	char *name;
+	int idr;
+};
+
+/* each registered chip must have a unique id */
+static DEFINE_IDR(bq2416x_idr);
+static DEFINE_MUTEX(bq2416x_idr_mutex);
+
+/*
+ * Return the index in 'tbl' of greatest value that is less than or equal to
+ * 'val'.  The index range returned is 0 to 'tbl_size' - 1.  Assumes that
+ * the values in 'tbl' are sorted from smallest to largest and 'tbl_size'
+ * is less than 2^8.
+ */
+static u8 bq2416x_find_idx(const int tbl[], int tbl_size, int v)
+{
+	int i;
+
+	for (i = 1; i < tbl_size; i++)
+		if (v < tbl[i])
+			break;
+
+	return i - 1;
+}
+
+/*
+ * Converts value to its regulation binary representation.
+ */
+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
+ * All registers except vendor register are writeable and should not be cached.
+ */
+static bool bq2416x_writeable(struct device *dev, unsigned int reg)
+{
+	return !(reg == BQ2416X_REG_VENDOR);
+}
+
+static struct regmap_config bq2416x_i2c_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.writeable_reg = bq2416x_writeable,
+	.volatile_reg = bq2416x_writeable,
+	.cache_type = REGCACHE_NONE,
+	.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_STATUS:
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+		ret = 1;
+		break;
+	default:
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static enum power_supply_property bq2416x_power_supply_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_PRESENT,
+	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_CHARGE_TERM_CURRENT,
+	POWER_SUPPLY_PROP_SCOPE
+};
+
+static int bq2416x_set_status(struct bq2416x_priv *bq2416x, int status)
+{
+	unsigned int charge_disable;
+	int ret;
+
+	if (status == POWER_SUPPLY_STATUS_CHARGING)
+		charge_disable = 0;
+	else if (status == POWER_SUPPLY_STATUS_NOT_CHARGING)
+		charge_disable = BQ2416X_REG_CONTROL_CE_MASK;
+	else
+		return -EINVAL;
+
+	ret = regmap_update_bits(bq2416x->regmap,
+			BQ2416X_REG_CONTROL,
+			BQ2416X_REG_CONTROL_RESET_MASK |
+			BQ2416X_REG_CONTROL_CE_MASK,
+			charge_disable);
+
+	return ret;
+}
+
+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];
+
+	return ret;
+}
+
+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] != 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;
+}
+
+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;
+}
+
+static int bq2416x_get_bat_present(struct bq2416x_priv *bq2416x, int *present)
+{
+	unsigned int reg_val;
+	int ret;
+
+	ret = regmap_read(bq2416x->regmap, BQ2416X_REG_SUP_STATUS, &reg_val);
+	if (unlikely(ret))
+		return ret;
+	reg_val = BF_GET(reg_val, BQ2416X_REG_SUP_STATUS_BATSTAT_MASK);
+
+	*present = (reg_val == BATSTAT_BATTERY_PRESENT) ||
+			(reg_val == BATSTAT_BATTERY_OVP);
+
+	return ret;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+static int bq2416x_get_term_current(struct bq2416x_priv *bq2416x,
+					unsigned int *term_current)
+{
+	int term_curr;
+	int ret;
+
+	ret = regmap_read(bq2416x->regmap, BQ2416X_REG_TERM, &term_curr);
+	if (unlikely(ret))
+		return ret;
+
+	*term_current = BF_GET(term_curr, BQ2416X_REG_TERM_TERM_CURR_MASK) *
+			BQ2416X_CHARGE_TERM_CURRENT_STEP +
+			BQ2416X_CHARGE_TERM_CURRENT_MIN;
+	return ret;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+/*
+ * Check (and clear) fault bits, detect new fault status and save last fault.
+ * It should be called before reading status register to not lose any faults.
+ */
+static int bq2416x_check_fault(struct bq2416x_priv *bq2416x, bool klog_en)
+{
+	int ret, reg_val, fault;
+
+	ret = regmap_read(bq2416x->regmap, BQ2416X_REG_STATUS, &reg_val);
+	if (ret)
+		return ret;
+
+	fault = BF_GET(reg_val, BQ2416X_REG_STATUS_FAULT_MASK);
+
+	if (fault && (fault != bq2416x->fault_reg)) {
+		if (klog_en)
+			dev_warn(bq2416x->dev, "%s",
+					bq2416x_charge_fault[fault].str);
+		bq2416x->fault_reg = fault;
+	}
+	return 0;
+}
+
+/*
+ * USB current detection, only available for bq24160/0A/3.
+ * No effect on other chips.
+ */
+static int bq2416x_current_detection(struct bq2416x_priv *bq2416x)
+{
+	int ret;
+
+	ret = regmap_update_bits(bq2416x->regmap,
+			BQ2416X_REG_BAT_VOLT,
+			BQ2416X_REG_BAT_VOLT_USB_DETECT_MASK,
+			BQ2416X_REG_BAT_VOLT_USB_DETECT_MASK);
+	dev_dbg(bq2416x->dev, "Current detection");
+	return ret;
+}
+
+static int bq2416x_reset_watchdog_tmr(struct bq2416x_priv *bq2416x)
+{
+	int ret;
+
+	/* update of watchdog bit would clear fault bits */
+	bq2416x_check_fault(bq2416x, 1);
+	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;
+}
+
+/*
+ * Check and returns last detected fault and reset fault state.
+ * (No additional IRQ gets triggered in case of a persistent fault).
+ * State 'good' only is returned if no fault occurred since last call.
+ */
+static int bq2416x_get_health(struct bq2416x_priv *bq2416x, int *health)
+{
+	bq2416x_check_fault(bq2416x, 0);
+	*health = bq2416x_charge_fault[bq2416x->fault_reg].id;
+	bq2416x->fault_reg = 0;
+	return 0;
+}
+
+static int bq2416x_configure(struct bq2416x_priv *bq2416x)
+{
+	struct bq2416x_initdata *idata = &bq2416x->idata;
+	int ret;
+	unsigned int mask, bits;
+
+	ret = bq2416x_reset_watchdog_tmr(bq2416x);
+	if (unlikely(ret))
+		return ret;
+
+	/* sequence of register writes important for nobat operation */
+	mask =  BQ2416X_REG_CONTROL_RESET_MASK |
+			BQ2416X_REG_CONTROL_EN_STAT_MASK |
+			BQ2416X_REG_CONTROL_CE_MASK;
+
+	bits =  BF_SHIFT(idata->stat_pin_en,
+			BQ2416X_REG_CONTROL_EN_STAT_MASK) |
+			BF_SHIFT(idata->nobat_mode,
+			BQ2416X_REG_CONTROL_CE_MASK);
+
+	ret = regmap_update_bits(bq2416x->regmap,
+			BQ2416X_REG_CONTROL,
+			mask,
+			bits);
+	if (unlikely(ret))
+		return ret;
+
+	ret = regmap_update_bits(bq2416x->regmap,
+			BQ2416X_REG_CONTROL,
+			BQ2416X_REG_CONTROL_RESET_MASK |
+			BQ2416X_REG_CONTROL_TE_MASK,
+			BF_SHIFT(idata->curr_term_en,
+				BQ2416X_REG_CONTROL_TE_MASK));
+	if (unlikely(ret))
+		return ret;
+
+	ret = regmap_update_bits(bq2416x->regmap,
+			BQ2416X_REG_SUP_STATUS,
+			BQ2416X_REG_SUP_STATUS_EN_NOBATOP_MASK,
+			BF_SHIFT(idata->nobat_mode,
+				BQ2416X_REG_SUP_STATUS_EN_NOBATOP_MASK));
+	if (unlikely(ret))
+		return ret;
+
+	ret = bq2416x_set_charge_voltage(bq2416x, idata->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(idata->in_curr_limit,
+				BQ2416X_REG_BAT_VOLT_IN_CURR_LIM_MASK));
+	if (unlikely(ret))
+		return ret;
+
+	if (idata->usb_curr_limit != USB_CURR_LIM_AUTO) {
+		ret = regmap_update_bits(bq2416x->regmap,
+			BQ2416X_REG_CONTROL, BQ2416X_REG_CONTROL_RESET_MASK |
+			BQ2416X_REG_CONTROL_USB_CURR_LIM_MASK,
+			BF_SHIFT(idata->usb_curr_limit,
+				BQ2416X_REG_CONTROL_USB_CURR_LIM_MASK));
+		if (unlikely(ret))
+			return ret;
+	}
+
+	ret = bq2416x_set_charge_current(bq2416x, idata->charge_current);
+	if (unlikely(ret))
+		return ret;
+
+	ret = bq2416x_set_term_current(bq2416x, idata->term_current);
+	if (unlikely(ret))
+		return ret;
+
+	ret = bq2416x_set_usb_dpm_voltage(bq2416x, idata->usb_dpm_voltage);
+	if (unlikely(ret))
+		return ret;
+
+	ret = bq2416x_set_in_dpm_voltage(bq2416x, idata->in_dpm_voltage);
+	if (unlikely(ret))
+		return ret;
+
+	mask = BQ2416X_REG_NTC_TMR_MASK | BQ2416X_REG_NTC_TS_EN_MASK;
+	bits = BF_SHIFT(idata->safety_timer, BQ2416X_REG_NTC_TMR_MASK);
+
+	/* disable battery temp monitor, if no battery defined */
+	if (!idata->nobat_mode)
+		bits |= BQ2416X_REG_NTC_TS_EN_MASK;
+
+	ret = regmap_update_bits(bq2416x->regmap,
+			BQ2416X_REG_NTC, mask, bits);
+
+	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;
+
+	dev_dbg(bq2416x->dev, "IRQ");
+	bq2416x_check_fault(bq2416x, 1);
+	if (bq2416x->idata.usb_curr_limit == -EINVAL)
+		bq2416x_current_detection(bq2416x);
+
+	/* 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 property callbacks */
+
+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_PRESENT:
+		ret = bq2416x_get_bat_present(bq2416x, &val->intval);
+		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);
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+		val->intval = BQ2416X_CHARGE_CURRENT_MAX;
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+		ret = bq2416x_get_charge_voltage(bq2416x, &val->intval);
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+		val->intval = BQ2416X_CHARGE_VOLTAGE_MAX;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+		ret = bq2416x_get_term_current(bq2416x, &val->intval);
+		break;
+	case POWER_SUPPLY_PROP_SCOPE:
+		val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	pm_runtime_put_sync(bq2416x->dev);
+	return ret;
+}
+
+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_STATUS:
+		ret = bq2416x_set_status(bq2416x, val->intval);
+		break;
+	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);
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+		ret = bq2416x_set_charge_voltage(bq2416x, val->intval);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+		ret = bq2416x_set_term_current(bq2416x, val->intval);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	pm_runtime_put_sync(bq2416x->dev);
+	return ret;
+}
+
+/* device attributes callbacks */
+
+
+static ssize_t supply_sel_show(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 (val & BQ2416X_REG_STATUS_SUPPLY_SEL_MASK)
+		str = "usb";
+	else
+		str = "in";
+
+	return sprintf(buf, "%s\n", str);
+}
+
+static ssize_t supply_sel_store(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 supply_status_show(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
+		return -EINVAL;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", str);
+}
+
+static ssize_t in_current_limit_show(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 in_current_limit_store(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 < 2500000)
+		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 usb_current_limit_show(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 usb_current_limit_store(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;
+
+	reg_bits = bq2416x_find_idx(bq2416x_usb_curr_lim,
+				ARRAY_SIZE(bq2416x_usb_curr_lim), curr);
+
+	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 dpm_voltage_show(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 dpm_voltage_store(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 ts_fault_show(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;
+
+	val = BF_GET(val, BQ2416X_REG_NTC_TS_FAULT_MASK);
+	str = bq2416x_ts_fault[val];
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", str);
+}
+
+static ssize_t sysfs_bit_show(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 sysfs_bit_store(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, sysfs_bit_show,	\
+	sysfs_bit_store, _reg, _bit)
+
+static DEVICE_ATTR_RW(supply_sel);
+static DEVICE_ATTR(in_status, 0444,
+			supply_status_show, NULL);
+static DEVICE_ATTR(usb_status, 0444,
+			supply_status_show, NULL);
+static DEVICE_ATTR_RW(in_current_limit);
+static DEVICE_ATTR_RW(usb_current_limit);
+static BIT_DEVICE_ATTR(high_impedance_enable, 0644,
+			BQ2416X_REG_CONTROL,
+			BQ2416X_REG_CONTROL_HZ_MODE_MASK);
+static BIT_DEVICE_ATTR(dpm_status, 0444,
+			BQ2416X_REG_DPM,
+			BQ2416X_REG_DPM_STATUS_MASK);
+static DEVICE_ATTR(usb_dpm_voltage, 0644,
+			dpm_voltage_show,
+			dpm_voltage_store);
+static DEVICE_ATTR(in_dpm_voltage, 0644,
+			dpm_voltage_show,
+			dpm_voltage_store);
+static DEVICE_ATTR_RO(ts_fault);
+
+static struct attribute *bq2416x_sysfs_attributes[] = {
+	&dev_attr_supply_sel.attr,
+	&dev_attr_in_status.attr,
+	&dev_attr_usb_status.attr,
+	&dev_attr_in_current_limit.attr,
+	&dev_attr_usb_current_limit.attr,
+	&sensor_dev_attr_high_impedance_enable.dev_attr.attr,
+	&sensor_dev_attr_dpm_status.dev_attr.attr,
+	&dev_attr_usb_dpm_voltage.attr,
+	&dev_attr_in_dpm_voltage.attr,
+	&dev_attr_ts_fault.attr,
+	NULL,
+};
+
+static const struct attribute_group bq2416x_sysfs_attr_group = {
+	.attrs = bq2416x_sysfs_attributes,
+};
+
+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 char *bq2416x_charger_supplied_to[] = {
+	"main-battery",
+};
+
+/*
+ * If battery present set charging options
+ * otherwise stay in no battery modus
+ * (ce = off, curr_term = off, nobat_op = 1)
+ */
+static void bq2416x_idata_from_of(struct bq2416x_priv *bq2416x)
+{
+	struct bq2416x_initdata *idata = &bq2416x->idata;
+	struct power_supply_battery_info info = {};
+	int ret, v;
+	unsigned int prop;
+
+	*idata = initdefault;
+
+	if (!power_supply_get_battery_info(bq2416x->psy, &info)) {
+		idata->nobat_mode = 0;
+		v = info.charge_term_current_ua;
+		if (v  != -EINVAL) {
+			idata->term_current = v;
+			idata->curr_term_en = 1;
+		}
+
+		v = info.constant_charge_current_max_ua;
+		idata->charge_current = v;
+
+		v = info.constant_charge_voltage_max_uv;
+		idata->charge_voltage = v;
+	}
+
+	ret = device_property_read_u32(bq2416x->dev,
+				"ti,in-current-limit-microamp", &prop);
+	if (!ret && prop >= 2500000)
+		idata->in_curr_limit = BQ2416X_IN_CURR_LIM_2500;
+
+	ret = device_property_read_u32(bq2416x->dev,
+				"ti,usb-current-limit-microamp", &prop);
+	if (!ret)
+		idata->usb_curr_limit = bq2416x_find_idx(bq2416x_usb_curr_lim,
+					ARRAY_SIZE(bq2416x_usb_curr_lim), prop);
+
+	ret = device_property_read_u32(bq2416x->dev,
+				"ti,usb-dpm-voltage-microvolt", &prop);
+	if (!ret)
+		idata->usb_dpm_voltage = prop;
+
+	ret = device_property_read_u32(bq2416x->dev,
+				"ti,in-dpm-voltage-microvolt", &prop);
+	if (!ret)
+		idata->in_dpm_voltage = prop;
+}
+
+/* power management op */
+
+static int __maybe_unused 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 __maybe_unused 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_configure(bq2416x);
+	pm_runtime_put_sync(bq2416x->dev);
+
+	schedule_delayed_work(&bq2416x->watchdog, BQ2416X_WATCHDOG_TIMER * HZ);
+
+	power_supply_changed(bq2416x->psy);
+
+	return 0;
+}
+
+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 };
+
+	dev_set_drvdata(bq2416x->dev, bq2416x);
+
+	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.of_node = bq2416x->dev->of_node;
+	psy_cfg.supplied_to = bq2416x_charger_supplied_to;
+	psy_cfg.num_supplicants = ARRAY_SIZE(bq2416x_charger_supplied_to);
+	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);
+	}
+
+	if (bq2416x->dev->of_node)
+		bq2416x_idata_from_of(bq2416x);
+	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, "Initial 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("Karsten Mueller <mueller.k@efe-gmbh.de>");
+MODULE_LICENSE("GPL");
-- 
2.17.1


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

* [PATCH 2/3] dt-bindings: power: supply: bq2416x driver
  2018-09-17 12:37 [PATCH 1/3] power: supply: Add driver for TI BQ2416X battery charger Karsten Mueller
@ 2018-09-17 12:37 ` Karsten Mueller
  2018-09-24 21:17   ` Rob Herring
  2018-09-17 12:37 ` [PATCH 3/3] Add sysfs ABI for bq2416x Karsten Mueller
  1 sibling, 1 reply; 4+ messages in thread
From: Karsten Mueller @ 2018-09-17 12:37 UTC (permalink / raw)
  To: mueller.k
  Cc: renner, Sebastian Reichel, Rob Herring, Mark Rutland, linux-pm,
	linux-kernel, devicetree

Signed-off-by: Karsten Mueller <mueller.k@efe-gmbh.de>
---
 .../bindings/power/supply/bq2416x.txt         | 75 +++++++++++++++++++
 1 file changed, 75 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/supply/bq2416x.txt

diff --git a/Documentation/devicetree/bindings/power/supply/bq2416x.txt b/Documentation/devicetree/bindings/power/supply/bq2416x.txt
new file mode 100644
index 000000000..742d41642
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/bq2416x.txt
@@ -0,0 +1,75 @@
+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 the INT pin of
+			the bq2416x
+- interrupts:		The IRQ number assigned for INT pin.
+
+Optional properties:
+====================
+
+- ti,in-current-limit-microamp:	IN source current limit.
+	Possible values: 1500000 (default), 2500000
+
+- ti,usb-current-limit-microamp: USB source current limit.
+	If not specified, auto detection of USB (D+/D- based) is used (for
+	BQ24160/0A/3 only) or settings of default mode are kept (BQ24161/1B/8).
+	Both pre-select only 100 mA or 1500 mA (refer to datasheet).
+	Possible values: 100000, 150000, 500000, 800000, 90000, 1500000
+
+- ti,usb-dpm-voltage-microvolt:	USB threshold for dynamic power path management.
+	Configures the threshold input voltage (usb input) for the dynamic power
+	path management. If not specified a default of 4,200,000 (= 4.2V) is
+	used.
+
+- ti,in-dpm-voltage-microvolt: IN threshold for dynamic power path management.
+	Configures the threshold input voltage (in input) for the dynamic power
+	path management. If not specified a default of 4,200,000 (= 4.2V) is
+	used.
+
+- monitored-battery: phandle of battery information devicetree node
+	If not specified, no battery is assumed (charge disable, nobat operation
+	mode, no NTC check of battery), Vsys output voltage is set to 4.2V. The
+	charger uses the following battery properties:
+	+ constant_charge_current_max_microamp: maximum current in fast charge
+	  mode
+	+ constant_charge_voltage_max_microvolt: maximum constant charging
+	  voltage / output voltage (refer to datasheet)
+	+ charge-term-current-microamp: a charge cycle terminates when the
+	  battery voltage is above recharge threshold, and the current is below
+	  this setting (typically 10% of battery capacity). If not specified,
+	  termination of current detection is disabled (refer to datasheet).
+
+Example:
+========
+
+bat: battery {
+		compatible = "simple-battery";
+		charge-term-current-microamp = <128000>;
+		constant-charge-current-max-microamp = <900000>;
+		constant-charge-voltage-max-microvolt = <4200000>;
+	};
+
+bq24160@6b {
+	compatible = "ti,bq24160";
+	reg = <0x6b>;
+
+	interrupt-parent = <&gpio5>;
+	interrupts = <31 IRQ_TYPE_EDGE_RISING>;
+
+	monitored-battery = <&bat>;
+	ti,in-current-limit-microamp = <1500000>;
+	ti,usb-current-limit-microamp = <500000>;
+	ti,usb-dpm-voltage-microvolt = <4300000>;
+	ti,in-dpm-voltage-microvolt = <4300000>;
+};
-- 
2.17.1


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

* [PATCH 3/3] Add sysfs ABI for bq2416x
  2018-09-17 12:37 [PATCH 1/3] power: supply: Add driver for TI BQ2416X battery charger Karsten Mueller
  2018-09-17 12:37 ` [PATCH 2/3] dt-bindings: power: supply: bq2416x driver Karsten Mueller
@ 2018-09-17 12:37 ` Karsten Mueller
  1 sibling, 0 replies; 4+ messages in thread
From: Karsten Mueller @ 2018-09-17 12:37 UTC (permalink / raw)
  To: mueller.k
  Cc: renner, Sebastian Reichel, Rob Herring, Mark Rutland, linux-pm,
	linux-kernel, devicetree

Signed-off-by: Karsten Mueller <mueller.k@efe-gmbh.de>
---
 Documentation/ABI/testing/sysfs-class-power | 118 ++++++++++++++++++++
 1 file changed, 118 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power
index 5e23e22dc..3881485ca 100644
--- a/Documentation/ABI/testing/sysfs-class-power
+++ b/Documentation/ABI/testing/sysfs-class-power
@@ -587,3 +587,121 @@ Description:
 		Valid values:
 		- 1: enabled
 		- 0: disabled
+
+What:		/sys/class/power_supply/bq2416x-charger/usb_curr_limit
+Date:		September 2018
+Contact:	Karsten Mueller <mueller.k@efe-gmbh.de>
+Description:
+		Reports and sets the maximum current the "usb" supply can
+		support.
+
+		Access: Read, Write
+		Valid values:
+		- 100000, 150000, 500000, 800000, 900000, 1500000 (uA)
+
+What:		/sys/class/power_supply/bq2416x-charger/in_curr_limit
+Date:		September 2018
+Contact:	Karsten Mueller <mueller.k@efe-gmbh.de>
+Description:
+		Reports and sets the maximum current the "in" (wall) supply can
+		support.
+
+		Access: Read, Write
+		Valid values:
+		- 1500000, 2500000 (uA)
+
+What:		/sys/class/power_supply/bq2416x-charger/in_dpm_voltage
+Date:		September 2018
+Contact:	Karsten Mueller <mueller.k@efe-gmbh.de>
+Description:
+		This entry configures the "in" (wall) input dynamic power path
+		management voltage of bq2416x-type charger devices. Once the
+		supply drops to the configured voltage, the input current limit
+		is reduced down to prevent the further drop of the supply. When
+		the IC enters this mode, the charge current is lower than the
+		set value.
+
+		Access: Read, Write
+		Valid values:
+		- 4200000, 4280000, 4360000, 4440000, 4520000, 4600000, 4680000,
+		  4760000 (all uV)
+
+What:		/sys/class/power_supply/bq2416x-charger/usb_dpm_voltage
+Date:		September 2018
+Contact:	Karsten Mueller <mueller.k@efe-gmbh.de>
+Description:
+		This entry configures the "usb" input dynamic power path
+		management voltage of bq2416x-type charger devices. Once the
+		supply drops to the configured voltage, the input current limit
+		is reduced down to prevent the further drop of the supply. When
+		the IC enters this mode, the charge current is lower than the
+		set value.
+
+		Access: Read, Write
+		Valid values:
+		- 4200000, 4280000, 4360000, 4440000, 4520000, 4600000, 4680000,
+		  4760000 (all uV)
+
+What:		/sys/class/power_supply/bq2416x-charger/high_impedance_enable
+Date:		September 2018
+Contact:	Karsten Mueller <mueller.k@efe-gmbh.de>
+Description:
+		This entry allows enabling the high-impedance mode of
+		bq2416x-type charger devices. If enabled, it places the charger
+		IC into low power standby mode with the switch mode controller
+		disabled. When disabled, the charger operates normally.
+
+		Access: Read, Write
+		Valid values:
+		- 1: enabled
+		- 0: disabled
+
+What:		/sys/class/power_supply/bq2416x-charger/dpm_status
+Date:		September 2018
+Contact:	Karsten Mueller <mueller.k@efe-gmbh.de>
+Description:
+		Reports whether dynamic power management is active, i.e. input
+		current of used source is limited to prevent further drop of
+		voltage.
+
+		Valid values:
+		- 0: not active
+		- 1: active
+
+What:		/sys/class/power_supply/bq2416x-charger/supply_sel
+Date:		September 2018
+Contact:	Karsten Mueller <mueller.k@efe-gmbh.de>
+Description:
+		This entry configures which input ("in", "usb") should be
+		preferred as input source.
+
+		Valid values:
+		- "in", "usb"
+
+What:		/sys/class/power_supply/bq2416x-charger/ts_fault
+Date:		September 2018
+Contact:	Karsten Mueller <mueller.k@efe-gmbh.de>
+Description:
+		Reports state of NTC temperature monitor.
+
+		Valid values:
+		- "normal", "cold/hot(charge suspended)",
+		  "cool(half current charge)", "warm(voltage reduced)"
+
+What:		/sys/class/power_supply/bq2416x-charger/in_status
+Date:		September 2018
+Contact:	Karsten Mueller <mueller.k@efe-gmbh.de>
+Description:
+		Reports state of "in" input source.
+
+		Valid values:
+		- "Normal", "OVP", "Weak source", "Faulty Adapter"
+
+What:		/sys/class/power_supply/bq2416x-charger/usb_status
+Date:		September 2018
+Contact:	Karsten Mueller <mueller.k@efe-gmbh.de>
+Description:
+		Reports state of "usb" input source.
+
+		Valid values:
+		- "Normal", "OVP", "Weak source", "Faulty Adapter"
-- 
2.17.1


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

* Re: [PATCH 2/3] dt-bindings: power: supply: bq2416x driver
  2018-09-17 12:37 ` [PATCH 2/3] dt-bindings: power: supply: bq2416x driver Karsten Mueller
@ 2018-09-24 21:17   ` Rob Herring
  0 siblings, 0 replies; 4+ messages in thread
From: Rob Herring @ 2018-09-24 21:17 UTC (permalink / raw)
  To: Karsten Mueller
  Cc: renner, Sebastian Reichel, Mark Rutland, linux-pm, linux-kernel,
	devicetree

On Mon, Sep 17, 2018 at 02:37:28PM +0200, Karsten Mueller wrote:
> Signed-off-by: Karsten Mueller <mueller.k@efe-gmbh.de>

Commit msg needed.

> ---
>  .../bindings/power/supply/bq2416x.txt         | 75 +++++++++++++++++++
>  1 file changed, 75 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/power/supply/bq2416x.txt
> 
> diff --git a/Documentation/devicetree/bindings/power/supply/bq2416x.txt b/Documentation/devicetree/bindings/power/supply/bq2416x.txt
> new file mode 100644
> index 000000000..742d41642
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/supply/bq2416x.txt
> @@ -0,0 +1,75 @@
> +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 the INT pin of
> +			the bq2416x

Drop this as it is implied (and could be in the parent).

> +- interrupts:		The IRQ number assigned for INT pin.
> +
> +Optional properties:
> +====================
> +
> +- ti,in-current-limit-microamp:	IN source current limit.
> +	Possible values: 1500000 (default), 2500000
> +
> +- ti,usb-current-limit-microamp: USB source current limit.
> +	If not specified, auto detection of USB (D+/D- based) is used (for
> +	BQ24160/0A/3 only) or settings of default mode are kept (BQ24161/1B/8).
> +	Both pre-select only 100 mA or 1500 mA (refer to datasheet).
> +	Possible values: 100000, 150000, 500000, 800000, 90000, 1500000

Can't these be common?

> +
> +- ti,usb-dpm-voltage-microvolt:	USB threshold for dynamic power path management.
> +	Configures the threshold input voltage (usb input) for the dynamic power
> +	path management. If not specified a default of 4,200,000 (= 4.2V) is
> +	used.
> +
> +- ti,in-dpm-voltage-microvolt: IN threshold for dynamic power path management.
> +	Configures the threshold input voltage (in input) for the dynamic power
> +	path management. If not specified a default of 4,200,000 (= 4.2V) is
> +	used.
> +
> +- monitored-battery: phandle of battery information devicetree node
> +	If not specified, no battery is assumed (charge disable, nobat operation
> +	mode, no NTC check of battery), Vsys output voltage is set to 4.2V. The
> +	charger uses the following battery properties:
> +	+ constant_charge_current_max_microamp: maximum current in fast charge
> +	  mode
> +	+ constant_charge_voltage_max_microvolt: maximum constant charging
> +	  voltage / output voltage (refer to datasheet)
> +	+ charge-term-current-microamp: a charge cycle terminates when the
> +	  battery voltage is above recharge threshold, and the current is below
> +	  this setting (typically 10% of battery capacity). If not specified,
> +	  termination of current detection is disabled (refer to datasheet).
> +
> +Example:
> +========
> +
> +bat: battery {
> +		compatible = "simple-battery";
> +		charge-term-current-microamp = <128000>;
> +		constant-charge-current-max-microamp = <900000>;
> +		constant-charge-voltage-max-microvolt = <4200000>;
> +	};
> +
> +bq24160@6b {

charger@6b

> +	compatible = "ti,bq24160";
> +	reg = <0x6b>;
> +
> +	interrupt-parent = <&gpio5>;
> +	interrupts = <31 IRQ_TYPE_EDGE_RISING>;
> +
> +	monitored-battery = <&bat>;
> +	ti,in-current-limit-microamp = <1500000>;
> +	ti,usb-current-limit-microamp = <500000>;
> +	ti,usb-dpm-voltage-microvolt = <4300000>;
> +	ti,in-dpm-voltage-microvolt = <4300000>;
> +};
> -- 
> 2.17.1
> 

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

end of thread, other threads:[~2018-09-24 21:17 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-17 12:37 [PATCH 1/3] power: supply: Add driver for TI BQ2416X battery charger Karsten Mueller
2018-09-17 12:37 ` [PATCH 2/3] dt-bindings: power: supply: bq2416x driver Karsten Mueller
2018-09-24 21:17   ` Rob Herring
2018-09-17 12:37 ` [PATCH 3/3] Add sysfs ABI for bq2416x Karsten Mueller

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.