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 #endif
338
339 /* Prevent going into suspend in the middle of the conversion */
340 pm_device_busy_set(dev);
341
342 if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_AMBIENT_TEMP) {
343 rc = si7210_sample_fetch_one(dev, SI7210_BIT_DSPSIGSEL_TEMP, &dspsig);
344 if (rc < 0) {
345 return rc;
346 }
347
348 data->temp_sample = dspsig >> 3;
349
350 }
351
352 if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_MAGN_Z) {
353 rc = si7210_sample_fetch_one(dev, SI7210_BIT_DSPSIGSEL_MAG, &dspsig);
354 if (rc < 0) {
355 return rc;
356 }
357
358 data->mag_sample = dspsig;
359 }
360
361 pm_device_busy_clear(dev);
362
363 return 0;
364 }
365
366
si7210_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)367 static int si7210_channel_get(const struct device *dev,
368 enum sensor_channel chan,
369 struct sensor_value *val)
370 {
371 struct si7210_data *data = dev->data;
372 int64_t tmp;
373
374 switch (chan) {
375 case SENSOR_CHAN_AMBIENT_TEMP:
376 /* type conversion */
377 tmp = data->temp_sample;
378
379 /* temperature_raw = -3.83e-6 * value^2 + 0.16094 * value - 279.80 */
380 tmp = (-383 * tmp * tmp) / 100 + (160940 * tmp) - 279800000;
381
382 /* temperature = (1 + gain / 2048) * temperature_raw + offset / 16 */
383 tmp = (tmp * (2048 + data->otp_temp_gain)) / 2048;
384 tmp = tmp + (data->otp_temp_offset * 62500);
385
386 /*
387 * Additional offset of -0.222 x VDD. The datasheet recommends
388 * to use VDD = 3.3V if not known.
389 */
390 tmp -= 732600;
391
392 val->val1 = tmp / 1000000;
393 val->val2 = tmp % 1000000;
394 break;
395 case SENSOR_CHAN_MAGN_Z:
396 /* type conversion */
397 tmp = data->mag_sample;
398
399 if (data->scale == si7210_scale_200G) {
400 /*
401 * Formula in mT (datasheet) for the 20mT scale: (value - 16384) * 0.00125
402 * => Formula in G for the 200G scale: (value - 16384) * 0.0125
403 */
404 tmp = (tmp - 16384) * 12500;
405 } else {
406 /*
407 * Formula in mT (datasheet) for the 200mT scale: (value - 16384) * 0.0125
408 * => Formula in G for the 2000G scale: (value - 16384) * 0.125
409 */
410 tmp = (tmp - 16384) * 1250;
411 }
412
413 val->val1 = tmp / 1000000;
414 val->val2 = tmp % 1000000;
415 break;
416 default:
417 return -ENOTSUP;
418 }
419
420 return 0;
421 }
422
423 static const struct sensor_driver_api si7210_api_funcs = {
424 .sample_fetch = si7210_sample_fetch,
425 .channel_get = si7210_channel_get,
426 };
427
428 #ifdef CONFIG_PM_DEVICE
si7210_pm_action(const struct device * dev,enum pm_device_action action)429 static int si7210_pm_action(const struct device *dev,
430 enum pm_device_action action)
431 {
432 int rc;
433
434 switch (action) {
435 case PM_DEVICE_ACTION_RESUME:
436 /* Wake-up the chip */
437 rc = si7210_wakeup(dev);
438 break;
439 case PM_DEVICE_ACTION_SUSPEND:
440 /* Put the chip into sleep mode */
441 rc = si7210_sleep(dev);
442 break;
443 default:
444 return -ENOTSUP;
445 }
446
447 return rc;
448 }
449 #endif /* CONFIG_PM_DEVICE */
450
si7210_init(const struct device * dev)451 static int si7210_init(const struct device *dev)
452 {
453 const struct si7210_config *config = dev->config;
454 struct si7210_data *data = dev->data;
455 uint8_t chipid, part_base, part_variant;
456 uint32_t sn;
457 char rev;
458 int rc;
459
460 if (!device_is_ready(config->bus.bus)) {
461 LOG_ERR("I2C bus %s not ready!", config->bus.bus->name);
462 return -ENODEV;
463 }
464
465 /* Possibly wakeup device */
466 rc = si7210_wakeup(dev);
467 if (rc < 0) {
468 LOG_ERR("Failed to wake-up device");
469 return rc;
470 }
471
472 /* Read chip ID */
473 rc = i2c_reg_read_byte_dt(&config->bus, SI7210_REG_CHIPID, &chipid);
474 if (rc < 0) {
475 LOG_ERR("Failed to read chip ID");
476 return rc;
477 }
478
479 if ((chipid & 0xf0) != 0x10) {
480 LOG_ERR("Unsupported device ID");
481 return -EINVAL;
482 }
483 switch (chipid & 0x0f) {
484 case 0x04:
485 rev = 'B';
486 break;
487 default:
488 LOG_WRN("Unknown revision %d", chipid & 0x0f);
489 rev = '.';
490 }
491
492 /* Read part number */
493 rc = si7210_otp_reg_read_byte(dev, SI7210_OTPREG_PART_BASE, &part_base);
494 if (rc < 0) {
495 return rc;
496 }
497 rc = si7210_otp_reg_read_byte(dev, SI7210_OTPREG_PART_VARIANT, &part_variant);
498 if (rc < 0) {
499 return rc;
500 }
501
502 /* Read serial number */
503 rc = si7210_read_sn(dev, &sn);
504 if (rc < 0) {
505 return rc;
506 }
507
508 LOG_INF("Found Si72%02d-%c-%02d S/N %08x, at I2C address 0x%x",
509 part_base, rev, part_variant, sn, config->bus.addr);
510
511 /* Set default scale depending on the part variant */
512 data->scale = (part_variant == 5 || part_variant == 15) ?
513 si7210_scale_2000G : si7210_scale_200G;
514
515 /* Read temperature adjustment values */
516 rc = si7210_otp_reg_read_byte(dev, SI7210_OTPREG_TEMP_OFFSET, &data->otp_temp_offset);
517 if (rc < 0) {
518 return rc;
519 }
520 rc = si7210_otp_reg_read_byte(dev, SI7210_OTPREG_TEMP_GAIN, &data->otp_temp_gain);
521 if (rc < 0) {
522 return rc;
523 }
524
525 /* Reset the device */
526 rc = si7210_reset(dev);
527 if (rc < 0) {
528 LOG_ERR("Failed to reset the device");
529 return rc;
530 }
531
532 return 0;
533 }
534
535 /* Main instantiation macro */
536 #define DEFINE_SI7210(inst) \
537 static struct si7210_data si7210_data_##inst; \
538 static const struct si7210_config si7210_config_##inst = { \
539 .bus = I2C_DT_SPEC_INST_GET(inst), \
540 }; \
541 PM_DEVICE_DT_INST_DEFINE(inst, si7210_pm_action); \
542 SENSOR_DEVICE_DT_INST_DEFINE(inst, si7210_init, PM_DEVICE_DT_INST_GET(inst), \
543 &si7210_data_##inst, &si7210_config_##inst, POST_KERNEL, \
544 CONFIG_SENSOR_INIT_PRIORITY, &si7210_api_funcs);
545
546 DT_INST_FOREACH_STATUS_OKAY(DEFINE_SI7210)
547