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