1 /* ST Microelectronics STTS22H temperature sensor
2  *
3  * Copyright (c) 2024 STMicroelectronics
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  *
7  * Datasheet:
8  * https://www.st.com/resource/en/datasheet/stts22h.pdf
9  */
10 
11 #define DT_DRV_COMPAT st_stts22h
12 
13 #include <zephyr/drivers/sensor.h>
14 #include <zephyr/kernel.h>
15 #include <zephyr/device.h>
16 #include <zephyr/init.h>
17 #include <zephyr/sys/byteorder.h>
18 #include <zephyr/sys/__assert.h>
19 #include <zephyr/logging/log.h>
20 
21 #include "stts22h.h"
22 
23 LOG_MODULE_REGISTER(STTS22H, CONFIG_SENSOR_LOG_LEVEL);
24 
25 #define ST22H_RANGE_LOWEST_TEMP	-40
26 #define ST22H_RANGE_HIGHEST_TEMP 127
27 
stts22h_set_odr_raw(const struct device * dev,stts22h_odr_temp_t odr)28 static inline int stts22h_set_odr_raw(const struct device *dev, stts22h_odr_temp_t odr)
29 {
30 	const struct stts22h_config *cfg = dev->config;
31 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
32 
33 	return stts22h_temp_data_rate_set(ctx, odr);
34 }
35 
stts22h_sample_fetch(const struct device * dev,enum sensor_channel chan)36 static int stts22h_sample_fetch(const struct device *dev, enum sensor_channel chan)
37 {
38 	struct stts22h_data *data = dev->data;
39 	const struct stts22h_config *cfg = dev->config;
40 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
41 	int16_t raw_temp;
42 
43 	if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) {
44 		LOG_ERR("Invalid channel: %d", chan);
45 		return -ENOTSUP;
46 	}
47 
48 	if (stts22h_temperature_raw_get(ctx, &raw_temp) < 0) {
49 		LOG_ERR("Failed to read sample");
50 		return -EIO;
51 	}
52 
53 	data->sample_temp = raw_temp;
54 
55 	return 0;
56 }
57 
stts22h_temp_convert(struct sensor_value * val,int16_t raw_val)58 static inline void stts22h_temp_convert(struct sensor_value *val, int16_t raw_val)
59 {
60 	val->val1 = raw_val / 100;
61 	val->val2 = ((int32_t)raw_val % 100) * 10000;
62 }
63 
stts22h_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)64 static int stts22h_channel_get(const struct device *dev,
65 			       enum sensor_channel chan,
66 			       struct sensor_value *val)
67 {
68 	struct stts22h_data *data = dev->data;
69 
70 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
71 		LOG_ERR("Invalid channel: %d", chan);
72 		return -ENOTSUP;
73 	}
74 
75 	stts22h_temp_convert(val, data->sample_temp);
76 
77 	return 0;
78 }
79 
80 static const uint8_t stts22h_map[6] = {0, 1, 25, 50, 100, 200};
81 
stts22h_odr_set(const struct device * dev,const struct sensor_value * val)82 static int stts22h_odr_set(const struct device *dev,
83 			   const struct sensor_value *val)
84 {
85 	int odr;
86 
87 	for (odr = 0; odr < ARRAY_SIZE(stts22h_map); odr++) {
88 		if (val->val1 <= stts22h_map[odr]) {
89 			break;
90 		}
91 	}
92 
93 	switch (odr) {
94 	case 0:
95 		return stts22h_set_odr_raw(dev, STTS22H_POWER_DOWN);
96 	case 1:
97 		return stts22h_set_odr_raw(dev, STTS22H_1Hz);
98 	case 2:
99 		return stts22h_set_odr_raw(dev, STTS22H_25Hz);
100 	case 3:
101 		return stts22h_set_odr_raw(dev, STTS22H_50Hz);
102 	case 4:
103 		return stts22h_set_odr_raw(dev, STTS22H_100Hz);
104 	case 5:
105 		return stts22h_set_odr_raw(dev, STTS22H_200Hz);
106 	default:
107 		LOG_ERR("bad frequency: %d (odr = %d)", val->val1, odr);
108 		return -EINVAL;
109 	}
110 }
111 #if CONFIG_STTS22H_TRIGGER
stts22h_val_to_threshold(const struct sensor_value * val)112 static inline uint32_t stts22h_val_to_threshold(const struct sensor_value *val)
113 {
114 	if (val->val1 < ST22H_RANGE_LOWEST_TEMP || val->val1 > ST22H_RANGE_HIGHEST_TEMP) {
115 		LOG_ERR("Invalid value: %d", val->val1);
116 		return UINT32_MAX;
117 	}
118 	/* The conversion formula is: (degC / 0.64) + 63, we multiply by 100*/
119 	int32_t temperature_degree_times100 = val->val1 * 100 + val->val2 / 10000;
120 	uint32_t raw_value = (temperature_degree_times100 / 64) + 63;
121 
122 	if (raw_value > 0xFF) {
123 		LOG_ERR("Invalid value: %d", raw_value);
124 		return UINT32_MAX;
125 	}
126 
127 	return raw_value;
128 }
129 #endif
130 
131 
stts22h_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)132 static int stts22h_attr_set(const struct device *dev,
133 			    enum sensor_channel chan,
134 			    enum sensor_attribute attr,
135 			    const struct sensor_value *val)
136 {
137 	if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) {
138 		LOG_ERR("Invalid channel: %d", chan);
139 		return -ENOTSUP;
140 	}
141 	switch (attr) {
142 	case SENSOR_ATTR_SAMPLING_FREQUENCY:
143 		return stts22h_odr_set(dev, val);
144 #if CONFIG_STTS22H_TRIGGER
145 	case SENSOR_ATTR_UPPER_THRESH: {
146 		const struct stts22h_config *cfg = dev->config;
147 		stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
148 
149 		uint32_t raw_value = stts22h_val_to_threshold(val);
150 
151 		if (raw_value == UINT32_MAX) {
152 			return -EINVAL;
153 		}
154 		if (stts22h_temp_trshld_high_set(ctx, (uint8_t)raw_value) < 0) {
155 			LOG_DBG("Could not set high threshold");
156 			return -EIO;
157 		}
158 		return 0;
159 	}
160 	case SENSOR_ATTR_LOWER_THRESH: {
161 		const struct stts22h_config *cfg = dev->config;
162 		stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
163 
164 		uint32_t raw_value = stts22h_val_to_threshold(val);
165 
166 		if (raw_value == UINT32_MAX) {
167 			return -EINVAL;
168 		}
169 		if (stts22h_temp_trshld_low_set(ctx, (uint8_t)raw_value) < 0) {
170 			LOG_DBG("Could not set low threshold ");
171 			return -EIO;
172 		}
173 		return 0;
174 	}
175 #endif
176 	default:
177 		LOG_ERR("Attribute %d not supported.", attr);
178 		return -ENOTSUP;
179 	}
180 
181 	return 0;
182 }
183 
184 static DEVICE_API(sensor, stts22h_api_funcs) = {
185 	.attr_set = stts22h_attr_set,
186 	.sample_fetch = stts22h_sample_fetch,
187 	.channel_get = stts22h_channel_get,
188 #if CONFIG_STTS22H_TRIGGER
189 	.trigger_set = stts22h_trigger_set,
190 #endif
191 };
192 
stts22h_init_chip(const struct device * dev)193 static int stts22h_init_chip(const struct device *dev)
194 {
195 	const struct stts22h_config *cfg = dev->config;
196 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
197 	uint8_t chip_id, odr;
198 
199 	if (stts22h_dev_id_get(ctx, &chip_id) < 0) {
200 		LOG_ERR("Failed reading chip id");
201 		return -EIO;
202 	}
203 
204 	LOG_INF("chip id 0x%02x", chip_id);
205 
206 	if (stts22h_auto_increment_set(ctx, 1) < 0) {
207 		LOG_ERR("Failed to set autoincr");
208 		return -EIO;
209 	}
210 
211 	/* set odr from DT */
212 	odr = cfg->odr;
213 	LOG_INF("sensor odr is %d", odr);
214 	if (stts22h_set_odr_raw(dev, odr) < 0) {
215 		LOG_ERR("Failed to set sampling rate");
216 		return -EIO;
217 	}
218 
219 	return 0;
220 }
221 
stts22h_init(const struct device * dev)222 static int stts22h_init(const struct device *dev)
223 {
224 	struct stts22h_data *data = dev->data;
225 #ifdef CONFIG_STTS22H_TRIGGER
226 	const struct stts22h_config *cfg = dev->config;
227 #endif
228 
229 	LOG_INF("Initialize device %s", dev->name);
230 	data->dev = dev;
231 
232 	if (stts22h_init_chip(dev) < 0) {
233 		LOG_ERR("Failed to initialize chip");
234 		return -EIO;
235 	}
236 
237 #ifdef CONFIG_STTS22H_TRIGGER
238 	if (cfg->int_gpio.port) {
239 		if (stts22h_init_interrupt(dev) < 0) {
240 			LOG_ERR("Failed to initialize interrupt.");
241 			return -EIO;
242 		}
243 	}
244 #endif
245 
246 	return 0;
247 }
248 
249 #define STTS22H_DEFINE(inst)									\
250 	static struct stts22h_data stts22h_data_##inst;						\
251 												\
252 	static const struct stts22h_config stts22h_config_##inst = {				\
253 		STMEMSC_CTX_I2C(&stts22h_config_##inst.i2c),					\
254 		.i2c = I2C_DT_SPEC_INST_GET(inst),						\
255 		.temp_hi = DT_INST_PROP(inst, temperature_hi_threshold),			\
256 		.temp_lo = DT_INST_PROP(inst, temperature_lo_threshold),			\
257 		.odr = DT_INST_PROP(inst, sampling_rate),					\
258 		IF_ENABLED(CONFIG_STTS22H_TRIGGER,						\
259 			   (.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, { 0 }),))	\
260 	};											\
261 												\
262 	SENSOR_DEVICE_DT_INST_DEFINE(inst, stts22h_init, NULL,					\
263 			      &stts22h_data_##inst, &stts22h_config_##inst, POST_KERNEL,	\
264 			      CONFIG_SENSOR_INIT_PRIORITY, &stts22h_api_funcs);			\
265 
266 DT_INST_FOREACH_STATUS_OKAY(STTS22H_DEFINE)
267