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