Re: STMicroelectronics accelerometers driver.

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

 



On 10/08/2012 05:39 PM, Denis CIOCCA wrote:
> Hi everybody,
> 
> I submit to you my linux device driver to support the latest ST
> accelerometers. I want do submission to the linux community and I ask
> you suggestions and proposals.
> Thanks
> 
> Denis
> 

Hi just a quick and rough review. One general thing: It would be good if you
would namespace your function names, struct names, defines, etc. Currently some
of them aren't namespaced at all and some just use 'accel'. Maybe use
"st_accel" instead.

> 
>  From c965b7f522d858a48e3bbcc723cb2ff4c00397f4 Mon Sep 17 00:00:00 2001
> From: Denis Ciocca <denis.ciocca@xxxxxx>
> Date: Mon, 8 Oct 2012 17:04:32 +0200
> Subject: [PATCH 1/2] add driver

You need a better patch description than this ;)

> 
> ---
>   .../STMicroelectronics_accelerometers_iio_buffer.c |  155 ++
>   .../STMicroelectronics_accelerometers_iio_core.c   | 1495
> ++++++++++++++++++++
>   .../STMicroelectronics_accelerometers_iio_i2c.c    |  124 ++
>   .../STMicroelectronics_accelerometers_iio_spi.c    |  209 +++
>   ...STMicroelectronics_accelerometers_iio_trigger.c |   96 ++
>   5 files changed, 2079 insertions(+), 0 deletions(-)
>   create mode 100644
> drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c
>   create mode 100644
> drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
>   create mode 100644
> drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
>   create mode 100644
> drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
>   create mode 100644
> drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c

the file names are a bit excessive in my opinion. How about
st_accel_{buffer,core,i2c,...}.c?

