Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fc6bd727 authored by Jonathan Cameron's avatar Jonathan Cameron
Browse files

staging:iio:lis3l02dq drop separate driver



Retire this venerable driver as the basic support is now in the
generic st-sensors accelerometer driver.

There are a few missing features in the new driver:
* Threshold events.
* Access to the calibration adjustment registers (patch shortly)

In exchange it brings a cleaner and more maintainable code base that actually
gets tested more than once every few years.  I'll actually be suprised
if anyone other than me has a board with one of these on that is running
an up to date kernel.

Signed-off-by: default avatarJonathan Cameron <jic23@kernel.org>
parent 4e68cfbf
Loading
Loading
Loading
Loading
+0 −14
Original line number Diff line number Diff line
@@ -51,20 +51,6 @@ config ADIS16240
	  To compile this driver as a module, say M here: the module will be
	  called adis16240.

config LIS3L02DQ
	tristate "ST Microelectronics LIS3L02DQ Accelerometer Driver"
	depends on SPI
	select IIO_TRIGGER if IIO_BUFFER
	depends on !IIO_BUFFER || IIO_KFIFO_BUF
	depends on GPIOLIB || COMPILE_TEST
	help
	  Say Y here to build SPI support for the ST microelectronics
	  accelerometer. The driver supplies direct access via sysfs files
	  and an event interface via a character device.

	  To compile this driver as a module, say M here: the module will be
	  called lis3l02dq.

config SCA3000
	depends on IIO_BUFFER
	depends on SPI
+0 −4
Original line number Diff line number Diff line
@@ -14,9 +14,5 @@ obj-$(CONFIG_ADIS16209) += adis16209.o
adis16240-y             := adis16240_core.o
obj-$(CONFIG_ADIS16240) += adis16240.o

lis3l02dq-y		:= lis3l02dq_core.o
lis3l02dq-$(CONFIG_IIO_BUFFER) += lis3l02dq_ring.o
obj-$(CONFIG_LIS3L02DQ)	+= lis3l02dq.o

sca3000-y		:= sca3000_core.o sca3000_ring.o
obj-$(CONFIG_SCA3000)	+= sca3000.o
+0 −217
Original line number Diff line number Diff line
/*
 * LISL02DQ.h -- support STMicroelectronics LISD02DQ
 *               3d 2g Linear Accelerometers via SPI
 *
 * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
 *
 * Loosely based upon tle62x0.c
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#ifndef SPI_LIS3L02DQ_H_
#define SPI_LIS3L02DQ_H_
#define LIS3L02DQ_READ_REG(a) ((a) | 0x80)
#define LIS3L02DQ_WRITE_REG(a) a

/* Calibration parameters */
#define LIS3L02DQ_REG_OFFSET_X_ADDR		0x16
#define LIS3L02DQ_REG_OFFSET_Y_ADDR		0x17
#define LIS3L02DQ_REG_OFFSET_Z_ADDR		0x18

#define LIS3L02DQ_REG_GAIN_X_ADDR		0x19
#define LIS3L02DQ_REG_GAIN_Y_ADDR		0x1A
#define LIS3L02DQ_REG_GAIN_Z_ADDR		0x1B

/* Control Register (1 of 2) */
#define LIS3L02DQ_REG_CTRL_1_ADDR		0x20
/* Power ctrl - either bit set corresponds to on*/
#define LIS3L02DQ_REG_CTRL_1_PD_ON		0xC0

/* Decimation Factor  */
#define LIS3L02DQ_DEC_MASK			0x30
#define LIS3L02DQ_REG_CTRL_1_DF_128		0x00
#define LIS3L02DQ_REG_CTRL_1_DF_64		0x10
#define LIS3L02DQ_REG_CTRL_1_DF_32		0x20
#define LIS3L02DQ_REG_CTRL_1_DF_8		(0x10 | 0x20)

/* Self Test Enable */
#define LIS3L02DQ_REG_CTRL_1_SELF_TEST_ON	0x08

