1 /*
2 * Copyright (c) 2018 Peter Bigot Consulting, LLC
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT ams_ccs811
8
9 #include <zephyr/drivers/sensor.h>
10 #include "ccs811.h"
11
12 #define LOG_LEVEL CONFIG_SENSOR_LOG_LEVEL
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_DECLARE(CCS811);
15
ccs811_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * thr)16 int ccs811_attr_set(const struct device *dev,
17 enum sensor_channel chan,
18 enum sensor_attribute attr,
19 const struct sensor_value *thr)
20 {
21 struct ccs811_data *drv_data = dev->data;
22 const struct ccs811_config *config = dev->config;
23 int rc;
24
25 if (!config->irq_gpio.port) {
26 return -ENOTSUP;
27 }
28
29 if (chan != SENSOR_CHAN_CO2) {
30 rc = -ENOTSUP;
31 } else if (attr == SENSOR_ATTR_LOWER_THRESH) {
32 rc = -EINVAL;
33 if ((thr->val1 >= CCS811_CO2_MIN_PPM)
34 && (thr->val1 <= CCS811_CO2_MAX_PPM)) {
35 drv_data->co2_l2m = thr->val1;
36 rc = 0;
37 }
38 } else if (attr == SENSOR_ATTR_UPPER_THRESH) {
39 rc = -EINVAL;
40 if ((thr->val1 >= CCS811_CO2_MIN_PPM)
41 && (thr->val1 <= CCS811_CO2_MAX_PPM)) {
42 drv_data->co2_m2h = thr->val1;
43 rc = 0;
44 }
45 } else {
46 rc = -ENOTSUP;
47 }
48 return rc;
49 }
50
setup_irq(const struct device * dev,bool enable)51 static inline void setup_irq(const struct device *dev,
52 bool enable)
53 {
54 const struct ccs811_config *config = dev->config;
55 unsigned int flags = enable
56 ? GPIO_INT_LEVEL_ACTIVE
57 : GPIO_INT_DISABLE;
58
59 gpio_pin_interrupt_configure_dt(&config->irq_gpio, flags);
60 }
61
handle_irq(const struct device * dev)62 static inline void handle_irq(const struct device *dev)
63 {
64 struct ccs811_data *data = dev->data;
65
66 setup_irq(dev, false);
67
68 #if defined(CONFIG_CCS811_TRIGGER_OWN_THREAD)
69 k_sem_give(&data->gpio_sem);
70 #elif defined(CONFIG_CCS811_TRIGGER_GLOBAL_THREAD)
71 k_work_submit(&data->work);
72 #endif
73 }
74
process_irq(const struct device * dev)75 static void process_irq(const struct device *dev)
76 {
77 struct ccs811_data *data = dev->data;
78
79 if (data->handler != NULL) {
80 data->handler(dev, data->trigger);
81 }
82
83 if (data->handler != NULL) {
84 setup_irq(dev, true);
85 }
86 }
87
gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)88 static void gpio_callback(const struct device *dev,
89 struct gpio_callback *cb,
90 uint32_t pins)
91 {
92 struct ccs811_data *data =
93 CONTAINER_OF(cb, struct ccs811_data, gpio_cb);
94
95 ARG_UNUSED(pins);
96
97 handle_irq(data->dev);
98 }
99
100 #ifdef CONFIG_CCS811_TRIGGER_OWN_THREAD
irq_thread(void * p1,void * p2,void * p3)101 static void irq_thread(void *p1, void *p2, void *p3)
102 {
103 ARG_UNUSED(p2);
104 ARG_UNUSED(p3);
105
106 struct ccs811_data *drv_data = p1;
107
108 while (1) {
109 k_sem_take(&drv_data->gpio_sem, K_FOREVER);
110 process_irq(drv_data->dev);
111 }
112 }
113 #elif defined(CONFIG_CCS811_TRIGGER_GLOBAL_THREAD)
work_cb(struct k_work * work)114 static void work_cb(struct k_work *work)
115 {
116 struct ccs811_data *data = CONTAINER_OF(work, struct ccs811_data, work);
117
118 process_irq(data->dev);
119 }
120 #else
121 #error Unhandled trigger configuration
122 #endif
123
ccs811_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)124 int ccs811_trigger_set(const struct device *dev,
125 const struct sensor_trigger *trig,
126 sensor_trigger_handler_t handler)
127 {
128 struct ccs811_data *drv_data = dev->data;
129 const struct ccs811_config *config = dev->config;
130 uint8_t drdy_thresh = CCS811_MODE_THRESH | CCS811_MODE_DATARDY;
131 int rc;
132
133 if (!config->irq_gpio.port) {
134 return -ENOTSUP;
135 }
136
137 LOG_DBG("CCS811 trigger set");
138 setup_irq(dev, false);
139
140 drv_data->handler = handler;
141 if (handler == NULL) {
142 return 0;
143 }
144
145 if (trig->type == SENSOR_TRIG_DATA_READY) {
146 rc = ccs811_mutate_meas_mode(dev, CCS811_MODE_DATARDY,
147 CCS811_MODE_THRESH);
148 } else if (trig->type == SENSOR_TRIG_THRESHOLD) {
149 rc = -EINVAL;
150 if ((drv_data->co2_l2m >= CCS811_CO2_MIN_PPM)
151 && (drv_data->co2_l2m <= CCS811_CO2_MAX_PPM)
152 && (drv_data->co2_m2h >= CCS811_CO2_MIN_PPM)
153 && (drv_data->co2_m2h <= CCS811_CO2_MAX_PPM)
154 && (drv_data->co2_l2m <= drv_data->co2_m2h)) {
155 rc = ccs811_set_thresholds(dev);
156 }
157 if (rc == 0) {
158 rc = ccs811_mutate_meas_mode(dev, drdy_thresh, 0);
159 }
160 } else {
161 rc = -ENOTSUP;
162 }
163
164 if (rc == 0) {
165 drv_data->trigger = trig;
166 setup_irq(dev, true);
167
168 if (gpio_pin_get_dt(&config->irq_gpio) > 0) {
169 handle_irq(dev);
170 }
171 } else {
172 drv_data->handler = NULL;
173 (void)ccs811_mutate_meas_mode(dev, 0, drdy_thresh);
174 }
175
176 return rc;
177 }
178
ccs811_init_interrupt(const struct device * dev)179 int ccs811_init_interrupt(const struct device *dev)
180 {
181 struct ccs811_data *drv_data = dev->data;
182 const struct ccs811_config *config = dev->config;
183
184 drv_data->dev = dev;
185
186 gpio_pin_configure_dt(&config->irq_gpio, GPIO_INPUT);
187
188 gpio_init_callback(&drv_data->gpio_cb, gpio_callback, BIT(config->irq_gpio.pin));
189
190 if (gpio_add_callback(config->irq_gpio.port, &drv_data->gpio_cb) < 0) {
191 LOG_DBG("Failed to set gpio callback!");
192 return -EIO;
193 }
194
195 #if defined(CONFIG_CCS811_TRIGGER_OWN_THREAD)
196 k_sem_init(&drv_data->gpio_sem, 0, K_SEM_MAX_LIMIT);
197
198 k_thread_create(&drv_data->thread, drv_data->thread_stack,
199 CONFIG_CCS811_THREAD_STACK_SIZE,
200 irq_thread, drv_data,
201 NULL, NULL, K_PRIO_COOP(CONFIG_CCS811_THREAD_PRIORITY),
202 0, K_NO_WAIT);
203 #elif defined(CONFIG_CCS811_TRIGGER_GLOBAL_THREAD)
204 drv_data->work.handler = work_cb;
205 #else
206 #error Unhandled trigger configuration
207 #endif
208 return 0;
209 }
210