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