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(struct iis2mdc_data * iis2mdc)86 static void iis2mdc_thread(struct iis2mdc_data *iis2mdc)
87 {
88 	while (1) {
89 		k_sem_take(&iis2mdc->gpio_sem, K_FOREVER);
90 		iis2mdc_handle_interrupt(iis2mdc->dev);
91 	}
92 }
93 #endif
94 
95 #ifdef CONFIG_IIS2MDC_TRIGGER_GLOBAL_THREAD
iis2mdc_work_cb(struct k_work * work)96 static void iis2mdc_work_cb(struct k_work *work)
97 {
98 	struct iis2mdc_data *iis2mdc =
99 		CONTAINER_OF(work, struct iis2mdc_data, work);
100 
101 	iis2mdc_handle_interrupt(iis2mdc->dev);
102 }
103 #endif
104 
iis2mdc_init_interrupt(const struct device * dev)105 int iis2mdc_init_interrupt(const struct device *dev)
106 {
107 	struct iis2mdc_data *iis2mdc = dev->data;
108 	const struct iis2mdc_dev_config *const config = dev->config;
109 	int ret;
110 
111 	/* setup data ready gpio interrupt */
112 	if (!device_is_ready(config->gpio_drdy.port)) {
113 		LOG_ERR("Cannot get pointer to drdy_gpio device");
114 		return -ENODEV;
115 	}
116 
117 #if defined(CONFIG_IIS2MDC_TRIGGER_OWN_THREAD)
118 	k_sem_init(&iis2mdc->gpio_sem, 0, K_SEM_MAX_LIMIT);
119 	k_thread_create(&iis2mdc->thread, iis2mdc->thread_stack,
120 			CONFIG_IIS2MDC_THREAD_STACK_SIZE,
121 			(k_thread_entry_t)iis2mdc_thread, iis2mdc,
122 			NULL, NULL, K_PRIO_COOP(CONFIG_IIS2MDC_THREAD_PRIORITY),
123 			0, K_NO_WAIT);
124 #elif defined(CONFIG_IIS2MDC_TRIGGER_GLOBAL_THREAD)
125 	iis2mdc->work.handler = iis2mdc_work_cb;
126 #endif
127 
128 	ret = gpio_pin_configure_dt(&config->gpio_drdy, GPIO_INPUT);
129 	if (ret < 0) {
130 		LOG_ERR("Could not configure gpio");
131 		return ret;
132 	}
133 
134 	gpio_init_callback(&iis2mdc->gpio_cb,
135 			   iis2mdc_gpio_callback,
136 			   BIT(config->gpio_drdy.pin));
137 
138 	if (gpio_add_callback(config->gpio_drdy.port, &iis2mdc->gpio_cb) < 0) {
139 		LOG_DBG("Could not set gpio callback");
140 		return -EIO;
141 	}
142 
143 	return gpio_pin_interrupt_configure_dt(&config->gpio_drdy,
144 					       GPIO_INT_EDGE_TO_ACTIVE);
145 }
146