1065 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1065 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0+
 | 
						|
/*
 | 
						|
 * STMicroelectronics st_lsm6dsr FIFO buffer library driver
 | 
						|
 *
 | 
						|
 * Copyright 2020 STMicroelectronics Inc.
 | 
						|
 *
 | 
						|
 * Lorenzo Bianconi <lorenzo.bianconi@st.com>
 | 
						|
 */
 | 
						|
#include <linux/module.h>
 | 
						|
#include <linux/interrupt.h>
 | 
						|
#include <linux/irq.h>
 | 
						|
#include <linux/iio/iio.h>
 | 
						|
#include <linux/iio/kfifo_buf.h>
 | 
						|
#include <linux/iio/events.h>
 | 
						|
#include <asm/unaligned.h>
 | 
						|
#include <linux/iio/trigger_consumer.h>
 | 
						|
#include <linux/iio/triggered_buffer.h>
 | 
						|
#include <linux/iio/trigger.h>
 | 
						|
#include <linux/iio/buffer.h>
 | 
						|
#include <linux/of.h>
 | 
						|
#include "st_lsm6dsr.h"
 | 
						|
 | 
						|
#define ST_LSM6DSR_REG_EMB_FUNC_STATUS_MAINPAGE		0x35
 | 
						|
#define ST_LSM6DSR_REG_INT_STEP_DET_MASK		BIT(3)
 | 
						|
#define ST_LSM6DSR_REG_INT_TILT_MASK			BIT(4)
 | 
						|
#define ST_LSM6DSR_REG_INT_SIGMOT_MASK			BIT(5)
 | 
						|
#define ST_LSM6DSR_REG_INT_GLANCE_MASK			BIT(0)
 | 
						|
#define ST_LSM6DSR_REG_INT_MOTION_MASK			BIT(1)
 | 
						|
#define ST_LSM6DSR_REG_INT_NO_MOTION_MASK		BIT(2)
 | 
						|
#define ST_LSM6DSR_REG_INT_WAKEUP_MASK			BIT(3)
 | 
						|
#define ST_LSM6DSR_REG_INT_PICKUP_MASK			BIT(4)
 | 
						|
#define ST_LSM6DSR_REG_INT_ORIENTATION_MASK		BIT(5)
 | 
						|
#define ST_LSM6DSR_REG_INT_WRIST_MASK			BIT(6)
 | 
						|
 | 
						|
#define ST_LSM6DSR_SAMPLE_DISCHARD			0x7ffd
 | 
						|
 | 
						|
#define ST_LSM6DSR_EWMA_LEVEL				120
 | 
						|
#define ST_LSM6DSR_EWMA_DIV				128
 | 
						|
 | 
						|
