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