1 /* ST Microelectronics LIS2MDL 3-axis magnetometer sensor
2 *
3 * Copyright (c) 2018-2019 STMicroelectronics
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Datasheet:
8 * https://www.st.com/resource/en/datasheet/lis2mdl.pdf
9 */
10
11 #define DT_DRV_COMPAT st_lis2mdl
12
13 #include <zephyr/init.h>
14 #include <zephyr/sys/__assert.h>
15 #include <zephyr/drivers/sensor.h>
16 #include <zephyr/pm/device.h>
17 #include <string.h>
18 #include <zephyr/logging/log.h>
19 #include "lis2mdl.h"
20
21 /* Based on the data sheet, the maximum turn-on time is ("9.4 ms + 1/ODR") when
22 * offset cancellation is on. But in the single mode the ODR is not dependent on
23 * the configured value in Reg A. It is dependent on the frequency of the I2C
24 * signal. The slowest value we could measure by I2C frequency of 100000HZ was
25 * 13 ms. So we chose 20 ms.
26 */
27 #define SAMPLE_FETCH_TIMEOUT_MS 20
28
29 struct lis2mdl_data lis2mdl_data;
30
31 LOG_MODULE_REGISTER(LIS2MDL, CONFIG_SENSOR_LOG_LEVEL);
32
33 #ifdef CONFIG_LIS2MDL_MAG_ODR_RUNTIME
lis2mdl_set_odr(const struct device * dev,const struct sensor_value * val)34 static int lis2mdl_set_odr(const struct device *dev,
35 const struct sensor_value *val)
36 {
37 const struct lis2mdl_config *cfg = dev->config;
38 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
39 lis2mdl_odr_t odr;
40
41 switch (val->val1) {
42 case 10:
43 odr = LIS2MDL_ODR_10Hz;
44 break;
45 case 20:
46 odr = LIS2MDL_ODR_20Hz;
47 break;
48 case 50:
49 odr = LIS2MDL_ODR_50Hz;
50 break;
51 case 100:
52 odr = LIS2MDL_ODR_100Hz;
53 break;
54 default:
55 return -EINVAL;
56 }
57
58 if (lis2mdl_data_rate_set(ctx, odr)) {
59 return -EIO;
60 }
61
62 return 0;
63 }
64 #endif /* CONFIG_LIS2MDL_MAG_ODR_RUNTIME */
65
lis2mdl_set_hard_iron(const struct device * dev,enum sensor_channel chan,const struct sensor_value * val)66 static int lis2mdl_set_hard_iron(const struct device *dev,
67 enum sensor_channel chan,
68 const struct sensor_value *val)
69 {
70 const struct lis2mdl_config *cfg = dev->config;
71 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
72 uint8_t i;
73 int16_t offset[3];
74
75 for (i = 0U; i < 3; i++) {
76 offset[i] = val->val1;
77 val++;
78 }
79
80 return lis2mdl_mag_user_offset_set(ctx, offset);
81 }
82
lis2mdl_channel_get_mag(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)83 static void lis2mdl_channel_get_mag(const struct device *dev,
84 enum sensor_channel chan,
85 struct sensor_value *val)
86 {
87 int32_t cval;
88 int i;
89 uint8_t ofs_start, ofs_stop;
90 struct lis2mdl_data *lis2mdl = dev->data;
91 struct sensor_value *pval = val;
92
93 switch (chan) {
94 case SENSOR_CHAN_MAGN_X:
95 ofs_start = ofs_stop = 0U;
96 break;
97 case SENSOR_CHAN_MAGN_Y:
98 ofs_start = ofs_stop = 1U;
99 break;
100 case SENSOR_CHAN_MAGN_Z:
101 ofs_start = ofs_stop = 2U;
102 break;
103 default:
104 ofs_start = 0U; ofs_stop = 2U;
105 break;
106 }
107
108 for (i = ofs_start; i <= ofs_stop; i++) {
109 cval = lis2mdl->mag[i] * 1500;
110 pval->val1 = cval / 1000000;
111 pval->val2 = cval % 1000000;
112 pval++;
113 }
114 }
115
116 /* read internal temperature */
lis2mdl_channel_get_temp(const struct device * dev,struct sensor_value * val)117 static void lis2mdl_channel_get_temp(const struct device *dev,
118 struct sensor_value *val)
119 {
120 struct lis2mdl_data *drv_data = dev->data;
121
122 /* formula is temp = 25 + (temp / 8) C */
123 val->val1 = 25 + drv_data->temp_sample / 8;
124 val->val2 = (drv_data->temp_sample % 8) * 1000000 / 8;
125 }
126
lis2mdl_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)127 static int lis2mdl_channel_get(const struct device *dev,
128 enum sensor_channel chan,
129 struct sensor_value *val)
130 {
131 switch (chan) {
132 case SENSOR_CHAN_MAGN_X:
133 case SENSOR_CHAN_MAGN_Y:
134 case SENSOR_CHAN_MAGN_Z:
135 case SENSOR_CHAN_MAGN_XYZ:
136 lis2mdl_channel_get_mag(dev, chan, val);
137 break;
138 case SENSOR_CHAN_DIE_TEMP:
139 lis2mdl_channel_get_temp(dev, val);
140 break;
141 default:
142 LOG_ERR("Channel not supported");
143 return -ENOTSUP;
144 }
145
146 return 0;
147 }
148
lis2mdl_config(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)149 static int lis2mdl_config(const struct device *dev, enum sensor_channel chan,
150 enum sensor_attribute attr,
151 const struct sensor_value *val)
152 {
153 switch (attr) {
154 #ifdef CONFIG_LIS2MDL_MAG_ODR_RUNTIME
155 case SENSOR_ATTR_SAMPLING_FREQUENCY:
156 return lis2mdl_set_odr(dev, val);
157 #endif /* CONFIG_LIS2MDL_MAG_ODR_RUNTIME */
158 case SENSOR_ATTR_OFFSET:
159 return lis2mdl_set_hard_iron(dev, chan, val);
160 default:
161 LOG_ERR("Mag attribute not supported");
162 return -ENOTSUP;
163 }
164
165 return 0;
166 }
167
lis2mdl_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)168 static int lis2mdl_attr_set(const struct device *dev,
169 enum sensor_channel chan,
170 enum sensor_attribute attr,
171 const struct sensor_value *val)
172 {
173 switch (chan) {
174 case SENSOR_CHAN_ALL:
175 case SENSOR_CHAN_MAGN_X:
176 case SENSOR_CHAN_MAGN_Y:
177 case SENSOR_CHAN_MAGN_Z:
178 case SENSOR_CHAN_MAGN_XYZ:
179 return lis2mdl_config(dev, chan, attr, val);
180 default:
181 LOG_ERR("attr_set() not supported on %d channel", chan);
182 return -ENOTSUP;
183 }
184
185 return 0;
186 }
187
get_single_mode_raw_data(const struct device * dev,int16_t * raw_mag)188 static int get_single_mode_raw_data(const struct device *dev,
189 int16_t *raw_mag)
190 {
191 struct lis2mdl_data *lis2mdl = dev->data;
192 const struct lis2mdl_config *cfg = dev->config;
193 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
194 int rc = 0;
195
196 rc = lis2mdl_operating_mode_set(ctx, LIS2MDL_SINGLE_TRIGGER);
197 if (rc) {
198 LOG_ERR("set single mode failed");
199 return rc;
200 }
201
202 if (k_sem_take(&lis2mdl->fetch_sem, K_MSEC(SAMPLE_FETCH_TIMEOUT_MS))) {
203 LOG_ERR("Magnetometer data not ready within %d ms",
204 SAMPLE_FETCH_TIMEOUT_MS);
205 return -EIO;
206 }
207
208 /* fetch raw data sample */
209 rc = lis2mdl_magnetic_raw_get(ctx, raw_mag);
210 if (rc) {
211 LOG_ERR("Failed to read sample");
212 return rc;
213 }
214 return 0;
215 }
216
lis2mdl_sample_fetch_mag(const struct device * dev)217 static int lis2mdl_sample_fetch_mag(const struct device *dev)
218 {
219 struct lis2mdl_data *lis2mdl = dev->data;
220 const struct lis2mdl_config *cfg = dev->config;
221 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
222 int16_t raw_mag[3];
223 int rc = 0;
224
225 if (cfg->single_mode) {
226 rc = get_single_mode_raw_data(dev, raw_mag);
227 if (rc) {
228 LOG_ERR("Failed to read raw data");
229 return rc;
230 }
231 lis2mdl->mag[0] = raw_mag[0];
232 lis2mdl->mag[1] = raw_mag[1];
233 lis2mdl->mag[2] = raw_mag[2];
234
235 if (cfg->cancel_offset) {
236 /* The second measurement is needed when offset
237 * cancellation is enabled in the single mode. Then the
238 * average of the first measurement done above and this
239 * one would be the final value. This process is not
240 * needed in continuous mode since it has been taken
241 * care by lis2mdl itself automatically. Please refer
242 * to the application note for more details.
243 */
244 rc = get_single_mode_raw_data(dev, raw_mag);
245 if (rc) {
246 LOG_ERR("Failed to read raw data");
247 return rc;
248 }
249 lis2mdl->mag[0] += raw_mag[0];
250 lis2mdl->mag[1] += raw_mag[1];
251 lis2mdl->mag[2] += raw_mag[2];
252 lis2mdl->mag[0] /= 2;
253 lis2mdl->mag[1] /= 2;
254 lis2mdl->mag[2] /= 2;
255 }
256
257 } else {
258 /* fetch raw data sample */
259 rc = lis2mdl_magnetic_raw_get(ctx, raw_mag);
260 if (rc) {
261 LOG_ERR("Failed to read sample");
262 return rc;
263 }
264 lis2mdl->mag[0] = raw_mag[0];
265 lis2mdl->mag[1] = raw_mag[1];
266 lis2mdl->mag[2] = raw_mag[2];
267 }
268 return 0;
269 }
270
lis2mdl_sample_fetch_temp(const struct device * dev)271 static int lis2mdl_sample_fetch_temp(const struct device *dev)
272 {
273 struct lis2mdl_data *lis2mdl = dev->data;
274 const struct lis2mdl_config *cfg = dev->config;
275 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
276 int16_t raw_temp;
277
278 /* fetch raw temperature sample */
279 if (lis2mdl_temperature_raw_get(ctx, &raw_temp) < 0) {
280 LOG_ERR("Failed to read sample");
281 return -EIO;
282 }
283
284 lis2mdl->temp_sample = raw_temp;
285
286 return 0;
287 }
288
lis2mdl_sample_fetch(const struct device * dev,enum sensor_channel chan)289 static int lis2mdl_sample_fetch(const struct device *dev,
290 enum sensor_channel chan)
291 {
292 switch (chan) {
293 case SENSOR_CHAN_MAGN_X:
294 case SENSOR_CHAN_MAGN_Y:
295 case SENSOR_CHAN_MAGN_Z:
296 case SENSOR_CHAN_MAGN_XYZ:
297 lis2mdl_sample_fetch_mag(dev);
298 break;
299 case SENSOR_CHAN_DIE_TEMP:
300 lis2mdl_sample_fetch_temp(dev);
301 break;
302 case SENSOR_CHAN_ALL:
303 lis2mdl_sample_fetch_mag(dev);
304 lis2mdl_sample_fetch_temp(dev);
305 break;
306 default:
307 return -ENOTSUP;
308 }
309
310 return 0;
311 }
312
313 static DEVICE_API(sensor, lis2mdl_driver_api) = {
314 .attr_set = lis2mdl_attr_set,
315 #if CONFIG_LIS2MDL_TRIGGER
316 .trigger_set = lis2mdl_trigger_set,
317 #endif
318 .sample_fetch = lis2mdl_sample_fetch,
319 .channel_get = lis2mdl_channel_get,
320 };
321
lis2mdl_init(const struct device * dev)322 static int lis2mdl_init(const struct device *dev)
323 {
324 struct lis2mdl_data *lis2mdl = dev->data;
325 const struct lis2mdl_config *cfg = dev->config;
326 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
327 uint8_t wai;
328 int rc = 0;
329
330 lis2mdl->dev = dev;
331
332 if (cfg->spi_4wires) {
333 /* Set SPI 4wires if it's the case */
334 if (lis2mdl_spi_mode_set(ctx, LIS2MDL_SPI_4_WIRE) < 0) {
335 return -EIO;
336 }
337 }
338
339 /* check chip ID */
340 if (lis2mdl_device_id_get(ctx, &wai) < 0) {
341 return -EIO;
342 }
343
344 if (wai != LIS2MDL_ID) {
345 LOG_ERR("Invalid chip ID: %02x", wai);
346 return -EINVAL;
347 }
348
349 /* reset sensor configuration */
350 if (lis2mdl_reset_set(ctx, PROPERTY_ENABLE) < 0) {
351 LOG_ERR("s/w reset failed");
352 return -EIO;
353 }
354
355 k_busy_wait(100);
356
357 if (cfg->spi_4wires) {
358 /* After s/w reset set SPI 4wires again if the case */
359 if (lis2mdl_spi_mode_set(ctx, LIS2MDL_SPI_4_WIRE) < 0) {
360 return -EIO;
361 }
362 }
363
364 /* enable BDU */
365 if (lis2mdl_block_data_update_set(ctx, PROPERTY_ENABLE) < 0) {
366 LOG_ERR("setting bdu failed");
367 return -EIO;
368 }
369
370 /* Set Output Data Rate */
371 if (lis2mdl_data_rate_set(ctx, LIS2MDL_ODR_10Hz)) {
372 LOG_ERR("set odr failed");
373 return -EIO;
374 }
375
376 if (cfg->cancel_offset) {
377 /* Set offset cancellation, common for both single and
378 * and continuous mode.
379 */
380 if (lis2mdl_set_rst_mode_set(ctx,
381 LIS2MDL_SENS_OFF_CANC_EVERY_ODR)) {
382 LOG_ERR("reset sensor mode failed");
383 return -EIO;
384 }
385 }
386
387 /* Enable temperature compensation */
388 if (lis2mdl_offset_temp_comp_set(ctx, PROPERTY_ENABLE)) {
389 LOG_ERR("enable temp compensation failed");
390 return -EIO;
391 }
392
393 if (cfg->cancel_offset && cfg->single_mode) {
394 /* Set OFF_CANC_ONE_SHOT bit. This setting is only needed in
395 * the single-mode when offset cancellation is enabled.
396 */
397 rc = lis2mdl_set_rst_sensor_single_set(ctx,
398 PROPERTY_ENABLE);
399 if (rc) {
400 LOG_ERR("Set offset cancellation failed");
401 return rc;
402 }
403 }
404
405 if (cfg->single_mode) {
406 /* Set drdy on pin 7 */
407 rc = lis2mdl_drdy_on_pin_set(ctx, 1);
408 if (rc) {
409 LOG_ERR("set drdy on pin failed!");
410 return rc;
411 }
412
413 /* Reboot sensor after setting the configuration registers */
414 rc = lis2mdl_boot_set(ctx, 1);
415 if (rc) {
416 LOG_ERR("Reboot failed.");
417 return rc;
418 }
419
420 k_sem_init(&lis2mdl->fetch_sem, 0, 1);
421
422 } else {
423 /* Set device in continuous mode */
424 rc = lis2mdl_operating_mode_set(ctx,
425 LIS2MDL_CONTINUOUS_MODE);
426 if (rc) {
427 LOG_ERR("set continuous mode failed");
428 return rc;
429 }
430 }
431
432 #ifdef CONFIG_LIS2MDL_TRIGGER
433 if (cfg->trig_enabled) {
434 if (lis2mdl_init_interrupt(dev) < 0) {
435 LOG_ERR("Failed to initialize interrupts");
436 return -EIO;
437 }
438 }
439 #endif
440
441 return 0;
442 }
443
444 #ifdef CONFIG_PM_DEVICE
lis2mdl_pm_action(const struct device * dev,enum pm_device_action action)445 static int lis2mdl_pm_action(const struct device *dev,
446 enum pm_device_action action)
447 {
448 const struct lis2mdl_config *config = dev->config;
449 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&config->ctx;
450 int status = 0;
451
452 switch (action) {
453 case PM_DEVICE_ACTION_RESUME:
454 if (config->single_mode) {
455 status = lis2mdl_operating_mode_set(ctx,
456 LIS2MDL_SINGLE_TRIGGER);
457 } else {
458 status = lis2mdl_operating_mode_set(ctx,
459 LIS2MDL_CONTINUOUS_MODE);
460 }
461 if (status) {
462 LOG_ERR("Power up failed");
463 }
464 LOG_DBG("State changed to active");
465 break;
466 case PM_DEVICE_ACTION_SUSPEND:
467 status = lis2mdl_operating_mode_set(ctx, LIS2MDL_POWER_DOWN);
468 if (status) {
469 LOG_ERR("Power down failed");
470 }
471 LOG_DBG("State changed to inactive");
472 break;
473 default:
474 return -ENOTSUP;
475 }
476
477 return status;
478 }
479 #endif /* CONFIG_PM_DEVICE */
480
481 #if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 0
482 #warning "LIS2MDL driver enabled without any devices"
483 #endif
484
485 /*
486 * Device creation macro, shared by LIS2MDL_DEFINE_SPI() and
487 * LIS2MDL_DEFINE_I2C().
488 */
489
490 #define LIS2MDL_DEVICE_INIT(inst) \
491 PM_DEVICE_DT_INST_DEFINE(inst, lis2mdl_pm_action); \
492 \
493 SENSOR_DEVICE_DT_INST_DEFINE(inst, \
494 lis2mdl_init, \
495 PM_DEVICE_DT_INST_GET(inst), \
496 &lis2mdl_data_##inst, \
497 &lis2mdl_config_##inst, \
498 POST_KERNEL, \
499 CONFIG_SENSOR_INIT_PRIORITY, \
500 &lis2mdl_driver_api);
501
502 /*
503 * Instantiation macros used when a device is on a SPI bus.
504 */
505
506 #ifdef CONFIG_LIS2MDL_TRIGGER
507 #define LIS2MDL_CFG_IRQ(inst) \
508 .trig_enabled = true, \
509 .gpio_drdy = GPIO_DT_SPEC_INST_GET(inst, irq_gpios)
510 #else
511 #define LIS2MDL_CFG_IRQ(inst)
512 #endif /* CONFIG_LIS2MDL_TRIGGER */
513
514 #define LIS2MDL_CONFIG_COMMON(inst) \
515 .cancel_offset = DT_INST_PROP(inst, cancel_offset), \
516 .single_mode = DT_INST_PROP(inst, single_mode), \
517 COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, irq_gpios), \
518 (LIS2MDL_CFG_IRQ(inst)), ())
519
520 #define LIS2MDL_SPI_OPERATION (SPI_WORD_SET(8) | \
521 SPI_OP_MODE_MASTER | \
522 SPI_MODE_CPOL | \
523 SPI_MODE_CPHA) \
524
525 #define LIS2MDL_CONFIG_SPI(inst) \
526 { \
527 STMEMSC_CTX_SPI(&lis2mdl_config_##inst.stmemsc_cfg), \
528 .stmemsc_cfg = { \
529 .spi = SPI_DT_SPEC_INST_GET(inst, \
530 LIS2MDL_SPI_OPERATION, \
531 0), \
532 }, \
533 .spi_4wires = DT_INST_PROP(inst, duplex) == \
534 SPI_FULL_DUPLEX, \
535 LIS2MDL_CONFIG_COMMON(inst) \
536 }
537
538 /*
539 * Instantiation macros used when a device is on an I2C bus.
540 */
541
542 #define LIS2MDL_CONFIG_I2C(inst) \
543 { \
544 STMEMSC_CTX_I2C(&lis2mdl_config_##inst.stmemsc_cfg), \
545 .stmemsc_cfg = { \
546 .i2c = I2C_DT_SPEC_INST_GET(inst), \
547 }, \
548 LIS2MDL_CONFIG_COMMON(inst) \
549 }
550
551 /*
552 * Main instantiation macro. Use of COND_CODE_1() selects the right
553 * bus-specific macro at preprocessor time.
554 */
555
556 #define LIS2MDL_DEFINE(inst) \
557 static struct lis2mdl_data lis2mdl_data_##inst; \
558 static const struct lis2mdl_config lis2mdl_config_##inst = \
559 COND_CODE_1(DT_INST_ON_BUS(inst, spi), \
560 (LIS2MDL_CONFIG_SPI(inst)), \
561 (LIS2MDL_CONFIG_I2C(inst))); \
562 LIS2MDL_DEVICE_INIT(inst)
563
564 DT_INST_FOREACH_STATUS_OKAY(LIS2MDL_DEFINE)
565