Commit fc6bd727 authored by Jonathan Cameron's avatar Jonathan Cameron

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
......@@ -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
......
......@@ -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
/*
* 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_ */
/*
* lis3l02dq.c support STMicroelectronics LISD02DQ
* 3d 2g Linear Accelerometers via SPI
*
* Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
*
* 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.
*
* Settings:
* 16 bit left justified mode used.
*/
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/events.h>
#include <linux/iio/buffer.h>
#include "lis3l02dq.h"
/* At the moment the spi framework doesn't allow global setting of cs_change.
* It's in the likely to be added comment at the top of spi.h.
* This means that use cannot be made of spi_write etc.
*/
/* direct copy of the irq_default_primary_handler */
#ifndef CONFIG_IIO_BUFFER
static irqreturn_t lis3l02dq_nobuffer(int irq, void *private)
{
return IRQ_WAKE_THREAD;
}
#endif
/**
* lis3l02dq_spi_read_reg_8() - read single byte from a single register
* @indio_dev: iio_dev for this actual device
* @reg_address: the address of the register to be read
* @val: pass back the resulting value
**/
int lis3l02dq_spi_read_reg_8(struct iio_dev *indio_dev,
u8 reg_address, u8 *val)
{
struct lis3l02dq_state *st = iio_priv(indio_dev);
int ret;
struct spi_transfer xfer = {
.tx_buf = st->tx,
.rx_buf = st->rx,
.bits_per_word = 8,
.len = 2,
};
mutex_lock(&st->buf_lock);
st->tx[0] = LIS3L02DQ_READ_REG(reg_address);
st->tx[1] = 0;
ret = spi_sync_transfer(st->us, &xfer, 1);
*val = st->rx[1];
mutex_unlock(&st->buf_lock);
return ret;
}
/**
* lis3l02dq_spi_write_reg_8() - write single byte to a register
* @indio_dev: iio_dev for this device
* @reg_address: the address of the register to be written
* @val: the value to write
**/
int lis3l02dq_spi_write_reg_8(struct iio_dev *indio_dev,
u8 reg_address,
u8 val)
{
int ret;
struct lis3l02dq_state *st = iio_priv(indio_dev);
mutex_lock(&st->buf_lock);
st->tx[0] = LIS3L02DQ_WRITE_REG(reg_address);
st->tx[1] = val;
ret = spi_write(st->us, st->tx, 2);
mutex_unlock(&st->buf_lock);
return ret;
}
/**
* lisl302dq_spi_write_reg_s16() - write 2 bytes to a pair of registers
* @indio_dev: iio_dev for this device
* @lower_reg_address: the address of the lower of the two registers.
* Second register is assumed to have address one greater.
* @value: value to be written
**/
static int lis3l02dq_spi_write_reg_s16(struct iio_dev *indio_dev,
u8 lower_reg_address,
s16 value)
{
int ret;
struct lis3l02dq_state *st = iio_priv(indio_dev);
struct spi_transfer xfers[] = { {
.tx_buf = st->tx,
.bits_per_word = 8,
.len = 2,
.cs_change = 1,
}, {
.tx_buf = st->tx + 2,
.bits_per_word = 8,
.len = 2,
},
};
mutex_lock(&st->buf_lock);
st->tx[0] = LIS3L02DQ_WRITE_REG(lower_reg_address);
st->tx[1] = value & 0xFF;
st->tx[2] = LIS3L02DQ_WRITE_REG(lower_reg_address + 1);
st->tx[3] = (value >> 8) & 0xFF;
ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
mutex_unlock(&st->buf_lock);
return ret;
}
static int lis3l02dq_read_reg_s16(struct iio_dev *indio_dev,
u8 lower_reg_address,
int *val)
{
struct lis3l02dq_state *st = iio_priv(indio_dev);
int ret;
s16 tempval;
struct spi_transfer xfers[] = { {
.tx_buf = st->tx,
.rx_buf = st->rx,
.bits_per_word = 8,
.len = 2,
.cs_change = 1,
}, {
.tx_buf = st->tx + 2,
.rx_buf = st->rx + 2,
.bits_per_word = 8,
.len = 2,
},
};
mutex_lock(&st->buf_lock);
st->tx[0] = LIS3L02DQ_READ_REG(lower_reg_address);
st->tx[1] = 0;
st->tx[2] = LIS3L02DQ_READ_REG(lower_reg_address + 1);
st->tx[3] = 0;
ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
if (ret) {
dev_err(&st->us->dev, "problem when reading 16 bit register");
goto error_ret;
}
tempval = (s16)(st->rx[1]) | ((s16)(st->rx[3]) << 8);
*val = tempval;
error_ret:
mutex_unlock(&st->buf_lock);
return ret;
}
enum lis3l02dq_rm_ind {
LIS3L02DQ_ACCEL,
LIS3L02DQ_GAIN,
LIS3L02DQ_BIAS,
};
static u8 lis3l02dq_axis_map[3][3] = {
[LIS3L02DQ_ACCEL] = { LIS3L02DQ_REG_OUT_X_L_ADDR,
LIS3L02DQ_REG_OUT_Y_L_ADDR,
LIS3L02DQ_REG_OUT_Z_L_ADDR },
[LIS3L02DQ_GAIN] = { LIS3L02DQ_REG_GAIN_X_ADDR,
LIS3L02DQ_REG_GAIN_Y_ADDR,
LIS3L02DQ_REG_GAIN_Z_ADDR },
[LIS3L02DQ_BIAS] = { LIS3L02DQ_REG_OFFSET_X_ADDR,
LIS3L02DQ_REG_OFFSET_Y_ADDR,
LIS3L02DQ_REG_OFFSET_Z_ADDR }
};
static int lis3l02dq_read_thresh(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int *val, int *val2)
{
int ret;
ret = lis3l02dq_read_reg_s16(indio_dev, LIS3L02DQ_REG_THS_L_ADDR, val);
if (ret)
return ret;
return IIO_VAL_INT;
}
static int lis3l02dq_write_thresh(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int val, int val2)
{
u16 value = val;
return lis3l02dq_spi_write_reg_s16(indio_dev,
LIS3L02DQ_REG_THS_L_ADDR,
value);
}
static int lis3l02dq_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
int val2,
long mask)
{
int ret = -EINVAL, reg;
u8 uval;
s8 sval;
switch (mask) {
case IIO_CHAN_INFO_CALIBBIAS:
if (val > 255 || val < -256)
return -EINVAL;
sval = val;
reg = lis3l02dq_axis_map[LIS3L02DQ_BIAS][chan->address];
ret = lis3l02dq_spi_write_reg_8(indio_dev, reg, sval);
break;
case IIO_CHAN_INFO_CALIBSCALE:
if (val & ~0xFF)
return -EINVAL;
uval = val;
reg = lis3l02dq_axis_map[LIS3L02DQ_GAIN][chan->address];
ret = lis3l02dq_spi_write_reg_8(indio_dev, reg, uval);
break;
}
return ret;
}
static int lis3l02dq_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
int *val2,
long mask)
{
u8 utemp;
s8 stemp;
ssize_t ret = 0;
u8 reg;
switch (mask) {
case IIO_CHAN_INFO_RAW:
/* Take the iio_dev status lock */
mutex_lock(&indio_dev->mlock);
if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
ret = -EBUSY;
} else {
reg = lis3l02dq_axis_map
[LIS3L02DQ_ACCEL][chan->address];
ret = lis3l02dq_read_reg_s16(indio_dev, reg, val);
}
mutex_unlock(&indio_dev->mlock);
if (ret < 0)
goto error_ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = 9580;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_CALIBSCALE:
reg = lis3l02dq_axis_map[LIS3L02DQ_GAIN][chan->address];
ret = lis3l02dq_spi_read_reg_8(indio_dev, reg, &utemp);
if (ret)
goto error_ret;
/* to match with what previous code does */
*val = utemp;
return IIO_VAL_INT;
case IIO_CHAN_INFO_CALIBBIAS:
reg = lis3l02dq_axis_map[LIS3L02DQ_BIAS][chan->address];
ret = lis3l02dq_spi_read_reg_8(indio_dev, reg, (u8 *)&stemp);
/* to match with what previous code does */
*val = stemp;
return IIO_VAL_INT;
}
error_ret:
return ret;
}
static ssize_t lis3l02dq_read_frequency(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
int ret, len = 0;
s8 t;
ret = lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
(u8 *)&t);
if (ret)
return ret;
t &= LIS3L02DQ_DEC_MASK;
switch (t) {
case LIS3L02DQ_REG_CTRL_1_DF_128:
len = sprintf(buf, "280\n");
break;
case LIS3L02DQ_REG_CTRL_1_DF_64:
len = sprintf(buf, "560\n");
break;
case LIS3L02DQ_REG_CTRL_1_DF_32:
len = sprintf(buf, "1120\n");
break;
case LIS3L02DQ_REG_CTRL_1_DF_8:
len = sprintf(buf, "4480\n");
break;
}
return len;
}
static ssize_t lis3l02dq_write_frequency(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
unsigned long val;
int ret;
u8 t;
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
mutex_lock(&indio_dev->mlock);
ret = lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
&t);
if (ret)
goto error_ret_mutex;
/* Wipe the bits clean */
t &= ~LIS3L02DQ_DEC_MASK;
switch (val) {
case 280:
t |= LIS3L02DQ_REG_CTRL_1_DF_128;
break;
case 560:
t |= LIS3L02DQ_REG_CTRL_1_DF_64;
break;
case 1120:
t |= LIS3L02DQ_REG_CTRL_1_DF_32;
break;
case 4480:
t |= LIS3L02DQ_REG_CTRL_1_DF_8;
break;
default:
ret = -EINVAL;
goto error_ret_mutex;
}
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
t);
error_ret_mutex:
mutex_unlock(&indio_dev->mlock);
return ret ? ret : len;
}
static int lis3l02dq_initial_setup(struct iio_dev *indio_dev)
{
struct lis3l02dq_state *st = iio_priv(indio_dev);
int ret;
u8 val, valtest;
st->us->mode = SPI_MODE_3;
spi_setup(st->us);
val = LIS3L02DQ_DEFAULT_CTRL1;
/* Write suitable defaults to ctrl1 */
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
val);
if (ret) {
dev_err(&st->us->dev, "problem with setup control register 1");
goto err_ret;
}
/* Repeat as sometimes doesn't work first time? */
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
val);
if (ret) {
dev_err(&st->us->dev, "problem with setup control register 1");
goto err_ret;
}
/*
* Read back to check this has worked acts as loose test of correct
* chip
*/
ret = lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
&valtest);
if (ret || (valtest != val)) {
dev_err(&indio_dev->dev,
"device not playing ball %d %d\n", valtest, val);
ret = -EINVAL;
goto err_ret;
}
val = LIS3L02DQ_DEFAULT_CTRL2;
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
val);
if (ret) {
dev_err(&st->us->dev, "problem with setup control register 2");
goto err_ret;
}
val = LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC;
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
val);
if (ret)
dev_err(&st->us->dev, "problem with interrupt cfg register");
err_ret:
return ret;
}
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
lis3l02dq_read_frequency,
lis3l02dq_write_frequency);
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("280 560 1120 4480");
static irqreturn_t lis3l02dq_event_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
u8 t;
s64 timestamp = iio_get_time_ns(indio_dev);
lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_WAKE_UP_SRC_ADDR,
&t);
if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL,
0,
IIO_MOD_Z,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING),
timestamp);
if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_LOW)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL,
0,
IIO_MOD_Z,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_FALLING),
timestamp);
if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_HIGH)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL,
0,
IIO_MOD_Y,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING),
timestamp);
if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_LOW)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL,
0,
IIO_MOD_Y,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_FALLING),
timestamp);
if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_HIGH)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL,
0,
IIO_MOD_X,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING),
timestamp);
if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_LOW)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL,
0,
IIO_MOD_X,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_FALLING),
timestamp);
/* Ack and allow for new interrupts */
lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_WAKE_UP_ACK_ADDR,
&t);
return IRQ_HANDLED;
}
static const struct iio_event_spec lis3l02dq_event[] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
}, {
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
}
};
#define LIS3L02DQ_CHAN(index, mod) \
{ \
.type = IIO_ACCEL, \
.modified = 1, \
.channel2 = mod, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_CALIBSCALE) | \
BIT(IIO_CHAN_INFO_CALIBBIAS), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.address = index, \
.scan_index = index, \
.scan_type = { \
.sign = 's', \
.realbits = 12, \
.storagebits = 16, \
}, \
.event_spec = lis3l02dq_event, \
.num_event_specs = ARRAY_SIZE(lis3l02dq_event), \
}
static const struct iio_chan_spec lis3l02dq_channels[] = {
LIS3L02DQ_CHAN(0, IIO_MOD_X),
LIS3L02DQ_CHAN(1, IIO_MOD_Y),
LIS3L02DQ_CHAN(2, IIO_MOD_Z),
IIO_CHAN_SOFT_TIMESTAMP(3)
};
static int lis3l02dq_read_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir)
{
u8 val;
int ret;
u8 mask = 1 << (chan->channel2 * 2 + (dir == IIO_EV_DIR_RISING));
ret = lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
&val);
if (ret < 0)
return ret;
return !!(val & mask);
}
int lis3l02dq_disable_all_events(struct iio_dev *indio_dev)
{
int ret;
u8 control, val;
ret = lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
&control);
control &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT;
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
control);
if (ret)
goto error_ret;
/* Also for consistency clear the mask */
ret = lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
&val);
if (ret)
goto error_ret;
val &= ~0x3f;
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
val);
if (ret)
goto error_ret;
ret = control;
error_ret:
return ret;
}
static int lis3l02dq_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
int state)
{
int ret = 0;
u8 val, control;
u8 currentlyset;
bool changed = false;
u8 mask = 1 << (chan->channel2 * 2 + (dir == IIO_EV_DIR_RISING));
mutex_lock(&indio_dev->mlock);
/* read current control */
ret = lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
&control);
if (ret)
goto error_ret;
ret = lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
&val);
if (ret < 0)
goto error_ret;
currentlyset = val & mask;
if (!currentlyset && state) {
changed = true;
val |= mask;
} else if (currentlyset && !state) {
changed = true;
val &= ~mask;
}
if (changed) {
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
val);
if (ret)
goto error_ret;
control = val & 0x3f ?
(control | LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT) :
(control & ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT);
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
control);
if (ret)
goto error_ret;
}
error_ret:
mutex_unlock(&indio_dev->mlock);
return ret;
}
static struct attribute *lis3l02dq_attributes[] = {
&iio_dev_attr_sampling_frequency.dev_attr.attr,
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
NULL
};
static const struct attribute_group lis3l02dq_attribute_group = {
.attrs = lis3l02dq_attributes,
};
static const struct iio_info lis3l02dq_info = {
.read_raw = &lis3l02dq_read_raw,
.write_raw = &lis3l02dq_write_raw,
.read_event_value = &lis3l02dq_read_thresh,
.write_event_value = &lis3l02dq_write_thresh,
.write_event_config = &lis3l02dq_write_event_config,
.read_event_config = &lis3l02dq_read_event_config,
.driver_module = THIS_MODULE,
.attrs = &lis3l02dq_attribute_group,
};
static int lis3l02dq_probe(struct spi_device *spi)
{
int ret;
struct lis3l02dq_state *st;
struct iio_dev *indio_dev;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
/* this is only used for removal purposes */
spi_set_drvdata(spi, indio_dev);
st->us = spi;
st->gpio = of_get_gpio(spi->dev.of_node, 0);
mutex_init(&st->buf_lock);
indio_dev->name = spi->dev.driver->name;
indio_dev->dev.parent = &spi->dev;
indio_dev->info = &lis3l02dq_info;
indio_dev->channels = lis3l02dq_channels;
indio_dev->num_channels = ARRAY_SIZE(lis3l02dq_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
ret = lis3l02dq_configure_buffer(indio_dev);
if (ret)
return ret;
if (spi->irq) {
ret = request_threaded_irq(st->us->irq,
&lis3l02dq_th,
&lis3l02dq_event_handler,
IRQF_TRIGGER_RISING,
"lis3l02dq",
indio_dev);
if (ret)
goto error_unreg_buffer_funcs;
ret = lis3l02dq_probe_trigger(indio_dev);
if (ret)
goto error_free_interrupt;
}
/* Get the device into a sane initial state */
ret = lis3l02dq_initial_setup(indio_dev);
if (ret)
goto error_remove_trigger;
ret = iio_device_register(indio_dev);
if (ret)
goto error_remove_trigger;
return 0;
error_remove_trigger:
if (spi->irq)
lis3l02dq_remove_trigger(indio_dev);
error_free_interrupt:
if (spi->irq)
free_irq(st->us->irq, indio_dev);
error_unreg_buffer_funcs:
lis3l02dq_unconfigure_buffer(indio_dev);
return ret;
}
/* Power down the device */
static int lis3l02dq_stop_device(struct iio_dev *indio_dev)
{
int ret;
struct lis3l02dq_state *st = iio_priv(indio_dev);
u8 val = 0;
mutex_lock(&indio_dev->mlock);
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
val);
if (ret) {
dev_err(&st->us->dev, "problem with turning device off: ctrl1");
goto err_ret;
}
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
val);
if (ret)
dev_err(&st->us->dev, "problem with turning device off: ctrl2");
err_ret:
mutex_unlock(&indio_dev->mlock);
return ret;
}
/* fixme, confirm ordering in this function */
static int lis3l02dq_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct lis3l02dq_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
lis3l02dq_disable_all_events(indio_dev);
lis3l02dq_stop_device(indio_dev);
if (spi->irq)
free_irq(st->us->irq, indio_dev);
lis3l02dq_remove_trigger(indio_dev);
lis3l02dq_unconfigure_buffer(indio_dev);
return 0;
}
static struct spi_driver lis3l02dq_driver = {
.driver = {
.name = "lis3l02dq",
},
.probe = lis3l02dq_probe,
.remove = lis3l02dq_remove,
};
module_spi_driver(lis3l02dq_driver);
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("ST LIS3L02DQ Accelerometer SPI driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("spi:lis3l02dq");
#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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment