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