1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 2023 Carl Zeiss Meditec AG
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT adi_adltc2990
8
9 #include <zephyr/sys/util.h>
10 #include <zephyr/drivers/i2c.h>
11
12 #include "adltc2990_reg.h"
13 #include "adltc2990.h"
14
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(adltc2990, CONFIG_SENSOR_LOG_LEVEL);
17
adltc2990_get_v1_v2_measurement_modes(uint8_t mode_4_3,uint8_t mode_2_0)18 static enum adltc2990_monitoring_type adltc2990_get_v1_v2_measurement_modes(uint8_t mode_4_3,
19 uint8_t mode_2_0)
20 {
21 if (mode_2_0 > ADLTC2990_MODE_2_0_MAX_VALUE || mode_4_3 > ADLTC2990_MODE_4_3_MAX_VALUE) {
22 LOG_ERR("Invalid Measurement Mode");
23 return -EINVAL;
24 }
25 if (mode_4_3 == ADLTC2990_MEASURE_INTERNAL_TEMPERATURE_ONLY ||
26 mode_4_3 == ADLTC2990_MEASURE_PINS_V3_V4_ONLY) {
27 return NOTHING;
28 }
29
30 enum adltc2990_monitoring_type type = NOTHING;
31
32 switch (mode_2_0) {
33 case ADLTC2990_MODE_V1_V2_TR2:
34 case ADLTC2990_MODE_V1_V2_V3_V4: {
35 type = VOLTAGE_SINGLEENDED;
36 break;
37 }
38 case ADLTC2990_MODE_V1_MINUS_V2_TR2:
39 case ADLTC2990_MODE_V1_MINUS_V2_V3_V4:
40 case ADLTC2990_MODE_V1_MINUS_V2_V3_MINUS_V4: {
41 type = VOLTAGE_DIFFERENTIAL;
42 break;
43 }
44 case ADLTC2990_MODE_TR1_V3_V4:
45 case ADLTC2990_MODE_TR1_V3_MINUS_V4: {
46 case ADLTC2990_MODE_TR1_TR2:
47 type = TEMPERATURE;
48 break;
49 }
50 default: {
51 break;
52 }
53 }
54 return type;
55 }
56
adltc2990_get_v3_v4_measurement_modes(uint8_t mode_4_3,uint8_t mode_2_0)57 static enum adltc2990_monitoring_type adltc2990_get_v3_v4_measurement_modes(uint8_t mode_4_3,
58 uint8_t mode_2_0)
59 {
60 if (mode_2_0 > ADLTC2990_MODE_2_0_MAX_VALUE || mode_4_3 > ADLTC2990_MODE_4_3_MAX_VALUE) {
61 LOG_ERR("Invalid Measurement Mode");
62 return -EINVAL;
63 }
64 if (mode_4_3 == ADLTC2990_MEASURE_INTERNAL_TEMPERATURE_ONLY ||
65 mode_4_3 == ADLTC2990_MEASURE_PINS_V1_V2_ONLY) {
66 return NOTHING;
67 }
68
69 enum adltc2990_monitoring_type type = NOTHING;
70
71 switch (mode_2_0) {
72 case ADLTC2990_MODE_V1_V2_TR2:
73 case ADLTC2990_MODE_V1_MINUS_V2_TR2:
74 case ADLTC2990_MODE_TR1_TR2: {
75 type = TEMPERATURE;
76 break;
77 }
78
79 case ADLTC2990_MODE_V1_MINUS_V2_V3_V4:
80 case ADLTC2990_MODE_TR1_V3_V4:
81 case ADLTC2990_MODE_V1_V2_V3_V4: {
82 type = VOLTAGE_SINGLEENDED;
83 break;
84 }
85 case ADLTC2990_MODE_TR1_V3_MINUS_V4:
86 case ADLTC2990_MODE_V1_MINUS_V2_V3_MINUS_V4: {
87 type = VOLTAGE_DIFFERENTIAL;
88 break;
89 }
90 default: {
91 break;
92 }
93 }
94 return type;
95 }
96
adltc2990_is_busy(const struct device * dev)97 static bool adltc2990_is_busy(const struct device *dev)
98 {
99 const struct adltc2990_config *cfg = dev->config;
100 uint8_t status_reg = 0;
101
102 i2c_reg_read_byte_dt(&cfg->bus, ADLTC2990_REG_STATUS, &status_reg);
103 return status_reg & BIT(0);
104 }
105
adltc2990_get_v1_v2_val(const struct device * dev,struct sensor_value * val,uint8_t num_values,uint8_t * const offset_index)106 static void adltc2990_get_v1_v2_val(const struct device *dev, struct sensor_value *val,
107 uint8_t num_values, uint8_t *const offset_index)
108 {
109 struct adltc2990_data *data = dev->data;
110
111 for (uint8_t index = 0; index < num_values; index++) {
112 val[index].val1 = data->pins_v1_v2_values[index] / 1000000;
113 val[index].val2 = data->pins_v1_v2_values[index] % 1000000;
114 *offset_index = index + 1;
115 }
116 }
117
adltc2990_get_v3_v4_val(const struct device * dev,struct sensor_value * val,uint8_t num_values,uint8_t const * const offset)118 static void adltc2990_get_v3_v4_val(const struct device *dev, struct sensor_value *val,
119 uint8_t num_values, uint8_t const *const offset)
120 {
121 struct adltc2990_data *data = dev->data;
122
123 uint8_t offset_index = *offset;
124
125 for (uint8_t index = 0; index < num_values; index++) {
126 val[index + offset_index].val1 = data->pins_v3_v4_values[index] / 1000000;
127 val[index + offset_index].val2 = data->pins_v3_v4_values[index] % 1000000;
128 }
129 }
130
adltc2990_trigger_measurement(const struct device * dev)131 static int adltc2990_trigger_measurement(const struct device *dev)
132 {
133 const struct adltc2990_config *cfg = dev->config;
134
135 return i2c_reg_write_byte_dt(&cfg->bus, ADLTC2990_REG_TRIGGER, 0x1);
136 }
137
adltc2990_get_property_value(const struct device * dev,enum adltc2990_monitoring_type type,enum adltc2990_monitor_pins pin)138 static int32_t adltc2990_get_property_value(const struct device *dev,
139 enum adltc2990_monitoring_type type,
140 enum adltc2990_monitor_pins pin)
141 {
142 const struct adltc2990_config *cfg = dev->config;
143
144 uint8_t msb_value = 0, lsb_value = 0;
145 uint8_t msb_address, lsb_address;
146
147 switch (pin) {
148 case V1: {
149 msb_address = ADLTC2990_REG_V1_MSB;
150 lsb_address = ADLTC2990_REG_V1_LSB;
151 break;
152 }
153 case V2: {
154 msb_address = ADLTC2990_REG_V2_MSB;
155 lsb_address = ADLTC2990_REG_V2_LSB;
156 break;
157 }
158 case V3: {
159 msb_address = ADLTC2990_REG_V3_MSB;
160 lsb_address = ADLTC2990_REG_V3_LSB;
161 break;
162 }
163 case V4: {
164 msb_address = ADLTC2990_REG_V4_MSB;
165 lsb_address = ADLTC2990_REG_V4_LSB;
166 break;
167 }
168 case INTERNAL_TEMPERATURE: {
169 msb_address = ADLTC2990_REG_INTERNAL_TEMP_MSB;
170 lsb_address = ADLTC2990_REG_INTERNAL_TEMP_LSB;
171 break;
172 }
173 case SUPPLY_VOLTAGE: {
174 msb_address = ADLTC2990_REG_VCC_MSB;
175 lsb_address = ADLTC2990_REG_VCC_LSB;
176 break;
177 }
178 default: {
179 LOG_ERR("Trying to access illegal register");
180 return -EINVAL;
181 }
182 }
183
184 i2c_reg_read_byte_dt(&cfg->bus, msb_address, &msb_value);
185 i2c_reg_read_byte_dt(&cfg->bus, lsb_address, &lsb_value);
186 uint16_t conversion_factor;
187 uint8_t negative_bit_index;
188
189 if (type == VOLTAGE_SINGLEENDED) {
190 conversion_factor = ADLTC2990_VOLTAGE_SINGLEENDED_CONVERSION_FACTOR;
191 negative_bit_index = 14;
192 } else if (type == VOLTAGE_DIFFERENTIAL) {
193 conversion_factor = ADLTC2990_VOLTAGE_DIFFERENTIAL_CONVERSION_FACTOR;
194 negative_bit_index = 14;
195 } else if (type == TEMPERATURE) {
196 conversion_factor = ADLTC2990_TEMPERATURE_CONVERSION_FACTOR;
197 negative_bit_index = 12;
198 } else {
199 LOG_ERR("unknown type");
200 return -EINVAL;
201 }
202
203 int16_t value = (msb_value<<8)+lsb_value;
204
205 int32_t voltage_value = (value << (31-negative_bit_index))>>(31-negative_bit_index);
206
207 return (voltage_value * conversion_factor) / 100;
208 }
209
adltc2990_init(const struct device * dev)210 static int adltc2990_init(const struct device *dev)
211 {
212 const struct adltc2990_config *cfg = dev->config;
213
214 if (!i2c_is_ready_dt(&cfg->bus)) {
215 LOG_ERR("I2C bus %s not ready", cfg->bus.bus->name);
216 return -ENODEV;
217 }
218
219 const uint8_t ctrl_reg_setting = cfg->temp_format << 7 | cfg->acq_format << 6 | 0 << 5 |
220 cfg->measurement_mode[1] << 3 | cfg->measurement_mode[0];
221
222 LOG_DBG("Setting Control Register to: 0x%x", ctrl_reg_setting);
223 int err = i2c_reg_write_byte_dt(&cfg->bus, ADLTC2990_REG_CONTROL, ctrl_reg_setting);
224
225 if (err < 0) {
226 LOG_ERR("configuring for single bus failed: %d", err);
227 return err;
228 }
229 LOG_INF("Initializing ADLTC2990 with name %s", dev->name);
230 return 0;
231 }
232
adltc2990_sample_fetch(const struct device * dev,enum sensor_channel chan)233 static int adltc2990_sample_fetch(const struct device *dev, enum sensor_channel chan)
234 {
235 struct adltc2990_data *data = dev->data;
236 const struct adltc2990_config *cfg = dev->config;
237 enum adltc2990_monitoring_type mode_v1_v2 = adltc2990_get_v1_v2_measurement_modes(
238 cfg->measurement_mode[1], cfg->measurement_mode[0]);
239 enum adltc2990_monitoring_type mode_v3_v4 = adltc2990_get_v3_v4_measurement_modes(
240 cfg->measurement_mode[1], cfg->measurement_mode[0]);
241
242 float voltage_divider_ratio;
243
244 switch (chan) {
245 case SENSOR_CHAN_DIE_TEMP: {
246 data->internal_temperature =
247 adltc2990_get_property_value(dev, TEMPERATURE, INTERNAL_TEMPERATURE);
248 break;
249 }
250 case SENSOR_CHAN_CURRENT: {
251 if (!(mode_v1_v2 == VOLTAGE_DIFFERENTIAL || mode_v3_v4 == VOLTAGE_DIFFERENTIAL)) {
252 LOG_ERR("Sensor is not configured to measure Current");
253 return -EINVAL;
254 }
255 if (mode_v1_v2 == VOLTAGE_DIFFERENTIAL) {
256 data->pins_v1_v2_values[0] =
257 (adltc2990_get_property_value(dev, VOLTAGE_DIFFERENTIAL, V1) *
258 ADLTC2990_MICROOHM_CONVERSION_FACTOR) /
259 cfg->pins_v1_v2.pins_current_resistor;
260 }
261 if (mode_v3_v4 == VOLTAGE_DIFFERENTIAL) {
262 data->pins_v3_v4_values[0] =
263 (adltc2990_get_property_value(dev, VOLTAGE_DIFFERENTIAL, V3) *
264 ADLTC2990_MICROOHM_CONVERSION_FACTOR) /
265 cfg->pins_v3_v4.pins_current_resistor;
266 }
267 break;
268 }
269 case SENSOR_CHAN_VOLTAGE: {
270 data->supply_voltage =
271 adltc2990_get_property_value(dev, VOLTAGE_SINGLEENDED, SUPPLY_VOLTAGE) +
272 2500000;
273
274 if (mode_v1_v2 == VOLTAGE_DIFFERENTIAL) {
275 data->pins_v1_v2_values[0] =
276 adltc2990_get_property_value(dev, VOLTAGE_DIFFERENTIAL, V1);
277 } else if (mode_v1_v2 == VOLTAGE_SINGLEENDED) {
278 uint32_t v1_r1 =
279 cfg->pins_v1_v2.voltage_divider_resistors.v1_r1_r2[0];
280 uint32_t v1_r2 =
281 cfg->pins_v1_v2.voltage_divider_resistors.v2_r1_r2[1];
282 voltage_divider_ratio = DIV_ROUND_CLOSEST(v1_r1 + v1_r2, v1_r2);
283 data->pins_v1_v2_values[0] =
284 adltc2990_get_property_value(dev, VOLTAGE_SINGLEENDED, V1) *
285 voltage_divider_ratio;
286
287 uint32_t v2_r1 =
288 cfg->pins_v1_v2.voltage_divider_resistors.v2_r1_r2[0];
289 uint32_t v2_r2 =
290 cfg->pins_v1_v2.voltage_divider_resistors.v2_r1_r2[1];
291 voltage_divider_ratio = DIV_ROUND_CLOSEST(v2_r1 + v2_r2, v2_r2);
292 data->pins_v1_v2_values[1] =
293 adltc2990_get_property_value(dev, VOLTAGE_SINGLEENDED, V2) *
294 voltage_divider_ratio;
295 }
296
297 if (mode_v3_v4 == VOLTAGE_DIFFERENTIAL) {
298 data->pins_v3_v4_values[0] =
299 adltc2990_get_property_value(dev, VOLTAGE_DIFFERENTIAL, V3);
300 } else if (mode_v3_v4 == VOLTAGE_SINGLEENDED) {
301 uint32_t v3_r1 =
302 cfg->pins_v3_v4.voltage_divider_resistors.v3_r1_r2[0];
303 uint32_t v3_r2 =
304 cfg->pins_v3_v4.voltage_divider_resistors.v3_r1_r2[1];
305 voltage_divider_ratio = DIV_ROUND_CLOSEST(v3_r1 + v3_r2, v3_r2);
306 data->pins_v3_v4_values[0] =
307 adltc2990_get_property_value(dev, VOLTAGE_SINGLEENDED, V3) *
308 voltage_divider_ratio;
309
310 uint32_t v4_r1 =
311 cfg->pins_v3_v4.voltage_divider_resistors.v4_r1_r2[0];
312 uint32_t v4_r2 =
313 cfg->pins_v3_v4.voltage_divider_resistors.v4_r1_r2[1];
314 voltage_divider_ratio = DIV_ROUND_CLOSEST((v4_r1 + v4_r2), v4_r2);
315 data->pins_v3_v4_values[1] =
316 adltc2990_get_property_value(dev, VOLTAGE_SINGLEENDED, V4) *
317 voltage_divider_ratio;
318 }
319 break;
320 }
321 case SENSOR_CHAN_AMBIENT_TEMP: {
322 if (!(mode_v1_v2 == TEMPERATURE || mode_v3_v4 == TEMPERATURE)) {
323 LOG_ERR("Sensor is not configured to measure Ambient Temperature");
324 return -EINVAL;
325 }
326 if (mode_v1_v2 == TEMPERATURE) {
327 data->pins_v1_v2_values[0] =
328 adltc2990_get_property_value(dev, TEMPERATURE, V1);
329 }
330 if (mode_v3_v4 == TEMPERATURE) {
331 data->pins_v3_v4_values[1] =
332 adltc2990_get_property_value(dev, TEMPERATURE, V3);
333 }
334 break;
335 }
336 case SENSOR_CHAN_ALL: {
337 if (adltc2990_is_busy(dev)) {
338 LOG_INF("ADLTC2990 conversion ongoing");
339 return -EBUSY;
340 }
341 adltc2990_trigger_measurement(dev);
342 break;
343 }
344 default: {
345 LOG_ERR("does not measure channel: %d", chan);
346 return -ENOTSUP;
347 }
348 }
349
350 return 0;
351 }
352
adltc2990_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)353 static int adltc2990_channel_get(const struct device *dev, enum sensor_channel chan,
354 struct sensor_value *val)
355 {
356 if (val == NULL) {
357 LOG_ERR("Argument of type sensor_value* cannot be null ");
358 return -EINVAL;
359 }
360 struct adltc2990_data *data = dev->data;
361 const struct adltc2990_config *cfg = dev->config;
362 enum adltc2990_monitoring_type mode_v1_v2 = adltc2990_get_v1_v2_measurement_modes(
363 cfg->measurement_mode[1], cfg->measurement_mode[0]);
364 enum adltc2990_monitoring_type mode_v3_v4 = adltc2990_get_v3_v4_measurement_modes(
365 cfg->measurement_mode[1], cfg->measurement_mode[0]);
366
367 uint8_t offset_index = 0, num_values_v1_v2 = 0, num_values_v3_v4 = 0;
368
369 switch (chan) {
370 case SENSOR_CHAN_DIE_TEMP: {
371 val->val1 = data->internal_temperature / 10000;
372 val->val2 = data->internal_temperature % 10000;
373 LOG_DBG("Internal Temperature Value is:%d.%d", val->val1, val->val2);
374 break;
375 }
376 case SENSOR_CHAN_VOLTAGE: {
377 if (mode_v1_v2 == VOLTAGE_SINGLEENDED) {
378 LOG_DBG("Getting V1,V2");
379 num_values_v1_v2 = ADLTC2990_VOLTAGE_SINGLE_ENDED_VALUES;
380 } else if (mode_v1_v2 == VOLTAGE_DIFFERENTIAL) {
381 LOG_DBG("Getting V3-V4");
382 num_values_v1_v2 = ADLTC2990_VOLTAGE_DIFF_VALUES;
383 }
384 if (mode_v3_v4 == VOLTAGE_SINGLEENDED) {
385 LOG_DBG("Getting V3,V4");
386 num_values_v3_v4 = ADLTC2990_VOLTAGE_SINGLE_ENDED_VALUES;
387 } else if (mode_v3_v4 == VOLTAGE_DIFFERENTIAL) {
388 LOG_DBG("Getting V3-V4");
389 num_values_v3_v4 = ADLTC2990_VOLTAGE_DIFF_VALUES;
390 }
391 val[num_values_v1_v2 + num_values_v3_v4].val1 = data->supply_voltage / 1000000;
392 val[num_values_v1_v2 + num_values_v3_v4].val2 = data->supply_voltage % 1000000;
393 break;
394 }
395 case SENSOR_CHAN_CURRENT: {
396 if (!(mode_v1_v2 == VOLTAGE_DIFFERENTIAL || mode_v3_v4 == VOLTAGE_DIFFERENTIAL)) {
397 LOG_ERR("Sensor is not configured to measure Current");
398 return -EINVAL;
399 }
400 if (mode_v1_v2 == VOLTAGE_DIFFERENTIAL && mode_v3_v4 == VOLTAGE_DIFFERENTIAL) {
401 LOG_DBG("Getting I12 and I34");
402 num_values_v1_v2 = ADLTC2990_CURRENT_VALUES;
403 num_values_v3_v4 = ADLTC2990_CURRENT_VALUES;
404 } else if (mode_v1_v2 == VOLTAGE_DIFFERENTIAL) {
405 LOG_DBG("Getting I12");
406 num_values_v1_v2 = ADLTC2990_CURRENT_VALUES;
407 } else if (mode_v3_v4 == VOLTAGE_DIFFERENTIAL) {
408 LOG_DBG("Getting I34");
409 num_values_v3_v4 = ADLTC2990_CURRENT_VALUES;
410 }
411 break;
412 }
413 case SENSOR_CHAN_AMBIENT_TEMP: {
414 if (!(mode_v1_v2 == TEMPERATURE || mode_v3_v4 == TEMPERATURE)) {
415 LOG_ERR("Sensor is not configured to measure Ambient Temperature");
416 return -EINVAL;
417 }
418 if (mode_v1_v2 == TEMPERATURE && mode_v3_v4 == TEMPERATURE) {
419 LOG_DBG("Getting T12 and T34");
420 num_values_v1_v2 = ADLTC2990_TEMP_VALUES;
421 num_values_v3_v4 = ADLTC2990_TEMP_VALUES;
422 } else if (mode_v1_v2 == TEMPERATURE) {
423 LOG_DBG("Getting T12");
424 num_values_v1_v2 = ADLTC2990_TEMP_VALUES;
425 } else if (mode_v3_v4 == TEMPERATURE) {
426 LOG_DBG("Getting T34");
427 num_values_v3_v4 = ADLTC2990_TEMP_VALUES;
428 }
429 break;
430 }
431 default: {
432 return -ENOTSUP;
433 }
434 }
435
436 adltc2990_get_v1_v2_val(dev, val, num_values_v1_v2, &offset_index);
437 adltc2990_get_v3_v4_val(dev, val, num_values_v3_v4, &offset_index);
438 return 0;
439 }
440
441 static const struct sensor_driver_api adltc2990_driver_api = {
442 .sample_fetch = adltc2990_sample_fetch,
443 .channel_get = adltc2990_channel_get,
444 };
445
446 #define ADLTC2990_DEFINE(inst) \
447 static struct adltc2990_data adltc2990_data_##inst; \
448 static const struct adltc2990_config adltc2990_config_##inst = { \
449 .bus = I2C_DT_SPEC_INST_GET(inst), \
450 .temp_format = DT_INST_PROP(inst, temperature_format), \
451 .acq_format = DT_INST_PROP(inst, acquistion_format), \
452 .measurement_mode = DT_INST_PROP(inst, measurement_mode), \
453 .pins_v1_v2.pins_current_resistor = \
454 DT_INST_PROP_OR(inst, pins_v1_v2_current_resistor, 1), \
455 .pins_v1_v2.voltage_divider_resistors.v1_r1_r2 = \
456 DT_INST_PROP_OR(inst, pin_v1_voltage_divider_resistors, NULL), \
457 .pins_v1_v2.voltage_divider_resistors.v2_r1_r2 = \
458 DT_INST_PROP_OR(inst, pin_v2_voltage_divider_resistors, NULL), \
459 .pins_v3_v4.pins_current_resistor = \
460 DT_INST_PROP_OR(inst, pins_v3_v4_current_resistor, 1), \
461 .pins_v3_v4.voltage_divider_resistors.v3_r1_r2 = \
462 DT_INST_PROP_OR(inst, pin_v3_voltage_divider_resistors, NULL), \
463 .pins_v3_v4.voltage_divider_resistors.v4_r1_r2 = \
464 DT_INST_PROP_OR(inst, pin_v4_voltage_divider_resistors, NULL)}; \
465 \
466 SENSOR_DEVICE_DT_INST_DEFINE(inst, adltc2990_init, NULL, &adltc2990_data_##inst, \
467 &adltc2990_config_##inst, POST_KERNEL, \
468 CONFIG_SENSOR_INIT_PRIORITY, &adltc2990_driver_api);
469
470 DT_INST_FOREACH_STATUS_OKAY(ADLTC2990_DEFINE)
471