1 /* ST Microelectronics LSM6DSO 6-axis IMU sensor driver
2  *
3  * Copyright (c) 2019 STMicroelectronics
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  *
7  * Datasheet:
8  * https://www.st.com/resource/en/datasheet/lsm6dso.pdf
9  */
10 
11 #define DT_DRV_COMPAT st_lsm6dso
12 
13 #include <zephyr/device.h>
14 #include <zephyr/drivers/i2c.h>
15 #include <zephyr/sys/byteorder.h>
16 #include <zephyr/sys/__assert.h>
17 #include <zephyr/sys/util.h>
18 #include <zephyr/kernel.h>
19 #include <zephyr/drivers/sensor.h>
20 #include <zephyr/logging/log.h>
21 
22 #include "lsm6dso.h"
23 
24 LOG_MODULE_DECLARE(LSM6DSO, CONFIG_SENSOR_LOG_LEVEL);
25 
26 static int lsm6dso_shub_write_target_reg(const struct device *dev,
27 					 uint8_t trgt_addr, uint8_t trgt_reg,
28 					 uint8_t *value, uint16_t len);
29 static int lsm6dso_shub_read_target_reg(const struct device *dev,
30 					uint8_t trgt_addr, uint8_t trgt_reg,
31 					uint8_t *value, uint16_t len);
32 static void lsm6dso_shub_enable(const struct device *dev, uint8_t enable);
33 
34 
35 /* ST HAL skips this register, only supports it via the slower lsm6dso_sh_status_get() */
lsm6dso_sh_status_mainpage_get(stmdev_ctx_t * ctx,lsm6dso_status_master_t * val)36 static int32_t lsm6dso_sh_status_mainpage_get(stmdev_ctx_t *ctx, lsm6dso_status_master_t *val)
37 {
38 	return lsm6dso_read_reg(ctx, LSM6DSO_STATUS_MASTER_MAINPAGE, (uint8_t *)val, 1);
39 }
40 
41 /*
42  * LIS2MDL magn device specific part
43  */
44 #ifdef CONFIG_LSM6DSO_EXT_LIS2MDL
45 
46 #define LIS2MDL_CFG_REG_A		0x60
47 #define LIS2MDL_CFG_REG_B		0x61
48 #define LIS2MDL_CFG_REG_C		0x62
49 #define LIS2MDL_STATUS_REG		0x67
50 
51 #define LIS2MDL_SW_RESET		0x20
52 #define LIS2MDL_ODR_10HZ		0x00
53 #define LIS2MDL_ODR_100HZ		0x0C
54 #define LIS2MDL_OFF_CANC		0x02
55 #define LIS2MDL_SENSITIVITY		1500
56 
lsm6dso_lis2mdl_init(const struct device * dev,uint8_t i2c_addr)57 static int lsm6dso_lis2mdl_init(const struct device *dev, uint8_t i2c_addr)
58 {
59 	struct lsm6dso_data *data = dev->data;
60 	uint8_t mag_cfg[2];
61 
62 	data->magn_gain = LIS2MDL_SENSITIVITY;
63 
64 	/* sw reset device */
65 	mag_cfg[0] = LIS2MDL_SW_RESET;
66 	lsm6dso_shub_write_target_reg(dev, i2c_addr,
67 				      LIS2MDL_CFG_REG_A, mag_cfg, 1);
68 
69 	k_sleep(K_MSEC(10)); /* turn-on time in ms */
70 
71 	/* configure mag */
72 	mag_cfg[0] = LIS2MDL_ODR_10HZ;
73 	mag_cfg[1] = LIS2MDL_OFF_CANC;
74 	lsm6dso_shub_write_target_reg(dev, i2c_addr,
75 				      LIS2MDL_CFG_REG_A, mag_cfg, 2);
76 
77 	return 0;
78 }
79 
80 static const uint16_t lis2mdl_map[] = {10, 20, 50, 100};
81 
lsm6dso_lis2mdl_odr_set(const struct device * dev,uint8_t i2c_addr,uint16_t freq)82 static int lsm6dso_lis2mdl_odr_set(const struct device *dev,
83 				   uint8_t i2c_addr, uint16_t freq)
84 {
85 	uint8_t odr, cfg;
86 
87 	for (odr = 0; odr < ARRAY_SIZE(lis2mdl_map); odr++) {
88 		if (freq == lis2mdl_map[odr]) {
89 			break;
90 		}
91 	}
92 
93 	if (odr == ARRAY_SIZE(lis2mdl_map)) {
94 		LOG_DBG("shub: LIS2MDL freq val %d not supported.", freq);
95 		return -ENOTSUP;
96 	}
97 
98 	cfg = (odr << 2);
99 	lsm6dso_shub_write_target_reg(dev, i2c_addr,
100 				      LIS2MDL_CFG_REG_A, &cfg, 1);
101 
102 	lsm6dso_shub_enable(dev, 1);
103 	return 0;
104 }
105 
lsm6dso_lis2mdl_conf(const struct device * dev,uint8_t i2c_addr,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)106 static int lsm6dso_lis2mdl_conf(const struct device *dev, uint8_t i2c_addr,
107 				enum sensor_channel chan,
108 				enum sensor_attribute attr,
109 				const struct sensor_value *val)
110 {
111 	switch (attr) {
112 	case SENSOR_ATTR_SAMPLING_FREQUENCY:
113 		return lsm6dso_lis2mdl_odr_set(dev, i2c_addr, val->val1);
114 	default:
115 		LOG_DBG("shub: LIS2MDL attribute not supported.");
116 		return -ENOTSUP;
117 	}
118 
119 	return 0;
120 }
121 #endif /* CONFIG_LSM6DSO_EXT_LIS2MDL */
122 
123 /*
124  * HTS221 humidity device specific part
125  */
126 #ifdef CONFIG_LSM6DSO_EXT_HTS221
127 
128 #define HTS221_AUTOINCREMENT		BIT(7)
129 
130 #define HTS221_REG_CTRL1		0x20
131 #define HTS221_ODR_1HZ			0x01
132 #define HTS221_BDU			0x04
133 #define HTS221_PD			0x80
134 
135 #define HTS221_REG_CONV_START		0x30
136 
lsm6dso_hts221_read_conv_data(const struct device * dev,uint8_t i2c_addr)137 static int lsm6dso_hts221_read_conv_data(const struct device *dev,
138 					uint8_t i2c_addr)
139 {
140 	struct lsm6dso_data *data = dev->data;
141 	uint8_t buf[16], i;
142 	struct hts221_data *ht = &data->hts221;
143 
144 	for (i = 0; i < sizeof(buf); i += 7) {
145 		unsigned char len = MIN(7, sizeof(buf) - i);
146 
147 		if (lsm6dso_shub_read_target_reg(dev, i2c_addr,
148 						 (HTS221_REG_CONV_START + i) |
149 						 HTS221_AUTOINCREMENT,
150 						 &buf[i], len) < 0) {
151 			LOG_DBG("shub: failed to read hts221 conv data");
152 			return -EIO;
153 		}
154 	}
155 
156 	ht->y0 = buf[0] / 2;
157 	ht->y1 = buf[1] / 2;
158 	ht->x0 = sys_le16_to_cpu(buf[6] | (buf[7] << 8));
159 	ht->x1 = sys_le16_to_cpu(buf[10] | (buf[11] << 8));
160 
161 	return 0;
162 }
163 
lsm6dso_hts221_init(const struct device * dev,uint8_t i2c_addr)164 static int lsm6dso_hts221_init(const struct device *dev, uint8_t i2c_addr)
165 {
166 	uint8_t hum_cfg;
167 
168 	/* configure ODR and BDU */
169 	hum_cfg = HTS221_ODR_1HZ | HTS221_BDU | HTS221_PD;
170 	lsm6dso_shub_write_target_reg(dev, i2c_addr,
171 				      HTS221_REG_CTRL1, &hum_cfg, 1);
172 
173 	return lsm6dso_hts221_read_conv_data(dev, i2c_addr);
174 }
175 
176 static const uint16_t hts221_map[] = {0, 1, 7, 12};
177 
lsm6dso_hts221_odr_set(const struct device * dev,uint8_t i2c_addr,uint16_t freq)178 static int lsm6dso_hts221_odr_set(const struct device *dev,
179 				   uint8_t i2c_addr, uint16_t freq)
180 {
181 	uint8_t odr, cfg;
182 
183 	for (odr = 0; odr < ARRAY_SIZE(hts221_map); odr++) {
184 		if (freq == hts221_map[odr]) {
185 			break;
186 		}
187 	}
188 
189 	if (odr == ARRAY_SIZE(hts221_map)) {
190 		LOG_DBG("shub: HTS221 freq val %d not supported.", freq);
191 		return -ENOTSUP;
192 	}
193 
194 	cfg = odr | HTS221_BDU | HTS221_PD;
195 	lsm6dso_shub_write_target_reg(dev, i2c_addr,
196 				      HTS221_REG_CTRL1, &cfg, 1);
197 
198 	lsm6dso_shub_enable(dev, 1);
199 	return 0;
200 }
201 
lsm6dso_hts221_conf(const struct device * dev,uint8_t i2c_addr,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)202 static int lsm6dso_hts221_conf(const struct device *dev, uint8_t i2c_addr,
203 				enum sensor_channel chan,
204 				enum sensor_attribute attr,
205 				const struct sensor_value *val)
206 {
207 	switch (attr) {
208 	case SENSOR_ATTR_SAMPLING_FREQUENCY:
209 		return lsm6dso_hts221_odr_set(dev, i2c_addr, val->val1);
210 	default:
211 		LOG_DBG("shub: HTS221 attribute not supported.");
212 		return -ENOTSUP;
213 	}
214 
215 	return 0;
216 }
217 #endif /* CONFIG_LSM6DSO_EXT_HTS221 */
218 
219 /*
220  * LPS22HB baro/temp device specific part
221  */
222 #ifdef CONFIG_LSM6DSO_EXT_LPS22HB
223 
224 #define LPS22HB_CTRL_REG1		0x10
225 #define LPS22HB_CTRL_REG2		0x11
226 
227 #define LPS22HB_SW_RESET		0x04
228 #define LPS22HB_ODR_10HZ		0x20
229 #define LPS22HB_LPF_EN			0x08
230 #define LPS22HB_BDU_EN			0x02
231 
lsm6dso_lps22hb_init(const struct device * dev,uint8_t i2c_addr)232 static int lsm6dso_lps22hb_init(const struct device *dev, uint8_t i2c_addr)
233 {
234 	uint8_t baro_cfg[2];
235 
236 	/* sw reset device */
237 	baro_cfg[0] = LPS22HB_SW_RESET;
238 	lsm6dso_shub_write_target_reg(dev, i2c_addr,
239 				      LPS22HB_CTRL_REG2, baro_cfg, 1);
240 
241 	k_sleep(K_MSEC(1)); /* turn-on time in ms */
242 
243 	/* configure device */
244 	baro_cfg[0] = LPS22HB_ODR_10HZ | LPS22HB_LPF_EN | LPS22HB_BDU_EN;
245 	lsm6dso_shub_write_target_reg(dev, i2c_addr,
246 				      LPS22HB_CTRL_REG1, baro_cfg, 1);
247 
248 	return 0;
249 }
250 #endif /* CONFIG_LSM6DSO_EXT_LPS22HB */
251 
252 /*
253  * LPS22HH baro/temp device specific part
254  */
255 #ifdef CONFIG_LSM6DSO_EXT_LPS22HH
256 
257 #define LPS22HH_CTRL_REG1		0x10
258 #define LPS22HH_CTRL_REG2		0x11
259 
260 #define LPS22HH_SW_RESET		0x04
261 #define LPS22HH_IF_ADD_INC		0x10
262 #define LPS22HH_ODR_10HZ		0x20
263 #define LPS22HH_LPF_EN			0x08
264 #define LPS22HH_BDU_EN			0x02
265 
lsm6dso_lps22hh_init(const struct device * dev,uint8_t i2c_addr)266 static int lsm6dso_lps22hh_init(const struct device *dev, uint8_t i2c_addr)
267 {
268 	uint8_t baro_cfg[2];
269 
270 	/* sw reset device */
271 	baro_cfg[0] = LPS22HH_SW_RESET;
272 	lsm6dso_shub_write_target_reg(dev, i2c_addr,
273 				      LPS22HH_CTRL_REG2, baro_cfg, 1);
274 
275 	k_sleep(K_MSEC(100)); /* turn-on time in ms */
276 
277 	/* configure device */
278 	baro_cfg[0] = LPS22HH_IF_ADD_INC;
279 	lsm6dso_shub_write_target_reg(dev, i2c_addr,
280 				      LPS22HH_CTRL_REG2, baro_cfg, 1);
281 
282 	baro_cfg[0] = LPS22HH_ODR_10HZ | LPS22HH_LPF_EN | LPS22HH_BDU_EN;
283 	lsm6dso_shub_write_target_reg(dev, i2c_addr,
284 				      LPS22HH_CTRL_REG1, baro_cfg, 1);
285 
286 	return 0;
287 }
288 
289 static const uint16_t lps22hh_map[] = {0, 1, 10, 25, 50, 75, 100, 200};
290 
lsm6dso_lps22hh_odr_set(const struct device * dev,uint8_t i2c_addr,uint16_t freq)291 static int lsm6dso_lps22hh_odr_set(const struct device *dev,
292 				   uint8_t i2c_addr, uint16_t freq)
293 {
294 	uint8_t odr, cfg;
295 
296 	for (odr = 0; odr < ARRAY_SIZE(lps22hh_map); odr++) {
297 		if (freq == lps22hh_map[odr]) {
298 			break;
299 		}
300 	}
301 
302 	if (odr == ARRAY_SIZE(lps22hh_map)) {
303 		LOG_DBG("shub: LPS22HH freq val %d not supported.", freq);
304 		return -ENOTSUP;
305 	}
306 
307 	cfg = (odr << 4) | LPS22HH_LPF_EN | LPS22HH_BDU_EN;
308 	lsm6dso_shub_write_target_reg(dev, i2c_addr,
309 				      LPS22HH_CTRL_REG1, &cfg, 1);
310 
311 	lsm6dso_shub_enable(dev, 1);
312 	return 0;
313 }
314 
lsm6dso_lps22hh_conf(const struct device * dev,uint8_t i2c_addr,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)315 static int lsm6dso_lps22hh_conf(const struct device *dev, uint8_t i2c_addr,
316 				enum sensor_channel chan,
317 				enum sensor_attribute attr,
318 				const struct sensor_value *val)
319 {
320 	switch (attr) {
321 	case SENSOR_ATTR_SAMPLING_FREQUENCY:
322 		return lsm6dso_lps22hh_odr_set(dev, i2c_addr, val->val1);
323 	default:
324 		LOG_DBG("shub: LPS22HH attribute not supported.");
325 		return -ENOTSUP;
326 	}
327 
328 	return 0;
329 }
330 #endif /* CONFIG_LSM6DSO_EXT_LPS22HH */
331 
332 /* List of supported external sensors */
333 static struct lsm6dso_shub_slist {
334 	enum sensor_channel type;
335 	uint8_t i2c_addr[2];
336 	uint8_t ext_i2c_addr;
337 	uint8_t wai_addr;
338 	uint8_t wai_val;
339 	uint8_t out_data_addr;
340 	uint8_t out_data_len;
341 	uint8_t sh_out_reg;
342 	int (*dev_init)(const struct device *dev, uint8_t i2c_addr);
343 	int (*dev_conf)(const struct device *dev, uint8_t i2c_addr,
344 			enum sensor_channel chan, enum sensor_attribute attr,
345 			const struct sensor_value *val);
346 } lsm6dso_shub_slist[] = {
347 #ifdef CONFIG_LSM6DSO_EXT_LIS2MDL
348 	{
349 		/* LIS2MDL */
350 		.type		= SENSOR_CHAN_MAGN_XYZ,
351 		.i2c_addr	= { 0x1E },
352 		.wai_addr	= 0x4F,
353 		.wai_val	= 0x40,
354 		.out_data_addr  = 0x68,
355 		.out_data_len   = 0x06,
356 		.dev_init	= (lsm6dso_lis2mdl_init),
357 		.dev_conf	= (lsm6dso_lis2mdl_conf),
358 	},
359 #endif /* CONFIG_LSM6DSO_EXT_LIS2MDL */
360 
361 #ifdef CONFIG_LSM6DSO_EXT_HTS221
362 	{
363 		/* HTS221 */
364 		.type		= SENSOR_CHAN_HUMIDITY,
365 		.i2c_addr	= { 0x5F },
366 		.wai_addr	= 0x0F,
367 		.wai_val	= 0xBC,
368 		.out_data_addr  = 0x28 | HTS221_AUTOINCREMENT,
369 		.out_data_len   = 0x02,
370 		.dev_init	= (lsm6dso_hts221_init),
371 		.dev_conf	= (lsm6dso_hts221_conf),
372 	},
373 #endif /* CONFIG_LSM6DSO_EXT_HTS221 */
374 
375 #ifdef CONFIG_LSM6DSO_EXT_LPS22HB
376 	{
377 		/* LPS22HB */
378 		.type		= SENSOR_CHAN_PRESS,
379 		.i2c_addr	= { 0x5C, 0x5D },
380 		.wai_addr	= 0x0F,
381 		.wai_val	= 0xB1,
382 		.out_data_addr  = 0x28,
383 		.out_data_len   = 0x05,
384 		.dev_init	= (lsm6dso_lps22hb_init),
385 	},
386 #endif /* CONFIG_LSM6DSO_EXT_LPS22HB */
387 
388 #ifdef CONFIG_LSM6DSO_EXT_LPS22HH
389 	{
390 		/* LPS22HH */
391 		.type		= SENSOR_CHAN_PRESS,
392 		.i2c_addr	= { 0x5C, 0x5D },
393 		.wai_addr	= 0x0F,
394 		.wai_val	= 0xB3,
395 		.out_data_addr  = 0x28,
396 		.out_data_len   = 0x05,
397 		.dev_init	= (lsm6dso_lps22hh_init),
398 		.dev_conf	= (lsm6dso_lps22hh_conf),
399 	},
400 #endif /* CONFIG_LSM6DSO_EXT_LPS22HH */
401 };
402 
lsm6dso_shub_wait_completed(stmdev_ctx_t * ctx)403 static int lsm6dso_shub_wait_completed(stmdev_ctx_t *ctx)
404 {
405 	lsm6dso_status_master_t status;
406 	int tries = 200; /* Should be max ~160 ms, from 2 cycles at slowest ODR 12.5 Hz */
407 
408 	do {
409 		if (!--tries) {
410 			LOG_DBG("shub: Timeout waiting for operation to complete");
411 			return -ETIMEDOUT;
412 		}
413 		k_msleep(1);
414 		lsm6dso_sh_status_mainpage_get(ctx, &status);
415 	} while (status.sens_hub_endop == 0);
416 
417 	return 1;
418 }
419 
lsm6dso_shub_enable(const struct device * dev,uint8_t enable)420 static void lsm6dso_shub_enable(const struct device *dev, uint8_t enable)
421 {
422 	const struct lsm6dso_config *cfg = dev->config;
423 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
424 	struct lsm6dso_data *data = dev->data;
425 
426 	/* Enable Accel @26hz */
427 	if (!data->accel_freq) {
428 		uint8_t odr = (enable) ? 2 : 0;
429 
430 		if (lsm6dso_xl_data_rate_set(ctx, odr) < 0) {
431 			LOG_DBG("shub: failed to set XL sampling rate");
432 			return;
433 		}
434 	}
435 
436 	if (enable) {
437 		lsm6dso_status_master_t status;
438 
439 		/* Clear any pending status flags */
440 		lsm6dso_sh_status_mainpage_get(ctx, &status);
441 	}
442 
443 	if (lsm6dso_sh_master_set(ctx, enable) < 0) {
444 		LOG_DBG("shub: failed to set master on");
445 		lsm6dso_mem_bank_set(ctx, LSM6DSO_USER_BANK);
446 		return;
447 	}
448 
449 	if (!enable) {
450 		/* wait 300us (necessary per AN5192 §7.2.1) */
451 		k_busy_wait(300);
452 	}
453 }
454 
455 /* must be called with master on */
lsm6dso_shub_check_slv0_nack(stmdev_ctx_t * ctx)456 static int lsm6dso_shub_check_slv0_nack(stmdev_ctx_t *ctx)
457 {
458 	lsm6dso_status_master_t status;
459 
460 	if (lsm6dso_sh_status_get(ctx, &status) < 0) {
461 		LOG_DBG("shub: error reading embedded reg");
462 		return -EIO;
463 	}
464 
465 	if (status.slave0_nack) {
466 		LOG_DBG("shub: TRGT 0 nacked");
467 		return -EIO;
468 	}
469 
470 	return 0;
471 }
472 
473 /*
474  * use TRGT 0 for generic read to target device
475  */
lsm6dso_shub_read_target_reg(const struct device * dev,uint8_t trgt_addr,uint8_t trgt_reg,uint8_t * value,uint16_t len)476 static int lsm6dso_shub_read_target_reg(const struct device *dev,
477 					uint8_t trgt_addr, uint8_t trgt_reg,
478 					uint8_t *value, uint16_t len)
479 {
480 	const struct lsm6dso_config *cfg = dev->config;
481 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
482 	lsm6dso_sh_cfg_read_t trgt_cfg;
483 
484 	trgt_cfg.slv_add = trgt_addr;
485 	trgt_cfg.slv_subadd = trgt_reg;
486 	trgt_cfg.slv_len = len;
487 
488 	lsm6dso_sh_slv_cfg_read(ctx, 0, &trgt_cfg);
489 
490 	/* turn SH on, wait for shub i2c read to finish */
491 	lsm6dso_shub_enable(dev, 1);
492 	lsm6dso_shub_wait_completed(ctx);
493 
494 	/* read data from external target */
495 	if (lsm6dso_sh_read_data_raw_get(ctx, value, len) < 0) {
496 		LOG_DBG("shub: error reading sensor data");
497 		return -EIO;
498 	}
499 
500 	if (lsm6dso_shub_check_slv0_nack(ctx) < 0) {
501 		lsm6dso_shub_enable(dev, 0);
502 		return -EIO;
503 	}
504 
505 	lsm6dso_shub_enable(dev, 0);
506 	return 0;
507 }
508 
509 /*
510  * use TRGT 0 to configure target device
511  */
lsm6dso_shub_write_target_reg(const struct device * dev,uint8_t trgt_addr,uint8_t trgt_reg,uint8_t * value,uint16_t len)512 static int lsm6dso_shub_write_target_reg(const struct device *dev,
513 					 uint8_t trgt_addr, uint8_t trgt_reg,
514 					 uint8_t *value, uint16_t len)
515 {
516 	const struct lsm6dso_config *cfg = dev->config;
517 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
518 	lsm6dso_sh_cfg_write_t trgt_cfg;
519 	uint8_t cnt = 0U;
520 
521 	lsm6dso_shub_enable(dev, 0);
522 
523 	while (cnt < len) {
524 		trgt_cfg.slv0_add = trgt_addr;
525 		trgt_cfg.slv0_subadd = trgt_reg + cnt;
526 		trgt_cfg.slv0_data = value[cnt];
527 
528 		lsm6dso_sh_cfg_write(ctx, &trgt_cfg);
529 
530 		/* turn SH on, wait for shub i2c write to finish */
531 		lsm6dso_shub_enable(dev, 1);
532 		lsm6dso_shub_wait_completed(ctx);
533 
534 		if (lsm6dso_shub_check_slv0_nack(ctx) < 0) {
535 			lsm6dso_shub_enable(dev, 0);
536 			return -EIO;
537 		}
538 
539 		lsm6dso_shub_enable(dev, 0);
540 
541 		cnt++;
542 	}
543 
544 	/* Put TRGT 0 in IDLE mode */
545 	trgt_cfg.slv0_add = 0x7;
546 	trgt_cfg.slv0_subadd = 0x0;
547 	trgt_cfg.slv0_data = 0x0;
548 	lsm6dso_sh_cfg_write(ctx, &trgt_cfg);
549 
550 	return 0;
551 }
552 
553 /*
554  * TARGETs configurations:
555  *
556  *  - TARGET 0: used for configuring all target devices
557  *  - TARGET 1: used as data read channel for external target device #1
558  *  - TARGET 2: used as data read channel for external target device #2
559  *  - TARGET 3: used for generic reads while data channel is enabled
560  */
lsm6dso_shub_set_data_channel(const struct device * dev)561 static int lsm6dso_shub_set_data_channel(const struct device *dev)
562 {
563 	struct lsm6dso_data *data = dev->data;
564 	const struct lsm6dso_config *cfg = dev->config;
565 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
566 	uint8_t n;
567 	struct lsm6dso_shub_slist *sp;
568 	lsm6dso_sh_cfg_read_t trgt_cfg;
569 
570 	/* Configure shub data channels to access external targets */
571 	for (n = 0; n < data->num_ext_dev; n++) {
572 		sp = &lsm6dso_shub_slist[data->shub_ext[n]];
573 
574 		trgt_cfg.slv_add = sp->ext_i2c_addr;
575 		trgt_cfg.slv_subadd = sp->out_data_addr;
576 		trgt_cfg.slv_len = sp->out_data_len;
577 
578 		if (lsm6dso_sh_slv_cfg_read(ctx, n + 1, &trgt_cfg) < 0) {
579 			LOG_DBG("shub: error configuring shub for ext targets");
580 			return -EIO;
581 		}
582 	}
583 
584 	/* Configure the master */
585 	lsm6dso_aux_sens_on_t aux = LSM6DSO_SLV_0_1_2;
586 
587 	if (lsm6dso_sh_slave_connected_set(ctx, aux) < 0) {
588 		LOG_DBG("shub: error setting aux sensors");
589 		return -EIO;
590 	}
591 
592 
593 	/* turn SH on, no need to wait for 1st shub i2c read, if any, to complete */
594 	lsm6dso_shub_enable(dev, 1);
595 
596 	return 0;
597 }
598 
lsm6dso_shub_get_idx(const struct device * dev,enum sensor_channel type)599 int lsm6dso_shub_get_idx(const struct device *dev, enum sensor_channel type)
600 {
601 	uint8_t n;
602 	struct lsm6dso_data *data = dev->data;
603 	struct lsm6dso_shub_slist *sp;
604 
605 	for (n = 0; n < data->num_ext_dev; n++) {
606 		sp = &lsm6dso_shub_slist[data->shub_ext[n]];
607 
608 		if (sp->type == type) {
609 			return n;
610 		}
611 	}
612 
613 	LOG_ERR("shub: dev %s type %d not supported", dev->name, type);
614 	return -ENOTSUP;
615 }
616 
lsm6dso_shub_fetch_external_devs(const struct device * dev)617 int lsm6dso_shub_fetch_external_devs(const struct device *dev)
618 {
619 	uint8_t n;
620 	const struct lsm6dso_config *cfg = dev->config;
621 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
622 	struct lsm6dso_data *data = dev->data;
623 	struct lsm6dso_shub_slist *sp;
624 
625 	/* read data from external target */
626 	if (lsm6dso_mem_bank_set(ctx, LSM6DSO_SENSOR_HUB_BANK) < 0) {
627 		LOG_DBG("failed to enter SENSOR_HUB bank");
628 		return -EIO;
629 	}
630 
631 	for (n = 0; n < data->num_ext_dev; n++) {
632 		sp = &lsm6dso_shub_slist[data->shub_ext[n]];
633 
634 		if (lsm6dso_read_reg(ctx, sp->sh_out_reg,
635 				     data->ext_data[n], sp->out_data_len) < 0) {
636 			LOG_DBG("shub: failed to read sample");
637 			(void) lsm6dso_mem_bank_set(ctx, LSM6DSO_USER_BANK);
638 			return -EIO;
639 		}
640 	}
641 
642 	return lsm6dso_mem_bank_set(ctx, LSM6DSO_USER_BANK);
643 }
644 
lsm6dso_shub_config(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)645 int lsm6dso_shub_config(const struct device *dev, enum sensor_channel chan,
646 			enum sensor_attribute attr,
647 			const struct sensor_value *val)
648 {
649 	struct lsm6dso_data *data = dev->data;
650 	struct lsm6dso_shub_slist *sp = NULL;
651 	uint8_t n;
652 
653 	for (n = 0; n < data->num_ext_dev; n++) {
654 		sp = &lsm6dso_shub_slist[data->shub_ext[n]];
655 
656 		if (sp->type == chan) {
657 			break;
658 		}
659 	}
660 
661 	if (n == data->num_ext_dev) {
662 		LOG_DBG("shub: %s chan %d not supported", dev->name, chan);
663 		return -ENOTSUP;
664 	}
665 
666 	if (sp == NULL || sp->dev_conf == NULL) {
667 		LOG_DBG("shub: chan not configurable");
668 		return -ENOTSUP;
669 	}
670 
671 	return sp->dev_conf(dev, sp->ext_i2c_addr, chan, attr, val);
672 }
673 
lsm6dso_shub_init(const struct device * dev)674 int lsm6dso_shub_init(const struct device *dev)
675 {
676 	struct lsm6dso_data *data = dev->data;
677 	const struct lsm6dso_config *cfg = dev->config;
678 	stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
679 	uint8_t i, n = 0, regn;
680 	uint8_t chip_id;
681 	struct lsm6dso_shub_slist *sp;
682 
683 	LOG_INF("shub: start sensorhub for %s", dev->name);
684 
685 	/* This must be set or lsm6dso_shub_write_target_reg() will repeatedly write the same reg */
686 	if (lsm6dso_sh_write_mode_set(ctx, LSM6DSO_ONLY_FIRST_CYCLE) < 0) {
687 		LOG_DBG("shub: error setting write once");
688 		return -EIO;
689 	}
690 
691 	for (n = 0; n < ARRAY_SIZE(lsm6dso_shub_slist); n++) {
692 		if (data->num_ext_dev >= LSM6DSO_SHUB_MAX_NUM_TARGETS) {
693 			break;
694 		}
695 
696 		chip_id = 0;
697 		sp = &lsm6dso_shub_slist[n];
698 
699 		/*
700 		 * The external sensor may have different I2C address.
701 		 * So, try them one by one until we read the correct
702 		 * chip ID.
703 		 */
704 		for (i = 0U; i < ARRAY_SIZE(sp->i2c_addr); i++) {
705 			if (lsm6dso_shub_read_target_reg(dev,
706 							sp->i2c_addr[i],
707 							sp->wai_addr,
708 							&chip_id, 1) < 0) {
709 				LOG_DBG("shub: failed reading chip id");
710 				continue;
711 			}
712 			if (chip_id == sp->wai_val) {
713 				break;
714 			}
715 		}
716 
717 		if (i >= ARRAY_SIZE(sp->i2c_addr)) {
718 			LOG_DBG("shub: invalid chip id 0x%x", chip_id);
719 			continue;
720 		}
721 		LOG_INF("shub: Ext Device Chip Id: %02x", chip_id);
722 		sp->ext_i2c_addr = sp->i2c_addr[i];
723 
724 		data->shub_ext[data->num_ext_dev++] = n;
725 	}
726 
727 	LOG_DBG("shub: dev %s - num_ext_dev %d", dev->name, data->num_ext_dev);
728 	if (data->num_ext_dev == 0) {
729 		LOG_ERR("shub: no target devices found");
730 		return -EINVAL;
731 	}
732 
733 	/* init external devices */
734 	for (n = 0, regn = 0; n < data->num_ext_dev; n++) {
735 		sp = &lsm6dso_shub_slist[data->shub_ext[n]];
736 		sp->sh_out_reg = LSM6DSO_SENSOR_HUB_1 + regn;
737 		regn += sp->out_data_len;
738 		sp->dev_init(dev, sp->ext_i2c_addr);
739 	}
740 
741 	lsm6dso_shub_set_data_channel(dev);
742 
743 	return 0;
744 }
745