/* 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(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);
	s->current_range = 1 << (16 + data->range);
	return EC_SUCCESS;
}

/*
 * bmp280_init() - Used to initialize barometer with default config
 *
 * @return results of bus communication function
 * @retval 0 -> Success
 */

static int bmp280_init(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,
	.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 */
