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/init.h>
14 #include <zephyr/sys/__assert.h>
15 #include <zephyr/drivers/sensor.h>
16 #include <string.h>
17 #include <zephyr/logging/log.h>
18 #include "iis2mdc.h"
19 
20 struct iis2mdc_data iis2mdc_data;
21 
22 LOG_MODULE_REGISTER(IIS2MDC, CONFIG_SENSOR_LOG_LEVEL);
23 
24 #ifdef CONFIG_IIS2MDC_MAG_ODR_RUNTIME
iis2mdc_set_odr(const struct device * dev,const struct sensor_value * val)25 static int iis2mdc_set_odr(const struct device *dev,
26 			   const struct sensor_value *val)
27 {
28 	struct iis2mdc_data *iis2mdc = dev->data;
29 	iis2mdc_odr_t odr;
30 
31 	switch (val->val1) {
32 	case 10:
33 		odr = IIS2MDC_ODR_10Hz;
34 		break;
35 	case 20:
36 		odr = IIS2MDC_ODR_20Hz;
37 		break;
38 	case 50:
39 		odr = IIS2MDC_ODR_50Hz;
40 		break;
41 	case 100:
42 		odr = IIS2MDC_ODR_100Hz;
43 		break;
44 	default:
45 		return -EINVAL;
46 	}
47 
48 	if (iis2mdc_data_rate_set(iis2mdc->ctx, odr)) {
49 		return -EIO;
50 	}
51 
52 	return 0;
53 }
54 #endif /* CONFIG_IIS2MDC_MAG_ODR_RUNTIME */
55 
iis2mdc_set_hard_iron(const struct device * dev,enum sensor_channel chan,const struct sensor_value * val)56 static int iis2mdc_set_hard_iron(const struct device *dev,
57 				   enum sensor_channel chan,
58 				   const struct sensor_value *val)
59 {
60 	struct iis2mdc_data *iis2mdc = dev->data;
61 	uint8_t i;
62 	int16_t offset[3];
63 
64 	for (i = 0U; i < 3; i++) {
65 		offset[i] = val->val1;
66 		val++;
67 	}
68 
69 	return iis2mdc_mag_user_offset_set(iis2mdc->ctx, offset);
70 }
71 
iis2mdc_channel_get_mag(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)72 static void iis2mdc_channel_get_mag(const struct device *dev,
73 				      enum sensor_channel chan,
74 				      struct sensor_value *val)
75 {
76 	int32_t cval;
77 	int i;
78 	uint8_t ofs_start, ofs_stop;
79 	struct iis2mdc_data *iis2mdc = dev->data;
80 	struct sensor_value *pval = val;
81 
82 	switch (chan) {
83 	case SENSOR_CHAN_MAGN_X:
84 		ofs_start = ofs_stop = 0U;
85 		break;
86 	case SENSOR_CHAN_MAGN_Y:
87 		ofs_start = ofs_stop = 1U;
88 		break;
89 	case SENSOR_CHAN_MAGN_Z:
90 		ofs_start = ofs_stop = 2U;
91 		break;
92 	default:
93 		ofs_start = 0U; ofs_stop = 2U;
94 		break;
95 	}
96 
97 	for (i = ofs_start; i <= ofs_stop; i++) {
98 		cval = iis2mdc->mag[i] * 1500;
99 		pval->val1 = cval / 1000000;
100 		pval->val2 = cval % 1000000;
101 		pval++;
102 	}
103 }
104 
105 /* read internal temperature */
iis2mdc_channel_get_temp(const struct device * dev,struct sensor_value * val)106 static void iis2mdc_channel_get_temp(const struct device *dev,
107 				       struct sensor_value *val)
108 {
109 	struct iis2mdc_data *drv_data = dev->data;
110 
111 	val->val1 = drv_data->temp_sample / 100;
112 	val->val2 = (drv_data->temp_sample % 100) * 10000;
113 }
114 
iis2mdc_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)115 static int iis2mdc_channel_get(const struct device *dev,
116 				 enum sensor_channel chan,
117 				 struct sensor_value *val)
118 {
119 	switch (chan) {
120 	case SENSOR_CHAN_MAGN_X:
121 	case SENSOR_CHAN_MAGN_Y:
122 	case SENSOR_CHAN_MAGN_Z:
123 	case SENSOR_CHAN_MAGN_XYZ:
124 		iis2mdc_channel_get_mag(dev, chan, val);
125 		break;
126 	case SENSOR_CHAN_DIE_TEMP:
127 		iis2mdc_channel_get_temp(dev, val);
128 		break;
129 	default:
130 		LOG_DBG("Channel not supported");
131 		return -ENOTSUP;
132 	}
133 
134 	return 0;
135 }
136 
iis2mdc_config(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)137 static int iis2mdc_config(const struct device *dev, enum sensor_channel chan,
138 			    enum sensor_attribute attr,
139 			    const struct sensor_value *val)
140 {
141 	switch (attr) {
142 #ifdef CONFIG_IIS2MDC_MAG_ODR_RUNTIME
143 	case SENSOR_ATTR_SAMPLING_FREQUENCY:
144 		return iis2mdc_set_odr(dev, val);
145 #endif /* CONFIG_IIS2MDC_MAG_ODR_RUNTIME */
146 	case SENSOR_ATTR_OFFSET:
147 		return iis2mdc_set_hard_iron(dev, chan, val);
148 	default:
149 		LOG_DBG("Mag attribute not supported");
150 		return -ENOTSUP;
151 	}
152 
153 	return 0;
154 }
155 
iis2mdc_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)156 static int iis2mdc_attr_set(const struct device *dev,
157 			      enum sensor_channel chan,
158 			      enum sensor_attribute attr,
159 			      const struct sensor_value *val)
160 {
161 	switch (chan) {
162 	case SENSOR_CHAN_ALL:
163 	case SENSOR_CHAN_MAGN_X:
164 	case SENSOR_CHAN_MAGN_Y:
165 	case SENSOR_CHAN_MAGN_Z:
166 	case SENSOR_CHAN_MAGN_XYZ:
167 		return iis2mdc_config(dev, chan, attr, val);
168 	default:
169 		LOG_DBG("attr_set() not supported on %d channel", chan);
170 		return -ENOTSUP;
171 	}
172 
173 	return 0;
174 }
175 
iis2mdc_sample_fetch_mag(const struct device * dev)176 static int iis2mdc_sample_fetch_mag(const struct device *dev)
177 {
178 	struct iis2mdc_data *iis2mdc = dev->data;
179 	int16_t raw_mag[3];
180 
181 	/* fetch raw data sample */
182 	if (iis2mdc_magnetic_raw_get(iis2mdc->ctx, raw_mag) < 0) {
183 		LOG_DBG("Failed to read sample");
184 		return -EIO;
185 	}
186 
187 	iis2mdc->mag[0] = raw_mag[0];
188 	iis2mdc->mag[1] = raw_mag[1];
189 	iis2mdc->mag[2] = raw_mag[2];
190 
191 	return 0;
192 }
193 
iis2mdc_sample_fetch_temp(const struct device * dev)194 static int iis2mdc_sample_fetch_temp(const struct device *dev)
195 {
196 	struct iis2mdc_data *iis2mdc = dev->data;
197 	int16_t raw_temp;
198 	int32_t temp;
199 
200 	/* fetch raw temperature sample */
201 	if (iis2mdc_temperature_raw_get(iis2mdc->ctx, &raw_temp) < 0) {
202 		LOG_DBG("Failed to read sample");
203 		return -EIO;
204 	}
205 
206 	/* formula is temp = 25 + (temp / 8) C */
207 	temp = raw_temp;
208 	iis2mdc->temp_sample = 2500 + (temp * 100) / 8;
209 
210 	return 0;
211 }
212 
iis2mdc_sample_fetch(const struct device * dev,enum sensor_channel chan)213 static int iis2mdc_sample_fetch(const struct device *dev,
214 				enum sensor_channel chan)
215 {
216 	switch (chan) {
217 	case SENSOR_CHAN_MAGN_X:
218 	case SENSOR_CHAN_MAGN_Y:
219 	case SENSOR_CHAN_MAGN_Z:
220 	case SENSOR_CHAN_MAGN_XYZ:
221 		iis2mdc_sample_fetch_mag(dev);
222 		break;
223 	case SENSOR_CHAN_DIE_TEMP:
224 		iis2mdc_sample_fetch_temp(dev);
225 		break;
226 	case SENSOR_CHAN_ALL:
227 		iis2mdc_sample_fetch_mag(dev);
228 		iis2mdc_sample_fetch_temp(dev);
229 		break;
230 	default:
231 		return -ENOTSUP;
232 	}
233 
234 	return 0;
235 }
236 
237 static DEVICE_API(sensor, iis2mdc_driver_api) = {
238 	.attr_set = iis2mdc_attr_set,
239 #if CONFIG_IIS2MDC_TRIGGER
240 	.trigger_set = iis2mdc_trigger_set,
241 #endif
242 	.sample_fetch = iis2mdc_sample_fetch,
243 	.channel_get = iis2mdc_channel_get,
244 };
245 
iis2mdc_init(const struct device * dev)246 static int iis2mdc_init(const struct device *dev)
247 {
248 	const struct iis2mdc_dev_config *const cfg = dev->config;
249 	struct iis2mdc_data *iis2mdc = dev->data;
250 	uint8_t wai;
251 
252 	iis2mdc->dev = dev;
253 
254 	if (cfg->bus_init(dev) < 0) {
255 		return -EINVAL;
256 	}
257 
258 	/* check chip ID */
259 	if (iis2mdc_device_id_get(iis2mdc->ctx, &wai) < 0) {
260 		return -EIO;
261 	}
262 
263 	if (wai != IIS2MDC_ID) {
264 		LOG_DBG("Invalid chip ID: %02x\n", wai);
265 		return -EINVAL;
266 	}
267 
268 	/* reset sensor configuration */
269 	if (iis2mdc_reset_set(iis2mdc->ctx, PROPERTY_ENABLE) < 0) {
270 		LOG_DBG("s/w reset failed\n");
271 		return -EIO;
272 	}
273 
274 	k_busy_wait(100);
275 
276 	/* enable BDU */
277 	if (iis2mdc_block_data_update_set(iis2mdc->ctx, PROPERTY_ENABLE) < 0) {
278 		LOG_DBG("setting bdu failed\n");
279 		return -EIO;
280 	}
281 
282 	/* Set Output Data Rate */
283 	if (iis2mdc_data_rate_set(iis2mdc->ctx, IIS2MDC_ODR_10Hz)) {
284 		LOG_DBG("set odr failed\n");
285 		return -EIO;
286 	}
287 
288 	/* Set / Reset sensor mode */
289 	if (iis2mdc_set_rst_mode_set(iis2mdc->ctx,
290 				     IIS2MDC_SENS_OFF_CANC_EVERY_ODR)) {
291 		LOG_DBG("reset sensor mode failed\n");
292 		return -EIO;
293 	}
294 
295 	/* Enable temperature compensation */
296 	if (iis2mdc_offset_temp_comp_set(iis2mdc->ctx, PROPERTY_ENABLE)) {
297 		LOG_DBG("enable temp compensation failed\n");
298 		return -EIO;
299 	}
300 
301 	/* Set device in continuous mode */
302 	if (iis2mdc_operating_mode_set(iis2mdc->ctx, IIS2MDC_CONTINUOUS_MODE)) {
303 		LOG_DBG("set continuous mode failed\n");
304 		return -EIO;
305 	}
306 
307 #ifdef CONFIG_IIS2MDC_TRIGGER
308 	if (iis2mdc_init_interrupt(dev) < 0) {
309 		LOG_DBG("Failed to initialize interrupts");
310 		return -EIO;
311 	}
312 #endif
313 
314 	return 0;
315 }
316 
317 #if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 0
318 #warning "IIS2MDC driver enabled without any devices"
319 #endif
320 
321 /*
322  * Device creation macro, shared by IIS2MDC_DEFINE_SPI() and
323  * IIS2MDC_DEFINE_I2C().
324  */
325 
326 #define IIS2MDC_DEVICE_INIT(inst)					\
327 	SENSOR_DEVICE_DT_INST_DEFINE(inst,				\
328 			    iis2mdc_init,				\
329 			    NULL,					\
330 			    &iis2mdc_data_##inst,			\
331 			    &iis2mdc_config_##inst,			\
332 			    POST_KERNEL,				\
333 			    CONFIG_SENSOR_INIT_PRIORITY,		\
334 			    &iis2mdc_driver_api);
335 
336 /*
337  * Instantiation macros used when a device is on a SPI bus.
338  */
339 
340 #ifdef CONFIG_IIS2MDC_TRIGGER
341 #define IIS2MDC_CFG_IRQ(inst) \
342 	.gpio_drdy = GPIO_DT_SPEC_INST_GET(inst, drdy_gpios),
343 #else
344 #define IIS2MDC_CFG_IRQ(inst)
345 #endif /* CONFIG_IIS2MDC_TRIGGER */
346 
347 #define IIS2MDC_SPI_OP  (SPI_WORD_SET(8) |				\
348 			 SPI_OP_MODE_MASTER |				\
349 			 SPI_MODE_CPOL |				\
350 			 SPI_MODE_CPHA)					\
351 
352 #define IIS2MDC_CONFIG_SPI(inst)					\
353 	{								\
354 		.spi = SPI_DT_SPEC_INST_GET(inst, IIS2MDC_SPI_OP, 0),	\
355 		.bus_init = iis2mdc_spi_init,				\
356 		COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, drdy_gpios),	\
357 			(IIS2MDC_CFG_IRQ(inst)), ())			\
358 	}
359 
360 /*
361  * Instantiation macros used when a device is on an I2C bus.
362  */
363 
364 #define IIS2MDC_CONFIG_I2C(inst)					\
365 	{								\
366 		.i2c = I2C_DT_SPEC_INST_GET(inst),			\
367 		.bus_init = iis2mdc_i2c_init,				\
368 		COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, drdy_gpios),	\
369 			(IIS2MDC_CFG_IRQ(inst)), ())			\
370 	}
371 
372 /*
373  * Main instantiation macro. Use of COND_CODE_1() selects the right
374  * bus-specific macro at preprocessor time.
375  */
376 
377 #define IIS2MDC_DEFINE(inst)						\
378 	static struct iis2mdc_data iis2mdc_data_##inst;			\
379 	static const struct iis2mdc_dev_config iis2mdc_config_##inst =	\
380 		COND_CODE_1(DT_INST_ON_BUS(inst, spi),			\
381 			(IIS2MDC_CONFIG_SPI(inst)),			\
382 			(IIS2MDC_CONFIG_I2C(inst)));			\
383 	IIS2MDC_DEVICE_INIT(inst)
384 
385 DT_INST_FOREACH_STATUS_OKAY(IIS2MDC_DEFINE)
386