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