> 
> diff --git
> a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c
> b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c
> new file mode 100644
> index 0000000..44e3f3d
> --- /dev/null
> +++ b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c
> @@ -0,0 +1,155 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@xxxxxx>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/stat.h>
> +#include <linux/interrupt.h>
> +#include <linux/byteorder/generic.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/kfifo_buf.h>
> +#include <linux/iio/trigger_consumer.h>
> +
> +#include <linux/iio/acc/STMicroelectronics_accelerometers_iio.h>
> +
> +
> +static int acc_read_all(struct iio_dev *indio_dev, u8 *rx_array)
> +{
> +       int len;
> +       struct acc_data *adata = iio_priv(indio_dev);
> +
> +       len = adata->read_multiple_byte(adata,
> +               indio_dev->channels[ACC_SCAN_X].address,
> +               ACC_BYTE_FOR_CHANNEL*ACC_NUMBER_DATA_CHANNELS, rx_array);
> +       if (len < 0)
> +               goto read_error;
> +
> +       return len;
> +
> +read_error:
> +       return -EIO;
> +}
> +
> +static int acc_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
> +{
> +       int ret, i, n = 0;
> +       u8 *rx_array;
> +       u8 mask = 0x01;
> +       s16 *data = (s16 *)buf;
> +
> +       rx_array = kzalloc(ACC_BYTE_FOR_CHANNEL*ACC_NUMBER_DATA_CHANNELS,
> +                                                               GFP_KERNEL);
> +       if (rx_array == NULL)
> +               return -ENOMEM;
> +       ret = acc_read_all(indio_dev, rx_array);
> +       if (ret < 0)
> +               return ret;
> +       for (i = 0; i < ACC_NUMBER_DATA_CHANNELS; i++) {
> +               if ((*indio_dev->active_scan_mask & mask) > 0) {
> +                       if (indio_dev->channels[i].scan_type.endianness ==
> +                                                               IIO_LE) {
> +                               data[n] = (s16)(cpu_to_le16(le16_to_cpu((
> +                               (__le16 *)rx_array)[i]))) >>
> +                               indio_dev->channels[i].scan_type.shift;
> +                               n++;
> +                       } else {
> +                               data[n] = (s16)(cpu_to_le16(be16_to_cpu((
> +                               (__be16 *)rx_array)[i]))) >>
> +                               indio_dev->channels[i].scan_type.shift;
> +                               n++;
> +                       }
> +               }
> +               mask = mask << 1;
> +       }


In buffered mode the samples should not be postprocessed. Userspace expects the
samples in the format as described by the scan_type. The buffer demuxing should
be handled by the IIO core. If you set up available_scan_masks correctly it
will automatically do the right thing.

> +       kfree(rx_array);
> +       return i*sizeof(data[0]);
> +}
> +
[...]
> +
> +int acc_allocate_ring(struct iio_dev *indio_dev)
> +{
> +       int ret;
> +       struct iio_buffer *buffer;
> +       struct acc_data *adata = iio_priv(indio_dev);
> +
> +       buffer = iio_kfifo_allocate(indio_dev);
> +       if (buffer == NULL) {
> +               ret = -ENOMEM;
> +               goto acc_configure_ring_error;
> +       }
> +       indio_dev->buffer = buffer;
> +       indio_dev->scan_timestamp = true;
> +       indio_dev->buffer->scan_timestamp = true;
> +       indio_dev->setup_ops = &acc_buf_setup_ops;
> +       indio_dev->pollfunc = iio_alloc_pollfunc(
> +                                               &iio_pollfunc_store_time,
> +                                               &acc_trigger_handler,
> +                                               IRQF_ONESHOT,
> +                                               indio_dev,
> +                                               "%s_consumer%d",
> +                                               indio_dev->name,
> +                                               indio_dev->id);
> +       if (indio_dev->pollfunc == NULL) {
> +               ret = -ENOMEM;
> +               goto iio_alloc_pollfunc_error;
> +       }
> +       indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
> +       ret = iio_buffer_register(indio_dev, indio_dev->channels,
> +                                               ACC_NUMBER_ALL_CHANNELS);
> +       if (ret < 0) {
> +               pr_err("%s: iio_buffer_register failed.\n", adata->name);
> +               goto iio_buffer_register_error;
> +       }
> +       pr_info("%s: allocate buffer -> done.\n", adata->name);


This looks like you could make use of the generic triggered_buffer code. Please
have a look at the different drivers which already make use of it.

> +       return 0;
> +
> +iio_buffer_register_error:
> +iio_alloc_pollfunc_error:
> +       iio_kfifo_free(buffer);
> +acc_configure_ring_error:
> +       return ret;
> +}
> +
> +void acc_deallocate_ring(struct iio_dev *indio_dev)
> +{
> +       iio_dealloc_pollfunc(indio_dev->pollfunc);
> +       iio_kfifo_free(indio_dev->buffer);
> +       iio_buffer_unregister(indio_dev);
> +}
> diff --git
> a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
> b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
> new file mode 100644
> index 0000000..f9ccbf6
> --- /dev/null
> +++ b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
> @@ -0,0 +1,1495 @@
[...]
> +static struct sensor {
> +       u8 wai;
> +       struct odr odr;
> +       struct power pw;
> +       struct fullscale fs;
> +       struct data_out data;
> +       struct data_ready_irq drdy_irq;
> +       struct iio_chan_spec *ch;
> +} sensor[] = {
> +       {
> +       .wai = S1_WAI_EXP,
> +       .ch = (struct iio_chan_spec *)default_channels,
> +       },
> +       {
> +       .wai = S2_WAI_EXP,
> +       .ch = (struct iio_chan_spec *)s2_channels,
> +       .odr = {
> +               .odr_avl[0] = {

I'd use
 .odr_avl = {
	[0] = {
		...
	},
	[1] = {
		...

Makes things a bit more readable in my opinion.
	

> +                       .hz = S2_ODR_AVL_3HZ,
> +                       .value = S2_ODR_AVL_3HZ_VALUE,
> +               },
[...]
> +
> +static int acc_write_data_with_mask(struct iio_dev *indio_dev, u8 reg_addr,
> +                                               u8 mask, short num_bit, u8 data)
> +{
> +       int err, j, pos;
> +       u8 prev_data;
> +       u8 new_data;
> +       struct acc_data *adata = iio_priv(indio_dev);
> +
> +       pos = 7;
> +       for (j = 128; j >= 0; j = j/2) {

Missing spaces, please run checkpatch.pl before submitting a patch

> +               if (mask/j > 0)

Missing space

> +                       break;
> +               else
> +                       pos--;
> +       }
> +
> +       prev_data = adata->read_byte(adata, reg_addr);
> +       if (prev_data < 0)
> +               goto i2c_write_data_with_mask_error;
> +       prev_data &= 0xff;
> +       new_data = ((prev_data & (~mask)) | ((data << (pos-num_bit+1)) & mask));

Missing spaces

> +       err = adata->write_byte(adata, reg_addr, new_data);
> +       if (err)
> +               goto i2c_write_data_with_mask_error;

The label you jump to here is just on the next line...

> +
> +i2c_write_data_with_mask_error:
> +       return err;
> +}
> +
[...]


> +static int acc_read_raw(struct iio_dev *indio_dev,
> +                       struct iio_chan_spec const *ch, int *val,
> +                                                       int *val2, long mask)
> +{
> +       int err;
> +       int data_tmp;
> +       u8 outdata[ACC_BYTE_FOR_CHANNEL];
> +       struct acc_data *adata = iio_priv(indio_dev);
> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_RAW:
> +               if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
> +                       err = -EBUSY;
> +               } else {
> +                       err = atomic_read(&adata->enabled);
> +                       if (!err) {
> +                               err = -EHOSTDOWN;

"Host is down", I don't think this is best error message here. Also is there
any reason why you can't powerup/powerdown the device on demand?


> +                       } else {
> +                               err = adata->read_multiple_byte(adata,
> +                                       ch->address, ACC_BYTE_FOR_CHANNEL,
> +                                       outdata);
> +                               if (err < 0)
> +                                       goto read_error;
> +
> +                               if (ch->scan_type.endianness == IIO_LE)
> +                                       *val = (s32)(s16)(cpu_to_le16(
> +                                       le16_to_cpu(((__le16 *)outdata)[0])))
> +                                       >> ch->scan_type.shift;
> +                               else
> +                                       *val = (s32)(s16)(cpu_to_le16(
> +                                       be16_to_cpu(((__be16 *)outdata)[0])))
> +                                       >> ch->scan_type.shift;
> +                       }
> +               }
> +               return IIO_VAL_INT;
> +       case IIO_CHAN_INFO_SCALE:
> +               data_tmp = adata->gain*UG_TO_MS2;
> +               *val = 0;
> +               *val2 = data_tmp;
> +               return IIO_VAL_INT_PLUS_NANO;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +read_error:
> +       pr_err("%s: failed to read i2c raw data!\n", adata->name);
> +       return err;
> +}
> +
> +static int acc_check_irq(struct iio_dev *indio_dev)
> +{
> +       int err;
> +       struct acc_data *adata = iio_priv(indio_dev);
> +
> +       if (adata->irq_data_ready <= 0)

This will always be true since you made irq_data_ready a pointer, doesn't the
compiler complain here?

> +               err = -EINVAL;
> +       else
> +               err = 0;
> +
> +       return err;
> +}
> +
> +
> +static int set_platform_data(struct iio_dev *indio_dev)
> +{
> +       int err;
> +       struct acc_data *adata;
> +
> +       adata = iio_priv(indio_dev);
> +       adata->pdata = kmalloc(sizeof(struct acc_platform_data), GFP_KERNEL);
> +       if (adata->pdata == NULL) {
> +               pr_err("%s: failed to allocate memory for pdata.\n",
> +                                                               adata->name);
> +               err = -ENOMEM;
> +               goto pdata_malloc_error;
> +       }
> +       if (adata->client == NULL) {
> +               if (adata->spi->dev.platform_data != NULL) {
> +                       memcpy(adata->pdata, adata->spi->dev.platform_data,
> +                                       sizeof(struct acc_platform_data));
> +               } else {
> +                       memcpy(adata->pdata, &acc_default_pdata,
> +                                       sizeof(struct acc_platform_data));
> +               }
> +       } else if (adata->spi == NULL) {
> +               if (adata->client->dev.platform_data != NULL) {
> +                       memcpy(adata->pdata, adata->client->dev.platform_data,
> +                                       sizeof(struct acc_platform_data));
> +               } else {
> +                       memcpy(adata->pdata, &acc_default_pdata,
> +                                       sizeof(struct acc_platform_data));
> +               }
> +       }
> +       return 0;
> +
> +pdata_malloc_error:
> +       return err;
> +}

Is platform_data really used outside of probe? If not you could get rid of this.

> +
> +static void set_sensor_parameters(struct iio_dev *indio_dev)
> +{
> +       struct acc_data *adata;
> +
> +       adata = iio_priv(indio_dev);
> +       if (!sensor[adata->index].odr.addr)
> +               sensor[adata->index].odr.addr = DEFAULT_ODR_ADDR;
> +       if (!sensor[adata->index].odr.mask)
> +               sensor[adata->index].odr.mask = DEFAULT_ODR_MASK;
> +       if (!sensor[adata->index].odr.num_bit)
> +                       sensor[adata->index].odr.num_bit = DEFAULT_ODR_N_BIT;
> +       if (!sensor[adata->index].pw.addr)
> +               sensor[adata->index].pw.addr = DEFAULT_POWER_ADDR;
> +       if (!sensor[adata->index].pw.mask)
> +               sensor[adata->index].pw.mask = DEFAULT_POWER_MASK;
> +       if (!sensor[adata->index].pw.num_bit)
> +                       sensor[adata->index].pw.num_bit = DEFAULT_POWER_N_BIT;
> +       if (!sensor[adata->index].pw.value_off)
> +               sensor[adata->index].pw.value_off = DEFAULT_POWER_OFF_VALUE;
> +       if (!sensor[adata->index].pw.value_on)
> +               sensor[adata->index].pw.value_on = DEFAULT_POWER_ON_VALUE;
> +       if (!sensor[adata->index].odr.odr_avl[0].hz) {
> +               sensor[adata->index].odr.odr_avl[0].hz = DEFAULT_ODR_AVL_1HZ;
> +               sensor[adata->index].odr.odr_avl[0].value =
> +                                               DEFAULT_ODR_AVL_1HZ_VALUE;
> +               sensor[adata->index].odr.odr_avl[1].hz = DEFAULT_ODR_AVL_10HZ;
> +               sensor[adata->index].odr.odr_avl[1].value =
> +                                               DEFAULT_ODR_AVL_10HZ_VALUE;
> +               sensor[adata->index].odr.odr_avl[2].hz = DEFAULT_ODR_AVL_25HZ;
> +               sensor[adata->index].odr.odr_avl[2].value =
> +                                               DEFAULT_ODR_AVL_25HZ_VALUE;
> +               sensor[adata->index].odr.odr_avl[3].hz = DEFAULT_ODR_AVL_50HZ;
> +               sensor[adata->index].odr.odr_avl[3].value =
> +                                               DEFAULT_ODR_AVL_50HZ_VALUE;
> +               sensor[adata->index].odr.odr_avl[4].hz =
> +                                               DEFAULT_ODR_AVL_100HZ;
> +               sensor[adata->index].odr.odr_avl[4].value =
> +                                               DEFAULT_ODR_AVL_100HZ_VALUE;
> +               sensor[adata->index].odr.odr_avl[5].hz =
> +                                               DEFAULT_ODR_AVL_200HZ;
> +               sensor[adata->index].odr.odr_avl[5].value =
> +                                               DEFAULT_ODR_AVL_200HZ_VALUE;
> +               sensor[adata->index].odr.odr_avl[6].hz =
> +                                               DEFAULT_ODR_AVL_400HZ;
> +               sensor[adata->index].odr.odr_avl[6].value =
> +                                               DEFAULT_ODR_AVL_400HZ_VALUE;
> +               sensor[adata->index].odr.odr_avl[7].hz =
> +                                               DEFAULT_ODR_AVL_1600HZ;
> +               sensor[adata->index].odr.odr_avl[7].value =
> +                                               DEFAULT_ODR_AVL_1600HZ_VALUE;
> +       }
> +       if (!sensor[adata->index].fs.addr)
> +               sensor[adata->index].fs.addr = DEFAULT_FS_ADDR;
> +       if (!sensor[adata->index].fs.mask)
> +               sensor[adata->index].fs.mask = DEFAULT_FS_MASK;
> +       if (!sensor[adata->index].fs.num_bit)
> +               sensor[adata->index].fs.num_bit = DEFAULT_FS_N_BIT;
> +       if (!sensor[adata->index].fs.fs_avl[0].num) {
> +               sensor[adata->index].fs.fs_avl[0].num = DEFAULT_FS_AVL_2_NUM;
> +               sensor[adata->index].fs.fs_avl[0].value =
> +                                               DEFAULT_FS_AVL_2_VALUE;
> +               sensor[adata->index].fs.fs_avl[0].gain =
> +                                               DEFAULT_FS_AVL_2_GAIN;
> +               sensor[adata->index].fs.fs_avl[1].num = DEFAULT_FS_AVL_4_NUM;
> +               sensor[adata->index].fs.fs_avl[1].value =
> +                                               DEFAULT_FS_AVL_4_VALUE;
> +               sensor[adata->index].fs.fs_avl[1].gain =
> +                                               DEFAULT_FS_AVL_4_GAIN;
> +               sensor[adata->index].fs.fs_avl[2].num = DEFAULT_FS_AVL_8_NUM;
> +               sensor[adata->index].fs.fs_avl[2].value =
> +                                               DEFAULT_FS_AVL_8_VALUE;
> +               sensor[adata->index].fs.fs_avl[2].gain =
> +                                               DEFAULT_FS_AVL_8_GAIN;
> +               sensor[adata->index].fs.fs_avl[3].num = DEFAULT_FS_AVL_16_NUM;
> +               sensor[adata->index].fs.fs_avl[3].value =
> +                                               DEFAULT_FS_AVL_16_VALUE;
> +               sensor[adata->index].fs.fs_avl[3].gain =
> +                                               DEFAULT_FS_AVL_16_GAIN;
> +       }
> +       if (!sensor[adata->index].data.addr[X_AXIS][DATA_LOW])
> +               sensor[adata->index].data.addr[X_AXIS][DATA_LOW] =
> +                                                       (DEFAULT_OUT_X_L);
> +       if (!sensor[adata->index].data.addr[X_AXIS][DATA_HIGH])
> +               sensor[adata->index].data.addr[X_AXIS][DATA_HIGH] =
> +                                                       DEFAULT_OUT_X_H;
> +       if (!sensor[adata->index].data.addr[Y_AXIS][DATA_LOW])
> +               sensor[adata->index].data.addr[Y_AXIS][DATA_LOW] =
> +                                                       (DEFAULT_OUT_Y_L);
> +       if (!sensor[adata->index].data.addr[Y_AXIS][DATA_HIGH])
> +               sensor[adata->index].data.addr[Y_AXIS][DATA_HIGH] =
> +                                                       DEFAULT_OUT_Y_H;
> +       if (!sensor[adata->index].data.addr[Z_AXIS][DATA_LOW])
> +               sensor[adata->index].data.addr[Z_AXIS][DATA_LOW] =
> +                                                       (DEFAULT_OUT_Z_L);
> +       if (!sensor[adata->index].data.addr[Z_AXIS][DATA_HIGH])
> +               sensor[adata->index].data.addr[Z_AXIS][DATA_HIGH] =
> +                                                       DEFAULT_OUT_Z_H;
> +       if (!sensor[adata->index].drdy_irq.addr)
> +               sensor[adata->index].drdy_irq.addr = DEFAULT_DRDY_IRQ_ADDR;
> +       if (!sensor[adata->index].drdy_irq.mask)
> +               sensor[adata->index].drdy_irq.mask = DEFAULT_DRDY_IRQ_MASK;
> +       if (!sensor[adata->index].data.bdu.addr)
> +               sensor[adata->index].data.bdu.addr = DEFAULT_BDU_ADDR;
> +       if (!sensor[adata->index].data.bdu.mask)
> +               sensor[adata->index].data.bdu.mask = DEFAULT_BDU_MASK;
> +       if (!sensor[adata->index].drdy_irq.ig1.en_addr)
> +               sensor[adata->index].drdy_irq.ig1.en_addr = DEFAULT_IG1_EN_ADDR;
> +       if (!sensor[adata->index].drdy_irq.ig1.en_mask)
> +               sensor[adata->index].drdy_irq.ig1.en_mask = DEFAULT_IG1_EN_MASK;
> +       if (!sensor[adata->index].drdy_irq.ig1.latching_mask_addr)
> +               sensor[adata->index].drdy_irq.ig1.latching_mask_addr =
> +                                               DEFAULT_IG1_LATCHING_MASK_ADDR;
> +       if (!sensor[adata->index].drdy_irq.ig1.latching_mask)
> +               sensor[adata->index].drdy_irq.ig1.latching_mask =
> +                                               DEFAULT_IG1_LATCHING_MASK;

Any reason to not initialize the static sensor array directly with these
defaults? This function is idempotent, but still it would be nice to make
"sensor" const.

> +}
> +
[...]
> +static ssize_t sysfs_sampling_frequency_available(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +       int i, n, len;
> +       char tmp[4];
> +       char sampling_frequency[30] = "";
> +       struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +       struct acc_data *adata = iio_priv(indio_dev);
> +
> +       mutex_lock(&indio_dev->mlock);
> +       n = ARRAY_SIZE(sensor[adata->index].odr.odr_avl);
> +       for (i = 0; i < n; i++) {
> +               if (sensor[adata->index].odr.odr_avl[i].hz != 0) {
> +                       len = strlen(&sampling_frequency[0]);
> +                       sprintf(tmp, "%d ",
> +                               sensor[adata->index].odr.odr_avl[i].hz);
> +                       strcpy(&sampling_frequency[len], tmp);
> +               } else
> +                       break;
> +       }
> +       mutex_unlock(&indio_dev->mlock);
> +
> +       return sprintf(buf, "%s\n", sampling_frequency);

Ok, so first you sprintf your value into tmp, then you copy temp to
sampling_frequency and finally you copy sampling_frequency. You could just
sprintf directly into buf. Also try to use scnprintf with a limit of PAGE_SIZE

> +}
> +
> +static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
> +                               sysfs_sampling_frequency_available, NULL , 0);
> +static IIO_DEVICE_ATTR(fullscale_available, S_IRUGO,
> +                               sysfs_fullscale_available, NULL , 0);
> +static IIO_DEVICE_ATTR(fullscale, S_IWUSR | S_IRUGO, sysfs_get_fullscale,
> +                               sysfs_set_fullscale , 0);
> +static IIO_DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, sysfs_get_enable,
> +                                               sysfs_set_enable , 0);
> +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
> sysfs_get_sampling_frequency,
> +                               sysfs_set_sampling_frequency);

Any non-standard attributes need documentation.

> +int acc_iio_default(struct iio_dev *indio_dev)

I'd call this _probe instead of _default.

> +{
> +       int err;
> +       u8 wai;
> +       struct acc_data *adata = iio_priv(indio_dev);
> +
> +       mutex_init(&adata->slock);
> +       indio_dev->modes = INDIO_DIRECT_MODE;
> +       indio_dev->info = &acc_info;
> +
> +       err = get_wai_device(indio_dev, DEFAULT_WAI_ADDRESS, &wai);
> +       if (err < 0)
> +               goto get_wai_error;
> +       err = check_device_list(indio_dev, wai);
> +       if (err < 0)
> +               goto check_device_list_error;
> +       set_sensor_parameters(indio_dev);
> +       err = set_platform_data(indio_dev);
> +       if (err < 0)
> +               goto set_platform_data_error;
> +       err = validate_platform_data(indio_dev);
> +       if (err < 0)
> +               goto validate_platform_data_error;
> +       register_channels(indio_dev);
> +
> +       err = init_sensor(indio_dev);
> +       if (err < 0)
> +               goto init_sensor_error;
> +
> +       if (sensor[adata->index].wai == S5_WAI_EXP)
> +               adata->multiread_bit = 0;
> +       else
> +               adata->multiread_bit = 1;
> +
> +       err = acc_check_irq(indio_dev);
> +       if (err < 0)
> +               goto gpio_check_error;
> +       err = acc_allocate_ring(indio_dev);
> +       if (err < 0)
> +               goto acc_allocate_ring_error;
> +
> +       err = acc_probe_trigger(indio_dev);
> +       if (err < 0)
> +               goto acc_probe_trigger_error;
> +
> +       err = iio_device_register(indio_dev);
> +       if (err)
> +               goto iio_device_register_error;
> +
> +       pr_info("%s: probe end correctly.\n", adata->name);
> +
> +       return err;
> +
> +iio_device_register_error:
> +       acc_remove_trigger(indio_dev);
> +acc_probe_trigger_error:
> +       acc_deallocate_ring(indio_dev);
> +acc_allocate_ring_error:
> +gpio_check_error:
> +init_sensor_error:
> +validate_platform_data_error:
> +       kfree(adata->pdata);
> +set_platform_data_error:
> +check_device_list_error:
> +get_wai_error:
> +       return err;
> +}
> +
> +int acc_iio_remove(struct iio_dev *indio_dev)
> +{
> +       struct acc_data *adata = iio_priv(indio_dev);
> +
> +       acc_remove_trigger(indio_dev);
> +       acc_deallocate_ring(indio_dev);
> +       kfree(adata->pdata);
> +       iio_device_unregister(indio_dev);
> +
> +       return 0;
> +}
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@xxxxxx>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
> +MODULE_LICENSE("GPL v2");
> diff --git
> a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
> b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
> new file mode 100644
> index 0000000..f443e52
> --- /dev/null
> +++ b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
> @@ -0,0 +1,124 @@
> +/*
> + * STMicroelectronics accelerometers 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/i2c.h>
> +#include <linux/iio/iio.h>
> +
> +#include <linux/iio/acc/STMicroelectronics_accelerometers_iio.h>
> +
> +
> +#define        ACC_I2C_MULTIREAD       0x80
> +
> +
> +static inline s32 acc_i2c_read_byte(struct acc_data *adata, u8 reg_addr)
> +{
> +       return i2c_smbus_read_byte_data(adata->client, reg_addr);
> +}
> +
> +static inline s32 acc_i2c_read_multiple_byte(struct acc_data *adata,
> +                                               u8 reg_addr, int len, u8 *data)
> +{
> +       if (adata->multiread_bit != 0)
> +               reg_addr |= ACC_I2C_MULTIREAD;
> +       return i2c_smbus_read_i2c_block_data(adata->client,
> +                                                       reg_addr, len, data);
> +}
> +
> +static inline s32 acc_i2c_write_byte(struct acc_data *adata,
> +                                                       u8 reg_addr, u8 data)
> +{
> +       return i2c_smbus_write_byte_data(adata->client, reg_addr, data);
> +}
> +
> +static int __devinit acc_i2c_probe(struct i2c_client *client,
> +                               const struct i2c_device_id *id)
> +{
> +       struct iio_dev *indio_dev;
> +       struct acc_data *adata;
> +       int err;
> +
> +       pr_info("%s: probe start.\n", client->name);

This line is just noise, I'd remove it

> +       indio_dev = iio_device_alloc(sizeof(*adata));
> +       if (indio_dev == NULL) {
> +               err = -ENOMEM;
> +               goto iio_device_alloc_error;
> +       }
> +
> +       adata = iio_priv(indio_dev);
> +       adata->client = client;
> +       i2c_set_clientdata(client, indio_dev);
> +
> +       indio_dev->dev.parent = &client->dev;
> +       indio_dev->name = client->name;
> +
> +       adata->read_byte = acc_i2c_read_byte;
> +       adata->write_byte = acc_i2c_write_byte;
> +       adata->read_multiple_byte = acc_i2c_read_multiple_byte;
> +       adata->name = &client->name[0];

adata->name = client->name;

> +       adata->irq_data_ready = &client->irq;

Why do you take a pointer here?

> +
> +       err = acc_iio_default(indio_dev);
> +       if (err < 0)
> +               goto acc_iio_default_error;
> +
> +       return 0;
> +
> +acc_iio_default_error:
> +       iio_device_free(indio_dev);
> +iio_device_alloc_error:
> +       return err;
> +}
> +
[...]

No new line her
> +MODULE_DEVICE_TABLE(i2c, acc_id_table);
> +
> +static struct i2c_driver acc_driver = {
> +       .driver = {
> +               .owner = THIS_MODULE,
> +               .name = "STMicroelectronics i2c accelerometers",
> +       },
> +       .probe = acc_i2c_probe,
> +       .remove = __devexit_p(acc_i2c_remove),
> +       .id_table = acc_id_table,
> +};
> +

No new line her

> +module_i2c_driver(acc_driver);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@xxxxxx>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
> +MODULE_LICENSE("GPL v2");
> diff --git
> a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
> b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
> new file mode 100644
> index 0000000..f1ac211
> --- /dev/null
> +++ b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
> @@ -0,0 +1,209 @@
[...]
> +
> +static int __devinit acc_spi_probe(struct spi_device *client)
> +{
> +       struct iio_dev *indio_dev;
> +       struct acc_data *adata;
> +       int err;
> +
> +       pr_info("%s: probe start.\n", client->modalias);

That's just noise, I'd remove it.

> +       indio_dev = iio_device_alloc(sizeof(*adata));
> +       if (indio_dev == NULL) {
> +               err = -ENOMEM;
> +               goto iio_device_alloc_error;
> +       }
> +
> +       adata = iio_priv(indio_dev);
> +       adata->spi = client;
> +       spi_set_drvdata(client, indio_dev);
> +
> +       indio_dev->dev.parent = &client->dev;
> +       indio_dev->name = client->modalias;
> +
> +       adata->read_byte = acc_spi_read_byte;
> +       adata->write_byte = acc_spi_write_byte;
> +       adata->read_multiple_byte = acc_spi_read_multiple_byte;
> +       adata->name = &client->modalias[0];

adata->name = client->modalias;

> +       adata->irq_data_ready = &client->irq;
> +
> +       /* dummy read */

It would be good if the comment mentioned why a dummy read is necessary.

> +       adata->read_byte(adata, 0x0f);
> +
> +       err = acc_iio_default(indio_dev);
> +       if (err < 0)
> +               goto acc_iio_default_error;
> +
> +       return 0;
> +
> +acc_iio_default_error:
> +       iio_device_free(indio_dev);
> +iio_device_alloc_error:
> +       return err;
> +}
> +
> +static int __devexit acc_spi_remove(struct spi_device *spi)
> +{
> +       int err;
> +       struct iio_dev *indio_dev = spi_get_drvdata(spi);
> +
> +       err = acc_iio_remove(indio_dev);

err is not used.

> +       iio_device_free(indio_dev);

Why not put the iio_device_free in acc_iio_remove?

> +
> +       return 0;
> +}
> +
[...]
> +

