1 /*
2  * Copyright (c) 2018 Analog Devices Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/device.h>
8 #include <zephyr/drivers/gpio.h>
9 #include <zephyr/drivers/i2c.h>
10 #include <zephyr/sys/util.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/drivers/sensor.h>
13 
14 #include "adt7420.h"
15 
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_DECLARE(ADT7420, CONFIG_SENSOR_LOG_LEVEL);
18 
setup_int(const struct device * dev,bool enable)19 static void setup_int(const struct device *dev,
20 		      bool enable)
21 {
22 	const struct adt7420_dev_config *cfg = dev->config;
23 	gpio_flags_t flags = enable
24 		? GPIO_INT_EDGE_TO_ACTIVE
25 		: GPIO_INT_DISABLE;
26 
27 	gpio_pin_interrupt_configure_dt(&cfg->int_gpio, flags);
28 }
29 
handle_int(const struct device * dev)30 static void handle_int(const struct device *dev)
31 {
32 	struct adt7420_data *drv_data = dev->data;
33 
34 	setup_int(dev, false);
35 
36 #if defined(CONFIG_ADT7420_TRIGGER_OWN_THREAD)
37 	k_sem_give(&drv_data->gpio_sem);
38 #elif defined(CONFIG_ADT7420_TRIGGER_GLOBAL_THREAD)
39 	k_work_submit(&drv_data->work);
40 #endif
41 }
42 
process_int(const struct device * dev)43 static void process_int(const struct device *dev)
44 {
45 	struct adt7420_data *drv_data = dev->data;
46 	const struct adt7420_dev_config *cfg = dev->config;
47 	uint8_t status;
48 
49 	/* Clear the status */
50 	if (i2c_reg_read_byte_dt(&cfg->i2c,
51 				 ADT7420_REG_STATUS, &status) < 0) {
52 		return;
53 	}
54 
55 	if (drv_data->th_handler != NULL) {
56 		drv_data->th_handler(dev, drv_data->th_trigger);
57 	}
58 
59 	setup_int(dev, true);
60 
61 	/* Check for pin that asserted while we were offline */
62 	int pv = gpio_pin_get_dt(&cfg->int_gpio);
63 
64 	if (pv > 0) {
65 		handle_int(dev);
66 	}
67 }
68 
adt7420_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)69 static void adt7420_gpio_callback(const struct device *dev,
70 				  struct gpio_callback *cb, uint32_t pins)
71 {
72 	struct adt7420_data *drv_data =
73 		CONTAINER_OF(cb, struct adt7420_data, gpio_cb);
74 
75 	handle_int(drv_data->dev);
76 }
77 
78 #if defined(CONFIG_ADT7420_TRIGGER_OWN_THREAD)
adt7420_thread(void * p1,void * p2,void * p3)79 static void adt7420_thread(void *p1, void *p2, void *p3)
80 {
81 	ARG_UNUSED(p2);
82 	ARG_UNUSED(p3);
83 
84 	struct adt7420_data *drv_data = p1;
85 
86 	while (true) {
87 		k_sem_take(&drv_data->gpio_sem, K_FOREVER);
88 		process_int(drv_data->dev);
89 	}
90 }
91 
92 #elif defined(CONFIG_ADT7420_TRIGGER_GLOBAL_THREAD)
adt7420_work_cb(struct k_work * work)93 static void adt7420_work_cb(struct k_work *work)
94 {
95 	struct adt7420_data *drv_data =
96 		CONTAINER_OF(work, struct adt7420_data, work);
97 
98 	process_int(drv_data->dev);
99 }
100 #endif
101 
adt7420_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)102 int adt7420_trigger_set(const struct device *dev,
103 			const struct sensor_trigger *trig,
104 			sensor_trigger_handler_t handler)
105 {
106 	struct adt7420_data *drv_data = dev->data;
107 	const struct adt7420_dev_config *cfg = dev->config;
108 
109 	if (!cfg->int_gpio.port) {
110 		return -ENOTSUP;
111 	}
112 
113 	setup_int(dev, false);
114 
115 	if (trig->type != SENSOR_TRIG_THRESHOLD) {
116 		LOG_ERR("Unsupported sensor trigger");
117 		return -ENOTSUP;
118 	}
119 	drv_data->th_handler = handler;
120 
121 	if (handler != NULL) {
122 		drv_data->th_trigger = trig;
123 
124 		setup_int(dev, true);
125 
126 		/* Check whether already asserted */
127 		int pv = gpio_pin_get_dt(&cfg->int_gpio);
128 
129 		if (pv > 0) {
130 			handle_int(dev);
131 		}
132 	}
133 
134 	return 0;
135 }
136 
adt7420_init_interrupt(const struct device * dev)137 int adt7420_init_interrupt(const struct device *dev)
138 {
139 	struct adt7420_data *drv_data = dev->data;
140 	const struct adt7420_dev_config *cfg = dev->config;
141 	int rc;
142 
143 	if (!gpio_is_ready_dt(&cfg->int_gpio)) {
144 		LOG_ERR("%s: device %s is not ready", dev->name,
145 			cfg->int_gpio.port->name);
146 		return -ENODEV;
147 	}
148 
149 	gpio_init_callback(&drv_data->gpio_cb,
150 			   adt7420_gpio_callback,
151 			   BIT(cfg->int_gpio.pin));
152 
153 	rc = gpio_pin_configure_dt(&cfg->int_gpio, GPIO_INPUT | cfg->int_gpio.dt_flags);
154 	if (rc < 0) {
155 		return rc;
156 	}
157 
158 	rc = gpio_add_callback(cfg->int_gpio.port, &drv_data->gpio_cb);
159 	if (rc < 0) {
160 		return rc;
161 	}
162 
163 	drv_data->dev = dev;
164 
165 #if defined(CONFIG_ADT7420_TRIGGER_OWN_THREAD)
166 	k_sem_init(&drv_data->gpio_sem, 0, K_SEM_MAX_LIMIT);
167 
168 	k_thread_create(&drv_data->thread, drv_data->thread_stack,
169 			CONFIG_ADT7420_THREAD_STACK_SIZE,
170 			adt7420_thread, drv_data,
171 			NULL, NULL, K_PRIO_COOP(CONFIG_ADT7420_THREAD_PRIORITY),
172 			0, K_NO_WAIT);
173 
174 	k_thread_name_set(&drv_data->thread, dev->name);
175 #elif defined(CONFIG_ADT7420_TRIGGER_GLOBAL_THREAD)
176 	drv_data->work.handler = adt7420_work_cb;
177 #endif
178 
179 	return 0;
180 }
181