1 /*
2  * Copyright (c) 2024 Jan Fäh
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT sensirion_sts4x
8 
9 #include <zephyr/device.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/drivers/sensor.h>
12 #include <zephyr/drivers/i2c.h>
13 #include <zephyr/logging/log.h>
14 #include <zephyr/sys/__assert.h>
15 #include <zephyr/sys/byteorder.h>
16 #include <zephyr/sys/crc.h>
17 #include <zephyr/devicetree.h>
18 
19 LOG_MODULE_REGISTER(STS4X, CONFIG_SENSOR_LOG_LEVEL);
20 
21 #define STS4X_CMD_RESET 0x94
22 
23 #define STS4X_RESET_TIME 1
24 
25 #define STS4X_CRC_POLY 0x31
26 #define STS4X_CRC_INIT 0xFF
27 
28 #define STS4X_MAX_TEMP 175
29 #define STS4X_MIN_TEMP -45
30 
31 struct sts4x_config {
32 	struct i2c_dt_spec bus;
33 	uint8_t repeatability;
34 };
35 
36 struct sts4x_data {
37 	uint16_t temp_sample;
38 };
39 
40 static const uint8_t measure_cmds[3] = {0xE0, 0xF6, 0xFD};
41 static const uint16_t measure_time_us[3] = {1600, 4500, 8300};
42 
sts4x_crc_check(uint16_t value,uint8_t sensor_crc)43 static int sts4x_crc_check(uint16_t value, uint8_t sensor_crc)
44 {
45 	uint8_t buf[2];
46 
47 	sys_put_be16(value, buf);
48 
49 	uint8_t calculated_crc = crc8(buf, 2, STS4X_CRC_POLY, STS4X_CRC_INIT, false);
50 
51 	if (calculated_crc == sensor_crc) {
52 		return 0;
53 	}
54 
55 	return -EIO;
56 }
57 
sts4x_write_command(const struct device * dev,uint8_t cmd)58 static int sts4x_write_command(const struct device *dev, uint8_t cmd)
59 {
60 	const struct sts4x_config *cfg = dev->config;
61 	uint8_t tx_buf = cmd;
62 
63 	return i2c_write_dt(&cfg->bus, &tx_buf, 1);
64 }
65 
sts4x_read_sample(const struct device * dev,uint16_t * temp_sample)66 static int sts4x_read_sample(const struct device *dev, uint16_t *temp_sample)
67 {
68 	const struct sts4x_config *cfg = dev->config;
69 	uint8_t rx_buf[3];
70 	int ret;
71 
72 	ret = i2c_read_dt(&cfg->bus, rx_buf, sizeof(rx_buf));
73 	if (ret < 0) {
74 		LOG_ERR("Failed to read data.");
75 		return ret;
76 	}
77 
78 	*temp_sample = sys_get_be16(rx_buf);
79 	ret = sts4x_crc_check(*temp_sample, rx_buf[2]);
80 	if (ret < 0) {
81 		LOG_ERR("Invalid CRC.");
82 		return ret;
83 	}
84 
85 	return 0;
86 }
87 
sts4x_sample_fetch(const struct device * dev,enum sensor_channel chan)88 static int sts4x_sample_fetch(const struct device *dev, enum sensor_channel chan)
89 {
90 	struct sts4x_data *data = dev->data;
91 	const struct sts4x_config *cfg = dev->config;
92 	int ret;
93 
94 	if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_AMBIENT_TEMP) {
95 		ret = sts4x_write_command(dev, measure_cmds[cfg->repeatability]);
96 		if (ret < 0) {
97 			LOG_ERR("Failed to write measure command.");
98 			return ret;
99 		}
100 
101 		k_usleep(measure_time_us[cfg->repeatability]);
102 
103 		ret = sts4x_read_sample(dev, &data->temp_sample);
104 		if (ret < 0) {
105 			LOG_ERR("Failed to get temperature data.");
106 			return ret;
107 		}
108 
109 		return 0;
110 	} else {
111 		return -ENOTSUP;
112 	}
113 }
114 
sts4x_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)115 static int sts4x_channel_get(const struct device *dev, enum sensor_channel chan,
116 			     struct sensor_value *val)
117 {
118 	const struct sts4x_data *data = dev->data;
119 
120 	if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
121 		int64_t temp;
122 
123 		temp = data->temp_sample * STS4X_MAX_TEMP;
124 		val->val1 = (int32_t)(temp / 0xFFFF) + STS4X_MIN_TEMP;
125 		val->val2 = ((temp % 0xFFFF) * 1000000) / 0xFFFF;
126 	} else {
127 		return -ENOTSUP;
128 	}
129 	return 0;
130 }
131 
sts4x_init(const struct device * dev)132 static int sts4x_init(const struct device *dev)
133 {
134 	const struct sts4x_config *cfg = dev->config;
135 	int ret;
136 
137 	if (!i2c_is_ready_dt(&cfg->bus)) {
138 		LOG_ERR("Device not ready.");
139 		return -ENODEV;
140 	}
141 
142 	ret = sts4x_write_command(dev, STS4X_CMD_RESET);
143 	if (ret < 0) {
144 		LOG_ERR("Failed to reset the device.");
145 		return ret;
146 	}
147 
148 	k_msleep(STS4X_RESET_TIME);
149 
150 	return 0;
151 }
152 
153 static DEVICE_API(sensor, sts4x_api_funcs) = {
154 	.sample_fetch = sts4x_sample_fetch,
155 	.channel_get = sts4x_channel_get,
156 };
157 
158 #define STS4X_INIT(inst)                                                                           \
159 	static struct sts4x_data sts4x_data_##inst;                                                \
160 	static const struct sts4x_config sts4x_config_##inst = {                                   \
161 		.bus = I2C_DT_SPEC_INST_GET(inst),                                                 \
162 		.repeatability = DT_INST_PROP(inst, repeatability),                                \
163 	};                                                                                         \
164 	SENSOR_DEVICE_DT_INST_DEFINE(inst, sts4x_init, NULL, &sts4x_data_##inst,                   \
165 				     &sts4x_config_##inst, POST_KERNEL,                            \
166 				     CONFIG_SENSOR_INIT_PRIORITY, &sts4x_api_funcs);
167 
168 DT_INST_FOREACH_STATUS_OKAY(STS4X_INIT)
169