diff options
Diffstat (limited to 'driver/accel_lis2dw12.c')
-rw-r--r-- | driver/accel_lis2dw12.c | 609 |
1 files changed, 0 insertions, 609 deletions
diff --git a/driver/accel_lis2dw12.c b/driver/accel_lis2dw12.c deleted file mode 100644 index 1872e572f2..0000000000 --- a/driver/accel_lis2dw12.c +++ /dev/null @@ -1,609 +0,0 @@ -/* Copyright 2019 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. - */ - -/* - * LIS2DW12 accelerometer module for Chrome EC 3D digital accelerometer. - * For more details on LIS2DW12 device please refers to www.st.com. - */ -#include "accelgyro.h" -#include "common.h" -#include "console.h" -#include "accel_lis2dw12.h" -#include "hooks.h" -#include "hwtimer.h" -#include "math_util.h" -#include "motion_sense_fifo.h" -#include "task.h" -#include "util.h" - -#define CPRINTF(format, args...) cprintf(CC_ACCEL, format, ## args) -#define CPRINTS(format, args...) cprints(CC_ACCEL, format, ## args) - -#if defined(CONFIG_ACCEL_INTERRUPTS) && defined(CONFIG_ACCEL_LIS2DW_AS_BASE) -/* - * Enable interrupts and FIFO only when the accelerometer is the main sensor. - */ -#define LIS2DW12_ENABLE_FIFO -#endif - -#if defined(CONFIG_ZEPHYR) && defined(CONFIG_ACCEL_INTERRUPTS) -/* Get the motion sensor ID of the LIS2DW12 sensor that generates the - * interrupt. The interrupt is converted to the event and transferred to - * motion sense task that actually handles the interrupt. - * - * Here we use an alias (lis2dw12_int) to get the motion sensor ID. This alias - * MUST be defined for this driver to work. - * aliases { - * lis2dw12-int = &lid_accel; - * }; - */ -#if DT_NODE_EXISTS(DT_ALIAS(lis2dw12_int)) -#define CONFIG_ACCEL_LIS2DW12_INT_EVENT \ - TASK_EVENT_MOTION_SENSOR_INTERRUPT(SENSOR_ID(DT_ALIAS(lis2dw12_int))) -#endif -#endif - -STATIC_IF(LIS2DW12_ENABLE_FIFO) volatile uint32_t last_interrupt_timestamp; - -/** - * lis2dw12_enable_fifo - Enable/Disable FIFO in LIS2DW12 - * @s: Motion sensor pointer - * @mode: fifo_modes - */ -static __maybe_unused int lis2dw12_enable_fifo(const struct motion_sensor_t *s, - enum lis2dw12_fmode mode) -{ - return st_write_data_with_mask(s, LIS2DW12_FIFO_CTRL_ADDR, - LIS2DW12_FIFO_MODE_MASK, mode); -} - -/** - * Load data from internal sensor FIFO. - * @s: Motion sensor pointer - */ -static __maybe_unused int lis2dw12_load_fifo(struct motion_sensor_t *s, - int nsamples, uint32_t *last_fifo_read_ts) -{ - int ret, left, length, i; - struct ec_response_motion_sensor_data vect; - uint32_t interrupt_timestamp = last_interrupt_timestamp; - int *axis = s->raw_xyz; - uint8_t fifo[FIFO_READ_LEN]; - - /* Each sample are OUT_XYZ_SIZE bytes. */ - left = nsamples * OUT_XYZ_SIZE; - - do { - /* - * Limit FIFO read data to burst of FIFO_READ_LEN size because - * read operatios in under i2c mutex lock. - */ - if (left > FIFO_READ_LEN) - length = FIFO_READ_LEN; - else - length = left; - - ret = st_raw_read_n(s->port, s->i2c_spi_addr_flags, - LIS2DW12_OUT_X_L_ADDR, fifo, length); - *last_fifo_read_ts = __hw_clock_source_read(); - if (ret != EC_SUCCESS) - return ret; - - for (i = 0; i < length; i += OUT_XYZ_SIZE) { - /* Apply precision, sensitivity and rotation vector. */ - st_normalize(s, axis, &fifo[i]); - - /* Fill vector array. */ - 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, - interrupt_timestamp); - } - left -= length; - } while (left > 0); - - motion_sense_fifo_commit_data(); - - return EC_SUCCESS; -} - -/** - * lis2dw12_get_fifo_samples - check for stored FIFO samples. - */ -static __maybe_unused int lis2dw12_get_fifo_samples(struct motion_sensor_t *s, - int *nsamples) -{ - int ret, tmp; - - ret = st_raw_read8(s->port, s->i2c_spi_addr_flags, - LIS2DW12_FIFO_SAMPLES_ADDR, &tmp); - if (ret != EC_SUCCESS) - return ret; - - *nsamples = tmp & LIS2DW12_FIFO_DIFF_MASK; - - return EC_SUCCESS; -} - -static __maybe_unused int fifo_data_avail(struct motion_sensor_t *s) -{ - int ret, nsamples; - - if (s->flags & MOTIONSENSE_FLAG_INT_SIGNAL) - return gpio_get_level(s->int_signal) == - !!(MOTIONSENSE_FLAG_INT_ACTIVE_HIGH & s->flags); - - ret = lis2dw12_get_fifo_samples(s, &nsamples); - /* If we failed to read the FIFO size assume empty. */ - if (ret != EC_SUCCESS) - return 0; - return nsamples; -} - -/** - * lis2dw12_config_interrupt- Configure interrupt for supported features. - * @s: Motion sensor pointer - * - * Must works with interface mutex locked - */ -static __maybe_unused int lis2dw12_config_interrupt( - const struct motion_sensor_t *s) -{ - int ret = EC_SUCCESS; - - if (IS_ENABLED(CONFIG_ACCEL_FIFO)) { - /* Configure FIFO watermark level. */ - ret = st_write_data_with_mask(s, LIS2DW12_FIFO_CTRL_ADDR, - LIS2DW12_FIFO_THRESHOLD_MASK, 1); - if (ret != EC_SUCCESS) - return ret; - - /* Enable interrupt on FIFO watermark and route to int1. */ - ret = st_write_data_with_mask(s, LIS2DW12_INT1_FTH_ADDR, - LIS2DW12_INT1_FTH_MASK, LIS2DW12_EN_BIT); - if (ret != EC_SUCCESS) - return ret; - } - - if (IS_ENABLED(CONFIG_GESTURE_SENSOR_DOUBLE_TAP)) { - /* - * Configure D-TAP event detection on 3 axis. - * For more details please refer to AN5038. - */ - ret = st_raw_write8(s->port, s->i2c_spi_addr_flags, - LIS2DW12_TAP_THS_X_ADDR, 0x09); - if (ret != EC_SUCCESS) - return ret; - ret = st_raw_write8(s->port, s->i2c_spi_addr_flags, - LIS2DW12_TAP_THS_Y_ADDR, 0x09); - if (ret != EC_SUCCESS) - return ret; - ret = st_raw_write8(s->port, s->i2c_spi_addr_flags, - LIS2DW12_TAP_THS_Z_ADDR, 0xE9); - if (ret != EC_SUCCESS) - return ret; - ret = st_raw_write8(s->port, s->i2c_spi_addr_flags, - LIS2DW12_INT_DUR_ADDR, 0x7F); - if (ret != EC_SUCCESS) - return ret; - - /* Enable D-TAP event detection. */ - ret = st_write_data_with_mask(s, LIS2DW12_WAKE_UP_THS_ADDR, - LIS2DW12_SINGLE_DOUBLE_TAP, - LIS2DW12_EN_BIT); - if (ret != EC_SUCCESS) - return ret; - - /* - * Enable D-TAP detection on int_1 pad. In any case D-TAP event - * can be detected only if ODR is over 200 Hz. - */ - ret = st_write_data_with_mask(s, LIS2DW12_INT1_TAP_ADDR, - LIS2DW12_INT1_DTAP_MASK, - LIS2DW12_EN_BIT); - } - return ret; -} - -#ifdef LIS2DW12_ENABLE_FIFO -static void lis2dw12_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_LIS2DW12_INT_EVENT); -} - -/** - * lis2dw12_interrupt - interrupt from int pin of sensor - * Schedule Motion Sense Task to manage Interrupts. - */ -void lis2dw12_interrupt(enum gpio_signal signal) -{ - lis2dw12_handle_interrupt_for_fifo(__hw_clock_source_read()); -} - -/** - * lis2dw12_irq_handler - bottom half of the interrupt stack. - */ -static int lis2dw12_irq_handler(struct motion_sensor_t *s, - uint32_t *event) -{ - int ret = EC_SUCCESS; - - if ((s->type != MOTIONSENSE_TYPE_ACCEL) || - (!(*event & CONFIG_ACCEL_LIS2DW12_INT_EVENT))) { - return EC_ERROR_NOT_HANDLED; - } - - if (IS_ENABLED(CONFIG_GESTURE_SENSOR_DOUBLE_TAP)) { - int status = 0; - - /* Read Status register to check TAP events. */ - st_raw_read8(s->port, s->i2c_spi_addr_flags, - LIS2DW12_STATUS_TAP, &status); - if (status & LIS2DW12_DOUBLE_TAP) - *event |= TASK_EVENT_MOTION_ACTIVITY_INTERRUPT( - MOTIONSENSE_ACTIVITY_DOUBLE_TAP); - } - - if (IS_ENABLED(CONFIG_ACCEL_FIFO)) { - int nsamples; - uint32_t last_fifo_read_ts; - uint32_t triggering_interrupt_timestamp = - last_interrupt_timestamp; - - ret = lis2dw12_get_fifo_samples(s, &nsamples); - if (ret != EC_SUCCESS) - return ret; - - last_fifo_read_ts = __hw_clock_source_read(); - if (nsamples == 0) - return EC_SUCCESS; - - ret = lis2dw12_load_fifo(s, nsamples, &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 (fifo_data_avail(s) && - triggering_interrupt_timestamp == last_interrupt_timestamp) - lis2dw12_handle_interrupt_for_fifo(last_fifo_read_ts); - } - - return ret; -} -#endif - -/** - * set_power_mode - set sensor power mode - * @s: Motion sensor pointer - * @mode: LIS2DW12_LOW_POWER, LIS2DW12_HIGH_PERF - * @lpmode: LIS2DW12_LOW_POWER_MODE_2, - * LIS2DW12_LOW_POWER_MODE_3, - * LIS2DW12_LOW_POWER_MODE_4 - * - * TODO: LIS2DW12_LOW_POWER_MODE_1 not implemented because output differ - * in resolution - */ -static int set_power_mode(const struct motion_sensor_t *s, - enum lis2sw12_mode mode, - enum lis2sw12_lpmode lpmode) -{ - int ret = EC_SUCCESS; - - if (mode == LIS2DW12_LOW_POWER && - lpmode == LIS2DW12_LOW_POWER_MODE_1) - return EC_ERROR_UNIMPLEMENTED; - - /* Set Mode and Low Power Mode. */ - ret = st_write_data_with_mask(s, LIS2DW12_ACC_MODE_ADDR, - LIS2DW12_ACC_MODE_MASK, mode); - if (ret != EC_SUCCESS) - return ret; - - ret = st_write_data_with_mask(s, LIS2DW12_ACC_LPMODE_ADDR, - LIS2DW12_ACC_LPMODE_MASK, lpmode); - return ret; -} - -/** - * set_range - set full scale range - * @s: Motion sensor pointer - * @range: Range - * @rnd: Round up/down flag - */ -static int set_range(struct motion_sensor_t *s, int range, int rnd) -{ - int err = EC_SUCCESS; - uint8_t reg_val; - int newrange = range; - - /* Adjust and check rounded value. */ - if (rnd && (newrange < LIS2DW12_NORMALIZE_FS(newrange))) - newrange <<= 1; - - if (newrange > LIS2DW12_ACCEL_FS_MAX_VAL) - newrange = LIS2DW12_ACCEL_FS_MAX_VAL; - - reg_val = LIS2DW12_FS_REG(newrange); - - mutex_lock(s->mutex); - /* - * FIFO stop collecting events. Restart FIFO in Bypass mode. - * If Range is changed all samples in FIFO must be discharged because - * with a different sensitivity. - */ - if (IS_ENABLED(LIS2DW12_ENABLE_FIFO)) { - err = lis2dw12_enable_fifo(s, LIS2DW12_FIFO_BYPASS_MODE); - if (err != EC_SUCCESS) - goto unlock_rate; - } - - err = st_write_data_with_mask(s, LIS2DW12_FS_ADDR, LIS2DW12_FS_MASK, - reg_val); - if (err == EC_SUCCESS) - s->current_range = newrange; - else - goto unlock_rate; - - /* FIFO restart collecting events in Cont. mode. */ - if (IS_ENABLED(LIS2DW12_ENABLE_FIFO)) - err = lis2dw12_enable_fifo(s, LIS2DW12_FIFO_CONT_MODE); - -unlock_rate: - mutex_unlock(s->mutex); - - return err; -} - -/** - * ODR reg value from selected data rate in mHz. - */ -static uint8_t odr_to_reg(int odr) -{ - if (odr <= LIS2DW12_ODR_MIN_VAL) - return LIS2DW12_ODR_12HZ_VAL; - - return (__fls(odr / LIS2DW12_ODR_MIN_VAL) + LIS2DW12_ODR_12HZ_VAL); -} - -/** - * Normalized ODR value from selected data rate in mHz. - */ -static int odr_to_normalize(int odr) -{ - if (odr <= LIS2DW12_ODR_MIN_VAL) - return LIS2DW12_ODR_MIN_VAL; - - return (LIS2DW12_ODR_MIN_VAL << (__fls(odr / LIS2DW12_ODR_MIN_VAL))); -} - -static int set_data_rate(const struct motion_sensor_t *s, int rate, int rnd) -{ - int ret, normalized_rate; - struct stprivate_data *data = s->drv_data; - uint8_t reg_val; - - mutex_lock(s->mutex); - - /* FIFO stop collecting events. Restart FIFO in Bypass mode. */ - if (IS_ENABLED(LIS2DW12_ENABLE_FIFO)) { - ret = lis2dw12_enable_fifo(s, LIS2DW12_FIFO_BYPASS_MODE); - if (ret != EC_SUCCESS) - goto unlock_rate; - } - - if (rate == 0) { - ret = st_write_data_with_mask(s, LIS2DW12_ACC_ODR_ADDR, - LIS2DW12_ACC_ODR_MASK, - LIS2DW12_ODR_POWER_OFF_VAL); - if (ret == EC_SUCCESS) - data->base.odr = LIS2DW12_ODR_POWER_OFF_VAL; - - goto unlock_rate; - } - - reg_val = odr_to_reg(rate); - normalized_rate = odr_to_normalize(rate); - - if (rnd && (normalized_rate < rate)) { - reg_val++; - normalized_rate <<= 1; - } - - if (reg_val > LIS2DW12_ODR_1_6kHZ_VAL) { - reg_val = LIS2DW12_ODR_1_6kHZ_VAL; - normalized_rate = LIS2DW12_ODR_MAX_VAL; - } else if (reg_val < LIS2DW12_ODR_12HZ_VAL) { - reg_val = LIS2DW12_ODR_12HZ_VAL; - normalized_rate = LIS2DW12_ODR_MIN_VAL; - } - - /* lis2dwl supports 14 bit resolution only at high performance mode, - * and it will always stay at high performance mode from initialization. - * But lis2dw12 needs switch low power mode according to odr value. - */ - if (!IS_ENABLED(CONFIG_ACCEL_LIS2DWL)) { - if (reg_val > LIS2DW12_ODR_200HZ_VAL) - ret = set_power_mode(s, LIS2DW12_HIGH_PERF, 0); - else - ret = set_power_mode(s, LIS2DW12_LOW_POWER, - LIS2DW12_LOW_POWER_MODE_2); - } - - ret = st_write_data_with_mask(s, LIS2DW12_ACC_ODR_ADDR, - LIS2DW12_ACC_ODR_MASK, reg_val); - if (ret == EC_SUCCESS) - data->base.odr = normalized_rate; - - /* FIFO restart collecting events in continuous mode. */ - if (IS_ENABLED(LIS2DW12_ENABLE_FIFO)) - ret = lis2dw12_enable_fifo(s, LIS2DW12_FIFO_CONT_MODE); - -unlock_rate: - 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, - LIS2DW12_STATUS_REG, &tmp); - if (ret != EC_SUCCESS) - return ret; - - *ready = (LIS2DW12_STS_DRDY_UP == (tmp & LIS2DW12_STS_DRDY_UP)); - - return EC_SUCCESS; -} - -static int read(const struct motion_sensor_t *s, intv3_t v) -{ - uint8_t raw[OUT_XYZ_SIZE]; - 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; - } - - /* Read 6 bytes starting at xyz_reg. */ - ret = st_raw_read_n_noinc(s->port, s->i2c_spi_addr_flags, - LIS2DW12_OUT_X_L_ADDR, raw, - OUT_XYZ_SIZE); - if (ret != EC_SUCCESS) { - CPRINTS("%s type:0x%X RD XYZ Error", s->name, s->type); - return ret; - } - - /* Transform from LSB to real data with rotation and gain. */ - st_normalize(s, v, raw); - - return EC_SUCCESS; -} - -static int init(struct motion_sensor_t *s) -{ - int ret = 0, tmp, timeout = 0, status; - struct stprivate_data *data = s->drv_data; - - ret = st_raw_read8(s->port, s->i2c_spi_addr_flags, - LIS2DW12_WHO_AM_I_REG, &tmp); - if (ret != EC_SUCCESS) - return ret; - - if (tmp != LIS2DW12_WHO_AM_I) - return EC_ERROR_ACCESS_DENIED; - - /* - * This sensor can be powered through an EC reboot, so the state of - * the sensor is unknown here. Initiate software reset to restore - * sensor to default. - */ - mutex_lock(s->mutex); - ret = st_raw_write8(s->port, s->i2c_spi_addr_flags, - LIS2DW12_SOFT_RESET_ADDR, LIS2DW12_SOFT_RESET_MASK); - if (ret != EC_SUCCESS) - goto err_unlock; - - /* Wait End of Reset. */ - do { - if (timeout > 10) { - ret = EC_ERROR_TIMEOUT; - goto err_unlock; - } - - msleep(1); - timeout += 1; - ret = st_raw_read8(s->port, s->i2c_spi_addr_flags, - LIS2DW12_SOFT_RESET_ADDR, &status); - } while (ret != EC_SUCCESS || (status & LIS2DW12_SOFT_RESET_MASK) != 0); - - /* Enable BDU. */ - ret = st_write_data_with_mask(s, LIS2DW12_BDU_ADDR, LIS2DW12_BDU_MASK, - LIS2DW12_EN_BIT); - if (ret != EC_SUCCESS) - goto err_unlock; - - ret = st_write_data_with_mask(s, LIS2DW12_LIR_ADDR, LIS2DW12_LIR_MASK, - LIS2DW12_EN_BIT); - if (ret != EC_SUCCESS) - goto err_unlock; - - /* Interrupt trigger level of power-on-reset is HIGH */ - if (IS_ENABLED(LIS2DW12_ENABLE_FIFO) && - !(MOTIONSENSE_FLAG_INT_ACTIVE_HIGH & s->flags)) { - ret = st_write_data_with_mask(s, LIS2DW12_H_ACTIVE_ADDR, - LIS2DW12_H_ACTIVE_MASK, - LIS2DW12_EN_BIT); - if (ret != EC_SUCCESS) - goto err_unlock; - } - - if (IS_ENABLED(CONFIG_ACCEL_LIS2DWL)) - /* - * lis2dwl supports 14 bit resolution only - * at high performance mode - */ - ret = set_power_mode(s, LIS2DW12_HIGH_PERF, 0); - else - /* Set default Mode and Low Power Mode. */ - ret = set_power_mode(s, LIS2DW12_LOW_POWER, - LIS2DW12_LOW_POWER_MODE_2); - if (ret != EC_SUCCESS) - goto err_unlock; - - if (IS_ENABLED(LIS2DW12_ENABLE_FIFO)) { - ret = lis2dw12_config_interrupt(s); - if (ret != EC_SUCCESS) - goto err_unlock; - } - mutex_unlock(s->mutex); - - /* Set default resolution. */ - data->resol = LIS2DW12_RESOLUTION; - return sensor_init_done(s); - -err_unlock: - mutex_unlock(s->mutex); - CPRINTS("%s: MS Init type:0x%X Error(%d)", s->name, s->type, ret); - return ret; -} - -const struct accelgyro_drv lis2dw12_drv = { - .init = init, - .read = read, - .set_range = set_range, - .get_resolution = st_get_resolution, - .set_data_rate = set_data_rate, - .get_data_rate = st_get_data_rate, - .set_offset = st_set_offset, - .get_offset = st_get_offset, -#ifdef LIS2DW12_ENABLE_FIFO - .irq_handler = lis2dw12_irq_handler, -#endif /* CONFIG_ACCEL_INTERRUPTS && CONFIG_ACCEL_LIS2DW_AS_BASE */ -}; |