1 /*
2  * Copyright (c) 2020, Laird Connectivity
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT honeywell_sm351lt
8 
9 #include "sm351lt.h"
10 
11 #include <zephyr/init.h>
12 #include <zephyr/sys/byteorder.h>
13 #include <zephyr/logging/log.h>
14 #include <stdio.h>
15 #include <zephyr/sys/util.h>
16 #include <zephyr/drivers/gpio.h>
17 #include <zephyr/drivers/sensor.h>
18 #include <string.h>
19 
20 LOG_MODULE_REGISTER(SM351LT, CONFIG_SENSOR_LOG_LEVEL);
21 
22 #if CONFIG_SM351LT_TRIGGER
sm351lt_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)23 static int sm351lt_trigger_set(const struct device *dev,
24 			       const struct sensor_trigger *trig,
25 			       sensor_trigger_handler_t handler)
26 {
27 	const struct sm351lt_config *const config = dev->config;
28 	struct sm351lt_data *data = dev->data;
29 	int ret = -ENOTSUP;
30 
31 	if (trig->chan == SENSOR_CHAN_PROX) {
32 		data->changed_handler = handler;
33 		data->changed_trigger = trig;
34 		ret = gpio_pin_interrupt_configure_dt(&config->int_gpio,
35 						      (handler ? data->trigger_type
36 							       : GPIO_INT_DISABLE));
37 
38 		if (ret < 0) {
39 			return ret;
40 		}
41 
42 		if (handler) {
43 			ret = gpio_add_callback(config->int_gpio.port,
44 						&data->gpio_cb);
45 		} else {
46 			ret = gpio_remove_callback(config->int_gpio.port,
47 						   &data->gpio_cb);
48 		}
49 	}
50 
51 	return ret;
52 }
53 
sm351lt_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)54 static void sm351lt_gpio_callback(const struct device *dev,
55 				  struct gpio_callback *cb, uint32_t pins)
56 {
57 	struct sm351lt_data *data =
58 		CONTAINER_OF(cb, struct sm351lt_data, gpio_cb);
59 
60 #if defined(CONFIG_SM351LT_TRIGGER_OWN_THREAD)
61 	k_sem_give(&data->gpio_sem);
62 #elif defined(CONFIG_SM351LT_TRIGGER_GLOBAL_THREAD)
63 	k_work_submit(&data->work);
64 #endif
65 }
66 
sm351lt_thread_cb(const struct device * dev)67 static void sm351lt_thread_cb(const struct device *dev)
68 {
69 	struct sm351lt_data *data = dev->data;
70 
71 	if (likely(data->changed_handler != NULL)) {
72 		data->changed_handler(dev, data->changed_trigger);
73 	}
74 
75 	return;
76 }
77 
78 #if defined(CONFIG_SM351LT_TRIGGER_OWN_THREAD)
sm351lt_thread(void * arg1,void * unused2,void * unused3)79 static void sm351lt_thread(void *arg1, void *unused2, void *unused3)
80 {
81 	struct sm351lt_data *data = arg1;
82 
83 	ARG_UNUSED(unused2);
84 	ARG_UNUSED(unused3);
85 
86 	while (1) {
87 		k_sem_take(&data->gpio_sem, K_FOREVER);
88 		sm351lt_thread_cb(data->dev);
89 	}
90 }
91 #endif
92 
93 #if defined(CONFIG_SM351LT_TRIGGER_GLOBAL_THREAD)
sm351lt_work_cb(struct k_work * work)94 static void sm351lt_work_cb(struct k_work *work)
95 {
96 	struct sm351lt_data *data =
97 		CONTAINER_OF(work, struct sm351lt_data, work);
98 
99 	sm351lt_thread_cb(data->dev);
100 }
101 #endif
102 #endif
103 
sm351lt_sample_fetch(const struct device * dev,enum sensor_channel chan)104 static int sm351lt_sample_fetch(const struct device *dev,
105 				enum sensor_channel chan)
106 {
107 	const struct sm351lt_config *config = dev->config;
108 	struct sm351lt_data *data = dev->data;
109 
110 	if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_PROX) {
111 		return -ENOTSUP;
112 	}
113 
114 	data->sample_status = gpio_pin_get_dt(&config->int_gpio);
115 
116 	return 0;
117 }
118 
sm351lt_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)119 static int sm351lt_channel_get(const struct device *dev,
120 			       enum sensor_channel chan,
121 			       struct sensor_value *val)
122 {
123 	struct sm351lt_data *data = dev->data;
124 
125 	if (chan == SENSOR_CHAN_PROX) {
126 		val->val1 = data->sample_status;
127 		val->val2 = 0;
128 	} else {
129 		return -ENOTSUP;
130 	}
131 
132 	return 0;
133 }
134 
135 #if CONFIG_SM351LT_TRIGGER
sm351lt_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)136 static int sm351lt_attr_set(const struct device *dev,
137 			    enum sensor_channel chan,
138 			    enum sensor_attribute attr,
139 			    const struct sensor_value *val)
140 {
141 	struct sm351lt_data *data = dev->data;
142 
143 	if (chan == SENSOR_CHAN_PROX) {
144 		if (attr == SENSOR_ATTR_SM351LT_TRIGGER_TYPE) {
145 			/* Interrupt triggering type */
146 			data->trigger_type = val->val1;
147 		} else {
148 			return -ENOTSUP;
149 		}
150 	} else {
151 		return -ENOTSUP;
152 	}
153 
154 	return 0;
155 }
156 
sm351lt_attr_get(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,struct sensor_value * val)157 static int sm351lt_attr_get(const struct device *dev,
158 			    enum sensor_channel chan,
159 			    enum sensor_attribute attr,
160 			    struct sensor_value *val)
161 {
162 	struct sm351lt_data *data = dev->data;
163 
164 	if (chan == SENSOR_CHAN_PROX) {
165 		if (attr == SENSOR_ATTR_SM351LT_TRIGGER_TYPE) {
166 			/* Interrupt triggering type */
167 			val->val1 = data->trigger_type;
168 			val->val2 = 0;
169 		} else {
170 			return -ENOTSUP;
171 		}
172 	} else {
173 		return -ENOTSUP;
174 	}
175 
176 	return 0;
177 }
178 #endif
179 
180 static const struct sensor_driver_api sm351lt_api_funcs = {
181 	.sample_fetch = sm351lt_sample_fetch,
182 	.channel_get = sm351lt_channel_get,
183 #if CONFIG_SM351LT_TRIGGER
184 	.attr_set = sm351lt_attr_set,
185 	.attr_get = sm351lt_attr_get,
186 	.trigger_set = sm351lt_trigger_set,
187 #endif
188 };
189 
sm351lt_init(const struct device * dev)190 static int sm351lt_init(const struct device *dev)
191 {
192 	const struct sm351lt_config *const config = dev->config;
193 	uint32_t ret;
194 
195 	if (!gpio_is_ready_dt(&config->int_gpio)) {
196 		LOG_ERR("GPIO device not ready");
197 		return -ENODEV;
198 	}
199 
200 	ret = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
201 	if (ret) {
202 		LOG_ERR("failed to configure gpio: %d", ret);
203 		return ret;
204 	}
205 
206 #if defined(CONFIG_SM351LT_TRIGGER)
207 	struct sm351lt_data *data = dev->data;
208 	data->dev = dev;
209 
210 #if defined(CONFIG_SM351LT_TRIGGER_OWN_THREAD)
211 	k_sem_init(&data->gpio_sem, 0, K_SEM_MAX_LIMIT);
212 
213 	k_thread_create(&data->thread, data->thread_stack,
214 			CONFIG_SM351LT_THREAD_STACK_SIZE,
215 			sm351lt_thread, data, NULL,
216 			NULL, K_PRIO_COOP(CONFIG_SM351LT_THREAD_PRIORITY),
217 			0, K_NO_WAIT);
218 
219 #if defined(CONFIG_THREAD_NAME) && defined(CONFIG_THREAD_MAX_NAME_LEN)
220 	/* Sets up thread name as the device name */
221 	k_thread_name_set(&data->thread, dev->name);
222 #endif
223 
224 #elif defined(CONFIG_SM351LT_TRIGGER_GLOBAL_THREAD)
225 	data->work.handler = sm351lt_work_cb;
226 #endif
227 
228 	data->trigger_type = GPIO_INT_DISABLE;
229 
230 	ret = gpio_pin_interrupt_configure_dt(&config->int_gpio,
231 					      GPIO_INT_DISABLE);
232 	if (ret) {
233 		LOG_ERR("failed to configure gpio interrupt: %d", ret);
234 		return ret;
235 	}
236 
237 	/* Setup callback struct but do not add it yet */
238 	gpio_init_callback(&data->gpio_cb, sm351lt_gpio_callback,
239 			   BIT(config->int_gpio.pin));
240 #endif
241 
242 	return 0;
243 }
244 
245 /* Instantiation macros for each individual device. */
246 #define SM351LT_DEFINE(inst)					     \
247 	static struct sm351lt_data sm351lt_data_##inst;		     \
248 	static const struct sm351lt_config sm351lt_config_##inst = { \
249 		.int_gpio = GPIO_DT_SPEC_INST_GET(inst, gpios),	     \
250 	};							     \
251 								     \
252 	SENSOR_DEVICE_DT_INST_DEFINE(inst,			     \
253 			    sm351lt_init,			     \
254 			    NULL,				     \
255 			    &sm351lt_data_##inst,		     \
256 			    &sm351lt_config_##inst,		     \
257 			    POST_KERNEL,			     \
258 			    CONFIG_SENSOR_INIT_PRIORITY,	     \
259 			    &sm351lt_api_funcs);
260 
261 
262 /* Main instantiation macro for every configured device in DTS. */
263 DT_INST_FOREACH_STATUS_OKAY(SM351LT_DEFINE)
264