648 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * I2C client/driver for the Linear Technology LTC2941, LTC2942, LTC29434 * and LTC2944 Battery Gas Gauge IC5 *6 * Copyright (C) 2014 Topic Embedded Systems7 *8 * Author: Auryn Verwegen9 * Author: Mike Looijmans10 */11#include <linux/devm-helpers.h>12#include <linux/kernel.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/types.h>16#include <linux/errno.h>17#include <linux/swab.h>18#include <linux/i2c.h>19#include <linux/delay.h>20#include <linux/power_supply.h>21#include <linux/slab.h>22 23#define I16_MSB(x) ((x >> 8) & 0xFF)24#define I16_LSB(x) (x & 0xFF)25 26#define LTC294X_WORK_DELAY 10 /* Update delay in seconds */27 28#define LTC294X_MAX_VALUE 0xFFFF29#define LTC294X_MID_SUPPLY 0x7FFF30 31#define LTC2941_MAX_PRESCALER_EXP 732#define LTC2943_MAX_PRESCALER_EXP 633 34enum ltc294x_reg {35 LTC294X_REG_STATUS = 0x00,36 LTC294X_REG_CONTROL = 0x01,37 LTC294X_REG_ACC_CHARGE_MSB = 0x02,38 LTC294X_REG_ACC_CHARGE_LSB = 0x03,39 LTC294X_REG_CHARGE_THR_HIGH_MSB = 0x04,40 LTC294X_REG_CHARGE_THR_HIGH_LSB = 0x05,41 LTC294X_REG_CHARGE_THR_LOW_MSB = 0x06,42 LTC294X_REG_CHARGE_THR_LOW_LSB = 0x07,43 LTC294X_REG_VOLTAGE_MSB = 0x08,44 LTC294X_REG_VOLTAGE_LSB = 0x09,45 LTC2942_REG_TEMPERATURE_MSB = 0x0C,46 LTC2942_REG_TEMPERATURE_LSB = 0x0D,47 LTC2943_REG_CURRENT_MSB = 0x0E,48 LTC2943_REG_CURRENT_LSB = 0x0F,49 LTC2943_REG_TEMPERATURE_MSB = 0x14,50 LTC2943_REG_TEMPERATURE_LSB = 0x15,51};52 53enum ltc294x_id {54 LTC2941_ID,55 LTC2942_ID,56 LTC2943_ID,57 LTC2944_ID,58};59 60#define LTC2941_REG_STATUS_CHIP_ID BIT(7)61 62#define LTC2942_REG_CONTROL_MODE_SCAN (BIT(7) | BIT(6))63#define LTC2943_REG_CONTROL_MODE_SCAN BIT(7)64#define LTC294X_REG_CONTROL_PRESCALER_MASK (BIT(5) | BIT(4) | BIT(3))65#define LTC294X_REG_CONTROL_SHUTDOWN_MASK (BIT(0))66#define LTC294X_REG_CONTROL_PRESCALER_SET(x) \67 ((x << 3) & LTC294X_REG_CONTROL_PRESCALER_MASK)68#define LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED 069#define LTC294X_REG_CONTROL_ADC_DISABLE(x) ((x) & ~(BIT(7) | BIT(6)))70 71struct ltc294x_info {72 struct i2c_client *client; /* I2C Client pointer */73 struct power_supply *supply; /* Supply pointer */74 struct power_supply_desc supply_desc; /* Supply description */75 struct delayed_work work; /* Work scheduler */76 enum ltc294x_id id; /* Chip type */77 int charge; /* Last charge register content */78 int r_sense; /* mOhm */79 int Qlsb; /* nAh */80};81 82static inline int convert_bin_to_uAh(83 const struct ltc294x_info *info, int Q)84{85 return ((Q * (info->Qlsb / 10))) / 100;86}87 88static inline int convert_uAh_to_bin(89 const struct ltc294x_info *info, int uAh)90{91 int Q;92 93 Q = (uAh * 100) / (info->Qlsb/10);94 return (Q < LTC294X_MAX_VALUE) ? Q : LTC294X_MAX_VALUE;95}96 97static int ltc294x_read_regs(struct i2c_client *client,98 enum ltc294x_reg reg, u8 *buf, int num_regs)99{100 int ret;101 struct i2c_msg msgs[2] = { };102 u8 reg_start = reg;103 104 msgs[0].addr = client->addr;105 msgs[0].len = 1;106 msgs[0].buf = ®_start;107 108 msgs[1].addr = client->addr;109 msgs[1].len = num_regs;110 msgs[1].buf = buf;111 msgs[1].flags = I2C_M_RD;112 113 ret = i2c_transfer(client->adapter, &msgs[0], 2);114 if (ret < 0) {115 dev_err(&client->dev, "ltc2941 read_reg(0x%x[%d]) failed: %pe\n",116 reg, num_regs, ERR_PTR(ret));117 return ret;118 }119 120 dev_dbg(&client->dev, "%s (%#x, %d) -> %#x\n",121 __func__, reg, num_regs, *buf);122 123 return 0;124}125 126static int ltc294x_write_regs(struct i2c_client *client,127 enum ltc294x_reg reg, const u8 *buf, int num_regs)128{129 int ret;130 u8 reg_start = reg;131 132 ret = i2c_smbus_write_i2c_block_data(client, reg_start, num_regs, buf);133 if (ret < 0) {134 dev_err(&client->dev, "ltc2941 write_reg(0x%x[%d]) failed: %pe\n",135 reg, num_regs, ERR_PTR(ret));136 return ret;137 }138 139 dev_dbg(&client->dev, "%s (%#x, %d) -> %#x\n",140 __func__, reg, num_regs, *buf);141 142 return 0;143}144 145static int ltc294x_reset(const struct ltc294x_info *info, int prescaler_exp)146{147 int ret;148 u8 value;149 u8 control;150 151 /* Read status and control registers */152 ret = ltc294x_read_regs(info->client, LTC294X_REG_CONTROL, &value, 1);153 if (ret < 0)154 return ret;155 156 control = LTC294X_REG_CONTROL_PRESCALER_SET(prescaler_exp) |157 LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED;158 /* Put device into "monitor" mode */159 switch (info->id) {160 case LTC2942_ID: /* 2942 measures every 2 sec */161 control |= LTC2942_REG_CONTROL_MODE_SCAN;162 break;163 case LTC2943_ID:164 case LTC2944_ID: /* 2943 and 2944 measure every 10 sec */165 control |= LTC2943_REG_CONTROL_MODE_SCAN;166 break;167 default:168 break;169 }170 171 if (value != control) {172 ret = ltc294x_write_regs(info->client,173 LTC294X_REG_CONTROL, &control, 1);174 if (ret < 0)175 return ret;176 }177 178 return 0;179}180 181static int ltc294x_read_charge_register(const struct ltc294x_info *info,182 enum ltc294x_reg reg)183 {184 int ret;185 u8 datar[2];186 187 ret = ltc294x_read_regs(info->client, reg, &datar[0], 2);188 if (ret < 0)189 return ret;190 return (datar[0] << 8) + datar[1];191}192 193static int ltc294x_get_charge(const struct ltc294x_info *info,194 enum ltc294x_reg reg, int *val)195{196 int value = ltc294x_read_charge_register(info, reg);197 198 if (value < 0)199 return value;200 /* When r_sense < 0, this counts up when the battery discharges */201 if (info->Qlsb < 0)202 value -= 0xFFFF;203 *val = convert_bin_to_uAh(info, value);204 return 0;205}206 207static int ltc294x_set_charge_now(const struct ltc294x_info *info, int val)208{209 int ret;210 u8 dataw[2];211 u8 ctrl_reg;212 s32 value;213 214 value = convert_uAh_to_bin(info, val);215 /* Direction depends on how sense+/- were connected */216 if (info->Qlsb < 0)217 value += 0xFFFF;218 if ((value < 0) || (value > 0xFFFF)) /* input validation */219 return -EINVAL;220 221 /* Read control register */222 ret = ltc294x_read_regs(info->client,223 LTC294X_REG_CONTROL, &ctrl_reg, 1);224 if (ret < 0)225 return ret;226 /* Disable analog section */227 ctrl_reg |= LTC294X_REG_CONTROL_SHUTDOWN_MASK;228 ret = ltc294x_write_regs(info->client,229 LTC294X_REG_CONTROL, &ctrl_reg, 1);230 if (ret < 0)231 return ret;232 /* Set new charge value */233 dataw[0] = I16_MSB(value);234 dataw[1] = I16_LSB(value);235 ret = ltc294x_write_regs(info->client,236 LTC294X_REG_ACC_CHARGE_MSB, &dataw[0], 2);237 if (ret < 0)238 goto error_exit;239 /* Enable analog section */240error_exit:241 ctrl_reg &= ~LTC294X_REG_CONTROL_SHUTDOWN_MASK;242 ret = ltc294x_write_regs(info->client,243 LTC294X_REG_CONTROL, &ctrl_reg, 1);244 245 return ret < 0 ? ret : 0;246}247 248static int ltc294x_set_charge_thr(const struct ltc294x_info *info,249 enum ltc294x_reg reg, int val)250{251 u8 dataw[2];252 s32 value;253 254 value = convert_uAh_to_bin(info, val);255 /* Direction depends on how sense+/- were connected */256 if (info->Qlsb < 0)257 value += 0xFFFF;258 if ((value < 0) || (value > 0xFFFF)) /* input validation */259 return -EINVAL;260 261 /* Set new charge value */262 dataw[0] = I16_MSB(value);263 dataw[1] = I16_LSB(value);264 return ltc294x_write_regs(info->client, reg, &dataw[0], 2);265}266 267static int ltc294x_get_charge_counter(268 const struct ltc294x_info *info, int *val)269{270 int value = ltc294x_read_charge_register(info, LTC294X_REG_ACC_CHARGE_MSB);271 272 if (value < 0)273 return value;274 value -= LTC294X_MID_SUPPLY;275 *val = convert_bin_to_uAh(info, value);276 return 0;277}278 279static int ltc294x_get_voltage(const struct ltc294x_info *info, int *val)280{281 int ret;282 u8 datar[2];283 u32 value;284 285 ret = ltc294x_read_regs(info->client,286 LTC294X_REG_VOLTAGE_MSB, &datar[0], 2);287 value = (datar[0] << 8) | datar[1];288 switch (info->id) {289 case LTC2943_ID:290 value *= 23600 * 2;291 value /= 0xFFFF;292 value *= 1000 / 2;293 break;294 case LTC2944_ID:295 value *= 70800 / 5*4;296 value /= 0xFFFF;297 value *= 1000 * 5/4;298 break;299 default:300 value *= 6000 * 10;301 value /= 0xFFFF;302 value *= 1000 / 10;303 break;304 }305 *val = value;306 return ret;307}308 309static int ltc294x_get_current(const struct ltc294x_info *info, int *val)310{311 int ret;312 u8 datar[2];313 s32 value;314 315 ret = ltc294x_read_regs(info->client,316 LTC2943_REG_CURRENT_MSB, &datar[0], 2);317 value = (datar[0] << 8) | datar[1];318 value -= 0x7FFF;319 if (info->id == LTC2944_ID)320 value *= 64000;321 else322 value *= 60000;323 /* Value is in range -32k..+32k, r_sense is usually 10..50 mOhm,324 * the formula below keeps everything in s32 range while preserving325 * enough digits */326 *val = 1000 * (value / (info->r_sense * 0x7FFF)); /* in uA */327 return ret;328}329 330static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val)331{332 enum ltc294x_reg reg;333 int ret;334 u8 datar[2];335 u32 value;336 337 if (info->id == LTC2942_ID) {338 reg = LTC2942_REG_TEMPERATURE_MSB;339 value = 6000; /* Full-scale is 600 Kelvin */340 } else {341 reg = LTC2943_REG_TEMPERATURE_MSB;342 value = 5100; /* Full-scale is 510 Kelvin */343 }344 ret = ltc294x_read_regs(info->client, reg, &datar[0], 2);345 value *= (datar[0] << 8) | datar[1];346 /* Convert to tenths of degree Celsius */347 *val = value / 0xFFFF - 2722;348 return ret;349}350 351static int ltc294x_get_property(struct power_supply *psy,352 enum power_supply_property prop,353 union power_supply_propval *val)354{355 struct ltc294x_info *info = power_supply_get_drvdata(psy);356 357 switch (prop) {358 case POWER_SUPPLY_PROP_CHARGE_FULL:359 return ltc294x_get_charge(info, LTC294X_REG_CHARGE_THR_HIGH_MSB,360 &val->intval);361 case POWER_SUPPLY_PROP_CHARGE_EMPTY:362 return ltc294x_get_charge(info, LTC294X_REG_CHARGE_THR_LOW_MSB,363 &val->intval);364 case POWER_SUPPLY_PROP_CHARGE_NOW:365 return ltc294x_get_charge(info, LTC294X_REG_ACC_CHARGE_MSB,366 &val->intval);367 case POWER_SUPPLY_PROP_CHARGE_COUNTER:368 return ltc294x_get_charge_counter(info, &val->intval);369 case POWER_SUPPLY_PROP_VOLTAGE_NOW:370 return ltc294x_get_voltage(info, &val->intval);371 case POWER_SUPPLY_PROP_CURRENT_NOW:372 return ltc294x_get_current(info, &val->intval);373 case POWER_SUPPLY_PROP_TEMP:374 return ltc294x_get_temperature(info, &val->intval);375 default:376 return -EINVAL;377 }378}379 380static int ltc294x_set_property(struct power_supply *psy,381 enum power_supply_property psp,382 const union power_supply_propval *val)383{384 struct ltc294x_info *info = power_supply_get_drvdata(psy);385 386 switch (psp) {387 case POWER_SUPPLY_PROP_CHARGE_FULL:388 return ltc294x_set_charge_thr(info,389 LTC294X_REG_CHARGE_THR_HIGH_MSB, val->intval);390 case POWER_SUPPLY_PROP_CHARGE_EMPTY:391 return ltc294x_set_charge_thr(info,392 LTC294X_REG_CHARGE_THR_LOW_MSB, val->intval);393 case POWER_SUPPLY_PROP_CHARGE_NOW:394 return ltc294x_set_charge_now(info, val->intval);395 default:396 return -EPERM;397 }398}399 400static int ltc294x_property_is_writeable(401 struct power_supply *psy, enum power_supply_property psp)402{403 switch (psp) {404 case POWER_SUPPLY_PROP_CHARGE_FULL:405 case POWER_SUPPLY_PROP_CHARGE_EMPTY:406 case POWER_SUPPLY_PROP_CHARGE_NOW:407 return 1;408 default:409 return 0;410 }411}412 413static void ltc294x_update(struct ltc294x_info *info)414{415 int charge = ltc294x_read_charge_register(info, LTC294X_REG_ACC_CHARGE_MSB);416 417 if (charge != info->charge) {418 info->charge = charge;419 power_supply_changed(info->supply);420 }421}422 423static void ltc294x_work(struct work_struct *work)424{425 struct ltc294x_info *info;426 427 info = container_of(work, struct ltc294x_info, work.work);428 ltc294x_update(info);429 schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ);430}431 432static enum power_supply_property ltc294x_properties[] = {433 POWER_SUPPLY_PROP_CHARGE_COUNTER,434 POWER_SUPPLY_PROP_CHARGE_FULL,435 POWER_SUPPLY_PROP_CHARGE_EMPTY,436 POWER_SUPPLY_PROP_CHARGE_NOW,437 POWER_SUPPLY_PROP_VOLTAGE_NOW,438 POWER_SUPPLY_PROP_TEMP,439 POWER_SUPPLY_PROP_CURRENT_NOW,440};441 442static int ltc294x_i2c_probe(struct i2c_client *client)443{444 struct power_supply_config psy_cfg = {};445 struct ltc294x_info *info;446 struct device_node *np;447 int ret;448 u32 prescaler_exp;449 s32 r_sense;450 u8 status;451 452 info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);453 if (info == NULL)454 return -ENOMEM;455 456 i2c_set_clientdata(client, info);457 458 np = of_node_get(client->dev.of_node);459 460 info->id = (enum ltc294x_id) (uintptr_t) of_device_get_match_data(461 &client->dev);462 info->supply_desc.name = np->name;463 464 /* r_sense can be negative, when sense+ is connected to the battery465 * instead of the sense-. This results in reversed measurements. */466 ret = of_property_read_u32(np, "lltc,resistor-sense", &r_sense);467 if (ret < 0)468 return dev_err_probe(&client->dev, ret,469 "Could not find lltc,resistor-sense in devicetree\n");470 info->r_sense = r_sense;471 472 ret = of_property_read_u32(np, "lltc,prescaler-exponent",473 &prescaler_exp);474 if (ret < 0) {475 dev_warn(&client->dev,476 "lltc,prescaler-exponent not in devicetree\n");477 prescaler_exp = LTC2941_MAX_PRESCALER_EXP;478 }479 480 if (info->id == LTC2943_ID) {481 if (prescaler_exp > LTC2943_MAX_PRESCALER_EXP)482 prescaler_exp = LTC2943_MAX_PRESCALER_EXP;483 info->Qlsb = ((340 * 50000) / r_sense) >>484 (12 - 2*prescaler_exp);485 } else {486 if (prescaler_exp > LTC2941_MAX_PRESCALER_EXP)487 prescaler_exp = LTC2941_MAX_PRESCALER_EXP;488 info->Qlsb = ((85 * 50000) / r_sense) >>489 (7 - prescaler_exp);490 }491 492 /* Read status register to check for LTC2942 */493 if (info->id == LTC2941_ID || info->id == LTC2942_ID) {494 ret = ltc294x_read_regs(client, LTC294X_REG_STATUS, &status, 1);495 if (ret < 0)496 return dev_err_probe(&client->dev, ret,497 "Could not read status register\n");498 if (status & LTC2941_REG_STATUS_CHIP_ID)499 info->id = LTC2941_ID;500 else501 info->id = LTC2942_ID;502 }503 504 info->client = client;505 info->supply_desc.type = POWER_SUPPLY_TYPE_BATTERY;506 info->supply_desc.properties = ltc294x_properties;507 switch (info->id) {508 case LTC2944_ID:509 case LTC2943_ID:510 info->supply_desc.num_properties =511 ARRAY_SIZE(ltc294x_properties);512 break;513 case LTC2942_ID:514 info->supply_desc.num_properties =515 ARRAY_SIZE(ltc294x_properties) - 1;516 break;517 case LTC2941_ID:518 default:519 info->supply_desc.num_properties =520 ARRAY_SIZE(ltc294x_properties) - 3;521 break;522 }523 info->supply_desc.get_property = ltc294x_get_property;524 info->supply_desc.set_property = ltc294x_set_property;525 info->supply_desc.property_is_writeable = ltc294x_property_is_writeable;526 info->supply_desc.external_power_changed = NULL;527 528 psy_cfg.drv_data = info;529 530 ret = devm_delayed_work_autocancel(&client->dev, &info->work,531 ltc294x_work);532 if (ret)533 return ret;534 535 ret = ltc294x_reset(info, prescaler_exp);536 if (ret < 0)537 return dev_err_probe(&client->dev, ret,538 "Communication with chip failed\n");539 540 info->supply = devm_power_supply_register(&client->dev,541 &info->supply_desc, &psy_cfg);542 if (IS_ERR(info->supply))543 return dev_err_probe(&client->dev, PTR_ERR(info->supply),544 "failed to register ltc2941\n");545 546 schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ);547 548 return 0;549}550 551static void ltc294x_i2c_shutdown(struct i2c_client *client)552{553 struct ltc294x_info *info = i2c_get_clientdata(client);554 int ret;555 u8 value;556 u8 control;557 558 /* The LTC2941 does not need any special handling */559 if (info->id == LTC2941_ID)560 return;561 562 /* Read control register */563 ret = ltc294x_read_regs(info->client, LTC294X_REG_CONTROL, &value, 1);564 if (ret < 0)565 return;566 567 /* Disable continuous ADC conversion as this drains the battery */568 control = LTC294X_REG_CONTROL_ADC_DISABLE(value);569 if (control != value)570 ltc294x_write_regs(info->client, LTC294X_REG_CONTROL,571 &control, 1);572}573 574#ifdef CONFIG_PM_SLEEP575 576static int ltc294x_suspend(struct device *dev)577{578 struct i2c_client *client = to_i2c_client(dev);579 struct ltc294x_info *info = i2c_get_clientdata(client);580 581 cancel_delayed_work(&info->work);582 return 0;583}584 585static int ltc294x_resume(struct device *dev)586{587 struct i2c_client *client = to_i2c_client(dev);588 struct ltc294x_info *info = i2c_get_clientdata(client);589 590 schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ);591 return 0;592}593 594static SIMPLE_DEV_PM_OPS(ltc294x_pm_ops, ltc294x_suspend, ltc294x_resume);595#define LTC294X_PM_OPS (<c294x_pm_ops)596 597#else598#define LTC294X_PM_OPS NULL599#endif /* CONFIG_PM_SLEEP */600 601 602static const struct i2c_device_id ltc294x_i2c_id[] = {603 { "ltc2941", LTC2941_ID, },604 { "ltc2942", LTC2942_ID, },605 { "ltc2943", LTC2943_ID, },606 { "ltc2944", LTC2944_ID, },607 { },608};609MODULE_DEVICE_TABLE(i2c, ltc294x_i2c_id);610 611static const struct of_device_id ltc294x_i2c_of_match[] = {612 {613 .compatible = "lltc,ltc2941",614 .data = (void *)LTC2941_ID,615 },616 {617 .compatible = "lltc,ltc2942",618 .data = (void *)LTC2942_ID,619 },620 {621 .compatible = "lltc,ltc2943",622 .data = (void *)LTC2943_ID,623 },624 {625 .compatible = "lltc,ltc2944",626 .data = (void *)LTC2944_ID,627 },628 { },629};630MODULE_DEVICE_TABLE(of, ltc294x_i2c_of_match);631 632static struct i2c_driver ltc294x_driver = {633 .driver = {634 .name = "LTC2941",635 .of_match_table = ltc294x_i2c_of_match,636 .pm = LTC294X_PM_OPS,637 },638 .probe = ltc294x_i2c_probe,639 .shutdown = ltc294x_i2c_shutdown,640 .id_table = ltc294x_i2c_id,641};642module_i2c_driver(ltc294x_driver);643 644MODULE_AUTHOR("Auryn Verwegen, Topic Embedded Systems");645MODULE_AUTHOR("Mike Looijmans, Topic Embedded Products");646MODULE_DESCRIPTION("LTC2941/LTC2942/LTC2943/LTC2944 Battery Gas Gauge IC driver");647MODULE_LICENSE("GPL");648