/* Axes enable ctrls */
#define LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE	0x04
#define LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE	0x02
#define LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE	0x01

/* Control Register (2 of 2) */
#define LIS3L02DQ_REG_CTRL_2_ADDR		0x21

/* Block Data Update only after MSB and LSB read */
#define LIS3L02DQ_REG_CTRL_2_BLOCK_UPDATE	0x40

/* Set to big endian output */
#define LIS3L02DQ_REG_CTRL_2_BIG_ENDIAN		0x20

/* Reboot memory content */
#define LIS3L02DQ_REG_CTRL_2_REBOOT_MEMORY	0x10

/* Interrupt Enable - applies data ready to the RDY pad */
#define LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT	0x08

/* Enable Data Ready Generation - relationship with previous unclear in docs */
#define LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION 0x04

/* SPI 3 wire mode */
#define LIS3L02DQ_REG_CTRL_2_THREE_WIRE_SPI_MODE	0x02

/* Data alignment, default is 12 bit right justified
 * - option for 16 bit left justified
 */
#define LIS3L02DQ_REG_CTRL_2_DATA_ALIGNMENT_16_BIT_LEFT_JUSTIFIED	0x01

/* Interrupt related stuff */
#define LIS3L02DQ_REG_WAKE_UP_CFG_ADDR			0x23

/* Switch from or combination of conditions to and */
#define LIS3L02DQ_REG_WAKE_UP_CFG_BOOLEAN_AND		0x80

/* Latch interrupt request,
 * if on ack must be given by reading the ack register
 */
#define LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC		0x40

/* Z Interrupt on High (above threshold) */
#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_HIGH	0x20
/* Z Interrupt on Low */
#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_LOW	0x10
/* Y Interrupt on High */
#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_HIGH	0x08
/* Y Interrupt on Low */
#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_LOW	0x04
/* X Interrupt on High */
#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_HIGH	0x02
/* X Interrupt on Low */
#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_LOW 0x01

/* Register that gives description of what caused interrupt
 * - latched if set in CFG_ADDRES
 */
#define LIS3L02DQ_REG_WAKE_UP_SRC_ADDR			0x24
/* top bit ignored */
/* Interrupt Active */
#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_ACTIVATED	0x40
/* Interupts that have been triggered */
#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH	0x20
#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_LOW	0x10
#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_HIGH	0x08
#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_LOW	0x04
#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_HIGH	0x02
#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_LOW	0x01

#define LIS3L02DQ_REG_WAKE_UP_ACK_ADDR			0x25

/* Status register */
#define LIS3L02DQ_REG_STATUS_ADDR			0x27
/* XYZ axis data overrun - first is all overrun? */
#define LIS3L02DQ_REG_STATUS_XYZ_OVERRUN		0x80
#define LIS3L02DQ_REG_STATUS_Z_OVERRUN			0x40
#define LIS3L02DQ_REG_STATUS_Y_OVERRUN			0x20
#define LIS3L02DQ_REG_STATUS_X_OVERRUN			0x10
/* XYZ new data available - first is all 3 available? */
#define LIS3L02DQ_REG_STATUS_XYZ_NEW_DATA		0x08
#define LIS3L02DQ_REG_STATUS_Z_NEW_DATA			0x04
#define LIS3L02DQ_REG_STATUS_Y_NEW_DATA			0x02
#define LIS3L02DQ_REG_STATUS_X_NEW_DATA			0x01

/* The accelerometer readings - low and high bytes.
 * Form of high byte dependent on justification set in ctrl reg
 */
#define LIS3L02DQ_REG_OUT_X_L_ADDR			0x28
#define LIS3L02DQ_REG_OUT_X_H_ADDR			0x29
#define LIS3L02DQ_REG_OUT_Y_L_ADDR			0x2A
#define LIS3L02DQ_REG_OUT_Y_H_ADDR			0x2B
#define LIS3L02DQ_REG_OUT_Z_L_ADDR			0x2C
#define LIS3L02DQ_REG_OUT_Z_H_ADDR			0x2D

