1 /*
2  * Copyright (c) 2020 Laird Connectivity
3  * Copyright (c) 2019 Electronut Labs
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT silabs_si7055
9 
10 #include <zephyr/drivers/sensor.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/device.h>
13 #include <zephyr/logging/log.h>
14 #include <zephyr/drivers/i2c.h>
15 #include "si7055.h"
16 #if CONFIG_SI7055_ENABLE_CHECKSUM
17 #include <zephyr/sys/crc.h>
18 #endif
19 
20 LOG_MODULE_REGISTER(si7055, CONFIG_SENSOR_LOG_LEVEL);
21 
22 struct si7055_data {
23 	uint16_t temperature;
24 };
25 
26 struct si7055_config {
27 	struct i2c_dt_spec i2c;
28 };
29 
30 /**
31  * @brief function to get temperature
32  *
33  * @return int 0 on success
34  *         -EIO for I/O and checksum errors
35  */
si7055_get_temperature(const struct device * dev)36 static int si7055_get_temperature(const struct device *dev)
37 {
38 	struct si7055_data *si_data = dev->data;
39 	const struct si7055_config *config = dev->config;
40 	int retval;
41 	#if CONFIG_SI7055_ENABLE_CHECKSUM
42 	uint8_t temp[SI7055_TEMPERATURE_READ_WITH_CHECKSUM_SIZE];
43 	#else
44 	uint8_t temp[SI7055_TEMPERATURE_READ_NO_CHECKSUM_SIZE];
45 	#endif
46 
47 	retval = i2c_burst_read_dt(&config->i2c, SI7055_MEAS_TEMP_MASTER_MODE,
48 				   temp, sizeof(temp));
49 
50 	/* Refer to
51 	 * https://www.silabs.com/documents/public/data-sheets/Si7050-1-3-4-5-A20.pdf
52 	 */
53 
54 	if (retval == 0) {
55 		#if CONFIG_SI7055_ENABLE_CHECKSUM
56 		if (crc8(temp, SI7055_DATA_SIZE, SI7055_CRC_POLY,
57 			SI7055_CRC_INIT, false) != temp[SI7055_DATA_SIZE]){
58 			LOG_ERR("checksum failed.\n");
59 			return(-EIO);
60 		}
61 		#endif
62 		si_data->temperature = (temp[SI7055_TEMPERATURE_DATA_BYTE_0]
63 					<< 8) |
64 					temp[SI7055_TEMPERATURE_DATA_BYTE_1];
65 	} else {
66 		LOG_ERR("read register err");
67 	}
68 
69 	return retval;
70 }
71 
72 /**
73  * @brief fetch a sample from the sensor
74  *
75  * @return 0
76  */
si7055_sample_fetch(const struct device * dev,enum sensor_channel chan)77 static int si7055_sample_fetch(const struct device *dev,
78 			       enum sensor_channel chan)
79 {
80 	int retval;
81 
82 	retval = si7055_get_temperature(dev);
83 
84 	return retval;
85 }
86 
87 /**
88  * @brief sensor value get
89  *
90  * @return -ENOTSUP for unsupported channels
91  */
si7055_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)92 static int si7055_channel_get(const struct device *dev,
93 			      enum sensor_channel chan,
94 			      struct sensor_value *val)
95 {
96 	struct si7055_data *si_data = dev->data;
97 
98 	/* Refer to
99 	 * https://www.silabs.com/documents/public/data-sheets/Si7050-1-3-4-5-A20.pdf
100 	 */
101 
102 	if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
103 
104 		int32_t temp_ucelcius = (((SI7055_CONV_FACTOR_1 *
105 					(int32_t)si_data->temperature) /
106 					(__UINT16_MAX__ + 1)) -
107 					SI7055_CONV_FACTOR_2) *
108 					SI7055_MULTIPLIER;
109 
110 		val->val1 = temp_ucelcius / SI7055_DIVIDER;
111 		val->val2 = temp_ucelcius % SI7055_DIVIDER;
112 
113 		LOG_DBG("temperature = val1:%d, val2:%d",
114 			val->val1, val->val2);
115 
116 		return 0;
117 	} else {
118 		return -ENOTSUP;
119 	}
120 }
121 
122 static DEVICE_API(sensor, si7055_api) = {
123 	.sample_fetch = &si7055_sample_fetch,
124 	.channel_get = &si7055_channel_get,
125 };
126 
127 /**
128  * @brief initialize the sensor
129  *
130  * @return 0 for success
131  */
132 
si7055_init(const struct device * dev)133 static int si7055_init(const struct device *dev)
134 {
135 	const struct si7055_config *config = dev->config;
136 
137 	if (!device_is_ready(config->i2c.bus)) {
138 		LOG_ERR("Bus device is not ready");
139 		return -ENODEV;
140 	}
141 
142 	LOG_DBG("si7055 init ok");
143 
144 	return 0;
145 }
146 
147 #define SI7055_DEFINE(inst)								\
148 	static struct si7055_data si7055_data_##inst;					\
149 											\
150 	static const struct si7055_config si7055_config_##inst = {			\
151 		.i2c = I2C_DT_SPEC_INST_GET(inst),					\
152 	};										\
153 											\
154 	SENSOR_DEVICE_DT_INST_DEFINE(inst, si7055_init, NULL,				\
155 			      &si7055_data_##inst, &si7055_config_##inst, POST_KERNEL,	\
156 			      CONFIG_SENSOR_INIT_PRIORITY, &si7055_api);		\
157 
158 DT_INST_FOREACH_STATUS_OKAY(SI7055_DEFINE)
159