1 /* ST Microelectronics IIS328DQ 3-axis accelerometer driver
2 *
3 * Copyright (c) 2020 STMicroelectronics
4 * Copyright (c) 2024 SILA Embedded Solutions GmbH
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 *
8 * Datasheet:
9 * https://www.st.com/resource/en/datasheet/iis328dq.pdf
10 */
11
12 #define DT_DRV_COMPAT st_iis328dq
13
14 #include <string.h>
15 #include <zephyr/init.h>
16 #include <zephyr/sys/__assert.h>
17 #include <zephyr/sys/byteorder.h>
18 #include <zephyr/logging/log.h>
19 #include <zephyr/drivers/sensor.h>
20
21 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
22 #include <zephyr/drivers/spi.h>
23 #elif DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
24 #include <zephyr/drivers/i2c.h>
25 #endif
26
27 #include "iis328dq.h"
28
29 LOG_MODULE_REGISTER(IIS328DQ, CONFIG_SENSOR_LOG_LEVEL);
30
31 /**
32 * iis328dq_set_odr - set new Full Scale (in ±g)
33 */
iis328dq_set_range(const struct device * dev,uint8_t fs)34 static int iis328dq_set_range(const struct device *dev, uint8_t fs)
35 {
36 int err;
37 struct iis328dq_data *iis328dq = dev->data;
38 const struct iis328dq_config *cfg = dev->config;
39 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
40 iis328dq_fs_t fs_reg;
41 uint8_t gain;
42
43 if (fs <= 2) {
44 fs_reg = IIS328DQ_2g;
45 gain = 1;
46 } else if (fs <= 4) {
47 fs_reg = IIS328DQ_4g;
48 gain = 2;
49 } else if (fs <= 8) {
50 fs_reg = IIS328DQ_8g;
51 gain = 4;
52 } else {
53 LOG_ERR("FS too high");
54 return -ENOTSUP;
55 }
56 err = iis328dq_full_scale_set(ctx, fs_reg);
57
58 if (!err) {
59 iis328dq->gain = gain;
60 }
61
62 return err;
63 }
64
65 /**
66 * iis328dq_set_odr - set new Output Data Rate/sampling frequency (in Hz)
67 */
iis328dq_set_odr(const struct device * dev,uint16_t odr)68 static int iis328dq_set_odr(const struct device *dev, uint16_t odr)
69 {
70 const struct iis328dq_config *cfg = dev->config;
71 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
72
73 iis328dq_dr_t odr_reg;
74
75 if (odr == 0U) {
76 odr_reg = IIS328DQ_ODR_OFF;
77 } else if (odr <= 1) {
78 odr_reg = IIS328DQ_ODR_1Hz;
79 } else if (odr <= 2) {
80 odr_reg = IIS328DQ_ODR_2Hz;
81 } else if (odr <= 5) {
82 odr_reg = IIS328DQ_ODR_5Hz;
83 } else if (odr <= 10) {
84 odr_reg = IIS328DQ_ODR_10Hz;
85 } else if (odr <= 50) {
86 odr_reg = IIS328DQ_ODR_50Hz;
87 } else if (odr <= 100) {
88 odr_reg = IIS328DQ_ODR_100Hz;
89 } else if (odr <= 400) {
90 odr_reg = IIS328DQ_ODR_400Hz;
91 } else if (odr <= 1000) {
92 odr_reg = IIS328DQ_ODR_1kHz;
93 } else {
94 LOG_ERR("ODR too high");
95 return -ENOTSUP;
96 }
97
98 if (iis328dq_data_rate_set(ctx, odr_reg) != 0) {
99 LOG_ERR("Failed to set ODR");
100 return -EIO;
101 }
102
103 return 0;
104 }
105
iis328dq_convert(struct sensor_value * val,int raw_val,uint8_t gain)106 static inline void iis328dq_convert(struct sensor_value *val, int raw_val, uint8_t gain)
107 {
108 int64_t dval;
109
110 /* Gain is in mg/LSB */
111 /* Convert to μm/s^2 */
112 dval = ((int64_t)raw_val * gain * SENSOR_G) / 1000LL;
113 val->val1 = dval / 1000000LL;
114 val->val2 = dval % 1000000LL;
115 }
116
iis328dq_channel_get_acc(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)117 static inline void iis328dq_channel_get_acc(const struct device *dev, enum sensor_channel chan,
118 struct sensor_value *val)
119 {
120 int i;
121 uint8_t ofs_start, ofs_stop;
122 struct iis328dq_data *iis328dq = dev->data;
123 struct sensor_value *pval = val;
124
125 switch (chan) {
126 case SENSOR_CHAN_ACCEL_X:
127 ofs_start = ofs_stop = 0U;
128 break;
129 case SENSOR_CHAN_ACCEL_Y:
130 ofs_start = ofs_stop = 1U;
131 break;
132 case SENSOR_CHAN_ACCEL_Z:
133 ofs_start = ofs_stop = 2U;
134 break;
135 default:
136 ofs_start = 0U;
137 ofs_stop = 2U;
138 break;
139 }
140
141 for (i = ofs_start; i <= ofs_stop; i++) {
142 iis328dq_convert(pval++, iis328dq->acc[i], iis328dq->gain);
143 }
144 }
145
iis328dq_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)146 static int iis328dq_channel_get(const struct device *dev, enum sensor_channel chan,
147 struct sensor_value *val)
148 {
149 switch (chan) {
150 case SENSOR_CHAN_ACCEL_X:
151 case SENSOR_CHAN_ACCEL_Y:
152 case SENSOR_CHAN_ACCEL_Z:
153 case SENSOR_CHAN_ACCEL_XYZ:
154 iis328dq_channel_get_acc(dev, chan, val);
155 return 0;
156 default:
157 LOG_DBG("Channel not supported");
158 break;
159 }
160
161 return -ENOTSUP;
162 }
163
164 #ifdef CONFIG_IIS328DQ_THRESHOLD
iis328dq_set_threshold(const struct device * dev,bool is_lower,const struct sensor_value * val)165 static int iis328dq_set_threshold(const struct device *dev, bool is_lower,
166 const struct sensor_value *val)
167 {
168 int err;
169 const struct iis328dq_config *cfg = dev->config;
170 struct iis328dq_data *iis328dq = dev->data;
171 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
172
173 if (val->val1 < 0 || val->val2 < 0) {
174 /* thresholds are absolute */
175 return -EINVAL;
176 }
177 int64_t micro_ms2 = (val->val1 * INT64_C(1000000)) + val->val2;
178 /* factor guessed from similar-looking LIS2DH12 datasheet */
179 uint8_t mg_per_digit = iis328dq->gain * 16;
180
181 int16_t val_raw = (micro_ms2 * 1000LL) / SENSOR_G / mg_per_digit;
182
183 if (is_lower) {
184 /* internal INT1 handles lower threshold */
185 err = iis328dq_int1_treshold_set(ctx, val_raw);
186 if (err) {
187 LOG_ERR("Could not set INT1_THS to 0x%02X, error %d", val_raw, err);
188 return err;
189 }
190 } else {
191 /* internal INT2 handles lower threshold */
192 err = iis328dq_int2_treshold_set(ctx, val_raw);
193 if (err) {
194 LOG_ERR("Could not set INT2_THS to 0x%02X, error %d", val_raw, err);
195 return err;
196 }
197 }
198 return 0;
199 }
200
iis328dq_set_duration(const struct device * dev,uint16_t dur)201 static int iis328dq_set_duration(const struct device *dev, uint16_t dur)
202 {
203 int err;
204 const struct iis328dq_config *cfg = dev->config;
205 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
206
207 if (dur > 0x7F) {
208 LOG_WRN("Duration value %u too large", dur);
209 return -EINVAL;
210 }
211
212 err = iis328dq_int1_dur_set(ctx, dur);
213 if (err) {
214 LOG_ERR("Could not set INT1_DUR to 0x%02X, error %d", dur, err);
215 return err;
216 }
217 err = iis328dq_int2_dur_set(ctx, dur);
218 if (err) {
219 LOG_ERR("Could not set INT2_DUR to 0x%02X, error %d", dur, err);
220 return err;
221 }
222 return 0;
223 }
224 #endif /* CONFIG_IIS328DQ_THRESHOLD */
225 #define IIS328DQ_ATTR_DURATION SENSOR_ATTR_PRIV_START
226
iis328dq_dev_config(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)227 static int iis328dq_dev_config(const struct device *dev, enum sensor_channel chan,
228 enum sensor_attribute attr, const struct sensor_value *val)
229 {
230 switch (attr) {
231 case SENSOR_ATTR_FULL_SCALE:
232 return iis328dq_set_range(dev, sensor_ms2_to_g(val));
233 case SENSOR_ATTR_SAMPLING_FREQUENCY:
234 return iis328dq_set_odr(dev, val->val1);
235 #ifdef CONFIG_IIS328DQ_THRESHOLD
236 case SENSOR_ATTR_LOWER_THRESH:
237 case SENSOR_ATTR_UPPER_THRESH:
238 if (chan != SENSOR_CHAN_ACCEL_XYZ) {
239 LOG_ERR("Threshold cannot be set per-channel");
240 return -ENOTSUP;
241 }
242 return iis328dq_set_threshold(dev, attr == SENSOR_ATTR_LOWER_THRESH, val);
243 case IIS328DQ_ATTR_DURATION:
244 if (chan != SENSOR_CHAN_ACCEL_XYZ) {
245 LOG_ERR("Duration cannot be set per-channel");
246 return -ENOTSUP;
247 }
248 return iis328dq_set_duration(dev, val->val1);
249 #endif /* CONFIG_IIS328DQ_THRESHOLD */
250 default:
251 LOG_DBG("Acc attribute not supported");
252 break;
253 }
254
255 return -ENOTSUP;
256 }
257
iis328dq_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)258 static int iis328dq_attr_set(const struct device *dev, enum sensor_channel chan,
259 enum sensor_attribute attr, const struct sensor_value *val)
260 {
261 switch (chan) {
262 case SENSOR_CHAN_ACCEL_X:
263 case SENSOR_CHAN_ACCEL_Y:
264 case SENSOR_CHAN_ACCEL_Z:
265 case SENSOR_CHAN_ACCEL_XYZ:
266 case SENSOR_CHAN_ALL:
267 return iis328dq_dev_config(dev, chan, attr, val);
268 default:
269 LOG_DBG("Attr not supported on %d channel", chan);
270 break;
271 }
272
273 return -ENOTSUP;
274 }
275
iis328dq_sample_fetch(const struct device * dev,enum sensor_channel chan)276 static int iis328dq_sample_fetch(const struct device *dev, enum sensor_channel chan)
277 {
278 struct iis328dq_data *iis328dq = dev->data;
279 const struct iis328dq_config *cfg = dev->config;
280 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
281 int16_t buf[3];
282
283 /* fetch raw data sample */
284 if (iis328dq_acceleration_raw_get(ctx, buf) < 0) {
285 LOG_DBG("Failed to fetch raw data sample");
286 return -EIO;
287 }
288
289 iis328dq->acc[0] = buf[0] >> 4;
290 iis328dq->acc[1] = buf[1] >> 4;
291 iis328dq->acc[2] = buf[2] >> 4;
292
293 return 0;
294 }
295
296 static DEVICE_API(sensor, iis328dq_driver_api) = {
297 .attr_set = iis328dq_attr_set,
298 #if CONFIG_IIS328DQ_TRIGGER
299 .trigger_set = iis328dq_trigger_set,
300 #endif /* CONFIG_IIS328DQ_TRIGGER */
301 .sample_fetch = iis328dq_sample_fetch,
302 .channel_get = iis328dq_channel_get,
303 };
304
iis328dq_init(const struct device * dev)305 static int iis328dq_init(const struct device *dev)
306 {
307 struct iis328dq_data *iis328dq = dev->data;
308 const struct iis328dq_config *cfg = dev->config;
309 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
310 uint8_t reg_value;
311
312 iis328dq->dev = dev;
313
314 /* check chip ID */
315 if (iis328dq_device_id_get(ctx, ®_value) < 0) {
316 return -EIO;
317 }
318
319 if (reg_value != IIS328DQ_ID) {
320 LOG_ERR("Invalid chip ID");
321 return -EINVAL;
322 }
323
324 /* reset device */
325 if (iis328dq_boot_set(ctx, PROPERTY_ENABLE) < 0) {
326 return -EIO;
327 }
328
329 k_sleep(K_MSEC(100));
330
331 if (iis328dq_boot_get(ctx, ®_value) < 0) {
332 return -EIO;
333 }
334 if (reg_value != PROPERTY_DISABLE) {
335 LOG_ERR("BOOT did not deassert");
336 return -EIO;
337 }
338
339 if (iis328dq_block_data_update_set(ctx, PROPERTY_ENABLE) < 0) {
340 return -EIO;
341 }
342
343 /* set default odr to 12.5Hz acc */
344 if (iis328dq_set_odr(dev, 12) < 0) {
345 LOG_ERR("odr init error (12.5 Hz)");
346 return -EIO;
347 }
348
349 if (iis328dq_set_range(dev, cfg->range) < 0) {
350 LOG_ERR("range init error %d", cfg->range);
351 return -EIO;
352 }
353
354 #ifdef CONFIG_IIS328DQ_TRIGGER
355 if (iis328dq_init_interrupt(dev) < 0) {
356 LOG_ERR("Failed to initialize interrupts");
357 return -EIO;
358 }
359 #endif /* CONFIG_IIS328DQ_TRIGGER */
360
361 return 0;
362 }
363
364 /*
365 * Device creation macro, shared by IIS328DQ_DEFINE_SPI() and
366 * IIS328DQ_DEFINE_I2C().
367 */
368
369 #define IIS328DQ_DEVICE_INIT(inst) \
370 SENSOR_DEVICE_DT_INST_DEFINE(inst, iis328dq_init, NULL, &iis328dq_data_##inst, \
371 &iis328dq_config_##inst, POST_KERNEL, \
372 CONFIG_SENSOR_INIT_PRIORITY, &iis328dq_driver_api);
373
374 #ifdef CONFIG_IIS328DQ_TRIGGER
375 #ifdef CONFIG_IIS328DQ_THRESHOLD
376 #define IIS328DQ_CFG_IRQ_THRESHOLD(inst) \
377 .threshold_pad = DT_INST_PROP_OR(inst, threshold_int_pad, -1),
378 #else
379 #define IIS328DQ_CFG_IRQ_THRESHOLD(inst)
380 #endif /* CONFIG_IIS328DQ_THRESHOLD */
381
382 #define IIS328DQ_CFG_IRQ(inst) \
383 .gpio_int1 = GPIO_DT_SPEC_INST_GET_OR(inst, int1_gpios, {0}), \
384 .gpio_int2 = GPIO_DT_SPEC_INST_GET_OR(inst, int2_gpios, {0}), \
385 .drdy_pad = DT_INST_PROP_OR(inst, drdy_int_pad, -1), IIS328DQ_CFG_IRQ_THRESHOLD(inst)
386 #else
387 #define IIS328DQ_CFG_IRQ(inst)
388 #endif /* CONFIG_IIS328DQ_TRIGGER */
389
390 #define IIS328DQ_CONFIG_COMMON(inst) \
391 .range = DT_INST_PROP(inst, range), \
392 IF_ENABLED(UTIL_OR(DT_INST_NODE_HAS_PROP(inst, int1_gpios), \
393 DT_INST_NODE_HAS_PROP(inst, int2_gpios)), \
394 (IIS328DQ_CFG_IRQ(inst)))
395
396 /*
397 * Instantiation macros used when a device is on a SPI bus.
398 */
399
400 #define IIS328DQ_SPI_OPERATION \
401 (SPI_WORD_SET(8) | SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA)
402
403 #define IIS328DQ_CONFIG_SPI(inst) \
404 { \
405 STMEMSC_CTX_SPI_INCR(&iis328dq_config_##inst.stmemsc_cfg), \
406 .stmemsc_cfg = \
407 { \
408 .spi = SPI_DT_SPEC_INST_GET(inst, IIS328DQ_SPI_OPERATION, \
409 0), \
410 }, \
411 IIS328DQ_CONFIG_COMMON(inst) \
412 }
413
414 /*
415 * Instantiation macros used when a device is on an I2C bus.
416 */
417
418 #define IIS328DQ_CONFIG_I2C(inst) \
419 { \
420 STMEMSC_CTX_I2C_INCR(&iis328dq_config_##inst.stmemsc_cfg), \
421 .stmemsc_cfg = \
422 { \
423 .i2c = I2C_DT_SPEC_INST_GET(inst), \
424 }, \
425 IIS328DQ_CONFIG_COMMON(inst) \
426 }
427
428 /*
429 * Main instantiation macro. Use of COND_CODE_1() selects the right
430 * bus-specific macro at preprocessor time.
431 */
432
433 #define IIS328DQ_DEFINE(inst) \
434 static struct iis328dq_data iis328dq_data_##inst; \
435 static const struct iis328dq_config iis328dq_config_##inst = \
436 COND_CODE_1(DT_INST_ON_BUS(inst, spi), (IIS328DQ_CONFIG_SPI(inst)), \
437 (IIS328DQ_CONFIG_I2C(inst))); \
438 IIS328DQ_DEVICE_INIT(inst) \
439 IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, drdy_int_pad), \
440 (BUILD_ASSERT( \
441 DT_INST_NODE_HAS_PROP( \
442 inst, CONCAT(int, DT_INST_PROP(inst, drdy_int_pad), _gpios)), \
443 "No GPIO pin defined for IIS328DQ DRDY interrupt");)) \
444 IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, threshold_int_pad), \
445 (BUILD_ASSERT(DT_INST_NODE_HAS_PROP( \
446 inst, CONCAT(int, DT_INST_PROP(inst, threshold_int_pad), \
447 _gpios)), \
448 "No GPIO pin defined for IIS328DQ threshold interrupt");)) \
449 IF_ENABLED( \
450 UTIL_AND(DT_INST_NODE_HAS_PROP(inst, drdy_int_pad), \
451 DT_INST_NODE_HAS_PROP(inst, threshold_int_pad)), \
452 (BUILD_ASSERT( \
453 DT_INST_PROP(inst, drdy_int_pad) != \
454 DT_INST_PROP(inst, threshold_int_pad), \
455 "IIS328DQ DRDY interrupt and threshold interrupt cannot share a pin");))
456
457 DT_INST_FOREACH_STATUS_OKAY(IIS328DQ_DEFINE)
458