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