18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Battery charger driver for TI BQ24735
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
78c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by
88c2ecf20Sopenharmony_ci * the Free Software Foundation;
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but WITHOUT
118c2ecf20Sopenharmony_ci * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
128c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
138c2ecf20Sopenharmony_ci * more details.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License along
168c2ecf20Sopenharmony_ci * with this program; if not, write to the Free Software Foundation, Inc.,
178c2ecf20Sopenharmony_ci * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <linux/err.h>
218c2ecf20Sopenharmony_ci#include <linux/gpio.h>
228c2ecf20Sopenharmony_ci#include <linux/i2c.h>
238c2ecf20Sopenharmony_ci#include <linux/init.h>
248c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
258c2ecf20Sopenharmony_ci#include <linux/kernel.h>
268c2ecf20Sopenharmony_ci#include <linux/module.h>
278c2ecf20Sopenharmony_ci#include <linux/of.h>
288c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
298c2ecf20Sopenharmony_ci#include <linux/power_supply.h>
308c2ecf20Sopenharmony_ci#include <linux/slab.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include <linux/power/bq24735-charger.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define BQ24735_CHG_OPT			0x12
358c2ecf20Sopenharmony_ci#define BQ24735_CHG_OPT_CHARGE_DISABLE	(1 << 0)
368c2ecf20Sopenharmony_ci#define BQ24735_CHG_OPT_AC_PRESENT	(1 << 4)
378c2ecf20Sopenharmony_ci#define BQ24735_CHARGE_CURRENT		0x14
388c2ecf20Sopenharmony_ci#define BQ24735_CHARGE_CURRENT_MASK	0x1fc0
398c2ecf20Sopenharmony_ci#define BQ24735_CHARGE_VOLTAGE		0x15
408c2ecf20Sopenharmony_ci#define BQ24735_CHARGE_VOLTAGE_MASK	0x7ff0
418c2ecf20Sopenharmony_ci#define BQ24735_INPUT_CURRENT		0x3f
428c2ecf20Sopenharmony_ci#define BQ24735_INPUT_CURRENT_MASK	0x1f80
438c2ecf20Sopenharmony_ci#define BQ24735_MANUFACTURER_ID		0xfe
448c2ecf20Sopenharmony_ci#define BQ24735_DEVICE_ID		0xff
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistruct bq24735 {
478c2ecf20Sopenharmony_ci	struct power_supply		*charger;
488c2ecf20Sopenharmony_ci	struct power_supply_desc	charger_desc;
498c2ecf20Sopenharmony_ci	struct i2c_client		*client;
508c2ecf20Sopenharmony_ci	struct bq24735_platform		*pdata;
518c2ecf20Sopenharmony_ci	struct mutex			lock;
528c2ecf20Sopenharmony_ci	struct gpio_desc		*status_gpio;
538c2ecf20Sopenharmony_ci	struct delayed_work		poll;
548c2ecf20Sopenharmony_ci	u32				poll_interval;
558c2ecf20Sopenharmony_ci	bool				charging;
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic inline struct bq24735 *to_bq24735(struct power_supply *psy)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	return power_supply_get_drvdata(psy);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic enum power_supply_property bq24735_charger_properties[] = {
648c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS,
658c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic int bq24735_charger_property_is_writeable(struct power_supply *psy,
698c2ecf20Sopenharmony_ci						 enum power_supply_property psp)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	switch (psp) {
728c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
738c2ecf20Sopenharmony_ci		return 1;
748c2ecf20Sopenharmony_ci	default:
758c2ecf20Sopenharmony_ci		break;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return 0;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic inline int bq24735_write_word(struct i2c_client *client, u8 reg,
828c2ecf20Sopenharmony_ci				     u16 value)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	return i2c_smbus_write_word_data(client, reg, value);
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic inline int bq24735_read_word(struct i2c_client *client, u8 reg)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	return i2c_smbus_read_word_data(client, reg);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int bq24735_update_word(struct i2c_client *client, u8 reg,
938c2ecf20Sopenharmony_ci			       u16 mask, u16 value)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	unsigned int tmp;
968c2ecf20Sopenharmony_ci	int ret;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	ret = bq24735_read_word(client, reg);
998c2ecf20Sopenharmony_ci	if (ret < 0)
1008c2ecf20Sopenharmony_ci		return ret;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	tmp = ret & ~mask;
1038c2ecf20Sopenharmony_ci	tmp |= value & mask;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	return bq24735_write_word(client, reg, tmp);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic int bq24735_config_charger(struct bq24735 *charger)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct bq24735_platform *pdata = charger->pdata;
1118c2ecf20Sopenharmony_ci	int ret;
1128c2ecf20Sopenharmony_ci	u16 value;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (pdata->ext_control)
1158c2ecf20Sopenharmony_ci		return 0;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (pdata->charge_current) {
1188c2ecf20Sopenharmony_ci		value = pdata->charge_current & BQ24735_CHARGE_CURRENT_MASK;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci		ret = bq24735_write_word(charger->client,
1218c2ecf20Sopenharmony_ci					 BQ24735_CHARGE_CURRENT, value);
1228c2ecf20Sopenharmony_ci		if (ret < 0) {
1238c2ecf20Sopenharmony_ci			dev_err(&charger->client->dev,
1248c2ecf20Sopenharmony_ci				"Failed to write charger current : %d\n",
1258c2ecf20Sopenharmony_ci				ret);
1268c2ecf20Sopenharmony_ci			return ret;
1278c2ecf20Sopenharmony_ci		}
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	if (pdata->charge_voltage) {
1318c2ecf20Sopenharmony_ci		value = pdata->charge_voltage & BQ24735_CHARGE_VOLTAGE_MASK;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci		ret = bq24735_write_word(charger->client,
1348c2ecf20Sopenharmony_ci					 BQ24735_CHARGE_VOLTAGE, value);
1358c2ecf20Sopenharmony_ci		if (ret < 0) {
1368c2ecf20Sopenharmony_ci			dev_err(&charger->client->dev,
1378c2ecf20Sopenharmony_ci				"Failed to write charger voltage : %d\n",
1388c2ecf20Sopenharmony_ci				ret);
1398c2ecf20Sopenharmony_ci			return ret;
1408c2ecf20Sopenharmony_ci		}
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (pdata->input_current) {
1448c2ecf20Sopenharmony_ci		value = pdata->input_current & BQ24735_INPUT_CURRENT_MASK;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci		ret = bq24735_write_word(charger->client,
1478c2ecf20Sopenharmony_ci					 BQ24735_INPUT_CURRENT, value);
1488c2ecf20Sopenharmony_ci		if (ret < 0) {
1498c2ecf20Sopenharmony_ci			dev_err(&charger->client->dev,
1508c2ecf20Sopenharmony_ci				"Failed to write input current : %d\n",
1518c2ecf20Sopenharmony_ci				ret);
1528c2ecf20Sopenharmony_ci			return ret;
1538c2ecf20Sopenharmony_ci		}
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return 0;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic inline int bq24735_enable_charging(struct bq24735 *charger)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	int ret;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (charger->pdata->ext_control)
1648c2ecf20Sopenharmony_ci		return 0;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	ret = bq24735_config_charger(charger);
1678c2ecf20Sopenharmony_ci	if (ret)
1688c2ecf20Sopenharmony_ci		return ret;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
1718c2ecf20Sopenharmony_ci				   BQ24735_CHG_OPT_CHARGE_DISABLE, 0);
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic inline int bq24735_disable_charging(struct bq24735 *charger)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	if (charger->pdata->ext_control)
1778c2ecf20Sopenharmony_ci		return 0;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
1808c2ecf20Sopenharmony_ci				   BQ24735_CHG_OPT_CHARGE_DISABLE,
1818c2ecf20Sopenharmony_ci				   BQ24735_CHG_OPT_CHARGE_DISABLE);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic bool bq24735_charger_is_present(struct bq24735 *charger)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	if (charger->status_gpio) {
1878c2ecf20Sopenharmony_ci		return !gpiod_get_value_cansleep(charger->status_gpio);
1888c2ecf20Sopenharmony_ci	} else {
1898c2ecf20Sopenharmony_ci		int ac = 0;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci		ac = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
1928c2ecf20Sopenharmony_ci		if (ac < 0) {
1938c2ecf20Sopenharmony_ci			dev_dbg(&charger->client->dev,
1948c2ecf20Sopenharmony_ci				"Failed to read charger options : %d\n",
1958c2ecf20Sopenharmony_ci				ac);
1968c2ecf20Sopenharmony_ci			return false;
1978c2ecf20Sopenharmony_ci		}
1988c2ecf20Sopenharmony_ci		return (ac & BQ24735_CHG_OPT_AC_PRESENT) ? true : false;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return false;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic int bq24735_charger_is_charging(struct bq24735 *charger)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	int ret;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	if (!bq24735_charger_is_present(charger))
2098c2ecf20Sopenharmony_ci		return 0;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	ret  = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
2128c2ecf20Sopenharmony_ci	if (ret < 0)
2138c2ecf20Sopenharmony_ci		return ret;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	return !(ret & BQ24735_CHG_OPT_CHARGE_DISABLE);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic void bq24735_update(struct bq24735 *charger)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	mutex_lock(&charger->lock);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (charger->charging && bq24735_charger_is_present(charger))
2238c2ecf20Sopenharmony_ci		bq24735_enable_charging(charger);
2248c2ecf20Sopenharmony_ci	else
2258c2ecf20Sopenharmony_ci		bq24735_disable_charging(charger);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	mutex_unlock(&charger->lock);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	power_supply_changed(charger->charger);
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic irqreturn_t bq24735_charger_isr(int irq, void *devid)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct power_supply *psy = devid;
2358c2ecf20Sopenharmony_ci	struct bq24735 *charger = to_bq24735(psy);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	bq24735_update(charger);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic void bq24735_poll(struct work_struct *work)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct bq24735 *charger = container_of(work, struct bq24735, poll.work);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	bq24735_update(charger);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	schedule_delayed_work(&charger->poll,
2498c2ecf20Sopenharmony_ci			      msecs_to_jiffies(charger->poll_interval));
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic int bq24735_charger_get_property(struct power_supply *psy,
2538c2ecf20Sopenharmony_ci					enum power_supply_property psp,
2548c2ecf20Sopenharmony_ci					union power_supply_propval *val)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	struct bq24735 *charger = to_bq24735(psy);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	switch (psp) {
2598c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
2608c2ecf20Sopenharmony_ci		val->intval = bq24735_charger_is_present(charger) ? 1 : 0;
2618c2ecf20Sopenharmony_ci		break;
2628c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
2638c2ecf20Sopenharmony_ci		switch (bq24735_charger_is_charging(charger)) {
2648c2ecf20Sopenharmony_ci		case 1:
2658c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_STATUS_CHARGING;
2668c2ecf20Sopenharmony_ci			break;
2678c2ecf20Sopenharmony_ci		case 0:
2688c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
2698c2ecf20Sopenharmony_ci			break;
2708c2ecf20Sopenharmony_ci		default:
2718c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
2728c2ecf20Sopenharmony_ci			break;
2738c2ecf20Sopenharmony_ci		}
2748c2ecf20Sopenharmony_ci		break;
2758c2ecf20Sopenharmony_ci	default:
2768c2ecf20Sopenharmony_ci		return -EINVAL;
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	return 0;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic int bq24735_charger_set_property(struct power_supply *psy,
2838c2ecf20Sopenharmony_ci					enum power_supply_property psp,
2848c2ecf20Sopenharmony_ci					const union power_supply_propval *val)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	struct bq24735 *charger = to_bq24735(psy);
2878c2ecf20Sopenharmony_ci	int ret;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	switch (psp) {
2908c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
2918c2ecf20Sopenharmony_ci		switch (val->intval) {
2928c2ecf20Sopenharmony_ci		case POWER_SUPPLY_STATUS_CHARGING:
2938c2ecf20Sopenharmony_ci			mutex_lock(&charger->lock);
2948c2ecf20Sopenharmony_ci			charger->charging = true;
2958c2ecf20Sopenharmony_ci			ret = bq24735_enable_charging(charger);
2968c2ecf20Sopenharmony_ci			mutex_unlock(&charger->lock);
2978c2ecf20Sopenharmony_ci			if (ret)
2988c2ecf20Sopenharmony_ci				return ret;
2998c2ecf20Sopenharmony_ci			break;
3008c2ecf20Sopenharmony_ci		case POWER_SUPPLY_STATUS_DISCHARGING:
3018c2ecf20Sopenharmony_ci		case POWER_SUPPLY_STATUS_NOT_CHARGING:
3028c2ecf20Sopenharmony_ci			mutex_lock(&charger->lock);
3038c2ecf20Sopenharmony_ci			charger->charging = false;
3048c2ecf20Sopenharmony_ci			ret = bq24735_disable_charging(charger);
3058c2ecf20Sopenharmony_ci			mutex_unlock(&charger->lock);
3068c2ecf20Sopenharmony_ci			if (ret)
3078c2ecf20Sopenharmony_ci				return ret;
3088c2ecf20Sopenharmony_ci			break;
3098c2ecf20Sopenharmony_ci		default:
3108c2ecf20Sopenharmony_ci			return -EINVAL;
3118c2ecf20Sopenharmony_ci		}
3128c2ecf20Sopenharmony_ci		power_supply_changed(psy);
3138c2ecf20Sopenharmony_ci		break;
3148c2ecf20Sopenharmony_ci	default:
3158c2ecf20Sopenharmony_ci		return -EPERM;
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return 0;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic struct bq24735_platform *bq24735_parse_dt_data(struct i2c_client *client)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct bq24735_platform *pdata;
3248c2ecf20Sopenharmony_ci	struct device_node *np = client->dev.of_node;
3258c2ecf20Sopenharmony_ci	u32 val;
3268c2ecf20Sopenharmony_ci	int ret;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
3298c2ecf20Sopenharmony_ci	if (!pdata) {
3308c2ecf20Sopenharmony_ci		dev_err(&client->dev,
3318c2ecf20Sopenharmony_ci			"Memory alloc for bq24735 pdata failed\n");
3328c2ecf20Sopenharmony_ci		return NULL;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "ti,charge-current", &val);
3368c2ecf20Sopenharmony_ci	if (!ret)
3378c2ecf20Sopenharmony_ci		pdata->charge_current = val;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "ti,charge-voltage", &val);
3408c2ecf20Sopenharmony_ci	if (!ret)
3418c2ecf20Sopenharmony_ci		pdata->charge_voltage = val;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "ti,input-current", &val);
3448c2ecf20Sopenharmony_ci	if (!ret)
3458c2ecf20Sopenharmony_ci		pdata->input_current = val;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	pdata->ext_control = of_property_read_bool(np, "ti,external-control");
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	return pdata;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic int bq24735_charger_probe(struct i2c_client *client,
3538c2ecf20Sopenharmony_ci				 const struct i2c_device_id *id)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	int ret;
3568c2ecf20Sopenharmony_ci	struct bq24735 *charger;
3578c2ecf20Sopenharmony_ci	struct power_supply_desc *supply_desc;
3588c2ecf20Sopenharmony_ci	struct power_supply_config psy_cfg = {};
3598c2ecf20Sopenharmony_ci	char *name;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	charger = devm_kzalloc(&client->dev, sizeof(*charger), GFP_KERNEL);
3628c2ecf20Sopenharmony_ci	if (!charger)
3638c2ecf20Sopenharmony_ci		return -ENOMEM;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	mutex_init(&charger->lock);
3668c2ecf20Sopenharmony_ci	charger->charging = true;
3678c2ecf20Sopenharmony_ci	charger->pdata = client->dev.platform_data;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_OF) && !charger->pdata && client->dev.of_node)
3708c2ecf20Sopenharmony_ci		charger->pdata = bq24735_parse_dt_data(client);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (!charger->pdata) {
3738c2ecf20Sopenharmony_ci		dev_err(&client->dev, "no platform data provided\n");
3748c2ecf20Sopenharmony_ci		return -EINVAL;
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	name = (char *)charger->pdata->name;
3788c2ecf20Sopenharmony_ci	if (!name) {
3798c2ecf20Sopenharmony_ci		name = devm_kasprintf(&client->dev, GFP_KERNEL,
3808c2ecf20Sopenharmony_ci				      "bq24735@%s",
3818c2ecf20Sopenharmony_ci				      dev_name(&client->dev));
3828c2ecf20Sopenharmony_ci		if (!name) {
3838c2ecf20Sopenharmony_ci			dev_err(&client->dev, "Failed to alloc device name\n");
3848c2ecf20Sopenharmony_ci			return -ENOMEM;
3858c2ecf20Sopenharmony_ci		}
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	charger->client = client;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	supply_desc = &charger->charger_desc;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	supply_desc->name = name;
3938c2ecf20Sopenharmony_ci	supply_desc->type = POWER_SUPPLY_TYPE_MAINS;
3948c2ecf20Sopenharmony_ci	supply_desc->properties = bq24735_charger_properties;
3958c2ecf20Sopenharmony_ci	supply_desc->num_properties = ARRAY_SIZE(bq24735_charger_properties);
3968c2ecf20Sopenharmony_ci	supply_desc->get_property = bq24735_charger_get_property;
3978c2ecf20Sopenharmony_ci	supply_desc->set_property = bq24735_charger_set_property;
3988c2ecf20Sopenharmony_ci	supply_desc->property_is_writeable =
3998c2ecf20Sopenharmony_ci				bq24735_charger_property_is_writeable;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	psy_cfg.supplied_to = charger->pdata->supplied_to;
4028c2ecf20Sopenharmony_ci	psy_cfg.num_supplicants = charger->pdata->num_supplicants;
4038c2ecf20Sopenharmony_ci	psy_cfg.of_node = client->dev.of_node;
4048c2ecf20Sopenharmony_ci	psy_cfg.drv_data = charger;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, charger);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	charger->status_gpio = devm_gpiod_get_optional(&client->dev,
4098c2ecf20Sopenharmony_ci						       "ti,ac-detect",
4108c2ecf20Sopenharmony_ci						       GPIOD_IN);
4118c2ecf20Sopenharmony_ci	if (IS_ERR(charger->status_gpio)) {
4128c2ecf20Sopenharmony_ci		ret = PTR_ERR(charger->status_gpio);
4138c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Getting gpio failed: %d\n", ret);
4148c2ecf20Sopenharmony_ci		return ret;
4158c2ecf20Sopenharmony_ci	}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	if (bq24735_charger_is_present(charger)) {
4188c2ecf20Sopenharmony_ci		ret = bq24735_read_word(client, BQ24735_MANUFACTURER_ID);
4198c2ecf20Sopenharmony_ci		if (ret < 0) {
4208c2ecf20Sopenharmony_ci			dev_err(&client->dev, "Failed to read manufacturer id : %d\n",
4218c2ecf20Sopenharmony_ci				ret);
4228c2ecf20Sopenharmony_ci			return ret;
4238c2ecf20Sopenharmony_ci		} else if (ret != 0x0040) {
4248c2ecf20Sopenharmony_ci			dev_err(&client->dev,
4258c2ecf20Sopenharmony_ci				"manufacturer id mismatch. 0x0040 != 0x%04x\n", ret);
4268c2ecf20Sopenharmony_ci			return -ENODEV;
4278c2ecf20Sopenharmony_ci		}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		ret = bq24735_read_word(client, BQ24735_DEVICE_ID);
4308c2ecf20Sopenharmony_ci		if (ret < 0) {
4318c2ecf20Sopenharmony_ci			dev_err(&client->dev, "Failed to read device id : %d\n", ret);
4328c2ecf20Sopenharmony_ci			return ret;
4338c2ecf20Sopenharmony_ci		} else if (ret != 0x000B) {
4348c2ecf20Sopenharmony_ci			dev_err(&client->dev,
4358c2ecf20Sopenharmony_ci				"device id mismatch. 0x000b != 0x%04x\n", ret);
4368c2ecf20Sopenharmony_ci			return -ENODEV;
4378c2ecf20Sopenharmony_ci		}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci		ret = bq24735_enable_charging(charger);
4408c2ecf20Sopenharmony_ci		if (ret < 0) {
4418c2ecf20Sopenharmony_ci			dev_err(&client->dev, "Failed to enable charging\n");
4428c2ecf20Sopenharmony_ci			return ret;
4438c2ecf20Sopenharmony_ci		}
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	charger->charger = devm_power_supply_register(&client->dev, supply_desc,
4478c2ecf20Sopenharmony_ci						      &psy_cfg);
4488c2ecf20Sopenharmony_ci	if (IS_ERR(charger->charger)) {
4498c2ecf20Sopenharmony_ci		ret = PTR_ERR(charger->charger);
4508c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Failed to register power supply: %d\n",
4518c2ecf20Sopenharmony_ci			ret);
4528c2ecf20Sopenharmony_ci		return ret;
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	if (client->irq) {
4568c2ecf20Sopenharmony_ci		ret = devm_request_threaded_irq(&client->dev, client->irq,
4578c2ecf20Sopenharmony_ci						NULL, bq24735_charger_isr,
4588c2ecf20Sopenharmony_ci						IRQF_TRIGGER_RISING |
4598c2ecf20Sopenharmony_ci						IRQF_TRIGGER_FALLING |
4608c2ecf20Sopenharmony_ci						IRQF_ONESHOT,
4618c2ecf20Sopenharmony_ci						supply_desc->name,
4628c2ecf20Sopenharmony_ci						charger->charger);
4638c2ecf20Sopenharmony_ci		if (ret) {
4648c2ecf20Sopenharmony_ci			dev_err(&client->dev,
4658c2ecf20Sopenharmony_ci				"Unable to register IRQ %d err %d\n",
4668c2ecf20Sopenharmony_ci				client->irq, ret);
4678c2ecf20Sopenharmony_ci			return ret;
4688c2ecf20Sopenharmony_ci		}
4698c2ecf20Sopenharmony_ci	} else {
4708c2ecf20Sopenharmony_ci		ret = device_property_read_u32(&client->dev, "poll-interval",
4718c2ecf20Sopenharmony_ci					       &charger->poll_interval);
4728c2ecf20Sopenharmony_ci		if (ret)
4738c2ecf20Sopenharmony_ci			return 0;
4748c2ecf20Sopenharmony_ci		if (!charger->poll_interval)
4758c2ecf20Sopenharmony_ci			return 0;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci		INIT_DELAYED_WORK(&charger->poll, bq24735_poll);
4788c2ecf20Sopenharmony_ci		schedule_delayed_work(&charger->poll,
4798c2ecf20Sopenharmony_ci				      msecs_to_jiffies(charger->poll_interval));
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	return 0;
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_cistatic int bq24735_charger_remove(struct i2c_client *client)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	struct bq24735 *charger = i2c_get_clientdata(client);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	if (charger->poll_interval)
4908c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&charger->poll);
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	return 0;
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic const struct i2c_device_id bq24735_charger_id[] = {
4968c2ecf20Sopenharmony_ci	{ "bq24735-charger", 0 },
4978c2ecf20Sopenharmony_ci	{}
4988c2ecf20Sopenharmony_ci};
4998c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, bq24735_charger_id);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cistatic const struct of_device_id bq24735_match_ids[] = {
5028c2ecf20Sopenharmony_ci	{ .compatible = "ti,bq24735", },
5038c2ecf20Sopenharmony_ci	{ /* end */ }
5048c2ecf20Sopenharmony_ci};
5058c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, bq24735_match_ids);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic struct i2c_driver bq24735_charger_driver = {
5088c2ecf20Sopenharmony_ci	.driver = {
5098c2ecf20Sopenharmony_ci		.name = "bq24735-charger",
5108c2ecf20Sopenharmony_ci		.of_match_table = bq24735_match_ids,
5118c2ecf20Sopenharmony_ci	},
5128c2ecf20Sopenharmony_ci	.probe = bq24735_charger_probe,
5138c2ecf20Sopenharmony_ci	.remove = bq24735_charger_remove,
5148c2ecf20Sopenharmony_ci	.id_table = bq24735_charger_id,
5158c2ecf20Sopenharmony_ci};
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cimodule_i2c_driver(bq24735_charger_driver);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("bq24735 battery charging driver");
5208c2ecf20Sopenharmony_ciMODULE_AUTHOR("Darbha Sriharsha <dsriharsha@nvidia.com>");
5218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
522