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