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, &regval, 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