1 /*
2  * Copyright 2021 Grinn
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ti_ina237
8 
9 #include "ina237.h"
10 #include "ina23x_common.h"
11 
12 #include <zephyr/logging/log.h>
13 #include <zephyr/drivers/sensor.h>
14 #include <zephyr/dt-bindings/sensor/ina237.h>
15 #include <zephyr/sys/byteorder.h>
16 
17 LOG_MODULE_REGISTER(INA237, CONFIG_SENSOR_LOG_LEVEL);
18 
19 /** @brief Calibration scaling value (scaled by 10^-5) */
20 #define INA237_CAL_SCALING 8192ULL
21 
22 /** @brief The LSB value for the bus voltage register, in microvolts/LSB. */
23 #define INA237_BUS_VOLTAGE_TO_uV(x) ((x) * 3125U)
24 
25 /** @brief Power scaling (need factor of 0.2) */
26 #define INA237_POWER_TO_uW(x) ((x) / 5ULL)
27 
28 /**
29  * @brief Scale die temperture from 0.125 degC/bit to micro-degrees C
30  *  Note that the bottom 4 bits are reserved and are always zero.
31  */
32 #define INA237_DIETEMP_TO_uDegC(x) (((x) >> 4) * 125000)
33 
micro_s32_to_sensor_value(struct sensor_value * val,int32_t value_microX)34 static void micro_s32_to_sensor_value(struct sensor_value *val, int32_t value_microX)
35 {
36 	val->val1 = value_microX / 1000000L;
37 	val->val2 = value_microX % 1000000L;
38 }
39 
micro_u64_to_sensor_value(struct sensor_value * val,uint64_t value_microX)40 static void micro_u64_to_sensor_value(struct sensor_value *val, uint64_t value_microX)
41 {
42 	val->val1 = value_microX / 1000000U;
43 	val->val2 = value_microX % 1000000U;
44 }
45 
ina237_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)46 static int ina237_channel_get(const struct device *dev, enum sensor_channel chan,
47 			      struct sensor_value *val)
48 {
49 	const struct ina237_data *data = dev->data;
50 	const struct ina237_config *config = dev->config;
51 
52 	switch (chan) {
53 	case SENSOR_CHAN_VOLTAGE:
54 		micro_s32_to_sensor_value(val, INA237_BUS_VOLTAGE_TO_uV(data->bus_voltage));
55 		break;
56 
57 	case SENSOR_CHAN_CURRENT:
58 		/* see datasheet "Current and Power calculations" section */
59 		micro_s32_to_sensor_value(val, data->current * config->current_lsb);
60 		break;
61 
62 	case SENSOR_CHAN_POWER:
63 		/* power in uW is power_reg * current_lsb * 0.2 */
64 		micro_u64_to_sensor_value(val,
65 			INA237_POWER_TO_uW((uint64_t)data->power * config->current_lsb));
66 		break;
67 
68 #ifdef CONFIG_INA237_VSHUNT
69 	case SENSOR_CHAN_VSHUNT:
70 		if (config->config & INA237_CFG_HIGH_PRECISION) {
71 			/* high-resolution mode - 1.25 uV/bit, sensor value is in mV */
72 			micro_s32_to_sensor_value(val, data->shunt_voltage * 1250);
73 		} else {
74 			/* standard-resolution - 5 uV/bit -> nano-volts */
75 			micro_s32_to_sensor_value(val, data->shunt_voltage * 5000);
76 		}
77 		break;
78 #endif /* CONFIG_INA237_VSHUNT */
79 
80 	case SENSOR_CHAN_DIE_TEMP:
81 		micro_s32_to_sensor_value(val, INA237_DIETEMP_TO_uDegC(data->die_temp));
82 		break;
83 
84 	default:
85 		return -ENOTSUP;
86 	}
87 
88 	return 0;
89 }
90 
91 /**
92  * @brief sensor operation mode check
93  *
94  * @retval true if set or false if not set
95  */
ina237_is_triggered_mode_set(const struct device * dev)96 static bool ina237_is_triggered_mode_set(const struct device *dev)
97 {
98 	const struct ina237_config *config = dev->config;
99 
100 	uint8_t mode = (config->adc_config & GENMASK(15, 12)) >> 12;
101 
102 	switch (mode) {
103 	case INA237_OPER_MODE_BUS_VOLTAGE_TRIG:
104 	case INA237_OPER_MODE_SHUNT_VOLTAGE_TRIG:
105 	case INA237_OPER_MODE_SHUNT_BUS_VOLTAGE_TRIG:
106 	case INA237_OPER_MODE_TEMP_TRIG:
107 	case INA237_OPER_MODE_TEMP_BUS_VOLTAGE_TRIG:
108 	case INA237_OPER_MODE_TEMP_SHUNT_VOLTAGE_TRIG:
109 	case INA237_OPER_MODE_BUS_SHUNT_VOLTAGE_TEMP_TRIG:
110 		return true;
111 	default:
112 		return false;
113 	}
114 }
115 
116 /**
117  * @brief request for one shot measurement
118  *
119  * @retval 0 for success
120  * @retval negative errno code on fail
121  */
ina237_trigg_one_shot_request(const struct device * dev)122 static int ina237_trigg_one_shot_request(const struct device *dev)
123 {
124 	const struct ina237_config *config = dev->config;
125 	int ret;
126 
127 	ret = ina23x_reg_write(&config->bus, INA237_REG_ADC_CONFIG, config->adc_config);
128 	if (ret < 0) {
129 		LOG_ERR("Failed to write ADC configuration register!");
130 		return ret;
131 	}
132 
133 	return 0;
134 }
135 
136 /**
137  * @brief sensor data read
138  *
139  * @retval 0 for success
140  * @retval -EIO in case of input / output error
141  */
ina237_read_data(const struct device * dev)142 static int ina237_read_data(const struct device *dev)
143 {
144 	struct ina237_data *data = dev->data;
145 	const struct ina237_config *config = dev->config;
146 	int ret;
147 
148 	if ((data->chan == SENSOR_CHAN_ALL) || (data->chan == SENSOR_CHAN_VOLTAGE)) {
149 		ret = ina23x_reg_read_16(&config->bus, INA237_REG_BUS_VOLT, &data->bus_voltage);
150 		if (ret < 0) {
151 			LOG_ERR("Failed to read bus voltage");
152 			return ret;
153 		}
154 	}
155 
156 	if ((data->chan == SENSOR_CHAN_ALL) || (data->chan == SENSOR_CHAN_CURRENT)) {
157 		ret = ina23x_reg_read_16(&config->bus, INA237_REG_CURRENT, &data->current);
158 		if (ret < 0) {
159 			LOG_ERR("Failed to read current");
160 			return ret;
161 		}
162 	}
163 
164 	if ((data->chan == SENSOR_CHAN_ALL) || (data->chan == SENSOR_CHAN_POWER)) {
165 		ret = ina23x_reg_read_24(&config->bus, INA237_REG_POWER, &data->power);
166 		if (ret < 0) {
167 			LOG_ERR("Failed to read power");
168 			return ret;
169 		}
170 	}
171 
172 	if ((data->chan == SENSOR_CHAN_ALL) || (data->chan == SENSOR_CHAN_DIE_TEMP)) {
173 		ret = ina23x_reg_read_16(&config->bus, INA237_REG_DIETEMP, &data->die_temp);
174 		if (ret < 0) {
175 			LOG_ERR("Failed to read temperature");
176 			return ret;
177 		}
178 	}
179 
180 #ifdef CONFIG_INA237_VSHUNT
181 	if ((data->chan == SENSOR_CHAN_ALL) || (data->chan == SENSOR_CHAN_VSHUNT)) {
182 		ret = ina23x_reg_read_16(&config->bus, INA237_REG_SHUNT_VOLT, &data->shunt_voltage);
183 		if (ret < 0) {
184 			LOG_ERR("Failed to read shunt voltage");
185 			return ret;
186 		}
187 	}
188 #endif /* CONFIG_INA237_VSHUNT */
189 
190 	return 0;
191 }
192 
193 /**
194  * @brief sensor sample fetch
195  *
196  * @retval 0 for success
197  * @retval negative errno code on fail
198  */
ina237_sample_fetch(const struct device * dev,enum sensor_channel chan)199 static int ina237_sample_fetch(const struct device *dev, enum sensor_channel chan)
200 {
201 	struct ina237_data *data = dev->data;
202 
203 	if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_VOLTAGE && chan != SENSOR_CHAN_CURRENT &&
204 		chan != SENSOR_CHAN_POWER &&
205 #ifdef CONFIG_INA237_VSHUNT
206 		chan != SENSOR_CHAN_VSHUNT &&
207 #endif /* CONFIG_INA237_VSHUNT */
208 		chan != SENSOR_CHAN_DIE_TEMP) {
209 		return -ENOTSUP;
210 	}
211 
212 	data->chan = chan;
213 
214 	if (ina237_is_triggered_mode_set(dev)) {
215 		return ina237_trigg_one_shot_request(dev);
216 	} else {
217 		return ina237_read_data(dev);
218 	}
219 }
220 
ina237_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)221 static int ina237_attr_set(const struct device *dev, enum sensor_channel chan,
222 			   enum sensor_attribute attr, const struct sensor_value *val)
223 {
224 	const struct ina237_config *config = dev->config;
225 	uint16_t data = val->val1;
226 
227 	switch (attr) {
228 	case SENSOR_ATTR_CONFIGURATION:
229 		return ina23x_reg_write(&config->bus, INA237_REG_CONFIG, data);
230 	case SENSOR_ATTR_CALIBRATION:
231 		return ina23x_reg_write(&config->bus, INA237_REG_CALIB, data);
232 	default:
233 		LOG_ERR("INA237 attribute not supported.");
234 		return -ENOTSUP;
235 	}
236 }
237 
ina237_attr_get(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,struct sensor_value * val)238 static int ina237_attr_get(const struct device *dev, enum sensor_channel chan,
239 			   enum sensor_attribute attr, struct sensor_value *val)
240 {
241 	const struct ina237_config *config = dev->config;
242 	uint16_t data;
243 	int ret;
244 
245 	switch (attr) {
246 	case SENSOR_ATTR_CONFIGURATION:
247 		ret = ina23x_reg_read_16(&config->bus, INA237_REG_CONFIG, &data);
248 		if (ret < 0) {
249 			return ret;
250 		}
251 		break;
252 	case SENSOR_ATTR_CALIBRATION:
253 		ret = ina23x_reg_read_16(&config->bus, INA237_REG_CALIB, &data);
254 		if (ret < 0) {
255 			return ret;
256 		}
257 		break;
258 	default:
259 		LOG_ERR("INA237 attribute not supported.");
260 		return -ENOTSUP;
261 	}
262 
263 	val->val1 = data;
264 	val->val2 = 0;
265 
266 	return 0;
267 }
268 
ina237_calibrate(const struct device * dev)269 static int ina237_calibrate(const struct device *dev)
270 {
271 	const struct ina237_config *config = dev->config;
272 	int ret;
273 
274 	/* see datasheet "Current and Power calculations" section */
275 	ret = ina23x_reg_write(&config->bus, INA237_REG_CALIB, config->cal);
276 	if (ret < 0) {
277 		return ret;
278 	}
279 
280 	return 0;
281 }
282 
ina237_trigger_work_handler(struct k_work * work)283 static void ina237_trigger_work_handler(struct k_work *work)
284 {
285 	struct ina23x_trigger *trigg = CONTAINER_OF(work, struct ina23x_trigger, conversion_work);
286 	struct ina237_data *data = CONTAINER_OF(trigg, struct ina237_data, trigger);
287 	const struct ina237_config *config = data->dev->config;
288 	int ret;
289 	uint16_t reg_alert;
290 
291 	/* Read reg alert to clear alerts */
292 	ret = ina23x_reg_read_16(&config->bus, INA237_REG_ALERT, &reg_alert);
293 	if (ret < 0) {
294 		LOG_ERR("Failed to read alert register!");
295 		return;
296 	}
297 
298 	ret = ina237_read_data(data->dev);
299 	if (ret < 0) {
300 		LOG_WRN("Unable to read data, ret %d", ret);
301 	}
302 
303 	if (data->trigger.handler_alert) {
304 		data->trigger.handler_alert(data->dev, data->trigger.trig_alert);
305 	}
306 }
307 
ina237_init(const struct device * dev)308 static int ina237_init(const struct device *dev)
309 {
310 	struct ina237_data *data = dev->data;
311 	const struct ina237_config *config = dev->config;
312 	uint16_t id;
313 	int ret;
314 
315 	if (!device_is_ready(config->bus.bus)) {
316 		LOG_ERR("I2C bus %s is not ready", config->bus.bus->name);
317 		return -ENODEV;
318 	}
319 
320 	data->dev = dev;
321 
322 	ret = ina23x_reg_read_16(&config->bus, INA237_REG_MANUFACTURER_ID, &id);
323 	if (ret < 0) {
324 		LOG_ERR("Failed to read manufacturer register!");
325 		return ret;
326 	}
327 
328 	if (id != INA237_MANUFACTURER_ID) {
329 		LOG_ERR("Manufacturer ID doesn't match!");
330 		return -ENODEV;
331 	}
332 
333 	ret = ina23x_reg_write(&config->bus, INA237_REG_ADC_CONFIG, config->adc_config);
334 	if (ret < 0) {
335 		LOG_ERR("Failed to write ADC configuration register!");
336 		return ret;
337 	}
338 
339 	ret = ina23x_reg_write(&config->bus, INA237_REG_CONFIG, config->config);
340 	if (ret < 0) {
341 		LOG_ERR("Failed to write configuration register!");
342 		return ret;
343 	}
344 
345 	ret = ina237_calibrate(dev);
346 	if (ret < 0) {
347 		LOG_ERR("Failed to write calibration register!");
348 		return ret;
349 	}
350 
351 	if (ina237_is_triggered_mode_set(dev)) {
352 		if ((config->alert_config & GENMASK(15, 14)) != GENMASK(15, 14)) {
353 			LOG_ERR("ALATCH and CNVR bits must be enabled in triggered mode!");
354 			return -ENODEV;
355 		}
356 
357 		k_work_init(&data->trigger.conversion_work, ina237_trigger_work_handler);
358 
359 		ret = ina23x_trigger_mode_init(&data->trigger, &config->alert_gpio);
360 		if (ret < 0) {
361 			LOG_ERR("Failed to init trigger mode");
362 			return ret;
363 		}
364 
365 		ret = ina23x_reg_write(&config->bus, INA237_REG_ALERT, config->alert_config);
366 		if (ret < 0) {
367 			LOG_ERR("Failed to write alert configuration register!");
368 			return ret;
369 		}
370 	}
371 
372 	return 0;
373 }
374 
ina237_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)375 static int ina237_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
376 			      sensor_trigger_handler_t handler)
377 {
378 	ARG_UNUSED(trig);
379 	struct ina237_data *ina237 = dev->data;
380 
381 	if (!ina237_is_triggered_mode_set(dev)) {
382 		return -ENOTSUP;
383 	}
384 
385 	ina237->trigger.handler_alert = handler;
386 	ina237->trigger.trig_alert = trig;
387 
388 	return 0;
389 }
390 
391 static const struct sensor_driver_api ina237_driver_api = {
392 	.attr_set = ina237_attr_set,
393 	.attr_get = ina237_attr_get,
394 	.trigger_set = ina237_trigger_set,
395 	.sample_fetch = ina237_sample_fetch,
396 	.channel_get = ina237_channel_get,
397 };
398 
399 /* Shunt calibration must be muliplied by 4 if high-prevision mode is selected */
400 #define CAL_PRECISION_MULTIPLIER(config) \
401 	(((config & INA237_CFG_HIGH_PRECISION) >> 4) * 3 + 1)
402 
403 #define INA237_DRIVER_INIT(inst)                                                                   \
404 	static struct ina237_data ina237_data_##inst;                                              \
405 	static const struct ina237_config ina237_config_##inst = {                                 \
406 		.bus = I2C_DT_SPEC_INST_GET(inst),                                                 \
407 		.config = DT_INST_PROP(inst, config),                                              \
408 		.adc_config = DT_INST_PROP(inst, adc_config) |                                     \
409 			(DT_INST_ENUM_IDX(inst, adc_mode) << 12) |                             \
410 			(DT_INST_ENUM_IDX(inst, vbus_conversion_time_us) << 9) |                 \
411 			(DT_INST_ENUM_IDX(inst, vshunt_conversion_time_us) << 6) |               \
412 			(DT_INST_ENUM_IDX(inst, temp_conversion_time_us) << 3) |                 \
413 			DT_INST_ENUM_IDX(inst, avg_count),                                        \
414 		.current_lsb = DT_INST_PROP(inst, current_lsb_microamps),                          \
415 		.cal = CAL_PRECISION_MULTIPLIER(DT_INST_PROP(inst, config)) *                      \
416 			INA237_CAL_SCALING * DT_INST_PROP(inst, current_lsb_microamps) *       \
417 			DT_INST_PROP(inst, rshunt_micro_ohms) / 10000000ULL,                   \
418 		.alert_config = DT_INST_PROP_OR(inst, alert_config, 0x01),                         \
419 		.alert_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, alert_gpios, {0}),                    \
420 	};                                                                                         \
421 	SENSOR_DEVICE_DT_INST_DEFINE(inst, &ina237_init, NULL, &ina237_data_##inst,                \
422 				     &ina237_config_##inst, POST_KERNEL,                           \
423 				     CONFIG_SENSOR_INIT_PRIORITY, &ina237_driver_api);             \
424 
425 DT_INST_FOREACH_STATUS_OKAY(INA237_DRIVER_INIT)
426