1 /* ST Microelectronics IIS2DLPC 3-axis accelerometer driver
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/iis2dlpc.pdf
9 */
10
11 #define DT_DRV_COMPAT st_iis2dlpc
12
13 #include <zephyr/init.h>
14 #include <zephyr/sys/__assert.h>
15 #include <zephyr/logging/log.h>
16 #include <zephyr/drivers/sensor.h>
17
18 #if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
19 #include <zephyr/drivers/spi.h>
20 #elif DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
21 #include <zephyr/drivers/i2c.h>
22 #endif
23
24 #include "iis2dlpc.h"
25
26 LOG_MODULE_REGISTER(IIS2DLPC, CONFIG_SENSOR_LOG_LEVEL);
27
28 /**
29 * iis2dlpc_set_range - set full scale range for acc
30 * @dev: Pointer to instance of struct device (I2C or SPI)
31 * @range: Full scale range (2, 4, 8 and 16 G)
32 */
iis2dlpc_set_range(const struct device * dev,uint8_t fs)33 static int iis2dlpc_set_range(const struct device *dev, uint8_t fs)
34 {
35 int err;
36 struct iis2dlpc_data *iis2dlpc = dev->data;
37 const struct iis2dlpc_config *cfg = dev->config;
38 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
39 uint8_t shift_gain = 0U;
40
41 err = iis2dlpc_full_scale_set(ctx, fs);
42
43 if (cfg->pm == IIS2DLPC_CONT_LOW_PWR_12bit) {
44 shift_gain = IIS2DLPC_SHFT_GAIN_NOLP1;
45 }
46
47 if (!err) {
48 /* save internally gain for optimization */
49 iis2dlpc->gain = IIS2DLPC_FS_TO_GAIN(fs, shift_gain);
50 }
51
52 return err;
53 }
54
55 /**
56 * iis2dlpc_set_odr - set new sampling frequency
57 * @dev: Pointer to instance of struct device (I2C or SPI)
58 * @odr: Output data rate
59 */
iis2dlpc_set_odr(const struct device * dev,uint16_t odr)60 static int iis2dlpc_set_odr(const struct device *dev, uint16_t odr)
61 {
62 const struct iis2dlpc_config *cfg = dev->config;
63 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
64 uint8_t val;
65
66 /* check if power off */
67 if (odr == 0U) {
68 return iis2dlpc_data_rate_set(ctx, IIS2DLPC_XL_ODR_OFF);
69 }
70
71 val = IIS2DLPC_ODR_TO_REG(odr);
72 if (val > IIS2DLPC_XL_ODR_1k6Hz) {
73 LOG_ERR("ODR too high");
74 return -ENOTSUP;
75 }
76
77 return iis2dlpc_data_rate_set(ctx, val);
78 }
79
iis2dlpc_convert(struct sensor_value * val,int raw_val,float gain)80 static inline void iis2dlpc_convert(struct sensor_value *val, int raw_val,
81 float gain)
82 {
83 int64_t dval;
84
85 /* Gain is in ug/LSB */
86 /* Convert to m/s^2 */
87 dval = ((int64_t)raw_val * gain * SENSOR_G) / 1000000LL;
88 val->val1 = dval / 1000000LL;
89 val->val2 = dval % 1000000LL;
90 }
91
iis2dlpc_channel_get_acc(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)92 static inline void iis2dlpc_channel_get_acc(const struct device *dev,
93 enum sensor_channel chan,
94 struct sensor_value *val)
95 {
96 int i;
97 uint8_t ofs_start, ofs_stop;
98 struct iis2dlpc_data *iis2dlpc = dev->data;
99 struct sensor_value *pval = val;
100
101 switch (chan) {
102 case SENSOR_CHAN_ACCEL_X:
103 ofs_start = ofs_stop = 0U;
104 break;
105 case SENSOR_CHAN_ACCEL_Y:
106 ofs_start = ofs_stop = 1U;
107 break;
108 case SENSOR_CHAN_ACCEL_Z:
109 ofs_start = ofs_stop = 2U;
110 break;
111 default:
112 ofs_start = 0U; ofs_stop = 2U;
113 break;
114 }
115
116 for (i = ofs_start; i <= ofs_stop ; i++) {
117 iis2dlpc_convert(pval++, iis2dlpc->acc[i], iis2dlpc->gain);
118 }
119 }
120
iis2dlpc_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)121 static int iis2dlpc_channel_get(const struct device *dev,
122 enum sensor_channel chan,
123 struct sensor_value *val)
124 {
125 switch (chan) {
126 case SENSOR_CHAN_ACCEL_X:
127 case SENSOR_CHAN_ACCEL_Y:
128 case SENSOR_CHAN_ACCEL_Z:
129 case SENSOR_CHAN_ACCEL_XYZ:
130 iis2dlpc_channel_get_acc(dev, chan, val);
131 return 0;
132 default:
133 LOG_DBG("Channel not supported");
134 break;
135 }
136
137 return -ENOTSUP;
138 }
139
140 #ifdef CONFIG_IIS2DLPC_ACTIVITY
iis2dlpc_set_slope_th(const struct device * dev,uint16_t th)141 static int iis2dlpc_set_slope_th(const struct device *dev, uint16_t th)
142 {
143 int err;
144 const struct iis2dlpc_config *cfg = dev->config;
145 stmdev_ctx_t *ctx = (stmdev_ctx_t *) &cfg->ctx;
146
147 err = iis2dlpc_wkup_threshold_set(ctx, th & 0x3F);
148 if (err) {
149 LOG_ERR("Could not set WK_THS to 0x%02X, error %d",
150 th & 0x3F, err);
151 return err;
152 }
153 return 0;
154 }
iis2dlpc_set_slope_dur(const struct device * dev,uint16_t dur)155 static int iis2dlpc_set_slope_dur(const struct device *dev, uint16_t dur)
156 {
157 int err;
158 const struct iis2dlpc_config *cfg = dev->config;
159 stmdev_ctx_t *ctx = (stmdev_ctx_t *) &cfg->ctx;
160 uint8_t val;
161
162 val = (dur & 0x0F);
163 err = iis2dlpc_act_sleep_dur_set(ctx, val);
164 if (err) {
165 LOG_ERR("Could not set SLEEP_DUR to 0x%02X, error %d",
166 val, err);
167 return err;
168 }
169 val = ((dur >> 5) & 0x03);
170 err = iis2dlpc_wkup_dur_set(ctx, val);
171 if (err) {
172 LOG_ERR("Could not set WAKE_DUR to 0x%02X, error %d",
173 val, err);
174 return err;
175 }
176 return 0;
177 }
178 #endif /* CONFIG_IIS2DLPC_ACTIVITY */
179
iis2dlpc_dev_config(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)180 static int iis2dlpc_dev_config(const struct device *dev,
181 enum sensor_channel chan,
182 enum sensor_attribute attr,
183 const struct sensor_value *val)
184 {
185 switch (attr) {
186 case SENSOR_ATTR_FULL_SCALE:
187 return iis2dlpc_set_range(dev,
188 IIS2DLPC_FS_TO_REG(sensor_ms2_to_g(val)));
189 case SENSOR_ATTR_SAMPLING_FREQUENCY:
190 return iis2dlpc_set_odr(dev, val->val1);
191 #ifdef CONFIG_IIS2DLPC_ACTIVITY
192 case SENSOR_ATTR_SLOPE_TH:
193 return iis2dlpc_set_slope_th(dev, val->val1);
194 case SENSOR_ATTR_SLOPE_DUR:
195 return iis2dlpc_set_slope_dur(dev, val->val1);
196 #endif /* CONFIG_IIS2DLPC_ACTIVITY */
197 default:
198 LOG_DBG("Acc attribute not supported");
199 break;
200 }
201
202 return -ENOTSUP;
203 }
204
iis2dlpc_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)205 static int iis2dlpc_attr_set(const struct device *dev,
206 enum sensor_channel chan,
207 enum sensor_attribute attr,
208 const struct sensor_value *val)
209 {
210 switch (chan) {
211 case SENSOR_CHAN_ACCEL_X:
212 case SENSOR_CHAN_ACCEL_Y:
213 case SENSOR_CHAN_ACCEL_Z:
214 case SENSOR_CHAN_ACCEL_XYZ:
215 return iis2dlpc_dev_config(dev, chan, attr, val);
216 default:
217 LOG_DBG("Attr not supported on %d channel", chan);
218 break;
219 }
220
221 return -ENOTSUP;
222 }
223
iis2dlpc_sample_fetch(const struct device * dev,enum sensor_channel chan)224 static int iis2dlpc_sample_fetch(const struct device *dev,
225 enum sensor_channel chan)
226 {
227 struct iis2dlpc_data *iis2dlpc = dev->data;
228 const struct iis2dlpc_config *cfg = dev->config;
229 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
230 uint8_t shift;
231 int16_t buf[3];
232
233 /* fetch raw data sample */
234 if (iis2dlpc_acceleration_raw_get(ctx, buf) < 0) {
235 LOG_DBG("Failed to fetch raw data sample");
236 return -EIO;
237 }
238
239 /* adjust to resolution */
240 if (cfg->pm == IIS2DLPC_CONT_LOW_PWR_12bit) {
241 shift = IIS2DLPC_SHIFT_PM1;
242 } else {
243 shift = IIS2DLPC_SHIFT_PMOTHER;
244 }
245
246 iis2dlpc->acc[0] = buf[0] >> shift;
247 iis2dlpc->acc[1] = buf[1] >> shift;
248 iis2dlpc->acc[2] = buf[2] >> shift;
249
250 return 0;
251 }
252
253 static DEVICE_API(sensor, iis2dlpc_driver_api) = {
254 .attr_set = iis2dlpc_attr_set,
255 #if CONFIG_IIS2DLPC_TRIGGER
256 .trigger_set = iis2dlpc_trigger_set,
257 #endif /* CONFIG_IIS2DLPC_TRIGGER */
258 .sample_fetch = iis2dlpc_sample_fetch,
259 .channel_get = iis2dlpc_channel_get,
260 };
261
iis2dlpc_set_power_mode(stmdev_ctx_t * ctx,iis2dlpc_mode_t pm)262 static int iis2dlpc_set_power_mode(stmdev_ctx_t *ctx, iis2dlpc_mode_t pm)
263 {
264 uint8_t regval = IIS2DLPC_CONT_LOW_PWR_12bit;
265
266 switch (pm) {
267 case IIS2DLPC_CONT_LOW_PWR_2:
268 case IIS2DLPC_CONT_LOW_PWR_3:
269 case IIS2DLPC_CONT_LOW_PWR_4:
270 case IIS2DLPC_HIGH_PERFORMANCE:
271 regval = pm;
272 break;
273 default:
274 LOG_DBG("Apply default Power Mode");
275 break;
276 }
277
278 return iis2dlpc_write_reg(ctx, IIS2DLPC_CTRL1, ®val, 1);
279 }
280
iis2dlpc_init(const struct device * dev)281 static int iis2dlpc_init(const struct device *dev)
282 {
283 struct iis2dlpc_data *iis2dlpc = dev->data;
284 const struct iis2dlpc_config *cfg = dev->config;
285 stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
286 uint8_t wai;
287
288 iis2dlpc->dev = dev;
289
290 /* check chip ID */
291 if (iis2dlpc_device_id_get(ctx, &wai) < 0) {
292 return -EIO;
293 }
294
295 if (wai != IIS2DLPC_ID) {
296 LOG_ERR("Invalid chip ID");
297 return -EINVAL;
298 }
299
300 /* reset device */
301 if (iis2dlpc_reset_set(ctx, PROPERTY_ENABLE) < 0) {
302 return -EIO;
303 }
304
305 k_busy_wait(100);
306
307 if (iis2dlpc_block_data_update_set(ctx, PROPERTY_ENABLE) < 0) {
308 return -EIO;
309 }
310
311 /* set power mode */
312 LOG_INF("power-mode is %d", cfg->pm);
313 if (iis2dlpc_set_power_mode(ctx, cfg->pm)) {
314 return -EIO;
315 }
316
317 /* set default odr to 12.5Hz acc */
318 if (iis2dlpc_set_odr(dev, 12) < 0) {
319 LOG_ERR("odr init error (12.5 Hz)");
320 return -EIO;
321 }
322
323 LOG_INF("range is %d", cfg->range);
324 if (iis2dlpc_set_range(dev, IIS2DLPC_FS_TO_REG(cfg->range)) < 0) {
325 LOG_ERR("range init error %d", cfg->range);
326 return -EIO;
327 }
328
329 #ifdef CONFIG_IIS2DLPC_TRIGGER
330 if (iis2dlpc_init_interrupt(dev) < 0) {
331 LOG_ERR("Failed to initialize interrupts");
332 return -EIO;
333 }
334
335 #ifdef CONFIG_IIS2DLPC_TAP
336 LOG_INF("TAP: tap mode is %d", cfg->tap_mode);
337 if (iis2dlpc_tap_mode_set(ctx, cfg->tap_mode) < 0) {
338 LOG_ERR("Failed to select tap trigger mode");
339 return -EIO;
340 }
341
342 LOG_INF("TAP: ths_x is %02x", cfg->tap_threshold[0]);
343 if (iis2dlpc_tap_threshold_x_set(ctx, cfg->tap_threshold[0]) < 0) {
344 LOG_ERR("Failed to set tap X axis threshold");
345 return -EIO;
346 }
347
348 LOG_INF("TAP: ths_y is %02x", cfg->tap_threshold[1]);
349 if (iis2dlpc_tap_threshold_y_set(ctx, cfg->tap_threshold[1]) < 0) {
350 LOG_ERR("Failed to set tap Y axis threshold");
351 return -EIO;
352 }
353
354 LOG_INF("TAP: ths_z is %02x", cfg->tap_threshold[2]);
355 if (iis2dlpc_tap_threshold_z_set(ctx, cfg->tap_threshold[2]) < 0) {
356 LOG_ERR("Failed to set tap Z axis threshold");
357 return -EIO;
358 }
359
360 if (cfg->tap_threshold[0] > 0) {
361 LOG_INF("TAP: tap_x enabled");
362 if (iis2dlpc_tap_detection_on_x_set(ctx, 1) < 0) {
363 LOG_ERR("Failed to set tap detection on X axis");
364 return -EIO;
365 }
366 }
367
368 if (cfg->tap_threshold[1] > 0) {
369 LOG_INF("TAP: tap_y enabled");
370 if (iis2dlpc_tap_detection_on_y_set(ctx, 1) < 0) {
371 LOG_ERR("Failed to set tap detection on Y axis");
372 return -EIO;
373 }
374 }
375
376 if (cfg->tap_threshold[2] > 0) {
377 LOG_INF("TAP: tap_z enabled");
378 if (iis2dlpc_tap_detection_on_z_set(ctx, 1) < 0) {
379 LOG_ERR("Failed to set tap detection on Z axis");
380 return -EIO;
381 }
382 }
383
384 LOG_INF("TAP: shock is %02x", cfg->tap_shock);
385 if (iis2dlpc_tap_shock_set(ctx, cfg->tap_shock) < 0) {
386 LOG_ERR("Failed to set tap shock duration");
387 return -EIO;
388 }
389
390 LOG_INF("TAP: latency is %02x", cfg->tap_latency);
391 if (iis2dlpc_tap_dur_set(ctx, cfg->tap_latency) < 0) {
392 LOG_ERR("Failed to set tap latency");
393 return -EIO;
394 }
395
396 LOG_INF("TAP: quiet time is %02x", cfg->tap_quiet);
397 if (iis2dlpc_tap_quiet_set(ctx, cfg->tap_quiet) < 0) {
398 LOG_ERR("Failed to set tap quiet time");
399 return -EIO;
400 }
401 #endif /* CONFIG_IIS2DLPC_TAP */
402 #endif /* CONFIG_IIS2DLPC_TRIGGER */
403
404 return 0;
405 }
406
407 #if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 0
408 #warning "IIS2DLPC driver enabled without any devices"
409 #endif
410
411 /*
412 * Device creation macro, shared by IIS2DLPC_DEFINE_SPI() and
413 * IIS2DLPC_DEFINE_I2C().
414 */
415
416 #define IIS2DLPC_DEVICE_INIT(inst) \
417 SENSOR_DEVICE_DT_INST_DEFINE(inst, \
418 iis2dlpc_init, \
419 NULL, \
420 &iis2dlpc_data_##inst, \
421 &iis2dlpc_config_##inst, \
422 POST_KERNEL, \
423 CONFIG_SENSOR_INIT_PRIORITY, \
424 &iis2dlpc_driver_api);
425
426 /*
427 * Instantiation macros used when a device is on a SPI bus.
428 */
429
430 #ifdef CONFIG_IIS2DLPC_TAP
431 #define IIS2DLPC_CONFIG_TAP(inst) \
432 .tap_mode = DT_INST_PROP(inst, tap_mode), \
433 .tap_threshold = DT_INST_PROP(inst, tap_threshold), \
434 .tap_shock = DT_INST_PROP(inst, tap_shock), \
435 .tap_latency = DT_INST_PROP(inst, tap_latency), \
436 .tap_quiet = DT_INST_PROP(inst, tap_quiet),
437 #else
438 #define IIS2DLPC_CONFIG_TAP(inst)
439 #endif /* CONFIG_IIS2DLPC_TAP */
440
441 #ifdef CONFIG_IIS2DLPC_TRIGGER
442 #define IIS2DLPC_CFG_IRQ(inst) \
443 .gpio_drdy = GPIO_DT_SPEC_INST_GET(inst, drdy_gpios), \
444 .drdy_int = DT_INST_PROP(inst, drdy_int),
445 #else
446 #define IIS2DLPC_CFG_IRQ(inst)
447 #endif /* CONFIG_IIS2DLPC_TRIGGER */
448
449 #define IIS2DLPC_CONFIG_COMMON(inst) \
450 .pm = DT_INST_PROP(inst, power_mode), \
451 .range = DT_INST_PROP(inst, range), \
452 IIS2DLPC_CONFIG_TAP(inst) \
453 COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, drdy_gpios), \
454 (IIS2DLPC_CFG_IRQ(inst)), ())
455
456 #define IIS2DLPC_SPI_OPERATION (SPI_WORD_SET(8) | \
457 SPI_OP_MODE_MASTER | \
458 SPI_MODE_CPOL | \
459 SPI_MODE_CPHA) \
460
461 #define IIS2DLPC_CONFIG_SPI(inst) \
462 { \
463 STMEMSC_CTX_SPI(&iis2dlpc_config_##inst.stmemsc_cfg), \
464 .stmemsc_cfg = { \
465 .spi = SPI_DT_SPEC_INST_GET(inst, \
466 IIS2DLPC_SPI_OPERATION, \
467 0), \
468 }, \
469 IIS2DLPC_CONFIG_COMMON(inst) \
470 }
471
472 /*
473 * Instantiation macros used when a device is on an I2C bus.
474 */
475
476 #define IIS2DLPC_CONFIG_I2C(inst) \
477 { \
478 STMEMSC_CTX_I2C(&iis2dlpc_config_##inst.stmemsc_cfg), \
479 .stmemsc_cfg = { \
480 .i2c = I2C_DT_SPEC_INST_GET(inst), \
481 }, \
482 IIS2DLPC_CONFIG_COMMON(inst) \
483 }
484
485 /*
486 * Main instantiation macro. Use of COND_CODE_1() selects the right
487 * bus-specific macro at preprocessor time.
488 */
489
490 #define IIS2DLPC_DEFINE(inst) \
491 static struct iis2dlpc_data iis2dlpc_data_##inst; \
492 static const struct iis2dlpc_config iis2dlpc_config_##inst = \
493 COND_CODE_1(DT_INST_ON_BUS(inst, spi), \
494 (IIS2DLPC_CONFIG_SPI(inst)), \
495 (IIS2DLPC_CONFIG_I2C(inst))); \
496 IIS2DLPC_DEVICE_INIT(inst)
497
498 DT_INST_FOREACH_STATUS_OKAY(IIS2DLPC_DEFINE)
499