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/kernel.h>
14 #include <zephyr/drivers/sensor.h>
15 #include <zephyr/drivers/gpio.h>
16 #include <zephyr/logging/log.h>
17 
18 #include "lps22hh.h"
19 
20 LOG_MODULE_DECLARE(LPS22HH, CONFIG_SENSOR_LOG_LEVEL);
21 
22 /**
23  * lps22hh_enable_int - enable selected int pin to generate interrupt
24  */
lps22hh_enable_int(const struct device * dev,int enable)25 static int lps22hh_enable_int(const struct device *dev, int enable)
26 {
27 	const struct lps22hh_config * const cfg = dev->config;
28 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
29 	lps22hh_pin_int_route_t int_route;
30 
31 	/* set interrupt */
32 	lps22hh_pin_int_route_get(ctx, &int_route);
33 	int_route.drdy_pres = enable;
34 	return lps22hh_pin_int_route_set(ctx, &int_route);
35 }
36 
37 /**
38  * lps22hh_trigger_set - link external trigger to event data ready
39  */
lps22hh_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)40 int lps22hh_trigger_set(const struct device *dev,
41 			  const struct sensor_trigger *trig,
42 			  sensor_trigger_handler_t handler)
43 {
44 	struct lps22hh_data *lps22hh = dev->data;
45 	const struct lps22hh_config * const cfg = dev->config;
46 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
47 	uint32_t raw_press;
48 
49 	if (trig->chan == SENSOR_CHAN_ALL) {
50 		lps22hh->handler_drdy = handler;
51 		lps22hh->data_ready_trigger = trig;
52 		if (handler) {
53 			/* dummy read: re-trigger interrupt */
54 			if (lps22hh_pressure_raw_get(ctx, &raw_press) < 0) {
55 				LOG_DBG("Failed to read sample");
56 				return -EIO;
57 			}
58 			return lps22hh_enable_int(dev, 1);
59 		} else {
60 			return lps22hh_enable_int(dev, 0);
61 		}
62 	}
63 
64 	return -ENOTSUP;
65 }
66 
67 /**
68  * lps22hh_handle_interrupt - handle the drdy event
69  * read data and call handler if registered any
70  */
lps22hh_handle_interrupt(const struct device * dev)71 static void lps22hh_handle_interrupt(const struct device *dev)
72 {
73 	int ret;
74 	struct lps22hh_data *lps22hh = dev->data;
75 	const struct lps22hh_config *cfg = dev->config;
76 
77 	if (lps22hh->handler_drdy != NULL) {
78 		lps22hh->handler_drdy(dev, lps22hh->data_ready_trigger);
79 	}
80 
81 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
82 	if (cfg->i3c.bus != NULL) {
83 		/*
84 		 * I3C IBI does not rely on GPIO.
85 		 * So no need to enable GPIO pin for interrupt trigger.
86 		 */
87 		return;
88 	}
89 #endif
90 
91 	ret = gpio_pin_interrupt_configure_dt(&cfg->gpio_int,
92 					      GPIO_INT_EDGE_TO_ACTIVE);
93 	if (ret < 0) {
94 		LOG_ERR("%s: Not able to configure pin_int", dev->name);
95 	}
96 }
97 
lps22hh_intr_callback(struct lps22hh_data * lps22hh)98 static void lps22hh_intr_callback(struct lps22hh_data *lps22hh)
99 {
100 #if defined(CONFIG_LPS22HH_TRIGGER_OWN_THREAD)
101 	k_sem_give(&lps22hh->intr_sem);
102 #elif defined(CONFIG_LPS22HH_TRIGGER_GLOBAL_THREAD)
103 	k_work_submit(&lps22hh->work);
104 #endif /* CONFIG_LPS22HH_TRIGGER_OWN_THREAD */
105 }
106 
lps22hh_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)107 static void lps22hh_gpio_callback(const struct device *dev,
108 				  struct gpio_callback *cb, uint32_t pins)
109 {
110 	struct lps22hh_data *lps22hh =
111 		CONTAINER_OF(cb, struct lps22hh_data, gpio_cb);
112 
113 	ARG_UNUSED(pins);
114 	const struct lps22hh_config *cfg = lps22hh->dev->config;
115 	int ret;
116 
117 	ret = gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_DISABLE);
118 	if (ret < 0) {
119 		LOG_ERR("%s: Not able to configure pin_int", dev->name);
120 	}
121 
122 	lps22hh_intr_callback(lps22hh);
123 }
124 
125 #ifdef CONFIG_LPS22HH_TRIGGER_OWN_THREAD
lps22hh_thread(void * p1,void * p2,void * p3)126 static void lps22hh_thread(void *p1, void *p2, void *p3)
127 {
128 	ARG_UNUSED(p2);
129 	ARG_UNUSED(p3);
130 
131 	struct lps22hh_data *lps22hh = p1;
132 
133 	while (1) {
134 		k_sem_take(&lps22hh->intr_sem, K_FOREVER);
135 		lps22hh_handle_interrupt(lps22hh->dev);
136 	}
137 }
138 #endif /* CONFIG_LPS22HH_TRIGGER_OWN_THREAD */
139 
140 #ifdef CONFIG_LPS22HH_TRIGGER_GLOBAL_THREAD
lps22hh_work_cb(struct k_work * work)141 static void lps22hh_work_cb(struct k_work *work)
142 {
143 	struct lps22hh_data *lps22hh =
144 		CONTAINER_OF(work, struct lps22hh_data, work);
145 
146 	lps22hh_handle_interrupt(lps22hh->dev);
147 }
148 #endif /* CONFIG_LPS22HH_TRIGGER_GLOBAL_THREAD */
149 
150 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
lps22hh_ibi_cb(struct i3c_device_desc * target,struct i3c_ibi_payload * payload)151 static int lps22hh_ibi_cb(struct i3c_device_desc *target,
152 			  struct i3c_ibi_payload *payload)
153 {
154 	const struct device *dev = target->dev;
155 	struct lps22hh_data *lps22hh = dev->data;
156 
157 	ARG_UNUSED(payload);
158 
159 	lps22hh_intr_callback(lps22hh);
160 
161 	return 0;
162 }
163 #endif
164 
lps22hh_init_interrupt(const struct device * dev)165 int lps22hh_init_interrupt(const struct device *dev)
166 {
167 	struct lps22hh_data *lps22hh = dev->data;
168 	const struct lps22hh_config *cfg = dev->config;
169 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
170 	int ret;
171 
172 	/* setup data ready gpio interrupt */
173 	if (!gpio_is_ready_dt(&cfg->gpio_int)
174 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
175 	    && (cfg->i3c.bus == NULL)
176 #endif
177 	   ) {
178 		if (cfg->gpio_int.port) {
179 			LOG_ERR("%s: device %s is not ready", dev->name,
180 						cfg->gpio_int.port->name);
181 			return -ENODEV;
182 		}
183 
184 		LOG_DBG("%s: gpio_int not defined in DT", dev->name);
185 		return 0;
186 	}
187 
188 	lps22hh->dev = dev;
189 
190 #if defined(CONFIG_LPS22HH_TRIGGER_OWN_THREAD)
191 	k_sem_init(&lps22hh->intr_sem, 0, K_SEM_MAX_LIMIT);
192 
193 	k_thread_create(&lps22hh->thread, lps22hh->thread_stack,
194 		       CONFIG_LPS22HH_THREAD_STACK_SIZE,
195 		       lps22hh_thread, lps22hh,
196 		       NULL, NULL, K_PRIO_COOP(CONFIG_LPS22HH_THREAD_PRIORITY),
197 		       0, K_NO_WAIT);
198 #elif defined(CONFIG_LPS22HH_TRIGGER_GLOBAL_THREAD)
199 	lps22hh->work.handler = lps22hh_work_cb;
200 #endif /* CONFIG_LPS22HH_TRIGGER_OWN_THREAD */
201 
202 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
203 	if (cfg->i3c.bus == NULL)
204 #endif
205 	{
206 		ret = gpio_pin_configure_dt(&cfg->gpio_int, GPIO_INPUT);
207 		if (ret < 0) {
208 			LOG_ERR("Could not configure gpio");
209 			return ret;
210 		}
211 
212 		LOG_INF("%s: int on %s.%02u", dev->name, cfg->gpio_int.port->name,
213 					      cfg->gpio_int.pin);
214 
215 		gpio_init_callback(&lps22hh->gpio_cb,
216 				   lps22hh_gpio_callback,
217 				   BIT(cfg->gpio_int.pin));
218 
219 		ret = gpio_add_callback(cfg->gpio_int.port, &lps22hh->gpio_cb);
220 		if (ret < 0) {
221 			LOG_ERR("Could not set gpio callback");
222 			return ret;
223 		}
224 	}
225 
226 	/* enable interrupt in pulse mode */
227 	if (lps22hh_int_notification_set(ctx, LPS22HH_INT_PULSED) < 0) {
228 		return -EIO;
229 	}
230 
231 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
232 	if (cfg->i3c.bus != NULL) {
233 		/* I3C IBI does not utilize GPIO interrupt. */
234 		lps22hh->i3c_dev->ibi_cb = lps22hh_ibi_cb;
235 
236 		if (i3c_ibi_enable(lps22hh->i3c_dev) != 0) {
237 			LOG_DBG("Could not enable I3C IBI");
238 			return -EIO;
239 		}
240 
241 		return 0;
242 	}
243 #endif
244 
245 	return gpio_pin_interrupt_configure_dt(&cfg->gpio_int,
246 					       GPIO_INT_EDGE_TO_ACTIVE);
247 }
248