2019-02-18 15:17:54 +01:00
|
|
|
/* ST Microelectronics LSM6DSO 6-axis IMU sensor driver
|
|
|
|
*
|
|
|
|
* Copyright (c) 2019 STMicroelectronics
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*
|
|
|
|
* Datasheet:
|
|
|
|
* https://www.st.com/resource/en/datasheet/lsm6dso.pdf
|
|
|
|
*/
|
|
|
|
|
2020-04-16 12:23:17 -05:00
|
|
|
#define DT_DRV_COMPAT st_lsm6dso
|
|
|
|
|
2022-05-06 10:25:46 +02:00
|
|
|
#include <zephyr/device.h>
|
|
|
|
#include <zephyr/drivers/i2c.h>
|
|
|
|
#include <zephyr/sys/byteorder.h>
|
|
|
|
#include <zephyr/sys/__assert.h>
|
|
|
|
#include <zephyr/sys/util.h>
|
|
|
|
#include <zephyr/kernel.h>
|
|
|
|
#include <zephyr/drivers/sensor.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
#include "lsm6dso.h"
|
|
|
|
|
2019-10-11 12:40:57 +02:00
|
|
|
LOG_MODULE_DECLARE(LSM6DSO, CONFIG_SENSOR_LOG_LEVEL);
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
#define LSM6DSO_SHUB_DATA_OUT 0x02
|
|
|
|
|
|
|
|
#define LSM6DSO_SHUB_SLV0_ADDR 0x15
|
|
|
|
#define LSM6DSO_SHUB_SLV0_SUBADDR 0x16
|
|
|
|
#define LSM6DSO_SHUB_SLV0_CONFIG 0x17
|
|
|
|
#define LSM6DSO_SHUB_SLV1_ADDR 0x18
|
|
|
|
#define LSM6DSO_SHUB_SLV1_SUBADDR 0x19
|
|
|
|
#define LSM6DSO_SHUB_SLV1_CONFIG 0x1A
|
|
|
|
#define LSM6DSO_SHUB_SLV2_ADDR 0x1B
|
|
|
|
#define LSM6DSO_SHUB_SLV2_SUBADDR 0x1C
|
|
|
|
#define LSM6DSO_SHUB_SLV2_CONFIG 0x1D
|
|
|
|
#define LSM6DSO_SHUB_SLV3_ADDR 0x1E
|
|
|
|
#define LSM6DSO_SHUB_SLV3_SUBADDR 0x1F
|
|
|
|
#define LSM6DSO_SHUB_SLV3_CONFIG 0x20
|
|
|
|
#define LSM6DSO_SHUB_SLV0_DATAWRITE 0x21
|
|
|
|
|
|
|
|
#define LSM6DSO_SHUB_STATUS_MASTER 0x22
|
|
|
|
#define LSM6DSO_SHUB_STATUS_SLV0_NACK BIT(3)
|
|
|
|
#define LSM6DSO_SHUB_STATUS_ENDOP BIT(0)
|
|
|
|
|
|
|
|
#define LSM6DSO_SHUB_SLVX_WRITE 0x0
|
|
|
|
#define LSM6DSO_SHUB_SLVX_READ 0x1
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsm6dso_shub_write_slave_reg(const struct device *dev,
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t slv_addr, uint8_t slv_reg,
|
|
|
|
uint8_t *value, uint16_t len);
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsm6dso_shub_read_slave_reg(const struct device *dev,
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t slv_addr, uint8_t slv_reg,
|
|
|
|
uint8_t *value, uint16_t len);
|
2021-04-30 13:57:42 +02:00
|
|
|
static void lsm6dso_shub_enable(const struct device *dev, uint8_t enable);
|
2019-02-18 15:17:54 +01:00
|
|
|
|
drivers/sensor: lsm6dso: Fix shub init and configuration failures
There is a flaw with I2C communication to peripherals behind the shub
that causes sporadic failures. Especially calls to configure a device
after the lsm6dso initialization is finished, e.g. to set the ODR, can
fail to work correctly.
Access to shub peripheral registers is done by putting the parameters of
the operation into SLV0 and then waiting for the lsm6dso to perform the
xfer on the shub I2C bus. The lsm6dso does this in sync with the
accelerometer update rate. Once the shub is enabled, it peforms the
xfer repeatedly as the accelermeter is sampled.
The wait has a problem: It might detect that a previous shub xfer has
finished, which was done before SLV0 was programmed with new parameters.
The shub status register is read-to-clear. This isn't in the data sheet
or app note, but it is. By reading the status before enabling the
sensor and after programming SLV0, we can be sure when it becomes set it
has finished the current operation and not a previous one.
Also set the write-once flag before shub init. This causes the shub to
only perform I2C writes once instead of continuously. This was set at
the end of init, so any writes done during it would repeat until the
shub was disabled.
Put a timeout in the code that polls for the sensor hub op complete. It
could possibly poll forever. More importantly, if there is no device
connected to the sensor hub, the lsm6dso does not timeout on the
operation for ~13 seconds. Since the shub init does a probe for devices
on startup, this will happen if shub support is enabled but a lsm6dso
has no sensor hub devices. There could be multiple devices, some with
additional sensors and some without. Initialization of the devices
without additional sensors takes tens of seconds without this timeout
being added.
Add a 300 µs wait after disabling the sensor hub. This is necessary
according to the ST app note AN5192 §7.2.1.
Read the shub status from the main bank register instead of the shub
bank register. This avoids an extra bank switch before and after each
status poll. Actually two bank switches on each side, since the lsm6dso
driver switched banks and then the ST HAL function to get the status
register switches again.
The wait for the shub I2C transaction to finish is not needed when the
shub is enabled at the end of init. We aren't starting a new I2C write
or reading the result of a read.
Signed-off-by: Trent Piepho <trent.piepho@igorinstitute.com>
2022-12-21 04:20:06 -08:00
|
|
|
|
|
|
|
/* ST HAL skips this register, only supports it via the slower lsm6dso_sh_status_get() */
|
|
|
|
static int32_t lsm6dso_sh_status_mainpage_get(stmdev_ctx_t *ctx, lsm6dso_status_master_t *val)
|
|
|
|
{
|
|
|
|
return lsm6dso_read_reg(ctx, LSM6DSO_STATUS_MASTER_MAINPAGE, (uint8_t *)val, 1);
|
|
|
|
}
|
|
|
|
|
2019-02-18 15:17:54 +01:00
|
|
|
/*
|
|
|
|
* LIS2MDL magn device specific part
|
|
|
|
*/
|
|
|
|
#ifdef CONFIG_LSM6DSO_EXT_LIS2MDL
|
|
|
|
|
|
|
|
#define LIS2MDL_CFG_REG_A 0x60
|
|
|
|
#define LIS2MDL_CFG_REG_B 0x61
|
|
|
|
#define LIS2MDL_CFG_REG_C 0x62
|
|
|
|
#define LIS2MDL_STATUS_REG 0x67
|
|
|
|
|
|
|
|
#define LIS2MDL_SW_RESET 0x20
|
|
|
|
#define LIS2MDL_ODR_10HZ 0x00
|
|
|
|
#define LIS2MDL_ODR_100HZ 0x0C
|
|
|
|
#define LIS2MDL_OFF_CANC 0x02
|
|
|
|
#define LIS2MDL_SENSITIVITY 1500
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsm6dso_lis2mdl_init(const struct device *dev, uint8_t i2c_addr)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2021-04-30 13:57:42 +02:00
|
|
|
struct lsm6dso_data *data = dev->data;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t mag_cfg[2];
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
data->magn_gain = LIS2MDL_SENSITIVITY;
|
|
|
|
|
|
|
|
/* sw reset device */
|
|
|
|
mag_cfg[0] = LIS2MDL_SW_RESET;
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_write_slave_reg(dev, i2c_addr,
|
2019-02-18 15:17:54 +01:00
|
|
|
LIS2MDL_CFG_REG_A, mag_cfg, 1);
|
|
|
|
|
2019-10-06 14:02:31 -05:00
|
|
|
k_sleep(K_MSEC(10)); /* turn-on time in ms */
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
/* configure mag */
|
|
|
|
mag_cfg[0] = LIS2MDL_ODR_10HZ;
|
|
|
|
mag_cfg[1] = LIS2MDL_OFF_CANC;
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_write_slave_reg(dev, i2c_addr,
|
2019-02-18 15:17:54 +01:00
|
|
|
LIS2MDL_CFG_REG_A, mag_cfg, 2);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
static const uint16_t lis2mdl_map[] = {10, 20, 50, 100};
|
2019-02-18 15:17:54 +01:00
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsm6dso_lis2mdl_odr_set(const struct device *dev,
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t i2c_addr, uint16_t freq)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t odr, cfg;
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
for (odr = 0; odr < ARRAY_SIZE(lis2mdl_map); odr++) {
|
|
|
|
if (freq == lis2mdl_map[odr]) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (odr == ARRAY_SIZE(lis2mdl_map)) {
|
|
|
|
LOG_DBG("shub: LIS2MDL freq val %d not supported.", freq);
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg = (odr << 2);
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_write_slave_reg(dev, i2c_addr,
|
2019-02-18 15:17:54 +01:00
|
|
|
LIS2MDL_CFG_REG_A, &cfg, 1);
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_enable(dev, 1);
|
2019-02-18 15:17:54 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsm6dso_lis2mdl_conf(const struct device *dev, uint8_t i2c_addr,
|
2019-02-18 15:17:54 +01:00
|
|
|
enum sensor_channel chan,
|
|
|
|
enum sensor_attribute attr,
|
|
|
|
const struct sensor_value *val)
|
|
|
|
{
|
|
|
|
switch (attr) {
|
|
|
|
case SENSOR_ATTR_SAMPLING_FREQUENCY:
|
2021-04-30 13:57:42 +02:00
|
|
|
return lsm6dso_lis2mdl_odr_set(dev, i2c_addr, val->val1);
|
2019-02-18 15:17:54 +01:00
|
|
|
default:
|
|
|
|
LOG_DBG("shub: LIS2MDL attribute not supported.");
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_LSM6DSO_EXT_LIS2MDL */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* HTS221 humidity device specific part
|
|
|
|
*/
|
|
|
|
#ifdef CONFIG_LSM6DSO_EXT_HTS221
|
|
|
|
|
|
|
|
#define HTS221_AUTOINCREMENT BIT(7)
|
|
|
|
|
|
|
|
#define HTS221_REG_CTRL1 0x20
|
|
|
|
#define HTS221_ODR_1HZ 0x01
|
|
|
|
#define HTS221_BDU 0x04
|
|
|
|
#define HTS221_PD 0x80
|
|
|
|
|
|
|
|
#define HTS221_REG_CONV_START 0x30
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsmdso_hts221_read_conv_data(const struct device *dev,
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t i2c_addr)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t buf[16], i;
|
2019-02-18 15:17:54 +01:00
|
|
|
struct hts221_data *ht = &data->hts221;
|
|
|
|
|
|
|
|
for (i = 0; i < sizeof(buf); i += 7) {
|
|
|
|
unsigned char len = MIN(7, sizeof(buf) - i);
|
|
|
|
|
|
|
|
if (lsm6dso_shub_read_slave_reg(data, i2c_addr,
|
|
|
|
(HTS221_REG_CONV_START + i) |
|
|
|
|
HTS221_AUTOINCREMENT,
|
|
|
|
&buf[i], len) < 0) {
|
|
|
|
LOG_DBG("shub: failed to read hts221 conv data");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ht->y0 = buf[0] / 2;
|
|
|
|
ht->y1 = buf[1] / 2;
|
|
|
|
ht->x0 = sys_le16_to_cpu(buf[6] | (buf[7] << 8));
|
|
|
|
ht->x1 = sys_le16_to_cpu(buf[10] | (buf[11] << 8));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsm6dso_hts221_init(const struct device *dev, uint8_t i2c_addr)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t hum_cfg;
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
/* configure ODR and BDU */
|
|
|
|
hum_cfg = HTS221_ODR_1HZ | HTS221_BDU | HTS221_PD;
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_write_slave_reg(dev, i2c_addr,
|
2019-02-18 15:17:54 +01:00
|
|
|
HTS221_REG_CTRL1, &hum_cfg, 1);
|
|
|
|
|
|
|
|
return lsmdso_hts221_read_conv_data(data, i2c_addr);
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
static const uint16_t hts221_map[] = {0, 1, 7, 12};
|
2019-02-18 15:17:54 +01:00
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsm6dso_hts221_odr_set(const struct device *dev,
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t i2c_addr, uint16_t freq)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t odr, cfg;
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
for (odr = 0; odr < ARRAY_SIZE(hts221_map); odr++) {
|
|
|
|
if (freq == hts221_map[odr]) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (odr == ARRAY_SIZE(hts221_map)) {
|
|
|
|
LOG_DBG("shub: HTS221 freq val %d not supported.", freq);
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg = odr | HTS221_BDU | HTS221_PD;
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_write_slave_reg(dev, i2c_addr,
|
2019-02-18 15:17:54 +01:00
|
|
|
HTS221_REG_CTRL1, &cfg, 1);
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_enable(dev, 1);
|
2019-02-18 15:17:54 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsm6dso_hts221_conf(const struct device *dev, uint8_t i2c_addr,
|
2019-02-18 15:17:54 +01:00
|
|
|
enum sensor_channel chan,
|
|
|
|
enum sensor_attribute attr,
|
|
|
|
const struct sensor_value *val)
|
|
|
|
{
|
|
|
|
switch (attr) {
|
|
|
|
case SENSOR_ATTR_SAMPLING_FREQUENCY:
|
|
|
|
return lsm6dso_hts221_odr_set(data, i2c_addr, val->val1);
|
|
|
|
default:
|
|
|
|
LOG_DBG("shub: HTS221 attribute not supported.");
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_LSM6DSO_EXT_HTS221 */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* LPS22HB baro/temp device specific part
|
|
|
|
*/
|
|
|
|
#ifdef CONFIG_LSM6DSO_EXT_LPS22HB
|
|
|
|
|
|
|
|
#define LPS22HB_CTRL_REG1 0x10
|
|
|
|
#define LPS22HB_CTRL_REG2 0x11
|
|
|
|
|
|
|
|
#define LPS22HB_SW_RESET 0x04
|
|
|
|
#define LPS22HB_ODR_10HZ 0x20
|
|
|
|
#define LPS22HB_LPF_EN 0x08
|
|
|
|
#define LPS22HB_BDU_EN 0x02
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsm6dso_lps22hb_init(const struct device *dev, uint8_t i2c_addr)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t baro_cfg[2];
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
/* sw reset device */
|
|
|
|
baro_cfg[0] = LPS22HB_SW_RESET;
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_write_slave_reg(dev, i2c_addr,
|
2019-02-18 15:17:54 +01:00
|
|
|
LPS22HB_CTRL_REG2, baro_cfg, 1);
|
|
|
|
|
2019-10-06 14:02:31 -05:00
|
|
|
k_sleep(K_MSEC(1)); /* turn-on time in ms */
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
/* configure device */
|
|
|
|
baro_cfg[0] = LPS22HB_ODR_10HZ | LPS22HB_LPF_EN | LPS22HB_BDU_EN;
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_write_slave_reg(dev, i2c_addr,
|
2019-02-18 15:17:54 +01:00
|
|
|
LPS22HB_CTRL_REG1, baro_cfg, 1);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_LSM6DSO_EXT_LPS22HB */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* LPS22HH baro/temp device specific part
|
|
|
|
*/
|
|
|
|
#ifdef CONFIG_LSM6DSO_EXT_LPS22HH
|
|
|
|
|
|
|
|
#define LPS22HH_CTRL_REG1 0x10
|
|
|
|
#define LPS22HH_CTRL_REG2 0x11
|
|
|
|
|
|
|
|
#define LPS22HH_SW_RESET 0x04
|
|
|
|
#define LPS22HH_IF_ADD_INC 0x10
|
|
|
|
#define LPS22HH_ODR_10HZ 0x20
|
|
|
|
#define LPS22HH_LPF_EN 0x08
|
|
|
|
#define LPS22HH_BDU_EN 0x02
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsm6dso_lps22hh_init(const struct device *dev, uint8_t i2c_addr)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t baro_cfg[2];
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
/* sw reset device */
|
|
|
|
baro_cfg[0] = LPS22HH_SW_RESET;
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_write_slave_reg(dev, i2c_addr,
|
2019-02-18 15:17:54 +01:00
|
|
|
LPS22HH_CTRL_REG2, baro_cfg, 1);
|
|
|
|
|
2019-10-06 14:02:31 -05:00
|
|
|
k_sleep(K_MSEC(100)); /* turn-on time in ms */
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
/* configure device */
|
|
|
|
baro_cfg[0] = LPS22HH_IF_ADD_INC;
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_write_slave_reg(dev, i2c_addr,
|
2019-02-18 15:17:54 +01:00
|
|
|
LPS22HH_CTRL_REG2, baro_cfg, 1);
|
|
|
|
|
|
|
|
baro_cfg[0] = LPS22HH_ODR_10HZ | LPS22HH_LPF_EN | LPS22HH_BDU_EN;
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_write_slave_reg(dev, i2c_addr,
|
2019-02-18 15:17:54 +01:00
|
|
|
LPS22HH_CTRL_REG1, baro_cfg, 1);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
static const uint16_t lps22hh_map[] = {0, 1, 10, 25, 50, 75, 100, 200};
|
2019-02-18 15:17:54 +01:00
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsm6dso_lps22hh_odr_set(const struct device *dev,
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t i2c_addr, uint16_t freq)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t odr, cfg;
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
for (odr = 0; odr < ARRAY_SIZE(lps22hh_map); odr++) {
|
|
|
|
if (freq == lps22hh_map[odr]) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (odr == ARRAY_SIZE(lps22hh_map)) {
|
|
|
|
LOG_DBG("shub: LPS22HH freq val %d not supported.", freq);
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg = (odr << 4) | LPS22HH_LPF_EN | LPS22HH_BDU_EN;
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_write_slave_reg(dev, i2c_addr,
|
2019-02-18 15:17:54 +01:00
|
|
|
LPS22HH_CTRL_REG1, &cfg, 1);
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_enable(dev, 1);
|
2019-02-18 15:17:54 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsm6dso_lps22hh_conf(const struct device *dev, uint8_t i2c_addr,
|
2019-02-18 15:17:54 +01:00
|
|
|
enum sensor_channel chan,
|
|
|
|
enum sensor_attribute attr,
|
|
|
|
const struct sensor_value *val)
|
|
|
|
{
|
|
|
|
switch (attr) {
|
|
|
|
case SENSOR_ATTR_SAMPLING_FREQUENCY:
|
2021-04-30 13:57:42 +02:00
|
|
|
return lsm6dso_lps22hh_odr_set(dev, i2c_addr, val->val1);
|
2019-02-18 15:17:54 +01:00
|
|
|
default:
|
|
|
|
LOG_DBG("shub: LPS22HH attribute not supported.");
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_LSM6DSO_EXT_LPS22HH */
|
|
|
|
|
|
|
|
/* List of supported external sensors */
|
|
|
|
static struct lsm6dso_shub_slist {
|
|
|
|
enum sensor_channel type;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t i2c_addr[2];
|
|
|
|
uint8_t ext_i2c_addr;
|
|
|
|
uint8_t wai_addr;
|
|
|
|
uint8_t wai_val;
|
|
|
|
uint8_t out_data_addr;
|
|
|
|
uint8_t out_data_len;
|
|
|
|
uint8_t sh_out_reg;
|
2021-04-30 13:57:42 +02:00
|
|
|
int (*dev_init)(const struct device *dev, uint8_t i2c_addr);
|
|
|
|
int (*dev_conf)(const struct device *dev, uint8_t i2c_addr,
|
2019-02-18 15:17:54 +01:00
|
|
|
enum sensor_channel chan, enum sensor_attribute attr,
|
|
|
|
const struct sensor_value *val);
|
|
|
|
} lsm6dso_shub_slist[] = {
|
|
|
|
#ifdef CONFIG_LSM6DSO_EXT_LIS2MDL
|
|
|
|
{
|
|
|
|
/* LIS2MDL */
|
|
|
|
.type = SENSOR_CHAN_MAGN_XYZ,
|
|
|
|
.i2c_addr = { 0x1E },
|
|
|
|
.wai_addr = 0x4F,
|
|
|
|
.wai_val = 0x40,
|
|
|
|
.out_data_addr = 0x68,
|
|
|
|
.out_data_len = 0x06,
|
|
|
|
.dev_init = (lsm6dso_lis2mdl_init),
|
|
|
|
.dev_conf = (lsm6dso_lis2mdl_conf),
|
|
|
|
},
|
|
|
|
#endif /* CONFIG_LSM6DSO_EXT_LIS2MDL */
|
|
|
|
|
|
|
|
#ifdef CONFIG_LSM6DSO_EXT_HTS221
|
|
|
|
{
|
|
|
|
/* HTS221 */
|
|
|
|
.type = SENSOR_CHAN_HUMIDITY,
|
|
|
|
.i2c_addr = { 0x5F },
|
|
|
|
.wai_addr = 0x0F,
|
|
|
|
.wai_val = 0xBC,
|
|
|
|
.out_data_addr = 0x28 | HTS221_AUTOINCREMENT,
|
|
|
|
.out_data_len = 0x02,
|
|
|
|
.dev_init = (lsm6dso_hts221_init),
|
|
|
|
.dev_conf = (lsm6dso_hts221_conf),
|
|
|
|
},
|
|
|
|
#endif /* CONFIG_LSM6DSO_EXT_HTS221 */
|
|
|
|
|
|
|
|
#ifdef CONFIG_LSM6DSO_EXT_LPS22HB
|
|
|
|
{
|
|
|
|
/* LPS22HB */
|
|
|
|
.type = SENSOR_CHAN_PRESS,
|
|
|
|
.i2c_addr = { 0x5C, 0x5D },
|
|
|
|
.wai_addr = 0x0F,
|
|
|
|
.wai_val = 0xB1,
|
|
|
|
.out_data_addr = 0x28,
|
|
|
|
.out_data_len = 0x05,
|
|
|
|
.dev_init = (lsm6dso_lps22hb_init),
|
|
|
|
},
|
|
|
|
#endif /* CONFIG_LSM6DSO_EXT_LPS22HB */
|
|
|
|
|
|
|
|
#ifdef CONFIG_LSM6DSO_EXT_LPS22HH
|
|
|
|
{
|
|
|
|
/* LPS22HH */
|
|
|
|
.type = SENSOR_CHAN_PRESS,
|
|
|
|
.i2c_addr = { 0x5C, 0x5D },
|
|
|
|
.wai_addr = 0x0F,
|
|
|
|
.wai_val = 0xB3,
|
|
|
|
.out_data_addr = 0x28,
|
|
|
|
.out_data_len = 0x05,
|
|
|
|
.dev_init = (lsm6dso_lps22hh_init),
|
|
|
|
.dev_conf = (lsm6dso_lps22hh_conf),
|
|
|
|
},
|
|
|
|
#endif /* CONFIG_LSM6DSO_EXT_LPS22HH */
|
|
|
|
};
|
|
|
|
|
drivers/sensor: lsm6dso: Fix shub init and configuration failures
There is a flaw with I2C communication to peripherals behind the shub
that causes sporadic failures. Especially calls to configure a device
after the lsm6dso initialization is finished, e.g. to set the ODR, can
fail to work correctly.
Access to shub peripheral registers is done by putting the parameters of
the operation into SLV0 and then waiting for the lsm6dso to perform the
xfer on the shub I2C bus. The lsm6dso does this in sync with the
accelerometer update rate. Once the shub is enabled, it peforms the
xfer repeatedly as the accelermeter is sampled.
The wait has a problem: It might detect that a previous shub xfer has
finished, which was done before SLV0 was programmed with new parameters.
The shub status register is read-to-clear. This isn't in the data sheet
or app note, but it is. By reading the status before enabling the
sensor and after programming SLV0, we can be sure when it becomes set it
has finished the current operation and not a previous one.
Also set the write-once flag before shub init. This causes the shub to
only perform I2C writes once instead of continuously. This was set at
the end of init, so any writes done during it would repeat until the
shub was disabled.
Put a timeout in the code that polls for the sensor hub op complete. It
could possibly poll forever. More importantly, if there is no device
connected to the sensor hub, the lsm6dso does not timeout on the
operation for ~13 seconds. Since the shub init does a probe for devices
on startup, this will happen if shub support is enabled but a lsm6dso
has no sensor hub devices. There could be multiple devices, some with
additional sensors and some without. Initialization of the devices
without additional sensors takes tens of seconds without this timeout
being added.
Add a 300 µs wait after disabling the sensor hub. This is necessary
according to the ST app note AN5192 §7.2.1.
Read the shub status from the main bank register instead of the shub
bank register. This avoids an extra bank switch before and after each
status poll. Actually two bank switches on each side, since the lsm6dso
driver switched banks and then the ST HAL function to get the status
register switches again.
The wait for the shub I2C transaction to finish is not needed when the
shub is enabled at the end of init. We aren't starting a new I2C write
or reading the result of a read.
Signed-off-by: Trent Piepho <trent.piepho@igorinstitute.com>
2022-12-21 04:20:06 -08:00
|
|
|
static int lsm6dso_shub_wait_completed(stmdev_ctx_t *ctx)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_status_master_t status;
|
drivers/sensor: lsm6dso: Fix shub init and configuration failures
There is a flaw with I2C communication to peripherals behind the shub
that causes sporadic failures. Especially calls to configure a device
after the lsm6dso initialization is finished, e.g. to set the ODR, can
fail to work correctly.
Access to shub peripheral registers is done by putting the parameters of
the operation into SLV0 and then waiting for the lsm6dso to perform the
xfer on the shub I2C bus. The lsm6dso does this in sync with the
accelerometer update rate. Once the shub is enabled, it peforms the
xfer repeatedly as the accelermeter is sampled.
The wait has a problem: It might detect that a previous shub xfer has
finished, which was done before SLV0 was programmed with new parameters.
The shub status register is read-to-clear. This isn't in the data sheet
or app note, but it is. By reading the status before enabling the
sensor and after programming SLV0, we can be sure when it becomes set it
has finished the current operation and not a previous one.
Also set the write-once flag before shub init. This causes the shub to
only perform I2C writes once instead of continuously. This was set at
the end of init, so any writes done during it would repeat until the
shub was disabled.
Put a timeout in the code that polls for the sensor hub op complete. It
could possibly poll forever. More importantly, if there is no device
connected to the sensor hub, the lsm6dso does not timeout on the
operation for ~13 seconds. Since the shub init does a probe for devices
on startup, this will happen if shub support is enabled but a lsm6dso
has no sensor hub devices. There could be multiple devices, some with
additional sensors and some without. Initialization of the devices
without additional sensors takes tens of seconds without this timeout
being added.
Add a 300 µs wait after disabling the sensor hub. This is necessary
according to the ST app note AN5192 §7.2.1.
Read the shub status from the main bank register instead of the shub
bank register. This avoids an extra bank switch before and after each
status poll. Actually two bank switches on each side, since the lsm6dso
driver switched banks and then the ST HAL function to get the status
register switches again.
The wait for the shub I2C transaction to finish is not needed when the
shub is enabled at the end of init. We aren't starting a new I2C write
or reading the result of a read.
Signed-off-by: Trent Piepho <trent.piepho@igorinstitute.com>
2022-12-21 04:20:06 -08:00
|
|
|
int tries = 200; /* Should be max ~160 ms, from 2 cycles at slowest ODR 12.5 Hz */
|
2019-02-18 15:17:54 +01:00
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
do {
|
drivers/sensor: lsm6dso: Fix shub init and configuration failures
There is a flaw with I2C communication to peripherals behind the shub
that causes sporadic failures. Especially calls to configure a device
after the lsm6dso initialization is finished, e.g. to set the ODR, can
fail to work correctly.
Access to shub peripheral registers is done by putting the parameters of
the operation into SLV0 and then waiting for the lsm6dso to perform the
xfer on the shub I2C bus. The lsm6dso does this in sync with the
accelerometer update rate. Once the shub is enabled, it peforms the
xfer repeatedly as the accelermeter is sampled.
The wait has a problem: It might detect that a previous shub xfer has
finished, which was done before SLV0 was programmed with new parameters.
The shub status register is read-to-clear. This isn't in the data sheet
or app note, but it is. By reading the status before enabling the
sensor and after programming SLV0, we can be sure when it becomes set it
has finished the current operation and not a previous one.
Also set the write-once flag before shub init. This causes the shub to
only perform I2C writes once instead of continuously. This was set at
the end of init, so any writes done during it would repeat until the
shub was disabled.
Put a timeout in the code that polls for the sensor hub op complete. It
could possibly poll forever. More importantly, if there is no device
connected to the sensor hub, the lsm6dso does not timeout on the
operation for ~13 seconds. Since the shub init does a probe for devices
on startup, this will happen if shub support is enabled but a lsm6dso
has no sensor hub devices. There could be multiple devices, some with
additional sensors and some without. Initialization of the devices
without additional sensors takes tens of seconds without this timeout
being added.
Add a 300 µs wait after disabling the sensor hub. This is necessary
according to the ST app note AN5192 §7.2.1.
Read the shub status from the main bank register instead of the shub
bank register. This avoids an extra bank switch before and after each
status poll. Actually two bank switches on each side, since the lsm6dso
driver switched banks and then the ST HAL function to get the status
register switches again.
The wait for the shub I2C transaction to finish is not needed when the
shub is enabled at the end of init. We aren't starting a new I2C write
or reading the result of a read.
Signed-off-by: Trent Piepho <trent.piepho@igorinstitute.com>
2022-12-21 04:20:06 -08:00
|
|
|
if (!--tries) {
|
|
|
|
LOG_DBG("shub: Timeout waiting for operation to complete");
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
2021-04-30 13:57:42 +02:00
|
|
|
k_msleep(1);
|
drivers/sensor: lsm6dso: Fix shub init and configuration failures
There is a flaw with I2C communication to peripherals behind the shub
that causes sporadic failures. Especially calls to configure a device
after the lsm6dso initialization is finished, e.g. to set the ODR, can
fail to work correctly.
Access to shub peripheral registers is done by putting the parameters of
the operation into SLV0 and then waiting for the lsm6dso to perform the
xfer on the shub I2C bus. The lsm6dso does this in sync with the
accelerometer update rate. Once the shub is enabled, it peforms the
xfer repeatedly as the accelermeter is sampled.
The wait has a problem: It might detect that a previous shub xfer has
finished, which was done before SLV0 was programmed with new parameters.
The shub status register is read-to-clear. This isn't in the data sheet
or app note, but it is. By reading the status before enabling the
sensor and after programming SLV0, we can be sure when it becomes set it
has finished the current operation and not a previous one.
Also set the write-once flag before shub init. This causes the shub to
only perform I2C writes once instead of continuously. This was set at
the end of init, so any writes done during it would repeat until the
shub was disabled.
Put a timeout in the code that polls for the sensor hub op complete. It
could possibly poll forever. More importantly, if there is no device
connected to the sensor hub, the lsm6dso does not timeout on the
operation for ~13 seconds. Since the shub init does a probe for devices
on startup, this will happen if shub support is enabled but a lsm6dso
has no sensor hub devices. There could be multiple devices, some with
additional sensors and some without. Initialization of the devices
without additional sensors takes tens of seconds without this timeout
being added.
Add a 300 µs wait after disabling the sensor hub. This is necessary
according to the ST app note AN5192 §7.2.1.
Read the shub status from the main bank register instead of the shub
bank register. This avoids an extra bank switch before and after each
status poll. Actually two bank switches on each side, since the lsm6dso
driver switched banks and then the ST HAL function to get the status
register switches again.
The wait for the shub I2C transaction to finish is not needed when the
shub is enabled at the end of init. We aren't starting a new I2C write
or reading the result of a read.
Signed-off-by: Trent Piepho <trent.piepho@igorinstitute.com>
2022-12-21 04:20:06 -08:00
|
|
|
lsm6dso_sh_status_mainpage_get(ctx, &status);
|
2021-04-30 13:57:42 +02:00
|
|
|
} while (status.sens_hub_endop == 0);
|
drivers/sensor: lsm6dso: Fix shub init and configuration failures
There is a flaw with I2C communication to peripherals behind the shub
that causes sporadic failures. Especially calls to configure a device
after the lsm6dso initialization is finished, e.g. to set the ODR, can
fail to work correctly.
Access to shub peripheral registers is done by putting the parameters of
the operation into SLV0 and then waiting for the lsm6dso to perform the
xfer on the shub I2C bus. The lsm6dso does this in sync with the
accelerometer update rate. Once the shub is enabled, it peforms the
xfer repeatedly as the accelermeter is sampled.
The wait has a problem: It might detect that a previous shub xfer has
finished, which was done before SLV0 was programmed with new parameters.
The shub status register is read-to-clear. This isn't in the data sheet
or app note, but it is. By reading the status before enabling the
sensor and after programming SLV0, we can be sure when it becomes set it
has finished the current operation and not a previous one.
Also set the write-once flag before shub init. This causes the shub to
only perform I2C writes once instead of continuously. This was set at
the end of init, so any writes done during it would repeat until the
shub was disabled.
Put a timeout in the code that polls for the sensor hub op complete. It
could possibly poll forever. More importantly, if there is no device
connected to the sensor hub, the lsm6dso does not timeout on the
operation for ~13 seconds. Since the shub init does a probe for devices
on startup, this will happen if shub support is enabled but a lsm6dso
has no sensor hub devices. There could be multiple devices, some with
additional sensors and some without. Initialization of the devices
without additional sensors takes tens of seconds without this timeout
being added.
Add a 300 µs wait after disabling the sensor hub. This is necessary
according to the ST app note AN5192 §7.2.1.
Read the shub status from the main bank register instead of the shub
bank register. This avoids an extra bank switch before and after each
status poll. Actually two bank switches on each side, since the lsm6dso
driver switched banks and then the ST HAL function to get the status
register switches again.
The wait for the shub I2C transaction to finish is not needed when the
shub is enabled at the end of init. We aren't starting a new I2C write
or reading the result of a read.
Signed-off-by: Trent Piepho <trent.piepho@igorinstitute.com>
2022-12-21 04:20:06 -08:00
|
|
|
|
|
|
|
return 1;
|
2019-02-18 15:17:54 +01:00
|
|
|
}
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
static inline void lsm6dso_shub_embedded_en(stmdev_ctx_t *ctx, bool on)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2022-12-20 15:51:26 -08:00
|
|
|
lsm6dso_func_cfg_access_t reg = {
|
|
|
|
.reg_access = on ? LSM6DSO_SENSOR_HUB_BANK : LSM6DSO_USER_BANK
|
|
|
|
};
|
|
|
|
|
|
|
|
lsm6dso_write_reg(ctx, LSM6DSO_FUNC_CFG_ACCESS, (uint8_t *)®, 1);
|
2019-02-18 15:17:54 +01:00
|
|
|
}
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsm6dso_shub_read_embedded_regs(stmdev_ctx_t *ctx,
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t reg_addr,
|
|
|
|
uint8_t *value, int len)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_embedded_en(ctx, true);
|
2019-02-18 15:17:54 +01:00
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
if (lsm6dso_read_reg(ctx, reg_addr, value, len) < 0) {
|
2019-02-18 15:17:54 +01:00
|
|
|
LOG_DBG("shub: failed to read external reg: %02x", reg_addr);
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_embedded_en(ctx, false);
|
2019-02-18 15:17:54 +01:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_embedded_en(ctx, false);
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsm6dso_shub_write_embedded_regs(stmdev_ctx_t *ctx,
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t reg_addr,
|
|
|
|
uint8_t *value, uint8_t len)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_embedded_en(ctx, true);
|
2019-02-18 15:17:54 +01:00
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
if (lsm6dso_write_reg(ctx, reg_addr, value, len) < 0) {
|
2019-02-18 15:17:54 +01:00
|
|
|
LOG_DBG("shub: failed to write external reg: %02x", reg_addr);
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_embedded_en(ctx, false);
|
2019-02-18 15:17:54 +01:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_embedded_en(ctx, false);
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
static void lsm6dso_shub_enable(const struct device *dev, uint8_t enable)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2021-04-30 13:57:42 +02:00
|
|
|
const struct lsm6dso_config *cfg = dev->config;
|
|
|
|
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
|
|
|
struct lsm6dso_data *data = dev->data;
|
|
|
|
|
2019-02-18 15:17:54 +01:00
|
|
|
/* Enable Accel @26hz */
|
|
|
|
if (!data->accel_freq) {
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t odr = (enable) ? 2 : 0;
|
2019-02-18 15:17:54 +01:00
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
if (lsm6dso_xl_data_rate_set(ctx, odr) < 0) {
|
2019-02-18 15:17:54 +01:00
|
|
|
LOG_DBG("shub: failed to set XL sampling rate");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
drivers/sensor: lsm6dso: Fix shub init and configuration failures
There is a flaw with I2C communication to peripherals behind the shub
that causes sporadic failures. Especially calls to configure a device
after the lsm6dso initialization is finished, e.g. to set the ODR, can
fail to work correctly.
Access to shub peripheral registers is done by putting the parameters of
the operation into SLV0 and then waiting for the lsm6dso to perform the
xfer on the shub I2C bus. The lsm6dso does this in sync with the
accelerometer update rate. Once the shub is enabled, it peforms the
xfer repeatedly as the accelermeter is sampled.
The wait has a problem: It might detect that a previous shub xfer has
finished, which was done before SLV0 was programmed with new parameters.
The shub status register is read-to-clear. This isn't in the data sheet
or app note, but it is. By reading the status before enabling the
sensor and after programming SLV0, we can be sure when it becomes set it
has finished the current operation and not a previous one.
Also set the write-once flag before shub init. This causes the shub to
only perform I2C writes once instead of continuously. This was set at
the end of init, so any writes done during it would repeat until the
shub was disabled.
Put a timeout in the code that polls for the sensor hub op complete. It
could possibly poll forever. More importantly, if there is no device
connected to the sensor hub, the lsm6dso does not timeout on the
operation for ~13 seconds. Since the shub init does a probe for devices
on startup, this will happen if shub support is enabled but a lsm6dso
has no sensor hub devices. There could be multiple devices, some with
additional sensors and some without. Initialization of the devices
without additional sensors takes tens of seconds without this timeout
being added.
Add a 300 µs wait after disabling the sensor hub. This is necessary
according to the ST app note AN5192 §7.2.1.
Read the shub status from the main bank register instead of the shub
bank register. This avoids an extra bank switch before and after each
status poll. Actually two bank switches on each side, since the lsm6dso
driver switched banks and then the ST HAL function to get the status
register switches again.
The wait for the shub I2C transaction to finish is not needed when the
shub is enabled at the end of init. We aren't starting a new I2C write
or reading the result of a read.
Signed-off-by: Trent Piepho <trent.piepho@igorinstitute.com>
2022-12-21 04:20:06 -08:00
|
|
|
if (enable) {
|
|
|
|
lsm6dso_status_master_t status;
|
|
|
|
|
|
|
|
/* Clear any pending status flags */
|
|
|
|
lsm6dso_sh_status_mainpage_get(ctx, &status);
|
|
|
|
}
|
2019-02-18 15:17:54 +01:00
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
if (lsm6dso_sh_master_set(ctx, enable) < 0) {
|
2019-02-18 15:17:54 +01:00
|
|
|
LOG_DBG("shub: failed to set master on");
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_embedded_en(ctx, false);
|
2019-02-18 15:17:54 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
drivers/sensor: lsm6dso: Fix shub init and configuration failures
There is a flaw with I2C communication to peripherals behind the shub
that causes sporadic failures. Especially calls to configure a device
after the lsm6dso initialization is finished, e.g. to set the ODR, can
fail to work correctly.
Access to shub peripheral registers is done by putting the parameters of
the operation into SLV0 and then waiting for the lsm6dso to perform the
xfer on the shub I2C bus. The lsm6dso does this in sync with the
accelerometer update rate. Once the shub is enabled, it peforms the
xfer repeatedly as the accelermeter is sampled.
The wait has a problem: It might detect that a previous shub xfer has
finished, which was done before SLV0 was programmed with new parameters.
The shub status register is read-to-clear. This isn't in the data sheet
or app note, but it is. By reading the status before enabling the
sensor and after programming SLV0, we can be sure when it becomes set it
has finished the current operation and not a previous one.
Also set the write-once flag before shub init. This causes the shub to
only perform I2C writes once instead of continuously. This was set at
the end of init, so any writes done during it would repeat until the
shub was disabled.
Put a timeout in the code that polls for the sensor hub op complete. It
could possibly poll forever. More importantly, if there is no device
connected to the sensor hub, the lsm6dso does not timeout on the
operation for ~13 seconds. Since the shub init does a probe for devices
on startup, this will happen if shub support is enabled but a lsm6dso
has no sensor hub devices. There could be multiple devices, some with
additional sensors and some without. Initialization of the devices
without additional sensors takes tens of seconds without this timeout
being added.
Add a 300 µs wait after disabling the sensor hub. This is necessary
according to the ST app note AN5192 §7.2.1.
Read the shub status from the main bank register instead of the shub
bank register. This avoids an extra bank switch before and after each
status poll. Actually two bank switches on each side, since the lsm6dso
driver switched banks and then the ST HAL function to get the status
register switches again.
The wait for the shub I2C transaction to finish is not needed when the
shub is enabled at the end of init. We aren't starting a new I2C write
or reading the result of a read.
Signed-off-by: Trent Piepho <trent.piepho@igorinstitute.com>
2022-12-21 04:20:06 -08:00
|
|
|
if (!enable) {
|
|
|
|
/* Necessary per AN5192 §7.2.1 */
|
|
|
|
k_busy_wait(300);
|
|
|
|
}
|
2019-02-18 15:17:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* must be called with master on */
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsm6dso_shub_check_slv0_nack(stmdev_ctx_t *ctx)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t status;
|
2019-02-18 15:17:54 +01:00
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
if (lsm6dso_shub_read_embedded_regs(ctx, LSM6DSO_SHUB_STATUS_MASTER,
|
2019-02-18 15:17:54 +01:00
|
|
|
&status, 1) < 0) {
|
|
|
|
LOG_DBG("shub: error reading embedded reg");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status & (LSM6DSO_SHUB_STATUS_SLV0_NACK)) {
|
|
|
|
LOG_DBG("shub: SLV0 nacked");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* use SLV0 for generic read to slave device
|
|
|
|
*/
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsm6dso_shub_read_slave_reg(const struct device *dev,
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t slv_addr, uint8_t slv_reg,
|
|
|
|
uint8_t *value, uint16_t len)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2021-04-30 13:57:42 +02:00
|
|
|
const struct lsm6dso_config *cfg = dev->config;
|
|
|
|
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t slave[3];
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
slave[0] = (slv_addr << 1) | LSM6DSO_SHUB_SLVX_READ;
|
|
|
|
slave[1] = slv_reg;
|
|
|
|
slave[2] = (len & 0x7);
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
if (lsm6dso_shub_write_embedded_regs(ctx, LSM6DSO_SHUB_SLV0_ADDR,
|
2019-02-18 15:17:54 +01:00
|
|
|
slave, 3) < 0) {
|
|
|
|
LOG_DBG("shub: error writing embedded reg");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
drivers/sensor: lsm6dso: Fix shub init and configuration failures
There is a flaw with I2C communication to peripherals behind the shub
that causes sporadic failures. Especially calls to configure a device
after the lsm6dso initialization is finished, e.g. to set the ODR, can
fail to work correctly.
Access to shub peripheral registers is done by putting the parameters of
the operation into SLV0 and then waiting for the lsm6dso to perform the
xfer on the shub I2C bus. The lsm6dso does this in sync with the
accelerometer update rate. Once the shub is enabled, it peforms the
xfer repeatedly as the accelermeter is sampled.
The wait has a problem: It might detect that a previous shub xfer has
finished, which was done before SLV0 was programmed with new parameters.
The shub status register is read-to-clear. This isn't in the data sheet
or app note, but it is. By reading the status before enabling the
sensor and after programming SLV0, we can be sure when it becomes set it
has finished the current operation and not a previous one.
Also set the write-once flag before shub init. This causes the shub to
only perform I2C writes once instead of continuously. This was set at
the end of init, so any writes done during it would repeat until the
shub was disabled.
Put a timeout in the code that polls for the sensor hub op complete. It
could possibly poll forever. More importantly, if there is no device
connected to the sensor hub, the lsm6dso does not timeout on the
operation for ~13 seconds. Since the shub init does a probe for devices
on startup, this will happen if shub support is enabled but a lsm6dso
has no sensor hub devices. There could be multiple devices, some with
additional sensors and some without. Initialization of the devices
without additional sensors takes tens of seconds without this timeout
being added.
Add a 300 µs wait after disabling the sensor hub. This is necessary
according to the ST app note AN5192 §7.2.1.
Read the shub status from the main bank register instead of the shub
bank register. This avoids an extra bank switch before and after each
status poll. Actually two bank switches on each side, since the lsm6dso
driver switched banks and then the ST HAL function to get the status
register switches again.
The wait for the shub I2C transaction to finish is not needed when the
shub is enabled at the end of init. We aren't starting a new I2C write
or reading the result of a read.
Signed-off-by: Trent Piepho <trent.piepho@igorinstitute.com>
2022-12-21 04:20:06 -08:00
|
|
|
/* turn SH on, wait for shub i2c read to finish */
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_enable(dev, 1);
|
|
|
|
lsm6dso_shub_wait_completed(ctx);
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
/* read data from external slave */
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_embedded_en(ctx, true);
|
|
|
|
if (lsm6dso_read_reg(ctx, LSM6DSO_SHUB_DATA_OUT,
|
2019-02-18 15:17:54 +01:00
|
|
|
value, len) < 0) {
|
|
|
|
LOG_DBG("shub: error reading sensor data");
|
|
|
|
return -EIO;
|
|
|
|
}
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_embedded_en(ctx, false);
|
2019-02-18 15:17:54 +01:00
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
if (lsm6dso_shub_check_slv0_nack(ctx) < 0) {
|
|
|
|
lsm6dso_shub_enable(dev, 0);
|
2019-02-18 15:17:54 +01:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_enable(dev, 0);
|
2019-02-18 15:17:54 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* use SLV0 to configure slave device
|
|
|
|
*/
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsm6dso_shub_write_slave_reg(const struct device *dev,
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t slv_addr, uint8_t slv_reg,
|
|
|
|
uint8_t *value, uint16_t len)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2021-04-30 13:57:42 +02:00
|
|
|
const struct lsm6dso_config *cfg = dev->config;
|
|
|
|
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t slv_cfg[3];
|
|
|
|
uint8_t cnt = 0U;
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
while (cnt < len) {
|
|
|
|
slv_cfg[0] = (slv_addr << 1) & ~LSM6DSO_SHUB_SLVX_READ;
|
|
|
|
slv_cfg[1] = slv_reg + cnt;
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
if (lsm6dso_shub_write_embedded_regs(ctx,
|
2019-02-18 15:17:54 +01:00
|
|
|
LSM6DSO_SHUB_SLV0_ADDR,
|
|
|
|
slv_cfg, 2) < 0) {
|
|
|
|
LOG_DBG("shub: error writing embedded reg");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
slv_cfg[0] = value[cnt];
|
2021-04-30 13:57:42 +02:00
|
|
|
if (lsm6dso_shub_write_embedded_regs(ctx,
|
2019-02-18 15:17:54 +01:00
|
|
|
LSM6DSO_SHUB_SLV0_DATAWRITE,
|
|
|
|
slv_cfg, 1) < 0) {
|
|
|
|
LOG_DBG("shub: error writing embedded reg");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
drivers/sensor: lsm6dso: Fix shub init and configuration failures
There is a flaw with I2C communication to peripherals behind the shub
that causes sporadic failures. Especially calls to configure a device
after the lsm6dso initialization is finished, e.g. to set the ODR, can
fail to work correctly.
Access to shub peripheral registers is done by putting the parameters of
the operation into SLV0 and then waiting for the lsm6dso to perform the
xfer on the shub I2C bus. The lsm6dso does this in sync with the
accelerometer update rate. Once the shub is enabled, it peforms the
xfer repeatedly as the accelermeter is sampled.
The wait has a problem: It might detect that a previous shub xfer has
finished, which was done before SLV0 was programmed with new parameters.
The shub status register is read-to-clear. This isn't in the data sheet
or app note, but it is. By reading the status before enabling the
sensor and after programming SLV0, we can be sure when it becomes set it
has finished the current operation and not a previous one.
Also set the write-once flag before shub init. This causes the shub to
only perform I2C writes once instead of continuously. This was set at
the end of init, so any writes done during it would repeat until the
shub was disabled.
Put a timeout in the code that polls for the sensor hub op complete. It
could possibly poll forever. More importantly, if there is no device
connected to the sensor hub, the lsm6dso does not timeout on the
operation for ~13 seconds. Since the shub init does a probe for devices
on startup, this will happen if shub support is enabled but a lsm6dso
has no sensor hub devices. There could be multiple devices, some with
additional sensors and some without. Initialization of the devices
without additional sensors takes tens of seconds without this timeout
being added.
Add a 300 µs wait after disabling the sensor hub. This is necessary
according to the ST app note AN5192 §7.2.1.
Read the shub status from the main bank register instead of the shub
bank register. This avoids an extra bank switch before and after each
status poll. Actually two bank switches on each side, since the lsm6dso
driver switched banks and then the ST HAL function to get the status
register switches again.
The wait for the shub I2C transaction to finish is not needed when the
shub is enabled at the end of init. We aren't starting a new I2C write
or reading the result of a read.
Signed-off-by: Trent Piepho <trent.piepho@igorinstitute.com>
2022-12-21 04:20:06 -08:00
|
|
|
/* turn SH on, wait for shub i2c write to finish */
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_enable(dev, 1);
|
|
|
|
lsm6dso_shub_wait_completed(ctx);
|
2019-02-18 15:17:54 +01:00
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
if (lsm6dso_shub_check_slv0_nack(ctx) < 0) {
|
|
|
|
lsm6dso_shub_enable(dev, 0);
|
2019-02-18 15:17:54 +01:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_enable(dev, 0);
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
cnt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Put SLV0 in IDLE mode */
|
|
|
|
slv_cfg[0] = 0x7;
|
|
|
|
slv_cfg[1] = 0x0;
|
|
|
|
slv_cfg[2] = 0x0;
|
2021-04-30 13:57:42 +02:00
|
|
|
if (lsm6dso_shub_write_embedded_regs(ctx, LSM6DSO_SHUB_SLV0_ADDR,
|
2019-02-18 15:17:54 +01:00
|
|
|
slv_cfg, 3) < 0) {
|
|
|
|
LOG_DBG("shub: error writing embedded reg");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SLAVEs configurations:
|
|
|
|
*
|
|
|
|
* - SLAVE 0: used for configuring all slave devices
|
|
|
|
* - SLAVE 1: used as data read channel for external slave device #1
|
|
|
|
* - SLAVE 2: used as data read channel for external slave device #2
|
|
|
|
* - SLAVE 3: used for generic reads while data channel is enabled
|
|
|
|
*/
|
2021-04-30 13:57:42 +02:00
|
|
|
static int lsm6dso_shub_set_data_channel(const struct device *dev)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2021-05-07 15:41:35 +02:00
|
|
|
struct lsm6dso_data *data = dev->data;
|
2021-04-30 13:57:42 +02:00
|
|
|
const struct lsm6dso_config *cfg = dev->config;
|
|
|
|
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t n, i, slv_cfg[6];
|
2019-02-18 15:17:54 +01:00
|
|
|
struct lsm6dso_shub_slist *sp;
|
|
|
|
|
|
|
|
/* Set data channel for slave devices */
|
2021-05-07 15:41:35 +02:00
|
|
|
for (n = 0; n < data->num_ext_dev; n++) {
|
|
|
|
sp = &lsm6dso_shub_slist[data->shub_ext[n]];
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
i = n * 3;
|
|
|
|
slv_cfg[i] = (sp->ext_i2c_addr << 1) | LSM6DSO_SHUB_SLVX_READ;
|
|
|
|
slv_cfg[i + 1] = sp->out_data_addr;
|
|
|
|
slv_cfg[i + 2] = sp->out_data_len;
|
|
|
|
}
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
if (lsm6dso_shub_write_embedded_regs(ctx,
|
2019-02-18 15:17:54 +01:00
|
|
|
LSM6DSO_SHUB_SLV1_ADDR,
|
|
|
|
slv_cfg, n*3) < 0) {
|
|
|
|
LOG_DBG("shub: error writing embedded reg");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Configure the master */
|
|
|
|
lsm6dso_aux_sens_on_t aux = LSM6DSO_SLV_0_1_2;
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
if (lsm6dso_sh_slave_connected_set(ctx, aux) < 0) {
|
2019-02-18 15:17:54 +01:00
|
|
|
LOG_DBG("shub: error setting aux sensors");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
drivers/sensor: lsm6dso: Fix shub init and configuration failures
There is a flaw with I2C communication to peripherals behind the shub
that causes sporadic failures. Especially calls to configure a device
after the lsm6dso initialization is finished, e.g. to set the ODR, can
fail to work correctly.
Access to shub peripheral registers is done by putting the parameters of
the operation into SLV0 and then waiting for the lsm6dso to perform the
xfer on the shub I2C bus. The lsm6dso does this in sync with the
accelerometer update rate. Once the shub is enabled, it peforms the
xfer repeatedly as the accelermeter is sampled.
The wait has a problem: It might detect that a previous shub xfer has
finished, which was done before SLV0 was programmed with new parameters.
The shub status register is read-to-clear. This isn't in the data sheet
or app note, but it is. By reading the status before enabling the
sensor and after programming SLV0, we can be sure when it becomes set it
has finished the current operation and not a previous one.
Also set the write-once flag before shub init. This causes the shub to
only perform I2C writes once instead of continuously. This was set at
the end of init, so any writes done during it would repeat until the
shub was disabled.
Put a timeout in the code that polls for the sensor hub op complete. It
could possibly poll forever. More importantly, if there is no device
connected to the sensor hub, the lsm6dso does not timeout on the
operation for ~13 seconds. Since the shub init does a probe for devices
on startup, this will happen if shub support is enabled but a lsm6dso
has no sensor hub devices. There could be multiple devices, some with
additional sensors and some without. Initialization of the devices
without additional sensors takes tens of seconds without this timeout
being added.
Add a 300 µs wait after disabling the sensor hub. This is necessary
according to the ST app note AN5192 §7.2.1.
Read the shub status from the main bank register instead of the shub
bank register. This avoids an extra bank switch before and after each
status poll. Actually two bank switches on each side, since the lsm6dso
driver switched banks and then the ST HAL function to get the status
register switches again.
The wait for the shub I2C transaction to finish is not needed when the
shub is enabled at the end of init. We aren't starting a new I2C write
or reading the result of a read.
Signed-off-by: Trent Piepho <trent.piepho@igorinstitute.com>
2022-12-21 04:20:06 -08:00
|
|
|
/* turn SH on, no need to wait for 1st shub i2c read, if any, to complete */
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_enable(dev, 1);
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-05-07 15:41:35 +02:00
|
|
|
int lsm6dso_shub_get_idx(const struct device *dev, enum sensor_channel type)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t n;
|
2021-05-07 15:41:35 +02:00
|
|
|
struct lsm6dso_data *data = dev->data;
|
2019-02-18 15:17:54 +01:00
|
|
|
struct lsm6dso_shub_slist *sp;
|
|
|
|
|
2021-05-07 15:41:35 +02:00
|
|
|
for (n = 0; n < data->num_ext_dev; n++) {
|
|
|
|
sp = &lsm6dso_shub_slist[data->shub_ext[n]];
|
2019-02-18 15:17:54 +01:00
|
|
|
|
2022-07-06 07:34:50 -04:00
|
|
|
if (sp->type == type) {
|
2019-02-18 15:17:54 +01:00
|
|
|
return n;
|
2022-07-06 07:34:50 -04:00
|
|
|
}
|
2019-02-18 15:17:54 +01:00
|
|
|
}
|
|
|
|
|
2021-05-07 15:41:35 +02:00
|
|
|
LOG_ERR("shub: dev %s type %d not supported", dev->name, type);
|
2019-02-18 15:17:54 +01:00
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
int lsm6dso_shub_fetch_external_devs(const struct device *dev)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t n;
|
2021-04-30 13:57:42 +02:00
|
|
|
const struct lsm6dso_config *cfg = dev->config;
|
|
|
|
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
2020-05-28 21:23:02 +02:00
|
|
|
struct lsm6dso_data *data = dev->data;
|
2019-02-18 15:17:54 +01:00
|
|
|
struct lsm6dso_shub_slist *sp;
|
|
|
|
|
|
|
|
/* read data from external slave */
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_embedded_en(ctx, true);
|
2019-02-18 15:17:54 +01:00
|
|
|
|
2021-05-07 15:41:35 +02:00
|
|
|
for (n = 0; n < data->num_ext_dev; n++) {
|
|
|
|
sp = &lsm6dso_shub_slist[data->shub_ext[n]];
|
2019-02-18 15:17:54 +01:00
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
if (lsm6dso_read_reg(ctx, sp->sh_out_reg,
|
2019-02-18 15:17:54 +01:00
|
|
|
data->ext_data[n], sp->out_data_len) < 0) {
|
|
|
|
LOG_DBG("shub: failed to read sample");
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_embedded_en(ctx, false);
|
2019-02-18 15:17:54 +01:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_embedded_en(ctx, false);
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
int lsm6dso_shub_config(const struct device *dev, enum sensor_channel chan,
|
2019-02-18 15:17:54 +01:00
|
|
|
enum sensor_attribute attr,
|
|
|
|
const struct sensor_value *val)
|
|
|
|
{
|
2021-05-07 15:41:35 +02:00
|
|
|
struct lsm6dso_data *data = dev->data;
|
2019-02-18 15:17:54 +01:00
|
|
|
struct lsm6dso_shub_slist *sp = NULL;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t n;
|
2019-02-18 15:17:54 +01:00
|
|
|
|
2021-05-07 15:41:35 +02:00
|
|
|
for (n = 0; n < data->num_ext_dev; n++) {
|
|
|
|
sp = &lsm6dso_shub_slist[data->shub_ext[n]];
|
2019-02-18 15:17:54 +01:00
|
|
|
|
2022-07-06 07:34:50 -04:00
|
|
|
if (sp->type == chan) {
|
2019-02-18 15:17:54 +01:00
|
|
|
break;
|
2022-07-06 07:34:50 -04:00
|
|
|
}
|
2019-02-18 15:17:54 +01:00
|
|
|
}
|
|
|
|
|
2021-05-07 15:41:35 +02:00
|
|
|
if (n == data->num_ext_dev) {
|
|
|
|
LOG_DBG("shub: %s chan %d not supported", dev->name, chan);
|
2019-02-18 15:17:54 +01:00
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sp == NULL || sp->dev_conf == NULL) {
|
|
|
|
LOG_DBG("shub: chan not configurable");
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
return sp->dev_conf(dev, sp->ext_i2c_addr, chan, attr, val);
|
2019-02-18 15:17:54 +01:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
int lsm6dso_shub_init(const struct device *dev)
|
2019-02-18 15:17:54 +01:00
|
|
|
{
|
2021-05-07 15:41:35 +02:00
|
|
|
struct lsm6dso_data *data = dev->data;
|
drivers/sensor: lsm6dso: Fix shub init and configuration failures
There is a flaw with I2C communication to peripherals behind the shub
that causes sporadic failures. Especially calls to configure a device
after the lsm6dso initialization is finished, e.g. to set the ODR, can
fail to work correctly.
Access to shub peripheral registers is done by putting the parameters of
the operation into SLV0 and then waiting for the lsm6dso to perform the
xfer on the shub I2C bus. The lsm6dso does this in sync with the
accelerometer update rate. Once the shub is enabled, it peforms the
xfer repeatedly as the accelermeter is sampled.
The wait has a problem: It might detect that a previous shub xfer has
finished, which was done before SLV0 was programmed with new parameters.
The shub status register is read-to-clear. This isn't in the data sheet
or app note, but it is. By reading the status before enabling the
sensor and after programming SLV0, we can be sure when it becomes set it
has finished the current operation and not a previous one.
Also set the write-once flag before shub init. This causes the shub to
only perform I2C writes once instead of continuously. This was set at
the end of init, so any writes done during it would repeat until the
shub was disabled.
Put a timeout in the code that polls for the sensor hub op complete. It
could possibly poll forever. More importantly, if there is no device
connected to the sensor hub, the lsm6dso does not timeout on the
operation for ~13 seconds. Since the shub init does a probe for devices
on startup, this will happen if shub support is enabled but a lsm6dso
has no sensor hub devices. There could be multiple devices, some with
additional sensors and some without. Initialization of the devices
without additional sensors takes tens of seconds without this timeout
being added.
Add a 300 µs wait after disabling the sensor hub. This is necessary
according to the ST app note AN5192 §7.2.1.
Read the shub status from the main bank register instead of the shub
bank register. This avoids an extra bank switch before and after each
status poll. Actually two bank switches on each side, since the lsm6dso
driver switched banks and then the ST HAL function to get the status
register switches again.
The wait for the shub I2C transaction to finish is not needed when the
shub is enabled at the end of init. We aren't starting a new I2C write
or reading the result of a read.
Signed-off-by: Trent Piepho <trent.piepho@igorinstitute.com>
2022-12-21 04:20:06 -08:00
|
|
|
const struct lsm6dso_config *cfg = dev->config;
|
|
|
|
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t i, n = 0, regn;
|
|
|
|
uint8_t chip_id;
|
2019-02-18 15:17:54 +01:00
|
|
|
struct lsm6dso_shub_slist *sp;
|
|
|
|
|
2021-05-07 15:41:35 +02:00
|
|
|
LOG_INF("shub: start sensorhub for %s", dev->name);
|
drivers/sensor: lsm6dso: Fix shub init and configuration failures
There is a flaw with I2C communication to peripherals behind the shub
that causes sporadic failures. Especially calls to configure a device
after the lsm6dso initialization is finished, e.g. to set the ODR, can
fail to work correctly.
Access to shub peripheral registers is done by putting the parameters of
the operation into SLV0 and then waiting for the lsm6dso to perform the
xfer on the shub I2C bus. The lsm6dso does this in sync with the
accelerometer update rate. Once the shub is enabled, it peforms the
xfer repeatedly as the accelermeter is sampled.
The wait has a problem: It might detect that a previous shub xfer has
finished, which was done before SLV0 was programmed with new parameters.
The shub status register is read-to-clear. This isn't in the data sheet
or app note, but it is. By reading the status before enabling the
sensor and after programming SLV0, we can be sure when it becomes set it
has finished the current operation and not a previous one.
Also set the write-once flag before shub init. This causes the shub to
only perform I2C writes once instead of continuously. This was set at
the end of init, so any writes done during it would repeat until the
shub was disabled.
Put a timeout in the code that polls for the sensor hub op complete. It
could possibly poll forever. More importantly, if there is no device
connected to the sensor hub, the lsm6dso does not timeout on the
operation for ~13 seconds. Since the shub init does a probe for devices
on startup, this will happen if shub support is enabled but a lsm6dso
has no sensor hub devices. There could be multiple devices, some with
additional sensors and some without. Initialization of the devices
without additional sensors takes tens of seconds without this timeout
being added.
Add a 300 µs wait after disabling the sensor hub. This is necessary
according to the ST app note AN5192 §7.2.1.
Read the shub status from the main bank register instead of the shub
bank register. This avoids an extra bank switch before and after each
status poll. Actually two bank switches on each side, since the lsm6dso
driver switched banks and then the ST HAL function to get the status
register switches again.
The wait for the shub I2C transaction to finish is not needed when the
shub is enabled at the end of init. We aren't starting a new I2C write
or reading the result of a read.
Signed-off-by: Trent Piepho <trent.piepho@igorinstitute.com>
2022-12-21 04:20:06 -08:00
|
|
|
|
|
|
|
/* This must be set or lsm6dso_shub_write_slave_reg() will repeatedly write the same reg */
|
|
|
|
if (lsm6dso_sh_write_mode_set(ctx, LSM6DSO_ONLY_FIRST_CYCLE) < 0) {
|
|
|
|
LOG_DBG("shub: error setting write once");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2019-02-18 15:17:54 +01:00
|
|
|
for (n = 0; n < ARRAY_SIZE(lsm6dso_shub_slist); n++) {
|
2022-07-06 07:34:50 -04:00
|
|
|
if (data->num_ext_dev >= LSM6DSO_SHUB_MAX_NUM_SLVS) {
|
2019-02-18 15:17:54 +01:00
|
|
|
break;
|
2022-07-06 07:34:50 -04:00
|
|
|
}
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
chip_id = 0;
|
|
|
|
sp = &lsm6dso_shub_slist[n];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The external sensor may have different I2C address.
|
|
|
|
* So, try them one by one until we read the correct
|
|
|
|
* chip ID.
|
|
|
|
*/
|
|
|
|
for (i = 0U; i < ARRAY_SIZE(sp->i2c_addr); i++) {
|
2021-04-30 13:57:42 +02:00
|
|
|
if (lsm6dso_shub_read_slave_reg(dev,
|
2019-02-18 15:17:54 +01:00
|
|
|
sp->i2c_addr[i],
|
|
|
|
sp->wai_addr,
|
|
|
|
&chip_id, 1) < 0) {
|
|
|
|
LOG_DBG("shub: failed reading chip id");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (chip_id == sp->wai_val) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i >= ARRAY_SIZE(sp->i2c_addr)) {
|
|
|
|
LOG_DBG("shub: invalid chip id 0x%x", chip_id);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
LOG_INF("shub: Ext Device Chip Id: %02x", chip_id);
|
|
|
|
sp->ext_i2c_addr = sp->i2c_addr[i];
|
|
|
|
|
2021-05-07 15:41:35 +02:00
|
|
|
data->shub_ext[data->num_ext_dev++] = n;
|
2019-02-18 15:17:54 +01:00
|
|
|
}
|
|
|
|
|
2021-05-07 15:41:35 +02:00
|
|
|
LOG_DBG("shub: dev %s - num_ext_dev %d", dev->name, data->num_ext_dev);
|
|
|
|
if (data->num_ext_dev == 0) {
|
2019-02-18 15:17:54 +01:00
|
|
|
LOG_ERR("shub: no slave devices found");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* init external devices */
|
2021-05-07 15:41:35 +02:00
|
|
|
for (n = 0, regn = 0; n < data->num_ext_dev; n++) {
|
|
|
|
sp = &lsm6dso_shub_slist[data->shub_ext[n]];
|
2019-02-18 15:17:54 +01:00
|
|
|
sp->sh_out_reg = LSM6DSO_SHUB_DATA_OUT + regn;
|
|
|
|
regn += sp->out_data_len;
|
2021-04-30 13:57:42 +02:00
|
|
|
sp->dev_init(dev, sp->ext_i2c_addr);
|
2019-02-18 15:17:54 +01:00
|
|
|
}
|
|
|
|
|
2021-04-30 13:57:42 +02:00
|
|
|
lsm6dso_shub_set_data_channel(dev);
|
2019-02-18 15:17:54 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|