/* Threshold values for all axes and both above and below thresholds
 * - i.e. there is only one value
 */
#define LIS3L02DQ_REG_THS_L_ADDR			0x2E
#define LIS3L02DQ_REG_THS_H_ADDR			0x2F

#define LIS3L02DQ_DEFAULT_CTRL1 (LIS3L02DQ_REG_CTRL_1_PD_ON	      \
				 | LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE \
				 | LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE \
				 | LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE \
				 | LIS3L02DQ_REG_CTRL_1_DF_128)

#define LIS3L02DQ_DEFAULT_CTRL2	0

#define LIS3L02DQ_MAX_TX 12
#define LIS3L02DQ_MAX_RX 12
/**
 * struct lis3l02dq_state - device instance specific data
 * @us:			actual spi_device
 * @trig:		data ready trigger registered with iio
 * @buf_lock:		mutex to protect tx and rx
 * @tx:			transmit buffer
 * @rx:			receive buffer
 **/
struct lis3l02dq_state {
	struct spi_device		*us;
	struct iio_trigger		*trig;
	struct mutex			buf_lock;
	int				gpio;
	bool				trigger_on;

	u8	tx[LIS3L02DQ_MAX_RX] ____cacheline_aligned;
	u8	rx[LIS3L02DQ_MAX_RX] ____cacheline_aligned;
};

int lis3l02dq_spi_read_reg_8(struct iio_dev *indio_dev,
			     u8 reg_address,
			     u8 *val);

int lis3l02dq_spi_write_reg_8(struct iio_dev *indio_dev,
			      u8 reg_address,
			      u8 val);

int lis3l02dq_disable_all_events(struct iio_dev *indio_dev);

#ifdef CONFIG_IIO_BUFFER
/* At the moment triggers are only used for buffer
 * filling. This may change!
 */
void lis3l02dq_remove_trigger(struct iio_dev *indio_dev);
int lis3l02dq_probe_trigger(struct iio_dev *indio_dev);

int lis3l02dq_configure_buffer(struct iio_dev *indio_dev);
void lis3l02dq_unconfigure_buffer(struct iio_dev *indio_dev);

irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private);
#define lis3l02dq_th lis3l02dq_data_rdy_trig_poll

#else /* CONFIG_IIO_BUFFER */
#define lis3l02dq_th lis3l02dq_nobuffer

static inline void lis3l02dq_remove_trigger(struct iio_dev *indio_dev)
{
}

static inline int lis3l02dq_probe_trigger(struct iio_dev *indio_dev)
{
	return 0;
}

static int lis3l02dq_configure_buffer(struct iio_dev *indio_dev)
{
	return 0;
}

static inline void lis3l02dq_unconfigure_buffer(struct iio_dev *indio_dev)
{
}
#endif /* CONFIG_IIO_BUFFER */
#endif /* SPI_LIS3L02DQ_H_ */
+0 −814

File deleted.

Preview size limit exceeded, changes collapsed.

+0 −428
Original line number Diff line number Diff line
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/mutex.h>
#include <linux/kernel.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <linux/export.h>

#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include "lis3l02dq.h"

/**
 * combine_8_to_16() utility function to munge two u8s into u16
 **/
static inline u16 combine_8_to_16(u8 lower, u8 upper)
{
	u16 _lower = lower;
	u16 _upper = upper;

	return _lower | (_upper << 8);
}

/**
 * lis3l02dq_data_rdy_trig_poll() the event handler for the data rdy trig
 **/
irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private)
{
	struct iio_dev *indio_dev = private;
	struct lis3l02dq_state *st = iio_priv(indio_dev);

	if (st->trigger_on) {
		iio_trigger_poll(st->trig);
		return IRQ_HANDLED;
	}

	return IRQ_WAKE_THREAD;
}

