/* Copyright 2018 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ /* * Sensor Hub Driver for LSM6DSM acce/gyro module to enable connecting * external sensors like magnetometer */ #include "console.h" #include "driver/accelgyro_lsm6dsm.h" #include "driver/sensorhub_lsm6dsm.h" #include "driver/stm_mems_common.h" #define CPRINTF(format, args...) cprintf(CC_ACCEL, format, ## args) static int set_reg_bit_field(const struct motion_sensor_t *s, uint8_t reg, uint8_t bit_field) { int tmp; int ret; ret = st_raw_read8(s->port, s->i2c_spi_addr_flags, reg, &tmp); if (ret != EC_SUCCESS) return ret; tmp |= bit_field; return st_raw_write8(s->port, s->i2c_spi_addr_flags, reg, tmp); } static int clear_reg_bit_field(const struct motion_sensor_t *s, uint8_t reg, uint8_t bit_field) { int tmp; int ret; ret = st_raw_read8(s->port, s->i2c_spi_addr_flags, reg, &tmp); if (ret != EC_SUCCESS) return ret; tmp &= ~(bit_field); return st_raw_write8(s->port, s->i2c_spi_addr_flags, reg, tmp); } static inline int enable_sensorhub_func(const struct motion_sensor_t *s) { return set_reg_bit_field(s, LSM6DSM_CTRL10_ADDR, LSM6DSM_EMBED_FUNC_EN); } static inline int disable_sensorhub_func(const struct motion_sensor_t *s) { return clear_reg_bit_field(s, LSM6DSM_CTRL10_ADDR, LSM6DSM_EMBED_FUNC_EN); } /* * Sensor hub includes embedded register banks associated with external * sensors. 4 external sensor slaves can be attached to the sensor hub * and hence 4 such register banks exist. The access to them are disabled * by default. Below 2 helper functions help enable/disable access to those * register banks. */ static inline int enable_ereg_bank_acc(const struct motion_sensor_t *s) { return set_reg_bit_field(s, LSM6DSM_FUNC_CFG_ACC_ADDR, LSM6DSM_FUNC_CFG_EN); } static inline int disable_ereg_bank_acc(const struct motion_sensor_t *s) { return clear_reg_bit_field(s, LSM6DSM_FUNC_CFG_ACC_ADDR, LSM6DSM_FUNC_CFG_EN); } static inline int enable_aux_i2c_master(const struct motion_sensor_t *s) { return set_reg_bit_field(s, LSM6DSM_MASTER_CFG_ADDR, LSM6DSM_I2C_MASTER_ON); } static inline int disable_aux_i2c_master(const struct motion_sensor_t *s) { return clear_reg_bit_field(s, LSM6DSM_MASTER_CFG_ADDR, LSM6DSM_I2C_MASTER_ON); } static inline int restore_master_cfg(const struct motion_sensor_t *s, int cache) { return st_raw_write8(s->port, s->i2c_spi_addr_flags, LSM6DSM_MASTER_CFG_ADDR, cache); } static int enable_i2c_pass_through(const struct motion_sensor_t *s, int *cache) { int ret; ret = st_raw_read8(s->port, s->i2c_spi_addr_flags, LSM6DSM_MASTER_CFG_ADDR, cache); if (ret != EC_SUCCESS) { CPRINTF("%s: %s type:0x%x MCR error ret: %d\n", __func__, s->name, s->type, ret); return ret; } /* * Fake set sensor hub to external trigger event and wait for 10ms. * Wait is for any pending bus activity(probably read) to settle down * so that there is no bus contention. */ ret = st_raw_write8(s->port, s->i2c_spi_addr_flags, LSM6DSM_MASTER_CFG_ADDR, *cache | LSM6DSM_EXT_TRIGGER_EN); if (ret != EC_SUCCESS) { CPRINTF("%s: %s type:0x%x MCETEN error ret: %d\n", __func__, s->name, s->type, ret); return ret; } msleep(10); ret = st_raw_write8(s->port, s->i2c_spi_addr_flags, LSM6DSM_MASTER_CFG_ADDR, *cache & ~(LSM6DSM_EXT_TRIGGER_EN | LSM6DSM_I2C_MASTER_ON)); if (ret != EC_SUCCESS) { CPRINTF("%s: %s type:0x%x MCC error ret: %d\n", __func__, s->name, s->type, ret); restore_master_cfg(s, *cache); return ret; } return st_raw_write8(s->port, s->i2c_spi_addr_flags, LSM6DSM_MASTER_CFG_ADDR, LSM6DSM_I2C_PASS_THRU_MODE); } static inline int power_down_accel(const struct motion_sensor_t *s, int *cache) { int ret; ret = st_raw_read8(s->port, s->i2c_spi_addr_flags, LSM6DSM_CTRL1_ADDR, cache); if (ret != EC_SUCCESS) { CPRINTF("%s: %s type:0x%x CTRL1R error ret: %d\n", __func__, s->name, s->type, ret); return ret; } return st_raw_write8(s->port, s->i2c_spi_addr_flags, LSM6DSM_CTRL1_ADDR, *cache & ~LSM6DSM_XL_ODR_MASK); } static inline int restore_ctrl1(const struct motion_sensor_t *s, int cache) { return st_raw_write8(s->port, s->i2c_spi_addr_flags, LSM6DSM_CTRL1_ADDR, cache); } static int config_slv0_read(const struct motion_sensor_t *s, const uint16_t slv_addr_flags, uint16_t reg, uint8_t len) { int ret; uint16_t addr_8bit = I2C_STRIP_FLAGS(slv_addr_flags) << 1; ret = st_raw_write8(s->port, s->i2c_spi_addr_flags, LSM6DSM_SLV0_ADD_ADDR, (addr_8bit | LSM6DSM_SLV0_RD_BIT)); if (ret != EC_SUCCESS) { CPRINTF("%s: %s type:0x%x SA error ret: %d\n", __func__, s->name, s->type, ret); return ret; } ret = st_raw_write8(s->port, s->i2c_spi_addr_flags, LSM6DSM_SLV0_SUBADD_ADDR, reg); if (ret != EC_SUCCESS) { CPRINTF("%s: %s type:0x%x RA error ret: %d\n", __func__, s->name, s->type, ret); return ret; } /* * No decimation for external sensor 0, * Number of sensors connected to external sensor hub 1 */ ret = st_raw_write8(s->port, s->i2c_spi_addr_flags, LSM6DSM_SLV0_CONFIG_ADDR, (len & LSM6DSM_SLV0_NUM_OPS_MASK)); if (ret != EC_SUCCESS) { CPRINTF("%s: %s type:0x%x CFG error ret: %d\n", __func__, s->name, s->type, ret); return ret; } return EC_SUCCESS; } int sensorhub_config_ext_reg(const struct motion_sensor_t *s, const uint16_t slv_addr_flags, uint8_t reg, uint8_t val) { int ret; int tmp; ret = enable_i2c_pass_through(s, &tmp); if (ret != EC_SUCCESS) { CPRINTF("%s: %s type:0x%x ENI2C error ret: %d\n", __func__, s->name, s->type, ret); return ret; } ret = st_raw_write8(s->port, slv_addr_flags, reg, val); restore_master_cfg(s, tmp); return ret; } int sensorhub_config_slv0_read(const struct motion_sensor_t *s, uint16_t slv_addr_flags, uint8_t reg, int len) { int tmp_xl_cfg; int ret; if (len <= 0 || len > OUT_XYZ_SIZE) { CPRINTF("%s: %s type:0x%x Invalid length: %d\n", __func__, s->name, s->type, len); return EC_ERROR_INVAL; } ret = power_down_accel(s, &tmp_xl_cfg); if (ret != EC_SUCCESS) { CPRINTF("%s: %s type:0x%x PDXL error ret: %d\n", __func__, s->name, s->type, ret); return ret; } ret = enable_ereg_bank_acc(s); if (ret != EC_SUCCESS) { CPRINTF("%s: %s type:0x%x ENERB error ret: %d\n", __func__, s->name, s->type, ret); goto out_restore_ctrl1; } ret = config_slv0_read(s, slv_addr_flags, reg, len); disable_ereg_bank_acc(s); if (ret != EC_SUCCESS) { CPRINTF("%s: %s type:0x%x CS0R error ret: %d\n", __func__, s->name, s->type, ret); goto out_restore_ctrl1; } ret = enable_sensorhub_func(s); if (ret != EC_SUCCESS) { CPRINTF("%s: %s type:0x%x ENSH error ret: %d\n", __func__, s->name, s->type, ret); goto out_restore_ctrl1; } ret = enable_aux_i2c_master(s); if (ret != EC_SUCCESS) { CPRINTF("%s: %s type:0x%x ENI2CM error ret: %d\n", __func__, s->name, s->type, ret); disable_sensorhub_func(s); } out_restore_ctrl1: restore_ctrl1(s, tmp_xl_cfg); return ret; } int sensorhub_slv0_data_read(const struct motion_sensor_t *s, uint8_t *raw) { int ret; /* * Accel/Gyro is already reading slave 0 data into the sensorhub1 * register as soon as the accel is in power-up mode. So return the * contents of that register. */ ret = st_raw_read_n_noinc(s->port, s->i2c_spi_addr_flags, LSM6DSM_SENSORHUB1_REG, raw, OUT_XYZ_SIZE); if (ret != EC_SUCCESS) { CPRINTF("%s: %s type:0x%x SH1R error ret: %d\n", __func__, s->name, s->type, ret); return ret; } return EC_SUCCESS; } int sensorhub_check_and_rst(const struct motion_sensor_t *s, const uint16_t slv_addr_flags, uint8_t whoami_reg, uint8_t whoami_val, uint8_t rst_reg, uint8_t rst_val) { int ret, tmp; int tmp_master_cfg; ret = enable_i2c_pass_through(s, &tmp_master_cfg); if (ret != EC_SUCCESS) { CPRINTF("%s: %s type:0x%x ENI2C error ret: %d\n", __func__, s->name, s->type, ret); return ret; } ret = st_raw_read8(s->port, slv_addr_flags, whoami_reg, &tmp); if (ret != EC_SUCCESS) { CPRINTF("%s: %s type:0x%x WAIR error ret: %d\n", __func__, s->name, s->type, ret); goto err_restore_master_cfg; } if (tmp != whoami_val) { CPRINTF("%s: %s type:0x%x WAIC error ret: %d\n", __func__, s->name, s->type, ret); ret = EC_ERROR_UNKNOWN; goto err_restore_master_cfg; } ret = st_raw_write8(s->port, slv_addr_flags, rst_reg, rst_val); err_restore_master_cfg: restore_master_cfg(s, tmp_master_cfg); return ret; }