1 /* ST Microelectronics ISM330DHCX 6-axis IMU sensor 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/ism330dhcx.pdf
9  */
10 
11 #define DT_DRV_COMPAT st_ism330dhcx
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 "ism330dhcx.h"
22 
23 LOG_MODULE_DECLARE(ISM330DHCX, CONFIG_SENSOR_LOG_LEVEL);
24 
25 #define ISM330DHCX_SHUB_DATA_OUT				0x02
26 
27 #define ISM330DHCX_SHUB_SLV0_ADDR				0x15
28 #define ISM330DHCX_SHUB_SLV0_SUBADDR			0x16
29 #define ISM330DHCX_SHUB_SLV0_CONFIG			0x17
30 #define ISM330DHCX_SHUB_SLV1_ADDR				0x18
31 #define ISM330DHCX_SHUB_SLV1_SUBADDR			0x19
32 #define ISM330DHCX_SHUB_SLV1_CONFIG			0x1A
33 #define ISM330DHCX_SHUB_SLV2_ADDR				0x1B
34 #define ISM330DHCX_SHUB_SLV2_SUBADDR			0x1C
35 #define ISM330DHCX_SHUB_SLV2_CONFIG			0x1D
36 #define ISM330DHCX_SHUB_SLV3_ADDR				0x1E
37 #define ISM330DHCX_SHUB_SLV3_SUBADDR			0x1F
38 #define ISM330DHCX_SHUB_SLV3_CONFIG			0x20
39 #define ISM330DHCX_SHUB_SLV0_DATAWRITE			0x21
40 
41 #define ISM330DHCX_SHUB_STATUS_MASTER			0x22
42 #define ISM330DHCX_SHUB_STATUS_SLV0_NACK			BIT(3)
43 #define ISM330DHCX_SHUB_STATUS_ENDOP			BIT(0)
44 
45 #define ISM330DHCX_SHUB_SLVX_WRITE				0x0
46 #define ISM330DHCX_SHUB_SLVX_READ				0x1
47 
48 static uint8_t num_ext_dev;
49 static uint8_t shub_ext[ISM330DHCX_SHUB_MAX_NUM_SLVS];
50 
51 static int ism330dhcx_shub_write_slave_reg(const struct device *dev, uint8_t slv_addr,
52 					   uint8_t slv_reg, uint8_t *value, uint16_t len);
53 static int ism330dhcx_shub_read_slave_reg(const struct device *dev, uint8_t slv_addr,
54 					  uint8_t slv_reg, uint8_t *value, uint16_t len);
55 static void ism330dhcx_shub_enable(const struct device *dev, uint8_t enable);
56 
57 /*
58  * LIS2MDL magn device specific part
59  */
60 #if defined(CONFIG_ISM330DHCX_EXT_LIS2MDL) || defined(CONFIG_ISM330DHCX_EXT_IIS2MDC)
61 
62 #define LIS2MDL_CFG_REG_A		0x60
63 #define LIS2MDL_CFG_REG_B		0x61
64 #define LIS2MDL_CFG_REG_C		0x62
65 #define LIS2MDL_STATUS_REG		0x67
66 
67 #define LIS2MDL_SW_RESET		0x20
68 #define LIS2MDL_ODR_10HZ		0x00
69 #define LIS2MDL_ODR_100HZ		0x0C
70 #define LIS2MDL_OFF_CANC		0x02
71 #define LIS2MDL_SENSITIVITY		1500
72 
ism330dhcx_lis2mdl_init(const struct device * dev,uint8_t i2c_addr)73 static int ism330dhcx_lis2mdl_init(const struct device *dev, uint8_t i2c_addr)
74 {
75 	struct ism330dhcx_data *data = dev->data;
76 	uint8_t mag_cfg[2];
77 
78 	data->magn_gain = LIS2MDL_SENSITIVITY;
79 
80 	/* sw reset device */
81 	mag_cfg[0] = LIS2MDL_SW_RESET;
82 	ism330dhcx_shub_write_slave_reg(dev, i2c_addr, LIS2MDL_CFG_REG_A, mag_cfg, 1);
83 
84 	k_sleep(K_MSEC(10)); /* turn-on time in ms */
85 
86 	/* configure mag */
87 	mag_cfg[0] = LIS2MDL_ODR_10HZ;
88 	mag_cfg[1] = LIS2MDL_OFF_CANC;
89 	ism330dhcx_shub_write_slave_reg(dev, i2c_addr, LIS2MDL_CFG_REG_A, mag_cfg, 2);
90 
91 	return 0;
92 }
93 
94 static const uint16_t lis2mdl_map[] = {10, 20, 50, 100};
95 
ism330dhcx_lis2mdl_odr_set(const struct device * dev,uint8_t i2c_addr,uint16_t freq)96 static int ism330dhcx_lis2mdl_odr_set(const struct device *dev, uint8_t i2c_addr, uint16_t freq)
97 {
98 	uint8_t odr, cfg;
99 
100 	for (odr = 0; odr < ARRAY_SIZE(lis2mdl_map); odr++) {
101 		if (freq == lis2mdl_map[odr]) {
102 			break;
103 		}
104 	}
105 
106 	if (odr == ARRAY_SIZE(lis2mdl_map)) {
107 		LOG_DBG("shub: LIS2MDL freq val %d not supported.", freq);
108 		return -ENOTSUP;
109 	}
110 
111 	cfg = (odr << 2);
112 	ism330dhcx_shub_write_slave_reg(dev, i2c_addr, LIS2MDL_CFG_REG_A, &cfg, 1);
113 
114 	ism330dhcx_shub_enable(dev, 1);
115 	return 0;
116 }
117 
ism330dhcx_lis2mdl_conf(const struct device * dev,uint8_t i2c_addr,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)118 static int ism330dhcx_lis2mdl_conf(const struct device *dev, uint8_t i2c_addr,
119 				   enum sensor_channel chan, enum sensor_attribute attr,
120 				   const struct sensor_value *val)
121 {
122 	switch (attr) {
123 	case SENSOR_ATTR_SAMPLING_FREQUENCY:
124 		return ism330dhcx_lis2mdl_odr_set(dev, i2c_addr, val->val1);
125 	default:
126 		LOG_DBG("shub: LIS2MDL attribute not supported.");
127 		return -ENOTSUP;
128 	}
129 
130 	return 0;
131 }
132 #endif /* CONFIG_ISM330DHCX_EXT_LIS2MDL || CONFIG_ISM330DHCX_EXT_IIS2MDC */
133 
134 /*
135  * HTS221 humidity device specific part
136  */
137 #ifdef CONFIG_ISM330DHCX_EXT_HTS221
138 
139 #define HTS221_AUTOINCREMENT		BIT(7)
140 
141 #define HTS221_REG_CTRL1		0x20
142 #define HTS221_ODR_1HZ			0x01
143 #define HTS221_BDU			0x04
144 #define HTS221_PD			0x80
145 
146 #define HTS221_REG_CONV_START		0x30
147 
ism330dhcx_hts221_read_conv_data(const struct device * dev,uint8_t i2c_addr)148 static int ism330dhcx_hts221_read_conv_data(const struct device *dev, uint8_t i2c_addr)
149 {
150 	struct ism330dhcx_data *data = dev->data;
151 	uint8_t buf[16], i;
152 	struct hts221_data *ht = &data->hts221;
153 
154 	for (i = 0; i < sizeof(buf); i += 7) {
155 		unsigned char len = MIN(7, sizeof(buf) - i);
156 
157 		if (ism330dhcx_shub_read_slave_reg(dev, i2c_addr, (HTS221_REG_CONV_START + i)
158 							| HTS221_AUTOINCREMENT, &buf[i],
159 						   len) < 0) {
160 			LOG_DBG("shub: failed to read hts221 conv data");
161 			return -EIO;
162 		}
163 	}
164 
165 	ht->y0 = buf[0] / 2;
166 	ht->y1 = buf[1] / 2;
167 	ht->x0 = buf[6] | (buf[7] << 8);
168 	ht->x1 = buf[10] | (buf[11] << 8);
169 
170 	return 0;
171 }
172 
ism330dhcx_hts221_init(const struct device * dev,uint8_t i2c_addr)173 static int ism330dhcx_hts221_init(const struct device *dev, uint8_t i2c_addr)
174 {
175 	uint8_t hum_cfg;
176 
177 	/* configure ODR and BDU */
178 	hum_cfg = HTS221_ODR_1HZ | HTS221_BDU | HTS221_PD;
179 	ism330dhcx_shub_write_slave_reg(dev, i2c_addr, HTS221_REG_CTRL1, &hum_cfg, 1);
180 
181 	return ism330dhcx_hts221_read_conv_data(dev, i2c_addr);
182 }
183 
184 static const uint16_t hts221_map[] = {0, 1, 7, 12};
185 
ism330dhcx_hts221_odr_set(const struct device * dev,uint8_t i2c_addr,uint16_t freq)186 static int ism330dhcx_hts221_odr_set(const struct device *dev, uint8_t i2c_addr, uint16_t freq)
187 {
188 	uint8_t odr, cfg;
189 
190 	for (odr = 0; odr < ARRAY_SIZE(hts221_map); odr++) {
191 		if (freq == hts221_map[odr]) {
192 			break;
193 		}
194 	}
195 
196 	if (odr == ARRAY_SIZE(hts221_map)) {
197 		LOG_DBG("shub: HTS221 freq val %d not supported.", freq);
198 		return -ENOTSUP;
199 	}
200 
201 	cfg = odr | HTS221_BDU | HTS221_PD;
202 	ism330dhcx_shub_write_slave_reg(dev, i2c_addr, HTS221_REG_CTRL1, &cfg, 1);
203 
204 	ism330dhcx_shub_enable(dev, 1);
205 	return 0;
206 }
207 
ism330dhcx_hts221_conf(const struct device * dev,uint8_t i2c_addr,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)208 static int ism330dhcx_hts221_conf(const struct device *dev, uint8_t i2c_addr,
209 				  enum sensor_channel chan, enum sensor_attribute attr,
210 				  const struct sensor_value *val)
211 {
212 	switch (attr) {
213 	case SENSOR_ATTR_SAMPLING_FREQUENCY:
214 		return ism330dhcx_hts221_odr_set(dev, i2c_addr, val->val1);
215 	default:
216 		LOG_DBG("shub: HTS221 attribute not supported.");
217 		return -ENOTSUP;
218 	}
219 
220 	return 0;
221 }
222 #endif /* CONFIG_ISM330DHCX_EXT_HTS221 */
223 
224 /*
225  * LPS22HB baro/temp device specific part
226  */
227 #ifdef CONFIG_ISM330DHCX_EXT_LPS22HB
228 
229 #define LPS22HB_CTRL_REG1		0x10
230 #define LPS22HB_CTRL_REG2		0x11
231 
232 #define LPS22HB_SW_RESET		0x04
233 #define LPS22HB_ODR_10HZ		0x20
234 #define LPS22HB_LPF_EN			0x08
235 #define LPS22HB_BDU_EN			0x02
236 
ism330dhcx_lps22hb_init(const struct device * dev,uint8_t i2c_addr)237 static int ism330dhcx_lps22hb_init(const struct device *dev, uint8_t i2c_addr)
238 {
239 	uint8_t baro_cfg[2];
240 
241 	/* sw reset device */
242 	baro_cfg[0] = LPS22HB_SW_RESET;
243 	ism330dhcx_shub_write_slave_reg(dev, i2c_addr, LPS22HB_CTRL_REG2, baro_cfg, 1);
244 
245 	k_sleep(K_MSEC(1)); /* turn-on time in ms */
246 
247 	/* configure device */
248 	baro_cfg[0] = LPS22HB_ODR_10HZ | LPS22HB_LPF_EN | LPS22HB_BDU_EN;
249 	ism330dhcx_shub_write_slave_reg(dev, i2c_addr, LPS22HB_CTRL_REG1, baro_cfg, 1);
250 
251 	return 0;
252 }
253 #endif /* CONFIG_ISM330DHCX_EXT_LPS22HB */
254 
255 /*
256  * LPS22HH baro/temp device specific part
257  */
258 #ifdef CONFIG_ISM330DHCX_EXT_LPS22HH
259 
260 #define LPS22HH_CTRL_REG1		0x10
261 #define LPS22HH_CTRL_REG2		0x11
262 
263 #define LPS22HH_SW_RESET		0x04
264 #define LPS22HH_IF_ADD_INC		0x10
265 #define LPS22HH_ODR_10HZ		0x20
266 #define LPS22HH_LPF_EN			0x08
267 #define LPS22HH_BDU_EN			0x02
268 
ism330dhcx_lps22hh_init(const struct device * dev,uint8_t i2c_addr)269 static int ism330dhcx_lps22hh_init(const struct device *dev, uint8_t i2c_addr)
270 {
271 	uint8_t baro_cfg[2];
272 
273 	/* sw reset device */
274 	baro_cfg[0] = LPS22HH_SW_RESET;
275 	ism330dhcx_shub_write_slave_reg(dev, i2c_addr, LPS22HH_CTRL_REG2, baro_cfg, 1);
276 
277 	k_sleep(K_MSEC(100)); /* turn-on time in ms */
278 
279 	/* configure device */
280 	baro_cfg[0] = LPS22HH_IF_ADD_INC;
281 	ism330dhcx_shub_write_slave_reg(dev, i2c_addr, LPS22HH_CTRL_REG2, baro_cfg, 1);
282 
283 	baro_cfg[0] = LPS22HH_ODR_10HZ | LPS22HH_LPF_EN | LPS22HH_BDU_EN;
284 	ism330dhcx_shub_write_slave_reg(dev, i2c_addr, 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 
ism330dhcx_lps22hh_odr_set(const struct device * dev,uint8_t i2c_addr,uint16_t freq)291 static int ism330dhcx_lps22hh_odr_set(const struct device *dev, 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 	ism330dhcx_shub_write_slave_reg(dev, i2c_addr, LPS22HH_CTRL_REG1, &cfg, 1);
308 
309 	ism330dhcx_shub_enable(dev, 1);
310 	return 0;
311 }
312 
ism330dhcx_lps22hh_conf(const struct device * dev,uint8_t i2c_addr,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)313 static int ism330dhcx_lps22hh_conf(const struct device *dev, uint8_t i2c_addr,
314 				   enum sensor_channel chan, enum sensor_attribute attr,
315 				   const struct sensor_value *val)
316 {
317 	switch (attr) {
318 	case SENSOR_ATTR_SAMPLING_FREQUENCY:
319 		return ism330dhcx_lps22hh_odr_set(dev, i2c_addr, val->val1);
320 	default:
321 		LOG_DBG("shub: LPS22HH attribute not supported.");
322 		return -ENOTSUP;
323 	}
324 
325 	return 0;
326 }
327 #endif /* CONFIG_ISM330DHCX_EXT_LPS22HH */
328 
329 /* List of supported external sensors */
330 static struct ism330dhcx_shub_slist {
331 	enum sensor_channel type;
332 	uint8_t i2c_addr[2];
333 	uint8_t ext_i2c_addr;
334 	uint8_t wai_addr;
335 	uint8_t wai_val;
336 	uint8_t out_data_addr;
337 	uint8_t out_data_len;
338 	uint8_t sh_out_reg;
339 	int (*dev_init)(const struct device *dev, uint8_t i2c_addr);
340 	int (*dev_conf)(const struct device *dev, uint8_t i2c_addr, enum sensor_channel chan,
341 			enum sensor_attribute attr, const struct sensor_value *val);
342 } ism330dhcx_shub_slist[] = {
343 #if defined(CONFIG_ISM330DHCX_EXT_LIS2MDL) || defined(CONFIG_ISM330DHCX_EXT_IIS2MDC)
344 	{
345 		/* LIS2MDL */
346 		.type		= SENSOR_CHAN_MAGN_XYZ,
347 		.i2c_addr       = { 0x1E },
348 		.wai_addr       = 0x4F,
349 		.wai_val        = 0x40,
350 		.out_data_addr  = 0x68,
351 		.out_data_len   = 0x06,
352 		.dev_init       = (ism330dhcx_lis2mdl_init),
353 		.dev_conf       = (ism330dhcx_lis2mdl_conf),
354 	},
355 #endif /* CONFIG_ISM330DHCX_EXT_LIS2MDL || CONFIG_ISM330DHCX_EXT_IIS2MDC */
356 
357 #ifdef CONFIG_ISM330DHCX_EXT_HTS221
358 	{
359 		/* HTS221 */
360 		.type		= SENSOR_CHAN_HUMIDITY,
361 		.i2c_addr       = { 0x5F },
362 		.wai_addr       = 0x0F,
363 		.wai_val        = 0xBC,
364 		.out_data_addr  = 0x28 | HTS221_AUTOINCREMENT,
365 		.out_data_len   = 0x02,
366 		.dev_init       = (ism330dhcx_hts221_init),
367 		.dev_conf       = (ism330dhcx_hts221_conf),
368 	},
369 #endif /* CONFIG_ISM330DHCX_EXT_HTS221 */
370 
371 #ifdef CONFIG_ISM330DHCX_EXT_LPS22HB
372 	{
373 		/* LPS22HB */
374 		.type		= SENSOR_CHAN_PRESS,
375 		.i2c_addr       = { 0x5C, 0x5D },
376 		.wai_addr       = 0x0F,
377 		.wai_val        = 0xB1,
378 		.out_data_addr  = 0x28,
379 		.out_data_len   = 0x05,
380 		.dev_init       = (ism330dhcx_lps22hb_init),
381 	},
382 #endif /* CONFIG_ISM330DHCX_EXT_LPS22HB */
383 
384 #ifdef CONFIG_ISM330DHCX_EXT_LPS22HH
385 	{
386 		/* LPS22HH */
387 		.type		= SENSOR_CHAN_PRESS,
388 		.i2c_addr       = { 0x5C, 0x5D },
389 		.wai_addr       = 0x0F,
390 		.wai_val        = 0xB3,
391 		.out_data_addr  = 0x28,
392 		.out_data_len   = 0x05,
393 		.dev_init       = (ism330dhcx_lps22hh_init),
394 		.dev_conf       = (ism330dhcx_lps22hh_conf),
395 	},
396 #endif /* CONFIG_ISM330DHCX_EXT_LPS22HH */
397 };
398 
ism330dhcx_shub_wait_completed(const struct device * dev)399 static inline void ism330dhcx_shub_wait_completed(const struct device *dev)
400 {
401 	struct ism330dhcx_data *data = dev->data;
402 	ism330dhcx_status_master_t status;
403 
404 	do {
405 		k_msleep(1);
406 		ism330dhcx_sh_status_get(data->ctx, &status);
407 	} while (status.sens_hub_endop == 0);
408 }
409 
ism330dhcx_shub_embedded_en(const struct device * dev,bool on)410 static inline void ism330dhcx_shub_embedded_en(const struct device *dev, bool on)
411 {
412 	struct ism330dhcx_data *data = dev->data;
413 
414 	if (on) {
415 		(void) ism330dhcx_mem_bank_set(data->ctx, ISM330DHCX_SENSOR_HUB_BANK);
416 	} else {
417 		(void) ism330dhcx_mem_bank_set(data->ctx, ISM330DHCX_USER_BANK);
418 	}
419 
420 	k_busy_wait(150);
421 }
422 
ism330dhcx_shub_read_embedded_regs(const struct device * dev,uint8_t reg_addr,uint8_t * value,int len)423 static int ism330dhcx_shub_read_embedded_regs(const struct device *dev, uint8_t reg_addr,
424 					      uint8_t *value, int len)
425 {
426 	struct ism330dhcx_data *data = dev->data;
427 
428 	ism330dhcx_shub_embedded_en(dev, true);
429 
430 	if (ism330dhcx_read_reg(data->ctx, reg_addr, value, len) < 0) {
431 		LOG_DBG("shub: failed to read external reg: %02x", reg_addr);
432 		ism330dhcx_shub_embedded_en(dev, false);
433 		return -EIO;
434 	}
435 
436 	ism330dhcx_shub_embedded_en(dev, false);
437 
438 	return 0;
439 }
440 
ism330dhcx_shub_write_embedded_regs(const struct device * dev,uint8_t reg_addr,uint8_t * value,uint8_t len)441 static int ism330dhcx_shub_write_embedded_regs(const struct device *dev, uint8_t reg_addr,
442 					       uint8_t *value, uint8_t len)
443 {
444 	struct ism330dhcx_data *data = dev->data;
445 
446 	ism330dhcx_shub_embedded_en(dev, true);
447 
448 	if (ism330dhcx_write_reg(data->ctx, reg_addr, value, len) < 0) {
449 		LOG_DBG("shub: failed to write external reg: %02x", reg_addr);
450 		ism330dhcx_shub_embedded_en(dev, false);
451 		return -EIO;
452 	}
453 
454 	ism330dhcx_shub_embedded_en(dev, false);
455 
456 	return 0;
457 }
458 
ism330dhcx_shub_enable(const struct device * dev,uint8_t enable)459 static void ism330dhcx_shub_enable(const struct device *dev, uint8_t enable)
460 {
461 	struct ism330dhcx_data *data = dev->data;
462 
463 	/* Enable Accel @26hz */
464 	if (!data->accel_freq) {
465 		uint8_t odr = (enable) ? 2 : 0;
466 
467 		if (ism330dhcx_xl_data_rate_set(data->ctx, odr) < 0) {
468 			LOG_DBG("shub: failed to set XL sampling rate");
469 			return;
470 		}
471 	}
472 
473 	ism330dhcx_shub_embedded_en(dev, true);
474 
475 	if (ism330dhcx_sh_master_set(data->ctx, enable) < 0) {
476 		LOG_DBG("shub: failed to set master on");
477 		ism330dhcx_shub_embedded_en(dev, false);
478 		return;
479 	}
480 
481 	ism330dhcx_shub_embedded_en(dev, false);
482 }
483 
484 /* must be called with master on */
ism330dhcx_shub_check_slv0_nack(const struct device * dev)485 static int ism330dhcx_shub_check_slv0_nack(const struct device *dev)
486 {
487 	uint8_t status;
488 
489 	if (ism330dhcx_shub_read_embedded_regs(dev, ISM330DHCX_SHUB_STATUS_MASTER, &status, 1) <
490 	    0) {
491 		LOG_DBG("shub: error reading embedded reg");
492 		return -EIO;
493 	}
494 
495 	if (status & (ISM330DHCX_SHUB_STATUS_SLV0_NACK)) {
496 		LOG_DBG("shub: SLV0 nacked");
497 		return -EIO;
498 	}
499 
500 	return 0;
501 }
502 
503 /*
504  * use SLV0 for generic read to slave device
505  */
ism330dhcx_shub_read_slave_reg(const struct device * dev,uint8_t slv_addr,uint8_t slv_reg,uint8_t * value,uint16_t len)506 static int ism330dhcx_shub_read_slave_reg(const struct device *dev, uint8_t slv_addr,
507 					  uint8_t slv_reg, uint8_t *value, uint16_t len)
508 {
509 	struct ism330dhcx_data *data = dev->data;
510 	uint8_t slave[3];
511 
512 	slave[0] = (slv_addr << 1) | ISM330DHCX_SHUB_SLVX_READ;
513 	slave[1] = slv_reg;
514 	slave[2] = (len & 0x7);
515 
516 	if (ism330dhcx_shub_write_embedded_regs(dev, ISM330DHCX_SHUB_SLV0_ADDR, slave, 3) < 0) {
517 		LOG_DBG("shub: error writing embedded reg");
518 		return -EIO;
519 	}
520 
521 	/* turn SH on */
522 	ism330dhcx_shub_enable(dev, 1);
523 	ism330dhcx_shub_wait_completed(dev);
524 
525 	if (ism330dhcx_shub_check_slv0_nack(dev) < 0) {
526 		ism330dhcx_shub_enable(dev, 0);
527 		return -EIO;
528 	}
529 
530 	/* read data from external slave */
531 	ism330dhcx_shub_embedded_en(dev, true);
532 	if (ism330dhcx_read_reg(data->ctx, ISM330DHCX_SHUB_DATA_OUT,
533 				value, len) < 0) {
534 		LOG_DBG("shub: error reading sensor data");
535 		ism330dhcx_shub_embedded_en(dev, false);
536 		return -EIO;
537 	}
538 	ism330dhcx_shub_embedded_en(dev, false);
539 
540 	ism330dhcx_shub_enable(dev, 0);
541 	return 0;
542 }
543 
544 /*
545  * use SLV0 to configure slave device
546  */
ism330dhcx_shub_write_slave_reg(const struct device * dev,uint8_t slv_addr,uint8_t slv_reg,uint8_t * value,uint16_t len)547 static int ism330dhcx_shub_write_slave_reg(const struct device *dev, uint8_t slv_addr,
548 					   uint8_t slv_reg, uint8_t *value, uint16_t len)
549 {
550 	uint8_t slv_cfg[3];
551 	uint8_t cnt = 0U;
552 
553 	while (cnt < len) {
554 		slv_cfg[0] = (slv_addr << 1) & ~ISM330DHCX_SHUB_SLVX_READ;
555 		slv_cfg[1] = slv_reg + cnt;
556 
557 		if (ism330dhcx_shub_write_embedded_regs(dev, ISM330DHCX_SHUB_SLV0_ADDR, slv_cfg,
558 							2) < 0) {
559 			LOG_DBG("shub: error writing embedded reg");
560 			return -EIO;
561 		}
562 
563 		slv_cfg[0] = value[cnt];
564 		if (ism330dhcx_shub_write_embedded_regs(dev, ISM330DHCX_SHUB_SLV0_DATAWRITE,
565 							slv_cfg, 1) < 0) {
566 			LOG_DBG("shub: error writing embedded reg");
567 			return -EIO;
568 		}
569 
570 		/* turn SH on */
571 		ism330dhcx_shub_enable(dev, 1);
572 		ism330dhcx_shub_wait_completed(dev);
573 
574 		if (ism330dhcx_shub_check_slv0_nack(dev) < 0) {
575 			ism330dhcx_shub_enable(dev, 0);
576 			return -EIO;
577 		}
578 
579 		ism330dhcx_shub_enable(dev, 0);
580 
581 		cnt++;
582 	}
583 
584 	/* Put SLV0 in IDLE mode */
585 	slv_cfg[0] = 0x7;
586 	slv_cfg[1] = 0x0;
587 	slv_cfg[2] = 0x0;
588 	if (ism330dhcx_shub_write_embedded_regs(dev, ISM330DHCX_SHUB_SLV0_ADDR, slv_cfg, 3) < 0) {
589 		LOG_DBG("shub: error writing embedded reg");
590 		return -EIO;
591 	}
592 
593 	return 0;
594 }
595 
596 /*
597  * SLAVEs configurations:
598  *
599  *  - SLAVE 0: used for configuring all slave devices
600  *  - SLAVE 1: used as data read channel for external slave device #1
601  *  - SLAVE 2: used as data read channel for external slave device #2
602  *  - SLAVE 3: used for generic reads while data channel is enabled
603  */
ism330dhcx_shub_set_data_channel(const struct device * dev)604 static int ism330dhcx_shub_set_data_channel(const struct device *dev)
605 {
606 	struct ism330dhcx_data *data = dev->data;
607 	uint8_t n, i, slv_cfg[6];
608 	struct ism330dhcx_shub_slist *sp;
609 
610 	/* Set data channel for slave devices */
611 	for (n = 0; n < num_ext_dev; n++) {
612 		sp = &ism330dhcx_shub_slist[shub_ext[n]];
613 
614 		i = n * 3;
615 		slv_cfg[i] = (sp->ext_i2c_addr << 1) | ISM330DHCX_SHUB_SLVX_READ;
616 		slv_cfg[i + 1] = sp->out_data_addr;
617 		slv_cfg[i + 2] = sp->out_data_len;
618 	}
619 
620 	if (ism330dhcx_shub_write_embedded_regs(dev, ISM330DHCX_SHUB_SLV1_ADDR, slv_cfg,
621 						n * 3) < 0) {
622 		LOG_DBG("shub: error writing embedded reg");
623 		return -EIO;
624 	}
625 
626 	/* Configure the master */
627 	ism330dhcx_aux_sens_on_t aux = ISM330DHCX_SLV_0_1_2;
628 
629 	if (ism330dhcx_sh_slave_connected_set(data->ctx, aux) < 0) {
630 		LOG_DBG("shub: error setting aux sensors");
631 		return -EIO;
632 	}
633 
634 	ism330dhcx_write_once_t wo = ISM330DHCX_ONLY_FIRST_CYCLE;
635 
636 	if (ism330dhcx_sh_write_mode_set(data->ctx, wo) < 0) {
637 		LOG_DBG("shub: error setting write once");
638 		return -EIO;
639 	}
640 
641 
642 	/* turn SH on */
643 	ism330dhcx_shub_enable(dev, 1);
644 	ism330dhcx_shub_wait_completed(dev);
645 
646 	return 0;
647 }
648 
ism330dhcx_shub_get_idx(enum sensor_channel type)649 int ism330dhcx_shub_get_idx(enum sensor_channel type)
650 {
651 	uint8_t n;
652 	struct ism330dhcx_shub_slist *sp;
653 
654 	for (n = 0; n < num_ext_dev; n++) {
655 		sp = &ism330dhcx_shub_slist[shub_ext[n]];
656 
657 		if (sp->type == type) {
658 			return n;
659 		}
660 	}
661 
662 	return -ENOTSUP;
663 }
664 
ism330dhcx_shub_fetch_external_devs(const struct device * dev)665 int ism330dhcx_shub_fetch_external_devs(const struct device *dev)
666 {
667 	uint8_t n;
668 	struct ism330dhcx_data *data = dev->data;
669 	struct ism330dhcx_shub_slist *sp;
670 
671 	/* read data from external slave */
672 	ism330dhcx_shub_embedded_en(dev, true);
673 
674 	for (n = 0; n < num_ext_dev; n++) {
675 		sp = &ism330dhcx_shub_slist[shub_ext[n]];
676 
677 		if (ism330dhcx_read_reg(data->ctx, sp->sh_out_reg,
678 				     data->ext_data[n], sp->out_data_len) < 0) {
679 			LOG_DBG("shub: failed to read sample");
680 			ism330dhcx_shub_embedded_en(dev, false);
681 			return -EIO;
682 		}
683 	}
684 
685 	ism330dhcx_shub_embedded_en(dev, false);
686 
687 	return 0;
688 }
689 
ism330dhcx_shub_config(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)690 int ism330dhcx_shub_config(const struct device *dev, enum sensor_channel chan,
691 			   enum sensor_attribute attr,
692 			   const struct sensor_value *val)
693 {
694 	struct ism330dhcx_shub_slist *sp = NULL;
695 	uint8_t n;
696 
697 	for (n = 0; n < num_ext_dev; n++) {
698 		sp = &ism330dhcx_shub_slist[shub_ext[n]];
699 
700 		if (sp->type == chan) {
701 			break;
702 		}
703 	}
704 
705 	if (n == num_ext_dev) {
706 		LOG_DBG("shub: chan not supported");
707 		return -ENOTSUP;
708 	}
709 
710 	if (sp == NULL || sp->dev_conf == NULL) {
711 		LOG_DBG("shub: chan not configurable");
712 		return -ENOTSUP;
713 	}
714 
715 	return sp->dev_conf(dev, sp->ext_i2c_addr, chan, attr, val);
716 }
717 
ism330dhcx_shub_init(const struct device * dev)718 int ism330dhcx_shub_init(const struct device *dev)
719 {
720 	uint8_t i, n = 0, regn;
721 	uint8_t chip_id;
722 	struct ism330dhcx_shub_slist *sp;
723 
724 	for (n = 0; n < ARRAY_SIZE(ism330dhcx_shub_slist); n++) {
725 		if (num_ext_dev >= ISM330DHCX_SHUB_MAX_NUM_SLVS) {
726 			break;
727 		}
728 
729 		chip_id = 0;
730 		sp = &ism330dhcx_shub_slist[n];
731 
732 		/*
733 		 * The external sensor may have different I2C address.
734 		 * So, try them one by one until we read the correct
735 		 * chip ID.
736 		 */
737 		for (i = 0U; i < ARRAY_SIZE(sp->i2c_addr); i++) {
738 			if (ism330dhcx_shub_read_slave_reg(dev, sp->i2c_addr[i], sp->wai_addr,
739 							   &chip_id, 1) < 0) {
740 				continue;
741 			}
742 			if (chip_id == sp->wai_val) {
743 				break;
744 			}
745 		}
746 
747 		if (i >= ARRAY_SIZE(sp->i2c_addr)) {
748 			LOG_DBG("shub: invalid chip id 0x%x", chip_id);
749 			continue;
750 		}
751 		LOG_INF("shub: Ext Device Chip Id: 0x%02x", chip_id);
752 		sp->ext_i2c_addr = sp->i2c_addr[i];
753 
754 		shub_ext[num_ext_dev++] = n;
755 	}
756 
757 	if (num_ext_dev == 0) {
758 		LOG_ERR("shub: no slave devices found");
759 		return -EINVAL;
760 	}
761 
762 	/* init external devices */
763 	for (n = 0, regn = 0; n < num_ext_dev; n++) {
764 		sp = &ism330dhcx_shub_slist[shub_ext[n]];
765 		sp->sh_out_reg = ISM330DHCX_SHUB_DATA_OUT + regn;
766 		regn += sp->out_data_len;
767 		sp->dev_init(dev, sp->ext_i2c_addr);
768 	}
769 
770 	ism330dhcx_shub_set_data_channel(dev);
771 
772 	return 0;
773 }
774