18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * BQ27xxx battery monitor I2C driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ 68c2ecf20Sopenharmony_ci * Andrew F. Davis <afd@ti.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/i2c.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/power/bq27xxx_battery.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic DEFINE_IDR(battery_id); 178c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(battery_mutex); 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic irqreturn_t bq27xxx_battery_irq_handler_thread(int irq, void *data) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci struct bq27xxx_device_info *di = data; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci bq27xxx_battery_update(di); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci return IRQ_HANDLED; 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg, 298c2ecf20Sopenharmony_ci bool single) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(di->dev); 328c2ecf20Sopenharmony_ci struct i2c_msg msg[2]; 338c2ecf20Sopenharmony_ci u8 data[2]; 348c2ecf20Sopenharmony_ci int ret; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci if (!client->adapter) 378c2ecf20Sopenharmony_ci return -ENODEV; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci msg[0].addr = client->addr; 408c2ecf20Sopenharmony_ci msg[0].flags = 0; 418c2ecf20Sopenharmony_ci msg[0].buf = ® 428c2ecf20Sopenharmony_ci msg[0].len = sizeof(reg); 438c2ecf20Sopenharmony_ci msg[1].addr = client->addr; 448c2ecf20Sopenharmony_ci msg[1].flags = I2C_M_RD; 458c2ecf20Sopenharmony_ci msg[1].buf = data; 468c2ecf20Sopenharmony_ci if (single) 478c2ecf20Sopenharmony_ci msg[1].len = 1; 488c2ecf20Sopenharmony_ci else 498c2ecf20Sopenharmony_ci msg[1].len = 2; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); 528c2ecf20Sopenharmony_ci if (ret < 0) 538c2ecf20Sopenharmony_ci return ret; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (!single) 568c2ecf20Sopenharmony_ci ret = get_unaligned_le16(data); 578c2ecf20Sopenharmony_ci else 588c2ecf20Sopenharmony_ci ret = data[0]; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return ret; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int bq27xxx_battery_i2c_write(struct bq27xxx_device_info *di, u8 reg, 648c2ecf20Sopenharmony_ci int value, bool single) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(di->dev); 678c2ecf20Sopenharmony_ci struct i2c_msg msg; 688c2ecf20Sopenharmony_ci u8 data[4]; 698c2ecf20Sopenharmony_ci int ret; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (!client->adapter) 728c2ecf20Sopenharmony_ci return -ENODEV; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci data[0] = reg; 758c2ecf20Sopenharmony_ci if (single) { 768c2ecf20Sopenharmony_ci data[1] = (u8) value; 778c2ecf20Sopenharmony_ci msg.len = 2; 788c2ecf20Sopenharmony_ci } else { 798c2ecf20Sopenharmony_ci put_unaligned_le16(value, &data[1]); 808c2ecf20Sopenharmony_ci msg.len = 3; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci msg.buf = data; 848c2ecf20Sopenharmony_ci msg.addr = client->addr; 858c2ecf20Sopenharmony_ci msg.flags = 0; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci ret = i2c_transfer(client->adapter, &msg, 1); 888c2ecf20Sopenharmony_ci if (ret < 0) 898c2ecf20Sopenharmony_ci return ret; 908c2ecf20Sopenharmony_ci if (ret != 1) 918c2ecf20Sopenharmony_ci return -EINVAL; 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int bq27xxx_battery_i2c_bulk_read(struct bq27xxx_device_info *di, u8 reg, 968c2ecf20Sopenharmony_ci u8 *data, int len) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(di->dev); 998c2ecf20Sopenharmony_ci int ret; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (!client->adapter) 1028c2ecf20Sopenharmony_ci return -ENODEV; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci ret = i2c_smbus_read_i2c_block_data(client, reg, len, data); 1058c2ecf20Sopenharmony_ci if (ret < 0) 1068c2ecf20Sopenharmony_ci return ret; 1078c2ecf20Sopenharmony_ci if (ret != len) 1088c2ecf20Sopenharmony_ci return -EINVAL; 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int bq27xxx_battery_i2c_bulk_write(struct bq27xxx_device_info *di, 1138c2ecf20Sopenharmony_ci u8 reg, u8 *data, int len) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(di->dev); 1168c2ecf20Sopenharmony_ci struct i2c_msg msg; 1178c2ecf20Sopenharmony_ci u8 buf[33]; 1188c2ecf20Sopenharmony_ci int ret; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (!client->adapter) 1218c2ecf20Sopenharmony_ci return -ENODEV; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci buf[0] = reg; 1248c2ecf20Sopenharmony_ci memcpy(&buf[1], data, len); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci msg.buf = buf; 1278c2ecf20Sopenharmony_ci msg.addr = client->addr; 1288c2ecf20Sopenharmony_ci msg.flags = 0; 1298c2ecf20Sopenharmony_ci msg.len = len + 1; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci ret = i2c_transfer(client->adapter, &msg, 1); 1328c2ecf20Sopenharmony_ci if (ret < 0) 1338c2ecf20Sopenharmony_ci return ret; 1348c2ecf20Sopenharmony_ci if (ret != 1) 1358c2ecf20Sopenharmony_ci return -EINVAL; 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic int bq27xxx_battery_i2c_probe(struct i2c_client *client, 1408c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct bq27xxx_device_info *di; 1438c2ecf20Sopenharmony_ci int ret; 1448c2ecf20Sopenharmony_ci char *name; 1458c2ecf20Sopenharmony_ci int num; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* Get new ID for the new battery device */ 1488c2ecf20Sopenharmony_ci mutex_lock(&battery_mutex); 1498c2ecf20Sopenharmony_ci num = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL); 1508c2ecf20Sopenharmony_ci mutex_unlock(&battery_mutex); 1518c2ecf20Sopenharmony_ci if (num < 0) 1528c2ecf20Sopenharmony_ci return num; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci name = devm_kasprintf(&client->dev, GFP_KERNEL, "%s-%d", id->name, num); 1558c2ecf20Sopenharmony_ci if (!name) 1568c2ecf20Sopenharmony_ci goto err_mem; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci di = devm_kzalloc(&client->dev, sizeof(*di), GFP_KERNEL); 1598c2ecf20Sopenharmony_ci if (!di) 1608c2ecf20Sopenharmony_ci goto err_mem; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci di->id = num; 1638c2ecf20Sopenharmony_ci di->dev = &client->dev; 1648c2ecf20Sopenharmony_ci di->chip = id->driver_data; 1658c2ecf20Sopenharmony_ci di->name = name; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci di->bus.read = bq27xxx_battery_i2c_read; 1688c2ecf20Sopenharmony_ci di->bus.write = bq27xxx_battery_i2c_write; 1698c2ecf20Sopenharmony_ci di->bus.read_bulk = bq27xxx_battery_i2c_bulk_read; 1708c2ecf20Sopenharmony_ci di->bus.write_bulk = bq27xxx_battery_i2c_bulk_write; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci ret = bq27xxx_battery_setup(di); 1738c2ecf20Sopenharmony_ci if (ret) 1748c2ecf20Sopenharmony_ci goto err_failed; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* Schedule a polling after about 1 min */ 1778c2ecf20Sopenharmony_ci schedule_delayed_work(&di->work, 60 * HZ); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci i2c_set_clientdata(client, di); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (client->irq) { 1828c2ecf20Sopenharmony_ci ret = request_threaded_irq(client->irq, 1838c2ecf20Sopenharmony_ci NULL, bq27xxx_battery_irq_handler_thread, 1848c2ecf20Sopenharmony_ci IRQF_ONESHOT, 1858c2ecf20Sopenharmony_ci di->name, di); 1868c2ecf20Sopenharmony_ci if (ret) { 1878c2ecf20Sopenharmony_ci dev_err(&client->dev, 1888c2ecf20Sopenharmony_ci "Unable to register IRQ %d error %d\n", 1898c2ecf20Sopenharmony_ci client->irq, ret); 1908c2ecf20Sopenharmony_ci bq27xxx_battery_teardown(di); 1918c2ecf20Sopenharmony_ci goto err_failed; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cierr_mem: 1988c2ecf20Sopenharmony_ci ret = -ENOMEM; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cierr_failed: 2018c2ecf20Sopenharmony_ci mutex_lock(&battery_mutex); 2028c2ecf20Sopenharmony_ci idr_remove(&battery_id, num); 2038c2ecf20Sopenharmony_ci mutex_unlock(&battery_mutex); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return ret; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int bq27xxx_battery_i2c_remove(struct i2c_client *client) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct bq27xxx_device_info *di = i2c_get_clientdata(client); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci free_irq(client->irq, di); 2138c2ecf20Sopenharmony_ci bq27xxx_battery_teardown(di); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci mutex_lock(&battery_mutex); 2168c2ecf20Sopenharmony_ci idr_remove(&battery_id, di->id); 2178c2ecf20Sopenharmony_ci mutex_unlock(&battery_mutex); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic const struct i2c_device_id bq27xxx_i2c_id_table[] = { 2238c2ecf20Sopenharmony_ci { "bq27200", BQ27000 }, 2248c2ecf20Sopenharmony_ci { "bq27210", BQ27010 }, 2258c2ecf20Sopenharmony_ci { "bq27500", BQ2750X }, 2268c2ecf20Sopenharmony_ci { "bq27510", BQ2751X }, 2278c2ecf20Sopenharmony_ci { "bq27520", BQ2752X }, 2288c2ecf20Sopenharmony_ci { "bq27500-1", BQ27500 }, 2298c2ecf20Sopenharmony_ci { "bq27510g1", BQ27510G1 }, 2308c2ecf20Sopenharmony_ci { "bq27510g2", BQ27510G2 }, 2318c2ecf20Sopenharmony_ci { "bq27510g3", BQ27510G3 }, 2328c2ecf20Sopenharmony_ci { "bq27520g1", BQ27520G1 }, 2338c2ecf20Sopenharmony_ci { "bq27520g2", BQ27520G2 }, 2348c2ecf20Sopenharmony_ci { "bq27520g3", BQ27520G3 }, 2358c2ecf20Sopenharmony_ci { "bq27520g4", BQ27520G4 }, 2368c2ecf20Sopenharmony_ci { "bq27521", BQ27521 }, 2378c2ecf20Sopenharmony_ci { "bq27530", BQ27530 }, 2388c2ecf20Sopenharmony_ci { "bq27531", BQ27531 }, 2398c2ecf20Sopenharmony_ci { "bq27541", BQ27541 }, 2408c2ecf20Sopenharmony_ci { "bq27542", BQ27542 }, 2418c2ecf20Sopenharmony_ci { "bq27546", BQ27546 }, 2428c2ecf20Sopenharmony_ci { "bq27742", BQ27742 }, 2438c2ecf20Sopenharmony_ci { "bq27545", BQ27545 }, 2448c2ecf20Sopenharmony_ci { "bq27411", BQ27411 }, 2458c2ecf20Sopenharmony_ci { "bq27421", BQ27421 }, 2468c2ecf20Sopenharmony_ci { "bq27425", BQ27425 }, 2478c2ecf20Sopenharmony_ci { "bq27426", BQ27426 }, 2488c2ecf20Sopenharmony_ci { "bq27441", BQ27441 }, 2498c2ecf20Sopenharmony_ci { "bq27621", BQ27621 }, 2508c2ecf20Sopenharmony_ci { "bq27z561", BQ27Z561 }, 2518c2ecf20Sopenharmony_ci { "bq28z610", BQ28Z610 }, 2528c2ecf20Sopenharmony_ci { "bq34z100", BQ34Z100 }, 2538c2ecf20Sopenharmony_ci {}, 2548c2ecf20Sopenharmony_ci}; 2558c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 2588c2ecf20Sopenharmony_cistatic const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = { 2598c2ecf20Sopenharmony_ci { .compatible = "ti,bq27200" }, 2608c2ecf20Sopenharmony_ci { .compatible = "ti,bq27210" }, 2618c2ecf20Sopenharmony_ci { .compatible = "ti,bq27500" }, 2628c2ecf20Sopenharmony_ci { .compatible = "ti,bq27510" }, 2638c2ecf20Sopenharmony_ci { .compatible = "ti,bq27520" }, 2648c2ecf20Sopenharmony_ci { .compatible = "ti,bq27500-1" }, 2658c2ecf20Sopenharmony_ci { .compatible = "ti,bq27510g1" }, 2668c2ecf20Sopenharmony_ci { .compatible = "ti,bq27510g2" }, 2678c2ecf20Sopenharmony_ci { .compatible = "ti,bq27510g3" }, 2688c2ecf20Sopenharmony_ci { .compatible = "ti,bq27520g1" }, 2698c2ecf20Sopenharmony_ci { .compatible = "ti,bq27520g2" }, 2708c2ecf20Sopenharmony_ci { .compatible = "ti,bq27520g3" }, 2718c2ecf20Sopenharmony_ci { .compatible = "ti,bq27520g4" }, 2728c2ecf20Sopenharmony_ci { .compatible = "ti,bq27521" }, 2738c2ecf20Sopenharmony_ci { .compatible = "ti,bq27530" }, 2748c2ecf20Sopenharmony_ci { .compatible = "ti,bq27531" }, 2758c2ecf20Sopenharmony_ci { .compatible = "ti,bq27541" }, 2768c2ecf20Sopenharmony_ci { .compatible = "ti,bq27542" }, 2778c2ecf20Sopenharmony_ci { .compatible = "ti,bq27546" }, 2788c2ecf20Sopenharmony_ci { .compatible = "ti,bq27742" }, 2798c2ecf20Sopenharmony_ci { .compatible = "ti,bq27545" }, 2808c2ecf20Sopenharmony_ci { .compatible = "ti,bq27411" }, 2818c2ecf20Sopenharmony_ci { .compatible = "ti,bq27421" }, 2828c2ecf20Sopenharmony_ci { .compatible = "ti,bq27425" }, 2838c2ecf20Sopenharmony_ci { .compatible = "ti,bq27426" }, 2848c2ecf20Sopenharmony_ci { .compatible = "ti,bq27441" }, 2858c2ecf20Sopenharmony_ci { .compatible = "ti,bq27621" }, 2868c2ecf20Sopenharmony_ci { .compatible = "ti,bq27z561" }, 2878c2ecf20Sopenharmony_ci { .compatible = "ti,bq28z610" }, 2888c2ecf20Sopenharmony_ci { .compatible = "ti,bq34z100" }, 2898c2ecf20Sopenharmony_ci {}, 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, bq27xxx_battery_i2c_of_match_table); 2928c2ecf20Sopenharmony_ci#endif 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic struct i2c_driver bq27xxx_battery_i2c_driver = { 2958c2ecf20Sopenharmony_ci .driver = { 2968c2ecf20Sopenharmony_ci .name = "bq27xxx-battery", 2978c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(bq27xxx_battery_i2c_of_match_table), 2988c2ecf20Sopenharmony_ci }, 2998c2ecf20Sopenharmony_ci .probe = bq27xxx_battery_i2c_probe, 3008c2ecf20Sopenharmony_ci .remove = bq27xxx_battery_i2c_remove, 3018c2ecf20Sopenharmony_ci .id_table = bq27xxx_i2c_id_table, 3028c2ecf20Sopenharmony_ci}; 3038c2ecf20Sopenharmony_cimodule_i2c_driver(bq27xxx_battery_i2c_driver); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); 3068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("BQ27xxx battery monitor i2c driver"); 3078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 308