static const u8 read_all_tx_array[] = {
	LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_L_ADDR), 0,
	LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_H_ADDR), 0,
	LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Y_L_ADDR), 0,
	LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Y_H_ADDR), 0,
	LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Z_L_ADDR), 0,
	LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Z_H_ADDR), 0,
};

/**
 * lis3l02dq_read_all() Reads all channels currently selected
 * @indio_dev:	IIO device state
 * @rx_array:	(dma capable) receive array, must be at least
 *		4*number of channels
 **/
static int lis3l02dq_read_all(struct iio_dev *indio_dev, u8 *rx_array)
{
	struct lis3l02dq_state *st = iio_priv(indio_dev);
	struct spi_transfer *xfers;
	struct spi_message msg;
	int ret, i, j = 0;

	xfers = kcalloc(bitmap_weight(indio_dev->active_scan_mask,
				      indio_dev->masklength) * 2,
			sizeof(*xfers), GFP_KERNEL);
	if (!xfers)
		return -ENOMEM;

	mutex_lock(&st->buf_lock);

	for (i = 0; i < ARRAY_SIZE(read_all_tx_array) / 4; i++)
		if (test_bit(i, indio_dev->active_scan_mask)) {
			/* lower byte */
			xfers[j].tx_buf = st->tx + (2 * j);
			st->tx[2 * j] = read_all_tx_array[i * 4];
			st->tx[2 * j + 1] = 0;
			if (rx_array)
				xfers[j].rx_buf = rx_array + (j * 2);
			xfers[j].bits_per_word = 8;
			xfers[j].len = 2;
			xfers[j].cs_change = 1;
			j++;

			/* upper byte */
			xfers[j].tx_buf = st->tx + (2 * j);
			st->tx[2 * j] = read_all_tx_array[i * 4 + 2];
			st->tx[2 * j + 1] = 0;
			if (rx_array)
				xfers[j].rx_buf = rx_array + (j * 2);
			xfers[j].bits_per_word = 8;
			xfers[j].len = 2;
			xfers[j].cs_change = 1;
			j++;
		}

	/* After these are transmitted, the rx_buff should have
	 * values in alternate bytes
	 */
	spi_message_init(&msg);
	for (j = 0; j < bitmap_weight(indio_dev->active_scan_mask,
				      indio_dev->masklength) * 2; j++)
		spi_message_add_tail(&xfers[j], &msg);

	ret = spi_sync(st->us, &msg);
	mutex_unlock(&st->buf_lock);
	kfree(xfers);

	return ret;
}

static int lis3l02dq_get_buffer_element(struct iio_dev *indio_dev,
					u8 *buf)
{
	int ret, i;
	u8 *rx_array;
	s16 *data = (s16 *)buf;
	int scan_count = bitmap_weight(indio_dev->active_scan_mask,
				       indio_dev->masklength);

	rx_array = kcalloc(4, scan_count, GFP_KERNEL);
	if (!rx_array)
		return -ENOMEM;
	ret = lis3l02dq_read_all(indio_dev, rx_array);
	if (ret < 0) {
		kfree(rx_array);
		return ret;
	}
	for (i = 0; i < scan_count; i++)
		data[i] = combine_8_to_16(rx_array[i * 4 + 1],
					rx_array[i * 4 + 3]);
	kfree(rx_array);

	return i * sizeof(data[0]);
}

static irqreturn_t lis3l02dq_trigger_handler(int irq, void *p)
{
	struct iio_poll_func *pf = p;
	struct iio_dev *indio_dev = pf->indio_dev;
	int len = 0;
	char *data;

	data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
	if (!data)
		goto done;

	if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
		len = lis3l02dq_get_buffer_element(indio_dev, data);

	iio_push_to_buffers_with_timestamp(indio_dev, data, pf->timestamp);

	kfree(data);
done:
	iio_trigger_notify_done(indio_dev->trig);
	return IRQ_HANDLED;
}

