brintos

brintos / linux-shallow public Read only

0
0
Text · 12.4 KiB · 73a7fc8 Raw
517 lines · c
1/*2 * Battery charger driver for TI BQ247353 *4 * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.5 *6 * This program is free software; you can redistribute it and/or modify7 * it under the terms of the GNU General Public License as published by8 * the Free Software Foundation;9 *10 * This program is distributed in the hope that it will be useful, but WITHOUT11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for13 * more details.14 *15 * You should have received a copy of the GNU General Public License along16 * with this program; if not, write to the Free Software Foundation, Inc.,17 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.18 */19 20#include <linux/devm-helpers.h>21#include <linux/err.h>22#include <linux/i2c.h>23#include <linux/init.h>24#include <linux/interrupt.h>25#include <linux/kernel.h>26#include <linux/module.h>27#include <linux/of.h>28#include <linux/gpio/consumer.h>29#include <linux/power_supply.h>30#include <linux/slab.h>31 32#include <linux/power/bq24735-charger.h>33 34/* BQ24735 available commands and their respective masks */35#define BQ24735_CHARGE_OPT		0x1236#define BQ24735_CHARGE_CURRENT		0x1437#define BQ24735_CHARGE_CURRENT_MASK	0x1fc038#define BQ24735_CHARGE_VOLTAGE		0x1539#define BQ24735_CHARGE_VOLTAGE_MASK	0x7ff040#define BQ24735_INPUT_CURRENT		0x3f41#define BQ24735_INPUT_CURRENT_MASK	0x1f8042#define BQ24735_MANUFACTURER_ID		0xfe43#define BQ24735_DEVICE_ID		0xff44 45/* ChargeOptions bits of interest */46#define BQ24735_CHARGE_OPT_CHG_DISABLE	(1 << 0)47#define BQ24735_CHARGE_OPT_AC_PRESENT	(1 << 4)48 49struct bq24735 {50	struct power_supply		*charger;51	struct power_supply_desc	charger_desc;52	struct i2c_client		*client;53	struct bq24735_platform		*pdata;54	struct mutex			lock;55	struct gpio_desc		*status_gpio;56	struct delayed_work		poll;57	u32				poll_interval;58	bool				charging;59};60 61static inline struct bq24735 *to_bq24735(struct power_supply *psy)62{63	return power_supply_get_drvdata(psy);64}65 66static enum power_supply_property bq24735_charger_properties[] = {67	POWER_SUPPLY_PROP_STATUS,68	POWER_SUPPLY_PROP_ONLINE,69};70 71static int bq24735_charger_property_is_writeable(struct power_supply *psy,72						 enum power_supply_property psp)73{74	switch (psp) {75	case POWER_SUPPLY_PROP_STATUS:76		return 1;77	default:78		break;79	}80 81	return 0;82}83 84static inline int bq24735_write_word(struct i2c_client *client, u8 reg,85				     u16 value)86{87	return i2c_smbus_write_word_data(client, reg, value);88}89 90static inline int bq24735_read_word(struct i2c_client *client, u8 reg)91{92	return i2c_smbus_read_word_data(client, reg);93}94 95static int bq24735_update_word(struct i2c_client *client, u8 reg,96			       u16 mask, u16 value)97{98	unsigned int tmp;99	int ret;100 101	ret = bq24735_read_word(client, reg);102	if (ret < 0)103		return ret;104 105	tmp = ret & ~mask;106	tmp |= value & mask;107 108	return bq24735_write_word(client, reg, tmp);109}110 111static int bq24735_config_charger(struct bq24735 *charger)112{113	struct bq24735_platform *pdata = charger->pdata;114	int ret;115	u16 value;116 117	if (pdata->ext_control)118		return 0;119 120	if (pdata->charge_current) {121		value = pdata->charge_current & BQ24735_CHARGE_CURRENT_MASK;122 123		ret = bq24735_write_word(charger->client,124					 BQ24735_CHARGE_CURRENT, value);125		if (ret < 0) {126			dev_err(&charger->client->dev,127				"Failed to write charger current : %d\n",128				ret);129			return ret;130		}131	}132 133	if (pdata->charge_voltage) {134		value = pdata->charge_voltage & BQ24735_CHARGE_VOLTAGE_MASK;135 136		ret = bq24735_write_word(charger->client,137					 BQ24735_CHARGE_VOLTAGE, value);138		if (ret < 0) {139			dev_err(&charger->client->dev,140				"Failed to write charger voltage : %d\n",141				ret);142			return ret;143		}144	}145 146	if (pdata->input_current) {147		value = pdata->input_current & BQ24735_INPUT_CURRENT_MASK;148 149		ret = bq24735_write_word(charger->client,150					 BQ24735_INPUT_CURRENT, value);151		if (ret < 0) {152			dev_err(&charger->client->dev,153				"Failed to write input current : %d\n",154				ret);155			return ret;156		}157	}158 159	return 0;160}161 162static inline int bq24735_enable_charging(struct bq24735 *charger)163{164	int ret;165 166	if (charger->pdata->ext_control)167		return 0;168 169	ret = bq24735_config_charger(charger);170	if (ret)171		return ret;172 173	return bq24735_update_word(charger->client, BQ24735_CHARGE_OPT,174				   BQ24735_CHARGE_OPT_CHG_DISABLE, 0);175}176 177static inline int bq24735_disable_charging(struct bq24735 *charger)178{179	if (charger->pdata->ext_control)180		return 0;181 182	return bq24735_update_word(charger->client, BQ24735_CHARGE_OPT,183				   BQ24735_CHARGE_OPT_CHG_DISABLE,184				   BQ24735_CHARGE_OPT_CHG_DISABLE);185}186 187static bool bq24735_charger_is_present(struct bq24735 *charger)188{189	if (charger->status_gpio) {190		return !gpiod_get_value_cansleep(charger->status_gpio);191	} else {192		int ac = 0;193 194		ac = bq24735_read_word(charger->client, BQ24735_CHARGE_OPT);195		if (ac < 0) {196			dev_dbg(&charger->client->dev,197				"Failed to read charger options : %d\n",198				ac);199			return false;200		}201		return (ac & BQ24735_CHARGE_OPT_AC_PRESENT) ? true : false;202	}203 204	return false;205}206 207static int bq24735_charger_is_charging(struct bq24735 *charger)208{209	int ret;210 211	if (!bq24735_charger_is_present(charger))212		return 0;213 214	ret  = bq24735_read_word(charger->client, BQ24735_CHARGE_OPT);215	if (ret < 0)216		return ret;217 218	return !(ret & BQ24735_CHARGE_OPT_CHG_DISABLE);219}220 221static void bq24735_update(struct bq24735 *charger)222{223	mutex_lock(&charger->lock);224 225	if (charger->charging && bq24735_charger_is_present(charger))226		bq24735_enable_charging(charger);227	else228		bq24735_disable_charging(charger);229 230	mutex_unlock(&charger->lock);231 232	power_supply_changed(charger->charger);233}234 235static irqreturn_t bq24735_charger_isr(int irq, void *devid)236{237	struct power_supply *psy = devid;238	struct bq24735 *charger = to_bq24735(psy);239 240	bq24735_update(charger);241 242	return IRQ_HANDLED;243}244 245static void bq24735_poll(struct work_struct *work)246{247	struct bq24735 *charger = container_of(work, struct bq24735, poll.work);248 249	bq24735_update(charger);250 251	schedule_delayed_work(&charger->poll,252			      msecs_to_jiffies(charger->poll_interval));253}254 255static int bq24735_charger_get_property(struct power_supply *psy,256					enum power_supply_property psp,257					union power_supply_propval *val)258{259	struct bq24735 *charger = to_bq24735(psy);260 261	switch (psp) {262	case POWER_SUPPLY_PROP_ONLINE:263		val->intval = bq24735_charger_is_present(charger) ? 1 : 0;264		break;265	case POWER_SUPPLY_PROP_STATUS:266		switch (bq24735_charger_is_charging(charger)) {267		case 1:268			val->intval = POWER_SUPPLY_STATUS_CHARGING;269			break;270		case 0:271			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;272			break;273		default:274			val->intval = POWER_SUPPLY_STATUS_UNKNOWN;275			break;276		}277		break;278	default:279		return -EINVAL;280	}281 282	return 0;283}284 285static int bq24735_charger_set_property(struct power_supply *psy,286					enum power_supply_property psp,287					const union power_supply_propval *val)288{289	struct bq24735 *charger = to_bq24735(psy);290	int ret;291 292	switch (psp) {293	case POWER_SUPPLY_PROP_STATUS:294		switch (val->intval) {295		case POWER_SUPPLY_STATUS_CHARGING:296			mutex_lock(&charger->lock);297			charger->charging = true;298			ret = bq24735_enable_charging(charger);299			mutex_unlock(&charger->lock);300			if (ret)301				return ret;302			break;303		case POWER_SUPPLY_STATUS_DISCHARGING:304		case POWER_SUPPLY_STATUS_NOT_CHARGING:305			mutex_lock(&charger->lock);306			charger->charging = false;307			ret = bq24735_disable_charging(charger);308			mutex_unlock(&charger->lock);309			if (ret)310				return ret;311			break;312		default:313			return -EINVAL;314		}315		power_supply_changed(psy);316		break;317	default:318		return -EPERM;319	}320 321	return 0;322}323 324static struct bq24735_platform *bq24735_parse_dt_data(struct i2c_client *client)325{326	struct bq24735_platform *pdata;327	struct device_node *np = client->dev.of_node;328	u32 val;329	int ret;330 331	pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);332	if (!pdata) {333		dev_err(&client->dev,334			"Memory alloc for bq24735 pdata failed\n");335		return NULL;336	}337 338	ret = of_property_read_u32(np, "ti,charge-current", &val);339	if (!ret)340		pdata->charge_current = val;341 342	ret = of_property_read_u32(np, "ti,charge-voltage", &val);343	if (!ret)344		pdata->charge_voltage = val;345 346	ret = of_property_read_u32(np, "ti,input-current", &val);347	if (!ret)348		pdata->input_current = val;349 350	pdata->ext_control = of_property_read_bool(np, "ti,external-control");351 352	return pdata;353}354 355static int bq24735_charger_probe(struct i2c_client *client)356{357	int ret;358	struct bq24735 *charger;359	struct power_supply_desc *supply_desc;360	struct power_supply_config psy_cfg = {};361	char *name;362 363	charger = devm_kzalloc(&client->dev, sizeof(*charger), GFP_KERNEL);364	if (!charger)365		return -ENOMEM;366 367	mutex_init(&charger->lock);368	charger->charging = true;369	charger->pdata = client->dev.platform_data;370 371	if (IS_ENABLED(CONFIG_OF) && !charger->pdata && client->dev.of_node)372		charger->pdata = bq24735_parse_dt_data(client);373 374	if (!charger->pdata) {375		dev_err(&client->dev, "no platform data provided\n");376		return -EINVAL;377	}378 379	name = (char *)charger->pdata->name;380	if (!name) {381		name = devm_kasprintf(&client->dev, GFP_KERNEL,382				      "bq24735@%s",383				      dev_name(&client->dev));384		if (!name) {385			dev_err(&client->dev, "Failed to alloc device name\n");386			return -ENOMEM;387		}388	}389 390	charger->client = client;391 392	supply_desc = &charger->charger_desc;393 394	supply_desc->name = name;395	supply_desc->type = POWER_SUPPLY_TYPE_MAINS;396	supply_desc->properties = bq24735_charger_properties;397	supply_desc->num_properties = ARRAY_SIZE(bq24735_charger_properties);398	supply_desc->get_property = bq24735_charger_get_property;399	supply_desc->set_property = bq24735_charger_set_property;400	supply_desc->property_is_writeable =401				bq24735_charger_property_is_writeable;402 403	psy_cfg.supplied_to = charger->pdata->supplied_to;404	psy_cfg.num_supplicants = charger->pdata->num_supplicants;405	psy_cfg.of_node = client->dev.of_node;406	psy_cfg.drv_data = charger;407 408	i2c_set_clientdata(client, charger);409 410	charger->status_gpio = devm_gpiod_get_optional(&client->dev,411						       "ti,ac-detect",412						       GPIOD_IN);413	if (IS_ERR(charger->status_gpio)) {414		ret = PTR_ERR(charger->status_gpio);415		dev_err(&client->dev, "Getting gpio failed: %d\n", ret);416		return ret;417	}418 419	if (bq24735_charger_is_present(charger)) {420		ret = bq24735_read_word(client, BQ24735_MANUFACTURER_ID);421		if (ret < 0) {422			dev_err(&client->dev, "Failed to read manufacturer id : %d\n",423				ret);424			return ret;425		} else if (ret != 0x0040) {426			dev_err(&client->dev,427				"manufacturer id mismatch. 0x0040 != 0x%04x\n", ret);428			return -ENODEV;429		}430 431		ret = bq24735_read_word(client, BQ24735_DEVICE_ID);432		if (ret < 0) {433			dev_err(&client->dev, "Failed to read device id : %d\n", ret);434			return ret;435		} else if (ret != 0x000B) {436			dev_err(&client->dev,437				"device id mismatch. 0x000b != 0x%04x\n", ret);438			return -ENODEV;439		}440 441		ret = bq24735_enable_charging(charger);442		if (ret < 0) {443			dev_err(&client->dev, "Failed to enable charging\n");444			return ret;445		}446	}447 448	charger->charger = devm_power_supply_register(&client->dev, supply_desc,449						      &psy_cfg);450	if (IS_ERR(charger->charger)) {451		ret = PTR_ERR(charger->charger);452		dev_err(&client->dev, "Failed to register power supply: %d\n",453			ret);454		return ret;455	}456 457	if (client->irq) {458		ret = devm_request_threaded_irq(&client->dev, client->irq,459						NULL, bq24735_charger_isr,460						IRQF_TRIGGER_RISING |461						IRQF_TRIGGER_FALLING |462						IRQF_ONESHOT,463						supply_desc->name,464						charger->charger);465		if (ret) {466			dev_err(&client->dev,467				"Unable to register IRQ %d err %d\n",468				client->irq, ret);469			return ret;470		}471	} else {472		ret = device_property_read_u32(&client->dev, "poll-interval",473					       &charger->poll_interval);474		if (ret)475			return 0;476		if (!charger->poll_interval)477			return 0;478 479		ret = devm_delayed_work_autocancel(&client->dev, &charger->poll,480						   bq24735_poll);481		if (ret)482			return ret;483 484		schedule_delayed_work(&charger->poll,485				      msecs_to_jiffies(charger->poll_interval));486	}487 488	return 0;489}490 491static const struct i2c_device_id bq24735_charger_id[] = {492	{ "bq24735-charger" },493	{}494};495MODULE_DEVICE_TABLE(i2c, bq24735_charger_id);496 497static const struct of_device_id bq24735_match_ids[] = {498	{ .compatible = "ti,bq24735", },499	{ /* end */ }500};501MODULE_DEVICE_TABLE(of, bq24735_match_ids);502 503static struct i2c_driver bq24735_charger_driver = {504	.driver = {505		.name = "bq24735-charger",506		.of_match_table = bq24735_match_ids,507	},508	.probe = bq24735_charger_probe,509	.id_table = bq24735_charger_id,510};511 512module_i2c_driver(bq24735_charger_driver);513 514MODULE_DESCRIPTION("bq24735 battery charging driver");515MODULE_AUTHOR("Darbha Sriharsha <dsriharsha@nvidia.com>");516MODULE_LICENSE("GPL v2");517