No newline here

> +MODULE_DEVICE_TABLE(spi, acc_id_table);
> +
> +static struct spi_driver acc_driver = {
> +       .driver = {
> +               .owner = THIS_MODULE,
> +               .name = "STMicroelectronics spi accelerometers",

Maybe use "st-accel" here instead

> +       },
> +       .probe = acc_spi_probe,
> +       .remove = __devexit_p(acc_spi_remove),
> +       .id_table = acc_id_table,
> +};
> +

No newline here

> +module_spi_driver(acc_driver);
> +
> +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@xxxxxx>");
> +MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
> +MODULE_LICENSE("GPL v2");
> diff --git
> a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c
> b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c
> new file mode 100644
> index 0000000..843af4c
> --- /dev/null
> +++ b/drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c
> @@ -0,0 +1,96 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@xxxxxx>
> + *
> + * Licensed under the GPL-2.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +
> +#include <linux/iio/acc/STMicroelectronics_accelerometers_iio.h>
> +
> +
> +static irqreturn_t acc_data_ready_trig_poll(int irq, void *trig)
> +{
> +       iio_trigger_poll(trig, iio_get_time_ns());
> +       return IRQ_HANDLED;
> +}

This is just a open-coded iio_trigger_generic_data_rdy_poll, use it instead.

