1 /* ST Microelectronics LPS22HH pressure and temperature sensor
2  *
3  * Copyright (c) 2019 STMicroelectronics
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  *
7  * Datasheet:
8  * https://www.st.com/resource/en/datasheet/lps22hh.pdf
9  */
10 
11 #define DT_DRV_COMPAT st_lps22hh
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 "lps22hh.h"
22 
23 LOG_MODULE_REGISTER(LPS22HH, CONFIG_SENSOR_LOG_LEVEL);
24 
lps22hh_set_odr_raw(const struct device * dev,uint8_t odr)25 static inline int lps22hh_set_odr_raw(const struct device *dev, uint8_t odr)
26 {
27 	const struct lps22hh_config * const cfg = dev->config;
28 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
29 
30 	return lps22hh_data_rate_set(ctx, odr);
31 }
32 
lps22hh_sample_fetch(const struct device * dev,enum sensor_channel chan)33 static int lps22hh_sample_fetch(const struct device *dev,
34 				enum sensor_channel chan)
35 {
36 	struct lps22hh_data *data = dev->data;
37 	const struct lps22hh_config * const cfg = dev->config;
38 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
39 	uint32_t raw_press;
40 	int16_t raw_temp;
41 
42 	__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL
43 			|| chan == SENSOR_CHAN_PRESS
44 			|| chan == SENSOR_CHAN_AMBIENT_TEMP);
45 
46 	switch (chan) {
47 	case SENSOR_CHAN_PRESS:
48 		if (lps22hh_pressure_raw_get(ctx, &raw_press) < 0) {
49 			LOG_DBG("Failed to read sample");
50 			return -EIO;
51 		}
52 		break;
53 	case SENSOR_CHAN_AMBIENT_TEMP:
54 		if (lps22hh_temperature_raw_get(ctx, &raw_temp) < 0) {
55 			LOG_DBG("Failed to read sample");
56 			return -EIO;
57 		}
58 		break;
59 	case SENSOR_CHAN_ALL:
60 		if (lps22hh_pressure_raw_get(ctx, &raw_press) < 0) {
61 			LOG_DBG("Failed to read sample");
62 			return -EIO;
63 		}
64 		if (lps22hh_temperature_raw_get(ctx, &raw_temp) < 0) {
65 			LOG_DBG("Failed to read sample");
66 			return -EIO;
67 		}
68 		break;
69 	default:
70 		LOG_ERR("Unsupported sensor channel");
71 		return -ENOTSUP;
72 	}
73 
74 	/** Prevent overwriting values that weren't fetched */
75 	if (chan == SENSOR_CHAN_PRESS || chan == SENSOR_CHAN_ALL) {
76 		data->sample_press = raw_press;
77 	}
78 	if (chan == SENSOR_CHAN_AMBIENT_TEMP || chan == SENSOR_CHAN_ALL) {
79 		data->sample_temp = raw_temp;
80 	}
81 
82 	return 0;
83 }
84 
lps22hh_press_convert(struct sensor_value * val,int32_t raw_val)85 static inline void lps22hh_press_convert(struct sensor_value *val,
86 					 int32_t raw_val)
87 {
88 	int32_t press_tmp = raw_val >> 8; /* raw value is left aligned (24 msb) */
89 
90 	/* Pressure sensitivity is 4096 LSB/hPa */
91 	/* Also convert hPa into kPa */
92 
93 	val->val1 = press_tmp / 40960;
94 
95 	/* For the decimal part use (3125 / 128) as a factor instead of
96 	 * (1000000 / 40960) to avoid int32 overflow
97 	 */
98 	val->val2 = (press_tmp % 40960) * 3125 / 128;
99 }
100 
lps22hh_temp_convert(struct sensor_value * val,int16_t raw_val)101 static inline void lps22hh_temp_convert(struct sensor_value *val,
102 					int16_t raw_val)
103 {
104 	/* Temperature sensitivity is 100 LSB/deg C */
105 	val->val1 = raw_val / 100;
106 	val->val2 = ((int32_t)raw_val % 100) * 10000;
107 }
108 
lps22hh_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)109 static int lps22hh_channel_get(const struct device *dev,
110 			       enum sensor_channel chan,
111 			       struct sensor_value *val)
112 {
113 	struct lps22hh_data *data = dev->data;
114 
115 	if (chan == SENSOR_CHAN_PRESS) {
116 		lps22hh_press_convert(val, data->sample_press);
117 	} else if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
118 		lps22hh_temp_convert(val, data->sample_temp);
119 	} else {
120 		return -ENOTSUP;
121 	}
122 
123 	return 0;
124 }
125 
126 static const uint16_t lps22hh_map[] = {0, 1, 10, 25, 50, 75, 100, 200};
127 
lps22hh_odr_set(const struct device * dev,uint16_t freq)128 static int lps22hh_odr_set(const struct device *dev, uint16_t freq)
129 {
130 	int odr;
131 
132 	for (odr = 0; odr < ARRAY_SIZE(lps22hh_map); odr++) {
133 		if (freq == lps22hh_map[odr]) {
134 			break;
135 		}
136 	}
137 
138 	if (odr == ARRAY_SIZE(lps22hh_map)) {
139 		LOG_DBG("bad frequency");
140 		return -EINVAL;
141 	}
142 
143 	if (lps22hh_set_odr_raw(dev, odr) < 0) {
144 		LOG_DBG("failed to set sampling rate");
145 		return -EIO;
146 	}
147 
148 	return 0;
149 }
150 
lps22hh_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)151 static int lps22hh_attr_set(const struct device *dev,
152 			    enum sensor_channel chan,
153 			    enum sensor_attribute attr,
154 			    const struct sensor_value *val)
155 {
156 	if (chan != SENSOR_CHAN_ALL) {
157 		LOG_WRN("attr_set() not supported on this channel.");
158 		return -ENOTSUP;
159 	}
160 
161 	switch (attr) {
162 	case SENSOR_ATTR_SAMPLING_FREQUENCY:
163 		return lps22hh_odr_set(dev, val->val1);
164 	default:
165 		LOG_DBG("operation not supported.");
166 		return -ENOTSUP;
167 	}
168 
169 	return 0;
170 }
171 
172 static DEVICE_API(sensor, lps22hh_driver_api) = {
173 	.attr_set = lps22hh_attr_set,
174 	.sample_fetch = lps22hh_sample_fetch,
175 	.channel_get = lps22hh_channel_get,
176 #if CONFIG_LPS22HH_TRIGGER
177 	.trigger_set = lps22hh_trigger_set,
178 #endif
179 };
180 
lps22hh_init_chip(const struct device * dev)181 static int lps22hh_init_chip(const struct device *dev)
182 {
183 	const struct lps22hh_config * const cfg = dev->config;
184 	struct lps22hh_data *data = dev->data;
185 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
186 	uint8_t chip_id;
187 	int ret;
188 
189 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
190 	if (cfg->i3c.bus != NULL) {
191 		/*
192 		 * Need to grab the pointer to the I3C device descriptor
193 		 * before we can talk to the sensor.
194 		 */
195 		data->i3c_dev = i3c_device_find(cfg->i3c.bus, &cfg->i3c.dev_id);
196 		if (data->i3c_dev == NULL) {
197 			LOG_ERR("Cannot find I3C device descriptor");
198 			return -ENODEV;
199 		}
200 	}
201 #endif
202 
203 	if (lps22hh_device_id_get(ctx, &chip_id) < 0) {
204 		LOG_ERR("%s: Not able to read dev id", dev->name);
205 		return -EIO;
206 	}
207 
208 	if (chip_id != LPS22HH_ID) {
209 		LOG_ERR("%s: Invalid chip ID 0x%02x", dev->name, chip_id);
210 		return -EIO;
211 	}
212 
213 	LOG_DBG("%s: chip id 0x%x", dev->name, chip_id);
214 
215 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
216 	if (cfg->i3c.bus != NULL) {
217 		/*
218 		 * Enabling I3C and disabling I2C are required
219 		 * for I3C IBI to work, or else the sensor will not
220 		 * send any IBIs.
221 		 */
222 
223 		ret = lps22hh_i3c_interface_set(ctx, LPS22HH_I3C_ENABLE);
224 		if (ret < 0) {
225 			LOG_ERR("Cannot enable I3C interface");
226 			return ret;
227 		}
228 
229 		ret = lps22hh_i2c_interface_set(ctx, LPS22HH_I2C_DISABLE);
230 		if (ret < 0) {
231 			LOG_ERR("Cannot disable I2C interface");
232 			return ret;
233 		}
234 	}
235 #else
236 	ARG_UNUSED(data);
237 #endif
238 
239 	/* set sensor default odr */
240 	LOG_DBG("%s: odr: %d", dev->name, cfg->odr);
241 	ret = lps22hh_set_odr_raw(dev, cfg->odr);
242 	if (ret < 0) {
243 		LOG_ERR("%s: Failed to set odr %d", dev->name, cfg->odr);
244 		return ret;
245 	}
246 
247 	if (lps22hh_block_data_update_set(ctx, PROPERTY_ENABLE) < 0) {
248 		LOG_ERR("%s: Failed to set BDU", dev->name);
249 		return ret;
250 	}
251 
252 	return 0;
253 }
254 
lps22hh_init(const struct device * dev)255 static int lps22hh_init(const struct device *dev)
256 {
257 	if (lps22hh_init_chip(dev) < 0) {
258 		LOG_DBG("Failed to initialize chip");
259 		return -EIO;
260 	}
261 
262 #ifdef CONFIG_LPS22HH_TRIGGER
263 	if (lps22hh_init_interrupt(dev) < 0) {
264 		LOG_ERR("Failed to initialize interrupt.");
265 		return -EIO;
266 	}
267 #endif
268 
269 	return 0;
270 }
271 
272 #if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 0
273 #warning "LPS22HH driver enabled without any devices"
274 #endif
275 
276 /*
277  * Instantiation macros used when a device is on a SPI bus.
278  */
279 
280 #ifdef CONFIG_LPS22HH_TRIGGER
281 #define LPS22HH_CFG_IRQ(inst) \
282 	.gpio_int = GPIO_DT_SPEC_INST_GET(inst, drdy_gpios),
283 #else
284 #define LPS22HH_CFG_IRQ(inst)
285 #endif /* CONFIG_LPS22HH_TRIGGER */
286 
287 #define LPS22HH_CONFIG_COMMON(inst)					\
288 	.odr = DT_INST_PROP(inst, odr),					\
289 	COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, drdy_gpios),		\
290 			(LPS22HH_CFG_IRQ(inst)), ())
291 
292 #define LPS22HH_SPI_OPERATION (SPI_WORD_SET(8) |			\
293 				SPI_OP_MODE_MASTER |			\
294 				SPI_MODE_CPOL |				\
295 				SPI_MODE_CPHA)				\
296 
297 #define LPS22HH_CONFIG_SPI(inst)					\
298 	{								\
299 		STMEMSC_CTX_SPI(&lps22hh_config_##inst.stmemsc_cfg),	\
300 		.stmemsc_cfg = {					\
301 			.spi = SPI_DT_SPEC_INST_GET(inst,		\
302 					   LPS22HH_SPI_OPERATION,	\
303 					   0),				\
304 		},							\
305 		LPS22HH_CONFIG_COMMON(inst)				\
306 	}
307 
308 /*
309  * Instantiation macros used when a device is on an I2C bus.
310  */
311 
312 #define LPS22HH_CONFIG_I2C(inst)					\
313 	{								\
314 		STMEMSC_CTX_I2C(&lps22hh_config_##inst.stmemsc_cfg),	\
315 		.stmemsc_cfg = {					\
316 			.i2c = I2C_DT_SPEC_INST_GET(inst),		\
317 		},							\
318 		LPS22HH_CONFIG_COMMON(inst)				\
319 	}
320 
321 /*
322  * Instantiation macros used when a device is on an I#C bus.
323  */
324 
325 #define LPS22HH_CONFIG_I3C(inst)					\
326 	{								\
327 		STMEMSC_CTX_I3C(&lps22hh_config_##inst.stmemsc_cfg),	\
328 		.stmemsc_cfg = {					\
329 			.i3c = &lps22hh_data_##inst.i3c_dev,		\
330 		},							\
331 		.i3c.bus = DEVICE_DT_GET(DT_INST_BUS(inst)),		\
332 		.i3c.dev_id = I3C_DEVICE_ID_DT_INST(inst),		\
333 		LPS22HH_CONFIG_COMMON(inst)				\
334 	}
335 
336 #define LPS22HH_CONFIG_I3C_OR_I2C(inst)					\
337 	COND_CODE_0(DT_INST_PROP_BY_IDX(inst, reg, 1),			\
338 		    (LPS22HH_CONFIG_I2C(inst)),				\
339 		    (LPS22HH_CONFIG_I3C(inst)))
340 
341 /*
342  * Main instantiation macro. Use of COND_CODE_1() selects the right
343  * bus-specific macro at preprocessor time.
344  */
345 
346 #define LPS22HH_DEFINE(inst)							\
347 	static struct lps22hh_data lps22hh_data_##inst;				\
348 	static const struct lps22hh_config lps22hh_config_##inst =		\
349 	COND_CODE_1(DT_INST_ON_BUS(inst, spi),					\
350 		    (LPS22HH_CONFIG_SPI(inst)),					\
351 		    (COND_CODE_1(DT_INST_ON_BUS(inst, i3c),			\
352 				 (LPS22HH_CONFIG_I3C_OR_I2C(inst)),		\
353 				 (LPS22HH_CONFIG_I2C(inst)))));			\
354 	SENSOR_DEVICE_DT_INST_DEFINE(inst, lps22hh_init, NULL, &lps22hh_data_##inst,	\
355 			      &lps22hh_config_##inst, POST_KERNEL,		\
356 			      CONFIG_SENSOR_INIT_PRIORITY, &lps22hh_driver_api);
357 
358 DT_INST_FOREACH_STATUS_OKAY(LPS22HH_DEFINE)
359