1 /* ST Microelectronics LPS2XDF 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  * Datasheet:
9  * https://www.st.com/resource/en/datasheet/lps22df.pdf
10  * https://www.st.com/resource/en/datasheet/lps28dfw.pdf
11  */
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 "lps2xdf.h"
19 
20 #if DT_HAS_COMPAT_STATUS_OKAY(st_lps22df)
21 #include "lps22df.h"
22 #endif
23 
24 #if DT_HAS_COMPAT_STATUS_OKAY(st_lps28dfw)
25 #include "lps28dfw.h"
26 #endif
27 
28 LOG_MODULE_DECLARE(LPS2XDF, CONFIG_SENSOR_LOG_LEVEL);
29 
lps2xdf_config_int(const struct device * dev)30 int lps2xdf_config_int(const struct device *dev)
31 {
32 	const struct lps2xdf_config *const cfg = dev->config;
33 	const struct lps2xdf_chip_api *chip_api = cfg->chip_api;
34 
35 	return chip_api->config_interrupt(dev);
36 }
37 
lps2xdf_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)38 int lps2xdf_trigger_set(const struct device *dev,
39 			  const struct sensor_trigger *trig,
40 			  sensor_trigger_handler_t handler)
41 {
42 	const struct lps2xdf_config *const cfg = dev->config;
43 	const struct lps2xdf_chip_api *chip_api = cfg->chip_api;
44 
45 	return chip_api->trigger_set(dev, trig, handler);
46 }
47 
lps2xdf_intr_callback(struct lps2xdf_data * lps2xdf)48 static void lps2xdf_intr_callback(struct lps2xdf_data *lps2xdf)
49 {
50 #if defined(CONFIG_LPS2XDF_TRIGGER_OWN_THREAD)
51 	k_sem_give(&lps2xdf->intr_sem);
52 #elif defined(CONFIG_LPS2XDF_TRIGGER_GLOBAL_THREAD)
53 	k_work_submit(&lps2xdf->work);
54 #endif /* CONFIG_LPS2XDF_TRIGGER_OWN_THREAD */
55 }
56 
lps2xdf_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)57 static void lps2xdf_gpio_callback(const struct device *dev,
58 				  struct gpio_callback *cb, uint32_t pins)
59 {
60 	struct lps2xdf_data *lps2xdf =
61 		CONTAINER_OF(cb, struct lps2xdf_data, gpio_cb);
62 
63 	ARG_UNUSED(pins);
64 	const struct lps2xdf_config *cfg = lps2xdf->dev->config;
65 	int ret;
66 
67 	ret = gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_DISABLE);
68 	if (ret < 0) {
69 		LOG_ERR("%s: Not able to configure pin_int", dev->name);
70 	}
71 
72 	lps2xdf_intr_callback(lps2xdf);
73 }
74 
75 #ifdef CONFIG_LPS2XDF_TRIGGER_OWN_THREAD
lps2xdf_thread(struct lps2xdf_data * lps2xdf)76 static void lps2xdf_thread(struct lps2xdf_data *lps2xdf)
77 {
78 	const struct device *dev = lps2xdf->dev;
79 	const struct lps2xdf_config *const cfg = dev->config;
80 	const struct lps2xdf_chip_api *chip_api = cfg->chip_api;
81 
82 	while (1) {
83 		k_sem_take(&lps2xdf->intr_sem, K_FOREVER);
84 		chip_api->handle_interrupt(dev);
85 	}
86 }
87 #endif /* CONFIG_LPS2XDF_TRIGGER_OWN_THREAD */
88 
89 #ifdef CONFIG_LPS2XDF_TRIGGER_GLOBAL_THREAD
lps2xdf_work_cb(struct k_work * work)90 static void lps2xdf_work_cb(struct k_work *work)
91 {
92 	struct lps2xdf_data *lps2xdf =
93 		CONTAINER_OF(work, struct lps2xdf_data, work);
94 	const struct device *dev = lps2xdf->dev;
95 	const struct lps2xdf_config *const cfg = dev->config;
96 	const struct lps2xdf_chip_api *chip_api = cfg->chip_api;
97 
98 	chip_api->handle_interrupt(dev);
99 }
100 #endif /* CONFIG_LPS2XDF_TRIGGER_GLOBAL_THREAD */
101 
102 #if (DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(st_lps22df, i3c) ||\
103 	DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(st_lps28dfw, i3c))
lps2xdf_ibi_cb(struct i3c_device_desc * target,struct i3c_ibi_payload * payload)104 static int lps2xdf_ibi_cb(struct i3c_device_desc *target,
105 			  struct i3c_ibi_payload *payload)
106 {
107 	const struct device *dev = target->dev;
108 	struct lps2xdf_data *lps2xdf = dev->data;
109 
110 	ARG_UNUSED(payload);
111 
112 	lps2xdf_intr_callback(lps2xdf);
113 
114 	return 0;
115 }
116 #endif
117 
lps2xdf_init_interrupt(const struct device * dev,enum sensor_variant variant)118 int lps2xdf_init_interrupt(const struct device *dev, enum sensor_variant variant)
119 {
120 	struct lps2xdf_data *lps2xdf = dev->data;
121 	const struct lps2xdf_config *cfg = dev->config;
122 	int ret;
123 
124 	/* setup data ready gpio interrupt */
125 	if (!gpio_is_ready_dt(&cfg->gpio_int) && !ON_I3C_BUS(cfg)) {
126 		if (cfg->gpio_int.port) {
127 			LOG_ERR("%s: device %s is not ready", dev->name,
128 						cfg->gpio_int.port->name);
129 			return -ENODEV;
130 		}
131 
132 		LOG_DBG("%s: gpio_int not defined in DT", dev->name);
133 		return 0;
134 	}
135 
136 	lps2xdf->dev = dev;
137 
138 #if defined(CONFIG_LPS2XDF_TRIGGER_OWN_THREAD)
139 	k_sem_init(&lps2xdf->intr_sem, 0, K_SEM_MAX_LIMIT);
140 
141 	k_thread_create(&lps2xdf->thread, lps2xdf->thread_stack,
142 		       CONFIG_LPS2XDF_THREAD_STACK_SIZE,
143 		       (k_thread_entry_t)lps2xdf_thread, lps2xdf,
144 		       NULL, NULL, K_PRIO_COOP(CONFIG_LPS2XDF_THREAD_PRIORITY),
145 		       0, K_NO_WAIT);
146 #elif defined(CONFIG_LPS2XDF_TRIGGER_GLOBAL_THREAD)
147 	lps2xdf->work.handler = lps2xdf_work_cb;
148 #endif /* CONFIG_LPS2XDF_TRIGGER_OWN_THREAD */
149 
150 	if (!ON_I3C_BUS(cfg)) {
151 		ret = gpio_pin_configure_dt(&cfg->gpio_int, GPIO_INPUT);
152 		if (ret < 0) {
153 			LOG_ERR("Could not configure gpio");
154 			return ret;
155 		}
156 
157 		LOG_INF("%s: int on %s.%02u", dev->name, cfg->gpio_int.port->name,
158 					      cfg->gpio_int.pin);
159 
160 		gpio_init_callback(&lps2xdf->gpio_cb,
161 				   lps2xdf_gpio_callback,
162 				   BIT(cfg->gpio_int.pin));
163 
164 		ret = gpio_add_callback(cfg->gpio_int.port, &lps2xdf->gpio_cb);
165 		if (ret < 0) {
166 			LOG_ERR("Could not set gpio callback");
167 			return ret;
168 		}
169 	}
170 
171 	LOG_DBG("drdy_pulsed is %d", (int)cfg->drdy_pulsed);
172 
173 	/* enable drdy in pulsed/latched mode */
174 	ret = lps2xdf_config_int(dev);
175 	if (ret < 0) {
176 		LOG_ERR("Could not configure interrupt mode");
177 		return ret;
178 	}
179 
180 #if (DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(st_lps22df, i3c) ||\
181 	DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(st_lps28dfw, i3c))
182 	if (cfg->i3c.bus != NULL) {
183 		/* I3C IBI does not utilize GPIO interrupt. */
184 		lps2xdf->i3c_dev->ibi_cb = lps2xdf_ibi_cb;
185 
186 		if (i3c_ibi_enable(lps2xdf->i3c_dev) != 0) {
187 			LOG_DBG("Could not enable I3C IBI");
188 			return -EIO;
189 		}
190 
191 		return 0;
192 	}
193 #endif
194 
195 	return gpio_pin_interrupt_configure_dt(&cfg->gpio_int,
196 					       GPIO_INT_EDGE_TO_ACTIVE);
197 }
198