> +
> +static int iio_trig_acc_set_state(struct iio_trigger *trig, bool state)
> +{
> +       struct iio_dev *indio_dev = trig->private_data;
> +       return acc_set_dataready_irq(indio_dev, state);
> +}
> +
> +static const struct iio_trigger_ops iio_acc_trigger_ops = {
> +       .owner = THIS_MODULE,
> +       .set_trigger_state = &iio_trig_acc_set_state,
> +};
> +
> +int acc_probe_trigger(struct iio_dev *indio_dev)
> +{
> +       int err;
> +       struct acc_data *adata = iio_priv(indio_dev);
> +
> +       adata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
> +       if (adata->trig == NULL) {
> +               err = -ENOMEM;
> +               pr_err("%s: failed to allocate iio trigger.\n", adata->name);
> +               goto iio_trigger_alloc_error;
> +       }
> +
> +       err = request_threaded_irq(*adata->irq_data_ready,
> +                       acc_data_ready_trig_poll,
> +                       NULL,
> +                       IRQF_TRIGGER_RISING,
> +                       "IRQ_data_ready",
> +                       adata->trig);

request_irq. Your thread_fn is NULL. Also maybe use a better name, I'd
recommand trigger->name.

> +       if (err) {
> +               pr_err("%s: failed to request threaded irq [%d].\n",
> +                                       adata->name, *adata->irq_data_ready);
> +               goto request_irq_error;
> +       }
> +       adata->trig->private_data = indio_dev;
> +       adata->trig->ops = &iio_acc_trigger_ops;
> +
> +       if (adata->client != NULL)
> +               adata->trig->dev.parent = &adata->client->dev;
> +       else
> +               adata->trig->dev.parent = &adata->spi->dev;
> +
> +       err = iio_trigger_register(adata->trig);
> +       if (err < 0) {
> +               pr_err("%s: failed to register iio trigger.\n", adata->name);
> +               goto iio_trigger_register_error;
> +       }
> +       indio_dev->trig = adata->trig;
> +       pr_info("%s: using [%s] trigger.\n", adata->name,
> +                                                       adata->trig->name);
> +       return 0;
> +
> +iio_trigger_register_error:
> +       free_irq(*adata->irq_data_ready, adata->trig);
> +request_irq_error:
> +       iio_trigger_free(adata->trig);
> +iio_trigger_alloc_error:
> +       return err;
> +}
[...]
> --
> 1.7.0.4
> 
> 
>  From 1fe8b75e0ec1197781c559935de23e116773c441 Mon Sep 17 00:00:00 2001
> From: Denis Ciocca <denis.ciocca@xxxxxx>
> Date: Mon, 8 Oct 2012 17:08:43 +0200
> Subject: [PATCH 2/2] add header file
> 
> ---
>   .../acc/STMicroelectronics_accelerometers_iio.h    |  116
> ++++++++++++++++++++
>   1 files changed, 116 insertions(+), 0 deletions(-)
>   create mode 100644
> include/linux/iio/acc/STMicroelectronics_accelerometers_iio.h
> 
> diff --git
> a/include/linux/iio/acc/STMicroelectronics_accelerometers_iio.h
> b/include/linux/iio/acc/STMicroelectronics_accelerometers_iio.h
> new file mode 100644
> index 0000000..e6f18ad
> --- /dev/null
> +++ b/include/linux/iio/acc/STMicroelectronics_accelerometers_iio.h
> @@ -0,0 +1,116 @@
> +/*
> + * STMicroelectronics accelerometers driver
> + *
> + * Copyright 2012 STMicroelectronics Inc.
> + *
> + * Denis Ciocca <denis.ciocca@xxxxxx>
> + * v. 1.0.0
> + * Licensed under the GPL-2.
> + */
> +
> [...]
> +
> +struct acc_platform_data {
> +       int fullscale;
> +       int sampling_frequency;
> +};
> +
> +struct acc_data {
> +       struct i2c_client *client;
> +       struct spi_device *spi;

Since a device will either be a spi or i2c device there is no need to have
pointers to both here. You can use a struct device and use to_i2c_client and
to_spi_device in the raw bus read/write callbacks. This will also help to clean
a few places in your driver where you do if(acc->client) .. else if (acc->spi)

> +       struct acc_platform_data *pdata;
> +       char *name;
> +
> +       short index;
> +
> +       atomic_t enabled;
> +       int fullscale;
> +       int gain;
> +       int odr;
> +
> +       int multiread_bit;
> +       int (*read_byte) (struct acc_data *adata, u8 reg_addr);
> +       int (*write_byte) (struct acc_data *adata, u8 reg_addr, u8 data);
> +       int (*read_multiple_byte) (struct acc_data *adata, u8 reg_addr,
> +                                                       int len, u8 *data);
> +
> +#ifdef CONFIG_STMICROELECTRONICS_ACCELEROMETERS_BUF_KFIFO
> +       struct iio_trigger *trig;
> +#endif /* CONFIG_STMICROELECTRONICS_ACCELEROMETERS_BUF_KFIFO */
> +
> +       int *irq_data_ready;
> +       struct mutex slock;
> +
> +};
> +
[...]
> +
> +#endif /* ST_ACCELEROMETERS_IIO_ACC_H */

There is no need for most of this to be globally visible. Put everything except
for the platform data into a local header drivers/iio/accel. And put the header
containing the platform data in include/linux/platform_data/


--
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