1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28c0984e5SSebastian Reichel /*
38c0984e5SSebastian Reichel  * bq2415x charger driver
48c0984e5SSebastian Reichel  *
5149ed3d4SPali Rohár  * Copyright (C) 2011-2013  Pali Rohár <pali@kernel.org>
68c0984e5SSebastian Reichel  *
78c0984e5SSebastian Reichel  * Datasheets:
881bd45fcSAlexander A. Klimov  * https://www.ti.com/product/bq24150
981bd45fcSAlexander A. Klimov  * https://www.ti.com/product/bq24150a
1081bd45fcSAlexander A. Klimov  * https://www.ti.com/product/bq24152
1181bd45fcSAlexander A. Klimov  * https://www.ti.com/product/bq24153
1281bd45fcSAlexander A. Klimov  * https://www.ti.com/product/bq24153a
1381bd45fcSAlexander A. Klimov  * https://www.ti.com/product/bq24155
1481bd45fcSAlexander A. Klimov  * https://www.ti.com/product/bq24157s
1581bd45fcSAlexander A. Klimov  * https://www.ti.com/product/bq24158
168c0984e5SSebastian Reichel  */
178c0984e5SSebastian Reichel 
188c0984e5SSebastian Reichel #include <linux/kernel.h>
198c0984e5SSebastian Reichel #include <linux/module.h>
208c0984e5SSebastian Reichel #include <linux/param.h>
218c0984e5SSebastian Reichel #include <linux/err.h>
228c0984e5SSebastian Reichel #include <linux/workqueue.h>
238c0984e5SSebastian Reichel #include <linux/sysfs.h>
248c0984e5SSebastian Reichel #include <linux/platform_device.h>
258c0984e5SSebastian Reichel #include <linux/power_supply.h>
268c0984e5SSebastian Reichel #include <linux/idr.h>
278c0984e5SSebastian Reichel #include <linux/i2c.h>
288c0984e5SSebastian Reichel #include <linux/slab.h>
298c0984e5SSebastian Reichel #include <linux/acpi.h>
308c0984e5SSebastian Reichel 
318c0984e5SSebastian Reichel #include <linux/power/bq2415x_charger.h>
328c0984e5SSebastian Reichel 
338c0984e5SSebastian Reichel /* timeout for resetting chip timer */
348c0984e5SSebastian Reichel #define BQ2415X_TIMER_TIMEOUT		10
358c0984e5SSebastian Reichel 
368c0984e5SSebastian Reichel #define BQ2415X_REG_STATUS		0x00
378c0984e5SSebastian Reichel #define BQ2415X_REG_CONTROL		0x01
388c0984e5SSebastian Reichel #define BQ2415X_REG_VOLTAGE		0x02
398c0984e5SSebastian Reichel #define BQ2415X_REG_VENDER		0x03
408c0984e5SSebastian Reichel #define BQ2415X_REG_CURRENT		0x04
418c0984e5SSebastian Reichel 
428c0984e5SSebastian Reichel /* reset state for all registers */
438c0984e5SSebastian Reichel #define BQ2415X_RESET_STATUS		BIT(6)
448c0984e5SSebastian Reichel #define BQ2415X_RESET_CONTROL		(BIT(4)|BIT(5))
458c0984e5SSebastian Reichel #define BQ2415X_RESET_VOLTAGE		(BIT(1)|BIT(3))
468c0984e5SSebastian Reichel #define BQ2415X_RESET_CURRENT		(BIT(0)|BIT(3)|BIT(7))
478c0984e5SSebastian Reichel 
488c0984e5SSebastian Reichel /* status register */
498c0984e5SSebastian Reichel #define BQ2415X_BIT_TMR_RST		7
508c0984e5SSebastian Reichel #define BQ2415X_BIT_OTG			7
518c0984e5SSebastian Reichel #define BQ2415X_BIT_EN_STAT		6
528c0984e5SSebastian Reichel #define BQ2415X_MASK_STAT		(BIT(4)|BIT(5))
538c0984e5SSebastian Reichel #define BQ2415X_SHIFT_STAT		4
548c0984e5SSebastian Reichel #define BQ2415X_BIT_BOOST		3
558c0984e5SSebastian Reichel #define BQ2415X_MASK_FAULT		(BIT(0)|BIT(1)|BIT(2))
568c0984e5SSebastian Reichel #define BQ2415X_SHIFT_FAULT		0
578c0984e5SSebastian Reichel 
588c0984e5SSebastian Reichel /* control register */
598c0984e5SSebastian Reichel #define BQ2415X_MASK_LIMIT		(BIT(6)|BIT(7))
608c0984e5SSebastian Reichel #define BQ2415X_SHIFT_LIMIT		6
618c0984e5SSebastian Reichel #define BQ2415X_MASK_VLOWV		(BIT(4)|BIT(5))
628c0984e5SSebastian Reichel #define BQ2415X_SHIFT_VLOWV		4
638c0984e5SSebastian Reichel #define BQ2415X_BIT_TE			3
648c0984e5SSebastian Reichel #define BQ2415X_BIT_CE			2
658c0984e5SSebastian Reichel #define BQ2415X_BIT_HZ_MODE		1
668c0984e5SSebastian Reichel #define BQ2415X_BIT_OPA_MODE		0
678c0984e5SSebastian Reichel 
688c0984e5SSebastian Reichel /* voltage register */
698c0984e5SSebastian Reichel #define BQ2415X_MASK_VO		(BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7))
708c0984e5SSebastian Reichel #define BQ2415X_SHIFT_VO		2
718c0984e5SSebastian Reichel #define BQ2415X_BIT_OTG_PL		1
728c0984e5SSebastian Reichel #define BQ2415X_BIT_OTG_EN		0
738c0984e5SSebastian Reichel 
748c0984e5SSebastian Reichel /* vender register */
758c0984e5SSebastian Reichel #define BQ2415X_MASK_VENDER		(BIT(5)|BIT(6)|BIT(7))
768c0984e5SSebastian Reichel #define BQ2415X_SHIFT_VENDER		5
778c0984e5SSebastian Reichel #define BQ2415X_MASK_PN			(BIT(3)|BIT(4))
788c0984e5SSebastian Reichel #define BQ2415X_SHIFT_PN		3
798c0984e5SSebastian Reichel #define BQ2415X_MASK_REVISION		(BIT(0)|BIT(1)|BIT(2))
808c0984e5SSebastian Reichel #define BQ2415X_SHIFT_REVISION		0
818c0984e5SSebastian Reichel 
828c0984e5SSebastian Reichel /* current register */
838c0984e5SSebastian Reichel #define BQ2415X_MASK_RESET		BIT(7)
848c0984e5SSebastian Reichel #define BQ2415X_MASK_VI_CHRG		(BIT(4)|BIT(5)|BIT(6))
858c0984e5SSebastian Reichel #define BQ2415X_SHIFT_VI_CHRG		4
868c0984e5SSebastian Reichel /* N/A					BIT(3) */
878c0984e5SSebastian Reichel #define BQ2415X_MASK_VI_TERM		(BIT(0)|BIT(1)|BIT(2))
888c0984e5SSebastian Reichel #define BQ2415X_SHIFT_VI_TERM		0
898c0984e5SSebastian Reichel 
908c0984e5SSebastian Reichel 
918c0984e5SSebastian Reichel enum bq2415x_command {
928c0984e5SSebastian Reichel 	BQ2415X_TIMER_RESET,
938c0984e5SSebastian Reichel 	BQ2415X_OTG_STATUS,
948c0984e5SSebastian Reichel 	BQ2415X_STAT_PIN_STATUS,
958c0984e5SSebastian Reichel 	BQ2415X_STAT_PIN_ENABLE,
968c0984e5SSebastian Reichel 	BQ2415X_STAT_PIN_DISABLE,
978c0984e5SSebastian Reichel 	BQ2415X_CHARGE_STATUS,
988c0984e5SSebastian Reichel 	BQ2415X_BOOST_STATUS,
998c0984e5SSebastian Reichel 	BQ2415X_FAULT_STATUS,
1008c0984e5SSebastian Reichel 
1018c0984e5SSebastian Reichel 	BQ2415X_CHARGE_TERMINATION_STATUS,
1028c0984e5SSebastian Reichel 	BQ2415X_CHARGE_TERMINATION_ENABLE,
1038c0984e5SSebastian Reichel 	BQ2415X_CHARGE_TERMINATION_DISABLE,
1048c0984e5SSebastian Reichel 	BQ2415X_CHARGER_STATUS,
1058c0984e5SSebastian Reichel 	BQ2415X_CHARGER_ENABLE,
1068c0984e5SSebastian Reichel 	BQ2415X_CHARGER_DISABLE,
1078c0984e5SSebastian Reichel 	BQ2415X_HIGH_IMPEDANCE_STATUS,
1088c0984e5SSebastian Reichel 	BQ2415X_HIGH_IMPEDANCE_ENABLE,
1098c0984e5SSebastian Reichel 	BQ2415X_HIGH_IMPEDANCE_DISABLE,
1108c0984e5SSebastian Reichel 	BQ2415X_BOOST_MODE_STATUS,
1118c0984e5SSebastian Reichel 	BQ2415X_BOOST_MODE_ENABLE,
1128c0984e5SSebastian Reichel 	BQ2415X_BOOST_MODE_DISABLE,
1138c0984e5SSebastian Reichel 
1148c0984e5SSebastian Reichel 	BQ2415X_OTG_LEVEL,
1158c0984e5SSebastian Reichel 	BQ2415X_OTG_ACTIVATE_HIGH,
1168c0984e5SSebastian Reichel 	BQ2415X_OTG_ACTIVATE_LOW,
1178c0984e5SSebastian Reichel 	BQ2415X_OTG_PIN_STATUS,
1188c0984e5SSebastian Reichel 	BQ2415X_OTG_PIN_ENABLE,
1198c0984e5SSebastian Reichel 	BQ2415X_OTG_PIN_DISABLE,
1208c0984e5SSebastian Reichel 
1218c0984e5SSebastian Reichel 	BQ2415X_VENDER_CODE,
1228c0984e5SSebastian Reichel 	BQ2415X_PART_NUMBER,
1238c0984e5SSebastian Reichel 	BQ2415X_REVISION,
1248c0984e5SSebastian Reichel };
1258c0984e5SSebastian Reichel 
1268c0984e5SSebastian Reichel enum bq2415x_chip {
1278c0984e5SSebastian Reichel 	BQUNKNOWN,
1288c0984e5SSebastian Reichel 	BQ24150,
1298c0984e5SSebastian Reichel 	BQ24150A,
1308c0984e5SSebastian Reichel 	BQ24151,
1318c0984e5SSebastian Reichel 	BQ24151A,
1328c0984e5SSebastian Reichel 	BQ24152,
1338c0984e5SSebastian Reichel 	BQ24153,
1348c0984e5SSebastian Reichel 	BQ24153A,
1358c0984e5SSebastian Reichel 	BQ24155,
1368c0984e5SSebastian Reichel 	BQ24156,
1378c0984e5SSebastian Reichel 	BQ24156A,
1388c0984e5SSebastian Reichel 	BQ24157S,
1398c0984e5SSebastian Reichel 	BQ24158,
1408c0984e5SSebastian Reichel };
1418c0984e5SSebastian Reichel 
1428c0984e5SSebastian Reichel static char *bq2415x_chip_name[] = {
1438c0984e5SSebastian Reichel 	"unknown",
1448c0984e5SSebastian Reichel 	"bq24150",
1458c0984e5SSebastian Reichel 	"bq24150a",
1468c0984e5SSebastian Reichel 	"bq24151",
1478c0984e5SSebastian Reichel 	"bq24151a",
1488c0984e5SSebastian Reichel 	"bq24152",
1498c0984e5SSebastian Reichel 	"bq24153",
1508c0984e5SSebastian Reichel 	"bq24153a",
1518c0984e5SSebastian Reichel 	"bq24155",
1528c0984e5SSebastian Reichel 	"bq24156",
1538c0984e5SSebastian Reichel 	"bq24156a",
1548c0984e5SSebastian Reichel 	"bq24157s",
1558c0984e5SSebastian Reichel 	"bq24158",
1568c0984e5SSebastian Reichel };
1578c0984e5SSebastian Reichel 
1588c0984e5SSebastian Reichel struct bq2415x_device {
1598c0984e5SSebastian Reichel 	struct device *dev;
1608c0984e5SSebastian Reichel 	struct bq2415x_platform_data init_data;
1618c0984e5SSebastian Reichel 	struct power_supply *charger;
1628c0984e5SSebastian Reichel 	struct power_supply_desc charger_desc;
1638c0984e5SSebastian Reichel 	struct delayed_work work;
1648c0984e5SSebastian Reichel 	struct device_node *notify_node;
1658c0984e5SSebastian Reichel 	struct notifier_block nb;
1668c0984e5SSebastian Reichel 	enum bq2415x_mode reported_mode;/* mode reported by hook function */
1678c0984e5SSebastian Reichel 	enum bq2415x_mode mode;		/* currently configured mode */
1688c0984e5SSebastian Reichel 	enum bq2415x_chip chip;
1698c0984e5SSebastian Reichel 	const char *timer_error;
1708c0984e5SSebastian Reichel 	char *model;
1718c0984e5SSebastian Reichel 	char *name;
1728c0984e5SSebastian Reichel 	int autotimer;	/* 1 - if driver automatically reset timer, 0 - not */
1738c0984e5SSebastian Reichel 	int automode;	/* 1 - enabled, 0 - disabled; -1 - not supported */
1748c0984e5SSebastian Reichel 	int id;
1758c0984e5SSebastian Reichel };
1768c0984e5SSebastian Reichel 
1778c0984e5SSebastian Reichel /* each registered chip must have unique id */
1788c0984e5SSebastian Reichel static DEFINE_IDR(bq2415x_id);
1798c0984e5SSebastian Reichel 
1808c0984e5SSebastian Reichel static DEFINE_MUTEX(bq2415x_id_mutex);
1818c0984e5SSebastian Reichel static DEFINE_MUTEX(bq2415x_timer_mutex);
1828c0984e5SSebastian Reichel static DEFINE_MUTEX(bq2415x_i2c_mutex);
1838c0984e5SSebastian Reichel 
1848c0984e5SSebastian Reichel /**** i2c read functions ****/
1858c0984e5SSebastian Reichel 
1868c0984e5SSebastian Reichel /* read value from register */
bq2415x_i2c_read(struct bq2415x_device * bq,u8 reg)1878c0984e5SSebastian Reichel static int bq2415x_i2c_read(struct bq2415x_device *bq, u8 reg)
1888c0984e5SSebastian Reichel {
1898c0984e5SSebastian Reichel 	struct i2c_client *client = to_i2c_client(bq->dev);
1908c0984e5SSebastian Reichel 	struct i2c_msg msg[2];
1918c0984e5SSebastian Reichel 	u8 val;
1928c0984e5SSebastian Reichel 	int ret;
1938c0984e5SSebastian Reichel 
1948c0984e5SSebastian Reichel 	if (!client->adapter)
1958c0984e5SSebastian Reichel 		return -ENODEV;
1968c0984e5SSebastian Reichel 
1978c0984e5SSebastian Reichel 	msg[0].addr = client->addr;
1988c0984e5SSebastian Reichel 	msg[0].flags = 0;
1998c0984e5SSebastian Reichel 	msg[0].buf = &reg;
2008c0984e5SSebastian Reichel 	msg[0].len = sizeof(reg);
2018c0984e5SSebastian Reichel 	msg[1].addr = client->addr;
2028c0984e5SSebastian Reichel 	msg[1].flags = I2C_M_RD;
2038c0984e5SSebastian Reichel 	msg[1].buf = &val;
2048c0984e5SSebastian Reichel 	msg[1].len = sizeof(val);
2058c0984e5SSebastian Reichel 
2068c0984e5SSebastian Reichel 	mutex_lock(&bq2415x_i2c_mutex);
2078c0984e5SSebastian Reichel 	ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
2088c0984e5SSebastian Reichel 	mutex_unlock(&bq2415x_i2c_mutex);
2098c0984e5SSebastian Reichel 
2108c0984e5SSebastian Reichel 	if (ret < 0)
2118c0984e5SSebastian Reichel 		return ret;
2128c0984e5SSebastian Reichel 
2138c0984e5SSebastian Reichel 	return val;
2148c0984e5SSebastian Reichel }
2158c0984e5SSebastian Reichel 
2168c0984e5SSebastian Reichel /* read value from register, apply mask and right shift it */
bq2415x_i2c_read_mask(struct bq2415x_device * bq,u8 reg,u8 mask,u8 shift)2178c0984e5SSebastian Reichel static int bq2415x_i2c_read_mask(struct bq2415x_device *bq, u8 reg,
2188c0984e5SSebastian Reichel 				 u8 mask, u8 shift)
2198c0984e5SSebastian Reichel {
2208c0984e5SSebastian Reichel 	int ret;
2218c0984e5SSebastian Reichel 
2228c0984e5SSebastian Reichel 	if (shift > 8)
2238c0984e5SSebastian Reichel 		return -EINVAL;
2248c0984e5SSebastian Reichel 
2258c0984e5SSebastian Reichel 	ret = bq2415x_i2c_read(bq, reg);
2268c0984e5SSebastian Reichel 	if (ret < 0)
2278c0984e5SSebastian Reichel 		return ret;
2288c0984e5SSebastian Reichel 	return (ret & mask) >> shift;
2298c0984e5SSebastian Reichel }
2308c0984e5SSebastian Reichel 
2318c0984e5SSebastian Reichel /* read value from register and return one specified bit */
bq2415x_i2c_read_bit(struct bq2415x_device * bq,u8 reg,u8 bit)2328c0984e5SSebastian Reichel static int bq2415x_i2c_read_bit(struct bq2415x_device *bq, u8 reg, u8 bit)
2338c0984e5SSebastian Reichel {
2348c0984e5SSebastian Reichel 	if (bit > 8)
2358c0984e5SSebastian Reichel 		return -EINVAL;
2368c0984e5SSebastian Reichel 	return bq2415x_i2c_read_mask(bq, reg, BIT(bit), bit);
2378c0984e5SSebastian Reichel }
2388c0984e5SSebastian Reichel 
2398c0984e5SSebastian Reichel /**** i2c write functions ****/
2408c0984e5SSebastian Reichel 
2418c0984e5SSebastian Reichel /* write value to register */
bq2415x_i2c_write(struct bq2415x_device * bq,u8 reg,u8 val)2428c0984e5SSebastian Reichel static int bq2415x_i2c_write(struct bq2415x_device *bq, u8 reg, u8 val)
2438c0984e5SSebastian Reichel {
2448c0984e5SSebastian Reichel 	struct i2c_client *client = to_i2c_client(bq->dev);
2458c0984e5SSebastian Reichel 	struct i2c_msg msg[1];
2468c0984e5SSebastian Reichel 	u8 data[2];
2478c0984e5SSebastian Reichel 	int ret;
2488c0984e5SSebastian Reichel 
2498c0984e5SSebastian Reichel 	data[0] = reg;
2508c0984e5SSebastian Reichel 	data[1] = val;
2518c0984e5SSebastian Reichel 
2528c0984e5SSebastian Reichel 	msg[0].addr = client->addr;
2538c0984e5SSebastian Reichel 	msg[0].flags = 0;
2548c0984e5SSebastian Reichel 	msg[0].buf = data;
2558c0984e5SSebastian Reichel 	msg[0].len = ARRAY_SIZE(data);
2568c0984e5SSebastian Reichel 
2578c0984e5SSebastian Reichel 	mutex_lock(&bq2415x_i2c_mutex);
2588c0984e5SSebastian Reichel 	ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
2598c0984e5SSebastian Reichel 	mutex_unlock(&bq2415x_i2c_mutex);
2608c0984e5SSebastian Reichel 
2618c0984e5SSebastian Reichel 	/* i2c_transfer returns number of messages transferred */
2628c0984e5SSebastian Reichel 	if (ret < 0)
2638c0984e5SSebastian Reichel 		return ret;
2648c0984e5SSebastian Reichel 	else if (ret != 1)
2658c0984e5SSebastian Reichel 		return -EIO;
2668c0984e5SSebastian Reichel 
2678c0984e5SSebastian Reichel 	return 0;
2688c0984e5SSebastian Reichel }
2698c0984e5SSebastian Reichel 
2708c0984e5SSebastian Reichel /* read value from register, change it with mask left shifted and write back */
bq2415x_i2c_write_mask(struct bq2415x_device * bq,u8 reg,u8 val,u8 mask,u8 shift)2718c0984e5SSebastian Reichel static int bq2415x_i2c_write_mask(struct bq2415x_device *bq, u8 reg, u8 val,
2728c0984e5SSebastian Reichel 				  u8 mask, u8 shift)
2738c0984e5SSebastian Reichel {
2748c0984e5SSebastian Reichel 	int ret;
2758c0984e5SSebastian Reichel 
2768c0984e5SSebastian Reichel 	if (shift > 8)
2778c0984e5SSebastian Reichel 		return -EINVAL;
2788c0984e5SSebastian Reichel 
2798c0984e5SSebastian Reichel 	ret = bq2415x_i2c_read(bq, reg);
2808c0984e5SSebastian Reichel 	if (ret < 0)
2818c0984e5SSebastian Reichel 		return ret;
2828c0984e5SSebastian Reichel 
2838c0984e5SSebastian Reichel 	ret &= ~mask;
2848c0984e5SSebastian Reichel 	ret |= val << shift;
2858c0984e5SSebastian Reichel 
2868c0984e5SSebastian Reichel 	return bq2415x_i2c_write(bq, reg, ret);
2878c0984e5SSebastian Reichel }
2888c0984e5SSebastian Reichel 
2898c0984e5SSebastian Reichel /* change only one bit in register */
bq2415x_i2c_write_bit(struct bq2415x_device * bq,u8 reg,bool val,u8 bit)2908c0984e5SSebastian Reichel static int bq2415x_i2c_write_bit(struct bq2415x_device *bq, u8 reg,
2918c0984e5SSebastian Reichel 				 bool val, u8 bit)
2928c0984e5SSebastian Reichel {
2938c0984e5SSebastian Reichel 	if (bit > 8)
2948c0984e5SSebastian Reichel 		return -EINVAL;
2958c0984e5SSebastian Reichel 	return bq2415x_i2c_write_mask(bq, reg, val, BIT(bit), bit);
2968c0984e5SSebastian Reichel }
2978c0984e5SSebastian Reichel 
2988c0984e5SSebastian Reichel /**** global functions ****/
2998c0984e5SSebastian Reichel 
3008c0984e5SSebastian Reichel /* exec command function */
bq2415x_exec_command(struct bq2415x_device * bq,enum bq2415x_command command)3018c0984e5SSebastian Reichel static int bq2415x_exec_command(struct bq2415x_device *bq,
3028c0984e5SSebastian Reichel 				enum bq2415x_command command)
3038c0984e5SSebastian Reichel {
3048c0984e5SSebastian Reichel 	int ret;
3058c0984e5SSebastian Reichel 
3068c0984e5SSebastian Reichel 	switch (command) {
3078c0984e5SSebastian Reichel 	case BQ2415X_TIMER_RESET:
3088c0984e5SSebastian Reichel 		return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS,
3098c0984e5SSebastian Reichel 				1, BQ2415X_BIT_TMR_RST);
3108c0984e5SSebastian Reichel 	case BQ2415X_OTG_STATUS:
3118c0984e5SSebastian Reichel 		return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS,
3128c0984e5SSebastian Reichel 				BQ2415X_BIT_OTG);
3138c0984e5SSebastian Reichel 	case BQ2415X_STAT_PIN_STATUS:
3148c0984e5SSebastian Reichel 		return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS,
3158c0984e5SSebastian Reichel 				BQ2415X_BIT_EN_STAT);
3168c0984e5SSebastian Reichel 	case BQ2415X_STAT_PIN_ENABLE:
3178c0984e5SSebastian Reichel 		return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, 1,
3188c0984e5SSebastian Reichel 				BQ2415X_BIT_EN_STAT);
3198c0984e5SSebastian Reichel 	case BQ2415X_STAT_PIN_DISABLE:
3208c0984e5SSebastian Reichel 		return bq2415x_i2c_write_bit(bq, BQ2415X_REG_STATUS, 0,
3218c0984e5SSebastian Reichel 				BQ2415X_BIT_EN_STAT);
3228c0984e5SSebastian Reichel 	case BQ2415X_CHARGE_STATUS:
3238c0984e5SSebastian Reichel 		return bq2415x_i2c_read_mask(bq, BQ2415X_REG_STATUS,
3248c0984e5SSebastian Reichel 				BQ2415X_MASK_STAT, BQ2415X_SHIFT_STAT);
3258c0984e5SSebastian Reichel 	case BQ2415X_BOOST_STATUS:
3268c0984e5SSebastian Reichel 		return bq2415x_i2c_read_bit(bq, BQ2415X_REG_STATUS,
3278c0984e5SSebastian Reichel 				BQ2415X_BIT_BOOST);
3288c0984e5SSebastian Reichel 	case BQ2415X_FAULT_STATUS:
3298c0984e5SSebastian Reichel 		return bq2415x_i2c_read_mask(bq, BQ2415X_REG_STATUS,
3308c0984e5SSebastian Reichel 			BQ2415X_MASK_FAULT, BQ2415X_SHIFT_FAULT);
3318c0984e5SSebastian Reichel 
3328c0984e5SSebastian Reichel 	case BQ2415X_CHARGE_TERMINATION_STATUS:
3338c0984e5SSebastian Reichel 		return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
3348c0984e5SSebastian Reichel 				BQ2415X_BIT_TE);
3358c0984e5SSebastian Reichel 	case BQ2415X_CHARGE_TERMINATION_ENABLE:
3368c0984e5SSebastian Reichel 		return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
3378c0984e5SSebastian Reichel 				1, BQ2415X_BIT_TE);
3388c0984e5SSebastian Reichel 	case BQ2415X_CHARGE_TERMINATION_DISABLE:
3398c0984e5SSebastian Reichel 		return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
3408c0984e5SSebastian Reichel 				0, BQ2415X_BIT_TE);
3418c0984e5SSebastian Reichel 	case BQ2415X_CHARGER_STATUS:
3428c0984e5SSebastian Reichel 		ret = bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
3438c0984e5SSebastian Reichel 			BQ2415X_BIT_CE);
3448c0984e5SSebastian Reichel 		if (ret < 0)
3458c0984e5SSebastian Reichel 			return ret;
3468c0984e5SSebastian Reichel 		return ret > 0 ? 0 : 1;
3478c0984e5SSebastian Reichel 	case BQ2415X_CHARGER_ENABLE:
3488c0984e5SSebastian Reichel 		return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
3498c0984e5SSebastian Reichel 				0, BQ2415X_BIT_CE);
3508c0984e5SSebastian Reichel 	case BQ2415X_CHARGER_DISABLE:
3518c0984e5SSebastian Reichel 		return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
3528c0984e5SSebastian Reichel 				1, BQ2415X_BIT_CE);
3538c0984e5SSebastian Reichel 	case BQ2415X_HIGH_IMPEDANCE_STATUS:
3548c0984e5SSebastian Reichel 		return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
3558c0984e5SSebastian Reichel 				BQ2415X_BIT_HZ_MODE);
3568c0984e5SSebastian Reichel 	case BQ2415X_HIGH_IMPEDANCE_ENABLE:
3578c0984e5SSebastian Reichel 		return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
3588c0984e5SSebastian Reichel 				1, BQ2415X_BIT_HZ_MODE);
3598c0984e5SSebastian Reichel 	case BQ2415X_HIGH_IMPEDANCE_DISABLE:
3608c0984e5SSebastian Reichel 		return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
3618c0984e5SSebastian Reichel 				0, BQ2415X_BIT_HZ_MODE);
3628c0984e5SSebastian Reichel 	case BQ2415X_BOOST_MODE_STATUS:
3638c0984e5SSebastian Reichel 		return bq2415x_i2c_read_bit(bq, BQ2415X_REG_CONTROL,
3648c0984e5SSebastian Reichel 				BQ2415X_BIT_OPA_MODE);
3658c0984e5SSebastian Reichel 	case BQ2415X_BOOST_MODE_ENABLE:
3668c0984e5SSebastian Reichel 		return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
3678c0984e5SSebastian Reichel 				1, BQ2415X_BIT_OPA_MODE);
3688c0984e5SSebastian Reichel 	case BQ2415X_BOOST_MODE_DISABLE:
3698c0984e5SSebastian Reichel 		return bq2415x_i2c_write_bit(bq, BQ2415X_REG_CONTROL,
3708c0984e5SSebastian Reichel 				0, BQ2415X_BIT_OPA_MODE);
3718c0984e5SSebastian Reichel 
3728c0984e5SSebastian Reichel 	case BQ2415X_OTG_LEVEL:
3738c0984e5SSebastian Reichel 		return bq2415x_i2c_read_bit(bq, BQ2415X_REG_VOLTAGE,
3748c0984e5SSebastian Reichel 				BQ2415X_BIT_OTG_PL);
3758c0984e5SSebastian Reichel 	case BQ2415X_OTG_ACTIVATE_HIGH:
3768c0984e5SSebastian Reichel 		return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
3778c0984e5SSebastian Reichel 				1, BQ2415X_BIT_OTG_PL);
3788c0984e5SSebastian Reichel 	case BQ2415X_OTG_ACTIVATE_LOW:
3798c0984e5SSebastian Reichel 		return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
3808c0984e5SSebastian Reichel 				0, BQ2415X_BIT_OTG_PL);
3818c0984e5SSebastian Reichel 	case BQ2415X_OTG_PIN_STATUS:
3828c0984e5SSebastian Reichel 		return bq2415x_i2c_read_bit(bq, BQ2415X_REG_VOLTAGE,
3838c0984e5SSebastian Reichel 				BQ2415X_BIT_OTG_EN);
3848c0984e5SSebastian Reichel 	case BQ2415X_OTG_PIN_ENABLE:
3858c0984e5SSebastian Reichel 		return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
3868c0984e5SSebastian Reichel 				1, BQ2415X_BIT_OTG_EN);
3878c0984e5SSebastian Reichel 	case BQ2415X_OTG_PIN_DISABLE:
3888c0984e5SSebastian Reichel 		return bq2415x_i2c_write_bit(bq, BQ2415X_REG_VOLTAGE,
3898c0984e5SSebastian Reichel 				0, BQ2415X_BIT_OTG_EN);
3908c0984e5SSebastian Reichel 
3918c0984e5SSebastian Reichel 	case BQ2415X_VENDER_CODE:
3928c0984e5SSebastian Reichel 		return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER,
3938c0984e5SSebastian Reichel 			BQ2415X_MASK_VENDER, BQ2415X_SHIFT_VENDER);
3948c0984e5SSebastian Reichel 	case BQ2415X_PART_NUMBER:
3958c0984e5SSebastian Reichel 		return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER,
3968c0984e5SSebastian Reichel 				BQ2415X_MASK_PN, BQ2415X_SHIFT_PN);
3978c0984e5SSebastian Reichel 	case BQ2415X_REVISION:
3988c0984e5SSebastian Reichel 		return bq2415x_i2c_read_mask(bq, BQ2415X_REG_VENDER,
3998c0984e5SSebastian Reichel 			BQ2415X_MASK_REVISION, BQ2415X_SHIFT_REVISION);
4008c0984e5SSebastian Reichel 	}
4018c0984e5SSebastian Reichel 	return -EINVAL;
4028c0984e5SSebastian Reichel }
4038c0984e5SSebastian Reichel 
4048c0984e5SSebastian Reichel /* detect chip type */
bq2415x_detect_chip(struct bq2415x_device * bq)4058c0984e5SSebastian Reichel static enum bq2415x_chip bq2415x_detect_chip(struct bq2415x_device *bq)
4068c0984e5SSebastian Reichel {
4078c0984e5SSebastian Reichel 	struct i2c_client *client = to_i2c_client(bq->dev);
4088c0984e5SSebastian Reichel 	int ret = bq2415x_exec_command(bq, BQ2415X_PART_NUMBER);
4098c0984e5SSebastian Reichel 
4108c0984e5SSebastian Reichel 	if (ret < 0)
4118c0984e5SSebastian Reichel 		return ret;
4128c0984e5SSebastian Reichel 
4138c0984e5SSebastian Reichel 	switch (client->addr) {
4148c0984e5SSebastian Reichel 	case 0x6b:
4158c0984e5SSebastian Reichel 		switch (ret) {
4168c0984e5SSebastian Reichel 		case 0:
4178c0984e5SSebastian Reichel 			if (bq->chip == BQ24151A)
4188c0984e5SSebastian Reichel 				return bq->chip;
4198c0984e5SSebastian Reichel 			return BQ24151;
4208c0984e5SSebastian Reichel 		case 1:
4218c0984e5SSebastian Reichel 			if (bq->chip == BQ24150A ||
4228c0984e5SSebastian Reichel 				bq->chip == BQ24152 ||
4238c0984e5SSebastian Reichel 				bq->chip == BQ24155)
4248c0984e5SSebastian Reichel 				return bq->chip;
4258c0984e5SSebastian Reichel 			return BQ24150;
4268c0984e5SSebastian Reichel 		case 2:
4278c0984e5SSebastian Reichel 			if (bq->chip == BQ24153A)
4288c0984e5SSebastian Reichel 				return bq->chip;
4298c0984e5SSebastian Reichel 			return BQ24153;
4308c0984e5SSebastian Reichel 		default:
4318c0984e5SSebastian Reichel 			return BQUNKNOWN;
4328c0984e5SSebastian Reichel 		}
4338c0984e5SSebastian Reichel 		break;
4348c0984e5SSebastian Reichel 
4358c0984e5SSebastian Reichel 	case 0x6a:
4368c0984e5SSebastian Reichel 		switch (ret) {
4378c0984e5SSebastian Reichel 		case 0:
4388c0984e5SSebastian Reichel 			if (bq->chip == BQ24156A)
4398c0984e5SSebastian Reichel 				return bq->chip;
4408c0984e5SSebastian Reichel 			return BQ24156;
4418c0984e5SSebastian Reichel 		case 2:
4428c0984e5SSebastian Reichel 			if (bq->chip == BQ24157S)
4438c0984e5SSebastian Reichel 				return bq->chip;
4448c0984e5SSebastian Reichel 			return BQ24158;
4458c0984e5SSebastian Reichel 		default:
4468c0984e5SSebastian Reichel 			return BQUNKNOWN;
4478c0984e5SSebastian Reichel 		}
4488c0984e5SSebastian Reichel 		break;
4498c0984e5SSebastian Reichel 	}
4508c0984e5SSebastian Reichel 
4518c0984e5SSebastian Reichel 	return BQUNKNOWN;
4528c0984e5SSebastian Reichel }
4538c0984e5SSebastian Reichel 
4548c0984e5SSebastian Reichel /* detect chip revision */
bq2415x_detect_revision(struct bq2415x_device * bq)4558c0984e5SSebastian Reichel static int bq2415x_detect_revision(struct bq2415x_device *bq)
4568c0984e5SSebastian Reichel {
4578c0984e5SSebastian Reichel 	int ret = bq2415x_exec_command(bq, BQ2415X_REVISION);
4588c0984e5SSebastian Reichel 	int chip = bq2415x_detect_chip(bq);
4598c0984e5SSebastian Reichel 
4608c0984e5SSebastian Reichel 	if (ret < 0 || chip < 0)
4618c0984e5SSebastian Reichel 		return -1;
4628c0984e5SSebastian Reichel 
4638c0984e5SSebastian Reichel 	switch (chip) {
4648c0984e5SSebastian Reichel 	case BQ24150:
4658c0984e5SSebastian Reichel 	case BQ24150A:
4668c0984e5SSebastian Reichel 	case BQ24151:
4678c0984e5SSebastian Reichel 	case BQ24151A:
4688c0984e5SSebastian Reichel 	case BQ24152:
4698c0984e5SSebastian Reichel 		if (ret >= 0 && ret <= 3)
4708c0984e5SSebastian Reichel 			return ret;
4718c0984e5SSebastian Reichel 		return -1;
4728c0984e5SSebastian Reichel 	case BQ24153:
4738c0984e5SSebastian Reichel 	case BQ24153A:
4748c0984e5SSebastian Reichel 	case BQ24156:
4758c0984e5SSebastian Reichel 	case BQ24156A:
4768c0984e5SSebastian Reichel 	case BQ24157S:
4778c0984e5SSebastian Reichel 	case BQ24158:
4788c0984e5SSebastian Reichel 		if (ret == 3)
4798c0984e5SSebastian Reichel 			return 0;
4808c0984e5SSebastian Reichel 		else if (ret == 1)
4818c0984e5SSebastian Reichel 			return 1;
4828c0984e5SSebastian Reichel 		return -1;
4838c0984e5SSebastian Reichel 	case BQ24155:
4848c0984e5SSebastian Reichel 		if (ret == 3)
4858c0984e5SSebastian Reichel 			return 3;
4868c0984e5SSebastian Reichel 		return -1;
4878c0984e5SSebastian Reichel 	case BQUNKNOWN:
4888c0984e5SSebastian Reichel 		return -1;
4898c0984e5SSebastian Reichel 	}
4908c0984e5SSebastian Reichel 
4918c0984e5SSebastian Reichel 	return -1;
4928c0984e5SSebastian Reichel }
4938c0984e5SSebastian Reichel 
4948c0984e5SSebastian Reichel /* return chip vender code */
bq2415x_get_vender_code(struct bq2415x_device * bq)4958c0984e5SSebastian Reichel static int bq2415x_get_vender_code(struct bq2415x_device *bq)
4968c0984e5SSebastian Reichel {
4978c0984e5SSebastian Reichel 	int ret;
4988c0984e5SSebastian Reichel 
4998c0984e5SSebastian Reichel 	ret = bq2415x_exec_command(bq, BQ2415X_VENDER_CODE);
5008c0984e5SSebastian Reichel 	if (ret < 0)
5018c0984e5SSebastian Reichel 		return 0;
5028c0984e5SSebastian Reichel 
5038c0984e5SSebastian Reichel 	/* convert to binary */
5048c0984e5SSebastian Reichel 	return (ret & 0x1) +
5058c0984e5SSebastian Reichel 	       ((ret >> 1) & 0x1) * 10 +
5068c0984e5SSebastian Reichel 	       ((ret >> 2) & 0x1) * 100;
5078c0984e5SSebastian Reichel }
5088c0984e5SSebastian Reichel 
5098c0984e5SSebastian Reichel /* reset all chip registers to default state */
bq2415x_reset_chip(struct bq2415x_device * bq)5108c0984e5SSebastian Reichel static void bq2415x_reset_chip(struct bq2415x_device *bq)
5118c0984e5SSebastian Reichel {
5128c0984e5SSebastian Reichel 	bq2415x_i2c_write(bq, BQ2415X_REG_CURRENT, BQ2415X_RESET_CURRENT);
5138c0984e5SSebastian Reichel 	bq2415x_i2c_write(bq, BQ2415X_REG_VOLTAGE, BQ2415X_RESET_VOLTAGE);
5148c0984e5SSebastian Reichel 	bq2415x_i2c_write(bq, BQ2415X_REG_CONTROL, BQ2415X_RESET_CONTROL);
5158c0984e5SSebastian Reichel 	bq2415x_i2c_write(bq, BQ2415X_REG_STATUS, BQ2415X_RESET_STATUS);
5168c0984e5SSebastian Reichel 	bq->timer_error = NULL;
5178c0984e5SSebastian Reichel }
5188c0984e5SSebastian Reichel 
5198c0984e5SSebastian Reichel /**** properties functions ****/
5208c0984e5SSebastian Reichel 
5218c0984e5SSebastian Reichel /* set current limit in mA */
bq2415x_set_current_limit(struct bq2415x_device * bq,int mA)5228c0984e5SSebastian Reichel static int bq2415x_set_current_limit(struct bq2415x_device *bq, int mA)
5238c0984e5SSebastian Reichel {
5248c0984e5SSebastian Reichel 	int val;
5258c0984e5SSebastian Reichel 
5268c0984e5SSebastian Reichel 	if (mA <= 100)
5278c0984e5SSebastian Reichel 		val = 0;
5288c0984e5SSebastian Reichel 	else if (mA <= 500)
5298c0984e5SSebastian Reichel 		val = 1;
5308c0984e5SSebastian Reichel 	else if (mA <= 800)
5318c0984e5SSebastian Reichel 		val = 2;
5328c0984e5SSebastian Reichel 	else
5338c0984e5SSebastian Reichel 		val = 3;
5348c0984e5SSebastian Reichel 
5358c0984e5SSebastian Reichel 	return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CONTROL, val,
5368c0984e5SSebastian Reichel 			BQ2415X_MASK_LIMIT, BQ2415X_SHIFT_LIMIT);
5378c0984e5SSebastian Reichel }
5388c0984e5SSebastian Reichel 
5398c0984e5SSebastian Reichel /* get current limit in mA */
bq2415x_get_current_limit(struct bq2415x_device * bq)5408c0984e5SSebastian Reichel static int bq2415x_get_current_limit(struct bq2415x_device *bq)
5418c0984e5SSebastian Reichel {
5428c0984e5SSebastian Reichel 	int ret;
5438c0984e5SSebastian Reichel 
5448c0984e5SSebastian Reichel 	ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL,
5458c0984e5SSebastian Reichel 			BQ2415X_MASK_LIMIT, BQ2415X_SHIFT_LIMIT);
5468c0984e5SSebastian Reichel 	if (ret < 0)
5478c0984e5SSebastian Reichel 		return ret;
5488c0984e5SSebastian Reichel 	else if (ret == 0)
5498c0984e5SSebastian Reichel 		return 100;
5508c0984e5SSebastian Reichel 	else if (ret == 1)
5518c0984e5SSebastian Reichel 		return 500;
5528c0984e5SSebastian Reichel 	else if (ret == 2)
5538c0984e5SSebastian Reichel 		return 800;
5548c0984e5SSebastian Reichel 	else if (ret == 3)
5558c0984e5SSebastian Reichel 		return 1800;
5568c0984e5SSebastian Reichel 	return -EINVAL;
5578c0984e5SSebastian Reichel }
5588c0984e5SSebastian Reichel 
5598c0984e5SSebastian Reichel /* set weak battery voltage in mV */
bq2415x_set_weak_battery_voltage(struct bq2415x_device * bq,int mV)5608c0984e5SSebastian Reichel static int bq2415x_set_weak_battery_voltage(struct bq2415x_device *bq, int mV)
5618c0984e5SSebastian Reichel {
5628c0984e5SSebastian Reichel 	int val;
5638c0984e5SSebastian Reichel 
5648c0984e5SSebastian Reichel 	/* round to 100mV */
5658c0984e5SSebastian Reichel 	if (mV <= 3400 + 50)
5668c0984e5SSebastian Reichel 		val = 0;
5678c0984e5SSebastian Reichel 	else if (mV <= 3500 + 50)
5688c0984e5SSebastian Reichel 		val = 1;
5698c0984e5SSebastian Reichel 	else if (mV <= 3600 + 50)
5708c0984e5SSebastian Reichel 		val = 2;
5718c0984e5SSebastian Reichel 	else
5728c0984e5SSebastian Reichel 		val = 3;
5738c0984e5SSebastian Reichel 
5748c0984e5SSebastian Reichel 	return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CONTROL, val,
5758c0984e5SSebastian Reichel 			BQ2415X_MASK_VLOWV, BQ2415X_SHIFT_VLOWV);
5768c0984e5SSebastian Reichel }
5778c0984e5SSebastian Reichel 
5788c0984e5SSebastian Reichel /* get weak battery voltage in mV */
bq2415x_get_weak_battery_voltage(struct bq2415x_device * bq)5798c0984e5SSebastian Reichel static int bq2415x_get_weak_battery_voltage(struct bq2415x_device *bq)
5808c0984e5SSebastian Reichel {
5818c0984e5SSebastian Reichel 	int ret;
5828c0984e5SSebastian Reichel 
5838c0984e5SSebastian Reichel 	ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CONTROL,
5848c0984e5SSebastian Reichel 			BQ2415X_MASK_VLOWV, BQ2415X_SHIFT_VLOWV);
5858c0984e5SSebastian Reichel 	if (ret < 0)
5868c0984e5SSebastian Reichel 		return ret;
5878c0984e5SSebastian Reichel 	return 100 * (34 + ret);
5888c0984e5SSebastian Reichel }
5898c0984e5SSebastian Reichel 
5908c0984e5SSebastian Reichel /* set battery regulation voltage in mV */
bq2415x_set_battery_regulation_voltage(struct bq2415x_device * bq,int mV)5918c0984e5SSebastian Reichel static int bq2415x_set_battery_regulation_voltage(struct bq2415x_device *bq,
5928c0984e5SSebastian Reichel 						  int mV)
5938c0984e5SSebastian Reichel {
5948c0984e5SSebastian Reichel 	int val = (mV/10 - 350) / 2;
5958c0984e5SSebastian Reichel 
5968c0984e5SSebastian Reichel 	/*
5978c0984e5SSebastian Reichel 	 * According to datasheet, maximum battery regulation voltage is
5988c0984e5SSebastian Reichel 	 * 4440mV which is b101111 = 47.
5998c0984e5SSebastian Reichel 	 */
6008c0984e5SSebastian Reichel 	if (val < 0)
6018c0984e5SSebastian Reichel 		val = 0;
6028c0984e5SSebastian Reichel 	else if (val > 47)
6038c0984e5SSebastian Reichel 		return -EINVAL;
6048c0984e5SSebastian Reichel 
6058c0984e5SSebastian Reichel 	return bq2415x_i2c_write_mask(bq, BQ2415X_REG_VOLTAGE, val,
6068c0984e5SSebastian Reichel 			BQ2415X_MASK_VO, BQ2415X_SHIFT_VO);
6078c0984e5SSebastian Reichel }
6088c0984e5SSebastian Reichel 
6098c0984e5SSebastian Reichel /* get battery regulation voltage in mV */
bq2415x_get_battery_regulation_voltage(struct bq2415x_device * bq)6108c0984e5SSebastian Reichel static int bq2415x_get_battery_regulation_voltage(struct bq2415x_device *bq)
6118c0984e5SSebastian Reichel {
6128c0984e5SSebastian Reichel 	int ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_VOLTAGE,
6138c0984e5SSebastian Reichel 			BQ2415X_MASK_VO, BQ2415X_SHIFT_VO);
6148c0984e5SSebastian Reichel 
6158c0984e5SSebastian Reichel 	if (ret < 0)
6168c0984e5SSebastian Reichel 		return ret;
6178c0984e5SSebastian Reichel 	return 10 * (350 + 2*ret);
6188c0984e5SSebastian Reichel }
6198c0984e5SSebastian Reichel 
6208c0984e5SSebastian Reichel /* set charge current in mA (platform data must provide resistor sense) */
bq2415x_set_charge_current(struct bq2415x_device * bq,int mA)6218c0984e5SSebastian Reichel static int bq2415x_set_charge_current(struct bq2415x_device *bq, int mA)
6228c0984e5SSebastian Reichel {
6238c0984e5SSebastian Reichel 	int val;
6248c0984e5SSebastian Reichel 
6258c0984e5SSebastian Reichel 	if (bq->init_data.resistor_sense <= 0)
6268c0984e5SSebastian Reichel 		return -EINVAL;
6278c0984e5SSebastian Reichel 
6288c0984e5SSebastian Reichel 	val = (mA * bq->init_data.resistor_sense - 37400) / 6800;
6298c0984e5SSebastian Reichel 	if (val < 0)
6308c0984e5SSebastian Reichel 		val = 0;
6318c0984e5SSebastian Reichel 	else if (val > 7)
6328c0984e5SSebastian Reichel 		val = 7;
6338c0984e5SSebastian Reichel 
6348c0984e5SSebastian Reichel 	return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CURRENT, val,
6358c0984e5SSebastian Reichel 			BQ2415X_MASK_VI_CHRG | BQ2415X_MASK_RESET,
6368c0984e5SSebastian Reichel 			BQ2415X_SHIFT_VI_CHRG);
6378c0984e5SSebastian Reichel }
6388c0984e5SSebastian Reichel 
6398c0984e5SSebastian Reichel /* get charge current in mA (platform data must provide resistor sense) */
bq2415x_get_charge_current(struct bq2415x_device * bq)6408c0984e5SSebastian Reichel static int bq2415x_get_charge_current(struct bq2415x_device *bq)
6418c0984e5SSebastian Reichel {
6428c0984e5SSebastian Reichel 	int ret;
6438c0984e5SSebastian Reichel 
6448c0984e5SSebastian Reichel 	if (bq->init_data.resistor_sense <= 0)
6458c0984e5SSebastian Reichel 		return -EINVAL;
6468c0984e5SSebastian Reichel 
6478c0984e5SSebastian Reichel 	ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT,
6488c0984e5SSebastian Reichel 			BQ2415X_MASK_VI_CHRG, BQ2415X_SHIFT_VI_CHRG);
6498c0984e5SSebastian Reichel 	if (ret < 0)
6508c0984e5SSebastian Reichel 		return ret;
6518c0984e5SSebastian Reichel 	return (37400 + 6800*ret) / bq->init_data.resistor_sense;
6528c0984e5SSebastian Reichel }
6538c0984e5SSebastian Reichel 
6548c0984e5SSebastian Reichel /* set termination current in mA (platform data must provide resistor sense) */
bq2415x_set_termination_current(struct bq2415x_device * bq,int mA)6558c0984e5SSebastian Reichel static int bq2415x_set_termination_current(struct bq2415x_device *bq, int mA)
6568c0984e5SSebastian Reichel {
6578c0984e5SSebastian Reichel 	int val;
6588c0984e5SSebastian Reichel 
6598c0984e5SSebastian Reichel 	if (bq->init_data.resistor_sense <= 0)
6608c0984e5SSebastian Reichel 		return -EINVAL;
6618c0984e5SSebastian Reichel 
6628c0984e5SSebastian Reichel 	val = (mA * bq->init_data.resistor_sense - 3400) / 3400;
6638c0984e5SSebastian Reichel 	if (val < 0)
6648c0984e5SSebastian Reichel 		val = 0;
6658c0984e5SSebastian Reichel 	else if (val > 7)
6668c0984e5SSebastian Reichel 		val = 7;
6678c0984e5SSebastian Reichel 
6688c0984e5SSebastian Reichel 	return bq2415x_i2c_write_mask(bq, BQ2415X_REG_CURRENT, val,
6698c0984e5SSebastian Reichel 			BQ2415X_MASK_VI_TERM | BQ2415X_MASK_RESET,
6708c0984e5SSebastian Reichel 			BQ2415X_SHIFT_VI_TERM);
6718c0984e5SSebastian Reichel }
6728c0984e5SSebastian Reichel 
6738c0984e5SSebastian Reichel /* get termination current in mA (platform data must provide resistor sense) */
bq2415x_get_termination_current(struct bq2415x_device * bq)6748c0984e5SSebastian Reichel static int bq2415x_get_termination_current(struct bq2415x_device *bq)
6758c0984e5SSebastian Reichel {
6768c0984e5SSebastian Reichel 	int ret;
6778c0984e5SSebastian Reichel 
6788c0984e5SSebastian Reichel 	if (bq->init_data.resistor_sense <= 0)
6798c0984e5SSebastian Reichel 		return -EINVAL;
6808c0984e5SSebastian Reichel 
6818c0984e5SSebastian Reichel 	ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT,
6828c0984e5SSebastian Reichel 			BQ2415X_MASK_VI_TERM, BQ2415X_SHIFT_VI_TERM);
6838c0984e5SSebastian Reichel 	if (ret < 0)
6848c0984e5SSebastian Reichel 		return ret;
6858c0984e5SSebastian Reichel 	return (3400 + 3400*ret) / bq->init_data.resistor_sense;
6868c0984e5SSebastian Reichel }
6878c0984e5SSebastian Reichel 
6888c0984e5SSebastian Reichel /* set default value of property */
6898c0984e5SSebastian Reichel #define bq2415x_set_default_value(bq, prop) \
6908c0984e5SSebastian Reichel 	do { \
6918c0984e5SSebastian Reichel 		int ret = 0; \
6928c0984e5SSebastian Reichel 		if (bq->init_data.prop != -1) \
6938c0984e5SSebastian Reichel 			ret = bq2415x_set_##prop(bq, bq->init_data.prop); \
6948c0984e5SSebastian Reichel 		if (ret < 0) \
6958c0984e5SSebastian Reichel 			return ret; \
6968c0984e5SSebastian Reichel 	} while (0)
6978c0984e5SSebastian Reichel 
6988c0984e5SSebastian Reichel /* set default values of all properties */
bq2415x_set_defaults(struct bq2415x_device * bq)6998c0984e5SSebastian Reichel static int bq2415x_set_defaults(struct bq2415x_device *bq)
7008c0984e5SSebastian Reichel {
7018c0984e5SSebastian Reichel 	bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_DISABLE);
7028c0984e5SSebastian Reichel 	bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE);
7038c0984e5SSebastian Reichel 	bq2415x_exec_command(bq, BQ2415X_CHARGE_TERMINATION_DISABLE);
7048c0984e5SSebastian Reichel 
7058c0984e5SSebastian Reichel 	bq2415x_set_default_value(bq, current_limit);
7068c0984e5SSebastian Reichel 	bq2415x_set_default_value(bq, weak_battery_voltage);
7078c0984e5SSebastian Reichel 	bq2415x_set_default_value(bq, battery_regulation_voltage);
7088c0984e5SSebastian Reichel 
7098c0984e5SSebastian Reichel 	if (bq->init_data.resistor_sense > 0) {
7108c0984e5SSebastian Reichel 		bq2415x_set_default_value(bq, charge_current);
7118c0984e5SSebastian Reichel 		bq2415x_set_default_value(bq, termination_current);
7128c0984e5SSebastian Reichel 		bq2415x_exec_command(bq, BQ2415X_CHARGE_TERMINATION_ENABLE);
7138c0984e5SSebastian Reichel 	}
7148c0984e5SSebastian Reichel 
7158c0984e5SSebastian Reichel 	bq2415x_exec_command(bq, BQ2415X_CHARGER_ENABLE);
7168c0984e5SSebastian Reichel 	return 0;
7178c0984e5SSebastian Reichel }
7188c0984e5SSebastian Reichel 
7198c0984e5SSebastian Reichel /**** charger mode functions ****/
7208c0984e5SSebastian Reichel 
7218c0984e5SSebastian Reichel /* set charger mode */
bq2415x_set_mode(struct bq2415x_device * bq,enum bq2415x_mode mode)7228c0984e5SSebastian Reichel static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode)
7238c0984e5SSebastian Reichel {
7248c0984e5SSebastian Reichel 	int ret = 0;
7258c0984e5SSebastian Reichel 	int charger = 0;
7268c0984e5SSebastian Reichel 	int boost = 0;
7278c0984e5SSebastian Reichel 
7288c0984e5SSebastian Reichel 	if (mode == BQ2415X_MODE_BOOST)
7298c0984e5SSebastian Reichel 		boost = 1;
7308c0984e5SSebastian Reichel 	else if (mode != BQ2415X_MODE_OFF)
7318c0984e5SSebastian Reichel 		charger = 1;
7328c0984e5SSebastian Reichel 
7338c0984e5SSebastian Reichel 	if (!charger)
7348c0984e5SSebastian Reichel 		ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_DISABLE);
7358c0984e5SSebastian Reichel 
7368c0984e5SSebastian Reichel 	if (!boost)
7378c0984e5SSebastian Reichel 		ret = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_DISABLE);
7388c0984e5SSebastian Reichel 
7398c0984e5SSebastian Reichel 	if (ret < 0)
7408c0984e5SSebastian Reichel 		return ret;
7418c0984e5SSebastian Reichel 
7428c0984e5SSebastian Reichel 	switch (mode) {
7438c0984e5SSebastian Reichel 	case BQ2415X_MODE_OFF:
7448c0984e5SSebastian Reichel 		dev_dbg(bq->dev, "changing mode to: Offline\n");
7458c0984e5SSebastian Reichel 		ret = bq2415x_set_current_limit(bq, 100);
7468c0984e5SSebastian Reichel 		break;
7478c0984e5SSebastian Reichel 	case BQ2415X_MODE_NONE:
7488c0984e5SSebastian Reichel 		dev_dbg(bq->dev, "changing mode to: N/A\n");
7498c0984e5SSebastian Reichel 		ret = bq2415x_set_current_limit(bq, 100);
7508c0984e5SSebastian Reichel 		break;
7518c0984e5SSebastian Reichel 	case BQ2415X_MODE_HOST_CHARGER:
7528c0984e5SSebastian Reichel 		dev_dbg(bq->dev, "changing mode to: Host/HUB charger\n");
7538c0984e5SSebastian Reichel 		ret = bq2415x_set_current_limit(bq, 500);
7548c0984e5SSebastian Reichel 		break;
7558c0984e5SSebastian Reichel 	case BQ2415X_MODE_DEDICATED_CHARGER:
7568c0984e5SSebastian Reichel 		dev_dbg(bq->dev, "changing mode to: Dedicated charger\n");
7578c0984e5SSebastian Reichel 		ret = bq2415x_set_current_limit(bq, 1800);
7588c0984e5SSebastian Reichel 		break;
7598c0984e5SSebastian Reichel 	case BQ2415X_MODE_BOOST: /* Boost mode */
7608c0984e5SSebastian Reichel 		dev_dbg(bq->dev, "changing mode to: Boost\n");
7618c0984e5SSebastian Reichel 		ret = bq2415x_set_current_limit(bq, 100);
7628c0984e5SSebastian Reichel 		break;
7638c0984e5SSebastian Reichel 	}
7648c0984e5SSebastian Reichel 
7658c0984e5SSebastian Reichel 	if (ret < 0)
7668c0984e5SSebastian Reichel 		return ret;
7678c0984e5SSebastian Reichel 
7688c0984e5SSebastian Reichel 	if (charger)
7698c0984e5SSebastian Reichel 		ret = bq2415x_exec_command(bq, BQ2415X_CHARGER_ENABLE);
7708c0984e5SSebastian Reichel 	else if (boost)
7718c0984e5SSebastian Reichel 		ret = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_ENABLE);
7728c0984e5SSebastian Reichel 
7738c0984e5SSebastian Reichel 	if (ret < 0)
7748c0984e5SSebastian Reichel 		return ret;
7758c0984e5SSebastian Reichel 
7768c0984e5SSebastian Reichel 	bq2415x_set_default_value(bq, weak_battery_voltage);
7778c0984e5SSebastian Reichel 	bq2415x_set_default_value(bq, battery_regulation_voltage);
7788c0984e5SSebastian Reichel 
7798c0984e5SSebastian Reichel 	bq->mode = mode;
7808c0984e5SSebastian Reichel 	sysfs_notify(&bq->charger->dev.kobj, NULL, "mode");
7818c0984e5SSebastian Reichel 
7828c0984e5SSebastian Reichel 	return 0;
7838c0984e5SSebastian Reichel 
7848c0984e5SSebastian Reichel }
7858c0984e5SSebastian Reichel 
bq2415x_update_reported_mode(struct bq2415x_device * bq,int mA)7868c0984e5SSebastian Reichel static bool bq2415x_update_reported_mode(struct bq2415x_device *bq, int mA)
7878c0984e5SSebastian Reichel {
7888c0984e5SSebastian Reichel 	enum bq2415x_mode mode;
7898c0984e5SSebastian Reichel 
7908c0984e5SSebastian Reichel 	if (mA == 0)
7918c0984e5SSebastian Reichel 		mode = BQ2415X_MODE_OFF;
7928c0984e5SSebastian Reichel 	else if (mA < 500)
7938c0984e5SSebastian Reichel 		mode = BQ2415X_MODE_NONE;
7948c0984e5SSebastian Reichel 	else if (mA < 1800)
7958c0984e5SSebastian Reichel 		mode = BQ2415X_MODE_HOST_CHARGER;
7968c0984e5SSebastian Reichel 	else
7978c0984e5SSebastian Reichel 		mode = BQ2415X_MODE_DEDICATED_CHARGER;
7988c0984e5SSebastian Reichel 
7998c0984e5SSebastian Reichel 	if (bq->reported_mode == mode)
8008c0984e5SSebastian Reichel 		return false;
8018c0984e5SSebastian Reichel 
8028c0984e5SSebastian Reichel 	bq->reported_mode = mode;
8038c0984e5SSebastian Reichel 	return true;
8048c0984e5SSebastian Reichel }
8058c0984e5SSebastian Reichel 
bq2415x_notifier_call(struct notifier_block * nb,unsigned long val,void * v)8068c0984e5SSebastian Reichel static int bq2415x_notifier_call(struct notifier_block *nb,
8078c0984e5SSebastian Reichel 		unsigned long val, void *v)
8088c0984e5SSebastian Reichel {
8098c0984e5SSebastian Reichel 	struct bq2415x_device *bq =
8108c0984e5SSebastian Reichel 		container_of(nb, struct bq2415x_device, nb);
8118c0984e5SSebastian Reichel 	struct power_supply *psy = v;
8128c0984e5SSebastian Reichel 	union power_supply_propval prop;
8138c0984e5SSebastian Reichel 	int ret;
8148c0984e5SSebastian Reichel 
8158c0984e5SSebastian Reichel 	if (val != PSY_EVENT_PROP_CHANGED)
8168c0984e5SSebastian Reichel 		return NOTIFY_OK;
8178c0984e5SSebastian Reichel 
8188c0984e5SSebastian Reichel 	/* Ignore event if it was not send by notify_node/notify_device */
8198c0984e5SSebastian Reichel 	if (bq->notify_node) {
8208c0984e5SSebastian Reichel 		if (!psy->dev.parent ||
8218c0984e5SSebastian Reichel 		    psy->dev.parent->of_node != bq->notify_node)
8228c0984e5SSebastian Reichel 			return NOTIFY_OK;
8238c0984e5SSebastian Reichel 	} else if (bq->init_data.notify_device) {
8248c0984e5SSebastian Reichel 		if (strcmp(psy->desc->name, bq->init_data.notify_device) != 0)
8258c0984e5SSebastian Reichel 			return NOTIFY_OK;
8268c0984e5SSebastian Reichel 	}
8278c0984e5SSebastian Reichel 
8288c0984e5SSebastian Reichel 	dev_dbg(bq->dev, "notifier call was called\n");
8298c0984e5SSebastian Reichel 
8308c0984e5SSebastian Reichel 	ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX,
8318c0984e5SSebastian Reichel 			&prop);
8328c0984e5SSebastian Reichel 	if (ret != 0)
8338c0984e5SSebastian Reichel 		return NOTIFY_OK;
8348c0984e5SSebastian Reichel 
8358c0984e5SSebastian Reichel 	if (!bq2415x_update_reported_mode(bq, prop.intval))
8368c0984e5SSebastian Reichel 		return NOTIFY_OK;
8378c0984e5SSebastian Reichel 
8388c0984e5SSebastian Reichel 	/* if automode is not enabled do not tell about reported_mode */
8398c0984e5SSebastian Reichel 	if (bq->automode < 1)
8408c0984e5SSebastian Reichel 		return NOTIFY_OK;
8418c0984e5SSebastian Reichel 
8428c0984e5SSebastian Reichel 	schedule_delayed_work(&bq->work, 0);
8438c0984e5SSebastian Reichel 
8448c0984e5SSebastian Reichel 	return NOTIFY_OK;
8458c0984e5SSebastian Reichel }
8468c0984e5SSebastian Reichel 
8478c0984e5SSebastian Reichel /**** timer functions ****/
8488c0984e5SSebastian Reichel 
8498c0984e5SSebastian Reichel /* enable/disable auto resetting chip timer */
bq2415x_set_autotimer(struct bq2415x_device * bq,int state)8508c0984e5SSebastian Reichel static void bq2415x_set_autotimer(struct bq2415x_device *bq, int state)
8518c0984e5SSebastian Reichel {
8528c0984e5SSebastian Reichel 	mutex_lock(&bq2415x_timer_mutex);
8538c0984e5SSebastian Reichel 
8548c0984e5SSebastian Reichel 	if (bq->autotimer == state) {
8558c0984e5SSebastian Reichel 		mutex_unlock(&bq2415x_timer_mutex);
8568c0984e5SSebastian Reichel 		return;
8578c0984e5SSebastian Reichel 	}
8588c0984e5SSebastian Reichel 
8598c0984e5SSebastian Reichel 	bq->autotimer = state;
8608c0984e5SSebastian Reichel 
8618c0984e5SSebastian Reichel 	if (state) {
8628c0984e5SSebastian Reichel 		schedule_delayed_work(&bq->work, BQ2415X_TIMER_TIMEOUT * HZ);
8638c0984e5SSebastian Reichel 		bq2415x_exec_command(bq, BQ2415X_TIMER_RESET);
8648c0984e5SSebastian Reichel 		bq->timer_error = NULL;
8658c0984e5SSebastian Reichel 	} else {
8668c0984e5SSebastian Reichel 		cancel_delayed_work_sync(&bq->work);
8678c0984e5SSebastian Reichel 	}
8688c0984e5SSebastian Reichel 
8698c0984e5SSebastian Reichel 	mutex_unlock(&bq2415x_timer_mutex);
8708c0984e5SSebastian Reichel }
8718c0984e5SSebastian Reichel 
8728c0984e5SSebastian Reichel /* called by bq2415x_timer_work on timer error */
bq2415x_timer_error(struct bq2415x_device * bq,const char * msg)8738c0984e5SSebastian Reichel static void bq2415x_timer_error(struct bq2415x_device *bq, const char *msg)
8748c0984e5SSebastian Reichel {
8758c0984e5SSebastian Reichel 	bq->timer_error = msg;
8768c0984e5SSebastian Reichel 	sysfs_notify(&bq->charger->dev.kobj, NULL, "timer");
8778c0984e5SSebastian Reichel 	dev_err(bq->dev, "%s\n", msg);
8788c0984e5SSebastian Reichel 	if (bq->automode > 0)
8798c0984e5SSebastian Reichel 		bq->automode = 0;
8808c0984e5SSebastian Reichel 	bq2415x_set_mode(bq, BQ2415X_MODE_OFF);
8818c0984e5SSebastian Reichel 	bq2415x_set_autotimer(bq, 0);
8828c0984e5SSebastian Reichel }
8838c0984e5SSebastian Reichel 
8848c0984e5SSebastian Reichel /* delayed work function for auto resetting chip timer */
bq2415x_timer_work(struct work_struct * work)8858c0984e5SSebastian Reichel static void bq2415x_timer_work(struct work_struct *work)
8868c0984e5SSebastian Reichel {
8878c0984e5SSebastian Reichel 	struct bq2415x_device *bq = container_of(work, struct bq2415x_device,
8888c0984e5SSebastian Reichel 						 work.work);
8898c0984e5SSebastian Reichel 	int ret;
8908c0984e5SSebastian Reichel 	int error;
8918c0984e5SSebastian Reichel 	int boost;
8928c0984e5SSebastian Reichel 
8938c0984e5SSebastian Reichel 	if (bq->automode > 0 && (bq->reported_mode != bq->mode)) {
8948c0984e5SSebastian Reichel 		sysfs_notify(&bq->charger->dev.kobj, NULL, "reported_mode");
8958c0984e5SSebastian Reichel 		bq2415x_set_mode(bq, bq->reported_mode);
8968c0984e5SSebastian Reichel 	}
8978c0984e5SSebastian Reichel 
8988c0984e5SSebastian Reichel 	if (!bq->autotimer)
8998c0984e5SSebastian Reichel 		return;
9008c0984e5SSebastian Reichel 
9018c0984e5SSebastian Reichel 	ret = bq2415x_exec_command(bq, BQ2415X_TIMER_RESET);
9028c0984e5SSebastian Reichel 	if (ret < 0) {
9038c0984e5SSebastian Reichel 		bq2415x_timer_error(bq, "Resetting timer failed");
9048c0984e5SSebastian Reichel 		return;
9058c0984e5SSebastian Reichel 	}
9068c0984e5SSebastian Reichel 
9078c0984e5SSebastian Reichel 	boost = bq2415x_exec_command(bq, BQ2415X_BOOST_MODE_STATUS);
9088c0984e5SSebastian Reichel 	if (boost < 0) {
9098c0984e5SSebastian Reichel 		bq2415x_timer_error(bq, "Unknown error");
9108c0984e5SSebastian Reichel 		return;
9118c0984e5SSebastian Reichel 	}
9128c0984e5SSebastian Reichel 
9138c0984e5SSebastian Reichel 	error = bq2415x_exec_command(bq, BQ2415X_FAULT_STATUS);
9148c0984e5SSebastian Reichel 	if (error < 0) {
9158c0984e5SSebastian Reichel 		bq2415x_timer_error(bq, "Unknown error");
9168c0984e5SSebastian Reichel 		return;
9178c0984e5SSebastian Reichel 	}
9188c0984e5SSebastian Reichel 
9198c0984e5SSebastian Reichel 	if (boost) {
9208c0984e5SSebastian Reichel 		switch (error) {
9218c0984e5SSebastian Reichel 		/* Non fatal errors, chip is OK */
9228c0984e5SSebastian Reichel 		case 0: /* No error */
9238c0984e5SSebastian Reichel 			break;
9248c0984e5SSebastian Reichel 		case 6: /* Timer expired */
9258c0984e5SSebastian Reichel 			dev_err(bq->dev, "Timer expired\n");
9268c0984e5SSebastian Reichel 			break;
9278c0984e5SSebastian Reichel 		case 3: /* Battery voltage too low */
9288c0984e5SSebastian Reichel 			dev_err(bq->dev, "Battery voltage to low\n");
9298c0984e5SSebastian Reichel 			break;
9308c0984e5SSebastian Reichel 
9318c0984e5SSebastian Reichel 		/* Fatal errors, disable and reset chip */
9328c0984e5SSebastian Reichel 		case 1: /* Overvoltage protection (chip fried) */
9338c0984e5SSebastian Reichel 			bq2415x_timer_error(bq,
9348c0984e5SSebastian Reichel 				"Overvoltage protection (chip fried)");
9358c0984e5SSebastian Reichel 			return;
9368c0984e5SSebastian Reichel 		case 2: /* Overload */
9378c0984e5SSebastian Reichel 			bq2415x_timer_error(bq, "Overload");
9388c0984e5SSebastian Reichel 			return;
9398c0984e5SSebastian Reichel 		case 4: /* Battery overvoltage protection */
9408c0984e5SSebastian Reichel 			bq2415x_timer_error(bq,
9418c0984e5SSebastian Reichel 				"Battery overvoltage protection");
9428c0984e5SSebastian Reichel 			return;
9438c0984e5SSebastian Reichel 		case 5: /* Thermal shutdown (too hot) */
9448c0984e5SSebastian Reichel 			bq2415x_timer_error(bq,
9458c0984e5SSebastian Reichel 					"Thermal shutdown (too hot)");
9468c0984e5SSebastian Reichel 			return;
9478c0984e5SSebastian Reichel 		case 7: /* N/A */
9488c0984e5SSebastian Reichel 			bq2415x_timer_error(bq, "Unknown error");
9498c0984e5SSebastian Reichel 			return;
9508c0984e5SSebastian Reichel 		}
9518c0984e5SSebastian Reichel 	} else {
9528c0984e5SSebastian Reichel 		switch (error) {
9538c0984e5SSebastian Reichel 		/* Non fatal errors, chip is OK */
9548c0984e5SSebastian Reichel 		case 0: /* No error */
9558c0984e5SSebastian Reichel 			break;
9568c0984e5SSebastian Reichel 		case 2: /* Sleep mode */
9578c0984e5SSebastian Reichel 			dev_err(bq->dev, "Sleep mode\n");
9588c0984e5SSebastian Reichel 			break;
9598c0984e5SSebastian Reichel 		case 3: /* Poor input source */
9608c0984e5SSebastian Reichel 			dev_err(bq->dev, "Poor input source\n");
9618c0984e5SSebastian Reichel 			break;
9628c0984e5SSebastian Reichel 		case 6: /* Timer expired */
9638c0984e5SSebastian Reichel 			dev_err(bq->dev, "Timer expired\n");
9648c0984e5SSebastian Reichel 			break;
9658c0984e5SSebastian Reichel 		case 7: /* No battery */
9668c0984e5SSebastian Reichel 			dev_err(bq->dev, "No battery\n");
9678c0984e5SSebastian Reichel 			break;
9688c0984e5SSebastian Reichel 
9698c0984e5SSebastian Reichel 		/* Fatal errors, disable and reset chip */
9708c0984e5SSebastian Reichel 		case 1: /* Overvoltage protection (chip fried) */
9718c0984e5SSebastian Reichel 			bq2415x_timer_error(bq,
9728c0984e5SSebastian Reichel 				"Overvoltage protection (chip fried)");
9738c0984e5SSebastian Reichel 			return;
9748c0984e5SSebastian Reichel 		case 4: /* Battery overvoltage protection */
9758c0984e5SSebastian Reichel 			bq2415x_timer_error(bq,
9768c0984e5SSebastian Reichel 				"Battery overvoltage protection");
9778c0984e5SSebastian Reichel 			return;
9788c0984e5SSebastian Reichel 		case 5: /* Thermal shutdown (too hot) */
9798c0984e5SSebastian Reichel 			bq2415x_timer_error(bq,
9808c0984e5SSebastian Reichel 				"Thermal shutdown (too hot)");
9818c0984e5SSebastian Reichel 			return;
9828c0984e5SSebastian Reichel 		}
9838c0984e5SSebastian Reichel 	}
9848c0984e5SSebastian Reichel 
9858c0984e5SSebastian Reichel 	schedule_delayed_work(&bq->work, BQ2415X_TIMER_TIMEOUT * HZ);
9868c0984e5SSebastian Reichel }
9878c0984e5SSebastian Reichel 
9888c0984e5SSebastian Reichel /**** power supply interface code ****/
9898c0984e5SSebastian Reichel 
9908c0984e5SSebastian Reichel static enum power_supply_property bq2415x_power_supply_props[] = {
9918c0984e5SSebastian Reichel 	/* TODO: maybe add more power supply properties */
9928c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_STATUS,
9938c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_MODEL_NAME,
9948c0984e5SSebastian Reichel };
9958c0984e5SSebastian Reichel 
bq2415x_power_supply_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)9968c0984e5SSebastian Reichel static int bq2415x_power_supply_get_property(struct power_supply *psy,
9978c0984e5SSebastian Reichel 					     enum power_supply_property psp,
9988c0984e5SSebastian Reichel 					     union power_supply_propval *val)
9998c0984e5SSebastian Reichel {
10008c0984e5SSebastian Reichel 	struct bq2415x_device *bq = power_supply_get_drvdata(psy);
10018c0984e5SSebastian Reichel 	int ret;
10028c0984e5SSebastian Reichel 
10038c0984e5SSebastian Reichel 	switch (psp) {
10048c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_STATUS:
10058c0984e5SSebastian Reichel 		ret = bq2415x_exec_command(bq, BQ2415X_CHARGE_STATUS);
10068c0984e5SSebastian Reichel 		if (ret < 0)
10078c0984e5SSebastian Reichel 			return ret;
10088c0984e5SSebastian Reichel 		else if (ret == 0) /* Ready */
10098c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
10108c0984e5SSebastian Reichel 		else if (ret == 1) /* Charge in progress */
10118c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_STATUS_CHARGING;
10128c0984e5SSebastian Reichel 		else if (ret == 2) /* Charge done */
10138c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_STATUS_FULL;
10148c0984e5SSebastian Reichel 		else
10158c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
10168c0984e5SSebastian Reichel 		break;
10178c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_MODEL_NAME:
10188c0984e5SSebastian Reichel 		val->strval = bq->model;
10198c0984e5SSebastian Reichel 		break;
10208c0984e5SSebastian Reichel 	default:
10218c0984e5SSebastian Reichel 		return -EINVAL;
10228c0984e5SSebastian Reichel 	}
10238c0984e5SSebastian Reichel 	return 0;
10248c0984e5SSebastian Reichel }
10258c0984e5SSebastian Reichel 
bq2415x_power_supply_exit(struct bq2415x_device * bq)10268c0984e5SSebastian Reichel static void bq2415x_power_supply_exit(struct bq2415x_device *bq)
10278c0984e5SSebastian Reichel {
10288c0984e5SSebastian Reichel 	bq->autotimer = 0;
10298c0984e5SSebastian Reichel 	if (bq->automode > 0)
10308c0984e5SSebastian Reichel 		bq->automode = 0;
10318c0984e5SSebastian Reichel 	cancel_delayed_work_sync(&bq->work);
10328c0984e5SSebastian Reichel 	power_supply_unregister(bq->charger);
10338c0984e5SSebastian Reichel 	kfree(bq->model);
10348c0984e5SSebastian Reichel }
10358c0984e5SSebastian Reichel 
10368c0984e5SSebastian Reichel /**** additional sysfs entries for power supply interface ****/
10378c0984e5SSebastian Reichel 
10388c0984e5SSebastian Reichel /* show *_status entries */
bq2415x_sysfs_show_status(struct device * dev,struct device_attribute * attr,char * buf)10398c0984e5SSebastian Reichel static ssize_t bq2415x_sysfs_show_status(struct device *dev,
10408c0984e5SSebastian Reichel 					 struct device_attribute *attr,
10418c0984e5SSebastian Reichel 					 char *buf)
10428c0984e5SSebastian Reichel {
10438c0984e5SSebastian Reichel 	struct power_supply *psy = dev_get_drvdata(dev);
10448c0984e5SSebastian Reichel 	struct bq2415x_device *bq = power_supply_get_drvdata(psy);
10458c0984e5SSebastian Reichel 	enum bq2415x_command command;
10468c0984e5SSebastian Reichel 	int ret;
10478c0984e5SSebastian Reichel 
10488c0984e5SSebastian Reichel 	if (strcmp(attr->attr.name, "otg_status") == 0)
10498c0984e5SSebastian Reichel 		command = BQ2415X_OTG_STATUS;
10508c0984e5SSebastian Reichel 	else if (strcmp(attr->attr.name, "charge_status") == 0)
10518c0984e5SSebastian Reichel 		command = BQ2415X_CHARGE_STATUS;
10528c0984e5SSebastian Reichel 	else if (strcmp(attr->attr.name, "boost_status") == 0)
10538c0984e5SSebastian Reichel 		command = BQ2415X_BOOST_STATUS;
10548c0984e5SSebastian Reichel 	else if (strcmp(attr->attr.name, "fault_status") == 0)
10558c0984e5SSebastian Reichel 		command = BQ2415X_FAULT_STATUS;
10568c0984e5SSebastian Reichel 	else
10578c0984e5SSebastian Reichel 		return -EINVAL;
10588c0984e5SSebastian Reichel 
10598c0984e5SSebastian Reichel 	ret = bq2415x_exec_command(bq, command);
10608c0984e5SSebastian Reichel 	if (ret < 0)
10618c0984e5SSebastian Reichel 		return ret;
1062a441f3b9Sye xingchen 	return sysfs_emit(buf, "%d\n", ret);
10638c0984e5SSebastian Reichel }
10648c0984e5SSebastian Reichel 
10658c0984e5SSebastian Reichel /*
10668c0984e5SSebastian Reichel  * set timer entry:
10678c0984e5SSebastian Reichel  *    auto - enable auto mode
10688c0984e5SSebastian Reichel  *    off - disable auto mode
10698c0984e5SSebastian Reichel  *    (other values) - reset chip timer
10708c0984e5SSebastian Reichel  */
bq2415x_sysfs_set_timer(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)10718c0984e5SSebastian Reichel static ssize_t bq2415x_sysfs_set_timer(struct device *dev,
10728c0984e5SSebastian Reichel 				       struct device_attribute *attr,
10738c0984e5SSebastian Reichel 				       const char *buf,
10748c0984e5SSebastian Reichel 				       size_t count)
10758c0984e5SSebastian Reichel {
10768c0984e5SSebastian Reichel 	struct power_supply *psy = dev_get_drvdata(dev);
10778c0984e5SSebastian Reichel 	struct bq2415x_device *bq = power_supply_get_drvdata(psy);
10788c0984e5SSebastian Reichel 	int ret = 0;
10798c0984e5SSebastian Reichel 
10808c0984e5SSebastian Reichel 	if (strncmp(buf, "auto", 4) == 0)
10818c0984e5SSebastian Reichel 		bq2415x_set_autotimer(bq, 1);
10828c0984e5SSebastian Reichel 	else if (strncmp(buf, "off", 3) == 0)
10838c0984e5SSebastian Reichel 		bq2415x_set_autotimer(bq, 0);
10848c0984e5SSebastian Reichel 	else
10858c0984e5SSebastian Reichel 		ret = bq2415x_exec_command(bq, BQ2415X_TIMER_RESET);
10868c0984e5SSebastian Reichel 
10878c0984e5SSebastian Reichel 	if (ret < 0)
10888c0984e5SSebastian Reichel 		return ret;
10898c0984e5SSebastian Reichel 	return count;
10908c0984e5SSebastian Reichel }
10918c0984e5SSebastian Reichel 
10928c0984e5SSebastian Reichel /* show timer entry (auto or off) */
bq2415x_sysfs_show_timer(struct device * dev,struct device_attribute * attr,char * buf)10938c0984e5SSebastian Reichel static ssize_t bq2415x_sysfs_show_timer(struct device *dev,
10948c0984e5SSebastian Reichel 					struct device_attribute *attr,
10958c0984e5SSebastian Reichel 					char *buf)
10968c0984e5SSebastian Reichel {
10978c0984e5SSebastian Reichel 	struct power_supply *psy = dev_get_drvdata(dev);
10988c0984e5SSebastian Reichel 	struct bq2415x_device *bq = power_supply_get_drvdata(psy);
10998c0984e5SSebastian Reichel 
11008c0984e5SSebastian Reichel 	if (bq->timer_error)
1101a441f3b9Sye xingchen 		return sysfs_emit(buf, "%s\n", bq->timer_error);
11028c0984e5SSebastian Reichel 
11038c0984e5SSebastian Reichel 	if (bq->autotimer)
1104a441f3b9Sye xingchen 		return sysfs_emit(buf, "auto\n");
1105a441f3b9Sye xingchen 	return sysfs_emit(buf, "off\n");
11068c0984e5SSebastian Reichel }
11078c0984e5SSebastian Reichel 
11088c0984e5SSebastian Reichel /*
11098c0984e5SSebastian Reichel  * set mode entry:
11108c0984e5SSebastian Reichel  *    auto - if automode is supported, enable it and set mode to reported
11118c0984e5SSebastian Reichel  *    none - disable charger and boost mode
11128c0984e5SSebastian Reichel  *    host - charging mode for host/hub chargers (current limit 500mA)
11138c0984e5SSebastian Reichel  *    dedicated - charging mode for dedicated chargers (unlimited current limit)
11148c0984e5SSebastian Reichel  *    boost - disable charger and enable boost mode
11158c0984e5SSebastian Reichel  */
bq2415x_sysfs_set_mode(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)11168c0984e5SSebastian Reichel static ssize_t bq2415x_sysfs_set_mode(struct device *dev,
11178c0984e5SSebastian Reichel 				      struct device_attribute *attr,
11188c0984e5SSebastian Reichel 				      const char *buf,
11198c0984e5SSebastian Reichel 				      size_t count)
11208c0984e5SSebastian Reichel {
11218c0984e5SSebastian Reichel 	struct power_supply *psy = dev_get_drvdata(dev);
11228c0984e5SSebastian Reichel 	struct bq2415x_device *bq = power_supply_get_drvdata(psy);
11238c0984e5SSebastian Reichel 	enum bq2415x_mode mode;
11248c0984e5SSebastian Reichel 	int ret = 0;
11258c0984e5SSebastian Reichel 
11268c0984e5SSebastian Reichel 	if (strncmp(buf, "auto", 4) == 0) {
11278c0984e5SSebastian Reichel 		if (bq->automode < 0)
11288c0984e5SSebastian Reichel 			return -EINVAL;
11298c0984e5SSebastian Reichel 		bq->automode = 1;
11308c0984e5SSebastian Reichel 		mode = bq->reported_mode;
11318c0984e5SSebastian Reichel 	} else if (strncmp(buf, "off", 3) == 0) {
11328c0984e5SSebastian Reichel 		if (bq->automode > 0)
11338c0984e5SSebastian Reichel 			bq->automode = 0;
11348c0984e5SSebastian Reichel 		mode = BQ2415X_MODE_OFF;
11358c0984e5SSebastian Reichel 	} else if (strncmp(buf, "none", 4) == 0) {
11368c0984e5SSebastian Reichel 		if (bq->automode > 0)
11378c0984e5SSebastian Reichel 			bq->automode = 0;
11388c0984e5SSebastian Reichel 		mode = BQ2415X_MODE_NONE;
11398c0984e5SSebastian Reichel 	} else if (strncmp(buf, "host", 4) == 0) {
11408c0984e5SSebastian Reichel 		if (bq->automode > 0)
11418c0984e5SSebastian Reichel 			bq->automode = 0;
11428c0984e5SSebastian Reichel 		mode = BQ2415X_MODE_HOST_CHARGER;
11438c0984e5SSebastian Reichel 	} else if (strncmp(buf, "dedicated", 9) == 0) {
11448c0984e5SSebastian Reichel 		if (bq->automode > 0)
11458c0984e5SSebastian Reichel 			bq->automode = 0;
11468c0984e5SSebastian Reichel 		mode = BQ2415X_MODE_DEDICATED_CHARGER;
11478c0984e5SSebastian Reichel 	} else if (strncmp(buf, "boost", 5) == 0) {
11488c0984e5SSebastian Reichel 		if (bq->automode > 0)
11498c0984e5SSebastian Reichel 			bq->automode = 0;
11508c0984e5SSebastian Reichel 		mode = BQ2415X_MODE_BOOST;
11518c0984e5SSebastian Reichel 	} else if (strncmp(buf, "reset", 5) == 0) {
11528c0984e5SSebastian Reichel 		bq2415x_reset_chip(bq);
11538c0984e5SSebastian Reichel 		bq2415x_set_defaults(bq);
11548c0984e5SSebastian Reichel 		if (bq->automode <= 0)
11558c0984e5SSebastian Reichel 			return count;
11568c0984e5SSebastian Reichel 		bq->automode = 1;
11578c0984e5SSebastian Reichel 		mode = bq->reported_mode;
11588c0984e5SSebastian Reichel 	} else {
11598c0984e5SSebastian Reichel 		return -EINVAL;
11608c0984e5SSebastian Reichel 	}
11618c0984e5SSebastian Reichel 
11628c0984e5SSebastian Reichel 	ret = bq2415x_set_mode(bq, mode);
11638c0984e5SSebastian Reichel 	if (ret < 0)
11648c0984e5SSebastian Reichel 		return ret;
11658c0984e5SSebastian Reichel 	return count;
11668c0984e5SSebastian Reichel }
11678c0984e5SSebastian Reichel 
11688c0984e5SSebastian Reichel /* show mode entry (auto, none, host, dedicated or boost) */
bq2415x_sysfs_show_mode(struct device * dev,struct device_attribute * attr,char * buf)11698c0984e5SSebastian Reichel static ssize_t bq2415x_sysfs_show_mode(struct device *dev,
11708c0984e5SSebastian Reichel 				       struct device_attribute *attr,
11718c0984e5SSebastian Reichel 				       char *buf)
11728c0984e5SSebastian Reichel {
11738c0984e5SSebastian Reichel 	struct power_supply *psy = dev_get_drvdata(dev);
11748c0984e5SSebastian Reichel 	struct bq2415x_device *bq = power_supply_get_drvdata(psy);
11758c0984e5SSebastian Reichel 	ssize_t ret = 0;
11768c0984e5SSebastian Reichel 
11778c0984e5SSebastian Reichel 	if (bq->automode > 0)
1178a441f3b9Sye xingchen 		ret += sysfs_emit_at(buf, ret, "auto (");
11798c0984e5SSebastian Reichel 
11808c0984e5SSebastian Reichel 	switch (bq->mode) {
11818c0984e5SSebastian Reichel 	case BQ2415X_MODE_OFF:
1182a441f3b9Sye xingchen 		ret += sysfs_emit_at(buf, ret, "off");
11838c0984e5SSebastian Reichel 		break;
11848c0984e5SSebastian Reichel 	case BQ2415X_MODE_NONE:
1185a441f3b9Sye xingchen 		ret += sysfs_emit_at(buf, ret, "none");
11868c0984e5SSebastian Reichel 		break;
11878c0984e5SSebastian Reichel 	case BQ2415X_MODE_HOST_CHARGER:
1188a441f3b9Sye xingchen 		ret += sysfs_emit_at(buf, ret, "host");
11898c0984e5SSebastian Reichel 		break;
11908c0984e5SSebastian Reichel 	case BQ2415X_MODE_DEDICATED_CHARGER:
1191a441f3b9Sye xingchen 		ret += sysfs_emit_at(buf, ret, "dedicated");
11928c0984e5SSebastian Reichel 		break;
11938c0984e5SSebastian Reichel 	case BQ2415X_MODE_BOOST:
1194a441f3b9Sye xingchen 		ret += sysfs_emit_at(buf, ret, "boost");
11958c0984e5SSebastian Reichel 		break;
11968c0984e5SSebastian Reichel 	}
11978c0984e5SSebastian Reichel 
11988c0984e5SSebastian Reichel 	if (bq->automode > 0)
1199a441f3b9Sye xingchen 		ret += sysfs_emit_at(buf, ret, ")");
12008c0984e5SSebastian Reichel 
1201a441f3b9Sye xingchen 	ret += sysfs_emit_at(buf, ret, "\n");
12028c0984e5SSebastian Reichel 	return ret;
12038c0984e5SSebastian Reichel }
12048c0984e5SSebastian Reichel 
12058c0984e5SSebastian Reichel /* show reported_mode entry (none, host, dedicated or boost) */
bq2415x_sysfs_show_reported_mode(struct device * dev,struct device_attribute * attr,char * buf)12068c0984e5SSebastian Reichel static ssize_t bq2415x_sysfs_show_reported_mode(struct device *dev,
12078c0984e5SSebastian Reichel 						struct device_attribute *attr,
12088c0984e5SSebastian Reichel 						char *buf)
12098c0984e5SSebastian Reichel {
12108c0984e5SSebastian Reichel 	struct power_supply *psy = dev_get_drvdata(dev);
12118c0984e5SSebastian Reichel 	struct bq2415x_device *bq = power_supply_get_drvdata(psy);
12128c0984e5SSebastian Reichel 
12138c0984e5SSebastian Reichel 	if (bq->automode < 0)
12148c0984e5SSebastian Reichel 		return -EINVAL;
12158c0984e5SSebastian Reichel 
12168c0984e5SSebastian Reichel 	switch (bq->reported_mode) {
12178c0984e5SSebastian Reichel 	case BQ2415X_MODE_OFF:
1218a441f3b9Sye xingchen 		return sysfs_emit(buf, "off\n");
12198c0984e5SSebastian Reichel 	case BQ2415X_MODE_NONE:
1220a441f3b9Sye xingchen 		return sysfs_emit(buf, "none\n");
12218c0984e5SSebastian Reichel 	case BQ2415X_MODE_HOST_CHARGER:
1222a441f3b9Sye xingchen 		return sysfs_emit(buf, "host\n");
12238c0984e5SSebastian Reichel 	case BQ2415X_MODE_DEDICATED_CHARGER:
1224a441f3b9Sye xingchen 		return sysfs_emit(buf, "dedicated\n");
12258c0984e5SSebastian Reichel 	case BQ2415X_MODE_BOOST:
1226a441f3b9Sye xingchen 		return sysfs_emit(buf, "boost\n");
12278c0984e5SSebastian Reichel 	}
12288c0984e5SSebastian Reichel 
12298c0984e5SSebastian Reichel 	return -EINVAL;
12308c0984e5SSebastian Reichel }
12318c0984e5SSebastian Reichel 
12328c0984e5SSebastian Reichel /* directly set raw value to chip register, format: 'register value' */
bq2415x_sysfs_set_registers(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)12338c0984e5SSebastian Reichel static ssize_t bq2415x_sysfs_set_registers(struct device *dev,
12348c0984e5SSebastian Reichel 					   struct device_attribute *attr,
12358c0984e5SSebastian Reichel 					   const char *buf,
12368c0984e5SSebastian Reichel 					   size_t count)
12378c0984e5SSebastian Reichel {
12388c0984e5SSebastian Reichel 	struct power_supply *psy = dev_get_drvdata(dev);
12398c0984e5SSebastian Reichel 	struct bq2415x_device *bq = power_supply_get_drvdata(psy);
12408c0984e5SSebastian Reichel 	ssize_t ret = 0;
12418c0984e5SSebastian Reichel 	unsigned int reg;
12428c0984e5SSebastian Reichel 	unsigned int val;
12438c0984e5SSebastian Reichel 
12448c0984e5SSebastian Reichel 	if (sscanf(buf, "%x %x", &reg, &val) != 2)
12458c0984e5SSebastian Reichel 		return -EINVAL;
12468c0984e5SSebastian Reichel 
12478c0984e5SSebastian Reichel 	if (reg > 4 || val > 255)
12488c0984e5SSebastian Reichel 		return -EINVAL;
12498c0984e5SSebastian Reichel 
12508c0984e5SSebastian Reichel 	ret = bq2415x_i2c_write(bq, reg, val);
12518c0984e5SSebastian Reichel 	if (ret < 0)
12528c0984e5SSebastian Reichel 		return ret;
12538c0984e5SSebastian Reichel 	return count;
12548c0984e5SSebastian Reichel }
12558c0984e5SSebastian Reichel 
12568c0984e5SSebastian Reichel /* print value of chip register, format: 'register=value' */
bq2415x_sysfs_print_reg(struct bq2415x_device * bq,u8 reg,char * buf)12578c0984e5SSebastian Reichel static ssize_t bq2415x_sysfs_print_reg(struct bq2415x_device *bq,
12588c0984e5SSebastian Reichel 				       u8 reg,
12598c0984e5SSebastian Reichel 				       char *buf)
12608c0984e5SSebastian Reichel {
12618c0984e5SSebastian Reichel 	int ret = bq2415x_i2c_read(bq, reg);
12628c0984e5SSebastian Reichel 
12638c0984e5SSebastian Reichel 	if (ret < 0)
1264a441f3b9Sye xingchen 		return sysfs_emit(buf, "%#.2x=error %d\n", reg, ret);
1265a441f3b9Sye xingchen 	return sysfs_emit(buf, "%#.2x=%#.2x\n", reg, ret);
12668c0984e5SSebastian Reichel }
12678c0984e5SSebastian Reichel 
12688c0984e5SSebastian Reichel /* show all raw values of chip register, format per line: 'register=value' */
bq2415x_sysfs_show_registers(struct device * dev,struct device_attribute * attr,char * buf)12698c0984e5SSebastian Reichel static ssize_t bq2415x_sysfs_show_registers(struct device *dev,
12708c0984e5SSebastian Reichel 					    struct device_attribute *attr,
12718c0984e5SSebastian Reichel 					    char *buf)
12728c0984e5SSebastian Reichel {
12738c0984e5SSebastian Reichel 	struct power_supply *psy = dev_get_drvdata(dev);
12748c0984e5SSebastian Reichel 	struct bq2415x_device *bq = power_supply_get_drvdata(psy);
12758c0984e5SSebastian Reichel 	ssize_t ret = 0;
12768c0984e5SSebastian Reichel 
12778c0984e5SSebastian Reichel 	ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_STATUS, buf+ret);
12788c0984e5SSebastian Reichel 	ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_CONTROL, buf+ret);
12798c0984e5SSebastian Reichel 	ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_VOLTAGE, buf+ret);
12808c0984e5SSebastian Reichel 	ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_VENDER, buf+ret);
12818c0984e5SSebastian Reichel 	ret += bq2415x_sysfs_print_reg(bq, BQ2415X_REG_CURRENT, buf+ret);
12828c0984e5SSebastian Reichel 	return ret;
12838c0984e5SSebastian Reichel }
12848c0984e5SSebastian Reichel 
12858c0984e5SSebastian Reichel /* set current and voltage limit entries (in mA or mV) */
bq2415x_sysfs_set_limit(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)12868c0984e5SSebastian Reichel static ssize_t bq2415x_sysfs_set_limit(struct device *dev,
12878c0984e5SSebastian Reichel 				       struct device_attribute *attr,
12888c0984e5SSebastian Reichel 				       const char *buf,
12898c0984e5SSebastian Reichel 				       size_t count)
12908c0984e5SSebastian Reichel {
12918c0984e5SSebastian Reichel 	struct power_supply *psy = dev_get_drvdata(dev);
12928c0984e5SSebastian Reichel 	struct bq2415x_device *bq = power_supply_get_drvdata(psy);
12938c0984e5SSebastian Reichel 	long val;
12948c0984e5SSebastian Reichel 	int ret;
12958c0984e5SSebastian Reichel 
12968c0984e5SSebastian Reichel 	if (kstrtol(buf, 10, &val) < 0)
12978c0984e5SSebastian Reichel 		return -EINVAL;
12988c0984e5SSebastian Reichel 
12998c0984e5SSebastian Reichel 	if (strcmp(attr->attr.name, "current_limit") == 0)
13008c0984e5SSebastian Reichel 		ret = bq2415x_set_current_limit(bq, val);
13018c0984e5SSebastian Reichel 	else if (strcmp(attr->attr.name, "weak_battery_voltage") == 0)
13028c0984e5SSebastian Reichel 		ret = bq2415x_set_weak_battery_voltage(bq, val);
13038c0984e5SSebastian Reichel 	else if (strcmp(attr->attr.name, "battery_regulation_voltage") == 0)
13048c0984e5SSebastian Reichel 		ret = bq2415x_set_battery_regulation_voltage(bq, val);
13058c0984e5SSebastian Reichel 	else if (strcmp(attr->attr.name, "charge_current") == 0)
13068c0984e5SSebastian Reichel 		ret = bq2415x_set_charge_current(bq, val);
13078c0984e5SSebastian Reichel 	else if (strcmp(attr->attr.name, "termination_current") == 0)
13088c0984e5SSebastian Reichel 		ret = bq2415x_set_termination_current(bq, val);
13098c0984e5SSebastian Reichel 	else
13108c0984e5SSebastian Reichel 		return -EINVAL;
13118c0984e5SSebastian Reichel 
13128c0984e5SSebastian Reichel 	if (ret < 0)
13138c0984e5SSebastian Reichel 		return ret;
13148c0984e5SSebastian Reichel 	return count;
13158c0984e5SSebastian Reichel }
13168c0984e5SSebastian Reichel 
13178c0984e5SSebastian Reichel /* show current and voltage limit entries (in mA or mV) */
bq2415x_sysfs_show_limit(struct device * dev,struct device_attribute * attr,char * buf)13188c0984e5SSebastian Reichel static ssize_t bq2415x_sysfs_show_limit(struct device *dev,
13198c0984e5SSebastian Reichel 					struct device_attribute *attr,
13208c0984e5SSebastian Reichel 					char *buf)
13218c0984e5SSebastian Reichel {
13228c0984e5SSebastian Reichel 	struct power_supply *psy = dev_get_drvdata(dev);
13238c0984e5SSebastian Reichel 	struct bq2415x_device *bq = power_supply_get_drvdata(psy);
13248c0984e5SSebastian Reichel 	int ret;
13258c0984e5SSebastian Reichel 
13268c0984e5SSebastian Reichel 	if (strcmp(attr->attr.name, "current_limit") == 0)
13278c0984e5SSebastian Reichel 		ret = bq2415x_get_current_limit(bq);
13288c0984e5SSebastian Reichel 	else if (strcmp(attr->attr.name, "weak_battery_voltage") == 0)
13298c0984e5SSebastian Reichel 		ret = bq2415x_get_weak_battery_voltage(bq);
13308c0984e5SSebastian Reichel 	else if (strcmp(attr->attr.name, "battery_regulation_voltage") == 0)
13318c0984e5SSebastian Reichel 		ret = bq2415x_get_battery_regulation_voltage(bq);
13328c0984e5SSebastian Reichel 	else if (strcmp(attr->attr.name, "charge_current") == 0)
13338c0984e5SSebastian Reichel 		ret = bq2415x_get_charge_current(bq);
13348c0984e5SSebastian Reichel 	else if (strcmp(attr->attr.name, "termination_current") == 0)
13358c0984e5SSebastian Reichel 		ret = bq2415x_get_termination_current(bq);
13368c0984e5SSebastian Reichel 	else
13378c0984e5SSebastian Reichel 		return -EINVAL;
13388c0984e5SSebastian Reichel 
13398c0984e5SSebastian Reichel 	if (ret < 0)
13408c0984e5SSebastian Reichel 		return ret;
1341a441f3b9Sye xingchen 	return sysfs_emit(buf, "%d\n", ret);
13428c0984e5SSebastian Reichel }
13438c0984e5SSebastian Reichel 
13448c0984e5SSebastian Reichel /* set *_enable entries */
bq2415x_sysfs_set_enable(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)13458c0984e5SSebastian Reichel static ssize_t bq2415x_sysfs_set_enable(struct device *dev,
13468c0984e5SSebastian Reichel 					struct device_attribute *attr,
13478c0984e5SSebastian Reichel 					const char *buf,
13488c0984e5SSebastian Reichel 					size_t count)
13498c0984e5SSebastian Reichel {
13508c0984e5SSebastian Reichel 	struct power_supply *psy = dev_get_drvdata(dev);
13518c0984e5SSebastian Reichel 	struct bq2415x_device *bq = power_supply_get_drvdata(psy);
13528c0984e5SSebastian Reichel 	enum bq2415x_command command;
13538c0984e5SSebastian Reichel 	long val;
13548c0984e5SSebastian Reichel 	int ret;
13558c0984e5SSebastian Reichel 
13568c0984e5SSebastian Reichel 	if (kstrtol(buf, 10, &val) < 0)
13578c0984e5SSebastian Reichel 		return -EINVAL;
13588c0984e5SSebastian Reichel 
13598c0984e5SSebastian Reichel 	if (strcmp(attr->attr.name, "charge_termination_enable") == 0)
13608c0984e5SSebastian Reichel 		command = val ? BQ2415X_CHARGE_TERMINATION_ENABLE :
13618c0984e5SSebastian Reichel 			BQ2415X_CHARGE_TERMINATION_DISABLE;
13628c0984e5SSebastian Reichel 	else if (strcmp(attr->attr.name, "high_impedance_enable") == 0)
13638c0984e5SSebastian Reichel 		command = val ? BQ2415X_HIGH_IMPEDANCE_ENABLE :
13648c0984e5SSebastian Reichel 			BQ2415X_HIGH_IMPEDANCE_DISABLE;
13658c0984e5SSebastian Reichel 	else if (strcmp(attr->attr.name, "otg_pin_enable") == 0)
13668c0984e5SSebastian Reichel 		command = val ? BQ2415X_OTG_PIN_ENABLE :
13678c0984e5SSebastian Reichel 			BQ2415X_OTG_PIN_DISABLE;
13688c0984e5SSebastian Reichel 	else if (strcmp(attr->attr.name, "stat_pin_enable") == 0)
13698c0984e5SSebastian Reichel 		command = val ? BQ2415X_STAT_PIN_ENABLE :
13708c0984e5SSebastian Reichel 			BQ2415X_STAT_PIN_DISABLE;
13718c0984e5SSebastian Reichel 	else
13728c0984e5SSebastian Reichel 		return -EINVAL;
13738c0984e5SSebastian Reichel 
13748c0984e5SSebastian Reichel 	ret = bq2415x_exec_command(bq, command);
13758c0984e5SSebastian Reichel 	if (ret < 0)
13768c0984e5SSebastian Reichel 		return ret;
13778c0984e5SSebastian Reichel 	return count;
13788c0984e5SSebastian Reichel }
13798c0984e5SSebastian Reichel 
13808c0984e5SSebastian Reichel /* show *_enable entries */
bq2415x_sysfs_show_enable(struct device * dev,struct device_attribute * attr,char * buf)13818c0984e5SSebastian Reichel static ssize_t bq2415x_sysfs_show_enable(struct device *dev,
13828c0984e5SSebastian Reichel 					 struct device_attribute *attr,
13838c0984e5SSebastian Reichel 					 char *buf)
13848c0984e5SSebastian Reichel {
13858c0984e5SSebastian Reichel 	struct power_supply *psy = dev_get_drvdata(dev);
13868c0984e5SSebastian Reichel 	struct bq2415x_device *bq = power_supply_get_drvdata(psy);
13878c0984e5SSebastian Reichel 	enum bq2415x_command command;
13888c0984e5SSebastian Reichel 	int ret;
13898c0984e5SSebastian Reichel 
13908c0984e5SSebastian Reichel 	if (strcmp(attr->attr.name, "charge_termination_enable") == 0)
13918c0984e5SSebastian Reichel 		command = BQ2415X_CHARGE_TERMINATION_STATUS;
13928c0984e5SSebastian Reichel 	else if (strcmp(attr->attr.name, "high_impedance_enable") == 0)
13938c0984e5SSebastian Reichel 		command = BQ2415X_HIGH_IMPEDANCE_STATUS;
13948c0984e5SSebastian Reichel 	else if (strcmp(attr->attr.name, "otg_pin_enable") == 0)
13958c0984e5SSebastian Reichel 		command = BQ2415X_OTG_PIN_STATUS;
13968c0984e5SSebastian Reichel 	else if (strcmp(attr->attr.name, "stat_pin_enable") == 0)
13978c0984e5SSebastian Reichel 		command = BQ2415X_STAT_PIN_STATUS;
13988c0984e5SSebastian Reichel 	else
13998c0984e5SSebastian Reichel 		return -EINVAL;
14008c0984e5SSebastian Reichel 
14018c0984e5SSebastian Reichel 	ret = bq2415x_exec_command(bq, command);
14028c0984e5SSebastian Reichel 	if (ret < 0)
14038c0984e5SSebastian Reichel 		return ret;
1404a441f3b9Sye xingchen 	return sysfs_emit(buf, "%d\n", ret);
14058c0984e5SSebastian Reichel }
14068c0984e5SSebastian Reichel 
14078c0984e5SSebastian Reichel static DEVICE_ATTR(current_limit, S_IWUSR | S_IRUGO,
14088c0984e5SSebastian Reichel 		bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
14098c0984e5SSebastian Reichel static DEVICE_ATTR(weak_battery_voltage, S_IWUSR | S_IRUGO,
14108c0984e5SSebastian Reichel 		bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
14118c0984e5SSebastian Reichel static DEVICE_ATTR(battery_regulation_voltage, S_IWUSR | S_IRUGO,
14128c0984e5SSebastian Reichel 		bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
14138c0984e5SSebastian Reichel static DEVICE_ATTR(charge_current, S_IWUSR | S_IRUGO,
14148c0984e5SSebastian Reichel 		bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
14158c0984e5SSebastian Reichel static DEVICE_ATTR(termination_current, S_IWUSR | S_IRUGO,
14168c0984e5SSebastian Reichel 		bq2415x_sysfs_show_limit, bq2415x_sysfs_set_limit);
14178c0984e5SSebastian Reichel 
14188c0984e5SSebastian Reichel static DEVICE_ATTR(charge_termination_enable, S_IWUSR | S_IRUGO,
14198c0984e5SSebastian Reichel 		bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
14208c0984e5SSebastian Reichel static DEVICE_ATTR(high_impedance_enable, S_IWUSR | S_IRUGO,
14218c0984e5SSebastian Reichel 		bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
14228c0984e5SSebastian Reichel static DEVICE_ATTR(otg_pin_enable, S_IWUSR | S_IRUGO,
14238c0984e5SSebastian Reichel 		bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
14248c0984e5SSebastian Reichel static DEVICE_ATTR(stat_pin_enable, S_IWUSR | S_IRUGO,
14258c0984e5SSebastian Reichel 		bq2415x_sysfs_show_enable, bq2415x_sysfs_set_enable);
14268c0984e5SSebastian Reichel 
14278c0984e5SSebastian Reichel static DEVICE_ATTR(reported_mode, S_IRUGO,
14288c0984e5SSebastian Reichel 		bq2415x_sysfs_show_reported_mode, NULL);
14298c0984e5SSebastian Reichel static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO,
14308c0984e5SSebastian Reichel 		bq2415x_sysfs_show_mode, bq2415x_sysfs_set_mode);
14318c0984e5SSebastian Reichel static DEVICE_ATTR(timer, S_IWUSR | S_IRUGO,
14328c0984e5SSebastian Reichel 		bq2415x_sysfs_show_timer, bq2415x_sysfs_set_timer);
14338c0984e5SSebastian Reichel 
14348c0984e5SSebastian Reichel static DEVICE_ATTR(registers, S_IWUSR | S_IRUGO,
14358c0984e5SSebastian Reichel 		bq2415x_sysfs_show_registers, bq2415x_sysfs_set_registers);
14368c0984e5SSebastian Reichel 
14378c0984e5SSebastian Reichel static DEVICE_ATTR(otg_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
14388c0984e5SSebastian Reichel static DEVICE_ATTR(charge_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
14398c0984e5SSebastian Reichel static DEVICE_ATTR(boost_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
14408c0984e5SSebastian Reichel static DEVICE_ATTR(fault_status, S_IRUGO, bq2415x_sysfs_show_status, NULL);
14418c0984e5SSebastian Reichel 
144206215ce9SSebastian Reichel static struct attribute *bq2415x_sysfs_attrs[] = {
14438c0984e5SSebastian Reichel 	/*
14448c0984e5SSebastian Reichel 	 * TODO: some (appropriate) of these attrs should be switched to
14458c0984e5SSebastian Reichel 	 * use power supply class props.
14468c0984e5SSebastian Reichel 	 */
14478c0984e5SSebastian Reichel 	&dev_attr_current_limit.attr,
14488c0984e5SSebastian Reichel 	&dev_attr_weak_battery_voltage.attr,
14498c0984e5SSebastian Reichel 	&dev_attr_battery_regulation_voltage.attr,
14508c0984e5SSebastian Reichel 	&dev_attr_charge_current.attr,
14518c0984e5SSebastian Reichel 	&dev_attr_termination_current.attr,
14528c0984e5SSebastian Reichel 
14538c0984e5SSebastian Reichel 	&dev_attr_charge_termination_enable.attr,
14548c0984e5SSebastian Reichel 	&dev_attr_high_impedance_enable.attr,
14558c0984e5SSebastian Reichel 	&dev_attr_otg_pin_enable.attr,
14568c0984e5SSebastian Reichel 	&dev_attr_stat_pin_enable.attr,
14578c0984e5SSebastian Reichel 
14588c0984e5SSebastian Reichel 	&dev_attr_reported_mode.attr,
14598c0984e5SSebastian Reichel 	&dev_attr_mode.attr,
14608c0984e5SSebastian Reichel 	&dev_attr_timer.attr,
14618c0984e5SSebastian Reichel 
14628c0984e5SSebastian Reichel 	&dev_attr_registers.attr,
14638c0984e5SSebastian Reichel 
14648c0984e5SSebastian Reichel 	&dev_attr_otg_status.attr,
14658c0984e5SSebastian Reichel 	&dev_attr_charge_status.attr,
14668c0984e5SSebastian Reichel 	&dev_attr_boost_status.attr,
14678c0984e5SSebastian Reichel 	&dev_attr_fault_status.attr,
14688c0984e5SSebastian Reichel 	NULL,
14698c0984e5SSebastian Reichel };
14708c0984e5SSebastian Reichel 
147106215ce9SSebastian Reichel ATTRIBUTE_GROUPS(bq2415x_sysfs);
147206215ce9SSebastian Reichel 
bq2415x_power_supply_init(struct bq2415x_device * bq)147306215ce9SSebastian Reichel static int bq2415x_power_supply_init(struct bq2415x_device *bq)
147406215ce9SSebastian Reichel {
147506215ce9SSebastian Reichel 	int ret;
147606215ce9SSebastian Reichel 	int chip;
147706215ce9SSebastian Reichel 	char revstr[8];
147806215ce9SSebastian Reichel 	struct power_supply_config psy_cfg = {
147906215ce9SSebastian Reichel 		.drv_data = bq,
148006215ce9SSebastian Reichel 		.of_node = bq->dev->of_node,
148106215ce9SSebastian Reichel 		.attr_grp = bq2415x_sysfs_groups,
14828c0984e5SSebastian Reichel 	};
14838c0984e5SSebastian Reichel 
148406215ce9SSebastian Reichel 	bq->charger_desc.name = bq->name;
148506215ce9SSebastian Reichel 	bq->charger_desc.type = POWER_SUPPLY_TYPE_USB;
148606215ce9SSebastian Reichel 	bq->charger_desc.properties = bq2415x_power_supply_props;
148706215ce9SSebastian Reichel 	bq->charger_desc.num_properties =
148806215ce9SSebastian Reichel 			ARRAY_SIZE(bq2415x_power_supply_props);
148906215ce9SSebastian Reichel 	bq->charger_desc.get_property = bq2415x_power_supply_get_property;
149006215ce9SSebastian Reichel 
149106215ce9SSebastian Reichel 	ret = bq2415x_detect_chip(bq);
149206215ce9SSebastian Reichel 	if (ret < 0)
149306215ce9SSebastian Reichel 		chip = BQUNKNOWN;
149406215ce9SSebastian Reichel 	else
149506215ce9SSebastian Reichel 		chip = ret;
149606215ce9SSebastian Reichel 
149706215ce9SSebastian Reichel 	ret = bq2415x_detect_revision(bq);
149806215ce9SSebastian Reichel 	if (ret < 0)
149906215ce9SSebastian Reichel 		strcpy(revstr, "unknown");
150006215ce9SSebastian Reichel 	else
150106215ce9SSebastian Reichel 		sprintf(revstr, "1.%d", ret);
150206215ce9SSebastian Reichel 
150306215ce9SSebastian Reichel 	bq->model = kasprintf(GFP_KERNEL,
150406215ce9SSebastian Reichel 				"chip %s, revision %s, vender code %.3d",
150506215ce9SSebastian Reichel 				bq2415x_chip_name[chip], revstr,
150606215ce9SSebastian Reichel 				bq2415x_get_vender_code(bq));
150706215ce9SSebastian Reichel 	if (!bq->model) {
150806215ce9SSebastian Reichel 		dev_err(bq->dev, "failed to allocate model name\n");
150906215ce9SSebastian Reichel 		return -ENOMEM;
15108c0984e5SSebastian Reichel 	}
15118c0984e5SSebastian Reichel 
151206215ce9SSebastian Reichel 	bq->charger = power_supply_register(bq->dev, &bq->charger_desc,
151306215ce9SSebastian Reichel 					    &psy_cfg);
151406215ce9SSebastian Reichel 	if (IS_ERR(bq->charger)) {
151506215ce9SSebastian Reichel 		kfree(bq->model);
151606215ce9SSebastian Reichel 		return PTR_ERR(bq->charger);
151706215ce9SSebastian Reichel 	}
151806215ce9SSebastian Reichel 
151906215ce9SSebastian Reichel 	return 0;
15208c0984e5SSebastian Reichel }
15218c0984e5SSebastian Reichel 
15228c0984e5SSebastian Reichel /* main bq2415x probe function */
bq2415x_probe(struct i2c_client * client)152331c05051SUwe Kleine-König static int bq2415x_probe(struct i2c_client *client)
15248c0984e5SSebastian Reichel {
152531c05051SUwe Kleine-König 	const struct i2c_device_id *id = i2c_client_get_device_id(client);
15268c0984e5SSebastian Reichel 	int ret;
15278c0984e5SSebastian Reichel 	int num;
15288c0984e5SSebastian Reichel 	char *name = NULL;
15298c0984e5SSebastian Reichel 	struct bq2415x_device *bq;
15308c0984e5SSebastian Reichel 	struct device_node *np = client->dev.of_node;
15318c0984e5SSebastian Reichel 	struct bq2415x_platform_data *pdata = client->dev.platform_data;
15328c0984e5SSebastian Reichel 	const struct acpi_device_id *acpi_id = NULL;
15338c0984e5SSebastian Reichel 	struct power_supply *notify_psy = NULL;
15348c0984e5SSebastian Reichel 	union power_supply_propval prop;
15358c0984e5SSebastian Reichel 
15368c0984e5SSebastian Reichel 	if (!np && !pdata && !ACPI_HANDLE(&client->dev)) {
15378c0984e5SSebastian Reichel 		dev_err(&client->dev, "Neither devicetree, nor platform data, nor ACPI support\n");
15388c0984e5SSebastian Reichel 		return -ENODEV;
15398c0984e5SSebastian Reichel 	}
15408c0984e5SSebastian Reichel 
15418c0984e5SSebastian Reichel 	/* Get new ID for the new device */
15428c0984e5SSebastian Reichel 	mutex_lock(&bq2415x_id_mutex);
15438c0984e5SSebastian Reichel 	num = idr_alloc(&bq2415x_id, client, 0, 0, GFP_KERNEL);
15448c0984e5SSebastian Reichel 	mutex_unlock(&bq2415x_id_mutex);
15458c0984e5SSebastian Reichel 	if (num < 0)
15468c0984e5SSebastian Reichel 		return num;
15478c0984e5SSebastian Reichel 
15488c0984e5SSebastian Reichel 	if (id) {
15498c0984e5SSebastian Reichel 		name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);
15508c0984e5SSebastian Reichel 	} else if (ACPI_HANDLE(&client->dev)) {
15518c0984e5SSebastian Reichel 		acpi_id =
15528c0984e5SSebastian Reichel 			acpi_match_device(client->dev.driver->acpi_match_table,
15538c0984e5SSebastian Reichel 					  &client->dev);
1554a1b94355SColin Ian King 		if (!acpi_id) {
1555a1b94355SColin Ian King 			dev_err(&client->dev, "failed to match device name\n");
1556a1b94355SColin Ian King 			ret = -ENODEV;
1557a1b94355SColin Ian King 			goto error_1;
1558a1b94355SColin Ian King 		}
15598c0984e5SSebastian Reichel 		name = kasprintf(GFP_KERNEL, "%s-%d", acpi_id->id, num);
15608c0984e5SSebastian Reichel 	}
15618c0984e5SSebastian Reichel 	if (!name) {
15628c0984e5SSebastian Reichel 		dev_err(&client->dev, "failed to allocate device name\n");
15638c0984e5SSebastian Reichel 		ret = -ENOMEM;
15648c0984e5SSebastian Reichel 		goto error_1;
15658c0984e5SSebastian Reichel 	}
15668c0984e5SSebastian Reichel 
15678c0984e5SSebastian Reichel 	bq = devm_kzalloc(&client->dev, sizeof(*bq), GFP_KERNEL);
15688c0984e5SSebastian Reichel 	if (!bq) {
15698c0984e5SSebastian Reichel 		ret = -ENOMEM;
15708c0984e5SSebastian Reichel 		goto error_2;
15718c0984e5SSebastian Reichel 	}
15728c0984e5SSebastian Reichel 
15738c0984e5SSebastian Reichel 	i2c_set_clientdata(client, bq);
15748c0984e5SSebastian Reichel 
15758c0984e5SSebastian Reichel 	bq->id = num;
15768c0984e5SSebastian Reichel 	bq->dev = &client->dev;
15778c0984e5SSebastian Reichel 	if (id)
15788c0984e5SSebastian Reichel 		bq->chip = id->driver_data;
15798c0984e5SSebastian Reichel 	else if (ACPI_HANDLE(bq->dev))
15808c0984e5SSebastian Reichel 		bq->chip = acpi_id->driver_data;
15818c0984e5SSebastian Reichel 	bq->name = name;
15828c0984e5SSebastian Reichel 	bq->mode = BQ2415X_MODE_OFF;
15838c0984e5SSebastian Reichel 	bq->reported_mode = BQ2415X_MODE_OFF;
15848c0984e5SSebastian Reichel 	bq->autotimer = 0;
15858c0984e5SSebastian Reichel 	bq->automode = 0;
15868c0984e5SSebastian Reichel 
15878c0984e5SSebastian Reichel 	if (np || ACPI_HANDLE(bq->dev)) {
15888c0984e5SSebastian Reichel 		ret = device_property_read_u32(bq->dev,
15898c0984e5SSebastian Reichel 					       "ti,current-limit",
15908c0984e5SSebastian Reichel 					       &bq->init_data.current_limit);
15918c0984e5SSebastian Reichel 		if (ret)
15928c0984e5SSebastian Reichel 			goto error_2;
15938c0984e5SSebastian Reichel 		ret = device_property_read_u32(bq->dev,
15948c0984e5SSebastian Reichel 					"ti,weak-battery-voltage",
15958c0984e5SSebastian Reichel 					&bq->init_data.weak_battery_voltage);
15968c0984e5SSebastian Reichel 		if (ret)
15978c0984e5SSebastian Reichel 			goto error_2;
15988c0984e5SSebastian Reichel 		ret = device_property_read_u32(bq->dev,
15998c0984e5SSebastian Reichel 				"ti,battery-regulation-voltage",
16008c0984e5SSebastian Reichel 				&bq->init_data.battery_regulation_voltage);
16018c0984e5SSebastian Reichel 		if (ret)
16028c0984e5SSebastian Reichel 			goto error_2;
16038c0984e5SSebastian Reichel 		ret = device_property_read_u32(bq->dev,
16048c0984e5SSebastian Reichel 					       "ti,charge-current",
16058c0984e5SSebastian Reichel 					       &bq->init_data.charge_current);
16068c0984e5SSebastian Reichel 		if (ret)
16078c0984e5SSebastian Reichel 			goto error_2;
16088c0984e5SSebastian Reichel 		ret = device_property_read_u32(bq->dev,
16098c0984e5SSebastian Reichel 				"ti,termination-current",
16108c0984e5SSebastian Reichel 				&bq->init_data.termination_current);
16118c0984e5SSebastian Reichel 		if (ret)
16128c0984e5SSebastian Reichel 			goto error_2;
16138c0984e5SSebastian Reichel 		ret = device_property_read_u32(bq->dev,
16148c0984e5SSebastian Reichel 					       "ti,resistor-sense",
16158c0984e5SSebastian Reichel 					       &bq->init_data.resistor_sense);
16168c0984e5SSebastian Reichel 		if (ret)
16178c0984e5SSebastian Reichel 			goto error_2;
16188c0984e5SSebastian Reichel 		if (np)
16198c0984e5SSebastian Reichel 			bq->notify_node = of_parse_phandle(np,
16208c0984e5SSebastian Reichel 						"ti,usb-charger-detection", 0);
16218c0984e5SSebastian Reichel 	} else {
16228c0984e5SSebastian Reichel 		memcpy(&bq->init_data, pdata, sizeof(bq->init_data));
16238c0984e5SSebastian Reichel 	}
16248c0984e5SSebastian Reichel 
16258c0984e5SSebastian Reichel 	bq2415x_reset_chip(bq);
16268c0984e5SSebastian Reichel 
16278c0984e5SSebastian Reichel 	ret = bq2415x_power_supply_init(bq);
16288c0984e5SSebastian Reichel 	if (ret) {
16298c0984e5SSebastian Reichel 		dev_err(bq->dev, "failed to register power supply: %d\n", ret);
16308c0984e5SSebastian Reichel 		goto error_2;
16318c0984e5SSebastian Reichel 	}
16328c0984e5SSebastian Reichel 
16338c0984e5SSebastian Reichel 	ret = bq2415x_set_defaults(bq);
16348c0984e5SSebastian Reichel 	if (ret) {
16358c0984e5SSebastian Reichel 		dev_err(bq->dev, "failed to set default values: %d\n", ret);
163606215ce9SSebastian Reichel 		goto error_3;
16378c0984e5SSebastian Reichel 	}
16388c0984e5SSebastian Reichel 
16398c0984e5SSebastian Reichel 	if (bq->notify_node || bq->init_data.notify_device) {
16408c0984e5SSebastian Reichel 		bq->nb.notifier_call = bq2415x_notifier_call;
16418c0984e5SSebastian Reichel 		ret = power_supply_reg_notifier(&bq->nb);
16428c0984e5SSebastian Reichel 		if (ret) {
16438c0984e5SSebastian Reichel 			dev_err(bq->dev, "failed to reg notifier: %d\n", ret);
164406215ce9SSebastian Reichel 			goto error_3;
16458c0984e5SSebastian Reichel 		}
16468c0984e5SSebastian Reichel 
16478c0984e5SSebastian Reichel 		bq->automode = 1;
16488c0984e5SSebastian Reichel 		dev_info(bq->dev, "automode supported, waiting for events\n");
16498c0984e5SSebastian Reichel 	} else {
16508c0984e5SSebastian Reichel 		bq->automode = -1;
16518c0984e5SSebastian Reichel 		dev_info(bq->dev, "automode not supported\n");
16528c0984e5SSebastian Reichel 	}
16538c0984e5SSebastian Reichel 
16548c0984e5SSebastian Reichel 	/* Query for initial reported_mode and set it */
16558c0984e5SSebastian Reichel 	if (bq->nb.notifier_call) {
16568c0984e5SSebastian Reichel 		if (np) {
16578c0984e5SSebastian Reichel 			notify_psy = power_supply_get_by_phandle(np,
16588c0984e5SSebastian Reichel 						"ti,usb-charger-detection");
16598c0984e5SSebastian Reichel 			if (IS_ERR(notify_psy))
16608c0984e5SSebastian Reichel 				notify_psy = NULL;
16618c0984e5SSebastian Reichel 		} else if (bq->init_data.notify_device) {
16628c0984e5SSebastian Reichel 			notify_psy = power_supply_get_by_name(
16638c0984e5SSebastian Reichel 						bq->init_data.notify_device);
16648c0984e5SSebastian Reichel 		}
16658c0984e5SSebastian Reichel 	}
16668c0984e5SSebastian Reichel 	if (notify_psy) {
16678c0984e5SSebastian Reichel 		ret = power_supply_get_property(notify_psy,
16688c0984e5SSebastian Reichel 					POWER_SUPPLY_PROP_CURRENT_MAX, &prop);
16698c0984e5SSebastian Reichel 		power_supply_put(notify_psy);
16708c0984e5SSebastian Reichel 
16718c0984e5SSebastian Reichel 		if (ret == 0) {
16728c0984e5SSebastian Reichel 			bq2415x_update_reported_mode(bq, prop.intval);
16738c0984e5SSebastian Reichel 			bq2415x_set_mode(bq, bq->reported_mode);
16748c0984e5SSebastian Reichel 		}
16758c0984e5SSebastian Reichel 	}
16768c0984e5SSebastian Reichel 
16778c0984e5SSebastian Reichel 	INIT_DELAYED_WORK(&bq->work, bq2415x_timer_work);
16788c0984e5SSebastian Reichel 	bq2415x_set_autotimer(bq, 1);
16798c0984e5SSebastian Reichel 
16808c0984e5SSebastian Reichel 	dev_info(bq->dev, "driver registered\n");
16818c0984e5SSebastian Reichel 	return 0;
16828c0984e5SSebastian Reichel 
16838c0984e5SSebastian Reichel error_3:
16848c0984e5SSebastian Reichel 	bq2415x_power_supply_exit(bq);
16858c0984e5SSebastian Reichel error_2:
16868c0984e5SSebastian Reichel 	if (bq)
16878c0984e5SSebastian Reichel 		of_node_put(bq->notify_node);
16888c0984e5SSebastian Reichel 	kfree(name);
16898c0984e5SSebastian Reichel error_1:
16908c0984e5SSebastian Reichel 	mutex_lock(&bq2415x_id_mutex);
16918c0984e5SSebastian Reichel 	idr_remove(&bq2415x_id, num);
16928c0984e5SSebastian Reichel 	mutex_unlock(&bq2415x_id_mutex);
16938c0984e5SSebastian Reichel 
16948c0984e5SSebastian Reichel 	return ret;
16958c0984e5SSebastian Reichel }
16968c0984e5SSebastian Reichel 
16978c0984e5SSebastian Reichel /* main bq2415x remove function */
16988c0984e5SSebastian Reichel 
bq2415x_remove(struct i2c_client * client)1699ed5c2f5fSUwe Kleine-König static void bq2415x_remove(struct i2c_client *client)
17008c0984e5SSebastian Reichel {
17018c0984e5SSebastian Reichel 	struct bq2415x_device *bq = i2c_get_clientdata(client);
17028c0984e5SSebastian Reichel 
17038c0984e5SSebastian Reichel 	if (bq->nb.notifier_call)
17048c0984e5SSebastian Reichel 		power_supply_unreg_notifier(&bq->nb);
17058c0984e5SSebastian Reichel 
17068c0984e5SSebastian Reichel 	of_node_put(bq->notify_node);
17078c0984e5SSebastian Reichel 	bq2415x_power_supply_exit(bq);
17088c0984e5SSebastian Reichel 
17098c0984e5SSebastian Reichel 	bq2415x_reset_chip(bq);
17108c0984e5SSebastian Reichel 
17118c0984e5SSebastian Reichel 	mutex_lock(&bq2415x_id_mutex);
17128c0984e5SSebastian Reichel 	idr_remove(&bq2415x_id, bq->id);
17138c0984e5SSebastian Reichel 	mutex_unlock(&bq2415x_id_mutex);
17148c0984e5SSebastian Reichel 
17158c0984e5SSebastian Reichel 	dev_info(bq->dev, "driver unregistered\n");
17168c0984e5SSebastian Reichel 
17178c0984e5SSebastian Reichel 	kfree(bq->name);
17188c0984e5SSebastian Reichel }
17198c0984e5SSebastian Reichel 
17208c0984e5SSebastian Reichel static const struct i2c_device_id bq2415x_i2c_id_table[] = {
17218c0984e5SSebastian Reichel 	{ "bq2415x", BQUNKNOWN },
17228c0984e5SSebastian Reichel 	{ "bq24150", BQ24150 },
17238c0984e5SSebastian Reichel 	{ "bq24150a", BQ24150A },
17248c0984e5SSebastian Reichel 	{ "bq24151", BQ24151 },
17258c0984e5SSebastian Reichel 	{ "bq24151a", BQ24151A },
17268c0984e5SSebastian Reichel 	{ "bq24152", BQ24152 },
17278c0984e5SSebastian Reichel 	{ "bq24153", BQ24153 },
17288c0984e5SSebastian Reichel 	{ "bq24153a", BQ24153A },
17298c0984e5SSebastian Reichel 	{ "bq24155", BQ24155 },
17308c0984e5SSebastian Reichel 	{ "bq24156", BQ24156 },
17318c0984e5SSebastian Reichel 	{ "bq24156a", BQ24156A },
17328c0984e5SSebastian Reichel 	{ "bq24157s", BQ24157S },
17338c0984e5SSebastian Reichel 	{ "bq24158", BQ24158 },
17348c0984e5SSebastian Reichel 	{},
17358c0984e5SSebastian Reichel };
17368c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(i2c, bq2415x_i2c_id_table);
17378c0984e5SSebastian Reichel 
17388c0984e5SSebastian Reichel #ifdef CONFIG_ACPI
17398c0984e5SSebastian Reichel static const struct acpi_device_id bq2415x_i2c_acpi_match[] = {
17408c0984e5SSebastian Reichel 	{ "BQ2415X", BQUNKNOWN },
17418c0984e5SSebastian Reichel 	{ "BQ241500", BQ24150 },
17428c0984e5SSebastian Reichel 	{ "BQA24150", BQ24150A },
17438c0984e5SSebastian Reichel 	{ "BQ241510", BQ24151 },
17448c0984e5SSebastian Reichel 	{ "BQA24151", BQ24151A },
17458c0984e5SSebastian Reichel 	{ "BQ241520", BQ24152 },
17468c0984e5SSebastian Reichel 	{ "BQ241530", BQ24153 },
17478c0984e5SSebastian Reichel 	{ "BQA24153", BQ24153A },
17488c0984e5SSebastian Reichel 	{ "BQ241550", BQ24155 },
17498c0984e5SSebastian Reichel 	{ "BQ241560", BQ24156 },
17508c0984e5SSebastian Reichel 	{ "BQA24156", BQ24156A },
17518c0984e5SSebastian Reichel 	{ "BQS24157", BQ24157S },
17528c0984e5SSebastian Reichel 	{ "BQ241580", BQ24158 },
17538c0984e5SSebastian Reichel 	{},
17548c0984e5SSebastian Reichel };
17558c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(acpi, bq2415x_i2c_acpi_match);
17568c0984e5SSebastian Reichel #endif
17578c0984e5SSebastian Reichel 
17588c0984e5SSebastian Reichel #ifdef CONFIG_OF
17598c0984e5SSebastian Reichel static const struct of_device_id bq2415x_of_match_table[] = {
17608c0984e5SSebastian Reichel 	{ .compatible = "ti,bq24150" },
17618c0984e5SSebastian Reichel 	{ .compatible = "ti,bq24150a" },
17628c0984e5SSebastian Reichel 	{ .compatible = "ti,bq24151" },
17638c0984e5SSebastian Reichel 	{ .compatible = "ti,bq24151a" },
17648c0984e5SSebastian Reichel 	{ .compatible = "ti,bq24152" },
17658c0984e5SSebastian Reichel 	{ .compatible = "ti,bq24153" },
17668c0984e5SSebastian Reichel 	{ .compatible = "ti,bq24153a" },
17678c0984e5SSebastian Reichel 	{ .compatible = "ti,bq24155" },
17688c0984e5SSebastian Reichel 	{ .compatible = "ti,bq24156" },
17698c0984e5SSebastian Reichel 	{ .compatible = "ti,bq24156a" },
17708c0984e5SSebastian Reichel 	{ .compatible = "ti,bq24157s" },
17718c0984e5SSebastian Reichel 	{ .compatible = "ti,bq24158" },
17728c0984e5SSebastian Reichel 	{},
17738c0984e5SSebastian Reichel };
17748c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(of, bq2415x_of_match_table);
17758c0984e5SSebastian Reichel #endif
17768c0984e5SSebastian Reichel 
17778c0984e5SSebastian Reichel static struct i2c_driver bq2415x_driver = {
17788c0984e5SSebastian Reichel 	.driver = {
17798c0984e5SSebastian Reichel 		.name = "bq2415x-charger",
17808c0984e5SSebastian Reichel 		.of_match_table = of_match_ptr(bq2415x_of_match_table),
17818c0984e5SSebastian Reichel 		.acpi_match_table = ACPI_PTR(bq2415x_i2c_acpi_match),
17828c0984e5SSebastian Reichel 	},
1783*fe20b1dcSUwe Kleine-König 	.probe = bq2415x_probe,
17848c0984e5SSebastian Reichel 	.remove = bq2415x_remove,
17858c0984e5SSebastian Reichel 	.id_table = bq2415x_i2c_id_table,
17868c0984e5SSebastian Reichel };
17878c0984e5SSebastian Reichel module_i2c_driver(bq2415x_driver);
17888c0984e5SSebastian Reichel 
1789149ed3d4SPali Rohár MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
17908c0984e5SSebastian Reichel MODULE_DESCRIPTION("bq2415x charger driver");
17918c0984e5SSebastian Reichel MODULE_LICENSE("GPL");
1792