cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

bq24735-charger.c (12698B)


      1/*
      2 * Battery charger driver for TI BQ24735
      3 *
      4 * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
      5 *
      6 * This program is free software; you can redistribute it and/or modify
      7 * it under the terms of the GNU General Public License as published by
      8 * the Free Software Foundation;
      9 *
     10 * This program is distributed in the hope that it will be useful, but WITHOUT
     11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
     13 * more details.
     14 *
     15 * You should have received a copy of the GNU General Public License along
     16 * 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		0x12
     36#define BQ24735_CHARGE_CURRENT		0x14
     37#define BQ24735_CHARGE_CURRENT_MASK	0x1fc0
     38#define BQ24735_CHARGE_VOLTAGE		0x15
     39#define BQ24735_CHARGE_VOLTAGE_MASK	0x7ff0
     40#define BQ24735_INPUT_CURRENT		0x3f
     41#define BQ24735_INPUT_CURRENT_MASK	0x1f80
     42#define BQ24735_MANUFACTURER_ID		0xfe
     43#define BQ24735_DEVICE_ID		0xff
     44
     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	else
    228		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				 const struct i2c_device_id *id)
    357{
    358	int ret;
    359	struct bq24735 *charger;
    360	struct power_supply_desc *supply_desc;
    361	struct power_supply_config psy_cfg = {};
    362	char *name;
    363
    364	charger = devm_kzalloc(&client->dev, sizeof(*charger), GFP_KERNEL);
    365	if (!charger)
    366		return -ENOMEM;
    367
    368	mutex_init(&charger->lock);
    369	charger->charging = true;
    370	charger->pdata = client->dev.platform_data;
    371
    372	if (IS_ENABLED(CONFIG_OF) && !charger->pdata && client->dev.of_node)
    373		charger->pdata = bq24735_parse_dt_data(client);
    374
    375	if (!charger->pdata) {
    376		dev_err(&client->dev, "no platform data provided\n");
    377		return -EINVAL;
    378	}
    379
    380	name = (char *)charger->pdata->name;
    381	if (!name) {
    382		name = devm_kasprintf(&client->dev, GFP_KERNEL,
    383				      "bq24735@%s",
    384				      dev_name(&client->dev));
    385		if (!name) {
    386			dev_err(&client->dev, "Failed to alloc device name\n");
    387			return -ENOMEM;
    388		}
    389	}
    390
    391	charger->client = client;
    392
    393	supply_desc = &charger->charger_desc;
    394
    395	supply_desc->name = name;
    396	supply_desc->type = POWER_SUPPLY_TYPE_MAINS;
    397	supply_desc->properties = bq24735_charger_properties;
    398	supply_desc->num_properties = ARRAY_SIZE(bq24735_charger_properties);
    399	supply_desc->get_property = bq24735_charger_get_property;
    400	supply_desc->set_property = bq24735_charger_set_property;
    401	supply_desc->property_is_writeable =
    402				bq24735_charger_property_is_writeable;
    403
    404	psy_cfg.supplied_to = charger->pdata->supplied_to;
    405	psy_cfg.num_supplicants = charger->pdata->num_supplicants;
    406	psy_cfg.of_node = client->dev.of_node;
    407	psy_cfg.drv_data = charger;
    408
    409	i2c_set_clientdata(client, charger);
    410
    411	charger->status_gpio = devm_gpiod_get_optional(&client->dev,
    412						       "ti,ac-detect",
    413						       GPIOD_IN);
    414	if (IS_ERR(charger->status_gpio)) {
    415		ret = PTR_ERR(charger->status_gpio);
    416		dev_err(&client->dev, "Getting gpio failed: %d\n", ret);
    417		return ret;
    418	}
    419
    420	if (bq24735_charger_is_present(charger)) {
    421		ret = bq24735_read_word(client, BQ24735_MANUFACTURER_ID);
    422		if (ret < 0) {
    423			dev_err(&client->dev, "Failed to read manufacturer id : %d\n",
    424				ret);
    425			return ret;
    426		} else if (ret != 0x0040) {
    427			dev_err(&client->dev,
    428				"manufacturer id mismatch. 0x0040 != 0x%04x\n", ret);
    429			return -ENODEV;
    430		}
    431
    432		ret = bq24735_read_word(client, BQ24735_DEVICE_ID);
    433		if (ret < 0) {
    434			dev_err(&client->dev, "Failed to read device id : %d\n", ret);
    435			return ret;
    436		} else if (ret != 0x000B) {
    437			dev_err(&client->dev,
    438				"device id mismatch. 0x000b != 0x%04x\n", ret);
    439			return -ENODEV;
    440		}
    441
    442		ret = bq24735_enable_charging(charger);
    443		if (ret < 0) {
    444			dev_err(&client->dev, "Failed to enable charging\n");
    445			return ret;
    446		}
    447	}
    448
    449	charger->charger = devm_power_supply_register(&client->dev, supply_desc,
    450						      &psy_cfg);
    451	if (IS_ERR(charger->charger)) {
    452		ret = PTR_ERR(charger->charger);
    453		dev_err(&client->dev, "Failed to register power supply: %d\n",
    454			ret);
    455		return ret;
    456	}
    457
    458	if (client->irq) {
    459		ret = devm_request_threaded_irq(&client->dev, client->irq,
    460						NULL, bq24735_charger_isr,
    461						IRQF_TRIGGER_RISING |
    462						IRQF_TRIGGER_FALLING |
    463						IRQF_ONESHOT,
    464						supply_desc->name,
    465						charger->charger);
    466		if (ret) {
    467			dev_err(&client->dev,
    468				"Unable to register IRQ %d err %d\n",
    469				client->irq, ret);
    470			return ret;
    471		}
    472	} else {
    473		ret = device_property_read_u32(&client->dev, "poll-interval",
    474					       &charger->poll_interval);
    475		if (ret)
    476			return 0;
    477		if (!charger->poll_interval)
    478			return 0;
    479
    480		ret = devm_delayed_work_autocancel(&client->dev, &charger->poll,
    481						   bq24735_poll);
    482		if (ret)
    483			return ret;
    484
    485		schedule_delayed_work(&charger->poll,
    486				      msecs_to_jiffies(charger->poll_interval));
    487	}
    488
    489	return 0;
    490}
    491
    492static const struct i2c_device_id bq24735_charger_id[] = {
    493	{ "bq24735-charger", 0 },
    494	{}
    495};
    496MODULE_DEVICE_TABLE(i2c, bq24735_charger_id);
    497
    498static const struct of_device_id bq24735_match_ids[] = {
    499	{ .compatible = "ti,bq24735", },
    500	{ /* end */ }
    501};
    502MODULE_DEVICE_TABLE(of, bq24735_match_ids);
    503
    504static struct i2c_driver bq24735_charger_driver = {
    505	.driver = {
    506		.name = "bq24735-charger",
    507		.of_match_table = bq24735_match_ids,
    508	},
    509	.probe = bq24735_charger_probe,
    510	.id_table = bq24735_charger_id,
    511};
    512
    513module_i2c_driver(bq24735_charger_driver);
    514
    515MODULE_DESCRIPTION("bq24735 battery charging driver");
    516MODULE_AUTHOR("Darbha Sriharsha <dsriharsha@nvidia.com>");
    517MODULE_LICENSE("GPL v2");