1 /* ST Microelectronics IIS2MDC 3-axis magnetometer sensor
2  *
3  * Copyright (c) 2020 STMicroelectronics
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  *
7  * Datasheet:
8  * https://www.st.com/resource/en/datasheet/iis2mdc.pdf
9  */
10 
11 #define DT_DRV_COMPAT st_iis2mdc
12 
13 #include <zephyr/kernel.h>
14 #include <zephyr/drivers/sensor.h>
15 #include <zephyr/drivers/gpio.h>
16 #include <zephyr/logging/log.h>
17 #include "iis2mdc.h"
18 
19 LOG_MODULE_DECLARE(IIS2MDC, CONFIG_SENSOR_LOG_LEVEL);
20 
iis2mdc_enable_int(const struct device * dev,int enable)21 static int iis2mdc_enable_int(const struct device *dev, int enable)
22 {
23 	struct iis2mdc_data *iis2mdc = dev->data;
24 
25 	/* set interrupt on mag */
26 	return iis2mdc_drdy_on_pin_set(iis2mdc->ctx, enable);
27 }
28 
29 /* link external trigger to event data ready */
iis2mdc_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)30 int iis2mdc_trigger_set(const struct device *dev,
31 			  const struct sensor_trigger *trig,
32 			  sensor_trigger_handler_t handler)
33 {
34 	struct iis2mdc_data *iis2mdc = dev->data;
35 	int16_t raw[3];
36 
37 	if (trig->chan == SENSOR_CHAN_MAGN_XYZ) {
38 		iis2mdc->handler_drdy = handler;
39 		iis2mdc->trig_drdy = trig;
40 		if (handler) {
41 			/* fetch raw data sample: re-trigger lost interrupt */
42 			iis2mdc_magnetic_raw_get(iis2mdc->ctx, raw);
43 
44 			return iis2mdc_enable_int(dev, 1);
45 		} else {
46 			return iis2mdc_enable_int(dev, 0);
47 		}
48 	}
49 
50 	return -ENOTSUP;
51 }
52 
53 /* handle the drdy event: read data and call handler if registered any */
iis2mdc_handle_interrupt(const struct device * dev)54 static void iis2mdc_handle_interrupt(const struct device *dev)
55 {
56 	struct iis2mdc_data *iis2mdc = dev->data;
57 	const struct iis2mdc_dev_config *const config = dev->config;
58 
59 	if (iis2mdc->handler_drdy != NULL) {
60 		iis2mdc->handler_drdy(dev, iis2mdc->trig_drdy);
61 	}
62 
63 	gpio_pin_interrupt_configure_dt(&config->gpio_drdy,
64 					GPIO_INT_EDGE_TO_ACTIVE);
65 }
66 
iis2mdc_gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)67 static void iis2mdc_gpio_callback(const struct device *dev,
68 				    struct gpio_callback *cb, uint32_t pins)
69 {
70 	struct iis2mdc_data *iis2mdc =
71 		CONTAINER_OF(cb, struct iis2mdc_data, gpio_cb);
72 	const struct iis2mdc_dev_config *const config = iis2mdc->dev->config;
73 
74 	ARG_UNUSED(pins);
75 
76 	gpio_pin_interrupt_configure_dt(&config->gpio_drdy, GPIO_INT_DISABLE);
77 
78 #if defined(CONFIG_IIS2MDC_TRIGGER_OWN_THREAD)
79 	k_sem_give(&iis2mdc->gpio_sem);
80 #elif defined(CONFIG_IIS2MDC_TRIGGER_GLOBAL_THREAD)
81 	k_work_submit(&iis2mdc->work);
82 #endif
83 }
84 
85 #ifdef CONFIG_IIS2MDC_TRIGGER_OWN_THREAD
iis2mdc_thread(void * p1,void * p2,void * p3)86 static void iis2mdc_thread(void *p1, void *p2, void *p3)
87 {
88 	ARG_UNUSED(p2);
89 	ARG_UNUSED(p3);
90 
91 	struct iis2mdc_data *iis2mdc = p1;
92 
93 	while (1) {
94 		k_sem_take(&iis2mdc->gpio_sem, K_FOREVER);
95 		iis2mdc_handle_interrupt(iis2mdc->dev);
96 	}
97 }
98 #endif
99 
100 #ifdef CONFIG_IIS2MDC_TRIGGER_GLOBAL_THREAD
iis2mdc_work_cb(struct k_work * work)101 static void iis2mdc_work_cb(struct k_work *work)
102 {
103 	struct iis2mdc_data *iis2mdc =
104 		CONTAINER_OF(work, struct iis2mdc_data, work);
105 
106 	iis2mdc_handle_interrupt(iis2mdc->dev);
107 }
108 #endif
109 
iis2mdc_init_interrupt(const struct device * dev)110 int iis2mdc_init_interrupt(const struct device *dev)
111 {
112 	struct iis2mdc_data *iis2mdc = dev->data;
113 	const struct iis2mdc_dev_config *const config = dev->config;
114 	int ret;
115 
116 	/* setup data ready gpio interrupt */
117 	if (!gpio_is_ready_dt(&config->gpio_drdy)) {
118 		LOG_ERR("Cannot get pointer to drdy_gpio device");
119 		return -ENODEV;
120 	}
121 
122 #if defined(CONFIG_IIS2MDC_TRIGGER_OWN_THREAD)
123 	k_sem_init(&iis2mdc->gpio_sem, 0, K_SEM_MAX_LIMIT);
124 	k_thread_create(&iis2mdc->thread, iis2mdc->thread_stack,
125 			CONFIG_IIS2MDC_THREAD_STACK_SIZE,
126 			iis2mdc_thread, iis2mdc,
127 			NULL, NULL, K_PRIO_COOP(CONFIG_IIS2MDC_THREAD_PRIORITY),
128 			0, K_NO_WAIT);
129 #elif defined(CONFIG_IIS2MDC_TRIGGER_GLOBAL_THREAD)
130 	iis2mdc->work.handler = iis2mdc_work_cb;
131 #endif
132 
133 	ret = gpio_pin_configure_dt(&config->gpio_drdy, GPIO_INPUT);
134 	if (ret < 0) {
135 		LOG_ERR("Could not configure gpio");
136 		return ret;
137 	}
138 
139 	gpio_init_callback(&iis2mdc->gpio_cb,
140 			   iis2mdc_gpio_callback,
141 			   BIT(config->gpio_drdy.pin));
142 
143 	if (gpio_add_callback(config->gpio_drdy.port, &iis2mdc->gpio_cb) < 0) {
144 		LOG_DBG("Could not set gpio callback");
145 		return -EIO;
146 	}
147 
148 	return gpio_pin_interrupt_configure_dt(&config->gpio_drdy,
149 					       GPIO_INT_EDGE_TO_ACTIVE);
150 }
151