/* 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"
#include "mag_cal.h"

#define BMM150_ADDR0_FLAGS       0x10
#define BMM150_ADDR1_FLAGS       0x11
#define BMM150_ADDR2_FLAGS       0x12
#define BMM150_ADDR3_FLAGS       0x13

#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                  (BIT(7) | BIT(1))
#define BMM150_PWR_ON                BIT(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

#define BMM150_REPXY             0x51
#define BMM150_LOW_POWER_nXY         3
#define BMM150_REGULAR_nXY           9
#define BMM150_ENHANCED_nXY         15
#define BMM150_HIGH_ACCURACY_nXY    47
#define BMM150_SPECIAL_nXY          75
#define BMM150_REPZ              0x52
#define BMM150_LOW_POWER_nZ          3
#define BMM150_REGULAR_nZ           15
#define BMM150_ENHANCED_nZ          27
#define BMM150_HIGH_ACCURACY_nZ     83
#define BMM150_SPECIAL_nZ           27

#define BMM150_REP(_preset, _axis) CONCAT4(BMM150_, _preset, _n, _axis)

/* Hidden registers for RHALL calculation */
#define BMM150_REGA_DIG_X1       0x5d
#define BMM150_REGA_DIG_Y1       0x5e
#define BMM150_REGA_DIG_Z4_LSB   0x62
#define BMM150_REGA_DIG_Z4_MSB   0x63
#define BMM150_REGA_DIG_X2       0x64
#define BMM150_REGA_DIG_Y2       0x65
#define BMM150_REGA_DIG_Z2_LSB   0x68
#define BMM150_REGA_DIG_Z2_MSB   0x69
#define BMM150_REGA_DIG_Z1_LSB   0x6a
#define BMM150_REGA_DIG_Z1_MSB   0x6b
#define BMM150_REGA_DIG_XYZ1_LSB 0x6c
#define BMM150_REGA_DIG_XYZ1_MSB 0x6d
#define BMM150_REGA_DIG_Z3_LSB   0x6e
#define BMM150_REGA_DIG_Z3_MSB   0x6f
#define BMM150_REGA_DIG_XY2      0x70
#define BMM150_REGA_DIG_XY1      0x71

/* Overflow */

#define BMM150_FLIP_OVERFLOW_ADCVAL             (-4096)
#define BMM150_HALL_OVERFLOW_ADCVAL             (-16384)
#define BMM150_OVERFLOW_OUTPUT                  (0x8000)

/* Min and Max sampling frequency in mHz */
#define BMM150_MAG_MIN_FREQ 781

/*
 * From Section 4.2.4, max frequency depends on the preset.
 *
 *  Fmax ~= 1 / (145us * nXY + 500us * nZ + 980us)
 *
 *  To be safe, declare only 75% of the value.
 */
#define __BMM150_MAG_MAX_FREQ(_preset) (750000000 / \
	(145 * BMM150_REP(_preset, XY) + 500 *  BMM150_REP(_preset, Z) + 980))

#if (__BMM150_MAG_MAX_FREQ(SPECIAL) > CONFIG_EC_MAX_SENSOR_FREQ_MILLIHZ)
#error "EC too slow for magnetometer"
#endif

struct bmm150_comp_registers {
	/* Local copy of the compensation registers. */
	int8_t       dig1[2];
	int8_t       dig2[2];

	uint16_t     dig_z1;
	int16_t      dig_z2;
	int16_t      dig_z3;
	int16_t      dig_z4;

	uint8_t      dig_xy1;
	int8_t       dig_xy2;

	uint16_t     dig_xyz1;
};

struct bmm150_private_data {
	/* lsm6dsm_data union requires cal be first element */
	struct mag_cal_t             cal;
	struct bmm150_comp_registers comp;
};
#define BMM150_COMP_REG(_s) \
	(&BMI160_GET_DATA(_s)->compass.comp)

#define BMM150_CAL(_s) \
	(&BMI160_GET_DATA(_s)->compass.cal)

#ifdef CONFIG_MAG_BMI160_BMM150
#include "accelgyro_bmi160.h"
/*
 * Behind a BMI160, the BMM150 is in forced mode. Be sure to choose a frequency
 * comptible with BMI160.
 */
#define BMM150_MAG_MAX_FREQ(_preset) \
	BMI160_REG_TO_ODR(BMI160_ODR_TO_REG(__BMM150_MAG_MAX_FREQ(_preset)))
#else
#define BMM150_MAG_MAX_FREQ(_preset) __BMM150_MAG_MAX_FREQ(_preset)
#endif

/* Specific initialization of BMM150 when behing BMI160 */
int bmm150_init(const struct motion_sensor_t *s);

/* Command to normalize and apply temperature compensation */
void bmm150_normalize(const struct motion_sensor_t *s,
		      intv3_t v,
		      uint8_t *data);

int bmm150_set_offset(const struct motion_sensor_t *s,
		      const intv3_t offset);

int bmm150_get_offset(const struct motion_sensor_t *s,
		      intv3_t   offset);

#endif /* __CROS_EC_MAG_BMM150_H */
