brintos

brintos / linux-shallow public Read only

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