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 = ®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