/* Caller responsible for locking as necessary. */
static int
__lis3l02dq_write_data_ready_config(struct iio_dev *indio_dev, bool state)
{
	int ret;
	u8 valold;
	bool currentlyset;
	struct lis3l02dq_state *st = iio_priv(indio_dev);

	/* Get the current event mask register */
	ret = lis3l02dq_spi_read_reg_8(indio_dev,
				       LIS3L02DQ_REG_CTRL_2_ADDR,
				       &valold);
	if (ret)
		goto error_ret;
	/* Find out if data ready is already on */
	currentlyset
		= valold & LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;

	/* Disable requested */
	if (!state && currentlyset) {
		/* Disable the data ready signal */
		valold &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;

		/* The double write is to overcome a hardware bug? */
		ret = lis3l02dq_spi_write_reg_8(indio_dev,
						LIS3L02DQ_REG_CTRL_2_ADDR,
						valold);
		if (ret)
			goto error_ret;
		ret = lis3l02dq_spi_write_reg_8(indio_dev,
						LIS3L02DQ_REG_CTRL_2_ADDR,
						valold);
		if (ret)
			goto error_ret;
		st->trigger_on = false;
	/* Enable requested */
	} else if (state && !currentlyset) {
		/* If not set, enable requested
		 * first disable all events
		 */
		ret = lis3l02dq_disable_all_events(indio_dev);
		if (ret < 0)
			goto error_ret;

		valold = ret |
			LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;

		st->trigger_on = true;
		ret = lis3l02dq_spi_write_reg_8(indio_dev,
						LIS3L02DQ_REG_CTRL_2_ADDR,
						valold);
		if (ret)
			goto error_ret;
	}

	return 0;
error_ret:
	return ret;
}

/**
 * lis3l02dq_data_rdy_trigger_set_state() set datardy interrupt state
 *
 * If disabling the interrupt also does a final read to ensure it is clear.
 * This is only important in some cases where the scan enable elements are
 * switched before the buffer is reenabled.
 **/
static int lis3l02dq_data_rdy_trigger_set_state(struct iio_trigger *trig,
						bool state)
{
	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
	int ret = 0;
	u8 t;

	__lis3l02dq_write_data_ready_config(indio_dev, state);
	if (!state) {
		/*
		 * A possible quirk with the handler is currently worked around
		 * by ensuring outstanding read events are cleared.
		 */
		ret = lis3l02dq_read_all(indio_dev, NULL);
	}
	lis3l02dq_spi_read_reg_8(indio_dev,
				 LIS3L02DQ_REG_WAKE_UP_SRC_ADDR,
				 &t);
	return ret;
}

/**
 * lis3l02dq_trig_try_reen() try reenabling irq for data rdy trigger
 * @trig:	the datardy trigger
 */
static int lis3l02dq_trig_try_reen(struct iio_trigger *trig)
{
	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
	struct lis3l02dq_state *st = iio_priv(indio_dev);
	int i;

	/* If gpio still high (or high again)
	 * In theory possible we will need to do this several times
	 */
	for (i = 0; i < 5; i++)
		if (gpio_get_value(st->gpio))
			lis3l02dq_read_all(indio_dev, NULL);
		else
			break;
	if (i == 5)
		pr_info("Failed to clear the interrupt for lis3l02dq\n");

	/* irq reenabled so success! */
	return 0;
}

static const struct iio_trigger_ops lis3l02dq_trigger_ops = {
	.owner = THIS_MODULE,
	.set_trigger_state = &lis3l02dq_data_rdy_trigger_set_state,
	.try_reenable = &lis3l02dq_trig_try_reen,
};

