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(struct adt7420_data * drv_data)79 static void adt7420_thread(struct adt7420_data *drv_data)
80 {
81 	while (true) {
82 		k_sem_take(&drv_data->gpio_sem, K_FOREVER);
83 		process_int(drv_data->dev);
84 	}
85 }
86 
87 #elif defined(CONFIG_ADT7420_TRIGGER_GLOBAL_THREAD)
adt7420_work_cb(struct k_work * work)88 static void adt7420_work_cb(struct k_work *work)
89 {
90 	struct adt7420_data *drv_data =
91 		CONTAINER_OF(work, struct adt7420_data, work);
92 
93 	process_int(drv_data->dev);
94 }
95 #endif
96 
adt7420_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)97 int adt7420_trigger_set(const struct device *dev,
98 			const struct sensor_trigger *trig,
99 			sensor_trigger_handler_t handler)
100 {
101 	struct adt7420_data *drv_data = dev->data;
102 	const struct adt7420_dev_config *cfg = dev->config;
103 
104 	if (!cfg->int_gpio.port) {
105 		return -ENOTSUP;
106 	}
107 
108 	setup_int(dev, false);
109 
110 	if (trig->type != SENSOR_TRIG_THRESHOLD) {
111 		LOG_ERR("Unsupported sensor trigger");
112 		return -ENOTSUP;
113 	}
114 	drv_data->th_handler = handler;
115 
116 	if (handler != NULL) {
117 		drv_data->th_trigger = trig;
118 
119 		setup_int(dev, true);
120 
121 		/* Check whether already asserted */
122 		int pv = gpio_pin_get_dt(&cfg->int_gpio);
123 
124 		if (pv > 0) {
125 			handle_int(dev);
126 		}
127 	}
128 
129 	return 0;
130 }
131 
adt7420_init_interrupt(const struct device * dev)132 int adt7420_init_interrupt(const struct device *dev)
133 {
134 	struct adt7420_data *drv_data = dev->data;
135 	const struct adt7420_dev_config *cfg = dev->config;
136 	int rc;
137 
138 	if (!gpio_is_ready_dt(&cfg->int_gpio)) {
139 		LOG_ERR("%s: device %s is not ready", dev->name,
140 			cfg->int_gpio.port->name);
141 		return -ENODEV;
142 	}
143 
144 	gpio_init_callback(&drv_data->gpio_cb,
145 			   adt7420_gpio_callback,
146 			   BIT(cfg->int_gpio.pin));
147 
148 	rc = gpio_pin_configure_dt(&cfg->int_gpio, GPIO_INPUT | cfg->int_gpio.dt_flags);
149 	if (rc < 0) {
150 		return rc;
151 	}
152 
153 	rc = gpio_add_callback(cfg->int_gpio.port, &drv_data->gpio_cb);
154 	if (rc < 0) {
155 		return rc;
156 	}
157 
158 	drv_data->dev = dev;
159 
160 #if defined(CONFIG_ADT7420_TRIGGER_OWN_THREAD)
161 	k_sem_init(&drv_data->gpio_sem, 0, K_SEM_MAX_LIMIT);
162 
163 	k_thread_create(&drv_data->thread, drv_data->thread_stack,
164 			CONFIG_ADT7420_THREAD_STACK_SIZE,
165 			(k_thread_entry_t)adt7420_thread, drv_data,
166 			NULL, NULL, K_PRIO_COOP(CONFIG_ADT7420_THREAD_PRIORITY),
167 			0, K_NO_WAIT);
168 #elif defined(CONFIG_ADT7420_TRIGGER_GLOBAL_THREAD)
169 	drv_data->work.handler = adt7420_work_cb;
170 #endif
171 
172 	return 0;
173 }
174