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