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