/*
 * STMicroelectronics st_asm330lhh sensor driver
 *
 * Copyright 2018 STMicroelectronics Inc.
 *
 * Lorenzo Bianconi <lorenzo.bianconi@st.com>
 *
 * Licensed under the GPL-2.
 */

#ifndef ST_ASM330LHH_H
#define ST_ASM330LHH_H

#include <linux/device.h>
#include <linux/iio/iio.h>
#include <linux/input.h>
#include <linux/ktime.h>
#include <linux/slab.h>

/*
 * Module version:
 * Version: Major.Minor
 * Commit: last commit in branch
 */
#define ST_ASM330LHH_REVISION		"1.0.1"
#define ST_ASM330LHH_PATCH		"1"

#define ST_ASM330LHH_VERSION		"v"	\
	ST_ASM330LHH_REVISION			\
	"-"					\
	ST_ASM330LHH_PATCH

#define ST_ASM330LHH_DEV_NAME		"asm330lhh"

#define ST_ASM330LHH_SAMPLE_SIZE	6
#define ST_ASM330LHH_TS_SAMPLE_SIZE	4
#define ST_ASM330LHH_TAG_SIZE		1
#define ST_ASM330LHH_FIFO_SAMPLE_SIZE	(ST_ASM330LHH_SAMPLE_SIZE + \
					 ST_ASM330LHH_TAG_SIZE)
#define ST_ASM330LHH_MAX_FIFO_DEPTH	416

#define ST_ASM330LHH_REG_FIFO_BATCH_ADDR	0x09
#define ST_ASM330LHH_REG_FIFO_CTRL4_ADDR	0x0a
#define ST_ASM330LHH_REG_STATUS_ADDR		0x1e
#define ST_ASM330LHH_REG_STATUS_TDA		BIT(2)
#define ST_ASM330LHH_REG_OUT_TEMP_L_ADDR	0x20
#define ST_ASM330LHH_REG_OUT_TEMP_H_ADDR	0x21

#define ST_ASM330LHH_MAX_ODR			416

/* Define Custom events for FIFO flush */
#define CUSTOM_IIO_EV_DIR_FIFO_EMPTY (IIO_EV_DIR_FALLING + 1)
#define CUSTOM_IIO_EV_DIR_FIFO_DATA (IIO_EV_DIR_FALLING + 2)
#define CUSTOM_IIO_EV_TYPE_FIFO_FLUSH (IIO_EV_TYPE_MAG_ADAPTIVE + 1)

#define ST_ASM330LHH_CHANNEL(chan_type, addr, mod, ch2, scan_idx,	\
			   rb, sb, sg)					\
{									\
	.type = chan_type,						\
	.address = addr,						\
	.modified = mod,						\
	.channel2 = ch2,						\
	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
			      BIT(IIO_CHAN_INFO_SCALE),			\
	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
	.scan_index = scan_idx,						\
	.scan_type = {							\
		.sign = sg,						\
		.realbits = rb,						\
		.storagebits = sb,					\
		.endianness = IIO_LE,					\
	},								\
}

static const struct iio_event_spec st_asm330lhh_flush_event = {
	.type = CUSTOM_IIO_EV_TYPE_FIFO_FLUSH,
	.dir = IIO_EV_DIR_EITHER,
};

#define ST_ASM330LHH_FLUSH_CHANNEL(dtype)		\
{							\
	.type = dtype,					\
	.modified = 0,					\
	.scan_index = -1,				\
	.indexed = -1,					\
	.event_spec = &st_asm330lhh_flush_event,	\
	.num_event_specs = 1,				\
}

#define ST_ASM330LHH_RX_MAX_LENGTH	8
#define ST_ASM330LHH_TX_MAX_LENGTH	8

#ifdef CONFIG_ENABLE_ASM_ACC_GYRO_BUFFERING
#define ASM_MAXSAMPLE        4000
#define G_MAX                    23920640
struct asm_sample {
	int xyz[3];
	unsigned int tsec;
	unsigned long long tnsec;
};
#endif

struct st_asm330lhh_transfer_buffer {
	u8 rx_buf[ST_ASM330LHH_RX_MAX_LENGTH];
	u8 tx_buf[ST_ASM330LHH_TX_MAX_LENGTH] ____cacheline_aligned;
};

struct st_asm330lhh_transfer_function {
	int (*read)(struct device *dev, u8 addr, int len, u8 *data);
	int (*write)(struct device *dev, u8 addr, int len, u8 *data);
};

struct st_asm330lhh_reg {
	u8 addr;
	u8 mask;
};

struct st_asm330lhh_odr {
	u16 hz;
	u8 val;
};

#define ST_ASM330LHH_ODR_LIST_SIZE	7
struct st_asm330lhh_odr_table_entry {
	struct st_asm330lhh_reg reg;
	struct st_asm330lhh_odr odr_avl[ST_ASM330LHH_ODR_LIST_SIZE];
};

struct st_asm330lhh_fs {
	u32 gain;
	u8 val;
};

#define ST_ASM330LHH_FS_ACC_LIST_SIZE		4
#define ST_ASM330LHH_FS_GYRO_LIST_SIZE		6
#define ST_ASM330LHH_FS_TEMP_LIST_SIZE		1
#define ST_ASM330LHH_FS_LIST_SIZE		6
struct st_asm330lhh_fs_table_entry {
	u32 size;
	struct st_asm330lhh_reg reg;
	struct st_asm330lhh_fs fs_avl[ST_ASM330LHH_FS_LIST_SIZE];
};

