1 /* ST Microelectronics IIS3DHHC accelerometer sensor
2 *
3 * Copyright (c) 2019 STMicroelectronics
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Datasheet:
8 * https://www.st.com/resource/en/datasheet/iis3dhhc.pdf
9 */
10
11 #define DT_DRV_COMPAT st_iis3dhhc
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 "iis3dhhc.h"
19
20 LOG_MODULE_DECLARE(IIS3DHHC, CONFIG_SENSOR_LOG_LEVEL);
21
22 /**
23 * iis3dhhc_enable_int - enable selected int pin to generate interrupt
24 */
iis3dhhc_enable_int(const struct device * dev,int enable)25 static int iis3dhhc_enable_int(const struct device *dev, int enable)
26 {
27 struct iis3dhhc_data *iis3dhhc = dev->data;
28
29 /* set interrupt */
30 #ifdef CONFIG_IIS3DHHC_DRDY_INT1
31 return iis3dhhc_drdy_on_int1_set(iis3dhhc->ctx, enable);
32 #else
33 return iis3dhhc_drdy_on_int2_set(iis3dhhc->ctx, enable);
34 #endif
35 }
36
37 /**
38 * iis3dhhc_trigger_set - link external trigger to event data ready
39 */
iis3dhhc_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)40 int iis3dhhc_trigger_set(const struct device *dev,
41 const struct sensor_trigger *trig,
42 sensor_trigger_handler_t handler)
43 {
44 struct iis3dhhc_data *iis3dhhc = dev->data;
45 const struct iis3dhhc_config *config = dev->config;
46 int16_t raw[3];
47
48 if (!config->int_gpio.port) {
49 return -ENOTSUP;
50 }
51
52 if (trig->chan == SENSOR_CHAN_ACCEL_XYZ) {
53 iis3dhhc->handler_drdy = handler;
54 iis3dhhc->trig_drdy = trig;
55 if (handler) {
56 /* dummy read: re-trigger interrupt */
57 iis3dhhc_acceleration_raw_get(iis3dhhc->ctx, raw);
58 return iis3dhhc_enable_int(dev, PROPERTY_ENABLE);
59 } else {
60 return iis3dhhc_enable_int(dev, PROPERTY_DISABLE);
61 }
62 }
63
64 return -ENOTSUP;
65 }
66
67 /**
68 * iis3dhhc_handle_interrupt - handle the drdy event
69 * read data and call handler if registered any
70 */
iis3dhhc_handle_interrupt(const struct device * dev)71 static void iis3dhhc_handle_interrupt(const struct device *dev)
72 {
73 struct iis3dhhc_data *iis3dhhc = dev->data;
74 const struct iis3dhhc_config *cfg = dev->config;
75
76 if (iis3dhhc->handler_drdy != NULL) {
77 iis3dhhc->handler_drdy(dev, iis3dhhc->trig_drdy);
78 }
79
80 gpio_pin_interrupt_configure_dt(&cfg->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
81 }
82
iis3dhhc_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)83 static void iis3dhhc_gpio_callback(const struct device *dev,
84 struct gpio_callback *cb, uint32_t pins)
85 {
86 struct iis3dhhc_data *iis3dhhc =
87 CONTAINER_OF(cb, struct iis3dhhc_data, gpio_cb);
88 const struct iis3dhhc_config *cfg = iis3dhhc->dev->config;
89
90 ARG_UNUSED(pins);
91
92 gpio_pin_interrupt_configure_dt(&cfg->int_gpio, GPIO_INT_DISABLE);
93
94 #if defined(CONFIG_IIS3DHHC_TRIGGER_OWN_THREAD)
95 k_sem_give(&iis3dhhc->gpio_sem);
96 #elif defined(CONFIG_IIS3DHHC_TRIGGER_GLOBAL_THREAD)
97 k_work_submit(&iis3dhhc->work);
98 #endif /* CONFIG_IIS3DHHC_TRIGGER_OWN_THREAD */
99 }
100
101 #ifdef CONFIG_IIS3DHHC_TRIGGER_OWN_THREAD
iis3dhhc_thread(void * p1,void * p2,void * p3)102 static void iis3dhhc_thread(void *p1, void *p2, void *p3)
103 {
104 ARG_UNUSED(p2);
105 ARG_UNUSED(p3);
106
107 struct iis3dhhc_data *iis3dhhc = p1;
108
109 while (1) {
110 k_sem_take(&iis3dhhc->gpio_sem, K_FOREVER);
111 iis3dhhc_handle_interrupt(iis3dhhc->dev);
112 }
113 }
114 #endif /* CONFIG_IIS3DHHC_TRIGGER_OWN_THREAD */
115
116 #ifdef CONFIG_IIS3DHHC_TRIGGER_GLOBAL_THREAD
iis3dhhc_work_cb(struct k_work * work)117 static void iis3dhhc_work_cb(struct k_work *work)
118 {
119 struct iis3dhhc_data *iis3dhhc =
120 CONTAINER_OF(work, struct iis3dhhc_data, work);
121
122 iis3dhhc_handle_interrupt(iis3dhhc->dev);
123 }
124 #endif /* CONFIG_IIS3DHHC_TRIGGER_GLOBAL_THREAD */
125
iis3dhhc_init_interrupt(const struct device * dev)126 int iis3dhhc_init_interrupt(const struct device *dev)
127 {
128 struct iis3dhhc_data *iis3dhhc = dev->data;
129 const struct iis3dhhc_config *cfg = dev->config;
130 int ret;
131
132 if (!gpio_is_ready_dt(&cfg->int_gpio)) {
133 LOG_ERR("%s: device %s is not ready", dev->name, cfg->int_gpio.port->name);
134 return -ENODEV;
135 }
136
137 iis3dhhc->dev = dev;
138
139 #if defined(CONFIG_IIS3DHHC_TRIGGER_OWN_THREAD)
140 k_sem_init(&iis3dhhc->gpio_sem, 0, K_SEM_MAX_LIMIT);
141
142 k_thread_create(&iis3dhhc->thread, iis3dhhc->thread_stack,
143 CONFIG_IIS3DHHC_THREAD_STACK_SIZE,
144 iis3dhhc_thread, iis3dhhc,
145 NULL, NULL, K_PRIO_COOP(CONFIG_IIS3DHHC_THREAD_PRIORITY),
146 0, K_NO_WAIT);
147 #elif defined(CONFIG_IIS3DHHC_TRIGGER_GLOBAL_THREAD)
148 iis3dhhc->work.handler = iis3dhhc_work_cb;
149 #endif /* CONFIG_IIS3DHHC_TRIGGER_OWN_THREAD */
150
151 ret = gpio_pin_configure_dt(&cfg->int_gpio, GPIO_INPUT);
152 if (ret < 0) {
153 LOG_DBG("Could not configure gpio");
154 return ret;
155 }
156
157 gpio_init_callback(&iis3dhhc->gpio_cb, iis3dhhc_gpio_callback, BIT(cfg->int_gpio.pin));
158
159 if (gpio_add_callback(cfg->int_gpio.port, &iis3dhhc->gpio_cb) < 0) {
160 LOG_DBG("Could not set gpio callback");
161 return -EIO;
162 }
163
164 /* enable interrupt on int1/int2 in pulse mode */
165 if (iis3dhhc_drdy_notification_mode_set(iis3dhhc->ctx, IIS3DHHC_PULSED)) {
166 return -EIO;
167 }
168
169 return gpio_pin_interrupt_configure_dt(&cfg->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
170 }
171