text: /*
   * Copyright (c) 2017 Intel Corporation
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  #ifndef ZEPHYR_DRIVERS_SENSOR_LIS2DH_LIS2DH_H_
  #define ZEPHYR_DRIVERS_SENSOR_LIS2DH_LIS2DH_H_
  
  #include <kernel.h>
  #include <device.h>
  #include <misc/util.h>
  #include <stdint.h>
  #include <gpio.h>
  #include <sensor.h>
  #include <string.h>
  
  #define LIS2DH_BUS_ADDRESS		DT_ST_LIS2DH_0_BASE_ADDRESS
  #define LIS2DH_BUS_DEV_NAME		DT_ST_LIS2DH_0_BUS_NAME
  
  #if defined(DT_ST_LIS2DH_0_BUS_SPI)
  #include <spi.h>
  
  #define LIS2DH_SPI_READ_BIT		BIT(7)
  #define LIS2DH_SPI_AUTOINC_ADDR		BIT(6)
  #define LIS2DH_SPI_ADDR_MASK		BIT_MASK(6)
  
  /* LIS2DH supports only SPI mode 0, word size 8 bits, MSB first */
  #define LIS2DH_SPI_CFG			SPI_WORD_SET(8)
  
  #elif defined(DT_ST_LIS2DH_0_BUS_I2C)
  #include <i2c.h>
  #else
  #error "define bus type (I2C/SPI)"
  #endif
  
  #define LIS2DH_AUTOINCREMENT_ADDR	BIT(7)
  
  #define LIS2DH_REG_CTRL1		0x20
  #define LIS2DH_ACCEL_XYZ_SHIFT		0
  #define LIS2DH_ACCEL_X_EN_BIT		BIT(0)
  #define LIS2DH_ACCEL_Y_EN_BIT		BIT(1)
  #define LIS2DH_ACCEL_Z_EN_BIT		BIT(2)
  #define LIS2DH_ACCEL_EN_BITS		(LIS2DH_ACCEL_X_EN_BIT | \
  					LIS2DH_ACCEL_Y_EN_BIT | \
  					LIS2DH_ACCEL_Z_EN_BIT)
  #define LIS2DH_ACCEL_XYZ_MASK		BIT_MASK(3)
  
  #define LIS2DH_LP_EN_BIT_MASK		BIT(3)
  #if defined(CONFIG_LIS2DH_POWER_MODE_LOW)
  	#define LIS2DH_LP_EN_BIT	BIT(3)
  #elif defined(CONFIG_LIS2DH_POWER_MODE_NORMAL)
  	#define LIS2DH_LP_EN_BIT	0
  #endif
  
  #define LIS2DH_ODR_1			1
  #define LIS2DH_ODR_2			2
  #define LIS2DH_ODR_3			3
  #define LIS2DH_ODR_4			4
  #define LIS2DH_ODR_5			5
  #define LIS2DH_ODR_6			6
  #define LIS2DH_ODR_7			7
  #define LIS2DH_ODR_8			8
  #define LIS2DH_ODR_9			9
  
  #if defined(CONFIG_LIS2DH_ODR_1)
  	#define LIS2DH_ODR_IDX		LIS2DH_ODR_1
  #elif defined(CONFIG_LIS2DH_ODR_2)
  	#define LIS2DH_ODR_IDX		LIS2DH_ODR_2
  #elif defined(CONFIG_LIS2DH_ODR_3)
  	#define LIS2DH_ODR_IDX		LIS2DH_ODR_3
  #elif defined(CONFIG_LIS2DH_ODR_4) || defined(CONFIG_LIS2DH_ODR_RUNTIME)
  	#define LIS2DH_ODR_IDX		LIS2DH_ODR_4
  #elif defined(CONFIG_LIS2DH_ODR_5)
  	#define LIS2DH_ODR_IDX		LIS2DH_ODR_5
  #elif defined(CONFIG_LIS2DH_ODR_6)
  	#define LIS2DH_ODR_IDX		LIS2DH_ODR_6
  #elif defined(CONFIG_LIS2DH_ODR_7)
  	#define LIS2DH_ODR_IDX		LIS2DH_ODR_7
  #elif defined(CONFIG_LIS2DH_ODR_8)
  	#define LIS2DH_ODR_IDX		LIS2DH_ODR_8
  #elif defined(CONFIG_LIS2DH_ODR_9_NORMAL) || defined(CONFIG_LIS2DH_ODR_9_LOW)
  	#define LIS2DH_ODR_IDX		LIS2DH_ODR_9
  #endif
  
  #define LIS2DH_ODR_SHIFT		4
  #define LIS2DH_ODR_RATE(r)		((r) << LIS2DH_ODR_SHIFT)
  #define LIS2DH_ODR_BITS			(LIS2DH_ODR_RATE(LIS2DH_ODR_IDX))
  #define LIS2DH_ODR_MASK			(BIT_MASK(4) << LIS2DH_ODR_SHIFT)
  
  #define LIS2DH_REG_CTRL2		0x21
  #define LIS2DH_HPIS2_EN_BIT		BIT(1)
  #define LIS2DH_FDS_EN_BIT		BIT(3)
  
  #define LIS2DH_REG_CTRL3		0x22
  #define LIS2DH_EN_DRDY1_INT1_SHIFT	4
  #define LIS2DH_EN_DRDY1_INT1		BIT(LIS2DH_EN_DRDY1_INT1_SHIFT)
  
  #define LIS2DH_REG_CTRL4		0x23
  #define LIS2DH_FS_SHIFT			4
  #define LIS2DH_FS_MASK			(BIT_MASK(2) << LIS2DH_FS_SHIFT)
  
  #if defined(CONFIG_LIS2DH_ACCEL_RANGE_2G) ||\
  	defined(CONFIG_LIS2DH_ACCEL_RANGE_RUNTIME)
  	#define LIS2DH_FS_IDX		0
  #elif defined(CONFIG_LIS2DH_ACCEL_RANGE_4G)
  	#define LIS2DH_FS_IDX		1
  #elif defined(CONFIG_LIS2DH_ACCEL_RANGE_8G)
  	#define LIS2DH_FS_IDX		2
  #elif defined(CONFIG_LIS2DH_ACCEL_RANGE_16G)
  	#define LIS2DH_FS_IDX		3
  #endif
  
  #define LIS2DH_FS_SELECT(fs)		((fs) << LIS2DH_FS_SHIFT)
  #define LIS2DH_FS_BITS			(LIS2DH_FS_SELECT(LIS2DH_FS_IDX))
  #define LIS2DH_ACCEL_SCALE(range_g)	((SENSOR_G * 2 * (range_g)) / 65636LL)
  
  #define LIS2DH_REG_CTRL5		0x24
  #define LIS2DH_LIR_INT2_SHIFT		1
  #define LIS2DH_EN_LIR_INT2		BIT(LIS2DH_LIR_INT2_SHIFT)
  
  #define LIS2DH_REG_CTRL6		0x25
  #define LIS2DH_EN_INT2_INT2_SHIFT	5
  #define LIS2DH_EN_INT2_INT2		BIT(LIS2DH_EN_INT2_INT2_SHIFT)
  
  #define LIS2DH_REG_REFERENCE		0x26
  
  #define LIS2DH_REG_STATUS		0x27
  #define LIS2DH_STATUS_ZYZ_OVR		BIT(7)
  #define LIS2DH_STATUS_Z_OVR		BIT(6)
  #define LIS2DH_STATUS_Y_OVR		BIT(5)
  #define LIS2DH_STATUS_X_OVR		BIT(4)
  #define LIS2DH_STATUS_OVR_MASK		(BIT_MASK(4) << 4)
  #define LIS2DH_STATUS_ZYX_DRDY		BIT(3)
  #define LIS2DH_STATUS_Z_DRDY		BIT(2)
  #define LIS2DH_STATUS_Y_DRDY		BIT(1)
  #define LIS2DH_STATUS_X_DRDY		BIT(0)
  #define LIS2DH_STATUS_DRDY_MASK		BIT_MASK(4)
  
  #define LIS2DH_REG_ACCEL_X_LSB		0x28
  #define LIS2DH_REG_ACCEL_Y_LSB		0x2A
  #define LIS2DH_REG_ACCEL_Z_LSB		0x2C
  #define LIS2DH_REG_ACCEL_X_MSB		0x29
  #define LIS2DH_REG_ACCEL_Y_MSB		0x2B
  #define LIS2DH_REG_ACCEL_Z_MSB		0x2D
  
  #define LIS2DH_REG_INT1_CFG		0x30
  #define LIS2DH_REG_INT2_CFG		0x34
  #define LIS2DH_AOI_CFG			BIT(7)
  #define LIS2DH_INT_CFG_ZHIE_ZUPE	BIT(5)
  #define LIS2DH_INT_CFG_ZLIE_ZDOWNE	BIT(4)
  #define LIS2DH_INT_CFG_YHIE_YUPE	BIT(3)
  #define LIS2DH_INT_CFG_YLIE_YDOWNE	BIT(2)
  #define LIS2DH_INT_CFG_XHIE_XUPE	BIT(1)
  #define LIS2DH_INT_CFG_XLIE_XDOWNE	BIT(0)
  
  #define LIS2DH_REG_INT2_SRC		0x35
  
  #define LIS2DH_REG_INT2_THS		0x36
  
  #define LIS2DH_REG_INT2_DUR		0x37
  
  /* sample buffer size includes status register */
  #if defined(DT_ST_LIS2DH_0_BUS_SPI)
  #define LIS2DH_BUF_SZ			8
  #define LIS2DH_DATA_OFS			1
  #else
  #define LIS2DH_BUF_SZ			7
  #define LIS2DH_DATA_OFS			0
  #endif
  
  #if defined(DT_ST_LIS2DH_0_IRQ_GPIOS_CONTROLLER_1)
  /* INT1 and INT2 are configured */
  #define DT_LIS2DH_INT1_GPIO_PIN		DT_ST_LIS2DH_0_IRQ_GPIOS_PIN_0
  #define DT_LIS2DH_INT1_GPIO_DEV_NAME	DT_ST_LIS2DH_0_IRQ_GPIOS_CONTROLLER_0
  #define DT_LIS2DH_INT2_GPIO_PIN		DT_ST_LIS2DH_0_IRQ_GPIOS_PIN_1
  #define DT_LIS2DH_INT2_GPIO_DEV_NAME	DT_ST_LIS2DH_0_IRQ_GPIOS_CONTROLLER_1
  #else
  /* INT1 only */
  #define DT_LIS2DH_INT1_GPIO_PIN		DT_ST_LIS2DH_0_IRQ_GPIOS_PIN
  #define DT_LIS2DH_INT1_GPIO_DEV_NAME	DT_ST_LIS2DH_0_IRQ_GPIOS_CONTROLLER
  #endif
  
  union lis2dh_sample {
  	u8_t raw[LIS2DH_BUF_SZ];
  	struct {
  #if defined(DT_ST_LIS2DH_0_BUS_SPI)
  		u8_t dummy;
  #endif
  		u8_t status;
  		s16_t xyz[3];
  	} __packed;
  };
  
  struct lis2dh_data {
  #if defined(DT_ST_LIS2DH_0_BUS_SPI)
  	struct device *spi;
  	struct spi_config spi_cfg;
  #else
  	struct device *bus;
  #endif
  	union lis2dh_sample sample;
  	/* current scaling factor, in micro m/s^2 / lsb */
  	u16_t scale;
  
  #ifdef CONFIG_LIS2DH_TRIGGER
  	struct device *gpio_int1;
  	struct device *gpio_int2;
  	struct gpio_callback gpio_int1_cb;
  	struct gpio_callback gpio_int2_cb;
  
  	sensor_trigger_handler_t handler_drdy;
  	sensor_trigger_handler_t handler_anymotion;
  	atomic_t trig_flags;
  	enum sensor_channel chan_drdy;
  
  #if defined(CONFIG_LIS2DH_TRIGGER_OWN_THREAD)
  	K_THREAD_STACK_MEMBER(thread_stack, CONFIG_LIS2DH_THREAD_STACK_SIZE);
  	struct k_thread thread;
  	struct k_sem gpio_sem;
  #elif defined(CONFIG_LIS2DH_TRIGGER_GLOBAL_THREAD)
  	struct k_work work;
  	struct device *dev;
  #endif
  
  #endif /* CONFIG_LIS2DH_TRIGGER */
  };
  
  #if defined(DT_ST_LIS2DH_0_BUS_SPI)
  int lis2dh_spi_access(struct lis2dh_data *ctx, u8_t cmd,
  		      void *data, size_t length);
  #endif
  
  static inline int lis2dh_bus_configure(struct device *dev)
  {
  	struct lis2dh_data *lis2dh = dev->driver_data;
  
  #if defined(DT_ST_LIS2DH_0_BUS_SPI)
  	lis2dh->spi = device_get_binding(LIS2DH_BUS_DEV_NAME);
  	if (lis2dh->spi == NULL) {
  		LOG_ERR("Could not get pointer to %s device",
  			    LIS2DH_BUS_DEV_NAME);
  		return -EINVAL;
  	}
  
  	lis2dh->spi_cfg.operation = LIS2DH_SPI_CFG;
  	lis2dh->spi_cfg.frequency = DT_ST_LIS2DH_0_SPI_MAX_FREQUENCY;
  	lis2dh->spi_cfg.slave = LIS2DH_BUS_ADDRESS;
  
  	return 0;
  #elif defined(DT_ST_LIS2DH_0_BUS_I2C)
  	lis2dh->bus = device_get_binding(LIS2DH_BUS_DEV_NAME);
  	if (lis2dh->bus == NULL) {
  		LOG_ERR("Could not get pointer to %s device",
  			    LIS2DH_BUS_DEV_NAME);
  		return -EINVAL;
  	}
  
  	return 0;
  #else
  	return -ENODEV;
  #endif
  }
  
  static inline int lis2dh_burst_read(struct device *dev, u8_t start_addr,
  				    u8_t *buf, u8_t num_bytes)
  {
  	struct lis2dh_data *lis2dh = dev->driver_data;
  
  #if defined(DT_ST_LIS2DH_0_BUS_SPI)
  	start_addr |= LIS2DH_SPI_READ_BIT | LIS2DH_SPI_AUTOINC_ADDR;
  
  	return lis2dh_spi_access(lis2dh, start_addr, buf, num_bytes);
  #elif defined(DT_ST_LIS2DH_0_BUS_I2C)
  	return i2c_burst_read(lis2dh->bus, LIS2DH_BUS_ADDRESS,
  			      start_addr | LIS2DH_AUTOINCREMENT_ADDR,
  			      buf, num_bytes);
  #else
  	return -ENODEV;
  #endif
  }
  
  static inline int lis2dh_reg_read_byte(struct device *dev, u8_t reg_addr,
  				       u8_t *value)
  {
  	struct lis2dh_data *lis2dh = dev->driver_data;
  
  #if defined(DT_ST_LIS2DH_0_BUS_SPI)
  	reg_addr |= LIS2DH_SPI_READ_BIT;
  
  	return lis2dh_spi_access(lis2dh, reg_addr, value, 1);
  #elif defined(DT_ST_LIS2DH_0_BUS_I2C)
  	return i2c_reg_read_byte(lis2dh->bus, LIS2DH_BUS_ADDRESS,
  				 reg_addr, value);
  #else
  	return -ENODEV;
  #endif
  }
  
  static inline int lis2dh_burst_write(struct device *dev, u8_t start_addr,
  				     u8_t *buf, u8_t num_bytes)
  {
  	struct lis2dh_data *lis2dh = dev->driver_data;
  
  #if defined(DT_ST_LIS2DH_0_BUS_SPI)
  	start_addr |= LIS2DH_SPI_AUTOINC_ADDR;
  
  	return lis2dh_spi_access(lis2dh, start_addr, buf, num_bytes);
  #elif defined(DT_ST_LIS2DH_0_BUS_I2C)
  	return i2c_burst_write(lis2dh->bus, LIS2DH_BUS_ADDRESS,
  			       start_addr | LIS2DH_AUTOINCREMENT_ADDR,
  			       buf, num_bytes);
  #else
  	return -ENODEV;
  #endif
  }
  
  static inline int lis2dh_reg_write_byte(struct device *dev, u8_t reg_addr,
  					u8_t value)
  {
  	struct lis2dh_data *lis2dh = dev->driver_data;
  
  #if defined(DT_ST_LIS2DH_0_BUS_SPI)
  	reg_addr &= LIS2DH_SPI_ADDR_MASK;
  
  	return lis2dh_spi_access(lis2dh, reg_addr, &value, 1);
  #elif defined(DT_ST_LIS2DH_0_BUS_I2C)
  	u8_t tx_buf[2] = {reg_addr, value};
  
  	return i2c_write(lis2dh->bus, tx_buf, sizeof(tx_buf),
  			 LIS2DH_BUS_ADDRESS);
  #else
  	return -ENODEV;
  #endif
  }
  
  #ifdef CONFIG_LIS2DH_TRIGGER
  int lis2dh_trigger_set(struct device *dev,
  		       const struct sensor_trigger *trig,
  		       sensor_trigger_handler_t handler);
  
  int lis2dh_init_interrupt(struct device *dev);
  
  int lis2dh_reg_field_update(struct device *dev, u8_t reg_addr,
  			    u8_t pos, u8_t mask, u8_t val);
  
  int lis2dh_acc_slope_config(struct device *dev, enum sensor_attribute attr,
  			    const struct sensor_value *val);
  #endif
  
  #endif /* __SENSOR_LIS2DH__ */
  