1 /*
2 * Copyright 2020 Google LLC
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/drivers/i2c.h>
8 #include <zephyr/drivers/sensor.h>
9 #include <zephyr/kernel.h>
10 #include <zephyr/sys/byteorder.h>
11
12 #include <zephyr/logging/log.h>
13 LOG_MODULE_REGISTER(max17055, CONFIG_SENSOR_LOG_LEVEL);
14
15 #include "max17055.h"
16 #include <zephyr/drivers/sensor/max17055.h>
17
18 #define DT_DRV_COMPAT maxim_max17055
19
20 /**
21 * @brief Read a register value
22 *
23 * Registers have an address and a 16-bit value
24 *
25 * @param priv Private data for the driver
26 * @param reg_addr Register address to read
27 * @param valp Place to put the value on success
28 * @return 0 if successful, or negative error code from I2C API
29 */
max17055_reg_read(const struct device * dev,uint8_t reg_addr,int16_t * valp)30 static int max17055_reg_read(const struct device *dev, uint8_t reg_addr,
31 int16_t *valp)
32 {
33 const struct max17055_config *config = dev->config;
34 uint8_t i2c_data[2];
35 int rc;
36
37 rc = i2c_burst_read_dt(&config->i2c, reg_addr, i2c_data, 2);
38 if (rc < 0) {
39 LOG_ERR("Unable to read register");
40 return rc;
41 }
42 *valp = sys_get_le16(i2c_data);
43
44 return 0;
45 }
46
max17055_reg_write(const struct device * dev,uint8_t reg_addr,uint16_t val)47 static int max17055_reg_write(const struct device *dev, uint8_t reg_addr,
48 uint16_t val)
49 {
50 const struct max17055_config *config = dev->config;
51 uint8_t buf[3];
52
53 buf[0] = reg_addr;
54 sys_put_le16(val, &buf[1]);
55
56 return i2c_write_dt(&config->i2c, buf, sizeof(buf));
57 }
58
59 /**
60 * @brief Convert current in MAX17055 units to milliamps
61 *
62 * @param rsense_mohms Value of Rsense in milliohms
63 * @param val Value to convert (taken from a MAX17055 register)
64 * @return corresponding value in milliamps
65 */
current_to_ma(uint16_t rsense_mohms,int16_t val)66 static int current_to_ma(uint16_t rsense_mohms, int16_t val)
67 {
68 return (int32_t)val * 25 / rsense_mohms / 16; /* * 1.5625 */
69 }
70
71 /**
72 * @brief Convert current in milliamps to MAX17055 units
73 *
74 * @param rsense_mohms Value of Rsense in milliohms
75 * @param val Value in mA to convert
76 * @return corresponding value in MAX17055 units, ready to write to a register
77 */
current_ma_to_max17055(uint16_t rsense_mohms,uint16_t val)78 static int current_ma_to_max17055(uint16_t rsense_mohms, uint16_t val)
79 {
80 return (int32_t)val * rsense_mohms * 16 / 25; /* / 1.5625 */
81 }
82
83 /**
84 * @brief Convert capacity in MAX17055 units to milliamps
85 *
86 * @param rsense_mohms Value of Rsense in milliohms
87 * @param val Value to convert (taken from a MAX17055 register)
88 * @return corresponding value in milliamps
89 */
capacity_to_ma(unsigned int rsense_mohms,int16_t val)90 static int capacity_to_ma(unsigned int rsense_mohms, int16_t val)
91 {
92 int lsb_units, rem;
93
94 /* Get units for the LSB in uA */
95 lsb_units = 5 * 1000 / rsense_mohms;
96 /* Get remaining capacity in uA */
97 rem = val * lsb_units;
98
99 return rem;
100 }
101
102 /**
103 * @brief Convert capacity in milliamphours to MAX17055 units
104 *
105 * @param rsense_mohms Value of Rsense in milliohms
106 * @param val_mha Value in milliamphours to convert
107 * @return corresponding value in MAX17055 units, ready to write to a register
108 */
capacity_to_max17055(unsigned int rsense_mohms,uint16_t val_mha)109 static int capacity_to_max17055(unsigned int rsense_mohms, uint16_t val_mha)
110 {
111 return val_mha * rsense_mohms / 5;
112 }
113
114 /**
115 * @brief Update empty voltage target in v_empty
116 *
117 * @param v_empty The register value to update
118 * @param val_mv Value in millivolts to convert
119 * @return 0 on success, -EINVAL on invalid val_mv
120 */
max17055_update_vempty(uint16_t * v_empty,uint16_t val_mv)121 static int max17055_update_vempty(uint16_t *v_empty, uint16_t val_mv)
122 {
123 uint32_t val = (val_mv / 10) << 7;
124
125 if (val & ~VEMPTY_VE) {
126 return -EINVAL;
127 }
128
129 *v_empty = (*v_empty & ~VEMPTY_VE) | (uint16_t)val;
130
131 return 0;
132 }
133
set_millis(struct sensor_value * val,int val_millis)134 static void set_millis(struct sensor_value *val, int val_millis)
135 {
136 val->val1 = val_millis / 1000;
137 val->val2 = (val_millis % 1000) * 1000;
138 }
139
140 /**
141 * @brief sensor value get
142 *
143 * @param dev MAX17055 device to access
144 * @param chan Channel number to read
145 * @param valp Returns the sensor value read on success
146 * @return 0 if successful
147 * @return -ENOTSUP for unsupported channels
148 */
max17055_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * valp)149 static int max17055_channel_get(const struct device *dev,
150 enum sensor_channel chan,
151 struct sensor_value *valp)
152 {
153 const struct max17055_config *const config = dev->config;
154 struct max17055_data *const priv = dev->data;
155 unsigned int tmp;
156
157 switch (chan) {
158 case SENSOR_CHAN_GAUGE_VOLTAGE:
159 /* Get voltage in uV */
160 tmp = priv->voltage * 1250 / 16;
161 valp->val1 = tmp / 1000000;
162 valp->val2 = tmp % 1000000;
163 break;
164 case SENSOR_CHAN_MAX17055_VFOCV:
165 tmp = priv->ocv * 1250 / 16;
166 valp->val1 = tmp / 1000000;
167 valp->val2 = tmp % 1000000;
168 break;
169 case SENSOR_CHAN_GAUGE_AVG_CURRENT: {
170 int current_ma;
171
172 current_ma = current_to_ma(config->rsense_mohms, priv->avg_current);
173 set_millis(valp, current_ma);
174 break;
175 }
176 case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE:
177 valp->val1 = priv->state_of_charge / 256;
178 valp->val2 = priv->state_of_charge % 256 * 1000000 / 256;
179 break;
180 case SENSOR_CHAN_GAUGE_TEMP:
181 valp->val1 = priv->internal_temp / 256;
182 valp->val2 = priv->internal_temp % 256 * 1000000 / 256;
183 break;
184 case SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY:
185 tmp = capacity_to_ma(config->rsense_mohms, priv->full_cap);
186 set_millis(valp, tmp);
187 break;
188 case SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY:
189 tmp = capacity_to_ma(config->rsense_mohms, priv->remaining_cap);
190 set_millis(valp, tmp);
191 break;
192 case SENSOR_CHAN_GAUGE_TIME_TO_EMPTY:
193 if (priv->time_to_empty == 0xffff) {
194 valp->val1 = 0;
195 valp->val2 = 0;
196 } else {
197 /* Get time in milli-minutes */
198 tmp = priv->time_to_empty * 5625 / 60;
199 set_millis(valp, tmp);
200 }
201 break;
202 case SENSOR_CHAN_GAUGE_TIME_TO_FULL:
203 if (priv->time_to_full == 0xffff) {
204 valp->val1 = 0;
205 valp->val2 = 0;
206 } else {
207 /* Get time in milli-minutes */
208 tmp = priv->time_to_full * 5625 / 60;
209 set_millis(valp, tmp);
210 }
211 break;
212 case SENSOR_CHAN_GAUGE_CYCLE_COUNT:
213 valp->val1 = priv->cycle_count / 100;
214 valp->val2 = priv->cycle_count % 100 * 10000;
215 break;
216 case SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY:
217 tmp = capacity_to_ma(config->rsense_mohms, priv->design_cap);
218 set_millis(valp, tmp);
219 break;
220 case SENSOR_CHAN_GAUGE_DESIGN_VOLTAGE:
221 set_millis(valp, config->design_voltage);
222 break;
223 case SENSOR_CHAN_GAUGE_DESIRED_VOLTAGE:
224 set_millis(valp, config->desired_voltage);
225 break;
226 case SENSOR_CHAN_GAUGE_DESIRED_CHARGING_CURRENT:
227 valp->val1 = config->desired_charging_current;
228 valp->val2 = 0;
229 break;
230 default:
231 return -ENOTSUP;
232 }
233
234 return 0;
235 }
236
max17055_sample_fetch(const struct device * dev,enum sensor_channel chan)237 static int max17055_sample_fetch(const struct device *dev,
238 enum sensor_channel chan)
239 {
240 struct max17055_data *priv = dev->data;
241 int ret = -ENOTSUP;
242
243 if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_VOLTAGE) {
244 ret = max17055_reg_read(dev, VCELL, &priv->voltage);
245 if (ret < 0) {
246 return ret;
247 }
248 }
249
250 if (chan == SENSOR_CHAN_ALL ||
251 (enum sensor_channel_max17055)chan == SENSOR_CHAN_MAX17055_VFOCV) {
252 ret = max17055_reg_read(dev, VFOCV, &priv->ocv);
253 if (ret < 0) {
254 return ret;
255 }
256 }
257
258 if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_AVG_CURRENT) {
259 ret = max17055_reg_read(dev, AVG_CURRENT, &priv->avg_current);
260 if (ret < 0) {
261 return ret;
262 }
263 }
264
265 if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_STATE_OF_CHARGE) {
266 ret = max17055_reg_read(dev, REP_SOC, &priv->state_of_charge);
267 if (ret < 0) {
268 return ret;
269 }
270 }
271
272 if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_TEMP) {
273 ret = max17055_reg_read(dev, INT_TEMP, &priv->internal_temp);
274 if (ret < 0) {
275 return ret;
276 }
277 }
278
279 if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY) {
280 ret = max17055_reg_read(dev, REP_CAP, &priv->remaining_cap);
281 if (ret < 0) {
282 return ret;
283 }
284 }
285
286 if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY) {
287 ret = max17055_reg_read(dev, FULL_CAP_REP, &priv->full_cap);
288 if (ret < 0) {
289 return ret;
290 }
291 }
292
293 if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_TIME_TO_EMPTY) {
294 ret = max17055_reg_read(dev, TTE, &priv->time_to_empty);
295 if (ret < 0) {
296 return ret;
297 }
298 }
299
300 if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_TIME_TO_FULL) {
301 ret = max17055_reg_read(dev, TTF, &priv->time_to_full);
302 if (ret < 0) {
303 return ret;
304 }
305 }
306
307 if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_CYCLE_COUNT) {
308 ret = max17055_reg_read(dev, CYCLES, &priv->cycle_count);
309 if (ret < 0) {
310 return ret;
311 }
312 }
313
314 if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY) {
315 ret = max17055_reg_read(dev, DESIGN_CAP, &priv->design_cap);
316 if (ret < 0) {
317 return ret;
318 }
319 }
320
321 return ret;
322 }
323
max17055_exit_hibernate(const struct device * dev)324 static int max17055_exit_hibernate(const struct device *dev)
325 {
326 LOG_DBG("Exit hibernate");
327
328 if (max17055_reg_write(dev, SOFT_WAKEUP, SOFT_WAKEUP_WAKEUP)) {
329 return -EIO;
330 }
331 if (max17055_reg_write(dev, HIB_CFG, HIB_CFG_CLEAR)) {
332 return -EIO;
333 }
334 if (max17055_reg_write(dev, SOFT_WAKEUP, SOFT_WAKEUP_CLEAR)) {
335 return -EIO;
336 }
337
338 return 0;
339 }
340
max17055_write_config(const struct device * dev)341 static int max17055_write_config(const struct device *dev)
342 {
343 const struct max17055_config *config = dev->config;
344
345 uint16_t design_capacity = capacity_to_max17055(config->rsense_mohms,
346 config->design_capacity);
347 uint16_t d_qacc = design_capacity / 32;
348 uint16_t d_pacc = d_qacc * 44138 / design_capacity;
349 uint16_t i_chg_term = current_ma_to_max17055(config->rsense_mohms, config->i_chg_term);
350 uint16_t v_empty;
351
352 LOG_DBG("Writing configuration parameters");
353 LOG_DBG("DesignCap: %u, dQAcc: %u, IChgTerm: %u, dPAcc: %u",
354 design_capacity, d_qacc, i_chg_term, d_pacc);
355
356 if (max17055_reg_write(dev, DESIGN_CAP, design_capacity)) {
357 return -EIO;
358 }
359
360 if (max17055_reg_write(dev, D_QACC, d_qacc)) {
361 return -EIO;
362 }
363
364 if (max17055_reg_write(dev, ICHG_TERM, i_chg_term)) {
365 return -EIO;
366 }
367
368 if (max17055_reg_read(dev, V_EMPTY, &v_empty)) {
369 return -EIO;
370 }
371 if (max17055_update_vempty(&v_empty, config->v_empty)) {
372 return -EINVAL;
373 }
374 if (max17055_reg_write(dev, V_EMPTY, v_empty)) {
375 return -EIO;
376 }
377
378 if (max17055_reg_write(dev, D_PACC, d_pacc)) {
379 return -EIO;
380 }
381
382 if (max17055_reg_write(dev, MODEL_CFG, MODELCFG_REFRESH)) {
383 return -EIO;
384 }
385
386 uint16_t model_cfg = MODELCFG_REFRESH;
387
388 while (model_cfg & MODELCFG_REFRESH) {
389 max17055_reg_read(dev, MODEL_CFG, &model_cfg);
390 k_sleep(K_MSEC(10));
391 }
392
393 return 0;
394 }
395
max17055_init_config(const struct device * dev)396 static int max17055_init_config(const struct device *dev)
397 {
398 int16_t hib_cfg;
399
400 if (max17055_reg_read(dev, HIB_CFG, &hib_cfg)) {
401 return -EIO;
402 }
403
404 if (max17055_exit_hibernate(dev)) {
405 return -EIO;
406 }
407
408 if (max17055_write_config(dev)) {
409 return -EIO;
410 }
411
412 if (max17055_reg_write(dev, HIB_CFG, hib_cfg)) {
413 return -EIO;
414 }
415
416 return 0;
417 }
418
419 /**
420 * @brief initialise the fuel gauge
421 *
422 * @return 0 for success
423 * @return -EIO on I2C communication error
424 * @return -EINVAL if the I2C controller could not be found
425 */
max17055_gauge_init(const struct device * dev)426 static int max17055_gauge_init(const struct device *dev)
427 {
428 int16_t tmp;
429 const struct max17055_config *const config = dev->config;
430
431 if (!device_is_ready(config->i2c.bus)) {
432 LOG_ERR("Bus device is not ready");
433 return -ENODEV;
434 }
435
436 if (max17055_reg_read(dev, STATUS, &tmp)) {
437 return -EIO;
438 }
439
440 if (!(tmp & STATUS_POR)) {
441 LOG_DBG("No POR event detected - skip device configuration");
442 return 0;
443 }
444
445 /* Wait for FSTAT_DNR to be cleared */
446 tmp = FSTAT_DNR;
447 while (tmp & FSTAT_DNR) {
448 max17055_reg_read(dev, FSTAT, &tmp);
449 }
450
451 if (max17055_init_config(dev)) {
452 return -EIO;
453 }
454
455 /* Clear PowerOnReset bit */
456 if (max17055_reg_read(dev, STATUS, &tmp)) {
457 return -EIO;
458 }
459
460 tmp &= ~STATUS_POR;
461 return max17055_reg_write(dev, STATUS, tmp);
462 }
463
464 static const struct sensor_driver_api max17055_battery_driver_api = {
465 .sample_fetch = max17055_sample_fetch,
466 .channel_get = max17055_channel_get,
467 };
468
469 #define MAX17055_INIT(index) \
470 static struct max17055_data max17055_driver_##index; \
471 \
472 static const struct max17055_config max17055_config_##index = { \
473 .i2c = I2C_DT_SPEC_INST_GET(index), \
474 .design_capacity = DT_INST_PROP(index, design_capacity), \
475 .design_voltage = DT_INST_PROP(index, design_voltage), \
476 .desired_charging_current = DT_INST_PROP(index, desired_charging_current), \
477 .desired_voltage = DT_INST_PROP(index, desired_voltage), \
478 .i_chg_term = DT_INST_PROP(index, i_chg_term), \
479 .rsense_mohms = DT_INST_PROP(index, rsense_mohms), \
480 .v_empty = DT_INST_PROP(index, v_empty), \
481 }; \
482 \
483 SENSOR_DEVICE_DT_INST_DEFINE(index, &max17055_gauge_init, \
484 NULL, \
485 &max17055_driver_##index, \
486 &max17055_config_##index, POST_KERNEL, \
487 CONFIG_SENSOR_INIT_PRIORITY, \
488 &max17055_battery_driver_api)
489
490 DT_INST_FOREACH_STATUS_OKAY(MAX17055_INIT);
491