Re: [PATCH 1/4] iio:common: Add STMicroelectronics common library

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

 



On 12/13/2012 01:11 PM, Denis CIOCCA wrote:
> This patch add generic library for STMicroelectronics 3-axis sensors.

Nice piece of work.  Couple of little bits and bobs, but basically there
as far as I am concerned...
(just the spi buffer alignment stuff and it really is just a case of
making sure they are the last element in the chunk of memory created
as mallocs are always a whole number of cachelines.)

> ---
>  drivers/iio/common/Kconfig                         |    1 +
>  drivers/iio/common/Makefile                        |    1 +
>  drivers/iio/common/st_sensors/Kconfig              |   30 ++
>  drivers/iio/common/st_sensors/Makefile             |    9 +
>  drivers/iio/common/st_sensors/st_sensors_buffer.c  |  115 +++++++
>  drivers/iio/common/st_sensors/st_sensors_core.c    |  335 ++++++++++++++++++++
>  drivers/iio/common/st_sensors/st_sensors_i2c.c     |   78 +++++
>  drivers/iio/common/st_sensors/st_sensors_spi.c     |  124 +++++++
>  drivers/iio/common/st_sensors/st_sensors_trigger.c |   76 +++++
>  include/linux/iio/common/st_sensors.h              |  246 ++++++++++++++
>  10 files changed, 1015 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/iio/common/st_sensors/Kconfig
>  create mode 100644 drivers/iio/common/st_sensors/Makefile
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_buffer.c
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_core.c
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_i2c.c
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_spi.c
>  create mode 100644 drivers/iio/common/st_sensors/st_sensors_trigger.c
>  create mode 100644 include/linux/iio/common/st_sensors.h
>
> diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
> index ed45ee5..0b6e97d 100644
> --- a/drivers/iio/common/Kconfig
> +++ b/drivers/iio/common/Kconfig
> @@ -3,3 +3,4 @@
>  #
>
>  source "drivers/iio/common/hid-sensors/Kconfig"
> +source "drivers/iio/common/st_sensors/Kconfig"
> diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
> index 8158400..c2352be 100644
> --- a/drivers/iio/common/Makefile
> +++ b/drivers/iio/common/Makefile
> @@ -7,3 +7,4 @@
>  #
>
>  obj-y += hid-sensors/
> +obj-y += st_sensors/
> diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig
> new file mode 100644
> index 0000000..ddcfcc6
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/Kconfig
> @@ -0,0 +1,30 @@
> +#
> +# Hid Sensor common modules
> +#
> +menu "STMicroelectronics sensors library"
> +
> +config ST_SENSORS_CORE
> +	tristate "Common modules for all ST sensors IIO drivers"
> +	help
> +	  Say yes here to build support for ST sensors to use
> +	  common core functions.
> +
> +config ST_SENSORS_I2C
> +	tristate "I2C common library for ST Sensor IIO drivers"
> +	help
> +	  Say yes here to build support for ST sensors to use
> +	  common i2c functions.
> +
> +config ST_SENSORS_SPI
> +	tristate "SPI common library for ST Sensor IIO drivers"
> +	help
> +	  Say yes here to build support for ST sensors to use
> +	  common spi functions.
> +
> +config ST_SENSORS_TRIGGERED_BUFFER
> +	tristate "trigger and buffer common library for ST Sensor IIO drivers"
> +	help
> +	  Say yes here to build support for ST sensors to use
> +	  common trigger and buffer functions.
> +
> +endmenu
> diff --git a/drivers/iio/common/st_sensors/Makefile b/drivers/iio/common/st_sensors/Makefile
> new file mode 100644
> index 0000000..a191e65
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/Makefile
> @@ -0,0 +1,9 @@
> +#
> +# Makefile for the STMicroelectronics sensor common modules.
> +#
> +
> +obj-$(CONFIG_ST_SENSORS_CORE) += st_sensors_core.o
> +obj-$(CONFIG_ST_SENSORS_I2C) += st_sensors_i2c.o
> +obj-$(CONFIG_ST_SENSORS_SPI) += st_sensors_spi.o
> +obj-$(CONFIG_ST_SENSORS_TRIGGERED_BUFFER) += st_sensors_triggered_buffer.o
> +st_sensors_triggered_buffer-y := st_sensors_trigger.o st_sensors_buffer.o
> diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c
> new file mode 100644
> index 0000000..b636751
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c
> @@ -0,0 +1,115 @@
> +/*
> + * STMicroelectronics sensors buffer library driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@xxxxxx>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/interrupt.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/irqreturn.h>
> +
> +#include <linux/iio/common/st_sensors.h>
> +
> +int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
> +{
> +	int i, n = 0, len;
> +	u8 addr[ST_SENSORS_NUMBER_DATA_CHANNELS];
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) {
> +		if (test_bit(i, indio_dev->active_scan_mask)) {
> +			addr[n] = indio_dev->channels[i].address;
> +			n++;
> +		}
> +	}
> +	switch (n) {
> +	case 1:
> +		len = sdata->tf.read_multiple_byte(&sdata->tf, sdata->dev,
> +			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL, buf,
> +			sdata->multiread_bit);
> +		break;
> +	case 2:
> +		if ((addr[1] - addr[0]) == ST_SENSORS_BYTE_FOR_CHANNEL) {
> +			len = sdata->tf.read_multiple_byte(&sdata->tf,
> +					sdata->dev, addr[0],
> +					ST_SENSORS_BYTE_FOR_CHANNEL*n,
> +					buf, sdata->multiread_bit);
> +		} else {
> +			u8 rx_array[ST_SENSORS_BYTE_FOR_CHANNEL*
> +				    ST_SENSORS_NUMBER_DATA_CHANNELS];
> +			len = sdata->tf.read_multiple_byte(&sdata->tf,
> +				sdata->dev, addr[0],
> +				ST_SENSORS_BYTE_FOR_CHANNEL*
> +				ST_SENSORS_NUMBER_DATA_CHANNELS,
> +				rx_array, sdata->multiread_bit);
> +			if (len < 0)
> +				goto read_data_channels_error;
> +
> +			for (i = 0; i < n * ST_SENSORS_NUMBER_DATA_CHANNELS;
> +									i++) {
> +				if (i < n)
> +					buf[i] = rx_array[i];
> +				else
> +					buf[i] = rx_array[n + i];
> +			}
> +			len = ST_SENSORS_BYTE_FOR_CHANNEL*n;
> +		}
> +		break;
> +	case 3:
> +		len = sdata->tf.read_multiple_byte(&sdata->tf, sdata->dev,
> +			addr[0], ST_SENSORS_BYTE_FOR_CHANNEL*
> +			ST_SENSORS_NUMBER_DATA_CHANNELS,
> +			buf, sdata->multiread_bit);
> +		break;
> +	default:
> +		len = -EINVAL;
> +		goto read_data_channels_error;
> +	}
> +	if (len != ST_SENSORS_BYTE_FOR_CHANNEL*n) {
> +		len = -EIO;
> +		goto read_data_channels_error;
> +	}
> +
> +read_data_channels_error:
> +	return len;
> +}
> +EXPORT_SYMBOL(st_sensors_get_buffer_element);
> +
> +irqreturn_t st_sensors_trigger_handler(int irq, void *p)
> +{
> +	int len;
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data);
> +	if (len < 0)
> +		goto st_sensors_get_buffer_element_error;
> +
> +	if (indio_dev->scan_timestamp)
> +		*(s64 *)((u8 *)sdata->buffer_data +
> +				ALIGN(len, sizeof(s64))) = pf->timestamp;
> +
> +	iio_push_to_buffers(indio_dev, sdata->buffer_data);
> +
> +st_sensors_get_buffer_element_error:
> +	iio_trigger_notify_done(indio_dev->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +EXPORT_SYMBOL(st_sensors_trigger_handler);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@xxxxxx>");
> +MODULE_DESCRIPTION("STMicroelectronics ST-sensors buffer");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
> new file mode 100644
> index 0000000..2b00ed7
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/st_sensors_core.c
> @@ -0,0 +1,335 @@
> +/*
> + * STMicroelectronics sensors core library driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@xxxxxx>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/iio/iio.h>
> +
> +#include <linux/iio/common/st_sensors.h>
> +
> +
> +#define ST_SENSORS_WAI_ADDRESS		0x0f
> +
> +int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, u8 reg_addr,
> +						u8 mask, short num_bit, u8 data)
> +{
> +	int err;
> +	u8 new_data;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	err = sdata->tf.read_byte(&sdata->tf, sdata->dev, reg_addr, &new_data);
> +	if (err < 0)
> +		goto st_sensors_write_data_with_mask_error;
> +
> +	new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask));
> +	err = sdata->tf.write_byte(&sdata->tf, sdata->dev, reg_addr, new_data);
> +
> +st_sensors_write_data_with_mask_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_write_data_with_mask);
> +
> +int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev,
> +			const struct st_sensor_odr_avl *odr_avl, char *buf)
> +{
> +	int i, len = 0;
> +
> +	mutex_lock(&indio_dev->mlock);
> +	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
> +		if (odr_avl[i].hz == 0)
> +			break;
> +
> +		len += sprintf(buf + len, "%d ", odr_avl[i].hz);
> +	}
> +	mutex_unlock(&indio_dev->mlock);
> +	buf[len - 1] = '\n';
> +
> +	return len;
> +}
> +EXPORT_SYMBOL(st_sensors_get_sampling_frequency_avl);
> +
> +int st_sensors_get_fullscale_avl(struct iio_dev *indio_dev,
> +		const struct st_sensor_fullscale_avl *fullscale_avl, char *buf)
> +{
> +	int i, len = 0;
> +
> +	mutex_lock(&indio_dev->mlock);
> +	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
> +		if (fullscale_avl[i].num == 0)
> +			break;
> +
> +		len += sprintf(buf + len, "0.%06u ", fullscale_avl[i].gain);
> +	}
> +	mutex_unlock(&indio_dev->mlock);
> +	buf[len - 1] = '\n';
> +
> +	return len;
> +}
> +EXPORT_SYMBOL(st_sensors_get_fullscale_avl);
> +
> +static int st_sensors_match_odr(const struct st_sensors *sensor,
> +			unsigned int odr, struct st_sensor_odr_avl *odr_out)
> +{
> +	int i, ret = -EINVAL;
> +
> +	for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
> +		if ((sensor->odr.odr_avl[i].hz == odr) &&
> +					(sensor->odr.odr_avl[i].hz != 0)) {
> +			odr_out->hz = sensor->odr.odr_avl[i].hz;
> +			odr_out->value = sensor->odr.odr_avl[i].value;
> +			ret = 0;
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +int st_sensors_set_odr(struct iio_dev *indio_dev,
> +			const struct st_sensors *sensor, unsigned int odr)
> +{
> +	int err;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +	struct st_sensor_odr_avl odr_out;
> +
> +	err = st_sensors_match_odr(sensor, odr, &odr_out);
> +	if (err < 0)
> +		goto st_sensors_match_odr_error;
> +
> +	if ((sensor->odr.addr == sensor->pw.addr) &&
> +				(sensor->odr.mask == sensor->pw.mask)) {
> +		if (sdata->enabled == true) {
> +			err = st_sensors_write_data_with_mask(indio_dev,
> +				sensor->odr.addr, sensor->odr.mask,
> +				sensor->odr.num_bit,
> +				odr_out.value);
> +		} else {
> +			err = 0;
> +		}
> +	} else {
> +		err = st_sensors_write_data_with_mask(indio_dev,
> +			sensor->odr.addr, sensor->odr.mask,
> +			sensor->odr.num_bit, odr_out.value);
> +	}
> +	if (err >= 0)
> +		sdata->odr = odr_out.hz;
> +
> +st_sensors_match_odr_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_set_odr);
> +
> +static int st_sensors_match_fs(const struct st_sensors *sensor,
> +			unsigned int fs, struct st_sensor_fullscale_avl *fs_out)
> +{
> +	int i, ret = -EINVAL;
> +
> +	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
> +		if ((sensor->fs.fs_avl[i].num == fs) &&
> +					(sensor->fs.fs_avl[i].num != 0)) {
> +			fs_out->num = sensor->fs.fs_avl[i].num;
> +			fs_out->gain = sensor->fs.fs_avl[i].gain;
> +			fs_out->value = sensor->fs.fs_avl[i].value;
  			why not
			fs_out = &sensor->fs.fs_avl[i];
			(obviously with the relevant changes to the call sites.)

> +			ret = 0;
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +int st_sensors_set_fullscale(struct iio_dev *indio_dev,
> +			const struct st_sensors *sensor, unsigned int fs)
> +{
> +	int err;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +	struct st_sensor_fullscale_avl fs_out;
> +
> +	err = st_sensors_match_fs(sensor, fs, &fs_out);
> +	if (err < 0)
> +		goto st_accel_set_fullscale_error;
> +
> +	err = st_sensors_write_data_with_mask(indio_dev, sensor->fs.addr,
> +		sensor->fs.mask, sensor->fs.num_bit, sensor->fs.fs_avl->value);
> +	if (err < 0)
> +		goto st_accel_set_fullscale_error;
> +
> +	sdata->fullscale = sensor->fs.fs_avl->num;
> +	sdata->gain = sensor->fs.fs_avl->gain;
> +	sdata->gain2 = sensor->fs.fs_avl->gain2;
Is there any reason not to just have fs_avl in sdata
so have a struct st_sensor_fullscale_avl *fs_avl in sdata and
then just set the pointer here?

> +	return err;
> +
> +st_accel_set_fullscale_error:
> +	dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_set_fullscale);
> +
> +int st_sensors_set_enable(struct iio_dev *indio_dev,
> +				const struct st_sensors *sensor, bool enable)
> +{
> +	int err = -EINVAL;
> +	bool found;
> +	u8 tmp_value;
> +	struct st_sensor_odr_avl odr_out;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	if (enable) {
> +		found = false;
> +		tmp_value = sensor->pw.value_on;
> +		if ((sensor->odr.addr == sensor->pw.addr) &&
> +				(sensor->odr.mask == sensor->pw.mask)) {
> +			err = st_sensors_match_odr(sensor,
> +							sdata->odr, &odr_out);
> +			if (err < 0)
> +				goto set_enable_error;
> +			tmp_value = odr_out.value;
> +			found = true;
> +		}
> +		err = st_sensors_write_data_with_mask(indio_dev,
> +				sensor->pw.addr, sensor->pw.mask,
> +						sensor->pw.num_bit, tmp_value);
> +		if (err < 0)
> +			goto set_enable_error;
> +		sdata->enabled = true;
> +		if (found)
> +			sdata->odr = odr_out.hz;
> +	} else {
> +			err = st_sensors_write_data_with_mask(indio_dev,
> +				sensor->pw.addr, sensor->pw.mask,
> +				sensor->pw.num_bit, sensor->pw.value_off);
> +		if (err < 0)
> +			goto set_enable_error;
> +		sdata->enabled = false;
> +	}
> +
> +set_enable_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_set_enable);
> +
> +int st_sensors_set_axis_enable(struct iio_dev *indio_dev,
> +			const struct st_sensors *sensor, u8 axis_enable)
> +{
> +	return st_sensors_write_data_with_mask(indio_dev,
> +		sensor->enable_axis.addr, sensor->enable_axis.mask,
> +		3, axis_enable);
> +}
> +EXPORT_SYMBOL(st_sensors_set_axis_enable);
> +
> +int st_sensors_init_sensor(struct iio_dev *indio_dev,
> +						const struct st_sensors *sensor)
> +{
> +	int err;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	err = st_sensors_set_enable(indio_dev, sensor, false);
> +	if (err < 0)
> +		goto init_error;
> +
> +	err = st_sensors_set_fullscale(indio_dev, sensor, sdata->fullscale);
> +	if (err < 0)
> +		goto init_error;
> +
> +	err = st_sensors_set_odr(indio_dev, sensor, sdata->odr);
> +	if (err < 0)
> +		goto init_error;
> +
> +	/* set BDU */
> +	err = st_sensors_write_data_with_mask(indio_dev, sensor->bdu.addr,
> +						sensor->bdu.mask, 1, true);
> +	if (err < 0)
> +		goto init_error;
> +
> +	err = st_sensors_set_axis_enable(indio_dev, sensor,
> +						ST_SENSORS_ENABLE_ALL_CHANNELS);
> +
> +init_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_init_sensor);
> +
> +int st_sensors_set_dataready_irq(struct iio_dev *indio_dev,
> +				const struct st_sensors *sensor, bool enable)
> +{
> +	int err;
> +
I'd like to see some comments in here as the flow / naming is not entirely
obvious.
> +	if (sensor->drdy_irq.ig1.en_addr > 0) {
> +		err = st_sensors_write_data_with_mask(indio_dev,
> +				sensor->drdy_irq.ig1.en_addr,
> +				sensor->drdy_irq.ig1.en_mask, 1, (int)enable);
> +		if (err < 0)
> +			goto st_accel_set_dataready_irq_error;
> +	}
> +
> +	if (sensor->drdy_irq.ig1.latch_mask_addr > 0) {
> +			err = st_sensors_write_data_with_mask(indio_dev,
> +				sensor->drdy_irq.ig1.latch_mask_addr,
> +				sensor->drdy_irq.ig1.latching_mask, 1,
> +			(int)enable);
> +		if (err < 0)
> +			goto st_accel_set_dataready_irq_error;
> +	}
> +
> +	err = st_sensors_write_data_with_mask(indio_dev,
> +			sensor->drdy_irq.addr,
> +			sensor->drdy_irq.mask, 1, (int)enable);
> +
> +st_accel_set_dataready_irq_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_set_dataready_irq);
> +
> +int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev,
> +				const struct st_sensors *sensor, int scale)
> +{
> +	int err = -EINVAL, i;
> +
> +	for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
> +		if ((sensor->fs.fs_avl[i].gain == scale) &&
> +					(sensor->fs.fs_avl[i].gain != 0)) {
> +			err = 0;
> +			break;
> +		}
> +	}
> +	if (err < 0)
> +		goto st_sensors_match_scale_error;
> +
> +	err = st_sensors_set_fullscale(indio_dev,
> +					sensor, sensor->fs.fs_avl[i].num);
> +
> +st_sensors_match_scale_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain);
> +
> +int st_sensors_read_axis_data(struct iio_dev *indio_dev, u8 ch_addr, int *data)
> +{
> +	int err;
> +	u8 outdata[ST_SENSORS_BYTE_FOR_CHANNEL];
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	err = sdata->tf.read_multiple_byte(&sdata->tf, sdata->dev,
> +			ch_addr, ST_SENSORS_BYTE_FOR_CHANNEL,
> +						outdata, sdata->multiread_bit);
> +	if (err < 0)
> +		goto read_error;
> +
> +	*data = ((s16)le16_to_cpup((u16 *)outdata));
> +
> +read_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_read_axis_data);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@xxxxxx>");
> +MODULE_DESCRIPTION("STMicroelectronics ST-sensors core");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c
> new file mode 100644
> index 0000000..fa4bc7d
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c
> @@ -0,0 +1,78 @@
> +/*
> + * STMicroelectronics sensors i2c library driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@xxxxxx>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/iio/iio.h>
> +
> +#include <linux/iio/common/st_sensors.h>
> +
> +
> +#define ST_SENSORS_I2C_MULTIREAD	0x80
> +
> +unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev)
> +{
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +	struct i2c_client *client = to_i2c_client(sdata->dev);
> +
> +	return client->irq;
> +}
> +
> +static int st_sensors_i2c_read_byte(struct st_sensor_transfer *st,
> +				struct device *dev, u8 reg_addr, u8 *res_byte)
> +{
> +	int err;
> +
> +	err = i2c_smbus_read_byte_data(to_i2c_client(dev), reg_addr);
> +	if (err < 0)
> +		goto st_accel_i2c_read_byte_error;
> +
> +	*res_byte = err & 0xff;
> +
> +st_accel_i2c_read_byte_error:
> +	return err < 0 ? err : 0;
> +}
> +
> +static int st_sensors_i2c_read_multiple_byte(struct st_sensor_transfer *st,
> +	struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
> +{
> +	if (multiread_bit == true)
> +		reg_addr |= ST_SENSORS_I2C_MULTIREAD;
> +
> +	return i2c_smbus_read_i2c_block_data(to_i2c_client(dev),
> +							reg_addr, len, data);
> +}
> +
> +static int st_sensors_i2c_write_byte(struct st_sensor_transfer *st,
> +				struct device *dev, u8 reg_addr, u8 data)
> +{
> +	return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data);
> +}
> +
> +void st_sensors_i2c_configure(struct iio_dev *indio_dev,
> +		struct i2c_client *client, struct st_sensor_data *sdata)
> +{
> +	i2c_set_clientdata(client, indio_dev);
> +
> +	indio_dev->dev.parent = &client->dev;
> +	indio_dev->name = client->name;
> +
> +	mutex_init(&sdata->tf.buf_lock);
> +	sdata->tf.read_byte = st_sensors_i2c_read_byte;
> +	sdata->tf.write_byte = st_sensors_i2c_write_byte;
> +	sdata->tf.read_multiple_byte = st_sensors_i2c_read_multiple_byte;
> +	sdata->get_irq_data_ready = st_sensors_i2c_get_irq;
I review backwards through code so the same comments as written for the
spi equivalent apply here...

> +}
> +EXPORT_SYMBOL(st_sensors_i2c_configure);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@xxxxxx>");
> +MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c
> new file mode 100644
> index 0000000..82fa633
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/st_sensors_spi.c
> @@ -0,0 +1,124 @@
> +/*
> + * STMicroelectronics sensors spi library driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@xxxxxx>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/iio/iio.h>
> +
> +#include <linux/iio/common/st_sensors.h>
> +
> +
> +#define ST_SENSORS_SPI_MULTIREAD	0xc0
> +#define ST_SENSORS_SPI_READ		0x80
> +
> +unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
> +{
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +	struct spi_device *spi = to_spi_device(sdata->dev);
> +
> +	return spi->irq;
I'd roll this up slightly and drop the intermediate variable.

return to_spi_device(sdata->dev)->irq;

> +}
> +
> +static int st_sensors_spi_read(struct st_sensor_transfer *st,
> +	struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
> +{
> +	struct spi_message msg;
> +	int err;
> +
> +	struct spi_transfer xfers[] = {
> +		{
> +			.tx_buf = st->tx_buf,
> +			.bits_per_word = 8,
> +			.len = 1,
> +		},
> +		{
> +			.rx_buf = st->rx_buf,
> +			.bits_per_word = 8,
> +			.len = len,
> +		}
> +	};
> +
> +	mutex_lock(&st->buf_lock);
> +	if ((multiread_bit == true) && (len > 1))
> +		st->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD;
> +	else
> +		st->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;
> +
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&xfers[0], &msg);
> +	spi_message_add_tail(&xfers[1], &msg);
> +	err = spi_sync(to_spi_device(dev), &msg);
> +	if (err)
> +		goto acc_spi_read_error;
> +
> +	memcpy(data, st->rx_buf, len*sizeof(u8));
> +	mutex_unlock(&st->buf_lock);
> +	return len;
> +
> +acc_spi_read_error:
> +	return err;
> +}
> +
> +static int st_sensors_spi_read_byte(struct st_sensor_transfer *st,
> +				struct device *dev, u8 reg_addr, u8 *res_byte)
> +{
> +	return st_sensors_spi_read(st, dev, reg_addr, 1, res_byte, false);
> +}
> +
> +static int st_sensors_spi_read_multiple_byte(struct st_sensor_transfer *st,
> +	struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
> +{
> +	return st_sensors_spi_read(st, dev, reg_addr, len, data, multiread_bit);
> +}
> +
> +static int st_sensors_spi_write_byte(struct st_sensor_transfer *st,
> +				struct device *dev, u8 reg_addr, u8 data)
> +{
> +	struct spi_message msg;
> +	int err;
> +
> +	struct spi_transfer xfers = {
> +		.tx_buf = st->tx_buf,
> +		.bits_per_word = 8,
> +		.len = 2,
> +	};
> +
> +	mutex_lock(&st->buf_lock);
> +	st->tx_buf[0] = reg_addr;
> +	st->tx_buf[1] = data;
> +
> +	spi_message_init(&msg);
> +	spi_message_add_tail(&xfers, &msg);
> +	err = spi_sync(to_spi_device(dev), &msg);
> +	mutex_unlock(&st->buf_lock);
> +
> +	return err;
> +}
> +
> +void st_sensors_spi_configure(struct iio_dev *indio_dev,
> +			struct spi_device *spi, struct st_sensor_data *sdata)
> +{
> +	spi_set_drvdata(spi, indio_dev);
> +
> +	indio_dev->dev.parent = &spi->dev;
> +	indio_dev->name = spi->modalias;
> +
> +	mutex_init(&sdata->tf.buf_lock);

Silly though it may seem I thik I'd prefer the mutex init in
st_sensors_init_sensor as it isn't spi or i2c specific whereas
everything else in here is...

> +	sdata->tf.read_byte = st_sensors_spi_read_byte;
> +	sdata->tf.write_byte = st_sensors_spi_write_byte;
> +	sdata->tf.read_multiple_byte = st_sensors_spi_read_multiple_byte;

Might be worth wrapping these callbacks up in a const static structure rather
than along side the dynamic data in tf. (like we do with struct iio_info within
struct iio_dev)

> +	sdata->get_irq_data_ready = st_sensors_spi_get_irq;
> +}
> +EXPORT_SYMBOL(st_sensors_spi_configure);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@xxxxxx>");
> +MODULE_DESCRIPTION("STMicroelectronics ST-sensors spi driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c
> new file mode 100644
> index 0000000..6d43b13
> --- /dev/null
> +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c
> @@ -0,0 +1,76 @@
> +/*
> + * STMicroelectronics sensors trigger library driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@xxxxxx>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/interrupt.h>
> +
> +#include <linux/iio/common/st_sensors.h>
> +
> +int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
> +					const struct iio_trigger_ops *trig_ops)
> +{
> +	int err;
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
> +	if (sdata->trig == NULL) {
> +		err = -ENOMEM;
> +		dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
> +		goto iio_trigger_alloc_error;
> +	}
> +
> +	err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
> +			iio_trigger_generic_data_rdy_poll,
> +			NULL,
> +			IRQF_TRIGGER_RISING,
> +			sdata->trig->name,
> +			sdata->trig);
> +	if (err)
> +		goto request_irq_error;
> +
> +	sdata->trig->private_data = indio_dev;
> +	sdata->trig->ops = trig_ops;
> +	sdata->trig->dev.parent = sdata->dev;
> +
> +	err = iio_trigger_register(sdata->trig);
> +	if (err < 0) {
> +		dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
> +		goto iio_trigger_register_error;
> +	}
> +	indio_dev->trig = sdata->trig;
> +
> +	return 0;
> +
> +iio_trigger_register_error:
> +	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
> +request_irq_error:
> +	iio_trigger_free(sdata->trig);
> +iio_trigger_alloc_error:
> +	return err;
> +}
> +EXPORT_SYMBOL(st_sensors_allocate_trigger);
> +
> +void st_sensors_deallocate_trigger(struct iio_dev *indio_dev)
> +{
> +	struct st_sensor_data *sdata = iio_priv(indio_dev);
> +
> +	iio_trigger_unregister(sdata->trig);
> +	free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
> +	iio_trigger_free(sdata->trig);
> +}
> +EXPORT_SYMBOL(st_sensors_deallocate_trigger);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@xxxxxx>");
> +MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h
> new file mode 100644
> index 0000000..c0f2acc
> --- /dev/null
> +++ b/include/linux/iio/common/st_sensors.h
> @@ -0,0 +1,246 @@
> +/*
> + * STMicroelectronics sensors library driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@xxxxxx>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#ifndef ST_SENSORS_H
> +#define ST_SENSORS_H
> +
> +#include <linux/i2c.h>
> +#include <linux/spi/spi.h>
> +#include <linux/irqreturn.h>
> +#include <linux/iio/trigger.h>
> +
> +
> +#define ST_SENSORS_TX_MAX_LENGHT		2
> +#define ST_SENSORS_RX_MAX_LENGHT		6
> +
> +#define ST_SENSORS_ODR_LIST_MAX			10
> +#define ST_SENSORS_FULLSCALE_AVL_MAX		10
> +
> +#define ST_SENSORS_NUMBER_ALL_CHANNELS		4
> +#define ST_SENSORS_NUMBER_DATA_CHANNELS		3
> +#define ST_SENSORS_ENABLE_ALL_CHANNELS		0x07
> +#define ST_SENSORS_BYTE_FOR_CHANNEL		2
> +#define ST_SENSORS_SCAN_X			0
> +#define ST_SENSORS_SCAN_Y			1
> +#define ST_SENSORS_SCAN_Z			2
> +#define ST_SENSORS_DEFAULT_12_REALBITS		12
> +#define ST_SENSORS_DEFAULT_16_REALBITS		16
> +#define ST_SENSORS_DEFAULT_POWER_ON_VALUE	0x01
> +#define ST_SENSORS_DEFAULT_POWER_OFF_VALUE	0x00
> +#define ST_SENSORS_DEFAULT_WAI_ADDRESS		0x0f
> +#define ST_SENSORS_DEFAULT_AXIS_ADDR		0x20
> +#define ST_SENSORS_DEFAULT_AXIS_MASK		0x07
> +#define ST_SENSORS_DEFAULT_AXIS_N_BIT		3
> +
> +#define ST_SENSORS_LSM_CHANNELS(device_type, index, mod, endian, bits, addr) \
> +{ \
> +	.type = device_type, \
> +	.modified = 1, \
> +	.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
> +					IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
> +	.scan_index = index, \
> +	.channel2 = mod, \
> +	.address = addr, \
> +	.scan_type = { \
> +		.sign = 's', \
> +		.realbits = bits, \
> +		.shift = 16 - bits, \
> +		.storagebits = 16, \
> +		.endianness = endian, \
> +	}, \
> +}
> +
> +struct st_sensor_odr_avl {
> +	unsigned int hz;
> +	u8 value;
> +};
> +
> +struct st_sensor_odr {
> +	u8 addr;
> +	u8 mask;
> +	short num_bit;
> +	struct st_sensor_odr_avl odr_avl[ST_SENSORS_ODR_LIST_MAX];
> +};
> +
> +struct st_sensor_power {
> +	u8 addr;
> +	u8 mask;
> +	unsigned short num_bit;
> +	u8 value_off;
> +	u8 value_on;
> +};
> +
> +struct st_sensor_axis {
> +	u8 addr;
> +	u8 mask;
> +};
> +
> +struct st_sensor_fullscale_avl {
> +	unsigned int num;
> +	u8 value;
> +	unsigned int gain;
> +	unsigned int gain2;
> +};
> +
> +struct st_sensor_fullscale {
> +	u8 addr;
> +	u8 mask;
> +	unsigned short num_bit;
> +	struct st_sensor_fullscale_avl fs_avl[ST_SENSORS_FULLSCALE_AVL_MAX];
> +};
> +
> +struct st_sensor_bdu {
> +	u8 addr;
> +	u8 mask;
> +};
> +
> +struct st_sensor_interrupt_generator {
> +	u8 en_addr;
> +	u8 latch_mask_addr;
> +	u8 en_mask;
> +	u8 latching_mask;
> +};
> +
> +struct st_sensor_data_ready_irq {
> +	u8 addr;
> +	u8 mask;
> +	struct st_sensor_interrupt_generator ig1;
I'd just embed the definition in here
i.e. (assuming I remember the syntax correctly...)

struct st_sensor_data_read_irq {
       u8 addr;
       u8 mask;
       struct {
       	      u8 en_addr;
	      u8 latch_mask_addr;
	      u8 en_mask;
	      u8 latching_mask;
       } ig1;
};
> +};
> +
> +/**
> + * struct st_sensor_transfer - ST sensor device I/O struct
> + * @buf_lock: Mutex to protect rx and tx buffers.
> + * @tx_buf: Buffer used by SPI transfer function to send data to the sensors.
> + *	This buffer is used to avoid DMA not-aligned issue.
> + * @rx_buf: Buffer used by SPI transfer to receive data from sensors.
> + *	This buffer is used to avoid DMA not-aligned issue.
> + * @read_byte: Function used to read one byte.
> + * @write_byte: Function used to write one byte.
> + * @read_multiple_byte: Function used to read multiple byte.
> + */
> +struct st_sensor_transfer {
> +	struct mutex buf_lock;
Move these to the end of the structure and all is fine
(as long as this is the last structure in any containing one as well -
see below).

Also only need to force alignment of the tx_buf
(as the rx_buf will pack after it and any hardware
that corrupts that is plain mad)

> +	u8 tx_buf[ST_SENSORS_TX_MAX_LENGHT] ____cacheline_aligned;
> +	u8 rx_buf[ST_SENSORS_RX_MAX_LENGHT] ____cacheline_aligned;
> +	int (*read_byte) (struct st_sensor_transfer *st, struct device *dev,
> +						u8 reg_addr, u8 *res_byte);
> +	int (*write_byte) (struct st_sensor_transfer *st, struct device *dev,
> +						u8 reg_addr, u8 data);
> +	int (*read_multiple_byte) (struct st_sensor_transfer *st,
> +		struct device *dev, u8 reg_addr, int len, u8 *data,
> +							bool multiread_bit);
> +};
> +
> +/**
> + * struct st_sensor_data - ST sensor device status
> + * @dev: Pointer to instance of struct device (I2C or SPI).
> + * @trig: The trigger in use by the core driver.
> + * @enabled: Status of the sensor (false->off, true->on).
> + * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread.
> + * @index: Number used to point the sensor being used in the st_sensors struct.
> + * @buffer_data: Data used by buffer part.
> + * @fullscale: Maximum range of measure by the sensor.
> + * @gain: Sensitivity of the sensor [m/s^2/LSB].
> + * @odr: Output data rate of the sensor [Hz].
> + * @tf: Transfer function structure used by I/O operations.
> + */
> +struct st_sensor_data {
> +	struct device *dev;
> +	struct iio_trigger *trig;
> +
> +	bool enabled;
> +	bool multiread_bit;
> +
> +	short index;
> +
> +	char *buffer_data;
> +
> +	unsigned int fullscale;
> +	unsigned int gain;
> +	unsigned int gain2;
> +	unsigned int odr;
> +
This will need to also be at the end of this structure to ensure
that the below callback pointer doesn't end up on the same cacheline as
the tx_buf and rx_buf.  A comment saying that this is a requirement here
will also make future bugs much less likely.
> +	struct st_sensor_transfer tf;
> +
> +	unsigned int (*get_irq_data_ready) (struct iio_dev *indio_dev);
> +};
> +
> +/**
> + * struct st_sensors - ST sensors list
> + * @wai: Contents of WhoAmI register.
> + * @ch: IIO channels for the sensor.
> + * @odr: Output data rate register and odr list available.
> + * @pw: Power register of the sensor.
> + * @enable_axis: Enable one or more axis of the sensor.
> + * @fs: Full scale register and fs list available.
> + * @bdu: Block data update register.
> + * @drdy_irq: Data ready register of the sensor.
> + * @multi_read_bit: Use or not particular bit for [I2C/SPI] multiread.
> + */
> +struct st_sensors {
> +	u8 wai;
> +	struct iio_chan_spec *ch;
> +	struct st_sensor_odr odr;
> +	struct st_sensor_power pw;
> +	struct st_sensor_axis enable_axis;
> +	struct st_sensor_fullscale fs;
> +	struct st_sensor_bdu bdu;
> +	struct st_sensor_data_ready_irq drdy_irq;
> +	bool multi_read_bit;
> +};
> +
Either break these out to separate headers or deal with the fact that
spi or i2c might not be present in a given build with ifdef fun and
games.

