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