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, ®_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