> +void st_sensors_spi_configure(struct iio_dev *indio_dev,
> +			struct spi_device *spi, struct st_sensor_data *sdata);
> +
> +void st_sensors_i2c_configure(struct iio_dev *indio_dev,
> +		struct i2c_client *client, struct st_sensor_data *sdata);
> +
> +int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, u8 reg_addr,
> +					u8 mask, short num_bit, u8 data);
> +
> +int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev,
> +			const struct st_sensor_odr_avl *odr_avl, char *buf);
> +
> +int st_sensors_get_fullscale_avl(struct iio_dev *indio_dev,
> +	const struct st_sensor_fullscale_avl *fullscale_avl, char *buf);
> +
> +int st_sensors_set_odr(struct iio_dev *indio_dev,
> +			const struct st_sensors *sensor, unsigned int odr);
> +
> +int st_sensors_set_fullscale(struct iio_dev *indio_dev,
> +			const struct st_sensors *sensor, unsigned int fs);
> +
> +int st_sensors_set_enable(struct iio_dev *indio_dev,
> +				const struct st_sensors *sensor, bool enable);
> +
> +int st_sensors_set_axis_enable(struct iio_dev *indio_dev,
> +			const struct st_sensors *sensor, u8 axis_enable);
> +
> +int st_sensors_init_sensor(struct iio_dev *indio_dev,
> +					const struct st_sensors *sensor);
> +
> +int st_sensors_set_dataready_irq(struct iio_dev *indio_dev,
> +				const struct st_sensors *sensor, bool enable);
> +
> +int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev,
> +				const struct st_sensors *sensor, int scale);
> +
> +int st_sensors_read_axis_data(struct iio_dev *indio_dev, u8 ch_addr, int *data);
> +
> +int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
> +					const struct iio_trigger_ops *trig_ops);
> +
> +void st_sensors_deallocate_trigger(struct iio_dev *indio_dev);
> +
> +int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf);
> +
> +irqreturn_t st_sensors_trigger_handler(int irq, void *p);
> +
> +#endif /* ST_SENSORS_H */
>
--
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