enum {
 | 
						|
	ST_LSM6DSR_GYRO_TAG = 0x01,
 | 
						|
	ST_LSM6DSR_ACC_TAG = 0x02,
 | 
						|
	ST_LSM6DSR_TEMP_TAG = 0x03,
 | 
						|
	ST_LSM6DSR_TS_TAG = 0x04,
 | 
						|
	ST_LSM6DSR_EXT0_TAG = 0x0f,
 | 
						|
	ST_LSM6DSR_EXT1_TAG = 0x10,
 | 
						|
	ST_LSM6DSR_SC_TAG = 0x12,
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Get Linux timestamp (SW)
 | 
						|
 *
 | 
						|
 * @return  timestamp in ns
 | 
						|
 */
 | 
						|
static inline s64 st_lsm6dsr_get_time_ns(void)
 | 
						|
{
 | 
						|
	return ktime_to_ns(ktime_get_boottime());
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Timestamp low pass filter
 | 
						|
 *
 | 
						|
 * @param  old: ST IMU MEMS hw instance
 | 
						|
 * @param  new: ST IMU MEMS hw instance
 | 
						|
 * @param  weight: ST IMU MEMS hw instance
 | 
						|
 * @return  estimation of the timestamp average
 | 
						|
 */
 | 
						|
static inline s64 st_lsm6dsr_ewma(s64 old, s64 new, int weight)
 | 
						|
{
 | 
						|
	s64 diff, incr;
 | 
						|
 | 
						|
	diff = new - old;
 | 
						|
	incr = div_s64((ST_LSM6DSR_EWMA_DIV - weight) * diff,
 | 
						|
			ST_LSM6DSR_EWMA_DIV);
 | 
						|
 | 
						|
	return old + incr;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Reset HW Timestamp counter and clear timestamp data structure
 | 
						|
 *
 | 
						|
 * @param  hw: ST IMU MEMS hw instance
 | 
						|
 * @return  < 0 if error, 0 otherwise
 | 
						|
 */
 | 
						|
inline int st_lsm6dsr_reset_hwts(struct st_lsm6dsr_hw *hw)
 | 
						|
{
 | 
						|
	u8 data = 0xaa;
 | 
						|
 | 
						|
	hw->ts = st_lsm6dsr_get_time_ns();
 | 
						|
	hw->ts_offset = hw->ts;
 | 
						|
	hw->val_ts_old = 0;
 | 
						|
	hw->hw_ts_high = 0;
 | 
						|
	hw->tsample = 0ull;
 | 
						|
 | 
						|
	return st_lsm6dsr_write_atomic(hw, ST_LSM6DSR_REG_TIMESTAMP2_ADDR,
 | 
						|
				       sizeof(data), &data);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Setting FIFO mode
 | 
						|
 *
 | 
						|
 * @param  hw: ST IMU MEMS hw instance
 | 
						|
 * @param  fifo_mode: ST_LSM6DSR_FIFO_BYPASS or ST_LSM6DSR_FIFO_CONT
 | 
						|
 * @return  0 FIFO configured accordingly, non zero otherwise
 | 
						|
 */
 | 
						|
int st_lsm6dsr_set_fifo_mode(struct st_lsm6dsr_hw *hw,
 | 
						|
			     enum st_lsm6dsr_fifo_mode fifo_mode)
 | 
						|
{
 | 
						|
	int err;
 | 
						|
 | 
						|
	err = st_lsm6dsr_write_with_mask(hw, ST_LSM6DSR_REG_FIFO_CTRL4_ADDR,
 | 
						|
					 ST_LSM6DSR_REG_FIFO_MODE_MASK,
 | 
						|
					 fifo_mode);
 | 
						|
	if (err < 0)
 | 
						|
		return err;
 | 
						|
 | 
						|
	hw->fifo_mode = fifo_mode;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Setting sensor ODR in batching mode
 | 
						|
 *
 | 
						|
 * @param  sensor: ST IMU sensor instance
 | 
						|
 * @param  enable: enable or disable batching mode
 | 
						|
 * @return  0 FIFO configured accordingly, non zero otherwise
 | 
						|
 */
 | 
						|
int __st_lsm6dsr_set_sensor_batching_odr(struct st_lsm6dsr_sensor *sensor,
 | 
						|
					 bool enable)
 | 
						|
{
 | 
						|
	struct st_lsm6dsr_hw *hw = sensor->hw;
 | 
						|
	u8 data = 0;
 | 
						|
	int err;
 | 
						|
	int podr, puodr;
 | 
						|
 | 
						|
	if (enable) {
 | 
						|
		err = st_lsm6dsr_get_odr_val(sensor->id, sensor->odr,
 | 
						|
					     sensor->uodr, &podr, &puodr,
 | 
						|
					     &data);
 | 
						|
		if (err < 0)
 | 
						|
			return err;
 | 
						|
	}
 | 
						|
 | 
						|
	err = __st_lsm6dsr_write_with_mask(hw, sensor->batch_reg.addr,
 | 
						|
					   sensor->batch_reg.mask, data);
 | 
						|
	return err < 0 ? err : 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Setting timestamp ODR in batching mode
 | 
						|
 *
 | 
						|
 * @param  hw: ST IMU MEMS hw instance
 | 
						|
 * @return  Timestamp ODR
 | 
						|
 */
 | 
						|
static int st_lsm6dsr_ts_odr(struct st_lsm6dsr_hw *hw)
 | 
						|
{
 | 
						|
	struct st_lsm6dsr_sensor *sensor;
 | 
						|
	int odr = 0;
 | 
						|
	u8 i;
 | 
						|
 | 
						|
	for (i = ST_LSM6DSR_ID_GYRO; i <= ST_LSM6DSR_ID_EXT1; i++) {
 | 
						|
		if (!hw->iio_devs[i])
 | 
						|
			continue;
 | 
						|
 | 
						|
		sensor = iio_priv(hw->iio_devs[i]);
 | 
						|
		if (hw->enable_mask & BIT(sensor->id))
 | 
						|
			odr = max_t(int, odr, sensor->odr);
 | 
						|
	}
 | 
						|
 | 
						|
	return odr;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Setting sensor ODR in batching mode
 | 
						|
 *
 | 
						|
 * @param  sensor: ST IMU sensor instance
 | 
						|
 * @param  enable: enable or disable batching mode
 | 
						|
 * @return  0 FIFO configured accordingly, non zero otherwise
 | 
						|
 */
 | 
						|
static inline int
 | 
						|
st_lsm6dsr_set_sensor_batching_odr(struct st_lsm6dsr_sensor *sensor,
 | 
						|
				   bool enable)
 | 
						|
{
 | 
						|
	struct st_lsm6dsr_hw *hw = sensor->hw;
 | 
						|
	int err;
 | 
						|
 | 
						|
	mutex_lock(&hw->page_lock);
 | 
						|
	err = __st_lsm6dsr_set_sensor_batching_odr(sensor, enable);
 | 
						|
	mutex_unlock(&hw->page_lock);
 | 
						|
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Update watermark level in FIFO
 | 
						|
 *
 | 
						|
 * @param  sensor: ST IMU sensor instance
 | 
						|
 * @param  watermark: New watermark level
 | 
						|
 * @return  0 if FIFO configured, non zero for error
 | 
						|
 */
 | 
						|
int st_lsm6dsr_update_watermark(struct st_lsm6dsr_sensor *sensor,
 | 
						|
				u16 watermark)
 | 
						|
{
 | 
						|
	u16 fifo_watermark = ST_LSM6DSR_MAX_FIFO_DEPTH, cur_watermark = 0;
 | 
						|
	struct st_lsm6dsr_hw *hw = sensor->hw;
 | 
						|
	struct st_lsm6dsr_sensor *cur_sensor;
 | 
						|
	__le16 wdata;
 | 
						|
	int i, err;
 | 
						|
	u8 data;
 | 
						|
 | 
						|
	for (i = ST_LSM6DSR_ID_GYRO; i <= ST_LSM6DSR_ID_STEP_COUNTER; i++) {
 | 
						|
		if (!hw->iio_devs[i])
 | 
						|
			continue;
 | 
						|
 | 
						|
		cur_sensor = iio_priv(hw->iio_devs[i]);
 | 
						|
 | 
						|
		if (!(hw->enable_mask & BIT(cur_sensor->id)))
 | 
						|
			continue;
 | 
						|
 | 
						|
		cur_watermark = (cur_sensor == sensor) ? watermark :
 | 
						|
							 cur_sensor->watermark;
 | 
						|
 | 
						|
		fifo_watermark = min_t(u16, fifo_watermark, cur_watermark);
 | 
						|
	}
 | 
						|
 | 
						|
	fifo_watermark = max_t(u16, fifo_watermark, 2);
 | 
						|
 | 
						|
	err = st_lsm6dsr_read_atomic(hw, ST_LSM6DSR_REG_FIFO_CTRL1_ADDR + 1,
 | 
						|
				     sizeof(data), &data);
 | 
						|
	if (err < 0)
 | 
						|
		goto out;
 | 
						|
 | 
						|
	fifo_watermark = ((data << 8) & ~ST_LSM6DSR_REG_FIFO_WTM_MASK) |
 | 
						|
			 (fifo_watermark & ST_LSM6DSR_REG_FIFO_WTM_MASK);
 | 
						|
	wdata = cpu_to_le16(fifo_watermark);
 | 
						|
	err = st_lsm6dsr_write_atomic(hw, ST_LSM6DSR_REG_FIFO_CTRL1_ADDR,
 | 
						|
				      sizeof(wdata), (u8 *)&wdata);
 | 
						|
out:
 | 
						|
	return err < 0 ? err : 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Timestamp correlation finction
 | 
						|
 *
 | 
						|
 * @param  hw: ST IMU MEMS hw instance
 | 
						|
 * @param  ts: New timestamp
 | 
						|
 */
 | 
						|
static inline void st_lsm6dsr_sync_hw_ts(struct st_lsm6dsr_hw *hw, s64 ts)
 | 
						|
{
 | 
						|
	s64 delta = ts - hw->hw_ts;
 | 
						|
 | 
						|
	hw->ts_offset = st_lsm6dsr_ewma(hw->ts_offset, delta,
 | 
						|
					ST_LSM6DSR_EWMA_LEVEL);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Return the iio device structure based on FIFO TAG ID
 | 
						|
 *
 | 
						|
 * @param  hw: ST IMU MEMS hw instance
 | 
						|
 * @param  tag: FIFO sample TAG ID
 | 
						|
 * @return  0 if FIFO configured, non zero for error
 | 
						|
 */
 | 
						|
static struct
 | 
						|
iio_dev *st_lsm6dsr_get_iiodev_from_tag(struct st_lsm6dsr_hw *hw,
 | 
						|
					u8 tag)
 | 
						|
{
 | 
						|
	struct iio_dev *iio_dev;
 | 
						|
 | 
						|
	switch (tag) {
 | 
						|
	case ST_LSM6DSR_GYRO_TAG:
 | 
						|
		iio_dev = hw->iio_devs[ST_LSM6DSR_ID_GYRO];
 | 
						|
		break;
 | 
						|
	case ST_LSM6DSR_ACC_TAG:
 | 
						|
		iio_dev = hw->iio_devs[ST_LSM6DSR_ID_ACC];
 | 
						|
		break;
 | 
						|
	case ST_LSM6DSR_TEMP_TAG:
 | 
						|
		iio_dev = hw->iio_devs[ST_LSM6DSR_ID_TEMP];
 | 
						|
		break;
 | 
						|
	case ST_LSM6DSR_EXT0_TAG:
 | 
						|
		if (hw->enable_mask & BIT(ST_LSM6DSR_ID_EXT0))
 | 
						|
			iio_dev = hw->iio_devs[ST_LSM6DSR_ID_EXT0];
 | 
						|
		else
 | 
						|
			iio_dev = hw->iio_devs[ST_LSM6DSR_ID_EXT1];
 | 
						|
		break;
 | 
						|
	case ST_LSM6DSR_EXT1_TAG:
 | 
						|
		iio_dev = hw->iio_devs[ST_LSM6DSR_ID_EXT1];
 | 
						|
		break;
 | 
						|
	case ST_LSM6DSR_SC_TAG:
 | 
						|
		iio_dev = hw->iio_devs[ST_LSM6DSR_ID_STEP_COUNTER];
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		iio_dev = NULL;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	return iio_dev;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Read all FIFO data stored after WTM FIFO irq fired interrupt
 | 
						|
 *
 | 
						|
 * @param hw: ST IMU MEMS hw instance
 | 
						|
 * @return Number of read bytes in FIFO or error if negative
 | 
						|
 */
 | 
						|
static int st_lsm6dsr_read_fifo(struct st_lsm6dsr_hw *hw)
 | 
						|
{
 | 
						|
	u8 iio_buf[ALIGN(ST_LSM6DSR_SAMPLE_SIZE, sizeof(s64)) + sizeof(s64)];
 | 
						|
	/* acc + gyro + 2 ext + ts + sc */
 | 
						|
	u8 buf[6 * ST_LSM6DSR_FIFO_SAMPLE_SIZE], tag, *ptr;
 | 
						|
	int i, err, word_len, fifo_len, read_len;
 | 
						|
	struct st_lsm6dsr_sensor *sensor;
 | 
						|
	struct iio_dev *iio_dev;
 | 
						|
	s64 ts_irq, hw_ts_old;
 | 
						|
	__le16 fifo_status;
 | 
						|
	u16 fifo_depth;
 | 
						|
	s16 drdymask;
 | 
						|
	u32 val;
 | 
						|
 | 
						|
	ts_irq = hw->ts - hw->delta_ts;
 | 
						|
 | 
						|
	err = st_lsm6dsr_read_atomic(hw, ST_LSM6DSR_REG_FIFO_STATUS1_ADDR,
 | 
						|
				     sizeof(fifo_status), (u8 *)&fifo_status);
 | 
						|
	if (err < 0)
 | 
						|
		return err;
 | 
						|
 | 
						|
	fifo_depth = le16_to_cpu(fifo_status) & ST_LSM6DSR_REG_FIFO_STATUS_DIFF;
 | 
						|
	if (!fifo_depth)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	fifo_len = fifo_depth * ST_LSM6DSR_FIFO_SAMPLE_SIZE;
 | 
						|
	read_len = 0;
 | 
						|
 | 
						|
	while (read_len < fifo_len) {
 | 
						|
		word_len = min_t(int, fifo_len - read_len, sizeof(buf));
 | 
						|
		err = st_lsm6dsr_read_atomic(hw,
 | 
						|
					ST_LSM6DSR_REG_FIFO_DATA_OUT_TAG_ADDR,
 | 
						|
					word_len, buf);
 | 
						|
		if (err < 0)
 | 
						|
			return err;
 | 
						|
 | 
						|
		for (i = 0; i < word_len; i += ST_LSM6DSR_FIFO_SAMPLE_SIZE) {
 | 
						|
			ptr = &buf[i + ST_LSM6DSR_TAG_SIZE];
 | 
						|
			tag = buf[i] >> 3;
 | 
						|
 | 
						|
			if (tag == ST_LSM6DSR_TS_TAG) {
 | 
						|
				val = get_unaligned_le32(ptr);
 | 
						|
 | 
						|
				if (hw->val_ts_old > val)
 | 
						|
					hw->hw_ts_high++;
 | 
						|
 | 
						|
				hw_ts_old = hw->hw_ts;
 | 
						|
 | 
						|
				/* check hw rollover */
 | 
						|
				hw->val_ts_old = val;
 | 
						|
				hw->hw_ts = (val +
 | 
						|
					     ((s64)hw->hw_ts_high << 32)) *
 | 
						|
					     hw->ts_delta_ns;
 | 
						|
				hw->ts_offset = st_lsm6dsr_ewma(hw->ts_offset,
 | 
						|
						ts_irq - hw->hw_ts,
 | 
						|
						ST_LSM6DSR_EWMA_LEVEL);
 | 
						|
 | 
						|
				if (!test_bit(ST_LSM6DSR_HW_FLUSH, &hw->state))
 | 
						|
					/* sync ap timestamp and sensor one */
 | 
						|
					st_lsm6dsr_sync_hw_ts(hw, ts_irq);
 | 
						|
 | 
						|
				ts_irq += hw->hw_ts;
 | 
						|
 | 
						|
				if (!hw->tsample)
 | 
						|
					hw->tsample = hw->ts_offset + hw->hw_ts;
 | 
						|
				else
 | 
						|
					hw->tsample = hw->tsample +
 | 
						|
						      hw->hw_ts - hw_ts_old;
 | 
						|
			} else {
 | 
						|
				iio_dev =
 | 
						|
					st_lsm6dsr_get_iiodev_from_tag(hw, tag);
 | 
						|
				if (!iio_dev)
 | 
						|
					continue;
 | 
						|
 | 
						|
				sensor = iio_priv(iio_dev);
 | 
						|
 | 
						|
				/* skip samples if not ready */
 | 
						|
				drdymask =
 | 
						|
				      (s16)le16_to_cpu(get_unaligned_le16(ptr));
 | 
						|
				if (unlikely(drdymask >=
 | 
						|
						ST_LSM6DSR_SAMPLE_DISCHARD)) {
 | 
						|
					continue;
 | 
						|
				}
 | 
						|
 | 
						|
				/*
 | 
						|
				 * hw ts in not queued in FIFO if only step
 | 
						|
				 * counter enabled
 | 
						|
				 */
 | 
						|
				if (sensor->id == ST_LSM6DSR_ID_STEP_COUNTER) {
 | 
						|
					val = get_unaligned_le32(ptr + 2);
 | 
						|
					hw->tsample = (val +
 | 
						|
						((s64)hw->hw_ts_high << 32)) *
 | 
						|
						hw->ts_delta_ns;
 | 
						|
 | 
						|
					/* avoid samples in the future */
 | 
						|
					hw->tsample = min_t(s64,
 | 
						|
							    st_lsm6dsr_get_time_ns(),
 | 
						|
							    hw->tsample);
 | 
						|
				} else {
 | 
						|
					hw->tsample = st_lsm6dsr_get_time_ns();
 | 
						|
				}
 | 
						|
 | 
						|
				memcpy(iio_buf, ptr, ST_LSM6DSR_SAMPLE_SIZE);
 | 
						|
 | 
						|
				sensor->last_fifo_timestamp = hw->tsample;
 | 
						|
 | 
						|
				/* support decimation for ODR < 12.5 Hz */
 | 
						|
				if (sensor->dec_counter > 0) {
 | 
						|
					sensor->dec_counter--;
 | 
						|
				} else {
 | 
						|
					sensor->dec_counter = sensor->decimator;
 | 
						|
					iio_push_to_buffers_with_timestamp(iio_dev,
 | 
						|
								   iio_buf,
 | 
						|
								   hw->tsample);
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		read_len += word_len;
 | 
						|
	}
 | 
						|
 | 
						|
	return read_len;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Report events after WTM FIFO irq fired interrupt
 | 
						|
 *
 | 
						|
 * @param hw: ST IMU MEMS hw instance
 | 
						|
 * @return 0 if OK, non zero for error
 | 
						|
 */
 | 
						|
static int st_lsm6dsr_report_events(struct st_lsm6dsr_hw *hw)
 | 
						|
{
 | 
						|
	struct iio_dev *iio_dev;
 | 
						|
	u8 status[3];
 | 
						|
	s64 event;
 | 
						|
	int err;
 | 
						|
 | 
						|
	if (hw->enable_mask & (BIT(ST_LSM6DSR_ID_STEP_DETECTOR) |
 | 
						|
			       BIT(ST_LSM6DSR_ID_SIGN_MOTION) |
 | 
						|
			       BIT(ST_LSM6DSR_ID_TILT) |
 | 
						|
			       BIT(ST_LSM6DSR_ID_MOTION) |
 | 
						|
			       BIT(ST_LSM6DSR_ID_NO_MOTION) |
 | 
						|
			       BIT(ST_LSM6DSR_ID_WAKEUP) |
 | 
						|
			       BIT(ST_LSM6DSR_ID_PICKUP) |
 | 
						|
			       BIT(ST_LSM6DSR_ID_ORIENTATION) |
 | 
						|
			       BIT(ST_LSM6DSR_ID_WRIST_TILT) |
 | 
						|
			       BIT(ST_LSM6DSR_ID_GLANCE))) {
 | 
						|
 | 
						|
		err = hw->tf->read(hw->dev,
 | 
						|
				   ST_LSM6DSR_REG_EMB_FUNC_STATUS_MAINPAGE,
 | 
						|
				   sizeof(status), status);
 | 
						|
		if (err < 0)
 | 
						|
			return err;
 | 
						|
 | 
						|
		/* embedded function sensors */
 | 
						|
		if (status[0] & ST_LSM6DSR_REG_INT_STEP_DET_MASK) {
 | 
						|
			iio_dev = hw->iio_devs[ST_LSM6DSR_ID_STEP_DETECTOR];
 | 
						|
			event = IIO_UNMOD_EVENT_CODE(IIO_STEP_DETECTOR, -1,
 | 
						|
						     IIO_EV_TYPE_THRESH,
 | 
						|
						     IIO_EV_DIR_RISING);
 | 
						|
			iio_push_event(iio_dev, event,
 | 
						|
				       st_lsm6dsr_get_time_ns());
 | 
						|
		}
 | 
						|
		if (status[0] & ST_LSM6DSR_REG_INT_SIGMOT_MASK) {
 | 
						|
			iio_dev = hw->iio_devs[ST_LSM6DSR_ID_SIGN_MOTION];
 | 
						|
			event = IIO_UNMOD_EVENT_CODE(IIO_SIGN_MOTION, -1,
 | 
						|
						     IIO_EV_TYPE_THRESH,
 | 
						|
						     IIO_EV_DIR_RISING);
 | 
						|
			iio_push_event(iio_dev, event,
 | 
						|
				       st_lsm6dsr_get_time_ns());
 | 
						|
		}
 | 
						|
		if (status[0] & ST_LSM6DSR_REG_INT_TILT_MASK) {
 | 
						|
			iio_dev = hw->iio_devs[ST_LSM6DSR_ID_TILT];
 | 
						|
			event = IIO_UNMOD_EVENT_CODE(IIO_TILT, -1,
 | 
						|
						     IIO_EV_TYPE_THRESH,
 | 
						|
						     IIO_EV_DIR_RISING);
 | 
						|
			iio_push_event(iio_dev, event,
 | 
						|
				       st_lsm6dsr_get_time_ns());
 | 
						|
		}
 | 
						|
		/*  fsm sensors */
 | 
						|
		if (status[1] & ST_LSM6DSR_REG_INT_GLANCE_MASK) {
 | 
						|
			iio_dev = hw->iio_devs[ST_LSM6DSR_ID_GLANCE];
 | 
						|
			event = IIO_UNMOD_EVENT_CODE(IIO_GESTURE, -1,
 | 
						|
						     IIO_EV_TYPE_THRESH,
 | 
						|
						     IIO_EV_DIR_RISING);
 | 
						|
			iio_push_event(iio_dev, event,
 | 
						|
				       st_lsm6dsr_get_time_ns());
 | 
						|
		}
 | 
						|
		if (status[1] & ST_LSM6DSR_REG_INT_MOTION_MASK) {
 | 
						|
			iio_dev = hw->iio_devs[ST_LSM6DSR_ID_MOTION];
 | 
						|
			event = IIO_UNMOD_EVENT_CODE(IIO_GESTURE, -1,
 | 
						|
						     IIO_EV_TYPE_THRESH,
 | 
						|
						     IIO_EV_DIR_RISING);
 | 
						|
			iio_push_event(iio_dev, event,
 | 
						|
				       st_lsm6dsr_get_time_ns());
 | 
						|
		}
 | 
						|
		if (status[1] & ST_LSM6DSR_REG_INT_NO_MOTION_MASK) {
 | 
						|
			iio_dev = hw->iio_devs[ST_LSM6DSR_ID_NO_MOTION];
 | 
						|
			event = IIO_UNMOD_EVENT_CODE(IIO_GESTURE, -1,
 | 
						|
						     IIO_EV_TYPE_THRESH,
 | 
						|
						     IIO_EV_DIR_RISING);
 | 
						|
			iio_push_event(iio_dev, event,
 | 
						|
				       st_lsm6dsr_get_time_ns());
 | 
						|
		}
 | 
						|
		if (status[1] & ST_LSM6DSR_REG_INT_WAKEUP_MASK) {
 | 
						|
			iio_dev = hw->iio_devs[ST_LSM6DSR_ID_WAKEUP];
 | 
						|
			event = IIO_UNMOD_EVENT_CODE(IIO_GESTURE, -1,
 | 
						|
						     IIO_EV_TYPE_THRESH,
 | 
						|
						     IIO_EV_DIR_RISING);
 | 
						|
			iio_push_event(iio_dev, event,
 | 
						|
				       st_lsm6dsr_get_time_ns());
 | 
						|
		}
 | 
						|
		if (status[1] & ST_LSM6DSR_REG_INT_PICKUP_MASK) {
 | 
						|
			iio_dev = hw->iio_devs[ST_LSM6DSR_ID_PICKUP];
 | 
						|
			event = IIO_UNMOD_EVENT_CODE(IIO_GESTURE, -1,
 | 
						|
						     IIO_EV_TYPE_THRESH,
 | 
						|
						     IIO_EV_DIR_RISING);
 | 
						|
			iio_push_event(iio_dev, event,
 | 
						|
				       st_lsm6dsr_get_time_ns());
 | 
						|
		}
 | 
						|
		if (status[1] & ST_LSM6DSR_REG_INT_ORIENTATION_MASK) {
 | 
						|
			struct st_lsm6dsr_sensor *sensor;
 | 
						|
 | 
						|
			iio_dev = hw->iio_devs[ST_LSM6DSR_ID_ORIENTATION];
 | 
						|
			sensor = iio_priv(iio_dev);
 | 
						|
			iio_trigger_poll_chained(sensor->trig);
 | 
						|
		}
 | 
						|
		if (status[1] & ST_LSM6DSR_REG_INT_WRIST_MASK) {
 | 
						|
			iio_dev = hw->iio_devs[ST_LSM6DSR_ID_WRIST_TILT];
 | 
						|
			event = IIO_UNMOD_EVENT_CODE(IIO_GESTURE, -1,
 | 
						|
						     IIO_EV_TYPE_THRESH,
 | 
						|
						     IIO_EV_DIR_RISING);
 | 
						|
			iio_push_event(iio_dev, event,
 | 
						|
				       st_lsm6dsr_get_time_ns());
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Return the max FIFO watermark level accepted
 | 
						|
 *
 | 
						|
 * @param  dev: Linux Device
 | 
						|
 * @param  attr: Device Attribute
 | 
						|
 * @param  buf: User Buffer
 | 
						|
 * @return  Number of chars printed into the buffer
 | 
						|
 */
 | 
						|
ssize_t st_lsm6dsr_get_max_watermark(struct device *dev,
 | 
						|
				     struct device_attribute *attr,
 | 
						|
				     char *buf)
 | 
						|
{
 | 
						|
	struct iio_dev *iio_dev = dev_get_drvdata(dev);
 | 
						|
	struct st_lsm6dsr_sensor *sensor = iio_priv(iio_dev);
 | 
						|
 | 
						|
	return sprintf(buf, "%d\n", sensor->max_watermark);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Return the FIFO watermark level
 | 
						|
 *
 | 
						|
 * @param  dev: Linux Device
 | 
						|
 * @param  attr: Device Attribute
 | 
						|
 * @param  buf: User Buffer
 | 
						|
 * @return  Number of chars printed into the buffer
 | 
						|
 */
 | 
						|
ssize_t st_lsm6dsr_get_watermark(struct device *dev,
 | 
						|
				 struct device_attribute *attr,
 | 
						|
				 char *buf)
 | 
						|
{
 | 
						|
	struct iio_dev *iio_dev = dev_get_drvdata(dev);
 | 
						|
	struct st_lsm6dsr_sensor *sensor = iio_priv(iio_dev);
 | 
						|
 | 
						|
	return sprintf(buf, "%d\n", sensor->watermark);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Set the FIFO watermark level
 | 
						|
 *
 | 
						|
 * @param  dev: Linux Device
 | 
						|
 * @param  attr: Device Attribute
 | 
						|
 * @param  buf: User Buffer
 | 
						|
 * @param  size: New FIFO watermark level
 | 
						|
 * @return  Watermark level if >= 0, error otherwise
 | 
						|
 */
 | 
						|
ssize_t st_lsm6dsr_set_watermark(struct device *dev,
 | 
						|
				 struct device_attribute *attr,
 | 
						|
				 const char *buf, size_t size)
 | 
						|
{
 | 
						|
	struct iio_dev *iio_dev = dev_get_drvdata(dev);
 | 
						|
	struct st_lsm6dsr_sensor *sensor = iio_priv(iio_dev);
 | 
						|
	int err, val;
 | 
						|
 | 
						|
	mutex_lock(&iio_dev->mlock);
 | 
						|
 | 
						|
	err = kstrtoint(buf, 10, &val);
 | 
						|
	if (err < 0)
 | 
						|
		goto out;
 | 
						|
 | 
						|
	err = st_lsm6dsr_update_watermark(sensor, val);
 | 
						|
	if (err < 0)
 | 
						|
		goto out;
 | 
						|
 | 
						|
	sensor->watermark = val;
 | 
						|
 | 
						|
out:
 | 
						|
	mutex_unlock(&iio_dev->mlock);
 | 
						|
 | 
						|
	return err < 0 ? err : size;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Flush internal HW FIFO
 | 
						|
 *
 | 
						|
 * @param  dev: Linux Device
 | 
						|
 * @param  attr: Device Attribute
 | 
						|
 * @param  buf: User Buffer
 | 
						|
 * @param  size: unused
 | 
						|
 * @return  Watermark level if >= 0, error otherwise
 | 
						|
 */
 | 
						|
ssize_t st_lsm6dsr_flush_fifo(struct device *dev,
 | 
						|
			      struct device_attribute *attr,
 | 
						|
			      const char *buf, size_t size)
 | 
						|
{
 | 
						|
	struct iio_dev *iio_dev = dev_get_drvdata(dev);
 | 
						|
	struct st_lsm6dsr_sensor *sensor = iio_priv(iio_dev);
 | 
						|
	struct st_lsm6dsr_hw *hw = sensor->hw;
 | 
						|
	int count;
 | 
						|
	s64 ts;
 | 
						|
#ifdef CONFIG_NO_GKI
 | 
						|
	s64 type;
 | 
						|
	s64 event;
 | 
						|
	s64 fts;
 | 
						|
#endif
 | 
						|
 | 
						|
	mutex_lock(&hw->fifo_lock);
 | 
						|
	ts = st_lsm6dsr_get_time_ns();
 | 
						|
	hw->delta_ts = ts - hw->ts;
 | 
						|
	hw->ts = ts;
 | 
						|
	set_bit(ST_LSM6DSR_HW_FLUSH, &hw->state);
 | 
						|
	count = st_lsm6dsr_read_fifo(hw);
 | 
						|
	sensor->dec_counter = 0;
 | 
						|
	mutex_unlock(&hw->fifo_lock);
 | 
						|
 | 
						|
#ifdef CONFIG_NO_GKI
 | 
						|
	if (count > 0)
 | 
						|
		fts = sensor->last_fifo_timestamp;
 | 
						|
	else
 | 
						|
		fts = ts;
 | 
						|
 | 
						|
	type = count > 0 ? IIO_EV_DIR_FIFO_DATA : IIO_EV_DIR_FIFO_EMPTY;
 | 
						|
	event = IIO_UNMOD_EVENT_CODE(iio_dev->channels[0].type, -1,
 | 
						|
				     IIO_EV_TYPE_FIFO_FLUSH, type);
 | 
						|
	iio_push_event(iio_dev, event, fts);
 | 
						|
#endif
 | 
						|
 | 
						|
	return size;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Empty FIFO and set HW FIFO in Bypass mode
 | 
						|
 *
 | 
						|
 * @param  hw: ST IMU MEMS hw instance
 | 
						|
 * @return  Watermark level if >= 0, error otherwise
 | 
						|
 */
 | 
						|
int st_lsm6dsr_suspend_fifo(struct st_lsm6dsr_hw *hw)
 | 
						|
{
 | 
						|
	int err;
 | 
						|
 | 
						|
	mutex_lock(&hw->fifo_lock);
 | 
						|
	st_lsm6dsr_read_fifo(hw);
 | 
						|
	err = st_lsm6dsr_set_fifo_mode(hw, ST_LSM6DSR_FIFO_BYPASS);
 | 
						|
	mutex_unlock(&hw->fifo_lock);
 | 
						|
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Update ODR batching in FIFO and Timestamp
 | 
						|
 *
 | 
						|
 * @param  iio_dev: Linux IIO device
 | 
						|
 * @param  enable: enable/disable batcing in FIFO
 | 
						|
 * @return  < 0 if error, 0 otherwise
 | 
						|
 */
 | 
						|
int st_lsm6dsr_update_batching(struct iio_dev *iio_dev, bool enable)
 | 
						|
{
 | 
						|
	struct st_lsm6dsr_sensor *sensor = iio_priv(iio_dev);
 | 
						|
	struct st_lsm6dsr_hw *hw = sensor->hw;
 | 
						|
	int err;
 | 
						|
 | 
						|
	disable_irq(hw->irq);
 | 
						|
 | 
						|
	err = st_lsm6dsr_set_sensor_batching_odr(sensor, enable);
 | 
						|
	if (err < 0)
 | 
						|
		goto out;
 | 
						|
 | 
						|
	/* Calc TS ODR */
 | 
						|
	hw->odr = st_lsm6dsr_ts_odr(hw);
 | 
						|
 | 
						|
out:
 | 
						|
	enable_irq(hw->irq);
 | 
						|
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Update FIFO watermark value based to the enabled sensors
 | 
						|
 *
 | 
						|
 * @param  iio_dev: Linux IIO device
 | 
						|
 * @param  enable: enable/disable batcing in FIFO
 | 
						|
 * @return  < 0 if error, 0 otherwise
 | 
						|
 */
 | 
						|
static int st_lsm6dsr_update_fifo(struct iio_dev *iio_dev, bool enable)
 | 
						|
{
 | 
						|
	struct st_lsm6dsr_sensor *sensor = iio_priv(iio_dev);
 | 
						|
	struct st_lsm6dsr_hw *hw = sensor->hw;
 | 
						|
	int err;
 | 
						|
	int podr, puodr;
 | 
						|
 | 
						|
	disable_irq(hw->irq);
 | 
						|
 | 
						|
	if (sensor->id == ST_LSM6DSR_ID_EXT0 ||
 | 
						|
	    sensor->id == ST_LSM6DSR_ID_EXT1) {
 | 
						|
		err = st_lsm6dsr_shub_set_enable(sensor, enable);
 | 
						|
		if (err < 0)
 | 
						|
			goto out;
 | 
						|
	} else {
 | 
						|
		if (sensor->id == ST_LSM6DSR_ID_STEP_COUNTER) {
 | 
						|
			err = st_lsm6dsr_step_counter_set_enable(sensor,
 | 
						|
								 enable);
 | 
						|
			if (err < 0)
 | 
						|
				goto out;
 | 
						|
		} else {
 | 
						|
			err = st_lsm6dsr_sensor_set_enable(sensor, enable);
 | 
						|
			if (err < 0)
 | 
						|
				goto out;
 | 
						|
 | 
						|
			err = st_lsm6dsr_set_sensor_batching_odr(sensor,
 | 
						|
								 enable);
 | 
						|
			if (err < 0)
 | 
						|
				goto out;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * this is an auxiliary sensor, it need to get batched
 | 
						|
	 * together at least with a primary sensor (Acc/Gyro)
 | 
						|
	 */
 | 
						|
	if (sensor->id == ST_LSM6DSR_ID_TEMP) {
 | 
						|
		if (!(hw->enable_mask & (BIT(ST_LSM6DSR_ID_ACC) |
 | 
						|
					 BIT(ST_LSM6DSR_ID_GYRO)))) {
 | 
						|
			struct st_lsm6dsr_sensor *acc_sensor;
 | 
						|
			u8 data = 0;
 | 
						|
 | 
						|
			acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSR_ID_ACC]);
 | 
						|
			if (enable) {
 | 
						|
				err = st_lsm6dsr_get_odr_val(ST_LSM6DSR_ID_ACC,
 | 
						|
						sensor->odr, sensor->uodr,
 | 
						|
						&podr, &puodr, &data);
 | 
						|
				if (err < 0)
 | 
						|
					goto out;
 | 
						|
			}
 | 
						|
 | 
						|
			err = st_lsm6dsr_write_with_mask(hw,
 | 
						|
					acc_sensor->batch_reg.addr,
 | 
						|
					acc_sensor->batch_reg.mask,
 | 
						|
					data);
 | 
						|
			if (err < 0)
 | 
						|
				goto out;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	err = st_lsm6dsr_update_watermark(sensor, sensor->watermark);
 | 
						|
	if (err < 0)
 | 
						|
		goto out;
 | 
						|
 | 
						|
	/* Calc TS ODR */
 | 
						|
	hw->odr = st_lsm6dsr_ts_odr(hw);
 | 
						|
 | 
						|
	if (enable && hw->fifo_mode == ST_LSM6DSR_FIFO_BYPASS) {
 | 
						|
		st_lsm6dsr_reset_hwts(hw);
 | 
						|
		err = st_lsm6dsr_set_fifo_mode(hw, ST_LSM6DSR_FIFO_CONT);
 | 
						|
	} else if (!hw->enable_mask) {
 | 
						|
		err = st_lsm6dsr_set_fifo_mode(hw, ST_LSM6DSR_FIFO_BYPASS);
 | 
						|
	}
 | 
						|
 | 
						|
out:
 | 
						|
	enable_irq(hw->irq);
 | 
						|
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Bottom handler for FSM Orientation sensor event generation
 | 
						|
 *
 | 
						|
 * @param  irq: IIO trigger irq number
 | 
						|
 * @param  p: iio poll function environment
 | 
						|
 * @return  IRQ_HANDLED or < 0 for error
 | 
						|
 */
 | 
						|
static irqreturn_t st_lsm6dsr_buffer_handler_thread(int irq, void *p)
 | 
						|
{
 | 
						|
	struct iio_poll_func *pf = p;
 | 
						|
	struct iio_dev *iio_dev = pf->indio_dev;
 | 
						|
	struct st_lsm6dsr_sensor *sensor = iio_priv(iio_dev);
 | 
						|
	u8 buffer[sizeof(u8) + sizeof(s64)];
 | 
						|
	int err;
 | 
						|
 | 
						|
	err = st_lsm6dsr_fsm_get_orientation(sensor->hw, buffer);
 | 
						|
	if (err < 0)
 | 
						|
		goto out;
 | 
						|
 | 
						|
	iio_push_to_buffers_with_timestamp(iio_dev, buffer,
 | 
						|
					   st_lsm6dsr_get_time_ns());
 | 
						|
out:
 | 
						|
	iio_trigger_notify_done(sensor->trig);
 | 
						|
 | 
						|
	return IRQ_HANDLED;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Top handler for sensor event generation + FIFO management
 | 
						|
 *
 | 
						|
 * @param  irq: IIO trigger irq number
 | 
						|
 * @param  private: iio poll function environment
 | 
						|
 * @return  IRQ_HANDLED or < 0 for error
 | 
						|
 */
 | 
						|
static irqreturn_t st_lsm6dsr_handler_irq(int irq, void *private)
 | 
						|
{
 | 
						|
	struct st_lsm6dsr_hw *hw = (struct st_lsm6dsr_hw *)private;
 | 
						|
	s64 ts = st_lsm6dsr_get_time_ns();
 | 
						|
 | 
						|
	hw->delta_ts = ts - hw->ts;
 | 
						|
	hw->ts = ts;
 | 
						|
 | 
						|
	return IRQ_WAKE_THREAD;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Bottom handler for sensor event generation + FIFO management
 | 
						|
 *
 | 
						|
 * @param  irq: irq line number
 | 
						|
 * @param  private: device private environment pointer
 | 
						|
 * @return  IRQ_HANDLED or < 0 for error
 | 
						|
 */
 | 
						|
static irqreturn_t st_lsm6dsr_handler_thread(int irq, void *private)
 | 
						|
{
 | 
						|
	struct st_lsm6dsr_hw *hw = (struct st_lsm6dsr_hw *)private;
 | 
						|
 | 
						|
	mutex_lock(&hw->fifo_lock);
 | 
						|
	st_lsm6dsr_read_fifo(hw);
 | 
						|
	clear_bit(ST_LSM6DSR_HW_FLUSH, &hw->state);
 | 
						|
	mutex_unlock(&hw->fifo_lock);
 | 
						|
 | 
						|
	st_lsm6dsr_report_events(hw);
 | 
						|
 | 
						|
	return IRQ_HANDLED;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * IIO fifo pre enabled callback function
 | 
						|
 *
 | 
						|
 * @param  iio_dev: IIO device
 | 
						|
 * @return  < 0 if error, 0 otherwise
 | 
						|
 */
 | 
						|
static int st_lsm6dsr_fifo_preenable(struct iio_dev *iio_dev)
 | 
						|
{
 | 
						|
	return st_lsm6dsr_update_fifo(iio_dev, true);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * IIO fifo post disable callback function
 | 
						|
 *
 | 
						|
 * @param  iio_dev: IIO device
 | 
						|
 * @return  < 0 if error, 0 otherwise
 | 
						|
 */
 | 
						|
static int st_lsm6dsr_fifo_postdisable(struct iio_dev *iio_dev)
 | 
						|
{
 | 
						|
	return st_lsm6dsr_update_fifo(iio_dev, false);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * IIO fifo callback registruction structure
 | 
						|
 */
 | 
						|
static const struct iio_buffer_setup_ops st_lsm6dsr_fifo_ops = {
 | 
						|
	.preenable = st_lsm6dsr_fifo_preenable,
 | 
						|
	.postdisable = st_lsm6dsr_fifo_postdisable,
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Enable HW FIFO
 | 
						|
 *
 | 
						|
 * @param  hw: ST IMU MEMS hw instance
 | 
						|
 * @return  < 0 if error, 0 otherwise
 | 
						|
 */
 | 
						|
static int st_lsm6dsr_fifo_init(struct st_lsm6dsr_hw *hw)
 | 
						|
{
 | 
						|
	return st_lsm6dsr_write_with_mask(hw,
 | 
						|
					  ST_LSM6DSR_REG_FIFO_CTRL4_ADDR,
 | 
						|
					  ST_LSM6DSR_REG_DEC_TS_MASK, 1);
 | 
						|
}
 | 
						|
 | 
						|
static const struct iio_trigger_ops st_lsm6dsr_trigger_ops;
 | 
						|
 | 
						|
static int st_lsm6dsr_buffer_preenable(struct iio_dev *iio_dev)
 | 
						|
{
 | 
						|
	return st_lsm6dsr_embfunc_sensor_set_enable(iio_priv(iio_dev), true);
 | 
						|
}
 | 
						|
 | 
						|
static int st_lsm6dsr_buffer_postdisable(struct iio_dev *iio_dev)
 | 
						|
{
 | 
						|
	return st_lsm6dsr_embfunc_sensor_set_enable(iio_priv(iio_dev), false);
 | 
						|
}
 | 
						|
 | 
						|
static const struct iio_buffer_setup_ops st_lsm6dsr_buffer_ops = {
 | 
						|
	.preenable = st_lsm6dsr_buffer_preenable,
 | 
						|
	.postenable = NULL,
 | 
						|
	.predisable = NULL,
 | 
						|
	.postdisable = st_lsm6dsr_buffer_postdisable,
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * Init IRQ
 | 
						|
 *
 | 
						|
 * @param  hw: ST IMU MEMS hw instance
 | 
						|
 * @return  < 0 if error, 0 otherwise
 | 
						|
 */
 | 
						|
int st_lsm6dsr_irq_setup(struct st_lsm6dsr_hw *hw)
 | 
						|
{
 | 
						|
	unsigned long irq_type;
 | 
						|
	bool irq_active_low;
 | 
						|
	int err;
 | 
						|
 | 
						|
	irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
 | 
						|
	if (irq_type == IRQF_TRIGGER_NONE)
 | 
						|
		irq_type = IRQF_TRIGGER_HIGH;
 | 
						|
 | 
						|
	switch (irq_type) {
 | 
						|
	case IRQF_TRIGGER_HIGH:
 | 
						|
	case IRQF_TRIGGER_RISING:
 | 
						|
		irq_active_low = false;
 | 
						|
		break;
 | 
						|
	case IRQF_TRIGGER_LOW:
 | 
						|
	case IRQF_TRIGGER_FALLING:
 | 
						|
		irq_active_low = true;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		dev_info(hw->dev, "mode %lx unsupported\n", irq_type);
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	err = st_lsm6dsr_write_with_mask(hw, ST_LSM6DSR_REG_CTRL3_C_ADDR,
 | 
						|
					 ST_LSM6DSR_REG_H_LACTIVE_MASK,
 | 
						|
					 irq_active_low);
 | 
						|
	if (err < 0)
 | 
						|
		return err;
 | 
						|
 | 
						|
	err = st_lsm6dsr_write_with_mask(hw, ST_LSM6DSR_REG_INT1_CTRL_ADDR,
 | 
						|
					 ST_LSM6DSR_REG_INT_FIFO_TH_MASK, 1);
 | 
						|
	if (err < 0)
 | 
						|
		return err;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Init IIO buffers and triggers
 | 
						|
 *
 | 
						|
 * @param  hw: ST IMU MEMS hw instance
 | 
						|
 * @return  < 0 if error, 0 otherwise
 | 
						|
 */
 | 
						|
int st_lsm6dsr_buffers_setup(struct st_lsm6dsr_hw *hw)
 | 
						|
{
 | 
						|
	struct device_node *np = hw->dev->of_node;
 | 
						|
	struct st_lsm6dsr_sensor *sensor;
 | 
						|
	struct iio_dev *iio_dev;
 | 
						|
	unsigned long irq_type;
 | 
						|
	int i, err;
 | 
						|
 | 
						|
	err = st_lsm6dsr_irq_setup(hw);
 | 
						|
	if (err < 0)
 | 
						|
		return err;
 | 
						|
 | 
						|
	irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
 | 
						|
	if (irq_type == IRQF_TRIGGER_NONE)
 | 
						|
		irq_type = IRQF_TRIGGER_HIGH;
 | 
						|
 | 
						|
	if (np && of_property_read_bool(np, "drive-open-drain")) {
 | 
						|
		err = st_lsm6dsr_write_with_mask(hw,
 | 
						|
						 ST_LSM6DSR_REG_CTRL3_C_ADDR,
 | 
						|
						 ST_LSM6DSR_REG_PP_OD_MASK, 1);
 | 
						|
		if (err < 0)
 | 
						|
			return err;
 | 
						|
 | 
						|
		irq_type |= IRQF_SHARED;
 | 
						|
	}
 | 
						|
 | 
						|
	err = devm_request_threaded_irq(hw->dev, hw->irq,
 | 
						|
					st_lsm6dsr_handler_irq,
 | 
						|
					st_lsm6dsr_handler_thread,
 | 
						|
					irq_type | IRQF_ONESHOT,
 | 
						|
					"lsm6dsr", hw);
 | 
						|
	if (err) {
 | 
						|
		dev_err(hw->dev, "failed to request trigger irq %d\n",
 | 
						|
			hw->irq);
 | 
						|
		return err;
 | 
						|
	}
 | 
						|
 | 
						|
	for (i = ST_LSM6DSR_ID_GYRO; i <= ST_LSM6DSR_ID_SIGN_MOTION; i++) {
 | 
						|
		if (!hw->iio_devs[i])
 | 
						|
			continue;
 | 
						|
 | 
						|
		err = devm_iio_kfifo_buffer_setup(hw->dev, hw->iio_devs[i],
 | 
						|
						  &st_lsm6dsr_fifo_ops);
 | 
						|
		if (err)
 | 
						|
			return err;
 | 
						|
	}
 | 
						|
 | 
						|
	err = st_lsm6dsr_fifo_init(hw);
 | 
						|
	if (err < 0)
 | 
						|
		return err;
 | 
						|
 | 
						|
	iio_dev = hw->iio_devs[ST_LSM6DSR_ID_ORIENTATION];
 | 
						|
	sensor = iio_priv(iio_dev);
 | 
						|
 | 
						|
	err = iio_triggered_buffer_setup(iio_dev,
 | 
						|
				NULL, st_lsm6dsr_buffer_handler_thread,
 | 
						|
				&st_lsm6dsr_buffer_ops);
 | 
						|
	if (err < 0)
 | 
						|
		return err;
 | 
						|
 | 
						|
	sensor->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger",
 | 
						|
					      iio_dev->name);
 | 
						|
	if (!sensor->trig)
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	iio_trigger_set_drvdata(sensor->trig, iio_dev);
 | 
						|
	sensor->trig->ops = &st_lsm6dsr_trigger_ops;
 | 
						|
	sensor->trig->dev.parent = hw->dev;
 | 
						|
	sensor->trig->owner = THIS_MODULE;
 | 
						|
	iio_dev->trig = iio_trigger_get(sensor->trig);
 | 
						|
 | 
						|
	err = iio_trigger_register(sensor->trig);
 | 
						|
	if (err < 0)
 | 
						|
		iio_triggered_buffer_cleanup(iio_dev);
 | 
						|
 | 
						|
	return err;
 | 
						|
}
 | 
						|
 | 
						|
int st_lsm6dsr_deallocate_buffers(struct st_lsm6dsr_hw *hw)
 | 
						|
{
 | 
						|
	struct iio_dev *iio_dev = hw->iio_devs[ST_LSM6DSR_ID_ORIENTATION];
 | 
						|
	struct st_lsm6dsr_sensor *sensor = iio_priv(iio_dev);
 | 
						|
 | 
						|
	iio_trigger_unregister(sensor->trig);
 | 
						|
	iio_triggered_buffer_cleanup(iio_dev);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 |