summaryrefslogtreecommitdiff
path: root/driver/accelgyro_bmi160.c
diff options
context:
space:
mode:
authorGwendal Grignou <gwendal@chromium.org>2015-05-15 13:07:26 -0700
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-05-16 04:17:15 +0000
commit628bf151769308e99f6019ce0bbd143461b57046 (patch)
tree585fb1459ea1674cc5fe16e9de0a2c3dd272fbd2 /driver/accelgyro_bmi160.c
parent0a71b4418da9d2c2b26f38e2f30c012bfc13bcd4 (diff)
downloadchrome-ec-628bf151769308e99f6019ce0bbd143461b57046.tar.gz
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 <gwendal@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/271525 Reviewed-by: Sheng-liang Song <ssl@chromium.org> Reviewed-by: Vincent Palatin <vpalatin@chromium.org> Trybot-Ready: Vincent Palatin <vpalatin@chromium.org>
Diffstat (limited to 'driver/accelgyro_bmi160.c')
-rw-r--r--driver/accelgyro_bmi160.c224
1 files changed, 190 insertions, 34 deletions
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",