1 /*
2  * Copyright (c) 2016 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT semtech_sx9500
8 
9 #include <errno.h>
10 
11 #include <zephyr/kernel.h>
12 #include <zephyr/drivers/i2c.h>
13 #include <zephyr/drivers/sensor.h>
14 #include <zephyr/drivers/gpio.h>
15 #include <zephyr/sys/util.h>
16 
17 #include "sx9500.h"
18 
19 #include <zephyr/logging/log.h>
20 LOG_MODULE_DECLARE(SX9500, CONFIG_SENSOR_LOG_LEVEL);
21 
22 #ifdef CONFIG_SX9500_TRIGGER_OWN_THREAD
23 static K_KERNEL_STACK_DEFINE(sx9500_thread_stack, CONFIG_SX9500_THREAD_STACK_SIZE);
24 static struct k_thread sx9500_thread;
25 #endif
26 
sx9500_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)27 int sx9500_trigger_set(const struct device *dev,
28 		       const struct sensor_trigger *trig,
29 		       sensor_trigger_handler_t handler)
30 {
31 	struct sx9500_data *data = dev->data;
32 	const struct sx9500_config *cfg = dev->config;
33 
34 	if (!cfg->int_gpio.port) {
35 		return -ENOTSUP;
36 	}
37 
38 	switch (trig->type) {
39 	case SENSOR_TRIG_DATA_READY:
40 		if (i2c_reg_update_byte_dt(&cfg->i2c,
41 					   SX9500_REG_IRQ_MSK,
42 					   SX9500_CONV_DONE_IRQ,
43 					   SX9500_CONV_DONE_IRQ) < 0) {
44 			return -EIO;
45 		}
46 		data->handler_drdy = handler;
47 		data->trigger_drdy = trig;
48 		break;
49 
50 	case SENSOR_TRIG_NEAR_FAR:
51 		if (i2c_reg_update_byte_dt(&cfg->i2c,
52 					   SX9500_REG_IRQ_MSK,
53 					   SX9500_NEAR_FAR_IRQ,
54 					   SX9500_NEAR_FAR_IRQ) < 0) {
55 			return -EIO;
56 		}
57 		data->handler_near_far = handler;
58 		data->trigger_near_far = trig;
59 		break;
60 
61 	default:
62 		return -EINVAL;
63 	}
64 
65 	return 0;
66 }
67 
sx9500_gpio_thread_cb(const struct device * dev)68 static void sx9500_gpio_thread_cb(const struct device *dev)
69 {
70 	struct sx9500_data *data = dev->data;
71 	const struct sx9500_config *cfg = dev->config;
72 	uint8_t reg_val;
73 
74 	if (i2c_reg_read_byte_dt(&cfg->i2c, SX9500_REG_IRQ_SRC, &reg_val) < 0) {
75 		LOG_DBG("sx9500: error reading IRQ source register");
76 		return;
77 	}
78 
79 	if ((reg_val & SX9500_CONV_DONE_IRQ) && data->handler_drdy) {
80 		data->handler_drdy(dev, data->trigger_drdy);
81 	}
82 
83 	if ((reg_val & SX9500_NEAR_FAR_IRQ) && data->handler_near_far) {
84 		data->handler_near_far(dev, data->trigger_near_far);
85 	}
86 }
87 
88 #ifdef CONFIG_SX9500_TRIGGER_OWN_THREAD
89 
sx9500_gpio_cb(const struct device * port,struct gpio_callback * cb,uint32_t pins)90 static void sx9500_gpio_cb(const struct device *port,
91 			   struct gpio_callback *cb, uint32_t pins)
92 {
93 	struct sx9500_data *data =
94 		CONTAINER_OF(cb, struct sx9500_data, gpio_cb);
95 
96 	ARG_UNUSED(pins);
97 
98 	k_sem_give(&data->sem);
99 }
100 
sx9500_thread_main(void * p1,void * p2,void * p3)101 static void sx9500_thread_main(void *p1, void *p2, void *p3)
102 {
103 	ARG_UNUSED(p2);
104 	ARG_UNUSED(p3);
105 
106 	struct sx9500_data *data = p1;
107 
108 	while (1) {
109 		k_sem_take(&data->sem, K_FOREVER);
110 		sx9500_gpio_thread_cb(data->dev);
111 	}
112 }
113 
114 #else /* CONFIG_SX9500_TRIGGER_GLOBAL_THREAD */
115 
sx9500_gpio_cb(const struct device * port,struct gpio_callback * cb,uint32_t pins)116 static void sx9500_gpio_cb(const struct device *port,
117 			   struct gpio_callback *cb, uint32_t pins)
118 {
119 	struct sx9500_data *data =
120 		CONTAINER_OF(cb, struct sx9500_data, gpio_cb);
121 
122 	ARG_UNUSED(pins);
123 
124 	k_work_submit(&data->work);
125 }
126 #endif /* CONFIG_SX9500_TRIGGER_GLOBAL_THREAD */
127 
128 #ifdef CONFIG_SX9500_TRIGGER_GLOBAL_THREAD
sx9500_work_cb(struct k_work * work)129 static void sx9500_work_cb(struct k_work *work)
130 {
131 	struct sx9500_data *data =
132 		CONTAINER_OF(work, struct sx9500_data, work);
133 
134 	sx9500_gpio_thread_cb(data->dev);
135 }
136 #endif
137 
sx9500_setup_interrupt(const struct device * dev)138 int sx9500_setup_interrupt(const struct device *dev)
139 {
140 	struct sx9500_data *data = dev->data;
141 	const struct sx9500_config *cfg = dev->config;
142 	int ret;
143 
144 #ifdef CONFIG_SX9500_TRIGGER_OWN_THREAD
145 	k_sem_init(&data->sem, 0, K_SEM_MAX_LIMIT);
146 #else
147 	data->work.handler = sx9500_work_cb;
148 #endif
149 
150 	data->dev = dev;
151 
152 	if (!gpio_is_ready_dt(&cfg->int_gpio)) {
153 		LOG_ERR("%s: device %s is not ready", dev->name,
154 			cfg->int_gpio.port->name);
155 		return -ENODEV;
156 	}
157 
158 	ret = gpio_pin_configure_dt(&cfg->int_gpio, GPIO_INPUT);
159 	if (ret < 0) {
160 		return ret;
161 	}
162 
163 	gpio_init_callback(&data->gpio_cb, sx9500_gpio_cb, BIT(cfg->int_gpio.pin));
164 
165 	ret = gpio_add_callback(cfg->int_gpio.port, &data->gpio_cb);
166 	if (ret < 0) {
167 		return ret;
168 	}
169 
170 	ret = gpio_pin_interrupt_configure_dt(&cfg->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
171 	if (ret < 0) {
172 		return ret;
173 	}
174 
175 #ifdef CONFIG_SX9500_TRIGGER_OWN_THREAD
176 	k_thread_create(&sx9500_thread, sx9500_thread_stack,
177 			CONFIG_SX9500_THREAD_STACK_SIZE,
178 			sx9500_thread_main, data, 0, NULL,
179 			K_PRIO_COOP(CONFIG_SX9500_THREAD_PRIORITY),
180 			0, K_NO_WAIT);
181 #endif
182 
183 	return 0;
184 }
185