diff options
Diffstat (limited to 'driver/accelgyro_lsm6dsm.c')
-rw-r--r-- | driver/accelgyro_lsm6dsm.c | 852 |
1 files changed, 0 insertions, 852 deletions
diff --git a/driver/accelgyro_lsm6dsm.c b/driver/accelgyro_lsm6dsm.c deleted file mode 100644 index 3d0c717a9b..0000000000 --- a/driver/accelgyro_lsm6dsm.c +++ /dev/null @@ -1,852 +0,0 @@ -/* Copyright 2016 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. - */ - -/** - * LSM6DSx (x is L/M/3) accelerometer and gyro module for Chrome EC - * 3D digital accelerometer & 3D digital gyroscope - * This driver supports both devices LSM6DSM and LSM6DSL - */ - -#include "driver/accelgyro_lsm6dsm.h" -#include "driver/mag_lis2mdl.h" -#include "hooks.h" -#include "hwtimer.h" -#include "mag_cal.h" -#include "math_util.h" -#include "motion_sense_fifo.h" -#include "queue.h" -#include "task.h" -#include "timer.h" - -#define CPUTS(outstr) cputs(CC_ACCEL, outstr) -#define CPRINTF(format, args...) cprintf(CC_ACCEL, format, ## args) -#define CPRINTS(format, args...) cprints(CC_ACCEL, format, ## args) - -#define IS_FSTS_EMPTY(s) ((s).len & LSM6DSM_FIFO_EMPTY) - -#ifndef FIFO_READ_LEN -#define FIFO_READ_LEN 0 -#endif - -#ifndef CONFIG_ACCEL_LSM6DSM_INT_EVENT -#define CONFIG_ACCEL_LSM6DSM_INT_EVENT 0 -#endif - -static volatile uint32_t last_interrupt_timestamp; - -/** - * Resets the lsm6dsm load fifo sensor states to the given timestamp. This - * should be called at the start of the fifo read sequence. - * - * @param s Pointer to the first sensor in the lsm6dsm (accelerometer). - * @param ts The timestamp to use for the interrupt timestamp. - */ -__maybe_unused static void reset_load_fifo_sensor_state( - struct motion_sensor_t *s, uint32_t ts) -{ - int i; - struct lsm6dsm_accel_fifo_state *fifo_state = - LSM6DSM_GET_DATA(s)->accel_fifo_state; - - for (i = 0; i < FIFO_DEV_NUM; i++) { - fifo_state->load_fifo_sensor_state[i].int_timestamp = ts; - fifo_state->load_fifo_sensor_state[i].sample_count = 0; - } -} - -/** - * Gets the dev_fifo enum value for a given sensor. - * - * @param s Pointer to the sensor in question. - * @return the dev_fifo enum value corresponding to the sensor. - */ -static inline enum dev_fifo get_fifo_type(const struct motion_sensor_t *s) -{ - static enum dev_fifo map[] = { - FIFO_DEV_ACCEL, - FIFO_DEV_GYRO, -#ifdef CONFIG_LSM6DSM_SEC_I2C - FIFO_DEV_MAG -#endif /* CONFIG_LSM6DSM_SEC_I2C */ - }; - return map[s->type]; -} - -/** - * Gets the sensor type associated with the dev_fifo enum. This type can be used - * to get the sensor number by using it as an offset from the first sensor in - * the lsm6dsm (the accelerometer). - * - * @param fifo_type The dev_fifo enum in question. - * @return the type of sensor represented by the fifo type. - */ -static inline uint8_t get_sensor_type(enum dev_fifo fifo_type) -{ - static uint8_t map[] = { - MOTIONSENSE_TYPE_GYRO, - MOTIONSENSE_TYPE_ACCEL, - MOTIONSENSE_TYPE_MAG, - }; - return map[fifo_type]; -} - -/** - * @return output base register for sensor - */ -static inline int get_xyz_reg(enum motionsensor_type type) -{ - return LSM6DSM_ACCEL_OUT_X_L_ADDR - - (LSM6DSM_ACCEL_OUT_X_L_ADDR - LSM6DSM_GYRO_OUT_X_L_ADDR) * type; -} - -/** - * Configure interrupt int 1 to fire handler for: - * - * FIFO threshold on watermark - * - * @accel: Motion sensor pointer to accelerometer. - */ -__maybe_unused static int config_interrupt(const struct motion_sensor_t *accel) -{ - int ret = EC_SUCCESS; - int int1_ctrl_val; - - ret = st_raw_read8(accel->port, accel->i2c_spi_addr_flags, - LSM6DSM_INT1_CTRL, &int1_ctrl_val); - if (ret != EC_SUCCESS) - return ret; - - if (IS_ENABLED(CONFIG_ACCEL_FIFO)) { - /* As soon as one sample is ready, trigger an interrupt. */ - ret = st_raw_write8(accel->port, accel->i2c_spi_addr_flags, - LSM6DSM_FIFO_CTRL1_ADDR, - OUT_XYZ_SIZE / sizeof(uint16_t)); - if (ret != EC_SUCCESS) - return ret; - int1_ctrl_val |= LSM6DSM_INT_FIFO_TH | LSM6DSM_INT_FIFO_OVR | - LSM6DSM_INT_FIFO_FULL; - } - - return st_raw_write8(accel->port, accel->i2c_spi_addr_flags, - LSM6DSM_INT1_CTRL, int1_ctrl_val); -} - - -/** - * fifo_disable - set fifo mode - * @accel: Motion sensor pointer: must be MOTIONSENSE_TYPE_ACCEL. - * @fmode: BYPASS or CONTINUOUS - */ -static int fifo_disable(const struct motion_sensor_t *accel) -{ - return st_raw_write8(accel->port, accel->i2c_spi_addr_flags, - LSM6DSM_FIFO_CTRL5_ADDR, 0x00); -} - -/** - * fifo_reset_pattern: called at each new FIFO pattern. - */ -static void fifo_reset_pattern(struct lsm6dsm_data *private) -{ - /* The fifo is ready to run. */ - memcpy(&private->accel_fifo_state->current, - &private->accel_fifo_state->config, - sizeof(struct lsm6dsm_fifo_data)); - private->accel_fifo_state->next_in_pattern = FIFO_DEV_INVALID; -} - -/** - * fifo_enable - Configure internal FIFO parameters - * @accel must be the accelerometer sensor. - * - * Configure FIFO decimators to have every time the right pattern - * with acc/gyro - */ -static int fifo_enable(const struct motion_sensor_t *accel) -{ - const struct motion_sensor_t *s; - int err, i, rate; - uint8_t decimators[FIFO_DEV_NUM] = { 0 }; - unsigned int odrs[FIFO_DEV_NUM]; - unsigned int min_odr = LSM6DSM_ODR_MAX_VAL; - unsigned int max_odr = 0; - uint8_t odr_reg_val; - struct lsm6dsm_data *private = LSM6DSM_GET_DATA(accel); - struct lsm6dsm_accel_fifo_state *fifo_state = private->accel_fifo_state; - /* In FIFO sensors are mapped in a different way. */ - uint8_t agm_maps[] = { - MOTIONSENSE_TYPE_GYRO, - MOTIONSENSE_TYPE_ACCEL, - MOTIONSENSE_TYPE_MAG, - }; - - - /* Search for min and max odr values for acc, gyro. */ - for (i = FIFO_DEV_GYRO; i < FIFO_DEV_NUM; i++) { - /* Check if sensor enabled with ODR. */ - s = accel + agm_maps[i]; - rate = s->drv->get_data_rate(s); - if (rate > 0) { - min_odr = MIN(min_odr, rate); - max_odr = MAX(max_odr, rate); - } - odrs[i] = rate; - } - - if (max_odr == 0) { - /* Leave FIFO disabled. */ - return EC_SUCCESS; - } - - /* FIFO ODR must be set before the decimation factors */ - odr_reg_val = LSM6DSM_ODR_TO_REG(max_odr) << - LSM6DSM_FIFO_CTRL5_ODR_OFF; - err = st_raw_write8(accel->port, accel->i2c_spi_addr_flags, - LSM6DSM_FIFO_CTRL5_ADDR, odr_reg_val); - - /* Scan all sensors configuration to calculate FIFO decimator. */ - fifo_state->config.total_samples_in_pattern = 0; - for (i = FIFO_DEV_GYRO; i < FIFO_DEV_NUM; i++) { - if (odrs[i] > 0) { - fifo_state->config.samples_in_pattern[i] = - odrs[i] / min_odr; - decimators[i] = - LSM6DSM_FIFO_DECIMATOR(max_odr / odrs[i]); - fifo_state->config.total_samples_in_pattern += - fifo_state->config.samples_in_pattern[i]; - } else { - /* Not in FIFO if sensor disabled. */ - fifo_state->config.samples_in_pattern[i] = 0; - } - } - st_raw_write8(accel->port, accel->i2c_spi_addr_flags, - LSM6DSM_FIFO_CTRL3_ADDR, - (decimators[FIFO_DEV_GYRO] << LSM6DSM_FIFO_DEC_G_OFF) | - (decimators[FIFO_DEV_ACCEL] << LSM6DSM_FIFO_DEC_XL_OFF)); -#ifdef CONFIG_LSM6DSM_SEC_I2C - st_raw_write8(accel->port, accel->i2c_spi_addr_flags, - LSM6DSM_FIFO_CTRL4_ADDR, - decimators[FIFO_DEV_MAG]); - - /* - * FIFO ODR is limited by odr of gyro or accel. - * If we are sampling magnetometer faster than gyro or accel, - * bump up ODR of accel. Thanks to decimation we will still measure at - * the specified ODR. - * Contrary to gyroscope, sampling faster will not affect measurements. - * Set the ODR behind the back of set/get_data_rate. - * - * First samples after ODR changes must be thrown out [See - * AN4987, section 3.9]. - * When increasing accel ODR, the FIFO is going to drop samples, - * - except the first one after ODR change. - * When decreasing accel ODR, we don't need to drop sample if - * frequency is less than 52Hz. - * At most, we need to drop one sample, but Android requirement specify - * that chaning one sensor ODR should not affect other sensors. - * Leave the bad sample alone, it will be a single glitch in the - * accelerometer data stream. - */ - if (max_odr > MAX(odrs[FIFO_DEV_ACCEL], odrs[FIFO_DEV_GYRO])) { - st_write_data_with_mask(accel, LSM6DSM_ODR_REG(accel->type), - LSM6DSM_ODR_MASK, - LSM6DSM_ODR_TO_REG(max_odr)); - } else { - st_write_data_with_mask(accel, LSM6DSM_ODR_REG(accel->type), - LSM6DSM_ODR_MASK, - LSM6DSM_ODR_TO_REG(odrs[FIFO_DEV_ACCEL])); - } -#endif /* CONFIG_MAG_LSM6DSM_LIS2MDL */ - /* - * After ODR and decimation values are set, continuous mode can be - * enabled - */ - err = st_raw_write8(accel->port, accel->i2c_spi_addr_flags, - LSM6DSM_FIFO_CTRL5_ADDR, - odr_reg_val | LSM6DSM_FIFO_MODE_CONTINUOUS_VAL); - if (err != EC_SUCCESS) - return err; - fifo_reset_pattern(private); - return EC_SUCCESS; -} - -/* - * Must order FIFO read based on ODR: - * For example Acc @ 52 Hz, Gyro @ 26 Hz Mag @ 13 Hz in FIFO we have - * for each pattern this data samples: - * ________ _______ _______ _______ ________ _______ _______ - * | Gyro_0 | Acc_0 | Mag_0 | Acc_1 | Gyro_1 | Acc_2 | Acc_3 | - * |________|_______|_______|_______|________|_______|_______| - * - * Total samples for each pattern: 2 Gyro, 4 Acc, 1 Mag. - * - * Returns dev_fifo enum value of next sample to process - */ -static int fifo_next(struct lsm6dsm_data *private) -{ - int next_id; - struct lsm6dsm_accel_fifo_state *fifo_state = private->accel_fifo_state; - - if (fifo_state->current.total_samples_in_pattern == 0) - fifo_reset_pattern(private); - - if (fifo_state->current.total_samples_in_pattern == 0) { - /* - * Not expected we are supposed to be called to process FIFO - * data. - */ - CPRINTS("FIFO empty pattern"); - return FIFO_DEV_INVALID; - } - - for (next_id = fifo_state->next_in_pattern + 1; 1; next_id++) { - if (next_id == FIFO_DEV_NUM) - next_id = FIFO_DEV_GYRO; - if (fifo_state->current.samples_in_pattern[next_id] != 0) { - fifo_state->current.samples_in_pattern[next_id]--; - fifo_state->current.total_samples_in_pattern--; - fifo_state->next_in_pattern = next_id; - return next_id; - } - } - /* Will never happen. */ - return FIFO_DEV_INVALID; -} - -/** - * push_fifo_data - Scan data pattern and push upside - */ -static void push_fifo_data(struct motion_sensor_t *accel, uint8_t *fifo, - uint16_t flen, - uint32_t timestamp) -{ - struct motion_sensor_t *s; - struct lsm6dsm_data *private = LSM6DSM_GET_DATA(accel); - - while (flen > 0) { - struct ec_response_motion_sensor_data vect; - int id; - int *axis; - int next_fifo = fifo_next(private); - /* - * This should never happen, but it could. There will be a - * report from inside fifo_next about it, so no extra message - * required here. - */ - if (next_fifo == FIFO_DEV_INVALID) { - return; - } - - id = get_sensor_type(next_fifo); - if (private->accel_fifo_state->samples_to_discard[id] > 0) { - private->accel_fifo_state->samples_to_discard[id]--; - } else { - s = accel + id; - axis = s->raw_xyz; - - /* Apply precision, sensitivity and rotation. */ -#ifdef CONFIG_MAG_LSM6DSM_LIS2MDL - if (s->type == MOTIONSENSE_TYPE_MAG) { - lis2mdl_normalize(s, axis, fifo); - rotate(axis, *s->rot_standard_ref, axis); - } else -#endif - { - st_normalize(s, axis, fifo); - } - - - vect.data[X] = axis[X]; - vect.data[Y] = axis[Y]; - vect.data[Z] = axis[Z]; - - vect.flags = 0; - vect.sensor_num = s - motion_sensors; - motion_sense_fifo_stage_data(&vect, s, 3, timestamp); - } - - fifo += OUT_XYZ_SIZE; - flen -= OUT_XYZ_SIZE; - } -} - -static int load_fifo(struct motion_sensor_t *s, const struct fstatus *fsts, - uint32_t *last_fifo_read_ts) -{ - uint32_t interrupt_timestamp = last_interrupt_timestamp; - int err, left, length; - uint8_t fifo[FIFO_READ_LEN]; - - /* Reset the load_fifo_sensor_state so we can start a new read. */ - reset_load_fifo_sensor_state(s, interrupt_timestamp); - - /* - * DIFF[11:0] are number of unread uint16 in FIFO - * mask DIFF and compute total byte len to read from FIFO. - */ - left = fsts->len & LSM6DSM_FIFO_DIFF_MASK; - left *= sizeof(uint16_t); - left = (left / OUT_XYZ_SIZE) * OUT_XYZ_SIZE; - - /* - * TODO(b/122912601): phaser360: Investigate Standard Deviation error - * during CtsSensorTests - * - check "pattern" register versus where code thinks it is parsing - */ - - /* Push all data on upper side. */ - do { - /* Fit len to pre-allocated static buffer. */ - if (left > FIFO_READ_LEN) - length = FIFO_READ_LEN; - else - length = left; - - /* Read data and copy in buffer. */ - err = st_raw_read_n_noinc(s->port, s->i2c_spi_addr_flags, - LSM6DSM_FIFO_DATA_ADDR, - fifo, length); - *last_fifo_read_ts = __hw_clock_source_read(); - if (err != EC_SUCCESS) - return err; - - /* - * Manage patterns and push data. Data is pushed with the - * timestamp of the interrupt that got us into this function - * in the first place. This avoids a potential race condition - * where we empty the FIFO, and a new IRQ comes in between - * reading the last sample and pushing it into the FIFO. - */ - - push_fifo_data(s, fifo, length, interrupt_timestamp); - left -= length; - } while (left > 0); - - motion_sense_fifo_commit_data(); - - return EC_SUCCESS; -} - -static int is_fifo_empty(struct motion_sensor_t *s, struct fstatus *fsts) -{ - int res; - - if (s->flags & MOTIONSENSE_FLAG_INT_SIGNAL) - return gpio_get_level(s->int_signal); - CPRINTS("Interrupt signal not set for %s", s->name); - res = st_raw_read_n_noinc(s->port, s->i2c_spi_addr_flags, - LSM6DSM_FIFO_STS1_ADDR, - (int8_t *)fsts, sizeof(*fsts)); - /* If we failed to read the FIFO size assume empty. */ - if (res != EC_SUCCESS) - return 1; - return IS_FSTS_EMPTY(*fsts); -} - -static void handle_interrupt_for_fifo(uint32_t ts) -{ - if (IS_ENABLED(CONFIG_ACCEL_FIFO) && - time_after(ts, last_interrupt_timestamp)) - last_interrupt_timestamp = ts; - task_set_event(TASK_ID_MOTIONSENSE, - CONFIG_ACCEL_LSM6DSM_INT_EVENT, 0); -} - -/** - * lsm6dsm_interrupt - interrupt from int1/2 pin of sensor - */ -void lsm6dsm_interrupt(enum gpio_signal signal) -{ - handle_interrupt_for_fifo(__hw_clock_source_read()); -} - -/** - * irq_handler - bottom half of the interrupt stack - */ -__maybe_unused static int irq_handler( - struct motion_sensor_t *s, uint32_t *event) -{ - int ret = EC_SUCCESS; - - if ((s->type != MOTIONSENSE_TYPE_ACCEL) || - (!(*event & CONFIG_ACCEL_LSM6DSM_INT_EVENT))) - return EC_ERROR_NOT_HANDLED; - - if (IS_ENABLED(CONFIG_ACCEL_FIFO)) { - struct fstatus fsts; - uint32_t last_fifo_read_ts; - uint32_t triggering_interrupt_timestamp = - last_interrupt_timestamp; - - /* Read how many data pattern on FIFO to read and pattern. */ - ret = st_raw_read_n_noinc(s->port, s->i2c_spi_addr_flags, - LSM6DSM_FIFO_STS1_ADDR, - (uint8_t *)&fsts, sizeof(fsts)); - if (ret != EC_SUCCESS) - return ret; - last_fifo_read_ts = __hw_clock_source_read(); - if (fsts.len & (LSM6DSM_FIFO_DATA_OVR | LSM6DSM_FIFO_FULL)) - CPRINTS("%s FIFO Overrun: %04x", s->name, fsts.len); - if (!IS_FSTS_EMPTY(fsts)) - ret = load_fifo(s, &fsts, &last_fifo_read_ts); - - /* - * Check if FIFO isn't empty and we never got an interrupt. - * This can happen if new entries were added to the FIFO after - * the count was read, but before the FIFO was cleared out. - * In the long term it might be better to use the last - * spread timestamp instead. - */ - if (!is_fifo_empty(s, &fsts) && - triggering_interrupt_timestamp == last_interrupt_timestamp) - handle_interrupt_for_fifo(last_fifo_read_ts); - } - - return ret; -} - -/** - * set_range - set full scale range - * @s: Motion sensor pointer - * @range: Range - * @rnd: Round up/down flag - * Note: Range is sensitivity/gain for speed purpose - */ -static int set_range(const struct motion_sensor_t *s, int range, int rnd) -{ - int err; - uint8_t ctrl_reg, reg_val; - struct stprivate_data *data = s->drv_data; - int newrange = range; - - switch (s->type) { - case MOTIONSENSE_TYPE_ACCEL: - /* Adjust and check rounded value for acc. */ - if (rnd && (newrange < LSM6DSM_ACCEL_NORMALIZE_FS(newrange))) - newrange *= 2; - - if (newrange > LSM6DSM_ACCEL_FS_MAX_VAL) - newrange = LSM6DSM_ACCEL_FS_MAX_VAL; - - reg_val = LSM6DSM_ACCEL_FS_REG(newrange); - break; - case MOTIONSENSE_TYPE_GYRO: - /* Adjust and check rounded value for gyro. */ - reg_val = LSM6DSM_GYRO_FS_REG(range); - if (rnd && (range > LSM6DSM_GYRO_NORMALIZE_FS(reg_val))) - reg_val++; - - if (reg_val > LSM6DSM_GYRO_FS_MAX_REG_VAL) - reg_val = LSM6DSM_GYRO_FS_MAX_REG_VAL; - newrange = LSM6DSM_GYRO_NORMALIZE_FS(reg_val); - break; - default: - return EC_RES_INVALID_PARAM; - } - - ctrl_reg = LSM6DSM_RANGE_REG(s->type); - mutex_lock(s->mutex); - err = st_write_data_with_mask(s, ctrl_reg, LSM6DSM_RANGE_MASK, reg_val); - if (err == EC_SUCCESS) - /* Save internally gain for speed optimization. */ - data->base.range = newrange; - mutex_unlock(s->mutex); - return err; -} - -/** - * get_range - get full scale range - * @s: Motion sensor pointer - * - * For mag range is fixed to LIS2MDL_RANGE by hardware - */ -static int get_range(const struct motion_sensor_t *s) -{ - struct stprivate_data *data = s->drv_data; - - return data->base.range; -} - -/** - * lsm6dsm_set_data_rate - * @s: Motion sensor pointer - * @range: Rate (mHz) - * @rnd: Round up/down flag - * - * For mag in cascade with lsm6dsm/l we use acc trigger and FIFO decimators - */ -int lsm6dsm_set_data_rate(const struct motion_sensor_t *s, int rate, int rnd) -{ - struct stprivate_data *data = s->drv_data; - const struct motion_sensor_t *accel = IS_ENABLED(CONFIG_ACCEL_FIFO) ? - LSM6DSM_MAIN_SENSOR(s) : NULL; - struct lsm6dsm_data *private = IS_ENABLED(CONFIG_ACCEL_FIFO) ? - LSM6DSM_GET_DATA(accel) : NULL; - int ret = EC_SUCCESS, normalized_rate = 0; - uint8_t ctrl_reg, reg_val = 0; - - if (IS_ENABLED(CONFIG_ACCEL_FIFO)) { - /* FIFO must be disabled before setting any ODR values */ - ret = fifo_disable(accel); - if (ret != EC_SUCCESS) { - CPRINTS("Failed to disable FIFO. Error: %d", ret); - return ret; - } - } - - if (rate > 0) { - reg_val = LSM6DSM_ODR_TO_REG(rate); - normalized_rate = LSM6DSM_REG_TO_ODR(reg_val); - - if (rnd && (normalized_rate < rate)) { - reg_val++; - normalized_rate = LSM6DSM_REG_TO_ODR(reg_val); - } - if (normalized_rate < LSM6DSM_ODR_MIN_VAL || - normalized_rate > LSM6DSM_ODR_MAX_VAL) - return EC_RES_INVALID_PARAM; - } - -#ifdef CONFIG_MAG_LSM6DSM_LIS2MDL - /* - * TODO(b:110143516) Improve data rate selection: - * Sensor is always running at 100Hz, even when not used. - */ - if (s->type == MOTIONSENSE_TYPE_MAG) { - struct mag_cal_t *cal = LIS2MDL_CAL(s); - - init_mag_cal(cal); - /* - * Magnetometer ODR is calculating at 100Hz, but we are reading - * less often. - */ - if (normalized_rate > 0) - cal->batch_size = MAX( - MAG_CAL_MIN_BATCH_SIZE, - (normalized_rate * 1000) / - MAG_CAL_MIN_BATCH_WINDOW_US); - else - cal->batch_size = 0; - CPRINTS("Batch size: %d", cal->batch_size); - mutex_lock(s->mutex); - } else -#endif - { - mutex_lock(s->mutex); - ctrl_reg = LSM6DSM_ODR_REG(s->type); - ret = st_write_data_with_mask(s, ctrl_reg, LSM6DSM_ODR_MASK, - reg_val); - } - if (ret == EC_SUCCESS) { - data->base.odr = normalized_rate; - if (IS_ENABLED(CONFIG_ACCEL_FIFO)) { - struct lsm6dsm_accel_fifo_state *fifo_state = - private->accel_fifo_state; - fifo_state->samples_to_discard[s->type] = - LSM6DSM_DISCARD_SAMPLES; - fifo_state->load_fifo_sensor_state[get_fifo_type(s)] - .sample_rate = normalized_rate == 0 - ? 0 : SECOND * 1000 / normalized_rate; - ret = fifo_enable(accel); - if (ret != EC_SUCCESS) - CPRINTS("Failed to enable FIFO. Error: %d", - ret); - } - } - - mutex_unlock(s->mutex); - return ret; -} - -static int is_data_ready(const struct motion_sensor_t *s, int *ready) -{ - int ret, tmp; - - ret = st_raw_read8(s->port, s->i2c_spi_addr_flags, - LSM6DSM_STATUS_REG, &tmp); - if (ret != EC_SUCCESS) { - CPRINTS("%s type:0x%X RS Error", s->name, s->type); - return ret; - } - - if (MOTIONSENSE_TYPE_ACCEL == s->type) - *ready = (LSM6DSM_STS_XLDA_UP == (tmp & LSM6DSM_STS_XLDA_MASK)); - else - *ready = (LSM6DSM_STS_GDA_UP == (tmp & LSM6DSM_STS_GDA_MASK)); - - return EC_SUCCESS; -} - -/* - * Is not very efficient to collect the data in read: better have an interrupt - * and collect the FIFO, even if it has one item: we don't have to check if the - * sensor is ready (minimize I2C access). - */ -static int read(const struct motion_sensor_t *s, intv3_t v) -{ - uint8_t raw[OUT_XYZ_SIZE]; - uint8_t xyz_reg; - int ret, tmp = 0; - - ret = is_data_ready(s, &tmp); - if (ret != EC_SUCCESS) - return ret; - - /* - * If sensor data is not ready, return the previous read data. - * Note: return success so that motion senor task can read again - * to get the latest updated sensor data quickly. - */ - if (!tmp) { - if (v != s->raw_xyz) - memcpy(v, s->raw_xyz, sizeof(s->raw_xyz)); - return EC_SUCCESS; - } - - xyz_reg = get_xyz_reg(s->type); - - /* Read data bytes starting at xyz_reg. */ - ret = st_raw_read_n_noinc(s->port, s->i2c_spi_addr_flags, - xyz_reg, raw, OUT_XYZ_SIZE); - if (ret != EC_SUCCESS) - return ret; - - /* Apply precision, sensitivity and rotation vector. */ - st_normalize(s, v, raw); - return EC_SUCCESS; -} - -static int init(const struct motion_sensor_t *s) -{ - int ret = 0, tmp; - struct stprivate_data *data = s->drv_data; - uint8_t ctrl_reg, reg_val = 0; - - ret = st_raw_read8(s->port, s->i2c_spi_addr_flags, - LSM6DSM_WHO_AM_I_REG, &tmp); - if (ret != EC_SUCCESS) - return EC_ERROR_UNKNOWN; - - if (tmp != LSM6DS3_WHO_AM_I && tmp != LSM6DSM_WHO_AM_I) { - /* Unrecognized sensor */ - CPRINTS("Unknown WHO_AM_I value: 0x%x", tmp); - return EC_ERROR_ACCESS_DENIED; - } - - /* - * This sensor can be powered through an EC reboot, so the state of the - * sensor is unknown here so reset it - * LSM6DSM/L supports both accel & gyro features - * Board will see two virtual sensor devices: accel & gyro - * Requirement: Accel need be init before gyro and mag - */ - if (s->type == MOTIONSENSE_TYPE_ACCEL) { - mutex_lock(s->mutex); - - /* Software reset procedure. */ - reg_val = LSM6DSM_ODR_TO_REG(LSM6DSM_ODR_MIN_VAL); - ctrl_reg = LSM6DSM_ODR_REG(MOTIONSENSE_TYPE_ACCEL); - - /* Power OFF gyro. */ - ret = st_raw_write8(s->port, s->i2c_spi_addr_flags, - LSM6DSM_CTRL2_ADDR, 0); - if (ret != EC_SUCCESS) - goto err_unlock; - - /* Power ON Accel. */ - ret = st_raw_write8(s->port, s->i2c_spi_addr_flags, - ctrl_reg, reg_val); - if (ret != EC_SUCCESS) - goto err_unlock; - - /* Software reset. */ - ret = st_raw_write8(s->port, s->i2c_spi_addr_flags, - LSM6DSM_CTRL3_ADDR, LSM6DSM_SW_RESET); - if (ret != EC_SUCCESS) - goto err_unlock; - -#ifdef CONFIG_LSM6DSM_SEC_I2C - /* - * Reboot to reload memory content as pass-through mode can get - * stuck. - * Direct to the AN: See "AN4987 - LSM6DSM: always-on 3D - * accelerometer and 3D gyroscope". - */ - - /* Power ON Accel. */ - ret = st_raw_write8(s->port, s->i2c_spi_addr_flags, - ctrl_reg, reg_val); - if (ret != EC_SUCCESS) - goto err_unlock; - - ret = st_raw_write8(s->port, s->i2c_spi_addr_flags, - LSM6DSM_CTRL3_ADDR, LSM6DSM_BOOT); - if (ret != EC_SUCCESS) - goto err_unlock; - - /* - * Refer to AN4987, wait 15ms for accelerometer to doing full - * reboot. - */ - msleep(15); - - /* Power OFF Accel. */ - ret = st_raw_write8(s->port, s->i2c_spi_addr_flags, - ctrl_reg, 0); - if (ret != EC_SUCCESS) - goto err_unlock; -#endif - - /* - * Output data not updated until have been read. - * Prefer interrupt to be active low. - */ - ret = st_raw_write8(s->port, s->i2c_spi_addr_flags, - LSM6DSM_CTRL3_ADDR, - LSM6DSM_BDU - | LSM6DSM_H_L_ACTIVE - | LSM6DSM_IF_INC); - if (ret != EC_SUCCESS) - goto err_unlock; - - if (IS_ENABLED(CONFIG_ACCEL_FIFO)) { - ret = fifo_disable(s); - if (ret != EC_SUCCESS) - goto err_unlock; - } - -#ifdef CONFIG_ACCEL_INTERRUPTS - ret = config_interrupt(s); - if (ret != EC_SUCCESS) - goto err_unlock; -#endif /* CONFIG_ACCEL_INTERRUPTS */ - - mutex_unlock(s->mutex); - } - - /* Set default resolution common to acc and gyro. */ - data->resol = LSM6DSM_RESOLUTION; - return sensor_init_done(s); - -err_unlock: - mutex_unlock(s->mutex); - CPRINTS("%s: MS Init type:0x%X Error", s->name, s->type); - return ret; -} - -const struct accelgyro_drv lsm6dsm_drv = { - .init = init, - .read = read, - .set_range = set_range, - .get_range = get_range, - .get_resolution = st_get_resolution, - .set_data_rate = lsm6dsm_set_data_rate, - .get_data_rate = st_get_data_rate, - .set_offset = st_set_offset, - .get_offset = st_get_offset, -#ifdef CONFIG_ACCEL_INTERRUPTS - .irq_handler = irq_handler, -#endif /* CONFIG_ACCEL_INTERRUPTS */ -}; |