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