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