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(struct lps22hh_data * lps22hh)126 static void lps22hh_thread(struct lps22hh_data *lps22hh)
127 {
128 	while (1) {
129 		k_sem_take(&lps22hh->intr_sem, K_FOREVER);
130 		lps22hh_handle_interrupt(lps22hh->dev);
131 	}
132 }
133 #endif /* CONFIG_LPS22HH_TRIGGER_OWN_THREAD */
134 
135 #ifdef CONFIG_LPS22HH_TRIGGER_GLOBAL_THREAD
lps22hh_work_cb(struct k_work * work)136 static void lps22hh_work_cb(struct k_work *work)
137 {
138 	struct lps22hh_data *lps22hh =
139 		CONTAINER_OF(work, struct lps22hh_data, work);
140 
141 	lps22hh_handle_interrupt(lps22hh->dev);
142 }
143 #endif /* CONFIG_LPS22HH_TRIGGER_GLOBAL_THREAD */
144 
145 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
lps22hh_ibi_cb(struct i3c_device_desc * target,struct i3c_ibi_payload * payload)146 static int lps22hh_ibi_cb(struct i3c_device_desc *target,
147 			  struct i3c_ibi_payload *payload)
148 {
149 	const struct device *dev = target->dev;
150 	struct lps22hh_data *lps22hh = dev->data;
151 
152 	ARG_UNUSED(payload);
153 
154 	lps22hh_intr_callback(lps22hh);
155 
156 	return 0;
157 }
158 #endif
159 
lps22hh_init_interrupt(const struct device * dev)160 int lps22hh_init_interrupt(const struct device *dev)
161 {
162 	struct lps22hh_data *lps22hh = dev->data;
163 	const struct lps22hh_config *cfg = dev->config;
164 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
165 	int ret;
166 
167 	/* setup data ready gpio interrupt */
168 	if (!gpio_is_ready_dt(&cfg->gpio_int)
169 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
170 	    && (cfg->i3c.bus == NULL)
171 #endif
172 	   ) {
173 		if (cfg->gpio_int.port) {
174 			LOG_ERR("%s: device %s is not ready", dev->name,
175 						cfg->gpio_int.port->name);
176 			return -ENODEV;
177 		}
178 
179 		LOG_DBG("%s: gpio_int not defined in DT", dev->name);
180 		return 0;
181 	}
182 
183 	lps22hh->dev = dev;
184 
185 #if defined(CONFIG_LPS22HH_TRIGGER_OWN_THREAD)
186 	k_sem_init(&lps22hh->intr_sem, 0, K_SEM_MAX_LIMIT);
187 
188 	k_thread_create(&lps22hh->thread, lps22hh->thread_stack,
189 		       CONFIG_LPS22HH_THREAD_STACK_SIZE,
190 		       (k_thread_entry_t)lps22hh_thread, lps22hh,
191 		       NULL, NULL, K_PRIO_COOP(CONFIG_LPS22HH_THREAD_PRIORITY),
192 		       0, K_NO_WAIT);
193 #elif defined(CONFIG_LPS22HH_TRIGGER_GLOBAL_THREAD)
194 	lps22hh->work.handler = lps22hh_work_cb;
195 #endif /* CONFIG_LPS22HH_TRIGGER_OWN_THREAD */
196 
197 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
198 	if (cfg->i3c.bus == NULL)
199 #endif
200 	{
201 		ret = gpio_pin_configure_dt(&cfg->gpio_int, GPIO_INPUT);
202 		if (ret < 0) {
203 			LOG_ERR("Could not configure gpio");
204 			return ret;
205 		}
206 
207 		LOG_INF("%s: int on %s.%02u", dev->name, cfg->gpio_int.port->name,
208 					      cfg->gpio_int.pin);
209 
210 		gpio_init_callback(&lps22hh->gpio_cb,
211 				   lps22hh_gpio_callback,
212 				   BIT(cfg->gpio_int.pin));
213 
214 		ret = gpio_add_callback(cfg->gpio_int.port, &lps22hh->gpio_cb);
215 		if (ret < 0) {
216 			LOG_ERR("Could not set gpio callback");
217 			return ret;
218 		}
219 	}
220 
221 	/* enable interrupt in pulse mode */
222 	if (lps22hh_int_notification_set(ctx, LPS22HH_INT_PULSED) < 0) {
223 		return -EIO;
224 	}
225 
226 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i3c)
227 	if (cfg->i3c.bus != NULL) {
228 		/* I3C IBI does not utilize GPIO interrupt. */
229 		lps22hh->i3c_dev->ibi_cb = lps22hh_ibi_cb;
230 
231 		if (i3c_ibi_enable(lps22hh->i3c_dev) != 0) {
232 			LOG_DBG("Could not enable I3C IBI");
233 			return -EIO;
234 		}
235 
236 		return 0;
237 	}
238 #endif
239 
240 	return gpio_pin_interrupt_configure_dt(&cfg->gpio_int,
241 					       GPIO_INT_EDGE_TO_ACTIVE);
242 }
243