1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #define DT_DRV_COMPAT nordic_npm2100_vbat
7
8 #include <zephyr/drivers/sensor.h>
9 #include <zephyr/drivers/mfd/npm2100.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/logging/log.h>
13 #include <zephyr/sys/linear_range.h>
14 #include <zephyr/drivers/sensor/npm2100_vbat.h>
15
16 LOG_MODULE_REGISTER(vbat_npm2100, CONFIG_SENSOR_LOG_LEVEL);
17
18 #define BOOST_START 0x20U
19 #define BOOST_OPER 0x24U
20 #define BOOST_DPSCOUNT 0x25U
21 #define BOOST_DPSLIMIT 0x26U
22 #define BOOST_DPSDUR 0x27U
23 #define BOOST_CTRLSET 0x2AU
24 #define BOOST_CTRLCLR 0x2BU
25 #define BOOST_VBATSEL 0x2EU
26 #define BOOST_VBATMINL 0x2FU
27 #define BOOST_VBATMINH 0x30U
28 #define BOOST_VOUTMIN 0x31U
29 #define BOOST_VOUTWRN 0x32U
30 #define BOOST_VOUTDPS 0x33U
31
32 #define ADC_TASKS_ADC 0x90U
33 #define ADC_CONFIG 0X91U
34 #define ADC_DELAY 0x92U
35 #define ADC_OFFSETCFG 0x93U
36 #define ADC_CTRLSET 0x94U
37 #define ADC_CTRLCLR 0x95U
38 #define ADC_RESULTS 0x96U
39 #define ADC_READVBAT 0x96U
40 #define ADC_READTEMP 0x97U
41 #define ADC_READDROOP 0x98U
42 #define ADC_READVOUT 0x99U
43 #define ADC_AVERAGE 0x9BU
44 #define ADC_OFFSETMEASURED 0x9FU
45
46 #define ADC_CONFIG_MODE_MASK 0x07U
47 #define ADC_CONFIG_MODE_INS_VBAT 0x00U
48 #define ADC_CONFIG_MODE_DEL_VBAT 0x01U
49 #define ADC_CONFIG_MODE_TEMP 0x02U
50 #define ADC_CONFIG_MODE_DROOP 0x03U
51 #define ADC_CONFIG_MODE_VOUT 0x04U
52 #define ADC_CONFIG_MODE_OFFSET 0x05U
53 #define ADC_CONFIG_AVG_MASK 0x38U
54
55 #define ADC_SAMPLE_TIME_US 100
56
57 #define VBAT_SCALING_OFFSET 0
58 #define VBAT_SCALING_MUL 3200000
59 #define VBAT_SCALING_DIV 256
60 #define VOUT_SCALING_OFFSET 1800000
61 #define VOUT_SCALING_MUL 1500000
62 #define VOUT_SCALING_DIV 256
63 #define TEMP_SCALING_OFFSET 389500000
64 #define TEMP_SCALING_MUL 2120000
65 #define TEMP_SCALING_DIV -1
66 #define DPS_SCALING_OFFSET 0
67 #define DPS_SCALING_MUL 1000000
68 #define DPS_SCALING_DIV 1
69
70 static const struct linear_range vbat_range = LINEAR_RANGE_INIT(650000, 50000, 0U, 50U);
71 static const struct linear_range vout_range = LINEAR_RANGE_INIT(1700000, 50000, 0U, 31U);
72 static const struct linear_range vdps_range = LINEAR_RANGE_INIT(1900000, 50000, 0U, 31U);
73 static const struct linear_range dpslim_range = LINEAR_RANGE_INIT(0, 1, 0U, 255U);
74 static const struct linear_range dpstimer_range = LINEAR_RANGE_INIT(0, 1, 0U, 3U);
75 static const struct linear_range oversample_range = LINEAR_RANGE_INIT(0, 1, 0U, 4U);
76 static const struct linear_range adcdelay_range = LINEAR_RANGE_INIT(5000, 4000, 0U, 255U);
77
78 struct npm2100_vbat_config {
79 struct i2c_dt_spec i2c;
80 struct sensor_value voutmin;
81 struct sensor_value vbatmin;
82 };
83
84 struct adc_config {
85 const enum sensor_channel chan;
86 const uint8_t result_reg;
87 uint8_t config;
88 uint8_t result;
89 bool enabled;
90 };
91
92 struct npm2100_vbat_data {
93 struct adc_config adc[4U];
94 uint8_t vbat_delay;
95 uint8_t dpsdur;
96 };
97
98 struct npm2100_attr_t {
99 enum sensor_channel chan;
100 enum sensor_attribute attr;
101 const struct linear_range *range;
102 uint8_t reg;
103 uint8_t reg_mask;
104 uint8_t ctrlsel_mask;
105 };
106
107 static const struct npm2100_attr_t npm2100_attr[] = {
108 {SENSOR_CHAN_GAUGE_VOLTAGE, SENSOR_ATTR_UPPER_THRESH, &vbat_range, BOOST_VBATMINH, 0xFF, 0},
109 {SENSOR_CHAN_GAUGE_VOLTAGE, SENSOR_ATTR_LOWER_THRESH, &vbat_range, BOOST_VBATMINL, 0xFF, 0},
110 {SENSOR_CHAN_VOLTAGE, SENSOR_ATTR_UPPER_THRESH, &vdps_range, BOOST_VOUTDPS, 0xFF, BIT(2)},
111 {SENSOR_CHAN_VOLTAGE, SENSOR_ATTR_LOWER_THRESH, &vout_range, BOOST_VOUTMIN, 0xFF, BIT(0)},
112 {SENSOR_CHAN_VOLTAGE, SENSOR_ATTR_ALERT, &vout_range, BOOST_VOUTWRN, 0xFF, BIT(1)},
113 {(enum sensor_channel)SENSOR_CHAN_NPM2100_DPS_COUNT, SENSOR_ATTR_UPPER_THRESH,
114 &dpslim_range, BOOST_DPSLIMIT, 0xFF, 0},
115 {(enum sensor_channel)SENSOR_CHAN_NPM2100_DPS_TIMER, SENSOR_ATTR_UPPER_THRESH,
116 &dpstimer_range, BOOST_OPER, 0x60, 0},
117 };
118
adc_cfg_get(const struct device * dev,enum sensor_channel chan)119 static struct adc_config *adc_cfg_get(const struct device *dev, enum sensor_channel chan)
120 {
121 struct npm2100_vbat_data *const data = dev->data;
122
123 for (int idx = 0; idx < ARRAY_SIZE(data->adc); idx++) {
124 if (data->adc[idx].chan == chan) {
125 return &data->adc[idx];
126 }
127 }
128
129 return NULL;
130 }
131
npm2100_vbat_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * valp)132 int npm2100_vbat_channel_get(const struct device *dev, enum sensor_channel chan,
133 struct sensor_value *valp)
134 {
135 uint8_t *result = NULL;
136
137 if ((uint32_t)chan == SENSOR_CHAN_NPM2100_DPS_DURATION) {
138 struct npm2100_vbat_data *const data = dev->data;
139
140 result = &data->dpsdur;
141 } else {
142 struct adc_config *adc_cfg = adc_cfg_get(dev, chan);
143
144 if (!adc_cfg) {
145 return -ENOTSUP;
146 }
147
148 result = &adc_cfg->result;
149 }
150
151 if (!result) {
152 return -ENOTSUP;
153 }
154
155 int32_t scaling_mul;
156 int32_t scaling_div;
157 int32_t scaling_off;
158
159 switch ((uint32_t)chan) {
160 case SENSOR_CHAN_GAUGE_VOLTAGE:
161 scaling_mul = VBAT_SCALING_MUL;
162 scaling_div = VBAT_SCALING_DIV;
163 scaling_off = VBAT_SCALING_OFFSET;
164 break;
165 case SENSOR_CHAN_VOLTAGE:
166 /* Fall through */
167 case SENSOR_CHAN_NPM2100_VOLT_DROOP:
168 scaling_mul = VOUT_SCALING_MUL;
169 scaling_div = VOUT_SCALING_DIV;
170 scaling_off = VOUT_SCALING_OFFSET;
171 break;
172 case SENSOR_CHAN_DIE_TEMP:
173 scaling_mul = TEMP_SCALING_MUL;
174 scaling_div = TEMP_SCALING_DIV;
175 scaling_off = TEMP_SCALING_OFFSET;
176 break;
177 case SENSOR_CHAN_NPM2100_DPS_DURATION:
178 scaling_mul = DPS_SCALING_MUL;
179 scaling_div = DPS_SCALING_DIV;
180 scaling_off = DPS_SCALING_OFFSET;
181 break;
182 default:
183 return -ENOTSUP;
184 }
185
186 int32_t tmp = scaling_off + ((int32_t)*result * scaling_mul) / scaling_div;
187
188 valp->val1 = tmp / 1000000;
189 valp->val2 = tmp % 1000000;
190
191 return 0;
192 }
193
npm2100_vbat_sample_fetch(const struct device * dev,enum sensor_channel chan)194 int npm2100_vbat_sample_fetch(const struct device *dev, enum sensor_channel chan)
195 {
196 const struct npm2100_vbat_config *const config = dev->config;
197 struct npm2100_vbat_data *data = dev->data;
198 int ret;
199
200 for (int idx = 0; idx < ARRAY_SIZE(data->adc); idx++) {
201 if (!data->adc[idx].enabled) {
202 continue;
203 }
204
205 /* Oversampling by 2^field */
206 const uint8_t oversampling =
207 BIT(FIELD_GET(ADC_CONFIG_AVG_MASK, data->adc[idx].config));
208 int32_t delay_usec;
209
210 if (chan == SENSOR_CHAN_GAUGE_VOLTAGE) {
211 ret = linear_range_get_value(&adcdelay_range, data->vbat_delay,
212 &delay_usec);
213 if (ret < 0) {
214 return ret;
215 }
216 } else {
217 delay_usec = 0;
218 }
219
220 ret = i2c_reg_write_byte_dt(&config->i2c, ADC_CONFIG, data->adc[idx].config);
221 if (ret < 0) {
222 return ret;
223 }
224
225 ret = i2c_reg_write_byte_dt(&config->i2c, ADC_TASKS_ADC, 1U);
226 if (ret < 0) {
227 return ret;
228 }
229
230 k_sleep(K_USEC(ADC_SAMPLE_TIME_US * oversampling + delay_usec));
231
232 if (oversampling > 1) {
233 ret = i2c_reg_read_byte_dt(&config->i2c, ADC_AVERAGE,
234 &data->adc[idx].result);
235 if (ret < 0) {
236 return ret;
237 }
238 } else {
239 ret = i2c_reg_read_byte_dt(&config->i2c, data->adc[idx].result_reg,
240 &data->adc[idx].result);
241 if (ret < 0) {
242 return ret;
243 }
244 }
245 }
246
247 /* Fetch previous DPS duration result before triggering new one.The time it takes to get
248 * the DPS duration depends on many factors and cannot be predicted here.
249 */
250 ret = i2c_reg_read_byte_dt(&config->i2c, BOOST_DPSDUR, &data->dpsdur);
251 if (ret < 0) {
252 return ret;
253 }
254
255 return i2c_reg_write_byte_dt(&config->i2c, BOOST_START, 2U);
256 }
257
npm2100_vbat_attr_get(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,struct sensor_value * val)258 static int npm2100_vbat_attr_get(const struct device *dev, enum sensor_channel chan,
259 enum sensor_attribute attr, struct sensor_value *val)
260 {
261 const struct npm2100_vbat_config *const config = dev->config;
262
263 if (attr == SENSOR_ATTR_FEATURE_MASK) {
264 struct adc_config *adc_cfg = adc_cfg_get(dev, chan);
265
266 if (!adc_cfg) {
267 return -EINVAL;
268 }
269
270 *val = (struct sensor_value){.val1 = adc_cfg->enabled, .val2 = 0};
271
272 return 0;
273 }
274
275 if (attr == SENSOR_ATTR_OVERSAMPLING) {
276 struct adc_config *adc_cfg = adc_cfg_get(dev, chan);
277
278 if (!adc_cfg) {
279 return -EINVAL;
280 }
281
282 *val = (struct sensor_value){
283 .val1 = FIELD_GET(ADC_CONFIG_AVG_MASK, adc_cfg->config), .val2 = 0};
284
285 return 0;
286 }
287
288 /* Delay of vbat ADC measurement */
289 if ((chan == SENSOR_CHAN_GAUGE_VOLTAGE) &&
290 (attr == (enum sensor_attribute)SENSOR_ATTR_NPM2100_ADC_DELAY)) {
291 struct npm2100_vbat_data *const data = dev->data;
292 struct adc_config *adc_cfg = adc_cfg_get(dev, chan);
293 int32_t val_usec;
294
295 if (!adc_cfg) {
296 return -ENOENT;
297 }
298
299 if (FIELD_GET(ADC_CONFIG_MODE_MASK, adc_cfg->config) == ADC_CONFIG_MODE_INS_VBAT) {
300 /* Instant measurement */
301 return sensor_value_from_micro(val, 0);
302 }
303
304 int ret = linear_range_get_value(&adcdelay_range, data->vbat_delay, &val_usec);
305
306 if (ret < 0) {
307 return ret;
308 }
309
310 return sensor_value_from_micro(val, val_usec);
311 }
312
313 for (int idx = 0; idx < ARRAY_SIZE(npm2100_attr); idx++) {
314 if ((npm2100_attr[idx].chan == chan) && (npm2100_attr[idx].attr == attr)) {
315
316 int32_t val_mv;
317 uint8_t reg_data;
318
319 int ret = i2c_reg_read_byte_dt(&config->i2c, npm2100_attr[idx].reg,
320 ®_data);
321
322 if (ret < 0) {
323 return ret;
324 }
325
326 reg_data = FIELD_GET(npm2100_attr[idx].reg_mask, reg_data);
327
328 ret = linear_range_get_value(npm2100_attr[idx].range, reg_data, &val_mv);
329 if (ret < 0) {
330 return ret;
331 }
332
333 val->val1 = val_mv / 1000000;
334 val->val2 = val_mv % 1000000;
335
336 return 0;
337 }
338 }
339
340 return -ENOTSUP;
341 }
342
npm2100_vbat_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)343 static int npm2100_vbat_attr_set(const struct device *dev, enum sensor_channel chan,
344 enum sensor_attribute attr, const struct sensor_value *val)
345 {
346 const struct npm2100_vbat_config *const config = dev->config;
347 int ret;
348
349 /* ADC sampling feature masks to enable individual measurements */
350 if (attr == SENSOR_ATTR_FEATURE_MASK) {
351 struct adc_config *adc_cfg = adc_cfg_get(dev, chan);
352
353 if (!adc_cfg) {
354 return -EINVAL;
355 }
356
357 adc_cfg->enabled = val->val1 == 0 ? false : true;
358
359 return 0;
360 }
361
362 /* Delay of vbat ADC measurement */
363 if ((chan == SENSOR_CHAN_GAUGE_VOLTAGE) &&
364 (attr == (enum sensor_attribute)SENSOR_ATTR_NPM2100_ADC_DELAY)) {
365 struct npm2100_vbat_data *const data = dev->data;
366 struct adc_config *adc_cfg = adc_cfg_get(dev, chan);
367 int32_t val_usec = sensor_value_to_micro(val);
368 uint16_t delay;
369
370 if (!adc_cfg) {
371 return -ENOENT;
372 }
373
374 if (val_usec == 0) {
375 delay = 0;
376 } else {
377 ret = linear_range_get_index(&adcdelay_range, val_usec, &delay);
378 if (ret < 0) {
379 return ret;
380 }
381
382 ret = i2c_reg_write_byte_dt(&config->i2c, ADC_DELAY, delay);
383 if (ret < 0) {
384 return ret;
385 }
386 }
387
388 /* Delayed vbat measurement uses different mode */
389 data->vbat_delay = delay;
390 adc_cfg->config &= ~ADC_CONFIG_MODE_MASK;
391 if (delay == 0) {
392 adc_cfg->config |=
393 FIELD_PREP(ADC_CONFIG_MODE_MASK, ADC_CONFIG_MODE_INS_VBAT);
394 } else {
395 adc_cfg->config |=
396 FIELD_PREP(ADC_CONFIG_MODE_MASK, ADC_CONFIG_MODE_DEL_VBAT);
397 }
398
399 return 0;
400 }
401
402 /* ADC per-channel oversampling */
403 if (attr == SENSOR_ATTR_OVERSAMPLING) {
404 struct adc_config *adc_cfg = adc_cfg_get(dev, chan);
405 uint16_t oversample;
406
407 if (!adc_cfg) {
408 return -ENOENT;
409 }
410
411 /* Oversample factor is 2^value */
412 ret = linear_range_get_index(&oversample_range, val->val1, &oversample);
413 if (ret < 0) {
414 return ret;
415 }
416
417 adc_cfg->config &= ~ADC_CONFIG_AVG_MASK;
418 adc_cfg->config |= FIELD_PREP(ADC_CONFIG_AVG_MASK, oversample);
419
420 return 0;
421 }
422
423 /* Threshold attributes */
424 for (int idx = 0; idx < ARRAY_SIZE(npm2100_attr); idx++) {
425 if ((npm2100_attr[idx].chan == chan) && (npm2100_attr[idx].attr == attr)) {
426
427 uint16_t range_idx;
428 uint8_t reg_data;
429
430 ret = linear_range_get_index(npm2100_attr[idx].range,
431 sensor_value_to_micro(val), &range_idx);
432
433 if (ret < 0) {
434 return ret;
435 }
436
437 reg_data = FIELD_PREP(npm2100_attr[idx].reg_mask, range_idx);
438
439 if (npm2100_attr[idx].ctrlsel_mask != 0) {
440 /* Disable comparator */
441 ret = i2c_reg_write_byte_dt(&config->i2c, BOOST_CTRLCLR,
442 npm2100_attr[idx].ctrlsel_mask);
443 if (ret < 0) {
444 return ret;
445 }
446 }
447
448 /* Set threshold */
449 if (npm2100_attr[idx].reg_mask == 0xFFU) {
450 ret = i2c_reg_write_byte_dt(&config->i2c, npm2100_attr[idx].reg,
451 reg_data);
452 } else {
453 ret = i2c_reg_update_byte_dt(&config->i2c, npm2100_attr[idx].reg,
454 npm2100_attr[idx].reg_mask, reg_data);
455 }
456 if (ret < 0) {
457 return ret;
458 }
459
460 if (npm2100_attr[idx].ctrlsel_mask != 0) {
461 /* Enable comparator */
462 ret = i2c_reg_write_byte_dt(&config->i2c, BOOST_CTRLSET,
463 npm2100_attr[idx].ctrlsel_mask);
464 }
465
466 return ret;
467 }
468 }
469
470 return -ENOTSUP;
471 }
472
npm2100_vbat_init(const struct device * dev)473 int npm2100_vbat_init(const struct device *dev)
474 {
475 const struct npm2100_vbat_config *const config = dev->config;
476 int ret;
477
478 if (!i2c_is_ready_dt(&config->i2c)) {
479 LOG_ERR("%s i2c not ready", dev->name);
480 return -ENODEV;
481 }
482
483 /* Set initial voltage thresholds */
484 if ((config->voutmin.val1 != 0) || (config->voutmin.val2 != 0)) {
485 ret = npm2100_vbat_attr_set(dev, SENSOR_CHAN_VOLTAGE, SENSOR_ATTR_LOWER_THRESH,
486 &config->voutmin);
487 if (ret < 0) {
488 return ret;
489 }
490 }
491
492 if ((config->vbatmin.val1 != 0) || (config->vbatmin.val2 != 0)) {
493 ret = npm2100_vbat_attr_set(dev, SENSOR_CHAN_GAUGE_VOLTAGE,
494 SENSOR_ATTR_UPPER_THRESH, &config->vbatmin);
495 if (ret < 0) {
496 return ret;
497 }
498
499 ret = npm2100_vbat_attr_set(dev, SENSOR_CHAN_GAUGE_VOLTAGE,
500 SENSOR_ATTR_LOWER_THRESH, &config->vbatmin);
501 if (ret < 0) {
502 return ret;
503 }
504 }
505
506 /* Set MEE thresholds to SW control */
507 ret = i2c_reg_write_byte_dt(&config->i2c, BOOST_VBATSEL, 3U);
508 if (ret < 0) {
509 return ret;
510 }
511
512 /* Allow VOUTMIN comparator to select VBATMIN threshold */
513 return i2c_reg_write_byte_dt(&config->i2c, BOOST_CTRLSET, 0x10U);
514 }
515
516 static DEVICE_API(sensor, npm2100_vbat_battery_driver_api) = {
517 .sample_fetch = npm2100_vbat_sample_fetch,
518 .channel_get = npm2100_vbat_channel_get,
519 .attr_set = npm2100_vbat_attr_set,
520 .attr_get = npm2100_vbat_attr_get,
521 };
522
523 /* DPS-related measurements off by default. Enable via attribute feature masks. */
524 #define NPM2100_VBAT_DATA_INIT() \
525 { \
526 .adc = \
527 { \
528 {.chan = SENSOR_CHAN_GAUGE_VOLTAGE, \
529 .config = ADC_CONFIG_MODE_INS_VBAT, \
530 .result_reg = ADC_READVBAT, \
531 .enabled = true}, \
532 {.chan = SENSOR_CHAN_VOLTAGE, \
533 .config = ADC_CONFIG_MODE_VOUT, \
534 .result_reg = ADC_READVOUT, \
535 .enabled = true}, \
536 {.chan = SENSOR_CHAN_DIE_TEMP, \
537 .config = ADC_CONFIG_MODE_TEMP, \
538 .result_reg = ADC_READTEMP, \
539 .enabled = true}, \
540 {.chan = (enum sensor_channel)SENSOR_CHAN_NPM2100_VOLT_DROOP, \
541 .config = ADC_CONFIG_MODE_DROOP, \
542 .result_reg = ADC_READDROOP, \
543 .enabled = false}, \
544 }, \
545 .vbat_delay = 0, .dpsdur = 0, \
546 }
547
548 #define NPM2100_VBAT_INIT(n) \
549 static struct npm2100_vbat_data npm2100_vbat_data_##n = NPM2100_VBAT_DATA_INIT(); \
550 \
551 static const struct npm2100_vbat_config npm2100_vbat_config_##n = { \
552 .i2c = I2C_DT_SPEC_GET(DT_INST_PARENT(n)), \
553 .voutmin = {.val1 = DT_INST_PROP_OR(n, vout_min_microvolt, 0) / 1000000, \
554 .val2 = DT_INST_PROP_OR(n, vout_min_microvolt, 0) % 1000000}, \
555 .vbatmin = {.val1 = DT_INST_PROP_OR(n, vbat_min_microvolt, 0) / 1000000, \
556 .val2 = DT_INST_PROP_OR(n, vbat_min_microvolt, 0) % 1000000}, \
557 }; \
558 \
559 SENSOR_DEVICE_DT_INST_DEFINE( \
560 n, &npm2100_vbat_init, NULL, &npm2100_vbat_data_##n, &npm2100_vbat_config_##n, \
561 POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &npm2100_vbat_battery_driver_api);
562
563 DT_INST_FOREACH_STATUS_OKAY(NPM2100_VBAT_INIT)
564