1 /* ST Microelectronics LPS28DFW pressure and temperature sensor
2 *
3 * Copyright (c) 2023 STMicroelectronics
4 * Copyright (c) 2023 PHYTEC Messtechnik GmbH
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include "lps2xdf.h"
10 #include "lps28dfw.h"
11 #include <zephyr/logging/log.h>
12
13 LOG_MODULE_DECLARE(LPS2XDF, CONFIG_SENSOR_LOG_LEVEL);
14
lps28dfw_mode_set_odr_raw(const struct device * dev,uint8_t odr)15 static inline int lps28dfw_mode_set_odr_raw(const struct device *dev, uint8_t odr)
16 {
17 const struct lps2xdf_config *const cfg = dev->config;
18 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
19 lps28dfw_md_t md;
20
21 md.odr = odr;
22 md.avg = cfg->avg;
23 md.lpf = cfg->lpf;
24 md.fs = cfg->fs;
25
26 return lps28dfw_mode_set(ctx, &md);
27 }
28
lps28dfw_sample_fetch(const struct device * dev,enum sensor_channel chan)29 static int lps28dfw_sample_fetch(const struct device *dev, enum sensor_channel chan)
30 {
31 struct lps2xdf_data *data = dev->data;
32 const struct lps2xdf_config *const cfg = dev->config;
33 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
34 lps28dfw_data_t raw_data;
35 lps28dfw_md_t md;
36
37 md.fs = cfg->fs;
38
39 if (lps28dfw_data_get(ctx, &md, &raw_data) < 0) {
40 LOG_DBG("Failed to read sample");
41 return -EIO;
42 }
43
44 data->sample_press = raw_data.pressure.raw;
45 data->sample_temp = raw_data.heat.raw;
46
47 return 0;
48 }
49
50 #ifdef CONFIG_LPS2XDF_TRIGGER
51 /**
52 * lps28dfw_config_interrupt - config the interrupt mode
53 */
lps28dfw_config_interrupt(const struct device * dev)54 static int lps28dfw_config_interrupt(const struct device *dev)
55 {
56 const struct lps2xdf_config *const cfg = dev->config;
57 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
58 lps28dfw_int_mode_t mode;
59
60 if (lps28dfw_interrupt_mode_get(ctx, &mode) < 0) {
61 return -EIO;
62 }
63
64 mode.drdy_latched = ~cfg->drdy_pulsed;
65
66 return lps28dfw_interrupt_mode_set(ctx, &mode);
67 }
68
69 /**
70 * lps28dfw_handle_interrupt - handle the drdy event
71 * read data and call handler if registered any
72 */
lps28dfw_handle_interrupt(const struct device * dev)73 static void lps28dfw_handle_interrupt(const struct device *dev)
74 {
75 int ret;
76 struct lps2xdf_data *lps28dfw = dev->data;
77 const struct lps2xdf_config *cfg = dev->config;
78 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
79 lps28dfw_all_sources_t status;
80
81 if (lps28dfw_all_sources_get(ctx, &status) < 0) {
82 LOG_DBG("failed reading status reg");
83 goto exit;
84 }
85
86 if (status.drdy_pres == 0) {
87 goto exit; /* spurious interrupt */
88 }
89
90 if (lps28dfw->handler_drdy != NULL) {
91 lps28dfw->handler_drdy(dev, lps28dfw->data_ready_trigger);
92 }
93
94 if (ON_I3C_BUS(cfg)) {
95 /*
96 * I3C IBI does not rely on GPIO.
97 * So no need to enable GPIO pin for interrupt trigger.
98 */
99 return;
100 }
101
102 exit:
103 ret = gpio_pin_interrupt_configure_dt(&cfg->gpio_int,
104 GPIO_INT_EDGE_TO_ACTIVE);
105 if (ret < 0) {
106 LOG_ERR("%s: Not able to configure pin_int", dev->name);
107 }
108 }
109
110 /**
111 * lps28dfw_enable_int - enable selected int pin to generate interrupt
112 */
lps28dfw_enable_int(const struct device * dev,int enable)113 static int lps28dfw_enable_int(const struct device *dev, int enable)
114 {
115 const struct lps2xdf_config * const cfg = dev->config;
116 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
117 lps28dfw_pin_int_route_t int_route;
118
119 /* set interrupt */
120 lps28dfw_pin_int_route_get(ctx, &int_route);
121 int_route.drdy_pres = enable;
122 return lps28dfw_pin_int_route_set(ctx, &int_route);
123 }
124
125 /**
126 * lps28dfw_trigger_set - link external trigger to event data ready
127 */
lps28dfw_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)128 static int lps28dfw_trigger_set(const struct device *dev,
129 const struct sensor_trigger *trig,
130 sensor_trigger_handler_t handler)
131 {
132 struct lps2xdf_data *lps28dfw = dev->data;
133 const struct lps2xdf_config * const cfg = dev->config;
134 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
135 lps28dfw_data_t raw_data;
136 lps28dfw_md_t md;
137
138 md.fs = cfg->fs;
139
140 if (trig->chan != SENSOR_CHAN_ALL) {
141 LOG_WRN("trigger set not supported on this channel.");
142 return -ENOTSUP;
143 }
144
145 lps28dfw->handler_drdy = handler;
146 lps28dfw->data_ready_trigger = trig;
147 if (handler) {
148 /* dummy read: re-trigger interrupt */
149 if (lps28dfw_data_get(ctx, &md, &raw_data) < 0) {
150 LOG_DBG("Failed to read sample");
151 return -EIO;
152 }
153 return lps28dfw_enable_int(dev, 1);
154 } else {
155 return lps28dfw_enable_int(dev, 0);
156 }
157
158 return -ENOTSUP;
159 }
160 #endif /* CONFIG_LPS2XDF_TRIGGER */
161
162 const struct lps2xdf_chip_api st_lps28dfw_chip_api = {
163 .mode_set_odr_raw = lps28dfw_mode_set_odr_raw,
164 .sample_fetch = lps28dfw_sample_fetch,
165 #if CONFIG_LPS2XDF_TRIGGER
166 .config_interrupt = lps28dfw_config_interrupt,
167 .handle_interrupt = lps28dfw_handle_interrupt,
168 .trigger_set = lps28dfw_trigger_set,
169 #endif
170 };
171
st_lps28dfw_init(const struct device * dev)172 int st_lps28dfw_init(const struct device *dev)
173 {
174 const struct lps2xdf_config *const cfg = dev->config;
175 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
176 lps28dfw_id_t id;
177 lps28dfw_stat_t status;
178 uint8_t tries = 10;
179 int ret;
180
181 #if DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(st_lps28dfw, i3c)
182 if (cfg->i3c.bus != NULL) {
183 struct lps2xdf_data *data = dev->data;
184 /*
185 * Need to grab the pointer to the I3C device descriptor
186 * before we can talk to the sensor.
187 */
188 data->i3c_dev = i3c_device_find(cfg->i3c.bus, &cfg->i3c.dev_id);
189 if (data->i3c_dev == NULL) {
190 LOG_ERR("Cannot find I3C device descriptor");
191 return -ENODEV;
192 }
193 }
194 #endif
195
196 if (lps28dfw_id_get(ctx, &id) < 0) {
197 LOG_ERR("%s: Not able to read dev id", dev->name);
198 return -EIO;
199 }
200
201 if (id.whoami != LPS28DFW_ID) {
202 LOG_ERR("%s: Invalid chip ID 0x%02x", dev->name, id.whoami);
203 return -EIO;
204 }
205
206 LOG_DBG("%s: chip id 0x%x", dev->name, id.whoami);
207
208 /* Restore default configuration */
209 if (lps28dfw_init_set(ctx, LPS28DFW_RESET) < 0) {
210 LOG_ERR("%s: Not able to reset device", dev->name);
211 return -EIO;
212 }
213
214 do {
215 if (!--tries) {
216 LOG_DBG("sw reset timed out");
217 return -ETIMEDOUT;
218 }
219 k_usleep(LPS2XDF_SWRESET_WAIT_TIME_US);
220
221 if (lps28dfw_status_get(ctx, &status) < 0) {
222 return -EIO;
223 }
224 } while (status.sw_reset);
225
226 /* Set bdu and if_inc recommended for driver usage */
227 if (lps28dfw_init_set(ctx, LPS28DFW_DRV_RDY) < 0) {
228 LOG_ERR("%s: Not able to set device to ready state", dev->name);
229 return -EIO;
230 }
231
232 if (ON_I3C_BUS(cfg)) {
233 lps28dfw_bus_mode_t bus_mode;
234
235 /* Select bus interface */
236 lps28dfw_bus_mode_get(ctx, &bus_mode);
237 bus_mode.filter = LPS28DFW_AUTO;
238 bus_mode.interface = LPS28DFW_SEL_BY_HW;
239 lps28dfw_bus_mode_set(ctx, &bus_mode);
240 }
241
242 /* set sensor default odr */
243 LOG_DBG("%s: odr: %d", dev->name, cfg->odr);
244 ret = lps28dfw_mode_set_odr_raw(dev, cfg->odr);
245 if (ret < 0) {
246 LOG_ERR("%s: Failed to set odr %d", dev->name, cfg->odr);
247 return ret;
248 }
249
250 #ifdef CONFIG_LPS2XDF_TRIGGER
251 if (cfg->trig_enabled) {
252 if (lps2xdf_init_interrupt(dev, DEVICE_VARIANT_LPS28DFW) < 0) {
253 LOG_ERR("Failed to initialize interrupt.");
254 return -EIO;
255 }
256 }
257 #endif
258
259 return 0;
260 }
261