blob: 70059dc1d7cfa8216a89276620cb957fcd8f17ac [file] [log] [blame]
/* Copyright 2015 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.
*/
/**
* BMI160 accelerometer and gyro module for Chrome EC
* 3D digital accelerometer & 3D digital gyroscope
*/
#include "accelgyro.h"
#include "common.h"
#include "console.h"
#include "driver/accelgyro_bmi_common.h"
#include "driver/accelgyro_bmi160.h"
#include "driver/mag_bmm150.h"
#include "hwtimer.h"
#include "i2c.h"
#include "math_util.h"
#include "motion_orientation.h"
#include "motion_sense_fifo.h"
#include "spi.h"
#include "task.h"
#include "timer.h"
#include "util.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)
STATIC_IF(CONFIG_BMI_ORIENTATION_SENSOR) void irq_set_orientation(
struct motion_sensor_t *s,
int interrupt);
STATIC_IF(CONFIG_ACCEL_FIFO) volatile uint32_t last_interrupt_timestamp;
static int wakeup_time[] = {
[MOTIONSENSE_TYPE_ACCEL] = 4,
[MOTIONSENSE_TYPE_GYRO] = 80,
[MOTIONSENSE_TYPE_MAG] = 1
};
/**
* Control access to the compass on the secondary i2c interface:
* enable values are:
* 1: manual access, we can issue i2c to the compass
* 0: data access: BMI160 gather data periodically from the compass.
*/
static __maybe_unused int bmi160_sec_access_ctrl(
const int port,
const uint16_t i2c_spi_addr_flags,
const int enable)
{
int mag_if_ctrl;
bmi_read8(port, i2c_spi_addr_flags,
BMI160_MAG_IF_1, &mag_if_ctrl);
if (enable) {
mag_if_ctrl |= BMI160_MAG_MANUAL_EN;
mag_if_ctrl &= ~BMI160_MAG_READ_BURST_MASK;
mag_if_ctrl |= BMI160_MAG_READ_BURST_1;
} else {
mag_if_ctrl &= ~BMI160_MAG_MANUAL_EN;
mag_if_ctrl &= ~BMI160_MAG_READ_BURST_MASK;
mag_if_ctrl |= BMI160_MAG_READ_BURST_8;
}
return bmi_write8(port, i2c_spi_addr_flags,
BMI160_MAG_IF_1, mag_if_ctrl);
}
/**
* Read register from compass.
* Assuming we are in manual access mode, read compass i2c register.
*/
int bmi160_sec_raw_read8(const int port,
const uint16_t i2c_spi_addr_flags,
const uint8_t reg, int *data_ptr)
{
/* Only read 1 bytes */
bmi_write8(port, i2c_spi_addr_flags,
BMI160_MAG_I2C_READ_ADDR, reg);
return bmi_read8(port, i2c_spi_addr_flags,
BMI160_MAG_I2C_READ_DATA, data_ptr);
}
/**
* Write register from compass.
* Assuming we are in manual access mode, write to compass i2c register.
*/
int bmi160_sec_raw_write8(const int port,
const uint16_t i2c_spi_addr_flags,
const uint8_t reg, int data)
{
bmi_write8(port, i2c_spi_addr_flags,
BMI160_MAG_I2C_WRITE_DATA, data);
return bmi_write8(port, i2c_spi_addr_flags,
BMI160_MAG_I2C_WRITE_ADDR, reg);
}
static int set_data_rate(const struct motion_sensor_t *s,
int rate,
int rnd)
{
int ret, normalized_rate;
uint8_t reg_val;
struct accelgyro_saved_data_t *data = BMI_GET_SAVED_DATA(s);
if (rate == 0) {
/* FIFO stop collecting events */
if (IS_ENABLED(CONFIG_ACCEL_FIFO))
bmi_enable_fifo(s, 0);
/* go to suspend mode */
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_CMD_REG,
BMI160_CMD_MODE_SUSPEND(s->type));
msleep(3);
data->odr = 0;
if (IS_ENABLED(CONFIG_MAG_BMI_BMM150) &&
(s->type == MOTIONSENSE_TYPE_MAG)) {
struct mag_cal_t *moc = BMM150_CAL(s);
moc->batch_size = 0;
}
return ret;
} else if (data->odr == 0) {
/* back from suspend mode. */
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_CMD_REG,
BMI160_CMD_MODE_NORMAL(s->type));
msleep(wakeup_time[s->type]);
}
ret = bmi_get_normalized_rate(s, rate, rnd, &normalized_rate, &reg_val);
if (ret)
return ret;
/*
* Lock accel resource to prevent another task from attempting
* to write accel parameters until we are done.
*/
mutex_lock(s->mutex);
ret = bmi_set_reg8(s, BMI_CONF_REG(s->type),
reg_val, BMI_ODR_MASK);
if (ret != EC_SUCCESS)
goto accel_cleanup;
/* Now that we have set the odr, update the driver's value. */
data->odr = normalized_rate;
if (IS_ENABLED(CONFIG_MAG_BMI_BMM150) &&
(s->type == MOTIONSENSE_TYPE_MAG)) {
struct mag_cal_t *moc = BMM150_CAL(s);
/* Reset the calibration */
init_mag_cal(moc);
/*
* We need at least MIN_BATCH_SIZE amd we must have collected
* for at least MIN_BATCH_WINDOW_US.
* Given odr is in mHz, multiply by 1000x
*/
moc->batch_size = MAX(
MAG_CAL_MIN_BATCH_SIZE,
(data->odr * 1000) / (MAG_CAL_MIN_BATCH_WINDOW_US));
CPRINTS("Batch size: %d", moc->batch_size);
}
/*
* FIFO start collecting events.
* They will be discarded if AP does not want them.
*/
if (IS_ENABLED(CONFIG_ACCEL_FIFO))
bmi_enable_fifo(s, 1);
accel_cleanup:
mutex_unlock(s->mutex);
return ret;
}
static int set_offset(const struct motion_sensor_t *s,
const int16_t *offset,
int16_t temp)
{
int ret, val98;
intv3_t v = { offset[X], offset[Y], offset[Z] };
rotate_inv(v, *s->rot_standard_ref, v);
ret = bmi_read8(s->port, s->i2c_spi_addr_flags,
BMI160_OFFSET_EN_GYR98, &val98);
if (ret != 0)
return ret;
switch (s->type) {
case MOTIONSENSE_TYPE_ACCEL:
bmi_set_accel_offset(s, v);
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_OFFSET_EN_GYR98,
val98 | BMI160_OFFSET_ACC_EN);
break;
case MOTIONSENSE_TYPE_GYRO:
bmi_set_gyro_offset(s, v, &val98);
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_OFFSET_EN_GYR98,
val98 | BMI160_OFFSET_GYRO_EN);
break;
#ifdef CONFIG_MAG_BMI_BMM150
case MOTIONSENSE_TYPE_MAG:
ret = bmm150_set_offset(s, v);
break;
#endif /* defined(CONFIG_MAG_BMI_BMM150) */
default:
ret = EC_RES_INVALID_PARAM;
}
return ret;
}
static int perform_calib(struct motion_sensor_t *s, int enable)
{
int ret, val, en_flag, status, rate, range = s->current_range;
timestamp_t deadline, timeout;
if (!enable)
return EC_SUCCESS;
rate = bmi_get_data_rate(s);
/*
* Temporary set frequency to 100Hz to get enough data in a short
* period of time.
*/
set_data_rate(s, 100000, 0);
switch (s->type) {
case MOTIONSENSE_TYPE_ACCEL:
/* We assume the device is laying flat for calibration */
if (s->rot_standard_ref == NULL ||
(*s->rot_standard_ref)[2][2] > INT_TO_FP(0))
val = BMI160_FOC_ACC_PLUS_1G;
else
val = BMI160_FOC_ACC_MINUS_1G;
val = (BMI160_FOC_ACC_0G << BMI160_FOC_ACC_X_OFFSET) |
(BMI160_FOC_ACC_0G << BMI160_FOC_ACC_Y_OFFSET) |
(val << BMI160_FOC_ACC_Z_OFFSET);
en_flag = BMI160_OFFSET_ACC_EN;
/*
* Temporary set range to minimum to run calibration with
* full sensitivity
*/
bmi_set_range(s, 2, 0);
/* Timeout for accelerometer calibration */
timeout.val = 400 * MSEC;
break;
case MOTIONSENSE_TYPE_GYRO:
val = BMI160_FOC_GYRO_EN;
en_flag = BMI160_OFFSET_GYRO_EN;
/*
* Temporary set range to minimum to run calibration with
* full sensitivity
*/
bmi_set_range(s, 125, 0);
/* Timeout for gyroscope calibration */
timeout.val = 800 * MSEC;
break;
default:
/* Not supported on Magnetometer */
ret = EC_RES_INVALID_PARAM;
goto end_perform_calib;
}
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_FOC_CONF, val);
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_CMD_REG, BMI160_CMD_START_FOC);
deadline.val = get_time().val + timeout.val;
do {
if (timestamp_expired(deadline, NULL)) {
ret = EC_RES_TIMEOUT;
goto end_perform_calib;
}
msleep(50);
ret = bmi_read8(s->port, s->i2c_spi_addr_flags,
BMI160_STATUS, &status);
if (ret != EC_SUCCESS)
goto end_perform_calib;
} while ((status & BMI160_FOC_RDY) == 0);
/* Calibration is successful, and loaded, use the result */
ret = bmi_enable_reg8(s, BMI160_OFFSET_EN_GYR98, en_flag, 1);
end_perform_calib:
bmi_set_range(s, range, 0);
set_data_rate(s, rate, 0);
return ret;
}
/*
* Manage gesture recognition.
* Defined even if host interface is not defined, to enable double tap even
* when the host does not deal with gesture.
*/
int manage_activity(const struct motion_sensor_t *s,
enum motionsensor_activity activity,
int enable,
const struct ec_motion_sense_activity *param)
{
int ret;
struct bmi_drv_data_t *data = BMI_GET_DATA(s);
switch (activity) {
#ifdef CONFIG_GESTURE_SIGMO
case MOTIONSENSE_ACTIVITY_SIG_MOTION: {
if (enable) {
/* We should use parameters from caller */
bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_INT_MOTION_3,
BMI160_MOTION_PROOF_TIME(
CONFIG_GESTURE_SIGMO_PROOF_MS) <<
BMI160_MOTION_PROOF_OFF |
BMI160_MOTION_SKIP_TIME(
CONFIG_GESTURE_SIGMO_SKIP_MS) <<
BMI160_MOTION_SKIP_OFF |
BMI160_MOTION_SIG_MOT_SEL);
bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_INT_MOTION_1,
BMI160_MOTION_TH(s,
CONFIG_GESTURE_SIGMO_THRES_MG));
}
ret = bmi_enable_reg8(s, BMI160_INT_EN_0,
BMI160_INT_ANYMO_X_EN |
BMI160_INT_ANYMO_Y_EN |
BMI160_INT_ANYMO_Z_EN,
enable);
if (ret)
ret = EC_RES_UNAVAILABLE;
break;
}
#endif
#ifdef CONFIG_GESTURE_SENSOR_DOUBLE_TAP
case MOTIONSENSE_ACTIVITY_DOUBLE_TAP: {
/* Set double tap interrupt */
ret = bmi_enable_reg8(s, BMI160_INT_EN_0,
BMI160_INT_D_TAP_EN,
enable);
if (ret)
ret = EC_RES_UNAVAILABLE;
break;
}
#endif
default:
ret = EC_RES_INVALID_PARAM;
}
if (ret == EC_RES_SUCCESS) {
if (enable) {
data->enabled_activities |= 1 << activity;
data->disabled_activities &= ~BIT(activity);
} else {
data->enabled_activities &= ~BIT(activity);
data->disabled_activities |= 1 << activity;
}
}
return ret;
}
#ifdef CONFIG_GESTURE_HOST_DETECTION
int list_activities(const struct motion_sensor_t *s,
uint32_t *enabled,
uint32_t *disabled)
{
struct bmi_drv_data_t *data = BMI_GET_DATA(s);
*enabled = data->enabled_activities;
*disabled = data->disabled_activities;
return EC_RES_SUCCESS;
}
#endif
static __maybe_unused int config_interrupt(const struct motion_sensor_t *s)
{
int ret, tmp;
if (s->type != MOTIONSENSE_TYPE_ACCEL)
return EC_SUCCESS;
mutex_lock(s->mutex);
bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_CMD_REG, BMI160_CMD_FIFO_FLUSH);
bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_CMD_REG, BMI160_CMD_INT_RESET);
if (IS_ENABLED(CONFIG_GESTURE_SENSOR_DOUBLE_TAP)) {
bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_INT_TAP_0,
BMI160_TAP_DUR(s, CONFIG_GESTURE_TAP_MAX_INTERSTICE_T));
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_INT_TAP_1,
BMI160_TAP_TH(s, CONFIG_GESTURE_TAP_THRES_MG));
}
/* only use orientation sensor on the lid sensor */
if (IS_ENABLED(CONFIG_BMI_ORIENTATION_SENSOR) &&
(s->location == MOTIONSENSE_LOC_LID)) {
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_INT_ORIENT_0,
BMI160_INT_ORIENT_0_INIT_VAL);
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_INT_ORIENT_1,
BMI160_INT_ORIENT_1_INIT_VAL);
}
if (IS_ENABLED(CONFIG_ACCELGYRO_BMI160_INT2_OUTPUT)) {
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_INT_LATCH, BMI160_LATCH_5MS);
} else {
/* Also, configure int2 as an external input. */
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_INT_LATCH,
BMI160_INT2_INPUT_EN | BMI160_LATCH_5MS);
}
/* configure int1 as an interrupt */
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_INT_OUT_CTRL,
BMI160_INT_CTRL(1, OUTPUT_EN));
/* Map activity interrupt to int 1 */
tmp = 0;
if (IS_ENABLED(CONFIG_GESTURE_SIGMO)) {
tmp |= BMI160_INT_ANYMOTION;
} else if (IS_ENABLED(CONFIG_GESTURE_SENSOR_DOUBLE_TAP)) {
tmp |= BMI160_INT_D_TAP;
} else if (IS_ENABLED(CONFIG_BMI_ORIENTATION_SENSOR) &&
(s->location == MOTIONSENSE_LOC_LID)) {
/* enable orientation interrupt for lid sensor only */
tmp |= BMI160_INT_ORIENT;
}
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_INT_MAP_REG(1), tmp);
if (IS_ENABLED(CONFIG_ACCEL_FIFO)) {
/* map fifo water mark to int 1 */
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_INT_FIFO_MAP,
BMI160_INT_MAP(1, FWM) |
BMI160_INT_MAP(1, FFULL));
/*
* Configure fifo watermark to int whenever there's any data in
* there
*/
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_FIFO_CONFIG_0, 1);
if (IS_ENABLED(CONFIG_ACCELGYRO_BMI160_INT2_OUTPUT))
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_FIFO_CONFIG_1,
BMI160_FIFO_HEADER_EN);
else
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_FIFO_CONFIG_1,
BMI160_FIFO_TAG_INT2_EN |
BMI160_FIFO_HEADER_EN);
/* Set fifo*/
bmi_enable_reg8(s, BMI160_INT_EN_1,
BMI160_INT_FWM_EN | BMI160_INT_FFUL_EN, 1);
}
mutex_unlock(s->mutex);
return ret;
}
#ifdef CONFIG_ACCEL_INTERRUPTS
#ifdef CONFIG_BMI_ORIENTATION_SENSOR
static void irq_set_orientation(struct motion_sensor_t *s,
int interrupt)
{
int shifted_masked_orientation =
(interrupt >> 24) & BMI160_ORIENT_XY_MASK;
if (BMI_GET_DATA(s)->raw_orientation != shifted_masked_orientation) {
enum motionsensor_orientation orientation =
MOTIONSENSE_ORIENTATION_UNKNOWN;
BMI_GET_DATA(s)->raw_orientation =
shifted_masked_orientation;
switch (shifted_masked_orientation) {
case BMI160_ORIENT_PORTRAIT:
orientation = MOTIONSENSE_ORIENTATION_PORTRAIT;
break;
case BMI160_ORIENT_PORTRAIT_INVERT:
orientation =
MOTIONSENSE_ORIENTATION_UPSIDE_DOWN_PORTRAIT;
break;
case BMI160_ORIENT_LANDSCAPE:
orientation = MOTIONSENSE_ORIENTATION_LANDSCAPE;
break;
case BMI160_ORIENT_LANDSCAPE_INVERT:
orientation =
MOTIONSENSE_ORIENTATION_UPSIDE_DOWN_LANDSCAPE;
break;
default:
break;
}
orientation = motion_orientation_remap(s, orientation);
*motion_orientation_ptr(s) = orientation;
}
}
#endif /* CONFIG_BMI_ORIENTATION_SENSOR */
/**
* bmi160_interrupt - called when the sensor activates the interrupt line.
*
* This is a "top half" interrupt handler, it just asks motion sense ask
* to schedule the "bottom half", ->irq_handler().
*/
void bmi160_interrupt(enum gpio_signal signal)
{
if (IS_ENABLED(CONFIG_ACCEL_FIFO))
last_interrupt_timestamp = __hw_clock_source_read();
task_set_event(TASK_ID_MOTIONSENSE, CONFIG_ACCELGYRO_BMI160_INT_EVENT);
}
/**
* irq_handler - bottom half of the interrupt stack.
* Ran from the motion_sense task, finds the events that raised the interrupt.
*
* For now, we just print out. We should set a bitmask motion sense code will
* act upon.
*/
static int irq_handler(struct motion_sensor_t *s,
uint32_t *event)
{
uint32_t interrupt;
int8_t has_read_fifo = 0;
int rv;
if ((s->type != MOTIONSENSE_TYPE_ACCEL) ||
(!(*event & CONFIG_ACCELGYRO_BMI160_INT_EVENT)))
return EC_ERROR_NOT_HANDLED;
do {
rv = bmi_read16(s->port, s->i2c_spi_addr_flags,
BMI160_INT_STATUS_0, &interrupt);
/*
* Bail out of this loop there was an error reading the register
*/
if (rv)
return rv;
if (IS_ENABLED(CONFIG_GESTURE_SENSOR_DOUBLE_TAP) &&
(interrupt & BMI160_D_TAP_INT))
*event |= TASK_EVENT_MOTION_ACTIVITY_INTERRUPT(
MOTIONSENSE_ACTIVITY_DOUBLE_TAP);
if (IS_ENABLED(CONFIG_GESTURE_SIGMO) &&
(interrupt & BMI160_SIGMOT_INT))
*event |= TASK_EVENT_MOTION_ACTIVITY_INTERRUPT(
MOTIONSENSE_ACTIVITY_SIG_MOTION);
if (IS_ENABLED(CONFIG_ACCEL_FIFO) &&
(interrupt & (BMI160_FWM_INT | BMI160_FFULL_INT))) {
bmi_load_fifo(s, last_interrupt_timestamp);
has_read_fifo = 1;
}
if (IS_ENABLED(CONFIG_BMI_ORIENTATION_SENSOR))
irq_set_orientation(s, interrupt);
} while (interrupt != 0);
if (IS_ENABLED(CONFIG_ACCEL_FIFO) && has_read_fifo)
motion_sense_fifo_commit_data();
return EC_SUCCESS;
}
#endif /* CONFIG_ACCEL_INTERRUPTS */
static int init(struct motion_sensor_t *s)
{
int ret = 0, tmp, i;
struct accelgyro_saved_data_t *saved_data = BMI_GET_SAVED_DATA(s);
ret = bmi_read8(s->port, s->i2c_spi_addr_flags,
BMI160_CHIP_ID, &tmp);
if (ret)
return EC_ERROR_UNKNOWN;
if (tmp != BMI160_CHIP_ID_MAJOR && tmp != BMI168_CHIP_ID_MAJOR) {
/* The device may be lock on paging mode. Try to unlock it. */
bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_CMD_REG, BMI160_CMD_EXT_MODE_EN_B0);
bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_CMD_REG, BMI160_CMD_EXT_MODE_EN_B1);
bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_CMD_REG, BMI160_CMD_EXT_MODE_EN_B2);
bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_CMD_EXT_MODE_ADDR, BMI160_CMD_PAGING_EN);
bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_CMD_EXT_MODE_ADDR, 0);
return EC_ERROR_ACCESS_DENIED;
}
if (s->type == MOTIONSENSE_TYPE_ACCEL) {
struct bmi_drv_data_t *data = BMI_GET_DATA(s);
/* Reset the chip to be in a good state */
bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_CMD_REG, BMI160_CMD_SOFT_RESET);
msleep(1);
data->flags &= ~(BMI_FLAG_SEC_I2C_ENABLED |
(BMI_FIFO_ALL_MASK <<
BMI_FIFO_FLAG_OFFSET));
if (IS_ENABLED(CONFIG_GESTURE_HOST_DETECTION)) {
data->enabled_activities = 0;
data->disabled_activities = 0;
if (IS_ENABLED(CONFIG_GESTURE_SIGMO))
data->disabled_activities |=
BIT(MOTIONSENSE_ACTIVITY_SIG_MOTION);
if (IS_ENABLED(CONFIG_GESTURE_SENSOR_DOUBLE_TAP))
data->disabled_activities |=
BIT(MOTIONSENSE_ACTIVITY_DOUBLE_TAP);
}
/* To avoid gyro wakeup */
bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_PMU_TRIGGER, 0);
}
#ifdef CONFIG_BMI_SEC_I2C
if (s->type == MOTIONSENSE_TYPE_MAG) {
struct bmi_drv_data_t *data = BMI_GET_DATA(s);
/*
* To be able to configure the real magnetometer, we must set
* the BMI160 magnetometer part (a pass through) in normal mode.
*/
bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_CMD_REG, BMI160_CMD_MODE_NORMAL(s->type));
msleep(wakeup_time[s->type]);
if ((data->flags & BMI_FLAG_SEC_I2C_ENABLED) == 0) {
int ext_page_reg;
/* Enable secondary interface */
/*
* This is not part of the normal configuration but from
* code on Bosh github repo:
* https://github.com/BoschSensortec/BMI160_driver
*
* Magic command sequences
*/
bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_CMD_REG, BMI160_CMD_EXT_MODE_EN_B0);
bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_CMD_REG, BMI160_CMD_EXT_MODE_EN_B1);
bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_CMD_REG, BMI160_CMD_EXT_MODE_EN_B2);
/*
* Change the register page to target mode, to change
* the internal pull ups of the secondary interface.
*/
bmi_enable_reg8(s, BMI160_CMD_EXT_MODE_ADDR,
BMI160_CMD_TARGET_PAGE, 1);
bmi_enable_reg8(s, BMI160_CMD_EXT_MODE_ADDR,
BMI160_CMD_PAGING_EN, 1);
bmi_enable_reg8(s, BMI160_COM_C_TRIM_ADDR,
BMI160_COM_C_TRIM, 1);
bmi_enable_reg8(s, BMI160_CMD_EXT_MODE_ADDR,
BMI160_CMD_TARGET_PAGE, 0);
bmi_read8(s->port, s->i2c_spi_addr_flags,
BMI160_CMD_EXT_MODE_ADDR, &ext_page_reg);
/* Set the i2c address of the compass */
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_MAG_IF_0,
I2C_STRIP_FLAGS(
CONFIG_ACCELGYRO_SEC_ADDR_FLAGS)
<< 1);
/* Enable the secondary interface as I2C */
ret = bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_IF_CONF,
BMI160_IF_MODE_AUTO_I2C <<
BMI160_IF_MODE_OFF);
data->flags |= BMI_FLAG_SEC_I2C_ENABLED;
}
bmi160_sec_access_ctrl(s->port, s->i2c_spi_addr_flags, 1);
ret = bmm150_init(s);
if (ret)
/* Leave the compass open for tinkering. */
return ret;
/* Leave the address for reading the data */
bmi_write8(s->port, s->i2c_spi_addr_flags,
BMI160_MAG_I2C_READ_ADDR, BMM150_BASE_DATA);
/*
* Put back the secondary interface in normal mode.
* BMI160 will poll based on the configure ODR.
*/
bmi160_sec_access_ctrl(s->port, s->i2c_spi_addr_flags, 0);
/*
* Clean interrupt event that may have occurred while the
* BMI160 was in management mode.
*/
task_set_event(TASK_ID_MOTIONSENSE,
CONFIG_ACCELGYRO_BMI160_INT_EVENT);
}
#endif
for (i = X; i <= Z; i++)
saved_data->scale[i] = MOTION_SENSE_DEFAULT_SCALE;
/*
* The sensor is in Suspend mode at init,
* so set data rate to 0.
*/
saved_data->odr = 0;
if (IS_ENABLED(CONFIG_ACCEL_INTERRUPTS) &&
(s->type == MOTIONSENSE_TYPE_ACCEL))
ret = config_interrupt(s);
return sensor_init_done(s);
}
const struct accelgyro_drv bmi160_drv = {
.init = init,
.read = bmi_read,
.set_range = bmi_set_range,
.get_resolution = bmi_get_resolution,
.set_data_rate = set_data_rate,
.get_data_rate = bmi_get_data_rate,
.set_offset = set_offset,
.get_scale = bmi_get_scale,
.set_scale = bmi_set_scale,
.get_offset = bmi_get_offset,
.perform_calib = perform_calib,
.read_temp = bmi_read_temp,
#ifdef CONFIG_ACCEL_INTERRUPTS
.irq_handler = irq_handler,
#endif
#ifdef CONFIG_GESTURE_HOST_DETECTION
.manage_activity = manage_activity,
.list_activities = list_activities,
#endif
#ifdef CONFIG_BODY_DETECTION
.get_rms_noise = bmi_get_rms_noise,
#endif
};
#ifdef CONFIG_CMD_I2C_STRESS_TEST_ACCEL
struct i2c_stress_test_dev bmi160_i2c_stress_test_dev = {
.reg_info = {
.read_reg = BMI160_CHIP_ID,
.read_val = BMI160_CHIP_ID_MAJOR,
.write_reg = BMI160_PMU_TRIGGER,
},
.i2c_read = &bmi_read8,
.i2c_write = &bmi_write8,
};
#endif /* CONFIG_CMD_I2C_STRESS_TEST_ACCEL */
/*
* TODO(chingkang): Replace bmi160_get_sensor_temp in some board config to
* bmi_get_sensor_temp. Then, remove this definition.
*/
int bmi160_get_sensor_temp(int idx, int *temp_ptr)
{
return bmi_get_sensor_temp(idx, temp_ptr);
}