linux/drivers/power/supply/bq27xxx_battery_i2c.c
<<
>>
Prefs
   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                        return ret;
 191                }
 192        }
 193
 194        return 0;
 195
 196err_mem:
 197        ret = -ENOMEM;
 198
 199err_failed:
 200        mutex_lock(&battery_mutex);
 201        idr_remove(&battery_id, num);
 202        mutex_unlock(&battery_mutex);
 203
 204        return ret;
 205}
 206
 207static int bq27xxx_battery_i2c_remove(struct i2c_client *client)
 208{
 209        struct bq27xxx_device_info *di = i2c_get_clientdata(client);
 210
 211        bq27xxx_battery_teardown(di);
 212
 213        mutex_lock(&battery_mutex);
 214        idr_remove(&battery_id, di->id);
 215        mutex_unlock(&battery_mutex);
 216
 217        return 0;
 218}
 219
 220static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
 221        { "bq27200", BQ27000 },
 222        { "bq27210", BQ27010 },
 223        { "bq27500", BQ2750X },
 224        { "bq27510", BQ2751X },
 225        { "bq27520", BQ2752X },
 226        { "bq27500-1", BQ27500 },
 227        { "bq27510g1", BQ27510G1 },
 228        { "bq27510g2", BQ27510G2 },
 229        { "bq27510g3", BQ27510G3 },
 230        { "bq27520g1", BQ27520G1 },
 231        { "bq27520g2", BQ27520G2 },
 232        { "bq27520g3", BQ27520G3 },
 233        { "bq27520g4", BQ27520G4 },
 234        { "bq27521", BQ27521 },
 235        { "bq27530", BQ27530 },
 236        { "bq27531", BQ27531 },
 237        { "bq27541", BQ27541 },
 238        { "bq27542", BQ27542 },
 239        { "bq27546", BQ27546 },
 240        { "bq27742", BQ27742 },
 241        { "bq27545", BQ27545 },
 242        { "bq27411", BQ27411 },
 243        { "bq27421", BQ27421 },
 244        { "bq27425", BQ27425 },
 245        { "bq27426", BQ27426 },
 246        { "bq27441", BQ27441 },
 247        { "bq27621", BQ27621 },
 248        { "bq27z561", BQ27Z561 },
 249        { "bq28z610", BQ28Z610 },
 250        { "bq34z100", BQ34Z100 },
 251        { "bq78z100", BQ78Z100 },
 252        {},
 253};
 254MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table);
 255
 256#ifdef CONFIG_OF
 257static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = {
 258        { .compatible = "ti,bq27200" },
 259        { .compatible = "ti,bq27210" },
 260        { .compatible = "ti,bq27500" },
 261        { .compatible = "ti,bq27510" },
 262        { .compatible = "ti,bq27520" },
 263        { .compatible = "ti,bq27500-1" },
 264        { .compatible = "ti,bq27510g1" },
 265        { .compatible = "ti,bq27510g2" },
 266        { .compatible = "ti,bq27510g3" },
 267        { .compatible = "ti,bq27520g1" },
 268        { .compatible = "ti,bq27520g2" },
 269        { .compatible = "ti,bq27520g3" },
 270        { .compatible = "ti,bq27520g4" },
 271        { .compatible = "ti,bq27521" },
 272        { .compatible = "ti,bq27530" },
 273        { .compatible = "ti,bq27531" },
 274        { .compatible = "ti,bq27541" },
 275        { .compatible = "ti,bq27542" },
 276        { .compatible = "ti,bq27546" },
 277        { .compatible = "ti,bq27742" },
 278        { .compatible = "ti,bq27545" },
 279        { .compatible = "ti,bq27411" },
 280        { .compatible = "ti,bq27421" },
 281        { .compatible = "ti,bq27425" },
 282        { .compatible = "ti,bq27426" },
 283        { .compatible = "ti,bq27441" },
 284        { .compatible = "ti,bq27621" },
 285        { .compatible = "ti,bq27z561" },
 286        { .compatible = "ti,bq28z610" },
 287        { .compatible = "ti,bq34z100" },
 288        { .compatible = "ti,bq78z100" },
 289        {},
 290};
 291MODULE_DEVICE_TABLE(of, bq27xxx_battery_i2c_of_match_table);
 292#endif
 293
 294static struct i2c_driver bq27xxx_battery_i2c_driver = {
 295        .driver = {
 296                .name = "bq27xxx-battery",
 297                .of_match_table = of_match_ptr(bq27xxx_battery_i2c_of_match_table),
 298        },
 299        .probe = bq27xxx_battery_i2c_probe,
 300        .remove = bq27xxx_battery_i2c_remove,
 301        .id_table = bq27xxx_i2c_id_table,
 302};
 303module_i2c_driver(bq27xxx_battery_i2c_driver);
 304
 305MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
 306MODULE_DESCRIPTION("BQ27xxx battery monitor i2c driver");
 307MODULE_LICENSE("GPL");
 308