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