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

bq27xxx_battery_i2c.c (7031B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * BQ27xxx battery monitor I2C driver
      4 *
      5 * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/
      6 *	Andrew F. Davis <afd@ti.com>
      7 */
      8
      9#include <linux/i2c.h>
     10#include <linux/interrupt.h>
     11#include <linux/module.h>
     12#include <asm/unaligned.h>
     13
     14#include <linux/power/bq27xxx_battery.h>
     15
     16static DEFINE_IDR(battery_id);
     17static DEFINE_MUTEX(battery_mutex);
     18
     19static irqreturn_t bq27xxx_battery_irq_handler_thread(int irq, void *data)
     20{
     21	struct bq27xxx_device_info *di = data;
     22
     23	bq27xxx_battery_update(di);
     24
     25	return IRQ_HANDLED;
     26}
     27
     28static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg,
     29				    bool single)
     30{
     31	struct i2c_client *client = to_i2c_client(di->dev);
     32	struct i2c_msg msg[2];
     33	u8 data[2];
     34	int ret;
     35
     36	if (!client->adapter)
     37		return -ENODEV;
     38
     39	msg[0].addr = client->addr;
     40	msg[0].flags = 0;
     41	msg[0].buf = &reg;
     42	msg[0].len = sizeof(reg);
     43	msg[1].addr = client->addr;
     44	msg[1].flags = I2C_M_RD;
     45	msg[1].buf = data;
     46	if (single)
     47		msg[1].len = 1;
     48	else
     49		msg[1].len = 2;
     50
     51	ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
     52	if (ret < 0)
     53		return ret;
     54
     55	if (!single)
     56		ret = get_unaligned_le16(data);
     57	else
     58		ret = data[0];
     59
     60	return ret;
     61}
     62
     63static int bq27xxx_battery_i2c_write(struct bq27xxx_device_info *di, u8 reg,
     64				     int value, bool single)
     65{
     66	struct i2c_client *client = to_i2c_client(di->dev);
     67	struct i2c_msg msg;
     68	u8 data[4];
     69	int ret;
     70
     71	if (!client->adapter)
     72		return -ENODEV;
     73
     74	data[0] = reg;
     75	if (single) {
     76		data[1] = (u8) value;
     77		msg.len = 2;
     78	} else {
     79		put_unaligned_le16(value, &data[1]);
     80		msg.len = 3;
     81	}
     82
     83	msg.buf = data;
     84	msg.addr = client->addr;
     85	msg.flags = 0;
     86
     87	ret = i2c_transfer(client->adapter, &msg, 1);
     88	if (ret < 0)
     89		return ret;
     90	if (ret != 1)
     91		return -EINVAL;
     92	return 0;
     93}
     94
     95static int bq27xxx_battery_i2c_bulk_read(struct bq27xxx_device_info *di, u8 reg,
     96					 u8 *data, int len)
     97{
     98	struct i2c_client *client = to_i2c_client(di->dev);
     99	int ret;
    100
    101	if (!client->adapter)
    102		return -ENODEV;
    103
    104	ret = i2c_smbus_read_i2c_block_data(client, reg, len, data);
    105	if (ret < 0)
    106		return ret;
    107	if (ret != len)
    108		return -EINVAL;
    109	return 0;
    110}
    111
    112static int bq27xxx_battery_i2c_bulk_write(struct bq27xxx_device_info *di,
    113					  u8 reg, u8 *data, int len)
    114{
    115	struct i2c_client *client = to_i2c_client(di->dev);
    116	struct i2c_msg msg;
    117	u8 buf[33];
    118	int ret;
    119
    120	if (!client->adapter)
    121		return -ENODEV;
    122
    123	buf[0] = reg;
    124	memcpy(&buf[1], data, len);
    125
    126	msg.buf = buf;
    127	msg.addr = client->addr;
    128	msg.flags = 0;
    129	msg.len = len + 1;
    130
    131	ret = i2c_transfer(client->adapter, &msg, 1);
    132	if (ret < 0)
    133		return ret;
    134	if (ret != 1)
    135		return -EINVAL;
    136	return 0;
    137}
    138
    139static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
    140				     const struct i2c_device_id *id)
    141{
    142	struct bq27xxx_device_info *di;
    143	int ret;
    144	char *name;
    145	int num;
    146
    147	/* Get new ID for the new battery device */
    148	mutex_lock(&battery_mutex);
    149	num = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL);
    150	mutex_unlock(&battery_mutex);
    151	if (num < 0)
    152		return num;
    153
    154	name = devm_kasprintf(&client->dev, GFP_KERNEL, "%s-%d", id->name, num);
    155	if (!name)
    156		goto err_mem;
    157
    158	di = devm_kzalloc(&client->dev, sizeof(*di), GFP_KERNEL);
    159	if (!di)
    160		goto err_mem;
    161
    162	di->id = num;
    163	di->dev = &client->dev;
    164	di->chip = id->driver_data;
    165	di->name = name;
    166
    167	di->bus.read = bq27xxx_battery_i2c_read;
    168	di->bus.write = bq27xxx_battery_i2c_write;
    169	di->bus.read_bulk = bq27xxx_battery_i2c_bulk_read;
    170	di->bus.write_bulk = bq27xxx_battery_i2c_bulk_write;
    171
    172	ret = bq27xxx_battery_setup(di);
    173	if (ret)
    174		goto err_failed;
    175
    176	/* Schedule a polling after about 1 min */
    177	schedule_delayed_work(&di->work, 60 * HZ);
    178
    179	i2c_set_clientdata(client, di);
    180
    181	if (client->irq) {
    182		ret = devm_request_threaded_irq(&client->dev, client->irq,
    183				NULL, bq27xxx_battery_irq_handler_thread,
    184				IRQF_ONESHOT,
    185				di->name, di);
    186		if (ret) {
    187			dev_err(&client->dev,
    188				"Unable to register IRQ %d error %d\n",
    189				client->irq, ret);
    190			bq27xxx_battery_teardown(di);
    191			goto err_failed;
    192		}
    193	}
    194
    195	return 0;
    196
    197err_mem:
    198	ret = -ENOMEM;
    199
    200err_failed:
    201	mutex_lock(&battery_mutex);
    202	idr_remove(&battery_id, num);
    203	mutex_unlock(&battery_mutex);
    204
    205	return ret;
    206}
    207
    208static int bq27xxx_battery_i2c_remove(struct i2c_client *client)
    209{
    210	struct bq27xxx_device_info *di = i2c_get_clientdata(client);
    211
    212	bq27xxx_battery_teardown(di);
    213
    214	mutex_lock(&battery_mutex);
    215	idr_remove(&battery_id, di->id);
    216	mutex_unlock(&battery_mutex);
    217
    218	return 0;
    219}
    220
    221static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
    222	{ "bq27200", BQ27000 },
    223	{ "bq27210", BQ27010 },
    224	{ "bq27500", BQ2750X },
    225	{ "bq27510", BQ2751X },
    226	{ "bq27520", BQ2752X },
    227	{ "bq27500-1", BQ27500 },
    228	{ "bq27510g1", BQ27510G1 },
    229	{ "bq27510g2", BQ27510G2 },
    230	{ "bq27510g3", BQ27510G3 },
    231	{ "bq27520g1", BQ27520G1 },
    232	{ "bq27520g2", BQ27520G2 },
    233	{ "bq27520g3", BQ27520G3 },
    234	{ "bq27520g4", BQ27520G4 },
    235	{ "bq27521", BQ27521 },
    236	{ "bq27530", BQ27530 },
    237	{ "bq27531", BQ27531 },
    238	{ "bq27541", BQ27541 },
    239	{ "bq27542", BQ27542 },
    240	{ "bq27546", BQ27546 },
    241	{ "bq27742", BQ27742 },
    242	{ "bq27545", BQ27545 },
    243	{ "bq27411", BQ27411 },
    244	{ "bq27421", BQ27421 },
    245	{ "bq27425", BQ27425 },
    246	{ "bq27426", BQ27426 },
    247	{ "bq27441", BQ27441 },
    248	{ "bq27621", BQ27621 },
    249	{ "bq27z561", BQ27Z561 },
    250	{ "bq28z610", BQ28Z610 },
    251	{ "bq34z100", BQ34Z100 },
    252	{ "bq78z100", BQ78Z100 },
    253	{},
    254};
    255MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table);
    256
    257#ifdef CONFIG_OF
    258static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = {
    259	{ .compatible = "ti,bq27200" },
    260	{ .compatible = "ti,bq27210" },
    261	{ .compatible = "ti,bq27500" },
    262	{ .compatible = "ti,bq27510" },
    263	{ .compatible = "ti,bq27520" },
    264	{ .compatible = "ti,bq27500-1" },
    265	{ .compatible = "ti,bq27510g1" },
    266	{ .compatible = "ti,bq27510g2" },
    267	{ .compatible = "ti,bq27510g3" },
    268	{ .compatible = "ti,bq27520g1" },
    269	{ .compatible = "ti,bq27520g2" },
    270	{ .compatible = "ti,bq27520g3" },
    271	{ .compatible = "ti,bq27520g4" },
    272	{ .compatible = "ti,bq27521" },
    273	{ .compatible = "ti,bq27530" },
    274	{ .compatible = "ti,bq27531" },
    275	{ .compatible = "ti,bq27541" },
    276	{ .compatible = "ti,bq27542" },
    277	{ .compatible = "ti,bq27546" },
    278	{ .compatible = "ti,bq27742" },
    279	{ .compatible = "ti,bq27545" },
    280	{ .compatible = "ti,bq27411" },
    281	{ .compatible = "ti,bq27421" },
    282	{ .compatible = "ti,bq27425" },
    283	{ .compatible = "ti,bq27426" },
    284	{ .compatible = "ti,bq27441" },
    285	{ .compatible = "ti,bq27621" },
    286	{ .compatible = "ti,bq27z561" },
    287	{ .compatible = "ti,bq28z610" },
    288	{ .compatible = "ti,bq34z100" },
    289	{ .compatible = "ti,bq78z100" },
    290	{},
    291};
    292MODULE_DEVICE_TABLE(of, bq27xxx_battery_i2c_of_match_table);
    293#endif
    294
    295static struct i2c_driver bq27xxx_battery_i2c_driver = {
    296	.driver = {
    297		.name = "bq27xxx-battery",
    298		.of_match_table = of_match_ptr(bq27xxx_battery_i2c_of_match_table),
    299	},
    300	.probe = bq27xxx_battery_i2c_probe,
    301	.remove = bq27xxx_battery_i2c_remove,
    302	.id_table = bq27xxx_i2c_id_table,
    303};
    304module_i2c_driver(bq27xxx_battery_i2c_driver);
    305
    306MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
    307MODULE_DESCRIPTION("BQ27xxx battery monitor i2c driver");
    308MODULE_LICENSE("GPL");