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