blob: 56376c8aa1766ef3aab9883f146a0c3398679ee7 [file] [log] [blame]
/* 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.
*/
/*
****************************************************************************
* Copyright (C) 2012 - 2015 Bosch Sensortec GmbH
*
* File : bmp280.h
*
* Date : 2015/03/27
*
* Revision : 2.0.4(Pressure and Temperature compensation code revision is 1.1)
*
* Usage: Sensor Driver for BMP280 sensor
*
****************************************************************************
*
* \section License
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of the copyright holder nor the names of the
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
*
* The information provided is believed to be accurate and reliable.
* The copyright holder assumes no responsibility
* for the consequences of use
* of such information nor for any infringement of patents or
* other rights of third parties which may result from its use.
* No license is granted by implication or otherwise under any patent or
* patent rights of the copyright holder.
**************************************************************************/
#include "accelgyro.h"
#include "common.h"
#include "console.h"
#include "driver/baro_bmp280.h"
#include "i2c.h"
#include "timer.h"
#define CPRINTF(format, args...) cprintf(CC_ACCEL, format, ## args)
#define CPRINTS(format, args...) cprints(CC_ACCEL, format, ## args)
static const uint16_t standby_durn[] = {1, 63, 125, 250, 500, 1000, 2000, 4000};
/*
* This function is used to get calibration parameters used for
* calculation in the registers
*
* parameter | Register address | bit
*------------|------------------|----------------
* dig_T1 | 0x88 and 0x89 | from 0 : 7 to 8: 15
* dig_T2 | 0x8A and 0x8B | from 0 : 7 to 8: 15
* dig_T3 | 0x8C and 0x8D | from 0 : 7 to 8: 15
* dig_P1 | 0x8E and 0x8F | from 0 : 7 to 8: 15
* dig_P2 | 0x90 and 0x91 | from 0 : 7 to 8: 15
* dig_P3 | 0x92 and 0x93 | from 0 : 7 to 8: 15
* dig_P4 | 0x94 and 0x95 | from 0 : 7 to 8: 15
* dig_P5 | 0x96 and 0x97 | from 0 : 7 to 8: 15
* dig_P6 | 0x98 and 0x99 | from 0 : 7 to 8: 15
* dig_P7 | 0x9A and 0x9B | from 0 : 7 to 8: 15
* dig_P8 | 0x9C and 0x9D | from 0 : 7 to 8: 15
* dig_P9 | 0x9E and 0x9F | from 0 : 7 to 8: 15
*
* @return results of bus communication function
* @retval 0 -> Success
*
*/
static int bmp280_get_calib_param(const struct motion_sensor_t *s)
{
int ret;
uint8_t a_data_u8[BMP280_CALIB_DATA_SIZE] = {0};
struct bmp280_drv_data_t *data = BMP280_GET_DATA(s);
ret = i2c_read_block(s->port, s->i2c_spi_addr_flags,
BMP280_TEMPERATURE_CALIB_DIG_T1_LSB_REG,
a_data_u8, BMP280_CALIB_DATA_SIZE);
if (ret)
return ret;
/* read calibration values*/
data->calib_param.dig_T1 = (a_data_u8[1] << 8) | a_data_u8[0];
data->calib_param.dig_T2 = (a_data_u8[3] << 8 | a_data_u8[2]);
data->calib_param.dig_T3 = (a_data_u8[5] << 8) | a_data_u8[4];
data->calib_param.dig_P1 = (a_data_u8[7] << 8) | a_data_u8[6];
data->calib_param.dig_P2 = (a_data_u8[9] << 8) | a_data_u8[8];
data->calib_param.dig_P3 = (a_data_u8[11] << 8) | a_data_u8[10];
data->calib_param.dig_P4 = (a_data_u8[13] << 8) | a_data_u8[12];
data->calib_param.dig_P5 = (a_data_u8[15] << 8) | a_data_u8[14];
data->calib_param.dig_P6 = (a_data_u8[17] << 8) | a_data_u8[16];
data->calib_param.dig_P7 = (a_data_u8[19] << 8) | a_data_u8[18];
data->calib_param.dig_P8 = (a_data_u8[21] << 8) | a_data_u8[20];
data->calib_param.dig_P9 = (a_data_u8[23] << 8) | a_data_u8[22];
return EC_SUCCESS;
}
static int bmp280_read_uncomp_pressure(const struct motion_sensor_t *s,
int *uncomp_pres)
{
int ret;
uint8_t a_data_u8[BMP280_PRESSURE_DATA_SIZE] = {0};
ret = i2c_read_block(s->port, s->i2c_spi_addr_flags,
BMP280_PRESSURE_MSB_REG,
a_data_u8, BMP280_PRESSURE_DATA_SIZE);
if (ret)
return ret;
*uncomp_pres = (int32_t)((a_data_u8[0] << 12) |
(a_data_u8[1] << 4) |
(a_data_u8[2] >> 4));
return EC_SUCCESS;
}
/*
* Reads actual pressure from uncompensated pressure
* and returns the value in Pascal(Pa)
* @note Output value of "96386" equals 96386 Pa =
* 963.86 hPa = 963.86 millibar
*
* Algorithm from BMP280 Datasheet Rev 1.15 Section 8.2
*
*/
static int bmp280_compensate_pressure(const struct motion_sensor_t *s,
int uncomp_pressure)
{
int var1, var2;
uint32_t p;
struct bmp280_drv_data_t *data = BMP280_GET_DATA(s);
/* calculate x1 */
var1 = (((int32_t)data->calib_param.t_fine)
>> 1) - 64000;
/* calculate x2 */
var2 = (((var1 >> 2) * (var1 >> 2)) >> 11)
* ((int32_t)data->calib_param.dig_P6);
var2 = var2 + ((var1 * ((int32_t)data->calib_param.dig_P5)) << 1);
var2 = (var2 >> 2) + (((int32_t)data->calib_param.dig_P4) << 16);
/* calculate x1 */
var1 = (((data->calib_param.dig_P3 *
(((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) +
((((int32_t)data->calib_param.dig_P2) * var1) >> 1)) >> 18;
var1 = ((((32768 + var1)) *
((int32_t)data->calib_param.dig_P1)) >> 15);
/* Avoid exception caused by division by zero */
if (!var1)
return 0;
/* calculate pressure */
p = (((uint32_t)((1048576) - uncomp_pressure) -
(var2 >> 12))) * 3125;
/* check overflow */
if (p < 0x80000000)
p = (p << 1) / ((uint32_t)var1);
else
p = (p / (uint32_t)var1) << 1;
/* calculate x1 */
var1 = (((int32_t)data->calib_param.dig_P9) *
((int32_t)(((p >> 3) * (p >> 3)) >> 13))) >> 12;
/* calculate x2 */
var2 = (((int32_t)(p >> 2)) *
((int32_t)data->calib_param.dig_P8)) >> 13;
/* calculate true pressure */
return (uint32_t)((int32_t)p + ((var1 + var2 +
data->calib_param.dig_P7) >> 4));
}
/*
* Set the standby duration
* standby_durn: The standby duration time value.
* value | standby duration
* ----------|--------------------
* 0x00 | 1_MS
* 0x01 | 63_MS
* 0x02 | 125_MS
* 0x03 | 250_MS
* 0x04 | 500_MS
* 0x05 | 1000_MS
* 0x06 | 2000_MS
* 0x07 | 4000_MS
*/
static int bmp280_set_standby_durn(const struct motion_sensor_t *s,
uint8_t durn)
{
int ret, val;
ret = i2c_read8(s->port, s->i2c_spi_addr_flags,
BMP280_CONFIG_REG, &val);
if (ret == EC_SUCCESS) {
val = (val & 0xE0) | ((durn << 5) & 0xE0);
/* write the standby duration*/
ret = i2c_write8(s->port, s->i2c_spi_addr_flags,
BMP280_CONFIG_REG, val);
}
return ret;
}
static int bmp280_set_power_mode(const struct motion_sensor_t *s,
uint8_t power_mode)
{
int val;
val = (BMP280_OVERSAMP_TEMP << 5) +
(BMP280_OVERSAMP_PRES << 2) + power_mode;
return i2c_write8(s->port, s->i2c_spi_addr_flags,
BMP280_CTRL_MEAS_REG, val);
}
static int bmp280_set_range(const struct motion_sensor_t *s,
int range,
int rnd)
{
struct bmp280_drv_data_t *data = BMP280_GET_DATA(s);
/*
* ->range contains the number of bit to right shift in order for the
* measurment to fit into 16 bits (or less if the AP wants to).
*/
data->range = 15 - __builtin_clz(range);
return EC_SUCCESS;
}
static int bmp280_get_range(const struct motion_sensor_t *s)
{
struct bmp280_drv_data_t *data = BMP280_GET_DATA(s);
return 1 << (16 + data->range);
}
/*
* bmp280_init() - Used to initialize barometer with default config
*
* @return results of bus communication function
* @retval 0 -> Success
*/
static int bmp280_init(const struct motion_sensor_t *s)
{
int val, ret;
if (!s)
return EC_ERROR_INVAL;
/* Read chip id */
ret = i2c_read8(s->port, s->i2c_spi_addr_flags,
BMP280_CHIP_ID_REG, &val);
if (ret)
return ret;
if (val != BMP280_CHIP_ID)
return EC_ERROR_INVAL;
/* set power mode */
ret = bmp280_set_power_mode(s, BMP280_SLEEP_MODE);
if (ret)
return ret;
/* Read bmp280 calibration parameter */
ret = bmp280_get_calib_param(s);
if (ret)
return ret;
return sensor_init_done(s);
}
static int bmp280_read(const struct motion_sensor_t *s, intv3_t v)
{
int ret, pres;
struct bmp280_drv_data_t *data = BMP280_GET_DATA(s);
ret = bmp280_read_uncomp_pressure(s, &pres);
if (ret)
return ret;
v[0] = bmp280_compensate_pressure(s, pres) >> data->range;
v[1] = v[2] = 0;
return EC_SUCCESS;
}
/*
* Set data rate, rate in mHz.
* Calculate the delay (in ms) to apply.
*/
static int bmp280_set_data_rate(const struct motion_sensor_t *s, int rate,
int roundup)
{
struct bmp280_drv_data_t *data = BMP280_GET_DATA(s);
int durn, i, ret;
int period; /* Period in ms */
if (rate == 0) {
/* Set to sleep mode */
data->rate = 0;
return bmp280_set_power_mode(s, BMP280_SLEEP_MODE);
} else
period = 1000000 / rate;
/* reset power mode, waking from sleep */
if (!data->rate) {
ret = bmp280_set_power_mode(s, BMP280_NORMAL_MODE);
if (ret)
return ret;
}
durn = 0;
for (i = BMP280_STANDBY_CNT-1; i > 0; i--) {
if (period >= standby_durn[i] + BMP280_COMPUTE_TIME) {
durn = i;
break;
} else if (period > standby_durn[i-1] + BMP280_COMPUTE_TIME) {
durn = roundup ? i-1 : i;
break;
}
}
ret = bmp280_set_standby_durn(s, durn);
if (ret == EC_SUCCESS)
/*
* The maximum frequency is around 76Hz. Be sure it fits in 16
* bits by shifting by one bit.
*/
data->rate = (1000000 >> BMP280_RATE_SHIFT) /
(standby_durn[durn] + BMP280_COMPUTE_TIME);
return ret;
}
static int bmp280_get_data_rate(const struct motion_sensor_t *s)
{
struct bmp280_drv_data_t *data = BMP280_GET_DATA(s);
return data->rate << BMP280_RATE_SHIFT;
}
const struct accelgyro_drv bmp280_drv = {
.init = bmp280_init,
.read = bmp280_read,
.set_range = bmp280_set_range,
.get_range = bmp280_get_range,
.set_data_rate = bmp280_set_data_rate,
.get_data_rate = bmp280_get_data_rate,
};
#ifdef CONFIG_CMD_I2C_STRESS_TEST_ACCEL
struct i2c_stress_test_dev bmp280_i2c_stress_test_dev = {
.reg_info = {
.read_reg = BMP280_CHIP_ID_REG,
.read_val = BMP280_CHIP_ID,
.write_reg = BMP280_CONFIG_REG,
},
.i2c_read = &i2c_read8,
.i2c_write = &i2c_write8,
};
#endif /* CONFIG_CMD_I2C_STRESS_TEST_ACCEL */