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