1 /* ST Microelectronics LPS22DF 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 "lps22df.h"
11 #include <zephyr/logging/log.h>
12 
13 LOG_MODULE_DECLARE(LPS2XDF, CONFIG_SENSOR_LOG_LEVEL);
14 
lps22df_mode_set_odr_raw(const struct device * dev,uint8_t odr)15 static inline int lps22df_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 	lps22df_md_t md;
20 
21 	md.odr = odr;
22 	md.avg = cfg->avg;
23 	md.lpf = cfg->lpf;
24 
25 	return lps22df_mode_set(ctx, &md);
26 }
27 
lps22df_sample_fetch(const struct device * dev,enum sensor_channel chan)28 static int lps22df_sample_fetch(const struct device *dev, enum sensor_channel chan)
29 {
30 	struct lps2xdf_data *data = dev->data;
31 	const struct lps2xdf_config *const cfg = dev->config;
32 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
33 	lps22df_data_t raw_data;
34 
35 	if (lps22df_data_get(ctx, &raw_data) < 0) {
36 		LOG_DBG("Failed to read sample");
37 		return -EIO;
38 	}
39 
40 	data->sample_press = raw_data.pressure.raw;
41 	data->sample_temp = raw_data.heat.raw;
42 
43 	return 0;
44 }
45 
46 #ifdef CONFIG_LPS2XDF_TRIGGER
47 /**
48  * lps22df_config_interrupt - config the interrupt mode
49  */
lps22df_config_interrupt(const struct device * dev)50 static int lps22df_config_interrupt(const struct device *dev)
51 {
52 	const struct lps2xdf_config *const cfg = dev->config;
53 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
54 	lps22df_int_mode_t mode;
55 
56 	if (lps22df_interrupt_mode_get(ctx, &mode) < 0) {
57 		return -EIO;
58 	}
59 
60 	mode.drdy_latched = ~cfg->drdy_pulsed;
61 
62 	return lps22df_interrupt_mode_set(ctx, &mode);
63 }
64 
65 /**
66  * lps22df_handle_interrupt - handle the drdy event
67  * read data and call handler if registered any
68  */
lps22df_handle_interrupt(const struct device * dev)69 static void lps22df_handle_interrupt(const struct device *dev)
70 {
71 	int ret;
72 	struct lps2xdf_data *lps22df = dev->data;
73 	const struct lps2xdf_config *cfg = dev->config;
74 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
75 	lps22df_all_sources_t status;
76 
77 	if (lps22df_all_sources_get(ctx, &status) < 0) {
78 		LOG_DBG("failed reading status reg");
79 		goto exit;
80 	}
81 
82 	if (status.drdy_pres == 0) {
83 		goto exit; /* spurious interrupt */
84 	}
85 
86 	if (lps22df->handler_drdy != NULL) {
87 		lps22df->handler_drdy(dev, lps22df->data_ready_trigger);
88 	}
89 
90 	if (ON_I3C_BUS(cfg)) {
91 		/*
92 		 * I3C IBI does not rely on GPIO.
93 		 * So no need to enable GPIO pin for interrupt trigger.
94 		 */
95 		return;
96 	}
97 
98 exit:
99 	ret = gpio_pin_interrupt_configure_dt(&cfg->gpio_int,
100 					      GPIO_INT_EDGE_TO_ACTIVE);
101 	if (ret < 0) {
102 		LOG_ERR("%s: Not able to configure pin_int", dev->name);
103 	}
104 }
105 
106 /**
107  * lps22df_enable_int - enable selected int pin to generate interrupt
108  */
lps22df_enable_int(const struct device * dev,int enable)109 static int lps22df_enable_int(const struct device *dev, int enable)
110 {
111 	const struct lps2xdf_config * const cfg = dev->config;
112 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
113 	lps22df_pin_int_route_t int_route;
114 
115 	/* set interrupt */
116 	lps22df_pin_int_route_get(ctx, &int_route);
117 	int_route.drdy_pres = enable;
118 	return lps22df_pin_int_route_set(ctx, &int_route);
119 }
120 
121 /**
122  * lps22df_trigger_set - link external trigger to event data ready
123  */
lps22df_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)124 static int lps22df_trigger_set(const struct device *dev,
125 			  const struct sensor_trigger *trig,
126 			  sensor_trigger_handler_t handler)
127 {
128 	struct lps2xdf_data *lps22df = dev->data;
129 	const struct lps2xdf_config * const cfg = dev->config;
130 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
131 	lps22df_data_t raw_data;
132 
133 	if (trig->chan != SENSOR_CHAN_ALL) {
134 		LOG_WRN("trigger set not supported on this channel.");
135 		return -ENOTSUP;
136 	}
137 
138 	lps22df->handler_drdy = handler;
139 	lps22df->data_ready_trigger = trig;
140 	if (handler) {
141 		/* dummy read: re-trigger interrupt */
142 		if (lps22df_data_get(ctx, &raw_data) < 0) {
143 			LOG_DBG("Failed to read sample");
144 			return -EIO;
145 		}
146 		return lps22df_enable_int(dev, 1);
147 	} else {
148 		return lps22df_enable_int(dev, 0);
149 	}
150 
151 	return -ENOTSUP;
152 }
153 #endif /* CONFIG_LPS2XDF_TRIGGER */
154 
155 const struct lps2xdf_chip_api st_lps22df_chip_api = {
156 	.mode_set_odr_raw = lps22df_mode_set_odr_raw,
157 	.sample_fetch = lps22df_sample_fetch,
158 #if CONFIG_LPS2XDF_TRIGGER
159 	.config_interrupt = lps22df_config_interrupt,
160 	.handle_interrupt = lps22df_handle_interrupt,
161 	.trigger_set = lps22df_trigger_set,
162 #endif
163 };
164 
st_lps22df_init(const struct device * dev)165 int st_lps22df_init(const struct device *dev)
166 {
167 	const struct lps2xdf_config *const cfg = dev->config;
168 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
169 	lps22df_id_t id;
170 	lps22df_stat_t status;
171 	uint8_t tries = 10;
172 	int ret;
173 
174 #if DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(st_lps22df, i3c)
175 	if (cfg->i3c.bus != NULL) {
176 		struct lps2xdf_data *data = dev->data;
177 		/*
178 		 * Need to grab the pointer to the I3C device descriptor
179 		 * before we can talk to the sensor.
180 		 */
181 		data->i3c_dev = i3c_device_find(cfg->i3c.bus, &cfg->i3c.dev_id);
182 		if (data->i3c_dev == NULL) {
183 			LOG_ERR("Cannot find I3C device descriptor");
184 			return -ENODEV;
185 		}
186 	}
187 #endif
188 
189 	if (lps22df_id_get(ctx, &id) < 0) {
190 		LOG_ERR("%s: Not able to read dev id", dev->name);
191 		return -EIO;
192 	}
193 
194 	if (id.whoami != LPS22DF_ID) {
195 		LOG_ERR("%s: Invalid chip ID 0x%02x", dev->name, id.whoami);
196 		return -EIO;
197 	}
198 
199 	LOG_DBG("%s: chip id 0x%x", dev->name, id.whoami);
200 
201 	/* Restore default configuration */
202 	if (lps22df_init_set(ctx, LPS22DF_RESET) < 0) {
203 		LOG_ERR("%s: Not able to reset device", dev->name);
204 		return -EIO;
205 	}
206 
207 	do {
208 		if (!--tries) {
209 			LOG_DBG("sw reset timed out");
210 			return -ETIMEDOUT;
211 		}
212 		k_usleep(LPS2XDF_SWRESET_WAIT_TIME_US);
213 
214 		if (lps22df_status_get(ctx, &status) < 0) {
215 			return -EIO;
216 		}
217 	} while (status.sw_reset);
218 
219 	/* Set bdu and if_inc recommended for driver usage */
220 	if (lps22df_init_set(ctx, LPS22DF_DRV_RDY) < 0) {
221 		LOG_ERR("%s: Not able to set device to ready state", dev->name);
222 		return -EIO;
223 	}
224 
225 	if (ON_I3C_BUS(cfg)) {
226 		lps22df_bus_mode_t bus_mode;
227 
228 		/* Select bus interface */
229 		lps22df_bus_mode_get(ctx, &bus_mode);
230 		bus_mode.filter = LPS22DF_FILTER_AUTO;
231 		bus_mode.interface = LPS22DF_SEL_BY_HW;
232 		lps22df_bus_mode_set(ctx, &bus_mode);
233 	}
234 
235 	/* set sensor default odr */
236 	LOG_DBG("%s: odr: %d", dev->name, cfg->odr);
237 	ret = lps22df_mode_set_odr_raw(dev, cfg->odr);
238 	if (ret < 0) {
239 		LOG_ERR("%s: Failed to set odr %d", dev->name, cfg->odr);
240 		return ret;
241 	}
242 
243 #ifdef CONFIG_LPS2XDF_TRIGGER
244 	if (cfg->trig_enabled) {
245 		if (lps2xdf_init_interrupt(dev, DEVICE_VARIANT_LPS22DF) < 0) {
246 			LOG_ERR("Failed to initialize interrupt.");
247 			return -EIO;
248 		}
249 	}
250 #endif
251 
252 	return 0;
253 }
254