1 /*
2 * Copyright 2021 Matija Tudan
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
11 #include <zephyr/logging/log.h>
12 LOG_MODULE_REGISTER(max17262, CONFIG_SENSOR_LOG_LEVEL);
13
14 #include "max17262.h"
15
16 #define DT_DRV_COMPAT maxim_max17262
17
18 /**
19 * @brief Read a register value
20 *
21 * Registers have an address and a 16-bit value
22 *
23 * @param dev MAX17262 device to access
24 * @param reg_addr Register address to read
25 * @param valp Place to put the value on success
26 * @return 0 if successful, or negative error code from I2C API
27 */
max17262_reg_read(const struct device * dev,uint8_t reg_addr,int16_t * valp)28 static int max17262_reg_read(const struct device *dev, uint8_t reg_addr, int16_t *valp)
29 {
30 const struct max17262_config *cfg = dev->config;
31 uint8_t i2c_data[2];
32 int rc;
33
34 rc = i2c_burst_read_dt(&cfg->i2c, reg_addr, i2c_data, 2);
35 if (rc < 0) {
36 LOG_ERR("Unable to read register 0x%02x", reg_addr);
37 return rc;
38 }
39 *valp = ((int16_t)i2c_data[1] << 8) | i2c_data[0];
40
41 return 0;
42 }
43
44 /**
45 * @brief Write a register value
46 *
47 * Registers have an address and a 16-bit value
48 *
49 * @param dev MAX17262 device to access
50 * @param reg_addr Register address to write to
51 * @param val Register value to write
52 * @return 0 if successful, or negative error code from I2C API
53 */
max17262_reg_write(const struct device * dev,uint8_t reg_addr,int16_t val)54 static int max17262_reg_write(const struct device *dev, uint8_t reg_addr, int16_t val)
55 {
56 const struct max17262_config *cfg = dev->config;
57 uint8_t i2c_data[3] = {reg_addr, val & 0xFF, (uint16_t)val >> 8};
58
59 return i2c_write_dt(&cfg->i2c, i2c_data, sizeof(i2c_data));
60 }
61
62 /**
63 * @brief Convert sensor value from millis
64 *
65 * @param val Where to store converted value in sensor_value format
66 * @param val_millis Value in millis
67 */
convert_millis(struct sensor_value * val,int32_t val_millis)68 static void convert_millis(struct sensor_value *val, int32_t val_millis)
69 {
70 val->val1 = val_millis / 1000;
71 val->val2 = (val_millis % 1000) * 1000;
72 }
73
74 /**
75 * @brief Convert raw register values for specific channel
76 *
77 * @param dev MAX17262 device to access
78 * @param chan Channel number to read
79 * @param valp Returns the sensor value read on success
80 * @return 0 if successful
81 * @return -ENOTSUP for unsupported channels
82 */
max17262_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * valp)83 static int max17262_channel_get(const struct device *dev, enum sensor_channel chan,
84 struct sensor_value *valp)
85 {
86 const struct max17262_config *const config = dev->config;
87 struct max17262_data *const data = dev->data;
88 int32_t tmp;
89
90 switch (chan) {
91 case SENSOR_CHAN_GAUGE_VOLTAGE:
92 /* Get voltage in uV */
93 tmp = data->voltage * VOLTAGE_MULTIPLIER_UV;
94 /* Convert to V */
95 valp->val1 = tmp / 1000000;
96 valp->val2 = tmp % 1000000;
97 break;
98 case SENSOR_CHAN_GAUGE_AVG_CURRENT: {
99 int current;
100 /* Get avg current in nA */
101 current = data->avg_current * CURRENT_MULTIPLIER_NA;
102 /* Convert to mA */
103 valp->val1 = current / 1000000;
104 valp->val2 = current % 1000000;
105 break;
106 }
107 case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE:
108 valp->val1 = data->state_of_charge / 256;
109 valp->val2 = data->state_of_charge % 256 * 1000000 / 256;
110 break;
111 case SENSOR_CHAN_GAUGE_TEMP:
112 valp->val1 = data->internal_temp / 256;
113 valp->val2 = data->internal_temp % 256 * 1000000 / 256;
114 break;
115 case SENSOR_CHAN_GAUGE_FULL_CHARGE_CAPACITY:
116 convert_millis(valp, data->full_cap);
117 break;
118 case SENSOR_CHAN_GAUGE_REMAINING_CHARGE_CAPACITY:
119 convert_millis(valp, data->remaining_cap);
120 break;
121 case SENSOR_CHAN_GAUGE_TIME_TO_EMPTY:
122 /* Get time in ms */
123 if (data->time_to_empty == 0xffff) {
124 valp->val1 = 0;
125 valp->val2 = 0;
126 } else {
127 tmp = data->time_to_empty * TIME_MULTIPLIER_MS;
128 convert_millis(valp, tmp);
129 }
130 break;
131 case SENSOR_CHAN_GAUGE_TIME_TO_FULL:
132 /* Get time in ms */
133 if (data->time_to_full == 0xffff) {
134 valp->val1 = 0;
135 valp->val2 = 0;
136 } else {
137 tmp = data->time_to_full * TIME_MULTIPLIER_MS;
138 convert_millis(valp, tmp);
139 }
140 break;
141 case SENSOR_CHAN_GAUGE_CYCLE_COUNT:
142 valp->val1 = data->cycle_count / 100;
143 valp->val2 = data->cycle_count % 100 * 10000;
144 break;
145 case SENSOR_CHAN_GAUGE_NOM_AVAIL_CAPACITY:
146 convert_millis(valp, data->design_cap);
147 break;
148 case SENSOR_CHAN_GAUGE_DESIGN_VOLTAGE:
149 convert_millis(valp, config->design_voltage);
150 break;
151 case SENSOR_CHAN_GAUGE_DESIRED_VOLTAGE:
152 convert_millis(valp, config->desired_voltage);
153 break;
154 case SENSOR_CHAN_GAUGE_DESIRED_CHARGING_CURRENT:
155 valp->val1 = data->ichg_term;
156 valp->val2 = 0;
157 break;
158 case MAX17262_COULOMB_COUNTER:
159 /* Get spent capacity in mAh */
160 data->coulomb_counter = 0xffff - data->coulomb_counter;
161 valp->val1 = data->coulomb_counter / 2;
162 valp->val2 = data->coulomb_counter % 2 * 10 / 2;
163 break;
164 default:
165 LOG_ERR("Unsupported channel!");
166 return -ENOTSUP;
167 }
168
169 return 0;
170 }
171
172 /**
173 * @brief Read register values for supported channels
174 *
175 * @param dev MAX17262 device to access
176 * @return 0 if successful, or negative error code from I2C API
177 */
max17262_sample_fetch(const struct device * dev,enum sensor_channel chan)178 static int max17262_sample_fetch(const struct device *dev, enum sensor_channel chan)
179 {
180 struct max17262_data *data = dev->data;
181
182 /* clang-format off */
183 struct {
184 int reg_addr;
185 int16_t *dest;
186 } regs[] = {
187 { VCELL, &data->voltage },
188 { AVG_CURRENT, &data->avg_current },
189 { ICHG_TERM, &data->ichg_term },
190 { REP_SOC, &data->state_of_charge },
191 { INT_TEMP, &data->internal_temp },
192 { REP_CAP, &data->remaining_cap },
193 { FULL_CAP_REP, &data->full_cap },
194 { TTE, &data->time_to_empty },
195 { TTF, &data->time_to_full },
196 { CYCLES, &data->cycle_count },
197 { DESIGN_CAP, &data->design_cap },
198 { COULOMB_COUNTER, &data->coulomb_counter },
199 };
200 /* clang-format on */
201
202 __ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL);
203 for (size_t i = 0; i < ARRAY_SIZE(regs); i++) {
204 int rc;
205
206 rc = max17262_reg_read(dev, regs[i].reg_addr, regs[i].dest);
207 if (rc != 0) {
208 LOG_ERR("Failed to read channel %d", chan);
209 return rc;
210 }
211 }
212
213 return 0;
214 }
215
216 /**
217 * @brief Initialise the fuel gauge
218 *
219 * @param dev MAX17262 device to access
220 * @return 0 for success
221 * @return -EINVAL if the I2C controller could not be found
222 */
max17262_gauge_init(const struct device * dev)223 static int max17262_gauge_init(const struct device *dev)
224 {
225 const struct max17262_config *const config = dev->config;
226 int16_t tmp, hibcfg;
227 int rc;
228
229 if (!device_is_ready(config->i2c.bus)) {
230 LOG_ERR("Bus device is not ready");
231 return -ENODEV;
232 }
233
234 /* Read Status register */
235 rc = max17262_reg_read(dev, STATUS, &tmp);
236 if (rc) {
237 return rc;
238 }
239
240 if (!(tmp & STATUS_POR)) {
241 /*
242 * Status.POR bit is set to 1 when MAX17262 detects that
243 * a software or hardware POR event has occurred and
244 * therefore a custom configuration needs to be set...
245 * If POR event did not happen (Status.POR == 0), skip
246 * init and continue with measurements.
247 */
248 LOG_DBG("No POR event detected - skip device configuration");
249 return 0;
250 }
251 LOG_DBG("POR detected, setting custom device configuration...");
252
253 /** STEP 1 */
254 rc = max17262_reg_read(dev, FSTAT, &tmp);
255 if (rc) {
256 return rc;
257 }
258
259 /* Do not continue until FSTAT.DNR bit is cleared */
260 while (tmp & FSTAT_DNR) {
261 k_sleep(K_MSEC(10));
262 rc = max17262_reg_read(dev, FSTAT, &tmp);
263 if (rc) {
264 return rc;
265 }
266 }
267
268 /** STEP 2 */
269 /* Store original HibCFG value */
270 rc = max17262_reg_read(dev, HIBCFG, &hibcfg);
271 if (rc) {
272 return rc;
273 }
274
275 /* Exit Hibernate Mode step 1 */
276 rc = max17262_reg_write(dev, SOFT_WAKEUP, 0x0090);
277 if (rc) {
278 return rc;
279 }
280
281 /* Exit Hibernate Mode step 2 */
282 rc = max17262_reg_write(dev, HIBCFG, 0x0000);
283 if (rc) {
284 return rc;
285 }
286
287 /* Exit Hibernate Mode step 3 */
288 rc = max17262_reg_write(dev, SOFT_WAKEUP, 0x0000);
289 if (rc) {
290 return rc;
291 }
292
293 /** STEP 2.1 --> OPTION 1 EZ Config (No INI file is needed) */
294 /* Write DesignCap */
295 rc = max17262_reg_write(dev, DESIGN_CAP, config->design_cap);
296 if (rc) {
297 return rc;
298 }
299
300 /* Write IChgTerm */
301 rc = max17262_reg_write(dev, ICHG_TERM, config->desired_charging_current);
302 if (rc) {
303 return rc;
304 }
305
306 /* Write VEmpty */
307 rc = max17262_reg_write(dev, VEMPTY,
308 ((config->empty_voltage / 10) << 7) |
309 ((config->recovery_voltage / 40) & 0x7F));
310 if (rc) {
311 return rc;
312 }
313
314 /* Write ModelCFG */
315 if (config->charge_voltage > 4275) {
316 rc = max17262_reg_write(dev, MODELCFG, 0x8400);
317 } else {
318 rc = max17262_reg_write(dev, MODELCFG, 0x8000);
319 }
320
321 if (rc) {
322 return rc;
323 }
324
325 /*
326 * Read ModelCFG.Refresh (highest bit),
327 * proceed to Step 3 when ModelCFG.Refresh == 0
328 */
329 rc = max17262_reg_read(dev, MODELCFG, &tmp);
330 if (rc) {
331 return rc;
332 }
333
334 /* Do not continue until ModelCFG.Refresh == 0 */
335 while (tmp & MODELCFG_REFRESH) {
336 k_sleep(K_MSEC(10));
337 rc = max17262_reg_read(dev, MODELCFG, &tmp);
338 if (rc) {
339 return rc;
340 }
341 }
342
343 /* Restore Original HibCFG value */
344 rc = max17262_reg_write(dev, HIBCFG, hibcfg);
345 if (rc) {
346 return rc;
347 }
348
349 /** STEP 3 */
350 /* Read Status register */
351 rc = max17262_reg_read(dev, STATUS, &tmp);
352 if (rc) {
353 return rc;
354 }
355
356 /* Clear PowerOnReset bit */
357 tmp &= ~STATUS_POR;
358 rc = max17262_reg_write(dev, STATUS, tmp);
359 if (rc) {
360 return rc;
361 }
362
363 return 0;
364 }
365
366 static DEVICE_API(sensor, max17262_battery_driver_api) = {
367 .sample_fetch = max17262_sample_fetch,
368 .channel_get = max17262_channel_get,
369 };
370
371 #define MAX17262_INIT(n) \
372 static struct max17262_data max17262_data_##n; \
373 \
374 static const struct max17262_config max17262_config_##n = { \
375 .i2c = I2C_DT_SPEC_INST_GET(n), \
376 .design_voltage = DT_INST_PROP(n, design_voltage), \
377 .desired_voltage = DT_INST_PROP(n, desired_voltage), \
378 .desired_charging_current = DT_INST_PROP(n, desired_charging_current), \
379 .design_cap = DT_INST_PROP(n, design_cap), \
380 .empty_voltage = DT_INST_PROP(n, empty_voltage), \
381 .recovery_voltage = DT_INST_PROP(n, recovery_voltage), \
382 .charge_voltage = DT_INST_PROP(n, charge_voltage), \
383 }; \
384 \
385 SENSOR_DEVICE_DT_INST_DEFINE(n, &max17262_gauge_init, NULL, &max17262_data_##n, \
386 &max17262_config_##n, POST_KERNEL, \
387 CONFIG_SENSOR_INIT_PRIORITY, &max17262_battery_driver_api);
388
389 DT_INST_FOREACH_STATUS_OKAY(MAX17262_INIT)
390