enum st_asm330lhh_sensor_id {
	ST_ASM330LHH_ID_ACC,
	ST_ASM330LHH_ID_GYRO,
	ST_ASM330LHH_ID_TEMP,
	ST_ASM330LHH_ID_MAX,
};

enum st_asm330lhh_fifo_mode {
	ST_ASM330LHH_FIFO_BYPASS = 0x0,
	ST_ASM330LHH_FIFO_CONT = 0x6,
};

enum {
	ST_ASM330LHH_HW_FLUSH,
	ST_ASM330LHH_HW_OPERATIONAL,
};

/**
 * struct st_asm330lhh_sensor - ST IMU sensor instance
 * @id: Sensor identifier.
 * @hw: Pointer to instance of struct st_asm330lhh_hw.
 * @gain: Configured sensor sensitivity.
 * @odr: Output data rate of the sensor [Hz].
 * @watermark: Sensor watermark level.
 * @batch_mask: Sensor mask for FIFO batching register
 */
struct st_asm330lhh_sensor {
	enum st_asm330lhh_sensor_id id;
	struct st_asm330lhh_hw *hw;

	u32 gain;
	u16 odr;
	u32 offset;

	__le16 old_data;

	u8 std_samples;
	u8 std_level;

	u16 watermark;
	u8 batch_mask;
	u8 batch_addr;
#ifdef CONFIG_ENABLE_ASM_ACC_GYRO_BUFFERING
	bool read_boot_sample;
	int bufsample_cnt;
	bool buffer_asm_samples;
	struct kmem_cache *asm_cachepool;
	struct asm_sample *asm_samplist[ASM_MAXSAMPLE];
	ktime_t timestamp;
	int max_buffer_time;
	struct input_dev *buf_dev;
	int report_evt_cnt;
#endif
};

/**
 * struct st_asm330lhh_hw - ST IMU MEMS hw instance
 * @dev: Pointer to instance of struct device (I2C or SPI).
 * @irq: Device interrupt line (I2C or SPI).
 * @lock: Mutex to protect read and write operations.
 * @fifo_lock: Mutex to prevent concurrent access to the hw FIFO.
 * @fifo_mode: FIFO operating mode supported by the device.
 * @state: hw operational state.
 * @enable_mask: Enabled sensor bitmask.
 * @ts_offset: Hw timestamp offset.
 * @hw_ts: Latest hw timestamp from the sensor.
 * @ts: Latest timestamp from irq handler.
 * @delta_ts: Delta time between two consecutive interrupts.
 * @iio_devs: Pointers to acc/gyro iio_dev instances.
 * @tf: Transfer function structure used by I/O operations.
 * @tb: Transfer buffers used by SPI I/O operations.
 */
struct st_asm330lhh_hw {
	struct device *dev;
	int irq;

	struct mutex lock;
	struct mutex fifo_lock;

	enum st_asm330lhh_fifo_mode fifo_mode;
	unsigned long state;
	u8 enable_mask;

	s64 ts_offset;
	s64 hw_ts;
	s64 delta_ts;
	s64 ts;
	s64 tsample;
	s64 hw_ts_old;
	s64 delta_hw_ts;

	/* Timestamp sample ODR */
	u16 odr;

	struct iio_dev *iio_devs[ST_ASM330LHH_ID_MAX];

	const struct st_asm330lhh_transfer_function *tf;
	struct st_asm330lhh_transfer_buffer tb;
};

extern const struct dev_pm_ops st_asm330lhh_pm_ops;

int st_asm330lhh_probe(struct device *dev, int irq,
		       const struct st_asm330lhh_transfer_function *tf_ops);
int st_asm330lhh_remove(struct device *dev);
int st_asm330lhh_sensor_set_enable(struct st_asm330lhh_sensor *sensor,
				   bool enable);
int st_asm330lhh_fifo_setup(struct st_asm330lhh_hw *hw);
int st_asm330lhh_deallocate_fifo(struct st_asm330lhh_hw *hw);
int st_asm330lhh_write_with_mask(struct st_asm330lhh_hw *hw, u8 addr, u8 mask,
				 u8 val);
int st_asm330lhh_get_odr_val(enum st_asm330lhh_sensor_id id, u16 odr, u8 *val);
ssize_t st_asm330lhh_flush_fifo(struct device *dev,
				struct device_attribute *attr,
				const char *buf, size_t size);
ssize_t st_asm330lhh_get_max_watermark(struct device *dev,
				       struct device_attribute *attr, char *buf);
ssize_t st_asm330lhh_get_watermark(struct device *dev,
				   struct device_attribute *attr, char *buf);
ssize_t st_asm330lhh_set_watermark(struct device *dev,
				   struct device_attribute *attr,
				   const char *buf, size_t size);
int st_asm330lhh_set_fifo_mode(struct st_asm330lhh_hw *hw,
			       enum st_asm330lhh_fifo_mode fifo_mode);
int st_asm330lhh_suspend_fifo(struct st_asm330lhh_hw *hw);
int st_asm330lhh_update_watermark(struct st_asm330lhh_sensor *sensor,
					u16 watermark);
int st_asm330lhh_update_fifo(struct iio_dev *iio_dev, bool enable);
int asm330_check_acc_gyro_early_buff_enable_flag(
		struct st_asm330lhh_sensor *sensor);
#endif /* ST_ASM330LHH_H */
