Re: [PATCH] staging:iio:accel: Add driver for LIS331DLH

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 11/02/11 12:15, Manuel Stahl wrote:
> Am Mittwoch, 2. November 2011, 12:34:19 schrieb Jonathan Cameron:
>> Hi Manuel,
>>
>> How similar is this to those parts supported by the lis3 driver
>> in misc?  I have no particular problem with supporting this in staging
>> but we are probably going to have more issues making sure nothing is lost
>> if we attempt to merge it with that driver when outside staging.
> 
> It seem this is the first driver to support I2C and SPI.
The driver in misc does.
> 
>>
>> There are also a number of previous drivers out there for this part
>> http://www.gossamer-threads.com/lists/linux/kernel/1300192
>>
>> I have no idea if the lis3lv02d driver support this part currently or
>> not (which was the issue I raised with that previous driver).
> 
> The lis331dlh if really different from the lis3lv02d device. Especially the 
> event stuff. That's why I started a new driver.
Fair enough!
> 
>> Anyhow, we are already carrying the lis3l02dq driver mainly because I have
>> one on a board and it is handy for testing various things, so what's one
>> more.  Having said that we probably want to document this in Kconfig to
>> make it clear why this is there.
> 
> OK, how would you do that?
"Note the the lis331dlh is also supported by misc/lis3... which is currently
the preferred choice for use as an input device."
> 
>> For that matter, I've not really looked at this with a view to merging the
>> support with that driver either!
>>
>> Review, ignoring all those possible issues follows.
>>
>> On 11/02/11 10:09, Manuel Stahl wrote:
>>> Signed-off-by: Manuel Stahl <manuel.stahl@xxxxxxxxxxxxxxxxx>
>>> ---
>>>
>>>  drivers/staging/iio/accel/Kconfig          |   24 ++
>>>  drivers/staging/iio/accel/Makefile         |    4 +
>>>  drivers/staging/iio/accel/lis331dlh.h      |  224 +++++++++++++++
>>>  drivers/staging/iio/accel/lis331dlh_core.c |  406
>>>  ++++++++++++++++++++++++++++ drivers/staging/iio/accel/lis331dlh_i2c.c 
>>>  |  176 ++++++++++++
>>>  drivers/staging/iio/accel/lis331dlh_spi.c  |  231 ++++++++++++++++
>>>  6 files changed, 1065 insertions(+), 0 deletions(-)
>>>  create mode 100644 drivers/staging/iio/accel/lis331dlh.h
>>>  create mode 100644 drivers/staging/iio/accel/lis331dlh_core.c
>>>  create mode 100644 drivers/staging/iio/accel/lis331dlh_i2c.c
>>>  create mode 100644 drivers/staging/iio/accel/lis331dlh_spi.c
>>>
>>> diff --git a/drivers/staging/iio/accel/Kconfig
>>> b/drivers/staging/iio/accel/Kconfig index 5ab7167..7473393 100644
>>> --- a/drivers/staging/iio/accel/Kconfig
>>> +++ b/drivers/staging/iio/accel/Kconfig
>>> @@ -94,6 +94,30 @@ config LIS3L02DQ_BUF_RING_SW
>>>
>>>  endchoice
>>>
>>> +config LIS331DLH
>>> +	bool "ST Microelectronics LIS331DLH Accelerometer Driver"
>>> +	help
>>> +	  Say yes here to build support for the ST microelectronics LIS331DLH
>>> +	  accelerometer. The driver supplies direct access via sysfs files.
>>> +
>>> +if LIS331DLH
>>> +
>>> +config LIS331DLH_SPI
>>> +	tristate "ST Microelectronics LIS331DLH Accelerometer Driver (SPI)"
>>> +	depends on SPI
>>> +	help
>>> +	  Say yes here to build SPI interface support for the
>>> +	  ST microelectronics LIS331DLH accelerometer.
>>> +
>>> +config LIS331DLH_I2C
>>> +	tristate "ST Microelectronics LIS331DLH Accelerometer Driver (I2C)"
>>> +	depends on I2C
>>> +	help
>>> +	  Say yes here to build I2C interface support for the
>>> +	  ST microelectronics LIS331DLH accelerometer.
>>> +
>>> +endif # LIS331DLH
>>> +
>>>
>>>  config SCA3000
>>>  
>>>  	depends on IIO_BUFFER
>>>  	depends on SPI
>>>
>>> diff --git a/drivers/staging/iio/accel/Makefile
>>> b/drivers/staging/iio/accel/Makefile index 95c6666..22986b7 100644
>>> --- a/drivers/staging/iio/accel/Makefile
>>> +++ b/drivers/staging/iio/accel/Makefile
>>> @@ -27,6 +27,10 @@ obj-$(CONFIG_ADIS16240) += adis16240.o
>>>
>>>  obj-$(CONFIG_KXSD9)	+= kxsd9.o
>>>
>>> +lis331dlh-y		:= lis331dlh_core.o
>>> +obj-$(CONFIG_LIS331DLH_SPI)	+= lis331dlh.o lis331dlh_spi.o
>>> +obj-$(CONFIG_LIS331DLH_I2C)	+= lis331dlh.o lis331dlh_i2c.o
>>> +
>>>
>>>  lis3l02dq-y		:= lis3l02dq_core.o
>>>  lis3l02dq-$(CONFIG_IIO_BUFFER) += lis3l02dq_ring.o
>>>  obj-$(CONFIG_LIS3L02DQ)	+= lis3l02dq.o
>>>
>>> diff --git a/drivers/staging/iio/accel/lis331dlh.h
>>> b/drivers/staging/iio/accel/lis331dlh.h new file mode 100644
>>> index 0000000..1c5a39d
>>> --- /dev/null
>>> +++ b/drivers/staging/iio/accel/lis331dlh.h
>>> @@ -0,0 +1,224 @@
>>> +/*
>>> + * lis331dlh.h -- support STMicroelectronics LIS331DLH
>>> + *                3d 2g/4g/8g Linear Accelerometers
>>> + *
>>> + * Copyright (c) 2007 Jonathan Cameron <jic23@xxxxxxxxx>
>>> + * Copyright (c) 2011 Manuel Stahl <manuel.stahl@xxxxxxxxxxxxxxxxx>
>>> + *
>>> + * 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_LIS331DLH_H_
>>> +#define SPI_LIS331DLH_H_
>>> +
>>> +#include <asm/byteorder.h>
>>
>> This is unusual to see in here, so perhaps a comment saying why you
>> are messing with endianness?
> 
> The endianess of the device can be configured by a register entry. So we don't 
> have to do it in the driver when the devices is set up correctly.
That will do nicely.
> 
>>> +
>>> +#define LIS331DLH "lis331dlh"
>>> +
>>> +#define LIS331DLH_READ_REG(a) ((a) | 0x80)
>>> +#define LIS331DLH_WRITE_REG(a) a
>>> +#define LIS331DLH_I2C_AUTO_INC(a) ((a) | 0x80)
>>> +
>>> +#define LIS331DLH_REG_WHO_AM_I_ADDR			0x0F
>>> +#define LIS331DLH_REG_WHO_AM_I_DEFAULT			0x32
>>> +
>>> +/* Control Register (1 of 5) */
>>> +#define LIS331DLH_REG_CTRL_1_ADDR			0x20
>>> +/* Power ctrl - either bit set corresponds to on */
>>> +#define LIS331DLH_REG_CTRL_1_POWER_OFF			0x00
>>> +#define LIS331DLH_REG_CTRL_1_POWER_ON			0x20
>>> +#define LIS331DLH_REG_CTRL_1_LOW_POWER_05		0x40
>>> +
>>> +/* Data Rate */
>>> +#define LIS331DLH_REG_CTRL_1_DR_50			0x00
>>> +#define LIS331DLH_REG_CTRL_1_DR_100			0x08
>>> +#define LIS331DLH_REG_CTRL_1_DR_400			0x10
>>> +#define LIS331DLH_REG_CTRL_1_DR_1000			0x18
>>> +#define LIS331DLH_REG_CTRL_1_DR_MASK			0x18
>>> +
>>> +/* Axes enable ctrls */
>>> +#define LIS331DLH_REG_CTRL_1_AXES_Z_ENABLE		0x04
>>> +#define LIS331DLH_REG_CTRL_1_AXES_Y_ENABLE		0x02
>>> +#define LIS331DLH_REG_CTRL_1_AXES_X_ENABLE		0x01
>>> +
>>> +/* Control Register (2 of 5) */
>>> +#define LIS331DLH_REG_CTRL_2_ADDR			0x21
>>> +
>>> +#define LIS331DLH_REG_CTRL_2_BOOT			0x80
>>> +
>>> +#define LIS331DLH_REG_CTRL_2_HP_NORM			0x00
>>> +#define LIS331DLH_REG_CTRL_2_HP_REF			0x40
>>> +
>>> +#define LIS331DLH_REG_CTRL_2_FDS_INT			0x10
>>> +#define LIS331DLH_REG_CTRL_2_HPEN2			0x08
>>> +#define LIS331DLH_REG_CTRL_2_HPEN1			0x04
>>> +#define LIS331DLH_REG_CTRL_2_HPCF1			0x02
>>> +#define LIS331DLH_REG_CTRL_2_HPCF0			0x01
>>> +
>>> +/* Control Register (3 of 5) */
>>> +#define LIS331DLH_REG_CTRL_3_ADDR			0x22
>>> +
>>> +#define LIS331DLH_REG_CTRL_3_IHL			0x80
>>> +#define LIS331DLH_REG_CTRL_3_PP_OD			0x40
>>> +#define LIS331DLH_REG_CTRL_3_LIR2			0x20
>>> +#define LIS331DLH_REG_CTRL_3_I2_I			0x00
>>> +#define LIS331DLH_REG_CTRL_3_I2_1OR2			0x08
>>> +#define LIS331DLH_REG_CTRL_3_I2_DRDY			0x10
>>> +#define LIS331DLH_REG_CTRL_3_I2_RUN			0x18
>>> +#define LIS331DLH_REG_CTRL_3_I2_MASK			0x18
>>> +#define LIS331DLH_REG_CTRL_3_LIR1			0x04
>>> +#define LIS331DLH_REG_CTRL_3_I1_I			0x00
>>> +#define LIS331DLH_REG_CTRL_3_I1_1OR2			0x01
>>> +#define LIS331DLH_REG_CTRL_3_I1_DRDY			0x02
>>> +#define LIS331DLH_REG_CTRL_3_I1_RUN			0x03
>>> +#define LIS331DLH_REG_CTRL_3_I1_MASK			0x03
>>> +
>>> +/* Control Register (4 of 5) */
>>> +#define LIS331DLH_REG_CTRL_4_ADDR			0x23
>>> +
>>> +/* Block Data Update only after MSB and LSB read */
>>> +#define LIS331DLH_REG_CTRL_4_BLOCK_UPDATE		0x80
>>> +
>>> +/* Set to big endian output */
>>> +#define LIS331DLH_REG_CTRL_4_BIG_ENDIAN			0x40
>>> +
>>> +/* Full scale selection */
>>> +#define LIS331DLH_REG_CTRL_4_FS_2G			0x00
>>> +#define LIS331DLH_REG_CTRL_4_FS_4G			0x10
>>> +#define LIS331DLH_REG_CTRL_4_FS_8G			0x30
>>> +#define LIS331DLH_REG_CTRL_4_FS_MASK			0x30
>>> +
>>> +/* Self Test Sign */
>>> +#define LIS331DLH_REG_CTRL_4_ST_SIGN			0x08
>>> +
>>> +/* Self Test Enable */
>>> +#define LIS331DLH_REG_CTRL_4_ST_ON			0x02
>>> +
>>> +/* SPI 3 wire mode */
>>> +#define LIS331DLH_REG_CTRL_4_THREE_WIRE_SPI_MODE	0x01
>>> +
>>> +/* Control Register (5 of 5) */
>>> +#define LIS331DLH_REG_CTRL_5_ADDR			0x24
>>> +
>>> +#define LIS331DLH_REG_CTRL_5_SLEEP_WAKE_OFF		0x00
>>> +#define LIS331DLH_REG_CTRL_5_LOW_POWER			0x03
>>> +
>>> +/* Dummy register */
>>> +#define LIS331DLH_REG_HP_FILTER_RESET			0x25
>>> +
>>> +/* Reference register */
>>> +#define LIS331DLH_REG_REFERENCE				0x26
>>> +
>>> +/* Status register */
>>> +#define LIS331DLH_REG_STATUS_ADDR			0x27
>>> +/* XYZ axis data overrun - first is all overrun? */
>>> +#define LIS331DLH_REG_STATUS_XYZ_OVERRUN		0x80
>>> +#define LIS331DLH_REG_STATUS_Z_OVERRUN			0x40
>>> +#define LIS331DLH_REG_STATUS_Y_OVERRUN			0x20
>>> +#define LIS331DLH_REG_STATUS_X_OVERRUN			0x10
>>> +/* XYZ new data available - first is all 3 available? */
>>> +#define LIS331DLH_REG_STATUS_XYZ_NEW_DATA		0x08
>>> +#define LIS331DLH_REG_STATUS_Z_NEW_DATA			0x04
>>> +#define LIS331DLH_REG_STATUS_Y_NEW_DATA			0x02
>>> +#define LIS331DLH_REG_STATUS_X_NEW_DATA			0x01
>>> +
>>> +/* The accelerometer readings - low and high bytes.
>>> +Form of high byte dependant on justification set in ctrl reg */
>>> +#define LIS331DLH_REG_OUT_X_L_ADDR			0x28
>>> +#define LIS331DLH_REG_OUT_X_H_ADDR			0x29
>>> +#define LIS331DLH_REG_OUT_Y_L_ADDR			0x2A
>>> +#define LIS331DLH_REG_OUT_Y_H_ADDR			0x2B
>>> +#define LIS331DLH_REG_OUT_Z_L_ADDR			0x2C
>>> +#define LIS331DLH_REG_OUT_Z_H_ADDR			0x2D
>>> +
>>> +/* Interrupt 1 config register */
>>> +#define LIS331DLH_REG_INT1_CFG_ADDR			0x30
>>> +#define LIS331DLH_REG_INT1_SRC_ADDR			0x31
>>> +#define LIS331DLH_REG_INT1_THS_ADDR			0x32
>>> +#define LIS331DLH_REG_INT1_DURATION_ADDR		0x33
>>> +
>>> +/* Interrupt 2 config register */
>>> +#define LIS331DLH_REG_INT2_CFG_ADDR			0x34
>>> +#define LIS331DLH_REG_INT2_SRC_ADDR			0x35
>>> +#define LIS331DLH_REG_INT2_THS_ADDR			0x36
>>> +#define LIS331DLH_REG_INT2_DURATION_ADDR		0x37
>>> +
>>> +#define LIS331DLH_REG_INT_AOI				0x80
>>> +#define LIS331DLH_REG_INT_6D				0x40
>>> +#define LIS331DLH_REG_INT_Z_HIGH			0x20
>>> +#define LIS331DLH_REG_INT_Z_LOW				0x10
>>> +#define LIS331DLH_REG_INT_Y_HIGH			0x08
>>> +#define LIS331DLH_REG_INT_Y_LOW				0x04
>>> +#define LIS331DLH_REG_INT_X_HIGH			0x02
>>> +#define LIS331DLH_REG_INT_X_LOW				0x01
>>> +
>>> +
>>> +/* Default control settings */
>>> +#define LIS331DLH_DEFAULT_CTRL1  (LIS331DLH_REG_CTRL_1_POWER_ON	     \
>>> +				| LIS331DLH_REG_CTRL_1_DR_50         \
>>> +				| LIS331DLH_REG_CTRL_1_AXES_Z_ENABLE \
>>> +				| LIS331DLH_REG_CTRL_1_AXES_Y_ENABLE \
>>> +				| LIS331DLH_REG_CTRL_1_AXES_X_ENABLE)
>>> +
>>> +#define LIS331DLH_DEFAULT_CTRL2	 LIS331DLH_REG_CTRL_2_HP_NORM
>>> +
>>> +#define LIS331DLH_DEFAULT_CTRL3  LIS331DLH_REG_CTRL_3_I1_DRDY
>>> +
>>> +#ifdef __BIG_ENDIAN
>>> +#define LIS331DLH_REG_CTRL_4_CPU_ENDIAN LIS331DLH_REG_CTRL_4_BIG_ENDIAN
>>> +#else
>>> +#define LIS331DLH_REG_CTRL_4_CPU_ENDIAN 0
>>> +#endif
>>> +
>>> +#define LIS331DLH_DEFAULT_CTRL4  (LIS331DLH_REG_CTRL_4_BLOCK_UPDATE \
>>> +				| LIS331DLH_REG_CTRL_4_CPU_ENDIAN \
>>> +				| LIS331DLH_REG_CTRL_4_FS_2G)
>>> +
>>> +#define LIS331DLH_MAX_TX 12
>>> +#define LIS331DLH_MAX_RX 12
>>> +
>>> +static const u8 read_all_tx_array[] = {
>>> +	LIS331DLH_READ_REG(LIS331DLH_REG_OUT_X_L_ADDR), 0,
>>> +	LIS331DLH_READ_REG(LIS331DLH_REG_OUT_X_H_ADDR), 0,
>>> +	LIS331DLH_READ_REG(LIS331DLH_REG_OUT_Y_L_ADDR), 0,
>>> +	LIS331DLH_READ_REG(LIS331DLH_REG_OUT_Y_H_ADDR), 0,
>>> +	LIS331DLH_READ_REG(LIS331DLH_REG_OUT_Z_L_ADDR), 0,
>>> +	LIS331DLH_READ_REG(LIS331DLH_REG_OUT_Z_H_ADDR), 0,
>>> +};
>>> +
>>> +/**
>>> + * struct lis331dlh_state - device instance specific data
>>> + * @us:			pointer to actual bus (spi/i2c)
>>> + * @buf_lock:		mutex to protect tx and rx
>>> + * @tx:			transmit buffer
>>> + * @rx:			receive buffer (16bit aligned)
>>> + * @read/write		bus specific functions to access registers
>>> + **/
>>> +struct lis331dlh_state {
>>> +	void			*us;
>>> +	struct mutex		buf_lock;
>>> +
>>> +	u8	tx[LIS331DLH_MAX_TX] ____cacheline_aligned;
>>> +	u16	rx[LIS331DLH_MAX_RX/2] ____cacheline_aligned;
>>> +
>>
>> Break this out into a const ops structure so that the two implementations
>> can assign it as one pointer.
> 
> Sounds good.
> 
>>
>>> +	int (*read_reg_8)  (struct lis331dlh_state *st, u8 addr, u8 *val);
>>> +	int (*write_reg_8) (struct lis331dlh_state *st, u8 addr, u8 val);
>>> +	int (*read_reg_16) (struct lis331dlh_state *st, u8 low_addr, u16 
> *val);
>>> +	int (*read_all) (struct lis331dlh_state *st, u8 *rx_array);
>>> +};
>>> +
>>> +int lis331dlh_probe(struct iio_dev *indio_dev, struct device *dev);
>>> +int lis331dlh_remove(struct iio_dev *indio_dev);
>>> +
>>> +int lis331dlh_stop_device(struct iio_dev *indio_dev);
>>> +
>>> +enum LIS331DLH_CHANNELS {
>>> +	LIS331DLH_ACCEL_X,
>>> +	LIS331DLH_ACCEL_Y,
>>> +	LIS331DLH_ACCEL_Z,
>>> +	LIS331DLH_TIMESTAMP,
>>> +};
>>> +
>>> +#endif /* SPI_LIS331DLH_H_ */
>>> diff --git a/drivers/staging/iio/accel/lis331dlh_core.c
>>> b/drivers/staging/iio/accel/lis331dlh_core.c new file mode 100644
>>> index 0000000..0d06f20
>>> --- /dev/null
>>> +++ b/drivers/staging/iio/accel/lis331dlh_core.c
>>> @@ -0,0 +1,406 @@
>>> +/*
>>> + * lis331dlh_core.c	support STMicroelectronics LIS331DLH
>>> + *			3d 2g/4g/8g Linear Accelerometers
>>> + *
>>> + * Copyright (c) 2007 Jonathan Cameron <jic23@xxxxxxxxx>
>>> + * Copyright (c) 2011 Manuel Stahl <manuel.stahl@xxxxxxxxxxxxxxxxx>
>>> + *
>>> + * 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.
>>> + */
>>> +
>>> +#include <linux/mutex.h>
>>> +#include <linux/device.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/sysfs.h>
>>> +#include <linux/module.h>
>>> +
>>> +#include "../iio.h"
>>> +#include "../sysfs.h"
>>> +#include "../events.h"
>>> +
>>> +#include "lis331dlh.h"
>>> +
>>> +static int lis331dlh_read_raw(struct iio_dev *indio_dev,
>>> +			      struct iio_chan_spec const *chan,
>>> +			      int *val,
>>> +			      int *val2,
>>> +			      long mask)
>>> +{
>>> +	struct lis331dlh_state *st = iio_priv(indio_dev);
>>> +	s16 value;
>>> +	u8 utemp;
>>> +	ssize_t ret = 0;
>>> +	u8 reg;
>>> +
>>> +	switch (mask) {
>>> +	case 0:
>>> +		/* Take the iio_dev status lock */
>>> +		mutex_lock(&indio_dev->mlock);
>>> +		ret = st->read_reg_16(st, chan->address, (u16 *)&value);
>>> +		*val = value;
>>> +		mutex_unlock(&indio_dev->mlock);
>>> +		return ret ? ret : IIO_VAL_INT;
>>> +	case IIO_CHAN_INFO_SCALE:
>>> +		ret = st->read_reg_8(st, LIS331DLH_REG_CTRL_4_ADDR, &utemp);
>>> +		if (ret)
>>> +			return ret;
>>> +
>>> +		utemp &= LIS331DLH_REG_CTRL_4_FS_MASK;
>>> +		switch (utemp) {
>>> +		case LIS331DLH_REG_CTRL_4_FS_2G:
>>> +			*val2 = 569;
>>> +			break;
>>> +		case LIS331DLH_REG_CTRL_4_FS_4G:
>>> +			*val2 = 1135;
>>> +			break;
>>> +		case LIS331DLH_REG_CTRL_4_FS_8G:
>>> +			*val2 = 2270;
>>> +			break;
>>> +		default:
>>> +			*val2 = 0;
>>> +			break;
>>> +		}
>>> +		*val = 0;
>>> +		return IIO_VAL_INT_PLUS_MICRO;
>>> +	}
>>> +	return -1;
>>> +}
>>> +
>>> +static ssize_t lis331dlh_read_frequency(struct device *dev,
>>> +					struct device_attribute *attr,
>>> +					char *buf)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	struct lis331dlh_state *st = iio_priv(indio_dev);
>>> +	int ret, len = 0;
>>> +	u8 val;
>>> +
>>> +	ret = st->read_reg_8(st, LIS331DLH_REG_CTRL_1_ADDR, &val);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	val &= LIS331DLH_REG_CTRL_1_DR_MASK;
>>> +	switch (val) {
>>> +	case LIS331DLH_REG_CTRL_1_DR_50:
>>> +		len = sprintf(buf, "50\n");
>>> +		break;
>>> +	case LIS331DLH_REG_CTRL_1_DR_100:
>>> +		len = sprintf(buf, "100\n");
>>> +		break;
>>> +	case LIS331DLH_REG_CTRL_1_DR_400:
>>> +		len = sprintf(buf, "400\n");
>>> +		break;
>>> +	case LIS331DLH_REG_CTRL_1_DR_1000:
>>> +		len = sprintf(buf, "1000\n");
>>> +		break;
>>> +	}
>>> +	return len;
>>> +}
>>> +
>>> +static ssize_t lis331dlh_write_frequency(struct device *dev,
>>> +					 struct device_attribute *attr,
>>> +					 const char *buf,
>>> +					 size_t len)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	struct lis331dlh_state *st = iio_priv(indio_dev);
>>> +	unsigned long freq;
>>> +	int ret;
>>> +	u8 val;
>>> +
>>> +	ret = strict_strtoul(buf, 10, &freq);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	mutex_lock(&indio_dev->mlock);
>>> +	ret = st->read_reg_8(st, LIS331DLH_REG_CTRL_1_ADDR, &val);
>>> +	if (ret)
>>> +		goto error_ret_mutex;
>>> +
>>> +	val &= ~LIS331DLH_REG_CTRL_1_DR_MASK;
>>> +	switch (freq) {
>>> +	case 50:
>>> +		val |= LIS331DLH_REG_CTRL_1_DR_50;
>>> +		break;
>>> +	case 100:
>>> +		val |= LIS331DLH_REG_CTRL_1_DR_100;
>>> +		break;
>>> +	case 400:
>>> +		val |= LIS331DLH_REG_CTRL_1_DR_400;
>>> +		break;
>>> +	case 1000:
>>> +		val |= LIS331DLH_REG_CTRL_1_DR_1000;
>>> +		break;
>>> +	default:
>>> +		ret = -EINVAL;
>>> +		goto error_ret_mutex;
>>> +	};
>>> +
>>> +	ret = st->write_reg_8(st, LIS331DLH_REG_CTRL_1_ADDR, val);
>>> +
>>> +error_ret_mutex:
>>> +	mutex_unlock(&indio_dev->mlock);
>>> +
>>> +	return ret ? ret : len;
>>> +}
>>> +
>>> +static ssize_t lis331dlh_read_range(struct device *dev,
>>> +				    struct device_attribute *attr,
>>> +				    char *buf)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	struct lis331dlh_state *st = iio_priv(indio_dev);
>>> +	int ret, len = 0;
>>> +	u8 val;
>>> +
>>> +	ret = st->read_reg_8(st, LIS331DLH_REG_CTRL_4_ADDR, &val);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	val &= LIS331DLH_REG_CTRL_4_FS_MASK;
>>> +	switch (val) {
>>> +	case LIS331DLH_REG_CTRL_4_FS_2G:
>>> +		len = sprintf(buf, "2G\n");
>>
>> We have accelerometers units as m/s^2 not G, so please adjust these
>> to match and verify the rest are correct as well.
> 
> Reported values are correct. This is kind of a special sysfs entry, where we 
> can configure the limits of the device. The possible values here would look 
> somehow strange if expressed in m/s^2. But in general I agree, we should have 
> consistent units whereever possible.
Do it anyway - if userspace apps want to convert this back to G then that's
up to them!
> 
>>
>>> +		break;
>>> +	case LIS331DLH_REG_CTRL_4_FS_4G:
>>> +		len = sprintf(buf, "4G\n");
>>> +		break;
>>> +	case LIS331DLH_REG_CTRL_4_FS_8G:
>>> +		len = sprintf(buf, "8G\n");
>>> +		break;
>>> +	}
>>> +	return len;
>>> +}
>>> +
>>> +static ssize_t lis331dlh_write_range(struct device *dev,
>>> +				     struct device_attribute *attr,
>>> +				     const char *buf,
>>> +				     size_t len)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	struct lis331dlh_state *st = iio_priv(indio_dev);
>>> +	unsigned long freq;
>>> +	int ret;
>>> +	u8 val;
>>> +
>>> +	ret = strict_strtoul(buf, 10, &freq);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	mutex_lock(&indio_dev->mlock);
>>> +	ret = st->read_reg_8(st, LIS331DLH_REG_CTRL_4_ADDR, &val);
>>> +	if (ret)
>>> +		goto error_ret_mutex;
>>> +
>>> +	val &= ~LIS331DLH_REG_CTRL_4_FS_MASK;
>>> +	switch (freq) {
>>> +	case 2:
>>> +		val |= LIS331DLH_REG_CTRL_4_FS_2G;
>>> +		break;
>>> +	case 4:
>>> +		val |= LIS331DLH_REG_CTRL_4_FS_4G;
>>> +		break;
>>> +	case 8:
>>> +		val |= LIS331DLH_REG_CTRL_4_FS_8G;
>>> +		break;
>>> +	default:
>>> +		ret = -EINVAL;
>>> +		goto error_ret_mutex;
>>> +	};
>>> +
>>> +	ret = st->write_reg_8(st, LIS331DLH_REG_CTRL_4_ADDR, val);
>>> +
>>> +error_ret_mutex:
>>> +	mutex_unlock(&indio_dev->mlock);
>>> +
>>> +	return ret ? ret : len;
>>> +}
>>> +
>>> +static int lis331dlh_init(struct iio_dev *indio_dev)
>>> +{
>>> +	struct lis331dlh_state *st = iio_priv(indio_dev);
>>> +	int ret;
>>> +	u8 val;
>>> +
>>> +	ret = st->read_reg_8(st, LIS331DLH_REG_WHO_AM_I_ADDR, &val);
>>> +	if (ret || (val != LIS331DLH_REG_WHO_AM_I_DEFAULT)) {
>>> +		dev_err(indio_dev->dev.parent,
>>> +			"reading WHO_AM_I register failed: 0x%02X", val);
>>> +		goto err_ret;
>>> +	}
>>> +
>>> +	/* Write suitable defaults to ctrl1 */
>>> +	ret = st->write_reg_8(st, LIS331DLH_REG_CTRL_1_ADDR,
>>> +				  LIS331DLH_DEFAULT_CTRL1);
>>> +	if (ret) {
>>> +		dev_err(indio_dev->dev.parent,
>>> +				"problem with setup control register 1");
>>> +		goto err_ret;
>>> +	}
>>> +
>>> +	/* Write suitable defaults to ctrl2 */
>>> +	ret = st->write_reg_8(st, LIS331DLH_REG_CTRL_2_ADDR,
>>> +				  LIS331DLH_DEFAULT_CTRL2);
>>> +	if (ret) {
>>> +		dev_err(indio_dev->dev.parent,
>>> +				"problem with setup control register 2");
>>> +		goto err_ret;
>>> +	}
>>> +
>>> +	/* Write suitable defaults to ctrl3 */
>>> +	ret = st->write_reg_8(st, LIS331DLH_REG_CTRL_3_ADDR,
>>> +				  LIS331DLH_DEFAULT_CTRL3);
>>> +	if (ret) {
>>> +		dev_err(indio_dev->dev.parent,
>>> +				"problem with setup control register 3");
>>> +		goto err_ret;
>>> +	}
>>> +
>>> +	/* Write suitable defaults to ctrl4 */
>>> +	ret = st->write_reg_8(st, LIS331DLH_REG_CTRL_4_ADDR,
>>> +				  LIS331DLH_DEFAULT_CTRL4);
>>> +	if (ret) {
>>> +		dev_err(indio_dev->dev.parent,
>>> +				"problem with setup control register 4");
>>> +		goto err_ret;
>>> +	}
>>> +
>>> +	/* Read int1 src reg to clear pending irqs */
>>> +	ret = st->read_reg_8(st, LIS331DLH_REG_INT1_SRC_ADDR, &val);
>>> +	if (ret) {
>>> +		dev_err(indio_dev->dev.parent,
>>> +				"problem with int1 src register");
>>> +		goto err_ret;
>>> +	}
>>> +
>>> +	/* Read int2 src reg to clear pending irqs */
>>> +	ret = st->read_reg_8(st, LIS331DLH_REG_INT2_SRC_ADDR, &val);
>>> +	if (ret) {
>>> +		dev_err(indio_dev->dev.parent,
>>> +				"problem with int2 src register");
>>> +		goto err_ret;
>>> +	}
>>> +
>>> +err_ret:
>>> +	return ret;
>>> +}
>>> +
>>> +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
>>> +			      lis331dlh_read_frequency,
>>> +			      lis331dlh_write_frequency);
>>> +
>>> +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("50 100 400 1000");
>>> +
>>
>> Not abi compliant. Should be in_accel_range and for that matter
>> we should add range to the iio_chan_info_enum as it seems to be
>> way more popular than I'd like...
> 
> _min and _max would be the alternative, but then it's rather hard to have 
> proper semantics for configuring these. Furthermore it's not really the range. 
> The device can report greater values than 2G if configured in that mode but 
> the accuracy will drop. 
Nasty! 
> That's why I tend to keep it as a special attribute. 
> Range or min/max should express hard limits.
I'd just map this on to the in_accel_range  (and in_accel_range_available)
and ignore the fact it can report outside the specified range.
> 
>>
>>> +static IIO_DEVICE_ATTR(range, S_IWUSR | S_IRUGO,
>>> +		       lis331dlh_read_range,
>>> +		       lis331dlh_write_range, 0);
>>> +
>>> +#define LIS331DLH_EVENT_MASK					\
>>> +	(IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) |	\
>>> +	 IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING))
>>> +
>>> +#define LIS331DLH_ACCEL_CHAN_SPEC(_mod, _si, _addr)		\
>>> +	{ .type = IIO_ACCEL,					\
>>> +	  .modified = 1,					\
>>> +	  .channel2 = _mod,					\
>>> +	  .address = _addr,					\
>>> +	  .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT,		\
>>> +	  .scan_index = _si,					\
>>> +	  .scan_type = IIO_ST('s', 16, 16, 0),			\
>>> +	  .event_mask = LIS331DLH_EVENT_MASK,			\
>>> +	}
>>> +
>>> +static struct iio_chan_spec lis331dlh_channels[] = {
>>> +	LIS331DLH_ACCEL_CHAN_SPEC(IIO_MOD_X, LIS331DLH_ACCEL_X,
>>> +				  LIS331DLH_REG_OUT_X_L_ADDR),
>>> +	LIS331DLH_ACCEL_CHAN_SPEC(IIO_MOD_Y, LIS331DLH_ACCEL_Y,
>>> +				  LIS331DLH_REG_OUT_Y_L_ADDR),
>>> +	LIS331DLH_ACCEL_CHAN_SPEC(IIO_MOD_Z, LIS331DLH_ACCEL_Z,
>>> +				  LIS331DLH_REG_OUT_Z_L_ADDR),
>>> +	IIO_CHAN_SOFT_TIMESTAMP(LIS331DLH_TIMESTAMP)
>>> +};
>>> +
>>> +static struct attribute *lis331dlh_attributes[] = {
>>> +	&iio_dev_attr_sampling_frequency.dev_attr.attr,
>>> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
>>> +	&iio_dev_attr_range.dev_attr.attr,
>>> +	NULL
>>> +};
>>> +
>>> +static const struct attribute_group lis331dlh_attribute_group = {
>>> +	.attrs = lis331dlh_attributes,
>>> +};
>>> +
>>> +static const struct iio_info lis331dlh_info = {
>>> +	.read_raw = &lis331dlh_read_raw,
>>> +	.driver_module = THIS_MODULE,
>>> +	.attrs = &lis331dlh_attribute_group,
>>> +};
>>> +
>>> +int lis331dlh_probe(struct iio_dev *indio_dev, struct device *dev)
>>> +{
>>> +	struct lis331dlh_state *st = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	mutex_init(&st->buf_lock);
>>> +
>>> +	indio_dev->name = LIS331DLH;
>>> +	indio_dev->dev.parent = dev;
>>> +	indio_dev->info = &lis331dlh_info;
>>> +	indio_dev->channels = lis331dlh_channels;
>>> +	indio_dev->num_channels = ARRAY_SIZE(lis331dlh_channels);
>>> +	indio_dev->modes = INDIO_DIRECT_MODE;
>>> +
>>> +	/* Get the device into a sane initial state */
>>> +	ret = lis331dlh_init(indio_dev);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	ret = iio_device_register(indio_dev);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(lis331dlh_probe);
>>> +
>>> +/* Power down the device */
>>> +int lis331dlh_power_down(struct iio_dev *indio_dev)
>>> +{
>>> +	struct lis331dlh_state *st = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	mutex_lock(&indio_dev->mlock);
>>> +	ret = st->write_reg_8(st, LIS331DLH_REG_CTRL_1_ADDR,
>>> +				  LIS331DLH_REG_CTRL_1_POWER_OFF);
>>> +	if (ret) {
>>> +		dev_err(&indio_dev->dev, "problem with turning device off");
>>> +		goto err_ret;
>>> +	}
>>> +
>>> +err_ret:
>>> +	mutex_unlock(&indio_dev->mlock);
>>> +	return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(lis331dlh_power_down);
>>> +
>>> +int lis331dlh_remove(struct iio_dev *indio_dev)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = lis331dlh_power_down(indio_dev);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	iio_device_unregister(indio_dev);
>>> +	iio_free_device(indio_dev);
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(lis331dlh_remove);
>>> +
>>> +MODULE_AUTHOR("Manuel Stahl <manuel.stahl@xxxxxxxxxxxxxxxxx>");
>>> +MODULE_DESCRIPTION("ST LIS331DLH Accelerometer driver");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/drivers/staging/iio/accel/lis331dlh_i2c.c
>>> b/drivers/staging/iio/accel/lis331dlh_i2c.c new file mode 100644
>>> index 0000000..35c9c98
>>> --- /dev/null
>>> +++ b/drivers/staging/iio/accel/lis331dlh_i2c.c
>>> @@ -0,0 +1,176 @@
>>> +/*
>>> + * lis331dlh_i2c.c	support STMicroelectronics LISD02DQ
>>> + *			3d 2g/4g/8g Linear Accelerometers
>>> + *
>>> + * Copyright (c) 2007 Jonathan Cameron <jic23@xxxxxxxxx>
>>> + * Copyright (c) 2011 Manuel Stahl <manuel.stahl@xxxxxxxxxxxxxxxxx>
>>> + *
>>> + * 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.
>>> + */
>>> +
>>> +#include <linux/i2c.h>
>>> +
>>> +#include "../iio.h"
>>> +
>>> +#include "lis331dlh.h"
>>> +
>>> +static int lis331dlh_i2c_read_reg_8(struct lis331dlh_state *st,
>>> +				    u8 reg_address, u8 *val)
>>> +{
>>> +	struct i2c_client *client = st->us;
>>> +	int ret;
>>> +
>>> +	struct i2c_msg msg[] = { {
>>> +		.addr = client->addr,
>>> +		.flags = client->flags & I2C_M_TEN,
>>> +		.len = 1,
>>> +		.buf = &reg_address,
>>> +	}, {
>>> +		.addr = client->addr,
>>> +		.flags = (client->flags & I2C_M_TEN) | I2C_M_RD,
>>> +		.len = 1,
>>> +		.buf = val,
>>> +	} };
>>> +
>>> +	reg_address = LIS331DLH_WRITE_REG(reg_address);
>>> +	ret = i2c_transfer(client->adapter, msg, 2);
>>> +	return (ret == 2) ? 0 : ret;
>>> +}
>>> +
>>> +static int lis331dlh_i2c_write_reg_8(struct lis331dlh_state *st,
>>> +				     u8 reg_address, u8 val)
>>> +{
>>> +	struct i2c_client *client = st->us;
>>> +	u8 buf[2];
>>> +	int ret;
>>> +
>>> +	buf[0] = reg_address;
>>> +	buf[1] = val;
>>
>> use u8 buf[2] = { reg_address, val };
>> to keep this compact. Also, I'd make only negative values errors then
>> you can avoid the messing around with the return value and shorten
>> this somewhat.  Also, thinking about it, this looks like an smbus
>> command, so even better to use the relevant one of those if possible?
>> (I guess they might not play with the 10 bit address). Is this really
>> a device that uses that?
> 
> Yes smbus API is an option here.
> 
>>
>>> +
>>> +	ret = i2c_master_send(client, buf, sizeof(buf));
>>> +	return (ret == sizeof(buf)) ? 0 : ret;
>>> +}
>>> +
>>> +static int lis331dlh_i2c_read_reg_16(struct lis331dlh_state *st,
>>> +				     u8 lower_reg_address, u16 *val)
>>> +{
>>> +	struct i2c_client *client = st->us;
>>> +	int ret;
>>> +
>>> +	struct i2c_msg msg[] = { {
>>> +		.addr = client->addr,
>>> +		.flags = client->flags & I2C_M_TEN,
>>> +		.len = 1,
>>> +		.buf = &lower_reg_address,
>>> +	}, {
>>> +		.addr = client->addr,
>>> +		.flags = (client->flags & I2C_M_TEN) | I2C_M_RD,
>>> +		.len = 2,
>>> +		.buf = (u8 *)val,
>>> +	} };
>>> +
>>> +	lower_reg_address = LIS331DLH_I2C_AUTO_INC(lower_reg_address);
>>> +	ret = i2c_transfer(client->adapter, msg, 2);
>>> +	le16_to_cpus(val);
>>> +	return (ret == 2) ? 0 : ret;
>>> +}
>>> +
>>> +static int lis331dlh_i2c_read_all(struct lis331dlh_state *st, u8
>>> *rx_array) +{
>>> +	struct i2c_client *client = st->us;
>>> +	int ret;
>>> +	u8 reg = LIS331DLH_I2C_AUTO_INC(LIS331DLH_REG_OUT_X_L_ADDR);
>>> +
>>> +	struct i2c_msg msg[] = { {
>>> +		.addr = client->addr,
>>> +		.flags = client->flags & I2C_M_TEN,
>>> +		.len = 1,
>>> +		.buf = &reg,
>>> +	}, {
>>> +		.addr = client->addr,
>>> +		.flags = (client->flags & I2C_M_TEN) | I2C_M_RD,
>>> +		.len = 6,
>>> +		.buf = rx_array,
>>> +	} };
>>> +
>>> +	ret = i2c_transfer(client->adapter, msg, 2);
>>> +	if (ret != 2)
>>> +		return ret;
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int lis331dlh_i2c_probe(struct i2c_client *i2c,
>>> +			       const struct i2c_device_id *id)
>>> +{
>>> +	int ret;
>>> +	struct lis331dlh_state *st;
>>> +	struct iio_dev *indio_dev;
>>> +
>>> +	indio_dev = iio_allocate_device(sizeof *st);
>>> +	if (!indio_dev)
>>> +		return -ENOMEM;
>>> +
>>> +	st = iio_priv(indio_dev);
>>> +	st->read_reg_8  = lis331dlh_i2c_read_reg_8;
>>> +	st->write_reg_8 = lis331dlh_i2c_write_reg_8;
>>> +	st->read_reg_16 = lis331dlh_i2c_read_reg_16;
>>> +	st->read_all	= lis331dlh_i2c_read_all;
>>> +
>>> +	st->us = i2c;
>>> +
>>> +	/* this is only used for removal purposes */
>>> +	i2c_set_clientdata(i2c, indio_dev);
>>> +
>>> +	ret = lis331dlh_probe(indio_dev, &i2c->dev);
>>> +	if (ret)
>>> +		goto error_free_dev;
>>> +
>>> +	return 0;
>>> +
>>> +error_free_dev:
>>> +	iio_free_device(indio_dev);
>>> +	return ret;
>>> +}
>>> +
>>> +static int lis331dlh_i2c_remove(struct i2c_client *i2c)
>>> +{
>>> +	struct iio_dev *indio_dev = i2c_get_clientdata(i2c);
>>> +	return lis331dlh_remove(indio_dev);
>>> +}
>>> +
>>> +static const struct i2c_device_id lis331dlh_id[] = {
>>> +	{"lis331dlh", 0x19 },
>>> +	{}
>>> +};
>>> +
>>> +MODULE_DEVICE_TABLE(i2c, lis331dlh_id);
>>> +
>>> +static struct i2c_driver lis331dlh_i2c_driver = {
>>> +	.driver	 = {
>>> +		.name   = "lis331dlh_i2c",
>>> +		.owner  = THIS_MODULE,
>>> +	},
>>> +	.probe	= lis331dlh_i2c_probe,
>>> +	.remove	= __devexit_p(lis331dlh_i2c_remove),
>>> +	.id_table = lis331dlh_id,
>>> +};
>>> +
>>> +static int __init lis331dlh_i2c_module_init(void)
>>> +{
>>> +	return i2c_add_driver(&lis331dlh_i2c_driver);
>>> +}
>>> +
>>> +static void __exit lis331dlh_i2c_module_exit(void)
>>> +{
>>> +	i2c_del_driver(&lis331dlh_i2c_driver);
>>> +}
>>> +
>>> +MODULE_AUTHOR("Manuel Stahl <manuel.stahl@xxxxxxxxxxxxxxxxx>");
>>> +MODULE_DESCRIPTION("ST LIS331DLH Accelerometer I2C driver");
>>> +MODULE_LICENSE("GPL v2");
>>> +
>>> +module_init(lis331dlh_i2c_module_init);
>>> +module_exit(lis331dlh_i2c_module_exit);
>>> diff --git a/drivers/staging/iio/accel/lis331dlh_spi.c
>>> b/drivers/staging/iio/accel/lis331dlh_spi.c new file mode 100644
>>> index 0000000..0aee730
>>> --- /dev/null
>>> +++ b/drivers/staging/iio/accel/lis331dlh_spi.c
>>> @@ -0,0 +1,231 @@
>>> +/*
>>> + * lis331dlh_spi.c	support STMicroelectronics LISD02DQ
>>> + *			3d 2g/4g/8g Linear Accelerometers via SPI
>>> + *
>>> + * Copyright (c) 2007 Jonathan Cameron <jic23@xxxxxxxxx>
>>> + * Copyright (c) 2011 Manuel Stahl <manuel.stahl@xxxxxxxxxxxxxxxxx>
>>> + *
>>> + * 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.
>>> + */
>>> +
>>> +#include <linux/spi/spi.h>
>>> +
>>> +#include "../iio.h"
>>> +
>>> +#include "lis331dlh.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.
>>> + */
>>> +
>>> +/**
>>> + * lis331dlh_spi_read_reg_8() - read single byte from a single register
>>> + * @dev: device asosciated with child of actual device (iio_dev or
>>> iio_trig) + * @reg_address: the address of the register to be read
>>> + * @val: pass back the resulting value
>>> + **/
>>> +static int lis331dlh_spi_read_reg_8(struct lis331dlh_state *st,
>>> +				   u8 reg_address, u8 *val)
>>> +{
>>> +	struct spi_device *spi = st->us;
>>> +	struct spi_message msg;
>>> +	int ret;
>>> +
>>> +	struct spi_transfer xfer = {
>>> +		.tx_buf = st->tx,
>>> +		.rx_buf = st->rx,
>>> +		.bits_per_word = 8,
>>> +		.len = 2,
>>> +		.cs_change = 1,
>>
>> Really, hold cs high between reads?  Surely doesn't do any
>> harm to not do so?
> 
> Ah yes, it's not needed.
> 
>>
>>> +	};
>>> +
>>> +	mutex_lock(&st->buf_lock);
>>> +	st->tx[0] = LIS331DLH_READ_REG(reg_address);
>>> +	st->tx[1] = 0;
>>> +
>>> +	spi_message_init(&msg);
>>> +	spi_message_add_tail(&xfer, &msg);
>>> +	ret = spi_sync(spi, &msg);
>>> +	*val = st->rx[1];
>>> +	mutex_unlock(&st->buf_lock);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/**
>>> + * lis331dlh_spi_write_reg_8() - write single byte to a register
>>> + * @dev: device associated with child of actual device (iio_dev or
>>> iio_trig) + * @reg_address: the address of the register to be writen
>>> + * @val: the value to write
>>> + **/
>>> +static int lis331dlh_spi_write_reg_8(struct lis331dlh_state *st,
>>> +				     u8 reg_address, u8 val)
>>> +{
>>> +	int ret;
>>> +	struct spi_message msg;
>>> +	struct spi_device *spi = st->us;
>>> +	struct spi_transfer xfer = {
>>> +		.tx_buf = st->tx,
>>> +		.bits_per_word = 8,
>>> +		.len = 2,
>>> +		.cs_change = 1,
>>> +	};
>>> +
>>> +	mutex_lock(&st->buf_lock);
>>> +	st->tx[0] = LIS331DLH_WRITE_REG(reg_address);
>>> +	st->tx[1] = val;
>>> +
>>> +	spi_message_init(&msg);
>>> +	spi_message_add_tail(&xfer, &msg);
>>> +	ret = spi_sync(spi, &msg);
>>> +	mutex_unlock(&st->buf_lock);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/**
>>> + * lisl302dq_spi_read_reg_16() - read 2 bytes from a pair of registers
>>> + * @dev: device associated with child of actual device (iio_dev or
>>> iio_trig) + * @reg_address: the address of the lower of the two
>>> registers. Second register + *               is assumed to have address
>>> one greater.
>>> + * @val: somewhere to pass back the value read
>>> + **/
>>> +static int lis331dlh_spi_read_reg_16(struct lis331dlh_state *st,
>>> +				     u8 lower_reg_address,
>>> +				     u16 *val)
>>> +{
>>> +	struct spi_message msg;
>>> +	struct spi_device *spi = st->us;
>>> +	int ret;
>>> +	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,
>>> +			.cs_change = 1,
>>> +
>>
>> weird white space.
> Oh, and now that I look over it, the device has multiple byte read/write 
> capability...
> 
>>
>>> +		},
>>> +	};
>>> +
>>> +	mutex_lock(&st->buf_lock);
>>> +	st->tx[0] = LIS331DLH_READ_REG(lower_reg_address);
>>> +	st->tx[1] = 0;
>>> +	st->tx[2] = LIS331DLH_READ_REG(lower_reg_address+1);
>>> +	st->tx[3] = 0;
>>> +
>>> +	spi_message_init(&msg);
>>> +	spi_message_add_tail(&xfers[0], &msg);
>>> +	spi_message_add_tail(&xfers[1], &msg);
>>> +	ret = spi_sync(spi, &msg);
>>> +	if (ret) {
>>> +		dev_err(&spi->dev, "problem when reading 16 bit register");
>>> +		goto error_ret;
>>> +	}
>>> +	*val = (u16)(st->rx[1]) | ((u16)(st->rx[3]) << 8);
>>> +
>>> +error_ret:
>>> +	mutex_unlock(&st->buf_lock);
>>> +	return ret;
>>> +}
>>> +
>>> +static int lis331dlh_spi_read_all(struct lis331dlh_state *st, u8
>>> *rx_array) +{
>>> +	struct spi_transfer xfers[2];
>>> +	struct spi_message msg;
>>> +	struct spi_device *spi = st->us;
>>> +
>>> +	st->tx[0] = read_all_tx_array[0];
>>> +	xfers[0].tx_buf = st->tx;
>>> +	xfers[0].rx_buf = NULL;
>>> +	xfers[0].bits_per_word = 8;
>>> +	xfers[0].len = 1;
>>> +	xfers[0].cs_change = 1;
>>> +
>>> +	xfers[1].tx_buf = NULL;
>>> +	xfers[1].rx_buf = rx_array;
>>> +	xfers[1].bits_per_word = 8;
>>> +	xfers[1].len = LIS331DLH_SCAN_ELEMENTS * 2;
>>> +	xfers[0].cs_change = 1;
>>> +
>>> +	spi_message_init(&msg);
>>> +	spi_message_add_tail(&xfers[0], &msg);
>>> +	spi_message_add_tail(&xfers[1], &msg);
>>> +
>>> +	return spi_sync(spi, &msg);
>>> +}
>>> +
>>> +static int __devinit lis331dlh_spi_probe(struct spi_device *spi)
>>> +{
>>> +	int ret;
>>> +	struct lis331dlh_state *st;
>>> +	struct iio_dev *indio_dev;
>>> +
>>> +	indio_dev = iio_allocate_device(sizeof *st);
>>> +	if (!indio_dev)
>>> +		return -ENOMEM;
>>> +
>>> +	st = iio_priv(indio_dev);
>>> +	st->read_reg_8  = lis331dlh_spi_read_reg_8;
>>> +	st->write_reg_8 = lis331dlh_spi_write_reg_8;
>>> +	st->read_reg_16 = lis331dlh_spi_read_reg_16;
>>> +	st->read_all	= lis331dlh_spi_read_all;
>>> +
>>> +	st->us = spi;
>>> +
>>> +	spi->mode = SPI_MODE_3;
>>> +	spi_setup(spi);
>>> +
>>> +	/* this is only used tor removal purposes */
>>> +	spi_set_drvdata(spi, indio_dev);
>>> +
>>> +	ret = lis331dlh_probe(indio_dev, &spi->dev);
>>> +	if (ret)
>>> +		goto error_free_st;
>>> +
>>> +	return 0;
>>> +
>>> +error_free_st:
>>> +	kfree(st);
>>> +	return ret;
>>> +}
>>> +
>>> +static int lis331dlh_spi_remove(struct spi_device *spi)
>>> +{
>>> +	struct iio_dev *indio_dev = spi_get_drvdata(spi);
>>> +	return lis331dlh_remove(indio_dev);
>>> +}
>>> +
>>> +static struct spi_driver lis331dlh_spi_driver = {
>>> +	.driver = {
>>> +		.name = "lis331dlh_spi",
>>> +		.owner = THIS_MODULE,
>>> +	},
>>> +	.probe = lis331dlh_spi_probe,
>>> +	.remove = __devexit_p(lis331dlh_spi_remove),
>>> +};
>>> +
>>> +static __init int lis331dlh_spi_module_init(void)
>>> +{
>>> +	return spi_register_driver(&lis331dlh_spi_driver);
>>> +}
>>> +
>>> +static __exit void lis331dlh_spi_module_exit(void)
>>> +{
>>> +	spi_unregister_driver(&lis331dlh_spi_driver);
>>> +}
>>> +
>>> +MODULE_AUTHOR("Manuel Stahl <manuel.stahl@xxxxxxxxxxxxxxxxx>");
>>> +MODULE_DESCRIPTION("ST LIS331DLH Accelerometer SPI driver");
>>> +MODULE_LICENSE("GPL v2");
>>> +
>>> +module_init(lis331dlh_spi_module_init);
>>> +module_exit(lis331dlh_spi_module_exit);
> 
> Thanks for the review. A new version will follow.
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux