1 /*
2  * Copyright (c) 2022, Basalte bv
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ti_bq274xx
8 
9 #include <zephyr/drivers/sensor.h>
10 #include <zephyr/drivers/gpio.h>
11 #include <zephyr/logging/log.h>
12 
13 #ifdef CONFIG_BQ274XX_PM
14 #include <zephyr/pm/device.h>
15 #endif
16 
17 #include "bq274xx.h"
18 
19 LOG_MODULE_DECLARE(bq274xx, CONFIG_SENSOR_LOG_LEVEL);
20 
bq274xx_handle_interrupts(const struct device * dev)21 static void bq274xx_handle_interrupts(const struct device *dev)
22 {
23 	struct bq274xx_data *data = dev->data;
24 
25 	if (data->ready_handler) {
26 		data->ready_handler(dev, data->ready_trig);
27 	}
28 }
29 
30 #ifdef CONFIG_BQ274XX_TRIGGER_OWN_THREAD
31 static K_KERNEL_STACK_DEFINE(bq274xx_thread_stack, CONFIG_BQ274XX_THREAD_STACK_SIZE);
32 static struct k_thread bq274xx_thread;
33 
bq274xx_thread_main(void * p1,void * p2,void * p3)34 static void bq274xx_thread_main(void *p1, void *p2, void *p3)
35 {
36 	ARG_UNUSED(p2);
37 	ARG_UNUSED(p3);
38 
39 	struct bq274xx_data *data = p1;
40 
41 	while (1) {
42 		k_sem_take(&data->sem, K_FOREVER);
43 		bq274xx_handle_interrupts(data->dev);
44 	}
45 }
46 #endif
47 
48 #ifdef CONFIG_BQ274XX_TRIGGER_GLOBAL_THREAD
bq274xx_work_handler(struct k_work * work)49 static void bq274xx_work_handler(struct k_work *work)
50 {
51 	struct bq274xx_data *data = CONTAINER_OF(work, struct bq274xx_data, work);
52 
53 	bq274xx_handle_interrupts(data->dev);
54 }
55 #endif
56 
bq274xx_ready_callback_handler(const struct device * port,struct gpio_callback * cb,gpio_port_pins_t pins)57 static void bq274xx_ready_callback_handler(const struct device *port,
58 					   struct gpio_callback *cb,
59 					   gpio_port_pins_t pins)
60 {
61 	struct bq274xx_data *data = CONTAINER_OF(cb, struct bq274xx_data,
62 						 ready_callback);
63 
64 	ARG_UNUSED(port);
65 	ARG_UNUSED(pins);
66 
67 #if defined(CONFIG_BQ274XX_TRIGGER_OWN_THREAD)
68 	k_sem_give(&data->sem);
69 #elif defined(CONFIG_BQ274XX_TRIGGER_GLOBAL_THREAD)
70 	k_work_submit(&data->work);
71 #endif
72 }
73 
bq274xx_trigger_mode_init(const struct device * dev)74 int bq274xx_trigger_mode_init(const struct device *dev)
75 {
76 	const struct bq274xx_config *const config = dev->config;
77 	struct bq274xx_data *data = dev->data;
78 	int ret;
79 
80 	data->dev = dev;
81 
82 #if defined(CONFIG_BQ274XX_TRIGGER_OWN_THREAD)
83 	k_sem_init(&data->sem, 0, K_SEM_MAX_LIMIT);
84 
85 	k_thread_create(&bq274xx_thread, bq274xx_thread_stack,
86 			CONFIG_BQ274XX_THREAD_STACK_SIZE,
87 			bq274xx_thread_main,
88 			data, NULL, NULL,
89 			K_PRIO_COOP(CONFIG_BQ274XX_THREAD_PRIORITY),
90 			0, K_NO_WAIT);
91 #elif defined(CONFIG_BQ274XX_TRIGGER_GLOBAL_THREAD)
92 	k_work_init(&data->work, bq274xx_work_handler);
93 #endif
94 
95 	ret = gpio_pin_configure_dt(&config->int_gpios, GPIO_INPUT);
96 	if (ret < 0) {
97 		LOG_ERR("Unable to configure interrupt pin");
98 		return ret;
99 	}
100 	gpio_init_callback(&data->ready_callback,
101 			   bq274xx_ready_callback_handler,
102 			   BIT(config->int_gpios.pin));
103 
104 	return 0;
105 }
106 
bq274xx_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)107 int bq274xx_trigger_set(const struct device *dev,
108 			const struct sensor_trigger *trig,
109 			sensor_trigger_handler_t handler)
110 {
111 	const struct bq274xx_config *config = dev->config;
112 	struct bq274xx_data *data = dev->data;
113 	int ret;
114 
115 #ifdef CONFIG_BQ274XX_PM
116 	enum pm_device_state state;
117 
118 	(void)pm_device_state_get(dev, &state);
119 	if (state != PM_DEVICE_STATE_ACTIVE) {
120 		return -EBUSY;
121 	}
122 #endif
123 
124 	if (trig->type != SENSOR_TRIG_DATA_READY) {
125 		return -ENOTSUP;
126 	}
127 
128 	if (!gpio_is_ready_dt(&config->int_gpios)) {
129 		LOG_ERR("GPIO device is not ready");
130 		return -ENODEV;
131 	}
132 
133 	data->ready_handler = handler;
134 	data->ready_trig = trig;
135 
136 	if (handler) {
137 		ret = gpio_pin_configure_dt(&config->int_gpios, GPIO_INPUT);
138 		if (ret < 0) {
139 			LOG_ERR("Unable to configure interrupt pin: %d", ret);
140 			return ret;
141 		}
142 
143 		ret = gpio_add_callback(config->int_gpios.port,
144 					&data->ready_callback);
145 		if (ret < 0) {
146 			LOG_ERR("Unable to add interrupt callback: %d", ret);
147 			return ret;
148 		}
149 
150 		ret = gpio_pin_interrupt_configure_dt(&config->int_gpios,
151 						      GPIO_INT_EDGE_TO_ACTIVE);
152 		if (ret < 0) {
153 			LOG_ERR("Unable to configure interrupt: %d", ret);
154 			return ret;
155 		}
156 	} else {
157 		ret = gpio_remove_callback(config->int_gpios.port,
158 					   &data->ready_callback);
159 		if (ret < 0) {
160 			LOG_ERR("Unable to remove interrupt callback: %d", ret);
161 			return ret;
162 		}
163 
164 		ret = gpio_pin_interrupt_configure_dt(&config->int_gpios, GPIO_INT_DISABLE);
165 		if (ret < 0) {
166 			LOG_ERR("Unable to disable interrupt: %d", ret);
167 			return ret;
168 		}
169 	}
170 
171 	return 0;
172 }
173