int lis3l02dq_probe_trigger(struct iio_dev *indio_dev)
{
	int ret;
	struct lis3l02dq_state *st = iio_priv(indio_dev);

	st->trig = iio_trigger_alloc("lis3l02dq-dev%d", indio_dev->id);
	if (!st->trig) {
		ret = -ENOMEM;
		goto error_ret;
	}

	st->trig->dev.parent = &st->us->dev;
	st->trig->ops = &lis3l02dq_trigger_ops;
	iio_trigger_set_drvdata(st->trig, indio_dev);
	ret = iio_trigger_register(st->trig);
	if (ret)
		goto error_free_trig;

	return 0;

error_free_trig:
	iio_trigger_free(st->trig);
error_ret:
	return ret;
}

void lis3l02dq_remove_trigger(struct iio_dev *indio_dev)
{
	struct lis3l02dq_state *st = iio_priv(indio_dev);

	iio_trigger_unregister(st->trig);
	iio_trigger_free(st->trig);
}

void lis3l02dq_unconfigure_buffer(struct iio_dev *indio_dev)
{
	iio_dealloc_pollfunc(indio_dev->pollfunc);
	iio_kfifo_free(indio_dev->buffer);
}

static int lis3l02dq_buffer_postenable(struct iio_dev *indio_dev)
{
	/* Disable unwanted channels otherwise the interrupt will not clear */
	u8 t;
	int ret;
	bool oneenabled = false;

	ret = lis3l02dq_spi_read_reg_8(indio_dev,
				       LIS3L02DQ_REG_CTRL_1_ADDR,
				       &t);
	if (ret)
		goto error_ret;

	if (test_bit(0, indio_dev->active_scan_mask)) {
		t |= LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE;
		oneenabled = true;
	} else {
		t &= ~LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE;
	}
	if (test_bit(1, indio_dev->active_scan_mask)) {
		t |= LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE;
		oneenabled = true;
	} else {
		t &= ~LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE;
	}
	if (test_bit(2, indio_dev->active_scan_mask)) {
		t |= LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE;
		oneenabled = true;
	} else {
		t &= ~LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE;
	}
	if (!oneenabled) /* what happens in this case is unknown */
		return -EINVAL;
	ret = lis3l02dq_spi_write_reg_8(indio_dev,
					LIS3L02DQ_REG_CTRL_1_ADDR,
					t);
	if (ret)
		goto error_ret;

	return iio_triggered_buffer_postenable(indio_dev);
error_ret:
	return ret;
}

/* Turn all channels on again */
static int lis3l02dq_buffer_predisable(struct iio_dev *indio_dev)
{
	u8 t;
	int ret;

	ret = iio_triggered_buffer_predisable(indio_dev);
	if (ret)
		goto error_ret;

	ret = lis3l02dq_spi_read_reg_8(indio_dev,
				       LIS3L02DQ_REG_CTRL_1_ADDR,
				       &t);
	if (ret)
		goto error_ret;
	t |= LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE |
		LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE |
		LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE;

	ret = lis3l02dq_spi_write_reg_8(indio_dev,
					LIS3L02DQ_REG_CTRL_1_ADDR,
					t);

error_ret:
	return ret;
}

static const struct iio_buffer_setup_ops lis3l02dq_buffer_setup_ops = {
	.postenable = &lis3l02dq_buffer_postenable,
	.predisable = &lis3l02dq_buffer_predisable,
};

int lis3l02dq_configure_buffer(struct iio_dev *indio_dev)
{
	int ret;
	struct iio_buffer *buffer;

	buffer = iio_kfifo_allocate();
	if (!buffer)
		return -ENOMEM;

	iio_device_attach_buffer(indio_dev, buffer);

	buffer->scan_timestamp = true;
	indio_dev->setup_ops = &lis3l02dq_buffer_setup_ops;

	/* Functions are NULL as we set handler below */
	indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
						 &lis3l02dq_trigger_handler,
						 0,
						 indio_dev,
						 "lis3l02dq_consumer%d",
						 indio_dev->id);

	if (!indio_dev->pollfunc) {
		ret = -ENOMEM;
		goto error_iio_sw_rb_free;
	}

	indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
	return 0;

error_iio_sw_rb_free:
	iio_kfifo_free(indio_dev->buffer);
	return ret;
}