1 /*
2 * Copyright (c) 2021 Aurelien Jarno
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT silabs_si7210
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/drivers/sensor.h>
12 #include <zephyr/pm/device.h>
13 #include <zephyr/sys/__assert.h>
14 #include <zephyr/sys/byteorder.h>
15 #include <zephyr/logging/log.h>
16
17 LOG_MODULE_REGISTER(SI7210, CONFIG_SENSOR_LOG_LEVEL);
18
19 /* Register addresses */
20 #define SI7210_REG_CHIPID 0xC0
21 #define SI7210_REG_DSPSIGM 0xC1
22 #define SI7210_REG_DSPSIGL 0xC2
23 #define SI7210_REG_DSPSIGSEL 0xC3
24 #define SI7210_REG_POWER_CTRL 0xC4
25 #define SI7210_REG_ARAUTOINC 0xC5
26 #define SI7210_REG_CTRL1 0xC6
27 #define SI7210_REG_CTRL2 0xC7
28 #define SI7210_REG_SLTIME 0xC8
29 #define SI7210_REG_CTRL3 0xC9
30 #define SI7210_REG_A0 0xCA
31 #define SI7210_REG_A1 0xCB
32 #define SI7210_REG_A2 0xCC
33 #define SI7210_REG_CTRL4 0xCD
34 #define SI7210_REG_A3 0xCE
35 #define SI7210_REG_A4 0xCF
36 #define SI7210_REG_A5 0xD0
37 #define SI7210_REG_OTP_ADDR 0xE1
38 #define SI7210_REG_OTP_DATA 0xE2
39 #define SI7210_REG_OTP_CTRL 0xE3
40 #define SI7210_REG_TM_FG 0xE4
41
42 /* Registers bits */
43 #define SI7210_BIT_DSPSIGSEL_MAG 0x00
44 #define SI7210_BIT_DSPSIGSEL_TEMP 0x01
45 #define SI7210_BIT_POWER_CTRL_MEAS 0x80
46 #define SI7210_BIT_POWER_CTRL_USESTORE 0x08
47 #define SI7210_BIT_POWER_CTRL_ONEBURST 0x04
48 #define SI7210_BIT_POWER_CTRL_STOP 0x02
49 #define SI7210_BIT_POWER_CTRL_SLEEP 0x01
50 #define SI7210_BIT_CTRL3_SLTIMEENA 0x01
51 #define SI7210_BIT_CTRL3_SLTFAST 0x02
52 #define SI7210_BIT_OTP_CTRL_READEN 0x02
53 #define SI7210_BIT_OTP_CTRL_BUSY 0x01
54
55 /* OT registers */
56 #define SI7210_OTPREG_DEF_CTRL1 0x04
57 #define SI7210_OTPREG_DEF_CTRL2 0x05
58 #define SI7210_OTPREG_DEF_SLTIME 0x06
59 #define SI7210_OTPREG_DEF_CTRL3 0x08
60 #define SI7210_OTPREG_DEF_A0 0x09
61 #define SI7210_OTPREG_DEF_A1 0x0A
62 #define SI7210_OTPREG_DEF_A2 0x0B
63 #define SI7210_OTPREG_DEF_CTRL4 0x0C
64 #define SI7210_OTPREG_DEF_A3 0x0D
65 #define SI7210_OTPREG_DEF_A4 0x0E
66 #define SI7210_OTPREG_DEF_A5 0x0F
67 #define SI7210_OTPREG_PART_BASE 0x14
68 #define SI7210_OTPREG_PART_VARIANT 0x15
69 #define SI7210_OTPREG_SN1 0x18
70 #define SI7210_OTPREG_SN2 0x19
71 #define SI7210_OTPREG_SN3 0x1A
72 #define SI7210_OTPREG_SN4 0x1B
73 #define SI7210_OTPREG_TEMP_OFFSET 0x1D
74 #define SI7210_OTPREG_TEMP_GAIN 0x1E
75 #define SI7210_OTPREG_200G_SCALE_A0 0x21
76 #define SI7210_OTPREG_200G_SCALE_A1 0x22
77 #define SI7210_OTPREG_200G_SCALE_A2 0x23
78 #define SI7210_OTPREG_200G_SCALE_A3 0x24
79 #define SI7210_OTPREG_200G_SCALE_A4 0x25
80 #define SI7210_OTPREG_200G_SCALE_A5 0x26
81 #define SI7210_OTPREG_2000G_SCALE_A0 0x27
82 #define SI7210_OTPREG_2000G_SCALE_A1 0x28
83 #define SI7210_OTPREG_2000G_SCALE_A2 0x29
84 #define SI7210_OTPREG_2000G_SCALE_A3 0x30
85 #define SI7210_OTPREG_2000G_SCALE_A4 0x31
86 #define SI7210_OTPREG_2000G_SCALE_A5 0x32
87
88 enum si7210_scale {
89 si7210_scale_200G,
90 si7210_scale_2000G,
91 };
92
93 struct si7210_config {
94 struct i2c_dt_spec bus;
95 };
96
97 struct si7210_data {
98 int8_t otp_temp_offset;
99 int8_t otp_temp_gain;
100
101 enum si7210_scale scale;
102
103 uint8_t reg_dspsigsel;
104 uint8_t reg_arautoinc;
105
106 uint16_t mag_sample;
107 uint16_t temp_sample;
108 };
109
110 /* Put the chip into sleep mode */
si7210_sleep(const struct device * dev)111 static int si7210_sleep(const struct device *dev)
112 {
113 const struct si7210_config *config = dev->config;
114 struct si7210_data *data = dev->data;
115 int rc;
116
117 /*
118 * Disable measurements during sleep. This overrides the other fields of
119 * the register, but they get reloaded from OTP during wake-up.
120 */
121 rc = i2c_reg_write_byte_dt(&config->bus, SI7210_REG_CTRL3, 0);
122 if (rc < 0) {
123 LOG_ERR("Failed to disable SLTIME");
124 return rc;
125 }
126
127 /* Go to sleep mode */
128 rc = i2c_reg_write_byte_dt(&config->bus, SI7210_REG_POWER_CTRL,
129 SI7210_BIT_POWER_CTRL_SLEEP);
130 if (rc < 0) {
131 LOG_ERR("Failed to go to sleep mode");
132 return rc;
133 }
134
135 /* Going to sleep mode resets some registers */
136 data->reg_dspsigsel = 0x00;
137 data->reg_arautoinc = 0x00;
138
139 return 0;
140 }
141
142 /* Wake a chip from idle or sleep mode */
si7210_wakeup(const struct device * dev)143 static int si7210_wakeup(const struct device *dev)
144 {
145 const struct si7210_config *config = dev->config;
146 uint8_t val;
147 int rc;
148
149 /*
150 * Read one byte from the chip to wake it up. The shorter alternative
151 * is to write a zero byte length message, but it might not be
152 * supported by all I2C controllers.
153 */
154 rc = i2c_read_dt(&config->bus, &val, 1);
155 if (rc < 0) {
156 LOG_ERR("Failed to go wake-up chip");
157 return rc;
158 }
159
160 return 0;
161 }
162
163 /*
164 * The Si7210 device do not have a reset function, but most of the registers
165 * are reloaded when exiting from sleep mode.
166 */
si7210_reset(const struct device * dev)167 static int si7210_reset(const struct device *dev)
168 {
169 int rc;
170
171 rc = si7210_sleep(dev);
172 if (rc < 0) {
173 return rc;
174 }
175
176 rc = si7210_wakeup(dev);
177
178 return rc;
179 }
180
si7210_otp_reg_read_byte(const struct device * dev,uint8_t otp_reg_addr,uint8_t * value)181 static int si7210_otp_reg_read_byte(const struct device *dev,
182 uint8_t otp_reg_addr, uint8_t *value)
183 {
184 const struct si7210_config *config = dev->config;
185 int rc;
186
187 rc = i2c_reg_write_byte_dt(&config->bus, SI7210_REG_OTP_ADDR, otp_reg_addr);
188 if (rc) {
189 LOG_ERR("Failed to write OTP address register");
190 return rc;
191 }
192
193 rc = i2c_reg_write_byte_dt(&config->bus, SI7210_REG_OTP_CTRL, SI7210_BIT_OTP_CTRL_READEN);
194 if (rc) {
195 LOG_ERR("Failed to write OTP control register");
196 return rc;
197 }
198
199 /*
200 * No need to check for the data availability (SI7210_REG_OTP_CTRL, bit
201 * !BUSY), as the I2C bus timing ensure the data is available (see
202 * datasheet).
203 */
204 rc = i2c_reg_read_byte_dt(&config->bus, SI7210_REG_OTP_DATA, value);
205 if (rc) {
206 LOG_ERR("Failed to read OTP data register");
207 return rc;
208 }
209
210 return 0;
211 }
212
si7210_read_sn(const struct device * dev,uint32_t * sn)213 static int si7210_read_sn(const struct device *dev, uint32_t *sn)
214 {
215 uint32_t val = 0;
216
217 for (int reg = SI7210_OTPREG_SN1; reg <= SI7210_OTPREG_SN4; reg++) {
218 uint8_t val8;
219 int rc;
220
221 rc = si7210_otp_reg_read_byte(dev, reg, &val8);
222 if (rc < 0) {
223 return rc;
224 }
225
226 val = (val << 8) | val8;
227 }
228
229 *sn = val;
230
231 return 0;
232 }
233
234 /* Set the DSPSIGSEL register unless it already has the correct value */
si7210_set_dspsigsel(const struct device * dev,uint8_t value)235 static int si7210_set_dspsigsel(const struct device *dev, uint8_t value)
236 {
237 const struct si7210_config *config = dev->config;
238 struct si7210_data *data = dev->data;
239 int rc;
240
241 if (data->reg_dspsigsel == value) {
242 return 0;
243 }
244
245 rc = i2c_reg_write_byte_dt(&config->bus, SI7210_REG_DSPSIGSEL, value);
246 if (rc < 0) {
247 LOG_ERR("Failed to select channel");
248 return rc;
249 }
250
251 data->reg_dspsigsel = value;
252
253 return 0;
254 }
255
256 /* Set the ARAUTOINC register unless it already has the correct value */
si7210_set_arautoinc(const struct device * dev,uint8_t value)257 static int si7210_set_arautoinc(const struct device *dev, uint8_t value)
258 {
259 const struct si7210_config *config = dev->config;
260 struct si7210_data *data = dev->data;
261 int rc;
262
263 if (data->reg_arautoinc == value) {
264 return 0;
265 }
266
267 rc = i2c_reg_write_byte_dt(&config->bus, SI7210_REG_ARAUTOINC, value);
268 if (rc < 0) {
269 LOG_ERR("Failed to set the auto increment register");
270 return rc;
271 }
272
273 data->reg_arautoinc = value;
274
275 return 0;
276 }
277
si7210_sample_fetch_one(const struct device * dev,uint8_t channel,uint16_t * dspsig)278 static int si7210_sample_fetch_one(const struct device *dev, uint8_t channel, uint16_t *dspsig)
279 {
280 const struct si7210_config *config = dev->config;
281 uint16_t val;
282 int rc;
283
284 /* Select the channel */
285 rc = si7210_set_dspsigsel(dev, channel);
286 if (rc < 0) {
287 return rc;
288 }
289
290 /* Enable auto increment to be able to read DSPSIGM and DSPSIGL sequentially */
291 rc = si7210_set_arautoinc(dev, 1);
292 if (rc < 0) {
293 return rc;
294 }
295
296 /* Start a single conversion */
297 rc = i2c_reg_write_byte_dt(&config->bus, SI7210_REG_POWER_CTRL,
298 SI7210_BIT_POWER_CTRL_ONEBURST);
299 if (rc < 0) {
300 LOG_ERR("Failed to write power control register");
301 return rc;
302 }
303
304 /*
305 * No need to wait for the conversion to end, the I2C bus guarantees
306 * the timing (even at 400kHz)
307 */
308
309 /* Read DSPSIG in one burst as auto increment is enabled */
310 rc = i2c_burst_read_dt(&config->bus, SI7210_REG_DSPSIGM, (uint8_t *)&val, sizeof(val));
311 if (rc < 0) {
312 LOG_ERR("Failed to read sample data");
313 return rc;
314 }
315
316 *dspsig = sys_be16_to_cpu(val) & 0x7fff;
317
318 return 0;
319 }
320
si7210_sample_fetch(const struct device * dev,enum sensor_channel chan)321 static int si7210_sample_fetch(const struct device *dev, enum sensor_channel chan)
322 {
323 struct si7210_data *data = dev->data;
324 uint16_t dspsig;
325 int rc;
326
327 __ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL ||
328 chan == SENSOR_CHAN_AMBIENT_TEMP ||
329 chan == SENSOR_CHAN_MAGN_Z);
330
331 #ifdef CONFIG_PM_DEVICE
332 enum pm_device_state state;
333 (void)pm_device_state_get(dev, &state);
334 /* Do not allow sample fetching from suspended state */
335 if (state == PM_DEVICE_STATE_SUSPENDED) {
336 return -EIO;
337 }
338 #endif
339
340 /* Prevent going into suspend in the middle of the conversion */
341 pm_device_busy_set(dev);
342
343 if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_AMBIENT_TEMP) {
344 rc = si7210_sample_fetch_one(dev, SI7210_BIT_DSPSIGSEL_TEMP, &dspsig);
345 if (rc < 0) {
346 return rc;
347 }
348
349 data->temp_sample = dspsig >> 3;
350
351 }
352
353 if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_MAGN_Z) {
354 rc = si7210_sample_fetch_one(dev, SI7210_BIT_DSPSIGSEL_MAG, &dspsig);
355 if (rc < 0) {
356 return rc;
357 }
358
359 data->mag_sample = dspsig;
360 }
361
362 pm_device_busy_clear(dev);
363
364 return 0;
365 }
366
367
si7210_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)368 static int si7210_channel_get(const struct device *dev,
369 enum sensor_channel chan,
370 struct sensor_value *val)
371 {
372 struct si7210_data *data = dev->data;
373 int64_t tmp;
374
375 switch (chan) {
376 case SENSOR_CHAN_AMBIENT_TEMP:
377 /* type conversion */
378 tmp = data->temp_sample;
379
380 /* temperature_raw = -3.83e-6 * value^2 + 0.16094 * value - 279.80 */
381 tmp = (-383 * tmp * tmp) / 100 + (160940 * tmp) - 279800000;
382
383 /* temperature = (1 + gain / 2048) * temperature_raw + offset / 16 */
384 tmp = (tmp * (2048 + data->otp_temp_gain)) / 2048;
385 tmp = tmp + (data->otp_temp_offset * 62500);
386
387 /*
388 * Additional offset of -0.222 x VDD. The datasheet recommends
389 * to use VDD = 3.3V if not known.
390 */
391 tmp -= 732600;
392
393 val->val1 = tmp / 1000000;
394 val->val2 = tmp % 1000000;
395 break;
396 case SENSOR_CHAN_MAGN_Z:
397 /* type conversion */
398 tmp = data->mag_sample;
399
400 if (data->scale == si7210_scale_200G) {
401 /*
402 * Formula in mT (datasheet) for the 20mT scale: (value - 16384) * 0.00125
403 * => Formula in G for the 200G scale: (value - 16384) * 0.0125
404 */
405 tmp = (tmp - 16384) * 12500;
406 } else {
407 /*
408 * Formula in mT (datasheet) for the 200mT scale: (value - 16384) * 0.0125
409 * => Formula in G for the 2000G scale: (value - 16384) * 0.125
410 */
411 tmp = (tmp - 16384) * 1250;
412 }
413
414 val->val1 = tmp / 1000000;
415 val->val2 = tmp % 1000000;
416 break;
417 default:
418 return -ENOTSUP;
419 }
420
421 return 0;
422 }
423
424 static DEVICE_API(sensor, si7210_api_funcs) = {
425 .sample_fetch = si7210_sample_fetch,
426 .channel_get = si7210_channel_get,
427 };
428
429 #ifdef CONFIG_PM_DEVICE
si7210_pm_action(const struct device * dev,enum pm_device_action action)430 static int si7210_pm_action(const struct device *dev,
431 enum pm_device_action action)
432 {
433 int rc;
434
435 switch (action) {
436 case PM_DEVICE_ACTION_RESUME:
437 /* Wake-up the chip */
438 rc = si7210_wakeup(dev);
439 break;
440 case PM_DEVICE_ACTION_SUSPEND:
441 /* Put the chip into sleep mode */
442 rc = si7210_sleep(dev);
443 break;
444 default:
445 return -ENOTSUP;
446 }
447
448 return rc;
449 }
450 #endif /* CONFIG_PM_DEVICE */
451
si7210_init(const struct device * dev)452 static int si7210_init(const struct device *dev)
453 {
454 const struct si7210_config *config = dev->config;
455 struct si7210_data *data = dev->data;
456 uint8_t chipid, part_base, part_variant;
457 uint32_t sn;
458 char rev;
459 int rc;
460
461 if (!device_is_ready(config->bus.bus)) {
462 LOG_ERR("I2C bus %s not ready!", config->bus.bus->name);
463 return -ENODEV;
464 }
465
466 /* Possibly wakeup device */
467 rc = si7210_wakeup(dev);
468 if (rc < 0) {
469 LOG_ERR("Failed to wake-up device");
470 return rc;
471 }
472
473 /* Read chip ID */
474 rc = i2c_reg_read_byte_dt(&config->bus, SI7210_REG_CHIPID, &chipid);
475 if (rc < 0) {
476 LOG_ERR("Failed to read chip ID");
477 return rc;
478 }
479
480 if ((chipid & 0xf0) != 0x10) {
481 LOG_ERR("Unsupported device ID");
482 return -EINVAL;
483 }
484 switch (chipid & 0x0f) {
485 case 0x04:
486 rev = 'B';
487 break;
488 default:
489 LOG_WRN("Unknown revision %d", chipid & 0x0f);
490 rev = '.';
491 }
492
493 /* Read part number */
494 rc = si7210_otp_reg_read_byte(dev, SI7210_OTPREG_PART_BASE, &part_base);
495 if (rc < 0) {
496 return rc;
497 }
498 rc = si7210_otp_reg_read_byte(dev, SI7210_OTPREG_PART_VARIANT, &part_variant);
499 if (rc < 0) {
500 return rc;
501 }
502
503 /* Read serial number */
504 rc = si7210_read_sn(dev, &sn);
505 if (rc < 0) {
506 return rc;
507 }
508
509 LOG_INF("Found Si72%02d-%c-%02d S/N %08x, at I2C address 0x%x",
510 part_base, rev, part_variant, sn, config->bus.addr);
511
512 /* Set default scale depending on the part variant */
513 data->scale = (part_variant == 5 || part_variant == 15) ?
514 si7210_scale_2000G : si7210_scale_200G;
515
516 /* Read temperature adjustment values */
517 rc = si7210_otp_reg_read_byte(dev, SI7210_OTPREG_TEMP_OFFSET, &data->otp_temp_offset);
518 if (rc < 0) {
519 return rc;
520 }
521 rc = si7210_otp_reg_read_byte(dev, SI7210_OTPREG_TEMP_GAIN, &data->otp_temp_gain);
522 if (rc < 0) {
523 return rc;
524 }
525
526 /* Reset the device */
527 rc = si7210_reset(dev);
528 if (rc < 0) {
529 LOG_ERR("Failed to reset the device");
530 return rc;
531 }
532
533 return 0;
534 }
535
536 /* Main instantiation macro */
537 #define DEFINE_SI7210(inst) \
538 static struct si7210_data si7210_data_##inst; \
539 static const struct si7210_config si7210_config_##inst = { \
540 .bus = I2C_DT_SPEC_INST_GET(inst), \
541 }; \
542 PM_DEVICE_DT_INST_DEFINE(inst, si7210_pm_action); \
543 SENSOR_DEVICE_DT_INST_DEFINE(inst, si7210_init, PM_DEVICE_DT_INST_GET(inst), \
544 &si7210_data_##inst, &si7210_config_##inst, POST_KERNEL, \
545 CONFIG_SENSOR_INIT_PRIORITY, &si7210_api_funcs);
546
547 DT_INST_FOREACH_STATUS_OKAY(DEFINE_SI7210)
548