From 628bf151769308e99f6019ce0bbd143461b57046 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Fri, 15 May 2015 13:07:26 -0700 Subject: driver: Add BMM150 behind BMI160 support. Add support for Bosh Sensortec BMM160 compass. We access it through BMI150. BRANCH=none BUG=chrome-os-partner:39900 TEST=Test on a nucleo board and smaug. Change-Id: I5b959cab4f9341ba0fcd3ed9bad815fa92f80a37 Signed-off-by: Gwendal Grignou Reviewed-on: https://chromium-review.googlesource.com/271525 Reviewed-by: Sheng-liang Song Reviewed-by: Vincent Palatin Trybot-Ready: Vincent Palatin --- board/ryu/board.c | 22 ++++- board/ryu/board.h | 2 + common/motion_sense.c | 2 +- driver/accelgyro_bmi160.c | 224 +++++++++++++++++++++++++++++++++++++++------- driver/accelgyro_bmi160.h | 42 ++++++++- driver/mag_bmm150.h | 37 ++++++++ include/config.h | 4 + 7 files changed, 291 insertions(+), 42 deletions(-) create mode 100644 driver/mag_bmm150.h diff --git a/board/ryu/board.c b/board/ryu/board.c index a361198748..7218a9f8fc 100644 --- a/board/ryu/board.c +++ b/board/ryu/board.c @@ -368,7 +368,7 @@ const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports); static struct mutex g_mutex; /* local sensor data (per-sensor) */ -struct motion_data_t g_saved_data[2]; +struct bmi160_drv_data_t g_bmi160_data; struct motion_sensor_t motion_sensors[] = { @@ -384,7 +384,7 @@ struct motion_sensor_t motion_sensors[] = { .location = MOTIONSENSE_LOC_LID, .drv = &bmi160_drv, .mutex = &g_mutex, - .drv_data = &g_saved_data[0], + .drv_data = &g_bmi160_data, .i2c_addr = BMI160_ADDR0, .rot_standard_ref = NULL, .default_config = { @@ -400,7 +400,7 @@ struct motion_sensor_t motion_sensors[] = { .location = MOTIONSENSE_LOC_LID, .drv = &bmi160_drv, .mutex = &g_mutex, - .drv_data = &g_saved_data[1], + .drv_data = &g_bmi160_data, .i2c_addr = BMI160_ADDR0, .rot_standard_ref = NULL, .default_config = { @@ -408,6 +408,22 @@ struct motion_sensor_t motion_sensors[] = { .range = 2000 } }, + + {.name = "Mag", + .active_mask = SENSOR_ACTIVE_S0_S3, + .chip = MOTIONSENSE_CHIP_BMI160, + .type = MOTIONSENSE_TYPE_MAG, + .location = MOTIONSENSE_LOC_LID, + .drv = &bmi160_drv, + .mutex = &g_mutex, + .drv_data = &g_bmi160_data, + .i2c_addr = BMI160_ADDR0, + .rot_standard_ref = NULL, + .default_config = { + .odr = 25000, + .range = 1 + } + }, }; const unsigned int motion_sensor_count = ARRAY_SIZE(motion_sensors); diff --git a/board/ryu/board.h b/board/ryu/board.h index 3b92e8d2d3..327b8b8055 100644 --- a/board/ryu/board.h +++ b/board/ryu/board.h @@ -78,6 +78,7 @@ #define I2C_PORT_BATTERY I2C_PORT_MASTER #define I2C_PORT_LIGHTBAR I2C_PORT_MASTER #define I2C_PORT_ACCEL I2C_PORT_MASTER +#define BMM150_I2C_ADDRESS BMM150_ADDR0 /* slave address for host commands */ #ifdef HAS_TASK_HOSTCMD @@ -126,6 +127,7 @@ /* Sensor support */ #define CONFIG_ACCELGYRO_BMI160 +#define CONFIG_MAG_BMI160_BMM150 #define CONFIG_CMD_ACCELS #define CONFIG_CMD_ACCEL_INFO diff --git a/common/motion_sense.c b/common/motion_sense.c index 0ae21e5056..030d5264d3 100644 --- a/common/motion_sense.c +++ b/common/motion_sense.c @@ -694,7 +694,7 @@ static int command_accel_init(int argc, char **argv) sensor = &motion_sensors[id]; motion_sense_init(sensor); - ccprintf("%s\n", sensor->name); + ccprintf("%s: %d\n", sensor->name, sensor->state); return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(accelinit, command_accel_init, diff --git a/driver/accelgyro_bmi160.c b/driver/accelgyro_bmi160.c index 469950f07d..dd0ca67768 100644 --- a/driver/accelgyro_bmi160.c +++ b/driver/accelgyro_bmi160.c @@ -12,6 +12,7 @@ #include "common.h" #include "console.h" #include "driver/accelgyro_bmi160.h" +#include "driver/mag_bmm150.h" #include "hooks.h" #include "i2c.h" #include "task.h" @@ -131,6 +132,51 @@ static inline int raw_write8(const int addr, const int reg, int data) return i2c_write8(I2C_PORT_ACCEL, addr, reg, data); } +#ifdef CONFIG_MAG_BMI160_BMM150 +/** + * 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 int bmm150_mag_access_ctrl(const int addr, const int enable) +{ + int mag_if_ctrl; + raw_read8(addr, 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 raw_write8(addr, BMI160_MAG_IF_1, mag_if_ctrl); +} + +/** + * Read register from compass. + * Assuming we are in manual access mode, read compass i2c register. + */ +static int raw_mag_read8(const int addr, const int reg, int *data_ptr) +{ + /* Only read 1 bytes */ + raw_write8(addr, BMI160_MAG_I2C_READ_ADDR, reg); + return raw_read8(addr, BMI160_MAG_I2C_READ_DATA, data_ptr); +} + +/** + * Write register from compass. + * Assuming we are in manual access mode, write to compass i2c register. + */ +static int raw_mag_write8(const int addr, const int reg, int data) +{ + raw_write8(addr, BMI160_MAG_I2C_WRITE_DATA, data); + return raw_write8(addr, BMI160_MAG_I2C_WRITE_ADDR, reg); +} +#endif + static int set_range(const struct motion_sensor_t *s, int range, int rnd) @@ -138,7 +184,13 @@ static int set_range(const struct motion_sensor_t *s, int ret, range_tbl_size; uint8_t reg_val, ctrl_reg; const struct accel_param_pair *ranges; - struct motion_data_t *data = (struct motion_data_t *)s->drv_data; + struct motion_data_t *data = + &((struct bmi160_drv_data_t *)s->drv_data)->saved_data[s->type]; + + if (s->type == MOTIONSENSE_TYPE_MAG) { + data->range = range; + return EC_SUCCESS; + } ctrl_reg = BMI160_RANGE_REG(s->type); ranges = get_range_table(s->type, &range_tbl_size); @@ -155,7 +207,8 @@ static int set_range(const struct motion_sensor_t *s, static int get_range(const struct motion_sensor_t *s, int *range) { - struct motion_data_t *data = (struct motion_data_t *)s->drv_data; + struct motion_data_t *data = + &((struct bmi160_drv_data_t *)s->drv_data)->saved_data[s->type]; *range = data->range; return EC_SUCCESS; @@ -182,12 +235,13 @@ static int set_data_rate(const struct motion_sensor_t *s, { int ret, val, normalized_rate; uint8_t ctrl_reg, reg_val; - struct motion_data_t *data = s->drv_data; + struct motion_data_t *data = + &((struct bmi160_drv_data_t *)s->drv_data)->saved_data[s->type]; if (rate == 0) { /* suspend */ ret = raw_write8(s->i2c_addr, BMI160_CMD_REG, - BMI150_CMD_MODE_SUSPEND(s->type)); + BMI160_CMD_MODE_SUSPEND(s->type)); msleep(30); return ret; } @@ -218,6 +272,16 @@ static int set_data_rate(const struct motion_sensor_t *s, normalized_rate = 25000; } break; + case MOTIONSENSE_TYPE_MAG: + if (reg_val > BMI160_ODR_800HZ) { + reg_val = BMI160_ODR_800HZ; + normalized_rate = 800000; + } else if (reg_val < BMI160_ODR_0_78HZ) { + reg_val = BMI160_ODR_0_78HZ; + normalized_rate = 780; + } + break; + default: return -1; } @@ -247,12 +311,44 @@ accel_cleanup: static int get_data_rate(const struct motion_sensor_t *s, int *rate) { - struct motion_data_t *data = s->drv_data; + struct motion_data_t *data = + &((struct bmi160_drv_data_t *)s->drv_data)->saved_data[s->type]; *rate = data->odr; return EC_SUCCESS; } +void normalize(const struct motion_sensor_t *s, vector_3_t v, uint8_t *data) +{ + int range; + + v[0] = ((int16_t)((data[1] << 8) | data[0])); + v[1] = ((int16_t)((data[3] << 8) | data[2])); + v[2] = ((int16_t)((data[5] << 8) | data[4])); + + get_range(s, &range); + + v[0] *= range; + v[1] *= range; + v[2] *= range; + + switch (s->type) { + case MOTIONSENSE_TYPE_ACCEL: + /* normalize the accel scale: 1G = 1024 */ + v[0] >>= 5; + v[1] >>= 5; + v[2] >>= 5; + break; + case MOTIONSENSE_TYPE_GYRO: + v[0] >>= 8; + v[1] >>= 8; + v[2] >>= 8; + break; + default: + break; + } +} + #ifdef CONFIG_ACCEL_INTERRUPTS static int set_interrupt(const struct motion_sensor_t *s, unsigned int threshold) @@ -281,7 +377,7 @@ static int read(const struct motion_sensor_t *s, vector_3_t v) { uint8_t data[6]; uint8_t xyz_reg; - int ret, tmp = 0, range = 0; + int ret, tmp = 0; ret = is_data_ready(s, &tmp); if (ret != EC_SUCCESS) @@ -312,30 +408,7 @@ static int read(const struct motion_sensor_t *s, vector_3_t v) s->name, s->type, ret); return ret; } - - v[0] = ((int16_t)((data[1] << 8) | data[0])); - v[1] = ((int16_t)((data[3] << 8) | data[2])); - v[2] = ((int16_t)((data[5] << 8) | data[4])); - - ret = get_range(s, &range); - if (ret) - return EC_ERROR_UNKNOWN; - - v[0] *= range; - v[1] *= range; - v[2] *= range; - - /* normalize the accel scale: 1G = 1024 */ - if (MOTIONSENSE_TYPE_ACCEL == s->type) { - v[0] >>= 5; - v[1] >>= 5; - v[2] >>= 5; - } else { - v[0] >>= 8; - v[1] >>= 8; - v[2] >>= 8; - } - + normalize(s, v, data); return EC_SUCCESS; } @@ -352,23 +425,106 @@ static int init(const struct motion_sensor_t *s) if (s->type == MOTIONSENSE_TYPE_ACCEL) { + struct bmi160_drv_data_t *data = + (struct bmi160_drv_data_t *)s->drv_data; + + /* Reset the chip to be in a good state */ raw_write8(s->i2c_addr, BMI160_CMD_REG, BMI160_CMD_SOFT_RESET); msleep(30); + data->flags &= ~BMI160_FLAG_SEC_I2C_ENABLED; /* To avoid gyro wakeup */ raw_write8(s->i2c_addr, BMI160_PMU_TRIGGER, 0); } raw_write8(s->i2c_addr, BMI160_CMD_REG, - BMI150_CMD_MODE_NORMAL(s->type)); + BMI160_CMD_MODE_NORMAL(s->type)); msleep(30); set_range(s, s->runtime_config.range, 0); - msleep(30); - set_data_rate(s, s->runtime_config.odr, 0); - msleep(30); +#ifdef CONFIG_MAG_BMI160_BMM150 + if (s->type == MOTIONSENSE_TYPE_MAG) { + struct bmi160_drv_data_t *data = + (struct bmi160_drv_data_t *)s->drv_data; + if ((data->flags & BMI160_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 + */ + raw_write8(s->i2c_addr, BMI160_CMD_REG, + BMI160_CMD_EXT_MODE_EN_B0); + raw_write8(s->i2c_addr, BMI160_CMD_REG, + BMI160_CMD_EXT_MODE_EN_B1); + raw_write8(s->i2c_addr, 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. + */ + raw_read8(s->i2c_addr, BMI160_CMD_EXT_MODE_ADDR, + &ext_page_reg); + raw_write8(s->i2c_addr, BMI160_CMD_EXT_MODE_ADDR, + ext_page_reg | BMI160_CMD_TARGET_PAGE); + raw_read8(s->i2c_addr, BMI160_CMD_EXT_MODE_ADDR, + &ext_page_reg); + raw_write8(s->i2c_addr, BMI160_CMD_EXT_MODE_ADDR, + ext_page_reg | BMI160_CMD_PAGING_EN); + raw_write8(s->i2c_addr, BMI160_COM_C_TRIM_ADDR, + BMI160_COM_C_TRIM); + raw_read8(s->i2c_addr, BMI160_CMD_EXT_MODE_ADDR, + &ext_page_reg); + raw_write8(s->i2c_addr, BMI160_CMD_EXT_MODE_ADDR, + ext_page_reg & ~BMI160_CMD_TARGET_PAGE); + raw_read8(s->i2c_addr, BMI160_CMD_EXT_MODE_ADDR, + &ext_page_reg); + + /* Set the i2c address of the compass */ + ret = raw_write8(s->i2c_addr, BMI160_MAG_IF_0, + BMM150_I2C_ADDRESS); + + /* Enable the secondary interface as I2C */ + ret = raw_write8(s->i2c_addr, BMI160_IF_CONF, + BMI160_IF_MODE_AUTO_I2C << BMI160_IF_MODE_OFF); + data->flags |= BMI160_FLAG_SEC_I2C_ENABLED; + } + + + bmm150_mag_access_ctrl(s->i2c_addr, 1); + /* Set the compass from Suspend to Sleep */ + ret = raw_mag_write8(s->i2c_addr, BMM150_PWR_CTRL, + BMM150_PWR_ON); + /* Now we can read the device id */ + ret = raw_mag_read8(s->i2c_addr, BMM150_CHIP_ID, &tmp); + if (ret) + return EC_ERROR_UNKNOWN; + + if (tmp != BMM150_CHIP_ID_MAJOR) + return EC_ERROR_ACCESS_DENIED; + + /* Leave the address for reading the data */ + raw_write8(s->i2c_addr, BMI160_MAG_I2C_READ_ADDR, + BMM150_BASE_DATA); + /* + * Set the compass forced mode, to sleep after each measure. + */ + ret = raw_mag_write8(s->i2c_addr, BMM150_OP_CTRL, + BMM150_OP_MODE_FORCED << BMM150_OP_MODE_OFFSET); + + /* + * Put back the secondary interface in normal mode. + * BMI160 will poll based on the configure ODR. + */ + bmm150_mag_access_ctrl(s->i2c_addr, 0); + } +#endif /* Fifo setup is done elsewhere */ CPRINTF("[%T %s: MS Done Init type:0x%X range:%d odr:%d]\n", diff --git a/driver/accelgyro_bmi160.h b/driver/accelgyro_bmi160.h index 7ea98a034f..51675d3248 100644 --- a/driver/accelgyro_bmi160.h +++ b/driver/accelgyro_bmi160.h @@ -102,8 +102,9 @@ /* odr = 100 / (1 << (8 - reg)) ,within limit */ #define BMI160_ODR_0_78HZ 0x01 #define BMI160_ODR_25HZ 0x06 -#define BMI160_ODR_1600HZ 0x0C -#define BMI160_ODR_3200HZ 0x0D +#define BMI160_ODR_800HZ 0x0b +#define BMI160_ODR_1600HZ 0x0c +#define BMI160_ODR_3200HZ 0x0d #define BMI160_REG_TO_ODR(_regval) (100000 / (1 << (8 - (_regval)))) #define BMI160_ODR_TO_REG(_odr) (__builtin_clz(100000 / (_odr)) - 23) @@ -116,10 +117,25 @@ #define BMI160_FIFO_CONFIG_1 0x47 #define BMI160_MAG_IF_0 0x4b +#define BMI160_MAG_I2C_ADDRESS BMI160_MAG_IF_0 #define BMI160_MAG_IF_1 0x4c +#define BMI160_MAG_I2C_CONTROL BMI160_MAG_IF_1 +#define BMI160_MAG_READ_BURST_MASK 3 +#define BMI160_MAG_READ_BURST_1 0 +#define BMI160_MAG_READ_BURST_2 1 +#define BMI160_MAG_READ_BURST_6 2 +#define BMI160_MAG_READ_BURST_8 3 +#define BMI160_MAG_OFFSET_OFF 3 +#define BMI160_MAG_OFFSET_MASK (0xf << BMI160_MAG_OFFSET_OFF) +#define BMI160_MAG_MANUAL_EN (1 << 7) + #define BMI160_MAG_IF_2 0x4d +#define BMI160_MAG_I2C_READ_ADDR BMI160_MAG_IF_2 #define BMI160_MAG_IF_3 0x4e +#define BMI160_MAG_I2C_WRITE_ADDR BMI160_MAG_IF_3 #define BMI160_MAG_IF_4 0x4f +#define BMI160_MAG_I2C_WRITE_DATA BMI160_MAG_IF_4 +#define BMI160_MAG_I2C_READ_DATA BMI160_MAG_X_L_G #define BMI160_INT_EN_0 0x50 #define BMI160_INT_EN_1 0x51 @@ -152,6 +168,11 @@ #define BMI160_FOC_CONF 0x69 #define BMI160_CONF 0x6a #define BMI160_IF_CONF 0x6b +#define BMI160_IF_MODE_OFF 4 +#define BMI160_IF_MODE_MASK 3 +#define BMI160_IF_MODE_AUTO_OFF 0 +#define BMI160_IF_MODE_I2C_IOS 1 +#define BMI160_IF_MODE_AUTO_I2C 2 #define BMI160_PMU_TRIGGER 0x6c #define BMI160_SELF_TEST 0x6d @@ -168,8 +189,8 @@ #define BMI160_CMD_MAG_MODE_SUSP 0x18 #define BMI160_CMD_MAG_MODE_NORMAL 0x19 #define BMI160_CMD_MAG_MODE_LOWPOWER 0x1a -#define BMI150_CMD_MODE_NORMAL(_sensor) (0x11 + 4 * (_sensor)) -#define BMI150_CMD_MODE_SUSPEND(_sensor) (0x10 + 4 * (_sensor)) +#define BMI160_CMD_MODE_NORMAL(_sensor) (0x11 + 4 * (_sensor)) +#define BMI160_CMD_MODE_SUSPEND(_sensor) (0x10 + 4 * (_sensor)) #define BMI160_CMD_FIFO_FLUSH 0xb0 #define BMI160_CMD_INT_RESET 0xb1 @@ -178,6 +199,14 @@ #define BMI160_CMD_EXT_MODE_EN_B1 0x9a #define BMI160_CMD_EXT_MODE_EN_B2 0xc0 +#define BMI160_CMD_EXT_MODE_ADDR 0x7f +#define BMI160_CMD_PAGING_EN (1 << 7) +#define BMI160_CMD_TARGET_PAGE (1 << 4) +#define BMI160_COM_C_TRIM_ADDR 0x85 +#define BMI160_COM_C_TRIM (3 << 4) + + + #define BMI160_CMD_TGT_PAGE 0 #define BMI160_CMD_TGT_PAGE_COM 1 #define BMI160_CMD_TGT_PAGE_ACC 2 @@ -251,4 +280,9 @@ enum bmi160_running_mode { APPLICATION_INDOOR_NAVIGATION = 8, }; +#define BMI160_FLAG_SEC_I2C_ENABLED (1 << 0) +struct bmi160_drv_data_t { + struct motion_data_t saved_data[3]; + uint8_t flags; +}; #endif /* __CROS_EC_ACCEL_BMI160_H */ diff --git a/driver/mag_bmm150.h b/driver/mag_bmm150.h new file mode 100644 index 0000000000..27ca5aebb6 --- /dev/null +++ b/driver/mag_bmm150.h @@ -0,0 +1,37 @@ +/* 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. + */ + +/* BMM150 magnetometer definition */ + +#ifndef __CROS_EC_MAG_BMM150_H +#define __CROS_EC_MAG_BMM150_H + +#include "accelgyro.h" + +#define BMM150_ADDR0 0x20 +#define BMM150_ADDR1 0x22 +#define BMM150_ADDR2 0x24 +#define BMM150_ADDR3 0x26 + +#define BMM150_CHIP_ID 0x40 +#define BMM150_CHIP_ID_MAJOR 0x32 + +#define BMM150_BASE_DATA 0x42 + +#define BMM150_INT_STATUS 0x4a +#define BMM150_PWR_CTRL 0x4b +#define BMM150_SRST ((1 << 7) | (1 << 1)) +#define BMM150_PWR_ON (1 << 0) + +#define BMM150_OP_CTRL 0x4c +#define BMM150_OP_MODE_OFFSET 1 +#define BMM150_OP_MODE_MASK 3 +#define BMM150_OP_MODE_NORMAL 0x00 +#define BMM150_OP_MODE_FORCED 0x01 +#define BMM150_OP_MODE_SLEEP 0x03 + +#define BMM150_INT_CTRL 0x4d + +#endif /* __CROS_EC_MAG_BMM150_H */ diff --git a/include/config.h b/include/config.h index 7bccb41a27..36c1b3f53a 100644 --- a/include/config.h +++ b/include/config.h @@ -42,6 +42,7 @@ /* Specify type of accelerometers attached. */ #undef CONFIG_ACCEL_KXCJ9 #undef CONFIG_ACCELGYRO_LSM6DS0 +#undef CONFIG_ACCELGYRO_BMI160 /* Compile chip support for analog-to-digital convertor */ #undef CONFIG_ADC @@ -969,6 +970,9 @@ /* Support LPC interface */ #undef CONFIG_LPC +/* Presence of a Bosh Sensortec BMM150 magnetometer behind a BMI160. */ +#undef CONFIG_MAG_BMI160_BMM150 + /* Support MKBP event */ #undef CONFIG_MKBP_EVENT -- cgit v1.2.1