1 /*
2  * Copyright (c) 2022 Würth Elektronik eiSos GmbH & Co. KG
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT we_wsen_hids
8 
9 #include <zephyr/logging/log.h>
10 
11 #include "wsen_hids.h"
12 
13 LOG_MODULE_DECLARE(WSEN_HIDS, CONFIG_SENSOR_LOG_LEVEL);
14 
hids_setup_drdy_interrupt(const struct device * dev,bool enable)15 static inline void hids_setup_drdy_interrupt(const struct device *dev, bool enable)
16 {
17 	const struct hids_config *cfg = dev->config;
18 	unsigned int flags = enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE;
19 
20 	gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy, flags);
21 }
22 
hids_handle_drdy_interrupt(const struct device * dev)23 static inline void hids_handle_drdy_interrupt(const struct device *dev)
24 {
25 	struct hids_data *data = dev->data;
26 
27 	hids_setup_drdy_interrupt(dev, false);
28 
29 #if defined(CONFIG_WSEN_HIDS_TRIGGER_OWN_THREAD)
30 	k_sem_give(&data->drdy_sem);
31 #elif defined(CONFIG_WSEN_HIDS_TRIGGER_GLOBAL_THREAD)
32 	k_work_submit(&data->work);
33 #endif
34 }
35 
hids_process_drdy_interrupt(const struct device * dev)36 static void hids_process_drdy_interrupt(const struct device *dev)
37 {
38 	struct hids_data *data = dev->data;
39 
40 	if (data->data_ready_handler != NULL) {
41 		data->data_ready_handler(dev, data->data_ready_trigger);
42 	}
43 
44 	if (data->data_ready_handler != NULL) {
45 		hids_setup_drdy_interrupt(dev, true);
46 	}
47 }
48 
hids_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)49 int hids_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
50 		     sensor_trigger_handler_t handler)
51 {
52 	struct hids_data *data = dev->data;
53 	const struct hids_config *cfg = dev->config;
54 
55 	if (trig->type != SENSOR_TRIG_DATA_READY) {
56 		LOG_ERR("Unsupported sensor trigger");
57 		return -ENOTSUP;
58 	}
59 
60 	hids_setup_drdy_interrupt(dev, false);
61 
62 	data->data_ready_handler = handler;
63 	if (handler == NULL) {
64 		return 0;
65 	}
66 
67 	data->data_ready_trigger = trig;
68 
69 	hids_setup_drdy_interrupt(dev, true);
70 
71 	/*
72 	 * If DRDY is active we probably won't get the rising edge, so
73 	 * invoke the callback manually.
74 	 */
75 	if (gpio_pin_get_dt(&cfg->gpio_drdy) > 0) {
76 		hids_handle_drdy_interrupt(dev);
77 	}
78 
79 	return 0;
80 }
81 
hids_drdy_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)82 static void hids_drdy_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
83 {
84 	struct hids_data *data = CONTAINER_OF(cb, struct hids_data, data_ready_cb);
85 
86 	ARG_UNUSED(pins);
87 
88 	hids_handle_drdy_interrupt(data->dev);
89 }
90 
91 #ifdef CONFIG_WSEN_HIDS_TRIGGER_OWN_THREAD
hids_thread(struct hids_data * data)92 static void hids_thread(struct hids_data *data)
93 {
94 	while (true) {
95 		k_sem_take(&data->drdy_sem, K_FOREVER);
96 		hids_process_drdy_interrupt(data->dev);
97 	}
98 }
99 #endif /* CONFIG_WSEN_HIDS_TRIGGER_OWN_THREAD */
100 
101 #ifdef CONFIG_WSEN_HIDS_TRIGGER_GLOBAL_THREAD
hids_work_cb(struct k_work * work)102 static void hids_work_cb(struct k_work *work)
103 {
104 	struct hids_data *data = CONTAINER_OF(work, struct hids_data, work);
105 
106 	hids_process_drdy_interrupt(data->dev);
107 }
108 #endif /* CONFIG_WSEN_HIDS_TRIGGER_GLOBAL_THREAD */
109 
hids_init_interrupt(const struct device * dev)110 int hids_init_interrupt(const struct device *dev)
111 {
112 	struct hids_data *data = dev->data;
113 	const struct hids_config *cfg = dev->config;
114 	int status;
115 
116 	data->dev = dev;
117 
118 	if (cfg->gpio_drdy.port == NULL) {
119 		LOG_ERR("drdy-gpios is not defined in the device tree.");
120 		return -EINVAL;
121 	}
122 
123 	if (!gpio_is_ready_dt(&cfg->gpio_drdy)) {
124 		LOG_ERR("Device %s is not ready", cfg->gpio_drdy.port->name);
125 		return -ENODEV;
126 	}
127 
128 	/* Setup data-ready gpio interrupt */
129 	status = gpio_pin_configure_dt(&cfg->gpio_drdy, GPIO_INPUT);
130 	if (status < 0) {
131 		LOG_ERR("Could not configure %s.%02u", cfg->gpio_drdy.port->name,
132 			cfg->gpio_drdy.pin);
133 		return status;
134 	}
135 
136 	gpio_init_callback(&data->data_ready_cb, hids_drdy_callback, BIT(cfg->gpio_drdy.pin));
137 
138 	status = gpio_add_callback(cfg->gpio_drdy.port, &data->data_ready_cb);
139 	if (status < 0) {
140 		LOG_ERR("Could not set gpio callback.");
141 		return status;
142 	}
143 
144 	/* Enable data-ready interrupt */
145 	if (HIDS_enableDataReadyInterrupt(&data->sensor_interface, HIDS_enable) != WE_SUCCESS) {
146 		LOG_ERR("Could not enable data-ready interrupt.");
147 		return -EIO;
148 	}
149 
150 #if defined(CONFIG_WSEN_HIDS_TRIGGER_OWN_THREAD)
151 	k_sem_init(&data->drdy_sem, 0, K_SEM_MAX_LIMIT);
152 
153 	k_thread_create(&data->thread, data->thread_stack, CONFIG_WSEN_HIDS_THREAD_STACK_SIZE,
154 			(k_thread_entry_t)hids_thread, data, NULL, NULL,
155 			K_PRIO_COOP(CONFIG_WSEN_HIDS_THREAD_PRIORITY), 0, K_NO_WAIT);
156 #elif defined(CONFIG_WSEN_HIDS_TRIGGER_GLOBAL_THREAD)
157 	data->work.handler = hids_work_cb;
158 #endif
159 
160 	hids_setup_drdy_interrupt(dev, true);
161 
162 	return 0;
163 }
164