1 /*
2  * Copyright (c) 2023 Michal Morsisko
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ti_tmag5170
8 
9 #include <zephyr/kernel.h>
10 #include <zephyr/drivers/sensor.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/pm/device.h>
13 #include <zephyr/logging/log.h>
14 
15 #include "tmag5170.h"
16 
17 LOG_MODULE_DECLARE(TMAG5170, CONFIG_SENSOR_LOG_LEVEL);
18 
tmag5170_handle_interrupts(const void * arg)19 static void tmag5170_handle_interrupts(const void *arg)
20 {
21 	const struct device *dev = (const struct device *)arg;
22 	struct tmag5170_data *data = dev->data;
23 
24 	if (data->handler_drdy) {
25 		data->handler_drdy(dev, data->trigger_drdy);
26 	}
27 }
28 
29 #if defined(CONFIG_TMAG5170_TRIGGER_OWN_THREAD)
tmag5170_thread_main(void * arg1,void * unused1,void * unused2)30 static void tmag5170_thread_main(void *arg1, void *unused1, void *unused2)
31 {
32 	ARG_UNUSED(unused1);
33 	ARG_UNUSED(unused2);
34 	const struct device *dev = (const struct device *)arg1;
35 	struct tmag5170_data *data = dev->data;
36 
37 	while (1) {
38 		k_sem_take(&data->sem, K_FOREVER);
39 		tmag5170_handle_interrupts(dev);
40 	}
41 }
42 #endif
43 
44 #if defined(CONFIG_TMAG5170_TRIGGER_GLOBAL_THREAD)
tmag5170_work_handler(struct k_work * work)45 static void tmag5170_work_handler(struct k_work *work)
46 {
47 	struct tmag5170_data *data = CONTAINER_OF(work,
48 						  struct tmag5170_data,
49 						  work);
50 
51 	tmag5170_handle_interrupts(data->dev);
52 }
53 #endif
54 
tmag5170_gpio_callback(const struct device * port,struct gpio_callback * cb,uint32_t pin)55 static void tmag5170_gpio_callback(const struct device *port,
56 				   struct gpio_callback *cb,
57 				   uint32_t pin)
58 {
59 	struct tmag5170_data *data = CONTAINER_OF(cb,
60 						  struct tmag5170_data,
61 						  gpio_cb);
62 
63 	ARG_UNUSED(port);
64 	ARG_UNUSED(pin);
65 
66 #if defined(CONFIG_TMAG5170_TRIGGER_OWN_THREAD)
67 	k_sem_give(&data->sem);
68 #elif defined(CONFIG_TMAG5170_TRIGGER_GLOBAL_THREAD)
69 	k_work_submit(&data->work);
70 #elif defined(CONFIG_TMAG5170_TRIGGER_DIRECT)
71 	tmag5170_handle_interrupts(data->dev);
72 #endif
73 }
74 
tmag5170_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)75 int tmag5170_trigger_set(
76 	const struct device *dev,
77 	const struct sensor_trigger *trig,
78 	sensor_trigger_handler_t handler)
79 {
80 	struct tmag5170_data *data = dev->data;
81 
82 #if defined(CONFIG_PM_DEVICE)
83 	enum pm_device_state state;
84 
85 	(void)pm_device_state_get(dev, &state);
86 	if (state != PM_DEVICE_STATE_ACTIVE) {
87 		return -EBUSY;
88 	}
89 #endif
90 
91 	if (trig->type != SENSOR_TRIG_DATA_READY) {
92 		return -ENOTSUP;
93 	}
94 
95 	data->trigger_drdy = trig;
96 	data->handler_drdy = handler;
97 
98 	return 0;
99 }
100 
tmag5170_trigger_init(const struct device * dev)101 int tmag5170_trigger_init(const struct device *dev)
102 {
103 	struct tmag5170_data *data = dev->data;
104 	const struct tmag5170_dev_config *cfg = dev->config;
105 	int ret;
106 
107 	if (!device_is_ready(cfg->int_gpio.port)) {
108 		LOG_ERR("%s: device %s is not ready", dev->name, cfg->int_gpio.port->name);
109 		return -ENODEV;
110 	}
111 
112 	data->dev = dev;
113 
114 #if defined(CONFIG_TMAG5170_TRIGGER_OWN_THREAD)
115 	k_sem_init(&data->sem, 0, 1);
116 	k_thread_create(
117 		&data->thread,
118 		data->thread_stack,
119 		CONFIG_TMAG5170_THREAD_STACK_SIZE,
120 		tmag5170_thread_main,
121 		(void *)dev,
122 		NULL,
123 		NULL,
124 		K_PRIO_COOP(CONFIG_TMAG5170_THREAD_PRIORITY),
125 		0,
126 		K_NO_WAIT);
127 #elif defined(CONFIG_TMAG5170_TRIGGER_GLOBAL_THREAD)
128 	data->work.handler = tmag5170_work_handler;
129 #endif
130 	ret = gpio_pin_configure_dt(&cfg->int_gpio, GPIO_INPUT);
131 
132 	if (ret < 0) {
133 		return ret;
134 	}
135 
136 	gpio_init_callback(&data->gpio_cb, tmag5170_gpio_callback, BIT(cfg->int_gpio.pin));
137 
138 	ret = gpio_add_callback(cfg->int_gpio.port, &data->gpio_cb);
139 	if (ret < 0) {
140 		return ret;
141 	}
142 
143 	ret = gpio_pin_interrupt_configure_dt(&cfg->int_gpio, GPIO_INT_EDGE_FALLING);
144 	if (ret < 0) {
145 		return ret;
146 	}
147 
148 	return ret;
149 }
150