STMicroelectronics accelerometers driver.
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
From c965b7f522d858a48e3bbcc723cb2ff4c00397f4 Mon Sep 17 00:00:00 2001
From: Denis Ciocca <[email protected]>
Date: Mon, 8 Oct 2012 17:04:32 +0200
Subject: [PATCH 1/2] add driver
--- .../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 100644drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c
create mode 100644drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
create mode 100644drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
create mode 100644drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
create mode 100644drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.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
drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c new file +49 -0
@@ -0,0 +1,155 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* 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 =3D iio_priv(indio_dev); + + len =3D 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 =3D 0; +u8 *rx_array; + u8 mask =3D 0x01; + s16 *data =3D (s16 *)buf; + + rx_array =3D kzalloc(ACC_BYTE_FOR_CHANNEL*ACC_NUMBER_DATA_CHANNELS, + GFP_KERNEL)=
;
+ if (rx_array =3D=3D NULL)
+ return -ENOMEM;
+ ret =3D acc_read_all(indio_dev, rx_array);
+ if (ret < 0)
+ return ret;
+ for (i =3D 0; i < ACC_NUMBER_DATA_CHANNELS; i++) {
+ if ((*indio_dev->active_scan_mask & mask) > 0) {
+ if (indio_dev->channels[i].scan_type.endianness =3D=
=3D
+ IIO_LE) {
+ data[n] =3D (s16)(cpu_to_le16(le16_to_cpu((
+ (__le16 *)rx_array)[i]))) >>
+ indio_dev->channels[i].scan_type.shift;
+ n++;
+ } else {
+ data[n] =3D (s16)(cpu_to_le16(be16_to_cpu((
+ (__be16 *)rx_array)[i]))) >>
+ indio_dev->channels[i].scan_type.shift;
+ n++;
+ }
+ }
+ mask =3D mask << 1;
+ }
+ kfree(rx_array);
+ return i*sizeof(data[0]);+}
+
+static irqreturn_t acc_trigger_handler(int irq, void *p) +{
+ struct iio_poll_func *pf =3D p; + struct iio_dev *indio_dev =3D pf->indio_dev; + int len =3D 0; + char *data;
+
+ data =3D kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (data =3D=3D NULL)
+ goto done;
+ if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklengt=
h))
+ len =3D acc_get_buffer_element(indio_dev, data);
+ else
+ goto done;
+ if (indio_dev->scan_timestamp)
+ *(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) =3D pf->time=
stamp;
+ iio_push_to_buffer(indio_dev->buffer, data);
+ kfree(data);
+
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+static const struct iio_buffer_setup_ops acc_buf_setup_ops =3D {
+ .preenable =3D &iio_sw_buffer_preenable, + .postenable =3D &iio_triggered_buffer_postenable, + .predisable =3D &iio_triggered_buffer_predisable,
+};
+
+int acc_allocate_ring(struct iio_dev *indio_dev)
+{
+ int ret; + struct iio_buffer *buffer; + struct acc_data *adata =3D iio_priv(indio_dev);
+
+ buffer =3D iio_kfifo_allocate(indio_dev);
+ if (buffer =3D=3D NULL) {
+ ret =3D -ENOMEM;
+ goto acc_configure_ring_error;
+ }
+ indio_dev->buffer =3D buffer;
+ indio_dev->scan_timestamp =3D true;
+ indio_dev->buffer->scan_timestamp =3D true;
+ indio_dev->setup_ops =3D &acc_buf_setup_ops;
+ indio_dev->pollfunc =3D 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 =3D=3D NULL) {
+ ret =3D -ENOMEM;
+ goto iio_alloc_pollfunc_error;
+ }
+ indio_dev->modes |=3D INDIO_BUFFER_TRIGGERED;
+ ret =3D 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);
+ 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
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +371 -0
371 lines not on this page · raw message
1,
+ },
+ },
+ .fs =3D {
+ .addr =3D S3_FS_ADDR,
+ .mask =3D S3_FS_MASK,
+ .num_bit =3D S3_FS_N_BIT,
+ .fs_avl[0] =3D {
+ .num =3D DEFAULT_FS_AVL_2_NUM,
+ .value =3D DEFAULT_FS_AVL_2_VALUE,
+ .gain =3D S2_FS_AVL_2_GAIN,
+ },
+ .fs_avl[1] =3D {
+ .num =3D DEFAULT_FS_AVL_4_NUM,
+ .value =3D DEFAULT_FS_AVL_4_VALUE,
+ .gain =3D S2_FS_AVL_4_GAIN,
+ },
+ .fs_avl[2] =3D {
+ .num =3D S2_FS_AVL_6_NUM,
+ .value =3D S2_FS_AVL_6_VALUE,
+ .gain =3D S2_FS_AVL_6_GAIN,
+ },
+ .fs_avl[3] =3D {
+ .num =3D DEFAULT_FS_AVL_8_NUM,
+ .value =3D DEFAULT_FS_AVL_8_VALUE + OFFSET_1,
+ .gain =3D S2_FS_AVL_8_GAIN,
+ },
+ .fs_avl[4] =3D {
+ .num =3D DEFAULT_FS_AVL_16_NUM,
+ .value =3D DEFAULT_FS_AVL_16_VALUE + OFFSET_1,
+ .gain =3D S2_FS_AVL_16_GAIN,+ },
+ },
+ .drdy_irq =3D {
+ .mask =3D S2_DRDY_IRQ_MASK,
+ },
+ .data =3D {
+ .bdu =3D {
+ .addr =3D S2_BDU_ADDR,
+ .mask =3D S2_BDU_MASK,
+ },
+ },
+ },
+ {
+ .wai =3D S3_WAI_EXP,
+ .ch =3D (struct iio_chan_spec *)default_channels,
+ .odr =3D {
+ .mask =3D S3_ODR_MASK,
+ .num_bit =3D S3_ODR_N_BIT,
+ .odr_avl[0] =3D {+ .hz =3D S3_ODR_AVL_40HZ,
+ .value =3D S3_ODR_AVL_40HZ_VALUE,
+ },
+ .odr_avl[1] =3D {
+ .hz =3D S3_ODR_AVL_160HZ,
+ .value =3D S3_ODR_AVL_160HZ_VALUE,
+ },
+ .odr_avl[2] =3D {
+ .hz =3D S3_ODR_AVL_640HZ,
+ .value =3D S3_ODR_AVL_640HZ_VALUE,
+ },
+ .odr_avl[3] =3D {
+ .hz =3D S3_ODR_AVL_2560HZ,
+ .value =3D S3_ODR_AVL_2560HZ_VALUE,
+ },
+ },
+ .pw =3D {
+ .addr =3D S3_POWER_ADDR,
+ .mask =3D S3_POWER_MASK,
+ .num_bit =3D S3_POWER_N_BIT,
+ .value_on =3D S3_POWER_ON_VALUE,
+ .value_off =3D S3_POWER_OFF_VALUE,
+ },
+ .fs =3D {
+ .addr =3D S2_FS_ADDR,
+ .mask =3D S2_FS_MASK,
+ .num_bit =3D S2_FS_N_BIT,
+ .fs_avl[0] =3D {
+ .num =3D S3_FS_AVL_2_NUM,
+ .value =3D S3_FS_AVL_2_VALUE,
+ .gain =3D S3_FS_AVL_2_GAIN,
+ },
+ .fs_avl[1] =3D {
+ .num =3D S3_FS_AVL_6_NUM,
+ .value =3D S3_FS_AVL_6_VALUE,
+ .gain =3D S3_FS_AVL_6_GAIN,+ },
+ },
+ .drdy_irq =3D {
+ .addr =3D S3_DRDY_IRQ_ADDR,
+ .mask =3D S3_DRDY_IRQ_MASK,
+ },
+ .data =3D {
+ .bdu =3D {
+ .addr =3D S3_BDU_ADDR,
+ .mask =3D S3_BDU_MASK,
+ },
+ },
+ },
+ {
+ .wai =3D S4_WAI_EXP,
+ .ch =3D (struct iio_chan_spec *)default_channels,
+ .odr =3D {
+ .mask =3D S4_ODR_MASK,
+ .num_bit =3D S4_ODR_N_BIT,
+ .odr_avl[0] =3D {+ .hz =3D DEFAULT_ODR_AVL_50HZ,
+ .value =3D S4_ODR_AVL_50HZ_VALUE,
+ },
+ .odr_avl[1] =3D {
+ .hz =3D DEFAULT_ODR_AVL_100HZ,
+ .value =3D S4_ODR_AVL_100HZ_VALUE,
+ },
+ .odr_avl[2] =3D {
+ .hz =3D DEFAULT_ODR_AVL_400HZ,
+ .value =3D S4_ODR_AVL_400HZ_VALUE,
+ },
+ .odr_avl[3] =3D {
+ .hz =3D S4_ODR_AVL_1000HZ,
+ .value =3D S4_ODR_AVL_1000HZ_VALUE,
+ },
+ },
+ .pw =3D {
+ .mask =3D S4_POWER_MASK,
+ .num_bit =3D S4_POWER_N_BIT,
+ },
+ .fs =3D {
+ .fs_avl[0] =3D {
+ .num =3D S4_FS_AVL_2_NUM,
+ .value =3D S4_FS_AVL_2_VALUE,
+ .gain =3D S4_FS_AVL_2_GAIN,
+ },
+ .fs_avl[1] =3D {
+ .num =3D S4_FS_AVL_4_NUM,
+ .value =3D S4_FS_AVL_4_VALUE,
+ .gain =3D S4_FS_AVL_4_GAIN,
+ },
+ .fs_avl[1] =3D {
+ .num =3D S4_FS_AVL_8_NUM,
+ .value =3D S4_FS_AVL_8_VALUE,
+ .gain =3D S4_FS_AVL_8_GAIN,
+ },
+ },
+ },
+ {
+ .wai =3D S5_WAI_EXP,
+ .ch =3D (struct iio_chan_spec *)s5_channels,
+ .odr =3D {+ .odr_avl[0] =3D {
+ .hz =3D S5_ODR_AVL_3HZ,
+ .value =3D S5_ODR_AVL_3HZ_VALUE,
+ },
+ .odr_avl[1] =3D {
+ .hz =3D S5_ODR_AVL_6HZ,
+ .value =3D S5_ODR_AVL_6HZ_VALUE,
+ },
+ .odr_avl[2] =3D {
+ .hz =3D S5_ODR_AVL_12HZ,
+ .value =3D S5_ODR_AVL_12HZ_VALUE,
+ },
+ .odr_avl[3] =3D {
+ .hz =3D DEFAULT_ODR_AVL_25HZ,
+ .value =3D DEFAULT_ODR_AVL_25HZ_VALUE + OFFSET_1,
+ },
+ .odr_avl[4] =3D {
+ .hz =3D DEFAULT_ODR_AVL_50HZ,
+ .value =3D DEFAULT_ODR_AVL_50HZ_VALUE + OFFSET_1,
+ },
+ .odr_avl[5] =3D {
+ .hz =3D DEFAULT_ODR_AVL_100HZ,
+ .value =3D DEFAULT_ODR_AVL_100HZ_VALUE + OFFSET_1,
+ },
+ .odr_avl[6] =3D {
+ .hz =3D DEFAULT_ODR_AVL_400HZ,
+ .value =3D DEFAULT_ODR_AVL_400HZ_VALUE,
+ },
+ .odr_avl[7] =3D {
+ .hz =3D S5_ODR_AVL_800HZ,
+ .value =3D S5_ODR_AVL_800HZ_VALUE - OFFSET_1,
+ },
+ .odr_avl[8] =3D {
+ .hz =3D DEFAULT_ODR_AVL_1600HZ,
+ .value =3D DEFAULT_ODR_AVL_1600HZ_VALUE + OFFSET_1,
+ },
+ },
+ .fs =3D {
+ .addr =3D S5_FS_ADDR,
+ .mask =3D S5_FS_MASK,
+ .num_bit =3D S5_FS_N_BIT,
+ .fs_avl[0] =3D {
+ .num =3D DEFAULT_FS_AVL_2_NUM,
+ .value =3D DEFAULT_FS_AVL_2_VALUE,
+ .gain =3D S5_FS_AVL_2_GAIN,
+ },
+ .fs_avl[1] =3D {
+ .num =3D DEFAULT_FS_AVL_4_NUM,
+ .value =3D DEFAULT_FS_AVL_4_VALUE,
+ .gain =3D S5_FS_AVL_4_GAIN,
+ },
+ .fs_avl[2] =3D {
+ .num =3D S5_FS_AVL_6_NUM,
+ .value =3D S5_FS_AVL_6_VALUE,
+ .gain =3D S5_FS_AVL_6_GAIN,
+ },
+ .fs_avl[3] =3D {
+ .num =3D DEFAULT_FS_AVL_8_NUM,
+ .value =3D DEFAULT_FS_AVL_8_VALUE + OFFSET_1,
+ .gain =3D S5_FS_AVL_8_GAIN,
+ },
+ .fs_avl[4] =3D {
+ .num =3D DEFAULT_FS_AVL_16_NUM,
+ .value =3D DEFAULT_FS_AVL_16_VALUE + OFFSET_1,
+ .gain =3D S5_FS_AVL_16_GAIN,
+ },
+ },
+ .drdy_irq =3D {
+ .addr =3D S5_DRDY_IRQ_ADDR,
+ .mask =3D S5_DRDY_IRQ_MASK,
+ .ig1 =3D {
+ .en_addr =3D S5_IG1_EN_ADDR,
+ .en_mask =3D S5_IG1_EN_MASK,
+ .latching_mask_addr =3D S5_IG1_LATCHING_MASK_ADDR,
+ .latching_mask =3D S5_IG1_LATCHING_MASK,+ },
+ },
+ .data =3D {
+ .bdu =3D {
+ .addr =3D S5_BDU_ADDR,
+ .mask =3D S5_BDU_MASK,
+ },
+ },
+ }
+};
+
+static struct acc_platform_data acc_default_pdata =3D {
+ .fullscale =3D DEFAULT_FS_AVL_2_NUM,
+ .sampling_frequency =3D DEFAULT_ODR_AVL_100HZ,
+};
+
+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 =3D iio_priv(indio_dev);
+
+ pos =3D 7;
+ for (j =3D 128; j >=3D 0; j =3D j/2) {
+ if (mask/j > 0)
+ break;
+ else
+ pos--;
+ }
+
+ prev_data =3D adata->read_byte(adata, reg_addr);
+ if (prev_data < 0)
+ goto i2c_write_data_with_mask_error;
+ prev_data &=3D 0xff;
+ new_data =3D ((prev_data & (~mask)) | ((data << (pos-num_bit+1)) & =
mask));
+ err =3D adata->write_byte(adata, reg_addr, new_data);
+ if (err)
+ goto i2c_write_data_with_mask_error;
+
+i2c_write_data_with_mask_error:
+ return err;
+}
+
+static int match_odr(struct sensor *sensor, int odr,
+ struct odr_available *odr_o=
ut)
+{
+ int n, i, ret =3D -1;
+
+ n =3D ARRAY_SIZE(sensor->odr.odr_avl);
+ for (i =3D 0; i < n; i++)
+ if (sensor->odr.odr_avl[i].hz =3D=3D odr) {
+ odr_out->hz =3D sensor->odr.odr_avl[i].hz; + odr_out->value =3D sensor->odr.odr_avl[i].value; + ret =3D 0; + break;
+ }
+
+ return ret;
+}
+
+static int match_fs(struct sensor *sensor, int fs,
+ struct fullscale_available *fs_out)
+{
+ int n, i, ret =3D -1;
+
+ n =3D ARRAY_SIZE(sensor->fs.fs_avl);
+ for (i =3D 0; i < n; i++)
+ if (sensor->fs.fs_avl[i].num =3D=3D fs) {
+ fs_out->num =3D sensor->fs.fs_avl[i].num; + fs_out->gain =3D sensor->fs.fs_avl[i].gain; + fs_out->value =3D sensor->fs.fs_avl[i].value; + ret =3D 0; + break;
+ }
+
+ return ret;
+}
+
+int acc_set_dataready_irq(struct iio_dev *indio_dev, bool enable) +{
+ int err;
+ struct acc_data *adata;
+
+ adata =3D iio_priv(indio_dev);
+ if (sensor[adata->index].drdy_irq.ig1.en_addr > 0) {
+ err =3D acc_write_data_with_mask(indio_dev,
+ sensor[adata->index].drdy_irq.ig1.en_addr, + sensor[adata->index].drdy_irq.ig1.en_mask, 1, + (int)enable);
+ if (err < 0)
+ goto acc_set_dataready_irq_error;
+ }
+
+ if (sensor[adata->index].drdy_irq.ig1.latching_mask_addr > 0) {
+ err =3D acc_write_data_with_mask(indio_dev,
+ sensor[adata->index].drdy_irq.ig1.latching_mask_add=
r,
+ sensor[adata->index].drdy_irq.ig1.latching_mask, 1,
+ (int)enable);
+ if (err < 0)
+ goto acc_set_dataready_irq_error;
+ }
+
+ err =3D acc_write_data_with_mask(indio_dev,
+ sensor[adata->index].drdy_irq.addr,
+ sensor[adata->index].drdy_irq.mask, 1, (int)enable)=
;
+ if (err < 0)
+ goto acc_set_dataready_irq_error;
+
+ return 0;
+
+acc_set_dataready_irq_error:
+ return -EIO;
+}
+
+static int set_bdu(struct iio_dev *indio_dev, struct bdu *bdu, u8 value) +{
+ int err;
+
+ err =3D acc_write_data_with_mask(indio_dev,
+ bdu->addr, + bdu->mask, + 1, + value);
+
+ return err;
+}
+
+static int set_odr(struct iio_dev *indio_dev,
+ struct odr_available *odr_available=
)
+{
+ int err;
+ struct acc_data *adata =3D iio_priv(indio_dev);
+
+ if ((sensor[adata->index].odr.addr =3D=3D sensor[adata->index].pw.a=
ddr) &&
+ (sensor[adata->index].odr.mask =3D=3D
+ sensor[adata->index].pw.mask)) {
+ if (atomic_read(&adata->enabled) =3D=3D POWER_ON) {
+ err =3D acc_write_data_with_mask(indio_dev,
+ sensor[adata->index].odr.addr, + sensor[adata->index].odr.mask, + sensor[adata->index].odr.num_bit, + odr_available->value);
+ if (err < 0)
+ goto set_odr_error;
+ } else {
+ adata->odr =3D odr_available->hz;
+ err =3D 0;
+ }
+ } else {
+ err =3D acc_write_data_with_mask(indio_dev,
+ sensor[adata->index].odr.addr, + sensor[adata->index].odr.mask, + sensor[adata->index].odr.num_bit, + odr_available->value);
+ if (err < 0)
+ goto set_odr_error;
+ }
+
+set_odr_error:
+ return err;
+}
+
+static int set_enable(struct iio_dev *indio_dev, int enable) +{
+ int found, err; + u8 tmp_value; + struct odr_available *odr_out; + struct acc_data *adata =3D iio_priv(indio_dev);
+
+ odr_out =3D kzalloc(sizeof(*odr_out), GFP_KERNEL);
+ if (odr_out =3D=3D NULL) {
+ err =3D -ENOMEM;
+ goto odr_out_allocate_memory_error;
+ }
+
+ switch (enable) {
+ case POWER_ON:
+ found =3D 0; + tmp_value =3D sensor[adata->index].pw.value_on; + if ((sensor[adata->index].odr.addr =3D=3D
+ sensor[adata->index].pw.addr) &&
+ (sensor[adata->index].odr.mask =3D=3D
+ sensor[adata->index].pw.mask)) {
+ err =3D match_odr(&sensor[adata->index],
+ adata->odr, odr_out=
);
+ if (err < 0)
+ goto set_enable_error;
+ tmp_value =3D odr_out->value;
+ found =3D 1;
+ }
+ err =3D acc_write_data_with_mask(indio_dev,
+ sensor[adata->index].pw.addr, + sensor[adata->index].pw.mask, + sensor[adata->index].pw.num_bit, + tmp_value);
+ if (err < 0)
+ goto set_enable_error;
+ atomic_set(&adata->enabled, POWER_ON);
+ if (found =3D=3D 1)
+ adata->odr =3D odr_out->hz;
+ break;
+ case POWER_OFF:
+ err =3D acc_write_data_with_mask(indio_dev,
+ sensor[adata->index].pw.addr, + sensor[adata->index].pw.mask, + sensor[adata->index].pw.num_bit, + sensor[adata->index].pw.value_off);
+ if (err < 0)
+ goto set_enable_error;
+ atomic_set(&adata->enabled, POWER_OFF);
+ break;
+ default:
+ err =3D -1;
+ goto set_enable_error;
+ }
+
+set_enable_error:
+ kfree(odr_out);
+odr_out_allocate_memory_error:
+ return err;
+}
+
+static int set_fullscale(struct iio_dev *indio_dev,
+ struct fullscale_available *fs_avl)
+{
+ int err;
+ struct acc_data *adata =3D iio_priv(indio_dev);
+
+ err =3D acc_write_data_with_mask(indio_dev,
+ sensor[adata->index].fs.addr, + sensor[adata->index].fs.mask, + sensor[adata->index].fs.num_bit, + fs_avl->value); + if (err < 0) + goto set_fullscale_error; + adata->fullscale =3D fs_avl->num; + adata->gain =3D fs_avl->gain; + return err;
+
+set_fullscale_error:
+ pr_err("%s: failed to set new fullscale.\n", adata->name);
+ return err;
+}
+
+static int acc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *ch, int *val,
+ int *val2, long mas=
k)
+{
+ int err; + int data_tmp; + u8 outdata[ACC_BYTE_FOR_CHANNEL]; + struct acc_data *adata =3D iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (indio_dev->currentmode =3D=3D INDIO_BUFFER_TRIGGERED) {
+ err =3D -EBUSY;
+ } else {
+ err =3D atomic_read(&adata->enabled);
+ if (!err) {
+ err =3D -EHOSTDOWN;
+ } else {
+ err =3D adata->read_multiple_byte(adata,
+ ch->address, ACC_BYTE_FOR_CHANNEL,
+ outdata);
+ if (err < 0)
+ goto read_error;
+
+ if (ch->scan_type.endianness =3D=3D IIO_LE)
+ *val =3D (s32)(s16)(cpu_to_le16(
+ le16_to_cpu(((__le16 *)outdata)[0])=
))
+ >> ch->scan_type.shift;
+ else
+ *val =3D (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 =3D adata->gain*UG_TO_MS2; + *val =3D 0; + *val2 =3D 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 =3D iio_priv(indio_dev);
+
+ if (adata->irq_data_ready <=3D 0)
+ err =3D -EINVAL;
+ else
+ err =3D 0;
+
+ return err;
+}
+
+static void register_channels(struct iio_dev *indio_dev) +{
+ struct acc_data *adata =3D iio_priv(indio_dev);
+
+ indio_dev->channels =3D sensor[adata->index].ch;
+ indio_dev->num_channels =3D ACC_NUMBER_ALL_CHANNELS;
+}
+
+static int validate_platform_data(struct iio_dev *indio_dev) +{
+ int err; + struct odr_available *odr_out; + struct fullscale_available *fs_out; + struct acc_data *adata =3D iio_priv(indio_dev);
+
+ odr_out =3D kzalloc(sizeof(*odr_out), GFP_KERNEL);
+ if (odr_out =3D=3D NULL) {
+ err =3D -ENOMEM;
+ goto odr_out_allocate_memory_error;
+ }
+ fs_out =3D kzalloc(sizeof(*fs_out), GFP_KERNEL);
+ if (fs_out =3D=3D NULL) {+ err =3D -ENOMEM;
+ goto fs_out_allocate_memory_error;
+ }
+ err =3D match_fs(&sensor[adata->index], adata->pdata->fullscale, fs=_out);
+ if (err < 0) {
+ pr_err("%s: validate fullscale pdata failed!\n", adata->nam=
e);
+ goto validate_fullscale_error;
+ }
+ err =3D match_odr(&sensor[adata->index],
+ adata->pdata->sampling_frequency, odr_out);
+ if (err < 0) {
+ pr_err("%s: validate sampling frequency pdata failed!\n",
+ adata->name=
);
+ goto validate_sampling_error;
+ }
+ return err;
+
+validate_sampling_error:
+validate_fullscale_error:
+ kfree(fs_out);
+fs_out_allocate_memory_error:
+ kfree(odr_out);
+odr_out_allocate_memory_error:
+ return err;
+}
+
+static int set_platform_data(struct iio_dev *indio_dev) +{
+ int err;
+ struct acc_data *adata;
+
+ adata =3D iio_priv(indio_dev);
+ adata->pdata =3D kmalloc(sizeof(struct acc_platform_data), GFP_KERN=
EL);
+ if (adata->pdata =3D=3D NULL) {
+ pr_err("%s: failed to allocate memory for pdata.\n",
+ adata->name=
);
+ err =3D -ENOMEM;
+ goto pdata_malloc_error;
+ }
+ if (adata->client =3D=3D NULL) {
+ if (adata->spi->dev.platform_data !=3D 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 =3D=3D NULL) {
+ if (adata->client->dev.platform_data !=3D NULL) {
+ memcpy(adata->pdata, adata->client->dev.platform_da=
ta,
+ sizeof(struct acc_platform_data));
+ } else {
+ memcpy(adata->pdata, &acc_default_pdata,
+ sizeof(struct acc_platform_data));
+ }
+ }
+ return 0;
+
+pdata_malloc_error:
+ return err;
+}
+
+static void set_sensor_parameters(struct iio_dev *indio_dev) +{
+ struct acc_data *adata;
+
+ adata =3D iio_priv(indio_dev);
+ if (!sensor[adata->index].odr.addr)
+ sensor[adata->index].odr.addr =3D DEFAULT_ODR_ADDR;
+ if (!sensor[adata->index].odr.mask)
+ sensor[adata->index].odr.mask =3D DEFAULT_ODR_MASK;
+ if (!sensor[adata->index].odr.num_bit)
+ sensor[adata->index].odr.num_bit =3D DEFAULT_ODR_N_=
BIT;
+ if (!sensor[adata->index].pw.addr)
+ sensor[adata->index].pw.addr =3D DEFAULT_POWER_ADDR;
+ if (!sensor[adata->index].pw.mask)
+ sensor[adata->index].pw.mask =3D DEFAULT_POWER_MASK;
+ if (!sensor[adata->index].pw.num_bit)
+ sensor[adata->index].pw.num_bit =3D DEFAULT_POWER_N=_BIT;
+ if (!sensor[adata->index].pw.value_off)
+ sensor[adata->index].pw.value_off =3D DEFAULT_POWER_OFF_VAL=
UE;
+ if (!sensor[adata->index].pw.value_on)
+ sensor[adata->index].pw.value_on =3D DEFAULT_POWER_ON_VALUE=
;
+ if (!sensor[adata->index].odr.odr_avl[0].hz) {
+ sensor[adata->index].odr.odr_avl[0].hz =3D DEFAULT_ODR_AVL_=
1HZ;
+ sensor[adata->index].odr.odr_avl[0].value =3D
+ DEFAULT_ODR_AVL_1HZ_VALUE;
+ sensor[adata->index].odr.odr_avl[1].hz =3D DEFAULT_ODR_AVL_=
10HZ;
+ sensor[adata->index].odr.odr_avl[1].value =3D
+ DEFAULT_ODR_AVL_10HZ_VALUE;
+ sensor[adata->index].odr.odr_avl[2].hz =3D DEFAULT_ODR_AVL_=
25HZ;
+ sensor[adata->index].odr.odr_avl[2].value =3D
+ DEFAULT_ODR_AVL_25HZ_VALUE;
+ sensor[adata->index].odr.odr_avl[3].hz =3D DEFAULT_ODR_AVL_=
50HZ;
+ sensor[adata->index].odr.odr_avl[3].value =3D
+ DEFAULT_ODR_AVL_50HZ_VALUE;
+ sensor[adata->index].odr.odr_avl[4].hz =3D
+ DEFAULT_ODR_AVL_100HZ;
+ sensor[adata->index].odr.odr_avl[4].value =3D
+ DEFAULT_ODR_AVL_100HZ_VALUE=
;
+ sensor[adata->index].odr.odr_avl[5].hz =3D
+ DEFAULT_ODR_AVL_200HZ;
+ sensor[adata->index].odr.odr_avl[5].value =3D
+ DEFAULT_ODR_AVL_200HZ_VALUE=
;
+ sensor[adata->index].odr.odr_avl[6].hz =3D
+ DEFAULT_ODR_AVL_400HZ;
+ sensor[adata->index].odr.odr_avl[6].value =3D
+ DEFAULT_ODR_AVL_400HZ_VALUE=
;
+ sensor[adata->index].odr.odr_avl[7].hz =3D
+ DEFAULT_ODR_AVL_1600HZ;
+ sensor[adata->index].odr.odr_avl[7].value =3D
+ DEFAULT_ODR_AVL_1600HZ_VALU=
E;
+ }
+ if (!sensor[adata->index].fs.addr)
+ sensor[adata->index].fs.addr =3D DEFAULT_FS_ADDR;
+ if (!sensor[adata->index].fs.mask)
+ sensor[adata->index].fs.mask =3D DEFAULT_FS_MASK;
+ if (!sensor[adata->index].fs.num_bit)
+ sensor[adata->index].fs.num_bit =3D DEFAULT_FS_N_BIT;
+ if (!sensor[adata->index].fs.fs_avl[0].num) {
+ sensor[adata->index].fs.fs_avl[0].num =3D DEFAULT_FS_AVL_2_=
NUM;
+ sensor[adata->index].fs.fs_avl[0].value =3D
+ DEFAULT_FS_AVL_2_VALUE;
+ sensor[adata->index].fs.fs_avl[0].gain =3D
+ DEFAULT_FS_AVL_2_GAIN;
+ sensor[adata->index].fs.fs_avl[1].num =3D DEFAULT_FS_AVL_4_=
NUM;
+ sensor[adata->index].fs.fs_avl[1].value =3D
+ DEFAULT_FS_AVL_4_VALUE;
+ sensor[adata->index].fs.fs_avl[1].gain =3D
+ DEFAULT_FS_AVL_4_GAIN;
+ sensor[adata->index].fs.fs_avl[2].num =3D DEFAULT_FS_AVL_8_=
NUM;
+ sensor[adata->index].fs.fs_avl[2].value =3D
+ DEFAULT_FS_AVL_8_VALUE;
+ sensor[adata->index].fs.fs_avl[2].gain =3D
+ DEFAULT_FS_AVL_8_GAIN;
+ sensor[adata->index].fs.fs_avl[3].num =3D DEFAULT_FS_AVL_16=_NUM;
+ sensor[adata->index].fs.fs_avl[3].value =3D
+ DEFAULT_FS_AVL_16_VALUE;
+ sensor[adata->index].fs.fs_avl[3].gain =3D
+ DEFAULT_FS_AVL_16_GAIN;
+ }
+ if (!sensor[adata->index].data.addr[X_AXIS][DATA_LOW])
+ sensor[adata->index].data.addr[X_AXIS][DATA_LOW] =3D
+ (DEFAULT_OUT_X_L);
+ if (!sensor[adata->index].data.addr[X_AXIS][DATA_HIGH])
+ sensor[adata->index].data.addr[X_AXIS][DATA_HIGH] =3D
+ DEFAULT_OUT_X_H;
+ if (!sensor[adata->index].data.addr[Y_AXIS][DATA_LOW])
+ sensor[adata->index].data.addr[Y_AXIS][DATA_LOW] =3D
+ (DEFAULT_OUT_Y_L);
+ if (!sensor[adata->index].data.addr[Y_AXIS][DATA_HIGH])
+ sensor[adata->index].data.addr[Y_AXIS][DATA_HIGH] =3D
+ DEFAULT_OUT_Y_H;
+ if (!sensor[adata->index].data.addr[Z_AXIS][DATA_LOW])
+ sensor[adata->index].data.addr[Z_AXIS][DATA_LOW] =3D
+ (DEFAULT_OUT_Z_L);
+ if (!sensor[adata->index].data.addr[Z_AXIS][DATA_HIGH])
+ sensor[adata->index].data.addr[Z_AXIS][DATA_HIGH] =3D
+ DEFAULT_OUT_Z_H;
+ if (!sensor[adata->index].drdy_irq.addr)
+ sensor[adata->index].drdy_irq.addr =3D DEFAULT_DRDY_IRQ_ADD=
R;
+ if (!sensor[adata->index].drdy_irq.mask)
+ sensor[adata->index].drdy_irq.mask =3D DEFAULT_DRDY_IRQ_MAS=
K;
+ if (!sensor[adata->index].data.bdu.addr)
+ sensor[adata->index].data.bdu.addr =3D DEFAULT_BDU_ADDR;
+ if (!sensor[adata->index].data.bdu.mask)
+ sensor[adata->index].data.bdu.mask =3D DEFAULT_BDU_MASK;
+ if (!sensor[adata->index].drdy_irq.ig1.en_addr)
+ sensor[adata->index].drdy_irq.ig1.en_addr =3D DEFAULT_IG1_E=N_ADDR;
+ if (!sensor[adata->index].drdy_irq.ig1.en_mask)
+ sensor[adata->index].drdy_irq.ig1.en_mask =3D DEFAULT_IG1_E=N_MASK;
+ if (!sensor[adata->index].drdy_irq.ig1.latching_mask_addr)
+ sensor[adata->index].drdy_irq.ig1.latching_mask_addr =3D
+ DEFAULT_IG1_LATCHING_MASK_A=
DDR;
+ if (!sensor[adata->index].drdy_irq.ig1.latching_mask)
+ sensor[adata->index].drdy_irq.ig1.latching_mask =3D
+ DEFAULT_IG1_LATCHING_MASK;
+}
+
+static int check_device_list(struct iio_dev *indio_dev, u8 wai) +{
+ int i, sensor_length, found;
+ struct acc_data *adata;
+
+ adata =3D iio_priv(indio_dev);
+ found =3D 0;
+ sensor_length =3D ARRAY_SIZE(sensor);
+ for (i =3D 0; i < sensor_length; i++) {+ if (sensor[i].wai =3D=3D wai) {
+ found =3D 1;
+ break;
+ }
+ }
+ if (found !=3D 1)
+ goto check_device_error;
+ adata->index =3D i;
+ return i;
+
+check_device_error:
+ pr_err("%s: device not supported -> wai (0x%x).\n", adata->name, wa=
i);
+ return -ENODEV;
+}
+
+static int get_wai_device(struct iio_dev *indio_dev, u8 reg_addr, u8 *value)
+{
+ int ret; + u8 buf; + struct acc_data *adata; + + adata =3D iio_priv(indio_dev); + buf =3D reg_addr; + ret =3D adata->read_byte(adata, reg_addr); + if (ret < 0)
+ goto read_byte_wai_error;
+ *value =3D ret;
+ return (int)ret;
+
+read_byte_wai_error:
+ pr_err("%s: failed to read wai register (0x%x).\n",
+ adata->name, reg_ad=
dr);
+ return -1;
+}
+
+static ssize_t sysfs_set_sampling_frequency(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size=
)
+{
+ int err; + unsigned long freq; + struct odr_available *odr_out; + struct iio_dev *indio_dev =3D dev_get_drvdata(dev); + struct acc_data *adata =3D iio_priv(indio_dev);
+
+ err =3D kstrtoul(buf, 10, &freq);
+ if (err) {
+ pr_err("%s: input is not a number! (errn %d).\n",
+ adata->name, err);
+ goto sysfs_set_sampling_frequency_error;
+ }
+
+ mutex_lock(&indio_dev->mlock); + odr_out =3D kzalloc(sizeof(*odr_out), GFP_KERNEL); + if (odr_out =3D=3D NULL)
+ goto odr_out_allocate_memory_error;
+
+ err =3D match_odr(&sensor[adata->index], (int)freq, odr_out);
+ if (err < 0)
+ goto sysfs_set_sampling_frequency_error;
+ err =3D set_odr(indio_dev, odr_out);
+ if (err < 0) {
+ pr_err("%s: failed to set sampling frequency to %d.\n",
+ adata->name, (int)f=
req);
+ goto sysfs_set_sampling_frequency_error;
+ } + adata->odr =3D odr_out->hz; + kfree(odr_out);
+
+odr_out_allocate_memory_error:
+ mutex_unlock(&indio_dev->mlock);
+sysfs_set_sampling_frequency_error:
+ return size;
+}
+
+static ssize_t sysfs_get_sampling_frequency(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret; + struct iio_dev *indio_dev =3D dev_get_drvdata(dev); + struct acc_data *adata =3D iio_priv(indio_dev);
+
+ ret =3D sprintf(buf, "%d\n", adata->odr);
+
+ return ret;
+}
+
+static ssize_t sysfs_set_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size=
)
+{
+ int err; + unsigned long en; + struct iio_dev *indio_dev =3D dev_get_drvdata(dev); + struct acc_data *adata =3D iio_priv(indio_dev);
+
+ err =3D kstrtoul(buf, 10, &en);
+ if (err) {
+ pr_err("%s: input is not a number! (errn %d).\n",
+ adata->name, err);
+ goto set_enable_error;
+ }
+
+ mutex_lock(&indio_dev->mlock); + err =3D set_enable(indio_dev, (int)en); + if (err < 0)
+ pr_err("%s: failed to set enable to %d.\n",
+ adata->name, (int)e=
n);
+ mutex_unlock(&indio_dev->mlock);
+
+set_enable_error:
+ return size;
+}
+
+static ssize_t sysfs_get_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret; + int status; + struct iio_dev *indio_dev =3D dev_get_drvdata(dev); + struct acc_data *adata =3D iio_priv(indio_dev);
+
+ status =3D atomic_read(&adata->enabled);
+ ret =3D sprintf(buf, "%d\n", status);
+
+ return ret;
+}
+
+static ssize_t sysfs_get_fullscale(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret; + struct iio_dev *indio_dev =3D dev_get_drvdata(dev); + struct acc_data *adata =3D iio_priv(indio_dev);
+
+ ret =3D sprintf(buf, "%d\n", adata->fullscale);
+
+ return ret;
+}
+
+static ssize_t sysfs_set_fullscale(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size=
)
+{
+ int err; + unsigned long fs; + struct fullscale_available *fs_out; + struct iio_dev *indio_dev =3D dev_get_drvdata(dev); + struct acc_data *adata =3D iio_priv(indio_dev);
+
+ err =3D kstrtoul(buf, 10, &fs);
+ if (err) {
+ pr_err("%s: input is not a number! (errn %d).\n",
+ adata->name, err);
+ goto set_fullscale_error;
+ }
+
+ mutex_lock(&indio_dev->mlock); + fs_out =3D kzalloc(sizeof(*fs_out), GFP_KERNEL); + if (fs_out =3D=3D NULL)
+ goto fs_out_allocate_memory_error;
+
+ err =3D match_fs(&sensor[adata->index], fs, fs_out);
+ if (err < 0) {
+ pr_err("%s: input is not a valid fullscale! (errn %d).\n",
+ adata->name, err);
+ goto match_fullscale_error;
+ }
+ err =3D set_fullscale(indio_dev, fs_out);
+ if (err < 0) {+ pr_err("%s: failed to set new fullscale. (errn %d).\n",
+ adata->name, err);
+ }
+
+match_fullscale_error:
+ kfree(fs_out);
+fs_out_allocate_memory_error:
+ mutex_unlock(&indio_dev->mlock);
+set_fullscale_error:
+ return size;
+}
+
+static ssize_t sysfs_fullscale_available(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i, n, len;
+ char tmp[4];
+ char fullscale[30] =3D "";
+ struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+ struct acc_data *adata =3D iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ n =3D ARRAY_SIZE(sensor[adata->index].fs.fs_avl);
+ for (i =3D 0; i < n; i++) {+ if (sensor[adata->index].fs.fs_avl[i].num !=3D 0) {
+ len =3D strlen(&fullscale[0]);
+ sprintf(tmp, "%d ",
+ sensor[adata->index].fs.fs_avl[i].num);
+ strcpy(&fullscale[len], tmp);
+ } else
+ break;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ return sprintf(buf, "%s\n", fullscale);
+}
+
+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] =3D "";
+ struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+ struct acc_data *adata =3D iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ n =3D ARRAY_SIZE(sensor[adata->index].odr.odr_avl);
+ for (i =3D 0; i < n; i++) {+ if (sensor[adata->index].odr.odr_avl[i].hz !=3D 0) {
+ len =3D 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);
+}
+
+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);
+
+static struct attribute *acc_attributes[] =3D {
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_fullscale_available.dev_attr.attr, + &iio_dev_attr_fullscale.dev_attr.attr, + &iio_dev_attr_enable.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, + NULL,
+};
+
+static const struct attribute_group acc_attribute_group =3D {
+ .attrs =3D acc_attributes,
+};
+
+static const struct iio_info acc_info =3D {
+ .driver_module =3D THIS_MODULE, + .attrs =3D &acc_attribute_group, + .read_raw =3D &acc_read_raw,
+};
+
+static int init_sensor(struct iio_dev *indio_dev) +{
+ int err; + struct odr_available *odr_out; + struct fullscale_available *fs_out; + struct acc_data *adata =3D iio_priv(indio_dev);
+
+ odr_out =3D kzalloc(sizeof(*odr_out), GFP_KERNEL);
+ if (odr_out =3D=3D NULL)
+ goto odr_out_allocate_memory_error;
+ fs_out =3D kzalloc(sizeof(*fs_out), GFP_KERNEL);
+ if (fs_out =3D=3D NULL)
+ goto fs_out_allocate_memory_error;
+
+ adata->fullscale =3D adata->pdata->fullscale; + adata->odr =3D adata->pdata->sampling_frequency; + set_enable(indio_dev, POWER_OFF); + match_fs(&sensor[adata->index], adata->fullscale, fs_out); + err =3D set_fullscale(indio_dev, fs_out); + if (err < 0) + goto init_error; + match_odr(&sensor[adata->index], adata->odr, odr_out); + err =3D set_odr(indio_dev, odr_out); + if (err < 0)
+ goto init_error;
+ err =3D set_bdu(indio_dev, &sensor[adata->index].data.bdu,
+ (u8)DEFAULT_POWER_ON_VALUE)=
;
+ if (err < 0)
+ goto init_error;
+ kfree(odr_out);
+ kfree(fs_out);
+
+ return 0;
+
+init_error:
+ kfree(fs_out);
+fs_out_allocate_memory_error:
+ kfree(odr_out);
+odr_out_allocate_memory_error:
+ return -EIO;
+}
+
+int acc_iio_default(struct iio_dev *indio_dev)
+{
+ int err; + u8 wai; + struct acc_data *adata =3D iio_priv(indio_dev); + + mutex_init(&adata->slock); + indio_dev->modes =3D INDIO_DIRECT_MODE; + indio_dev->info =3D &acc_info;
+
+ err =3D get_wai_device(indio_dev, DEFAULT_WAI_ADDRESS, &wai);
+ if (err < 0)
+ goto get_wai_error;
+ err =3D check_device_list(indio_dev, wai);
+ if (err < 0)
+ goto check_device_list_error;
+ set_sensor_parameters(indio_dev); + err =3D set_platform_data(indio_dev); + if (err < 0)
+ goto set_platform_data_error;
+ err =3D validate_platform_data(indio_dev);
+ if (err < 0)
+ goto validate_platform_data_error;
+ register_channels(indio_dev);
+
+ err =3D init_sensor(indio_dev);
+ if (err < 0)
+ goto init_sensor_error;
+
+ if (sensor[adata->index].wai =3D=3D S5_WAI_EXP)
+ adata->multiread_bit =3D 0;
+ else
+ adata->multiread_bit =3D 1;
+
+ err =3D acc_check_irq(indio_dev);
+ if (err < 0)
+ goto gpio_check_error;
+ err =3D acc_allocate_ring(indio_dev);
+ if (err < 0)
+ goto acc_allocate_ring_error;
+
+ err =3D acc_probe_trigger(indio_dev);
+ if (err < 0)
+ goto acc_probe_trigger_error;
+
+ err =3D 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 =3D 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 <[email protected]>"); +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
drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c new file +29 -0
@@ -0,0 +1,124 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* 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 *d=
ata)
+{
+ if (adata->multiread_bit !=3D 0)
+ reg_addr |=3D 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 dat=
a)
+{
+ 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);
+ indio_dev =3D iio_device_alloc(sizeof(*adata));
+ if (indio_dev =3D=3D NULL) {+ err =3D -ENOMEM;
+ goto iio_device_alloc_error;
+ }
+
+ adata =3D iio_priv(indio_dev); + adata->client =3D client; + i2c_set_clientdata(client, indio_dev);
+
+ indio_dev->dev.parent =3D &client->dev;
+ indio_dev->name =3D client->name;
+
+ adata->read_byte =3D acc_i2c_read_byte; + adata->write_byte =3D acc_i2c_write_byte; + adata->read_multiple_byte =3D acc_i2c_read_multiple_byte; + adata->name =3D &client->name[0]; + adata->irq_data_ready =3D &client->irq;
+
+ err =3D 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_i2c_remove(struct i2c_client *client) +{
+ int err;
+ struct iio_dev *indio_dev =3D i2c_get_clientdata(client);
+
+ err =3D acc_iio_remove(indio_dev);
+ iio_device_free(indio_dev);
+ return err;
+}
+
+static const struct i2c_device_id acc_id_table[] =3D {
+ { LSM303DLH_ACC_IIO_DEV_NAME, 0 },
+ { LSM303DLHC_ACC_IIO_DEV_NAME, 0 },
+ { LIS3DH_ACC_IIO_DEV_NAME, 0 },
+ { LSM330D_ACC_IIO_DEV_NAME, 0 },
+ { LSM330DL_ACC_IIO_DEV_NAME, 0 },
+ { LSM330DLC_ACC_IIO_DEV_NAME, 0 },
+ { LSM303D_ACC_IIO_DEV_NAME, 0 },
+ { LSM9DS0_ACC_IIO_DEV_NAME, 0 },
+ { LIS3LV02DL_ACC_IIO_DEV_NAME, 0 },
+ { LSM303DL_ACC_IIO_DEV_NAME, 0 },
+ { LSM303DLM_ACC_IIO_DEV_NAME, 0 },
+ { LSM330_ACC_IIO_DEV_NAME, 0 },
+ {},+};
+
+MODULE_DEVICE_TABLE(i2c, acc_id_table);
+
+static struct i2c_driver acc_driver =3D {
+ .driver =3D {
+ .owner =3D THIS_MODULE,
+ .name =3D "STMicroelectronics i2c accelerometers",
+ }, + .probe =3D acc_i2c_probe, + .remove =3D __devexit_p(acc_i2c_remove), + .id_table =3D acc_id_table,
+};
+
+module_i2c_driver(acc_driver);
+
+MODULE_AUTHOR("Denis Ciocca <[email protected]>"); +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
drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c new file +59 -0
59 lines not on this page · raw message
ata)
+{
+ struct spi_message msg;
+ int err;
+ u8 tx;
+
+ struct spi_transfer xfers[] =3D {
+ {
+ .tx_buf =3D &tx,
+ .bits_per_word =3D 8,
+ .len =3D 1,
+ .cs_change =3D 0,
+ .delay_usecs =3D 10,
+ },
+ {
+ .rx_buf =3D data,
+ .bits_per_word =3D 8,
+ .len =3D len,
+ .cs_change =3D 0,
+ .delay_usecs =3D 10,+ }
+ };
+
+ mutex_lock(&adata->slock);
+ if (adata->multiread_bit !=3D 0)
+ tx =3D reg_addr | ACC_SPI_MULTIREAD;
+ else
+ tx =3D reg_addr | ACC_SPI_READ;
+
+ spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + err =3D spi_sync(adata->spi, &msg); + mutex_unlock(&adata->slock);
+
+ return err;
+}
+
+static inline s32 acc_spi_write_byte(struct acc_data *adata,
+ u8 reg_addr, u8 dat=
a)
+{
+ struct spi_message msg;
+ int err;
+ u8 tx[2];
+
+ struct spi_transfer xfers[] =3D {
+ {
+ .tx_buf =3D tx,
+ .bits_per_word =3D 8,
+ .len =3D 2,
+ .cs_change =3D 0,
+ .delay_usecs =3D 10,
+ }
+ };
+
+ mutex_lock(&adata->slock);
+ tx[0] =3D reg_addr;
+ tx[1] =3D data;
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfers[0], &msg);
+ err =3D spi_sync(adata->spi, &msg);
+ mutex_unlock(&adata->slock);+
+ return err;
+}
+
+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);
+ indio_dev =3D iio_device_alloc(sizeof(*adata));
+ if (indio_dev =3D=3D NULL) {+ err =3D -ENOMEM;
+ goto iio_device_alloc_error;
+ }
+
+ adata =3D iio_priv(indio_dev); + adata->spi =3D client; + spi_set_drvdata(client, indio_dev);
+
+ indio_dev->dev.parent =3D &client->dev;
+ indio_dev->name =3D client->modalias;
+
+ adata->read_byte =3D acc_spi_read_byte; + adata->write_byte =3D acc_spi_write_byte; + adata->read_multiple_byte =3D acc_spi_read_multiple_byte; + adata->name =3D &client->modalias[0]; + adata->irq_data_ready =3D &client->irq;
+
+ /* dummy read */
+ adata->read_byte(adata, 0x0f);
+
+ err =3D 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 =3D spi_get_drvdata(spi);
+
+ err =3D acc_iio_remove(indio_dev);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id acc_id_table[] =3D {
+ { LSM303DLH_ACC_IIO_DEV_NAME, 0 },
+ { LSM303DLHC_ACC_IIO_DEV_NAME, 0 },
+ { LIS3DH_ACC_IIO_DEV_NAME, 0 },
+ { LSM330D_ACC_IIO_DEV_NAME, 0 },
+ { LSM330DL_ACC_IIO_DEV_NAME, 0 },
+ { LSM330DLC_ACC_IIO_DEV_NAME, 0 },
+ { LSM303D_ACC_IIO_DEV_NAME, 0 },
+ { LSM9DS0_ACC_IIO_DEV_NAME, 0 },
+ { LIS3LV02DL_ACC_IIO_DEV_NAME, 0 },
+ { LSM303DL_ACC_IIO_DEV_NAME, 0 },
+ { LSM303DLM_ACC_IIO_DEV_NAME, 0 },
+ { LSM330_ACC_IIO_DEV_NAME, 0 },
+ {},+};
+
+MODULE_DEVICE_TABLE(spi, acc_id_table);
+
+static struct spi_driver acc_driver =3D {
+ .driver =3D {
+ .owner =3D THIS_MODULE,
+ .name =3D "STMicroelectronics spi accelerometers",
+ }, + .probe =3D acc_spi_probe, + .remove =3D __devexit_p(acc_spi_remove), + .id_table =3D acc_id_table,
+};
+
+module_spi_driver(acc_driver);
+
+MODULE_AUTHOR("Denis Ciocca <[email protected]>"); +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
drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c new file +46 -0
46 lines not on this page · raw message
);
+ goto iio_trigger_alloc_error;
+ }
+
+ err =3D request_threaded_irq(*adata->irq_data_ready,
+ acc_data_ready_trig_poll, + NULL, + IRQF_TRIGGER_RISING, + "IRQ_data_ready", + adata->trig);
+ 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 =3D indio_dev; + adata->trig->ops =3D &iio_acc_trigger_ops;
+
+ if (adata->client !=3D NULL)
+ adata->trig->dev.parent =3D &adata->client->dev;
+ else
+ adata->trig->dev.parent =3D &adata->spi->dev;
+
+ err =3D 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 =3D 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;
+}
+
+int acc_remove_trigger(struct iio_dev *indio_dev) +{
+ struct acc_data *adata =3D iio_priv(indio_dev);
+
+ iio_trigger_unregister(adata->trig); + free_irq(*adata->irq_data_ready, adata->trig); + iio_trigger_free(adata->trig);
+
+ return 0;
+}
--
1.7.0.4
From 1fe8b75e0ec1197781c559935de23e116773c441 Mon Sep 17 00:00:00 2001
From: Denis Ciocca <[email protected]>
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 100644include/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
include/linux/iio/acc/STMicroelectronics_accelerometers_iio.h new file +116 -0
116 lines not on this page · raw message
--
1.7.0.4
Quoted 9 lines · Denis CIOCCA
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.
ThanksDenis
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.
Quoted 5 lines
From
c965b7f522d858a48e3bbcc723cb2ff4c00397f4Mon Sep 17 00:00:00 2001
From: Denis Ciocca <[email protected]>
Date: Mon, 8 Oct 2012 17:04:32 +0200
Subject: [PATCH 1/2] add driver
You need a better patch description than this ;)
Quoted 9 lines
--- .../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(-)
Quoted 10 lines
create mode 100644
drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c
create mode 100644drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
create mode 100644drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
create mode 100644drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
create mode 100644drivers/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?
Quoted 70 lines
diff --git
a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.cb/drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.cnew file mode 100644
index 0000000..44e3f3ddrivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c new file +61
-0@@ -0,0 +1,155 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* 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;
drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c line 71
Quoted 10 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c new file +10
-0+ 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.
Quoted 4 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c new file +4
-0+kfree (rx_array); +return i*sizeof (data[0 ]); +} +
[...]
Quoted 26 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c new file +26
-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 ;
drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c
Quoted 10 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c new file +10
-0+ 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.
Quoted 23 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_buffer.c new file +15
-0+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.cb/drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.cnew file mode 100644
index 0000000..f9ccbf6drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +0
-0@@ -0,0 +1,1495 @@
[...]
Quoted 8 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +8
-0+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;
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
Quoted 10 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +10
-0+} 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.
Quoted 3 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +3
-0+ .hz = S2_ODR_AVL_3HZ, + .value = S2_ODR_AVL_3HZ_VALUE, + },
[...]
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
Quoted 11 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +11
-0+ +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
Quoted 1 line
+ if (mask/j > 0)
Missing space
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
Quoted 10 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +10
-0+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
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
Quoted 3 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +3
-0+ 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...
Quoted 5 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +5
-0+ +i2c_write_data_with_mask_error: +return err; +} +
[...]
Quoted 7 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +7
-0+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 ];
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
Quoted 10 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +10
-0+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?
Quoted 28 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +28
-0+ }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:
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
Quoted 10 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +10
-0+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?
Quoted 33 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +33
-0+ 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 ));
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
Quoted 10 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +10
-0+ }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.
Quoted 106 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +106
-0+ +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 ;
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
Quoted 10 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +10
-0+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.
Quoted 2 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +2
-0+} +
[...]
Quoted 13 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +13
-0+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 ) {
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c
Quoted 10 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +10
-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
Quoted 11 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +11
-0+} + +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,
Quoted 2 lines
sysfs_get_sampling_frequency,
+sysfs_set_sampling_frequency);
Any non-standard attributes need documentation.
Quoted 1 line
+int
acc_iio_default(struct iio_dev *indio_dev)
I'd call this _probe instead of _default.
Quoted 130 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_core.c new file +82
-0+{ +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 <[email protected]>" ); +MODULE_DESCRIPTION ("STMicroelectronics accelerometers driver" ); +MODULE_LICENSE ("GPL v2" );diff --git
a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.cb/drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.cnew file mode 100644
index 0000000..f443e52drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c new file +40
-0@@ -0,0 +1,124 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* 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);
drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c line 50
Quoted 10 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c new file +10
-0+} + +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
Quoted 7 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c new file +7
-0+ indio_dev =iio_device_alloc (sizeof (*adata)); +if (indio_dev ==NULL ) { + err = -ENOMEM ; + goto iio_device_alloc_error; + } + + adata =iio_priv (indio_dev);
drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c line 67
Quoted 10 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c new file +10
-0+ 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;
drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c line 68
Quoted 1 line
drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c new file +1
-0+ adata->irq_data_ready = &client->irq;
Why do you take a pointer here?
drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c line 81
Quoted 13 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c new file +13
-0+ + 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
drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c line 93
Quoted 12 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c new file +12
-0+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
Quoted 13 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c new file +5
-0+module_i2c_driver (acc_driver); + +MODULE_AUTHOR ("Denis Ciocca <[email protected]>" ); +MODULE_DESCRIPTION ("STMicroelectronics accelerometers i2c driver" ); +MODULE_LICENSE ("GPL v2" );diff --git
a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.cb/drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.cnew file mode 100644
index 0000000..f1ac211drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c new file +0
-0@@ -0,0 +1,209 @@
[...]
drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
Quoted 8 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c new file +8
-0+ +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.
Quoted 7 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c new file +7
-0+ indio_dev =iio_device_alloc (sizeof (*adata)); +if (indio_dev ==NULL ) { + err = -ENOMEM ; + goto iio_device_alloc_error; + } + + adata =iio_priv (indio_dev);
drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
Quoted 10 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c new file +10
-0+ 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;
drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
Quoted 3 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c new file +3
-0+ adata->irq_data_ready = &client->irq; + +/* dummy read */
It would be good if the comment mentioned why a dummy read is necessary.
Quoted 10 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c new file +10
-0+ 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);
drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
Quoted 10 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c new file +10
-0+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.
Quoted 1 line
+
iio_device_free(indio_dev);
Why not put the iio_device_free in acc_iio_remove?
Quoted 4 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c new file +4
-0+ +return 0 ; +} +
[...]
Quoted 1 line
+
No newline here
drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
Quoted 6 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c new file +6
-0+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
drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c line 46
Quoted 6 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c new file +6
-0+ }, + .probe = acc_spi_probe, + .remove = __devexit_p(acc_spi_remove), + .id_table = acc_id_table, +}; +
No newline here
Quoted 28 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c new file +5
-0+module_spi_driver (acc_driver); + +MODULE_AUTHOR ("Denis Ciocca <[email protected]>" ); +MODULE_DESCRIPTION ("STMicroelectronics accelerometers spi driver" ); +MODULE_LICENSE ("GPL v2" );diff --git
a/drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.cb/drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.cnew file mode 100644
index 0000000..843af4cdrivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c new file +15
-0@@ -0,0 +1,96 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* 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>
drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c line 25
Quoted 10 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c new file +10
-0+#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.
Quoted 20 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c new file +20
-0+ +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 ;
drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c line 55
Quoted 10 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c new file +10
-0+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.
Quoted 30 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.c new file +30
-0+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; +}
[...]
Quoted 34 lines
--
1.7.0.4From
1fe8b75e0ec1197781c559935de23e116773c441Mon Sep 17 00:00:00 2001
From: Denis Ciocca <[email protected]>
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.hdiff --git
a/include/linux/iio/acc/STMicroelectronics_accelerometers_iio.hb/include/linux/iio/acc/STMicroelectronics_accelerometers_iio.hnew file mode 100644
index 0000000..e6f18adinclude/linux/iio/acc/STMicroelectronics_accelerometers_iio.h new file +10
-0@@ -0,0 +1,116 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* v. 1.0.0 +* Licensed under the GPL-2. +*/ +
include/linux/iio/acc/STMicroelectronics_accelerometers_iio.h line 62
Quoted 10 lines
include/linux/iio/acc/STMicroelectronics_accelerometers_iio.h new file +9
-0+ +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)
Quoted 25 lines
include/linux/iio/acc/STMicroelectronics_accelerometers_iio.h new file +25
-0+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; + +}; +
[...]
include/linux/iio/acc/STMicroelectronics_accelerometers_iio.h line 116
Quoted 2 lines
include/linux/iio/acc/STMicroelectronics_accelerometers_iio.h new file +2
-0+ +#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/
Quoted 9 lines
Quoted 6 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
create mode 100644drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
create mode 100644drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.cthe file names are a bit excessive in my opinion. How about st_accel_{buffer,core,i2c,...}.c?
drivers/iio/accel/st/iio_spi.c ?
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Quoted 11 lines · Pavel Machek
Quoted 9 lines
Quoted 6 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
create mode 100644drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
create mode 100644drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.cthe file names are a bit excessive in my opinion. How about st_accel_{buffer,core,i2c,...}.c?
drivers/iio/accel/st/iio_spi.c?
Maybe. I'd still vote for st_accel though, since I think it is most descriptive. Since trigger and buffer should go down to a few lines it probably makes sense to put their content into the core file and I don't think we need a extra directory for the driver then.
- Lars
Quoted 11 lines · Lars-Peter Clausen
Quoted 10 lines · Pavel Machek
Quoted 9 lines
Quoted 6 lines
drivers/iio/accel/STMicroelectronics_accelerometers_iio_i2c.c
create mode 100644drivers/iio/accel/STMicroelectronics_accelerometers_iio_spi.c
create mode 100644drivers/iio/accel/STMicroelectronics_accelerometers_iio_trigger.cthe file names are a bit excessive in my opinion. How about st_accel_{buffer,core,i2c,...}.c?
Quoted 9 lines
Quoted 1 line
drivers/iio/accel/st/iio_spi.c?Maybe. I'd still vote for
st_accelthough, since I think it is most descriptive. Since trigger and buffer should go down to a few lines it probably
makes sense to put their content into the core file and I don't think we need a
extra directory for the driver then.
Agreed. Either is an improvement but Lars' is more in keeping with other drivers. Doesn't need its own dir. Whilst this one is large it isn't exceptionally so.
Quoted 1 line
- Lars
Signature · 1 line
Sent from my Android phone with K-9 Mail. Please excuse my brevity.
Hi guys,
according to your requests I have modified my driver. Only one things about this function:
static int acc_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
You tell to me:
Quoted 4 lines
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 upavailable_scan_maskscorrectly it will automatically do the right thing.
I agree with you for the postprocessed samples part, but I don't understand very well the second one.
I saw the other drivers, if I check only the available_scan_masks variable I don't know which channels are actives and which not.
I will read every time all data channels, but I choose which channel put in the buffer.
Thanks,
Denis
Quoted 1 line
From
0917bcc1e22462db0469709d1020b5378d91959cMon Sep 17 00:00:00 2001
From: userid <userid@MacBookAir.(none)>
Date: Sun, 14 Oct 2012 00:05:54 +0200
Subject: [PATCH] Add STMicroelectronics accelerometers driver to support I2C
and SPI devices.
--- drivers/iio/accel/Kconfig | 32 + drivers/iio/accel/Makefile | 6 + drivers/iio/accel/ST_accel_buffer.c | 122 +++ drivers/iio/accel/ST_accel_core.c | 1335 ++++++++++++++++++++++++++ drivers/iio/accel/ST_accel_i2c.c | 165 ++++ drivers/iio/accel/ST_accel_spi.c | 236 +++++ drivers/iio/accel/ST_accel_trigger.c | 87 ++ include/linux/iio/accel/ST_accel.h | 120 +++ include/linux/platform_data/ST_accel_pdata.h | 34 + 9 files changed, 2137 insertions(+) create mode 100644 drivers/iio/accel/ST_accel_buffer.c create mode 100644 drivers/iio/accel/ST_accel_core.c create mode 100644 drivers/iio/accel/ST_accel_i2c.c create mode 100644 drivers/iio/accel/ST_accel_spi.c create mode 100644 drivers/iio/accel/ST_accel_trigger.c create mode 100644 include/linux/iio/accel/ST_accel.h create mode 100644 include/linux/platform_data/ST_accel_pdata.h
drivers/iio/accel/Kconfig +32 -0
@@ -13,4 +13,36 @@ config HID_SENSOR_ACCEL_3D Say yes here to build support for the HID SENSOR accelerometers 3D. +config ST_ACCEL_3AXIS + tristate "STMicroelectronics accelerometers 3-Axis Driver" + depends on (I2C || SPI) + help + Say yes here to build support for STMicroelectronics accelerometers. + +choice + prompt "Bus type" + depends on ST_ACCEL_3AXIS + +config ST_ACCEL_3AXIS_I2C + bool "support I2C bus connection" + depends on I2C && ST_ACCEL_3AXIS + help + Say yes here to build I2C support for STMicroelectronics accelerometers. + +config ST_ACCEL_3AXIS_SPI + bool "support SPI bus connection" + depends on SPI && ST_ACCEL_3AXIS + help + Say yes here to build SPI support for STMicroelectronics accelerometers. + +endchoice + +config ST_ACCEL_3AXIS_TRIGGERED_BUFFER + tristate "support triggered buffer" + depends on ST_ACCEL_3AXIS + select IIO_TRIGGERED_BUFFER + select IIO_BUFFER + help + Default trigger and buffer for STMicroelectronics accelerometers driver. + endmenu
drivers/iio/accel/Makefile +5 -0
@@ -3,3 +3,9 @@ # obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o + +ST_accel-y := ST_accel_core.o +obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) += ST_accel_i2c.o +obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) += ST_accel_spi.o +obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) += ST_accel_trigger.o
ST_accel_buffer.o
+obj-$(CONFIG_ST_ACCEL_3AXIS) += ST_accel.o
\ No newline at end of file
(quoted diff) +0 -0
b/drivers/iio/accel/ST_accel_buffer.c
new file mode 100644
index 0000000..61ee836
drivers/iio/accel/ST_accel_buffer.c new file +122 -0
122 lines not on this page · raw message
(quoted diff) +0 -0
b/drivers/iio/accel/ST_accel_core.c
new file mode 100644
index 0000000..5b30155
drivers/iio/accel/ST_accel_core.c new file +1335 -0
1,335 lines not on this page · raw message
drivers/iio/accel/ST_accel_i2c.c new file +165 -0
165 lines not on this page · raw message
drivers/iio/accel/ST_accel_spi.c new file +236 -0
236 lines not on this page · raw message
(quoted diff) +0 -0
b/drivers/iio/accel/ST_accel_trigger.c
new file mode 100644
index 0000000..4375c31
drivers/iio/accel/ST_accel_trigger.c new file +87 -0
87 lines not on this page · raw message
(quoted diff) +0 -0
b/include/linux/iio/accel/ST_accel.h
new file mode 100644
index 0000000..20d1973
include/linux/iio/accel/ST_accel.h new file +120 -0
120 lines not on this page · raw message
(quoted diff) +0 -0
b/include/linux/platform_data/ST_accel_pdata.h
new file mode 100644
index 0000000..51f7b2c
include/linux/platform_data/ST_accel_pdata.h new file +34 -0
@@ -0,0 +1,34 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + + +#ifndef ST_ACCELEROMETERS_PDATA_ACC_H +#define ST_ACCELEROMETERS_PDATA_ACC_H + +#define DEFAULT_ACCEL_ODR_AVL_100HZ 100 +#define DEFAULT_ACCEL_FS_AVL_2G 2 + +/** +* struct st_acc_platform_data - ST accel device platform data +* @fullscale: Value of fullscale used for the sensor. +* @sampling_frequency: Value of sampling frequency used for the sensor. +*/ + +struct st_acc_platform_data { +int fullscale; +int sampling_frequency; +}; + +static const struct st_acc_platform_data st_acc_default_pdata = { + .fullscale =DEFAULT_ACCEL_FS_AVL_2G , + .sampling_frequency =DEFAULT_ACCEL_ODR_AVL_100HZ , +}; + +#endif /* ST_ACCELEROMETERS_PDATA_ACC_H */
Signature · 1 line
1.7.9.5
Quoted 14 lines · Denis Ciocca
Hi guys,
according to your requests I have modified my driver. Only one things about this function:
static int
acc_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)You tell to me:
Quoted 4 lines
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 upavailable_scan_maskscorrectly it will automatically do the right thing.
Quoted 6 lines
I agree with you for the postprocessed samples part, but I don't understand very well the second one.
I saw the other drivers, if I check only theavailable_scan_masksvariable I don't know which channels are actives and which not.
I will read every time all data channels, but I choose which channel put in the buffer.
You can tell the IIO core that you can only handle certain combinations of channels which can be sampled at the same time. E.g. in you case there is only one possible combination, which is that all channels are always sampled. If in userspace only a subset of that list is selected the IIO core will take of only passing those samples on to userspace which it actually requested.
Right now you do this by hand in your driver. But if you setup scan_masks_available for you IIO device with only one scan mask which has all channels set you can skip that step and just pass the buffer containing all samples on to the IIO core and it will take care of picking those out which are actually needed. There are a few examples of this, grep for available_scan_masks to find them.
I now there are also some examples of IIO drivers which do what you did in your driver, but these are older drivers which were written when the demuxing infrastructure in the core was not in place. But the plan is to switch this over as we continue to improve the existing drivers.
Will try take a look at the new patch tomorrow.
- Lars
Quoted 4 lines · Denis Ciocca
Hi guys,
according to your requests I have modified my driver. Only one things about this function:
Hi,
you did not address all the issues addressed during the last review. Most importantly the functions, structs, defines, etc. are still no namespaced. Please add a st_acc_ prefix to your functions, etc. Add a st_acc_spi_ st_acc_i2c_ prefix to functions which are spi or i2c specific.
Quoted 24 lines
static int
acc_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)You tell to me:
Quoted 4 lines
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 upavailable_scan_maskscorrectly it will automatically do the right thing.I agree with you for the postprocessed samples part, but I don't understand very well the second one.
I saw the other drivers, if I check only theavailable_scan_masksvariable I don't know which channels are actives and which not.
I will read every time all data channels, but I choose which channel put in the buffer.Thanks,
Denis
Quoted 2 lines
From
0917bcc1e22462db0469709d1020b5378d91959cMon Sep 17 00:00:00 2001
From: userid <userid@MacBookAir.(none)>
This needs a proper from tag. See here
http://git-scm.com/book/en/Customizing-Git-Git-Configuration how to setup your name and email for git.
Quoted 3 lines
Date: Sun, 14 Oct 2012 00:05:54 +0200
Subject: [PATCH] Add STMicroelectronics accelerometers driver to support I2C
and SPI devices.
The title should start with the subsystem tag, in this case "iio:accel:" Also no fullstop at the end of the title, I'd also not include the "to support I2C and SPI devices" to keep the title short.
This also needs a short description of what the patch does.
E.g. "This patch adds generic accelerometer driver for STMicroelectronics accelerometers, currently it supports ..."
Also it is helpful for reviews to include a small changelog of what was changed in this revision of the driver.
Quoted 11 lines
--- drivers/iio/accel/Kconfig | 32 + drivers/iio/accel/Makefile | 6 + drivers/iio/accel/ST_accel_buffer.c | 122 +++ drivers/iio/accel/ST_accel_core.c | 1335 ++++++++++++++++++++++++++ drivers/iio/accel/ST_accel_i2c.c | 165 ++++ drivers/iio/accel/ST_accel_spi.c | 236 +++++ drivers/iio/accel/ST_accel_trigger.c | 87 ++ include/linux/iio/accel/ST_accel.h | 120 +++ include/linux/platform_data/ST_accel_pdata.h | 34 +
Quoted 9 lines
9 files changed, 2137 insertions(+) create mode 100644 drivers/iio/accel/ST_accel_buffer.c create mode 100644 drivers/iio/accel/ST_accel_core.c create mode 100644 drivers/iio/accel/ST_accel_i2c.c create mode 100644 drivers/iio/accel/ST_accel_spi.c create mode 100644 drivers/iio/accel/ST_accel_trigger.c create mode 100644 include/linux/iio/accel/ST_accel.h create mode 100644 include/linux/platform_data/ST_accel_pdata.h
Make the file names all lowercase
Quoted 7 lines
drivers/iio/accel/Kconfig +0
-0@@ -13,4 +13,36 @@ config HID_SENSOR_ACCEL_3D Say yes here to build support for the HID SENSOR accelerometers 3D.
drivers/iio/accel/Kconfig config HID_SENSOR_ACCEL_3D · line 24
Quoted 10 lines
drivers/iio/accel/Kconfig +9
-0+config ST_ACCEL_3AXIS + tristate "STMicroelectronics accelerometers 3-Axis Driver" + depends on (I2C || SPI) + help + Say yes here to build support for STMicroelectronics accelerometers. + +choice + prompt "Bus type" + depends on ST_ACCEL_3AXIS
I don't think there is a reason to make SPI and I2C support exclusive of each other. The driver should work just fine if both are built in.
Quoted 110 lines
drivers/iio/accel/Kconfig +23
-0+ +config ST_ACCEL_3AXIS_I2C + bool "support I2C bus connection" + depends on I2C && ST_ACCEL_3AXIS + help + Say yes here to build I2C support for STMicroelectronics accelerometers. + +config ST_ACCEL_3AXIS_SPI + bool "support SPI bus connection" + depends on SPI && ST_ACCEL_3AXIS + help + Say yes here to build SPI support for STMicroelectronics accelerometers. + +endchoice + +config ST_ACCEL_3AXIS_TRIGGERED_BUFFER + tristate "support triggered buffer" + depends on ST_ACCEL_3AXIS + select IIO_TRIGGERED_BUFFER + select IIO_BUFFER + help + Default trigger and buffer for STMicroelectronics accelerometers driver. + endmenudrivers/iio/accel/Makefile +5
-0@@ -3,3 +3,9 @@ # obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o + +ST_accel-y := ST_accel_core.o +obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) += ST_accel_i2c.o +obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) += ST_accel_spi.o +obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) += ST_accel_trigger.oST_accel_buffer.o
drivers/iio/accel/Makefile +1
-0+obj-$(CONFIG_ST_ACCEL_3AXIS) += ST_accel.odrivers/iio/accel/ST_accel_buffer.c new file +63
-0@@ -0,0 +1,122 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/module.h> +#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/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#include <linux/iio/accel/ST_accel.h> + + +#define ST_ACCEL_NUMBER_DATA_CHANNELS 3 + +static int acc_read_all (struct iio_dev *indio_dev,u8 *rx_array) +{ +int len; +struct st_acc_data *adata =iio_priv (indio_dev); + + len = adata->read_multiple_byte (adata, + indio_dev->channels[ST_ACC_SCAN_X ].address, +ST_ACC_BYTE_FOR_CHANNEL *ST_ACCEL_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; +int scan_count =bitmap_weight (indio_dev->active_scan_mask, + indio_dev->masklength); + + rx_array =kzalloc (ST_ACC_BYTE_FOR_CHANNEL * +ST_ACCEL_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 <ST_ACCEL_NUMBER_DATA_CHANNELS ; i++) { +if ((*indio_dev->active_scan_mask & mask) >0 ) {
drivers/iio/accel/ST_accel_buffer.c line 73
Quoted 10 lines
drivers/iio/accel/ST_accel_buffer.c new file +10
-0+if (indio_dev->channels[i].scan_type.endianness == +IIO_LE ) { + data[n] = (s16)(cpu_to_le16 (le16_to_cpu (( + (__le16 *)rx_array)[i]))); + n++; + }else { + data[n] = (s16)(cpu_to_be16 (be16_to_cpu (( + (__be16 *)rx_array)[i]))); + n++; + }
Don't do any endian conversion, userspace expects the data to be layed out as described by the scan_type
Quoted 26 lines
drivers/iio/accel/ST_accel_buffer.c new file +26
-0+ } + mask = mask <<1 ; + } +kfree (rx_array); +return i*sizeof (data[0 ]); +} + +static irqreturn_t acc_trigger_handler (int irq,void *p) +{ +struct iio_poll_func *pf = p; +struct iio_dev *indio_dev = pf->indio_dev; +int len =0 ; +char *data; + + data =kmalloc (indio_dev->scan_bytes,GFP_KERNEL ); +if (data ==NULL ) + goto done; +if (!bitmap_empty (indio_dev->active_scan_mask, indio_dev->masklength)) + len =acc_get_buffer_element (indio_dev, data); +else + goto done; +if (indio_dev->scan_timestamp) + *(s64 *)((u8 *)data +ALIGN (len,sizeof (s64))) = pf->timestamp; +iio_push_to_buffer (indio_dev->buffer, data); +kfree (data); +
drivers/iio/accel/ST_accel_buffer.c line 109
Quoted 10 lines
drivers/iio/accel/ST_accel_buffer.c new file +10
-0+done: +iio_trigger_notify_done (indio_dev->trig); +return IRQ_HANDLED ; +} + +static const struct iio_buffer_setup_ops acc_buf_setup_ops = { + .preenable = &iio_sw_buffer_preenable, + .postenable = &iio_triggered_buffer_postenable, + .predisable = &iio_triggered_buffer_predisable, +};
This is just the default buffer_ops for triggered buffers. Just pass NULL as the last parameter to iio_triggered_buffer_setup.
Quoted 48 lines
drivers/iio/accel/ST_accel_buffer.c new file +13
-0+ +int acc_allocate_ring (struct iio_dev *indio_dev) +{ +return iio_triggered_buffer_setup (indio_dev, &iio_pollfunc_store_time, + &acc_trigger_handler, &acc_buf_setup_ops); +} +EXPORT_SYMBOL (acc_allocate_ring); + +void acc_deallocate_ring (struct iio_dev *indio_dev) +{ +iio_triggered_buffer_cleanup (indio_dev); +} +EXPORT_SYMBOL (acc_deallocate_ring);drivers/iio/accel/ST_accel_core.c new file +28
-0@@ -0,0 +1,1335 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/mutex.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/irq.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/buffer.h> + +#include <linux/iio/accel/ST_accel.h> + +
[...]
drivers/iio/accel/ST_accel_core.c line 200
Quoted 3 lines
drivers/iio/accel/ST_accel_core.c new file +3
-0+ +struct odr_available { +int hz;
unsigned int some of the other structs memebers could also be made unsigned
Quoted 5 lines
drivers/iio/accel/ST_accel_core.c new file +4
-0+u8 value; +}; + +
drivers/iio/accel/ST_accel_core.c line 297
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+/** +* struct st_accel_sensors - ST accel 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. +* @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.
extra space
drivers/iio/accel/ST_accel_core.c line 310
Quoted 13 lines
+ */
drivers/iio/accel/ST_accel_core.c new file +12
-0+ +static const struct st_accel_sensors { +u8 wai; +struct iio_chan_spec *ch; +struct odr odr; +struct power pw; +struct fullscale fs; +struct bdu bdu; +struct data_ready_irq drdy_irq; +bool multi_read_bit; +} st_accel_sensors[] = { + {
An extra level of indention would be good after this line
Quoted 2 lines
drivers/iio/accel/ST_accel_core.c new file +2
-0+ .wai = S1_WAI_EXP, + .ch = (struct iio_chan_spec *)default_channels,
[...]
drivers/iio/accel/ST_accel_core.c line 668
Quoted 13 lines
}
drivers/iio/accel/ST_accel_core.c new file +12
-0+ +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 st_acc_data *adata =iio_priv (indio_dev); + + pos =7 ; +for (j =128 ; j >=0 ; j = j/2 ) { +if (mask/j >0 )
Missing space around the "/"
drivers/iio/accel/ST_accel_core.c
Quoted 2 lines
drivers/iio/accel/ST_accel_core.c new file +2
-0+break ; + else
I'd remove the else
drivers/iio/accel/ST_accel_core.c line 681
Quoted 11 lines
drivers/iio/accel/ST_accel_core.c new file +11
-0+ pos--; + } + + err = adata->read_byte (adata, reg_addr, &prev_data); +if (err <0 ) + goto i2c_write_data_with_mask_error; + + new_data = ((prev_data & (~mask)) | ((data << (pos-num_bit+1 )) & mask)); + err = adata->write_byte (adata, reg_addr, new_data); + +i2c_write_data_with_mask_error:
Since this is not i2c specific I'd remove the "i2c" from the label name
drivers/iio/accel/ST_accel_core.c line 691
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+return err; +} + +static int match_odr (const struct st_accel_sensors *sensor,int odr, +struct odr_available *odr_out) +{ +int n, i, ret =-1 ; + + n =ARRAY_SIZE (sensor->odr.odr_avl); +for (i =0 ; i < n; i++)
I'd put the ARRAY_SIZE in the for header, also brackets around the for body.
Quoted 7 lines
drivers/iio/accel/ST_accel_core.c new file +7
-0+if (sensor->odr.odr_avl[i].hz == odr) { + odr_out->hz = sensor->odr.odr_avl[i].hz; + odr_out->value = sensor->odr.odr_avl[i].value; + ret =0 ; +break ; + } +
drivers/iio/accel/ST_accel_core.c line 708
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+return ret; +} + +static int match_fs (const struct st_accel_sensors *sensor,int fs, +struct fullscale_available *fs_out) +{ +int n, i, ret =-1 ; + + n =ARRAY_SIZE (sensor->fs.fs_avl); +for (i =0 ; i < n; i++)
same here
Quoted 35 lines
drivers/iio/accel/ST_accel_core.c new file +35
-0+if (sensor->fs.fs_avl[i].num == fs) { + 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; + ret =0 ; +break ; + } + +return ret; +} + +int acc_set_dataready_irq (struct iio_dev *indio_dev,bool enable) +{ +int err; +struct st_acc_data *adata; + + adata =iio_priv (indio_dev); +if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr >0 ) { + err =acc_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].drdy_irq.ig1.en_addr, + st_accel_sensors[adata->index].drdy_irq.ig1.en_mask,1 , + (int )enable); +if (err <0 ) + goto acc_set_dataready_irq_error; + } + +if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr >0 ) { + err =acc_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr, + st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask,1 , + (int )enable); +if (err <0 ) + goto acc_set_dataready_irq_error; + } +
drivers/iio/accel/ST_accel_core.c line 753
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+ err =acc_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].drdy_irq.addr, + st_accel_sensors[adata->index].drdy_irq.mask,1 , (int )enable); +if (err <0 ) + goto acc_set_dataready_irq_error; + +return 0 ; + +acc_set_dataready_irq_error: +return -EIO ;
just pass on the error code in err.
drivers/iio/accel/ST_accel_core.c line 765
Quoted 12 lines
drivers/iio/accel/ST_accel_core.c new file +12
-0+} +EXPORT_SYMBOL (acc_set_dataready_irq); + +static int set_bdu (struct iio_dev *indio_dev,const struct bdu *bdu,u8 value) +{ +int err; + + err =acc_write_data_with_mask (indio_dev, + bdu->addr, + bdu->mask, +1 , + value);
I think this would fit all in one line without hitting the 80 char limit
Quoted 42 lines
drivers/iio/accel/ST_accel_core.c new file +42
-0+ +return err; +} + +static int set_odr (struct iio_dev *indio_dev, +struct odr_available *odr_available) +{ +int err; +struct st_acc_data *adata =iio_priv (indio_dev); + +if ((st_accel_sensors[adata->index].odr.addr == + st_accel_sensors[adata->index].pw.addr) && + (st_accel_sensors[adata->index].odr.mask == + st_accel_sensors[adata->index].pw.mask)) { +if (atomic_read (&adata->enabled) ==ST_ACCEL_ON ) { + err =acc_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].odr.addr, + st_accel_sensors[adata->index].odr.mask, + st_accel_sensors[adata->index].odr.num_bit, + odr_available->value); +if (err <0 ) + goto set_odr_error; + }else { + adata->odr = odr_available->hz; + err =0 ; + } + }else { + err =acc_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].odr.addr, + st_accel_sensors[adata->index].odr.mask, + st_accel_sensors[adata->index].odr.num_bit, + odr_available->value); +if (err <0 ) + goto set_odr_error; + } + +set_odr_error: +return err; +} + +static int set_enable (struct iio_dev *indio_dev,int enable) +{
drivers/iio/accel/ST_accel_core.c line 817
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+int found, err; +u8 tmp_value; +struct odr_available *odr_out; +struct st_acc_data *adata =iio_priv (indio_dev); + + odr_out =kzalloc (sizeof (*odr_out),GFP_KERNEL ); +if (odr_out ==NULL ) { + err = -ENOMEM ; + goto odr_out_allocate_memory_error; + }
Allocate odr_out on the stack
Quoted 29 lines
drivers/iio/accel/ST_accel_core.c new file +29
-0+ +switch (enable) { +case ST_ACCEL_ON : + found =0 ; + tmp_value = st_accel_sensors[adata->index].pw.value_on; +if ((st_accel_sensors[adata->index].odr.addr == + st_accel_sensors[adata->index].pw.addr) && + (st_accel_sensors[adata->index].odr.mask == + st_accel_sensors[adata->index].pw.mask)) { + err =match_odr (&st_accel_sensors[adata->index], + adata->odr, odr_out); +if (err <0 ) + goto set_enable_error; + tmp_value = odr_out->value; + found =1 ; + } + err =acc_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].pw.addr, + st_accel_sensors[adata->index].pw.mask, + st_accel_sensors[adata->index].pw.num_bit, + tmp_value); +if (err <0 ) + goto set_enable_error; +atomic_set (&adata->enabled,ST_ACCEL_ON ); +if (found ==1 ) + adata->odr = odr_out->hz; +break ; +case ST_ACCEL_OFF : + err = acc_write_data_with_mask(indio_dev,
drivers/iio/accel/ST_accel_core.c line 856
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+ st_accel_sensors[adata->index].pw.addr, + st_accel_sensors[adata->index].pw.mask, + st_accel_sensors[adata->index].pw.num_bit, + st_accel_sensors[adata->index].pw.value_off); +if (err <0 ) + goto set_enable_error; +atomic_set (&adata->enabled,ST_ACCEL_OFF ); +break ; +default : + err =-1 ;
Use a proper error value e.g. -EINVAL
Quoted 19 lines
drivers/iio/accel/ST_accel_core.c new file +18
-0+ goto set_enable_error; + } + +set_enable_error: +kfree (odr_out); +odr_out_allocate_memory_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[ST_ACC_BYTE_FOR_CHANNEL ]; +struct st_acc_data *adata =iio_priv (indio_dev);
drivers/iio/accel/ST_accel_core.c line 905
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+ +switch (mask) { +case IIO_CHAN_INFO_RAW : +if (indio_dev->currentmode ==INDIO_BUFFER_TRIGGERED ) { + err = -EBUSY ; + goto read_error; + }else { + err =atomic_read (&adata->enabled); +if (!err) { + err = -EHOSTDOWN ;
EHOSTDOWN is not a appropricate error in this case. This is not a socket!
Quoted 6 lines
drivers/iio/accel/ST_accel_core.c new file +6
-0+ goto read_error; + }else { + err = adata->read_multiple_byte (adata, + ch->address,ST_ACC_BYTE_FOR_CHANNEL , + outdata); +if (err <0 )
drivers/iio/accel/ST_accel_core.c line 921
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-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;
All these casts look kind of crazy.
Quoted 16 lines
drivers/iio/accel/ST_accel_core.c new file +16
-0+ } + } +return IIO_VAL_INT ; +case IIO_CHAN_INFO_SCALE : + data_tmp =UG_TO_MS2 (adata->gain); + *val =0 ; + *val2 = data_tmp; +return IIO_VAL_INT_PLUS_NANO ; +default : +return -EINVAL ; + } + +read_error: +return err; +} +
drivers/iio/accel/ST_accel_core.c line 947
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+static int acc_check_irq (struct iio_dev *indio_dev) +{ +int err; +struct st_acc_data *adata =iio_priv (indio_dev); + +if (*adata->irq_data_ready <=0 ) + err = -EINVAL ; +else + err =0 ; +
There is onlu one caller of this function, just inline the code int that function
drivers/iio/accel/ST_accel_core.c line 956
Quoted 9 lines
drivers/iio/accel/ST_accel_core.c new file +9
-0+return err; +} + +static void register_channels (struct iio_dev *indio_dev) +{ +struct st_acc_data *adata =iio_priv (indio_dev); + + indio_dev->channels = st_accel_sensors[adata->index].ch; + indio_dev->num_channels =ST_ACC_NUMBER_ALL_CHANNELS ;
Same here
drivers/iio/accel/ST_accel_core.c line 963
Quoted 7 lines
drivers/iio/accel/ST_accel_core.c new file +7
-0+} + +static void set_sensor_parameters (struct iio_dev *indio_dev) +{ +struct st_acc_data *adata =iio_priv (indio_dev); + + adata->multiread_bit = st_accel_sensors[adata->index].multi_read_bit;
Same here
drivers/iio/accel/ST_accel_core.c line 973
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+} + +static int check_device_list (struct iio_dev *indio_dev,u8 wai) +{ +int i, sensor_length, found; +struct st_acc_data *adata =iio_priv (indio_dev); + + found =0 ; + sensor_length =ARRAY_SIZE (st_accel_sensors); +for (i =0 ; i < sensor_length; i++) {
I'd just put the ARRAY_SIZE(...) in the for header and get rid of the extra variable.
drivers/iio/accel/ST_accel_core.c line 985
Quoted 12 lines
drivers/iio/accel/ST_accel_core.c new file +12
-0+if (st_accel_sensors[i].wai == wai) { + found =1 ; +break ; + } + } +if (found !=1 ) + goto check_device_error; + adata->index = i; +return i; + +check_device_error: +pr_err ("%s: device not supported -> wai (0x%x).\n" , adata->name, wai);
dev_err. As a rule of thumb don't use the pr_ functions in device drivers.
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+return -ENODEV ; +} + +static int get_wai_device (struct iio_dev *indio_dev,u8 reg_addr,u8 *value) +{ +int ret; +u8 buf; +struct st_acc_data *adata; + + adata =iio_priv (indio_dev);
drivers/iio/accel/ST_accel_core.c line 1005
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+ buf = reg_addr; + ret = adata->read_byte (adata, reg_addr, value); +if (ret <0 ) + goto read_byte_wai_error; + +return 0 ; + +read_byte_wai_error: +pr_err ("%s: failed to read wai register (0x%x).\n" , + adata->name, reg_addr);
dev_err, there are a few more pr_errs throughout the driver, those should be fixed up as well. Useing dev_err will also allow you to remove the name field from the st_acc_data struct
drivers/iio/accel/ST_accel_core.c line 1018
Quoted 13 lines
drivers/iio/accel/ST_accel_core.c new file +13
-0+return -EIO ; +} + +static ssize_t sysfs_set_sampling_frequency (struct device *dev, +struct device_attribute *attr,const char *buf,size_t size) +{ +int err; +unsigned long freq; +struct odr_available *odr_out; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_acc_data *adata =iio_priv (indio_dev); + + err =kstrtoul (buf,10 , &freq);
use kstrtoint instead of casting freq to int later on
drivers/iio/accel/ST_accel_core.c line 1021
Quoted 3 lines
drivers/iio/accel/ST_accel_core.c new file +3
-0+if (err) { +pr_err ("%s: input is not a number! (errn %d).\n" , + adata->name, err);
This message is just noise
drivers/iio/accel/ST_accel_core.c line 1026
Quoted 5 lines
drivers/iio/accel/ST_accel_core.c new file +5
-0+ goto sysfs_set_sampling_frequency_error; + } + +mutex_lock (&indio_dev->mlock); + odr_out =kzalloc (sizeof (*odr_out),GFP_KERNEL );
Allocate odr_out on the stack
Quoted 21 lines
drivers/iio/accel/ST_accel_core.c new file +21
-0+if (odr_out ==NULL ) + goto odr_out_allocate_memory_error; + + err =match_odr (&st_accel_sensors[adata->index], (int )freq, odr_out); +if (err <0 ) + goto sysfs_set_sampling_frequency_error; + err =set_odr (indio_dev, odr_out); +if (err <0 ) { +pr_err ("%s: failed to set sampling frequency to %d.\n" , + adata->name, (int )freq); + goto sysfs_set_sampling_frequency_error; + } + adata->odr = odr_out->hz; +kfree (odr_out); + +odr_out_allocate_memory_error: +mutex_unlock (&indio_dev->mlock); +sysfs_set_sampling_frequency_error: +return size; +} +
drivers/iio/accel/ST_accel_core.c line 1057
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+static ssize_t sysfs_get_sampling_frequency (struct device *dev, +struct device_attribute *attr,char *buf) +{ +ssize_t ret; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_acc_data *adata =iio_priv (indio_dev); + + ret =sprintf (buf,"%d\n" , adata->odr); + +return ret;
I'd just do return sprintf(...)
Quoted 9 lines
drivers/iio/accel/ST_accel_core.c new file +9
-0+} + +static ssize_t sysfs_set_enable (struct device *dev, +struct device_attribute *attr,const char *buf,size_t size) +{ +int err; +unsigned long en; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_acc_data *adata =iio_priv (indio_dev);
drivers/iio/accel/ST_accel_core.c line 1073
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +7
-0+ + err =kstrtoul (buf,10 , &en); +if (err) { +pr_err ("%s: input is not a number! (errn %d).\n" , + adata->name, err); + goto set_enable_error; + }
strtobool
Quoted 12 lines
drivers/iio/accel/ST_accel_core.c new file +12
-0+ +mutex_lock (&indio_dev->mlock); + err =set_enable (indio_dev, (int )en); +if (err <0 ) +pr_err ("%s: failed to set enable to %d.\n" , + adata->name, (int )en); +mutex_unlock (&indio_dev->mlock); + +set_enable_error: +return size; +} +
drivers/iio/accel/ST_accel_core.c line 1095
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+static ssize_t sysfs_get_enable (struct device *dev, +struct device_attribute *attr,char *buf) +{ +ssize_t ret; +int status; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_acc_data *adata =iio_priv (indio_dev); + + status =atomic_read (&adata->enabled); + ret =sprintf (buf,"%d\n" , status);
I'd just do return sprintf(...)
drivers/iio/accel/ST_accel_core.c line 1107
Quoted 12 lines
drivers/iio/accel/ST_accel_core.c new file +12
-0+ +return ret; +} + +static ssize_t sysfs_get_fullscale (struct device *dev, +struct device_attribute *attr,char *buf) +{ +ssize_t ret; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_acc_data *adata =iio_priv (indio_dev); + + ret =sprintf (buf,"%d\n" , adata->fullscale);
same here
Quoted 7 lines
drivers/iio/accel/ST_accel_core.c new file +7
-0+ +return ret; +} + +static ssize_t sysfs_set_fullscale (struct device *dev, +struct device_attribute *attr,const char *buf,size_t size) +{
drivers/iio/accel/ST_accel_core.c line 1124
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+int err; +unsigned long fs; +struct fullscale_available *fs_out; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_acc_data *adata =iio_priv (indio_dev); + + err =kstrtoul (buf,10 , &fs); +if (err) { +pr_err ("%s: input is not a number! (errn %d).\n" , + adata->name, err);
This is just noise, the caller will be notified by the error code that it's input was invalid.
drivers/iio/accel/ST_accel_core.c line 1129
Quoted 5 lines
drivers/iio/accel/ST_accel_core.c new file +5
-0+ goto set_fullscale_error; + } + +mutex_lock (&indio_dev->mlock); + fs_out =kzalloc (sizeof (*fs_out),GFP_KERNEL );
Allocate fs_out on the stack
drivers/iio/accel/ST_accel_core.c line 1136
Quoted 7 lines
drivers/iio/accel/ST_accel_core.c new file +7
-0+if (fs_out ==NULL ) + goto fs_out_allocate_memory_error; + + err =match_fs (&st_accel_sensors[adata->index], fs, fs_out); +if (err <0 ) { +pr_err ("%s: input is not a valid fullscale! (errn %d).\n" , + adata->name, err);
This too
Quoted 29 lines
drivers/iio/accel/ST_accel_core.c new file +29
-0+ goto match_fullscale_error; + } + err =set_fullscale (indio_dev, fs_out); +if (err <0 ) { +pr_err ("%s: failed to set new fullscale. (errn %d).\n" , + adata->name, err); + } + +match_fullscale_error: +kfree (fs_out); +fs_out_allocate_memory_error: +mutex_unlock (&indio_dev->mlock); +set_fullscale_error: +return size; +} + +static ssize_t sysfs_fullscale_available (struct device *dev, +struct device_attribute *attr,char *buf) +{ +int i, n, len; +char tmp[4 ]; +char fullscale[30 ] ="" ; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_acc_data *adata =iio_priv (indio_dev); + +mutex_lock (&indio_dev->mlock); + n =ARRAY_SIZE (st_accel_sensors[adata->index].fs.fs_avl); +for (i =0 ; i < n; i++) { +if (st_accel_sensors[adata->index].fs.fs_avl[i].num !=0 ) {
drivers/iio/accel/ST_accel_core.c line 1175
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+ len =strlen (&fullscale[0 ]); +sprintf (tmp,"%d " , + st_accel_sensors[adata->index].fs.fs_avl[i].num); +strcpy (&fullscale[len], tmp); + }else +break ; + } +mutex_unlock (&indio_dev->mlock); + +return sprintf (buf,"%s\n" , fullscale);
This function does still the crazy sprintf, strcpy, sprintf combo
In general the same comments as to sysfs_sampling_frequency_available apply here.
drivers/iio/accel/ST_accel_core.c line 1188
Quoted 13 lines
drivers/iio/accel/ST_accel_core.c new file +13
-0+} + +static ssize_t sysfs_sampling_frequency_available (struct device *dev, +struct device_attribute *attr,char *buf) +{ +int i, n, len; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_acc_data *adata =iio_priv (indio_dev); + +mutex_lock (&indio_dev->mlock); + n =ARRAY_SIZE (st_accel_sensors[adata->index].odr.odr_avl); +for (i =0 ; i < n; i++) { +if (st_accel_sensors[adata->index].odr.odr_avl[i].hz !=0 ) {
I'd rewrite this as
if (st_accel_sensors[adata->index].odr.odr_avl[i].hz == 0) break; ...
drivers/iio/accel/ST_accel_core.c line 1191
Quoted 3 lines
drivers/iio/accel/ST_accel_core.c new file +3
-0+ len =strlen (buf); +sprintf (buf+len,"%d " , + st_accel_sensors[adata->index].odr.odr_avl[i].hz);
sprintf returns the number of characters written, so you don't need to run strlen each loop interation. Also use scnprint with a limit of PAGE_SIZE this avoids the (theoretical) buffer overflow. Your code could look something like this:
len += scnprintf(buf+len, PAGE_SIZE, ...)
drivers/iio/accel/ST_accel_core.c line 1198
Quoted 7 lines
drivers/iio/accel/ST_accel_core.c new file +7
-0+ }else +break ; + } +mutex_unlock (&indio_dev->mlock); + + len =strlen (buf); +return sprintf (buf+len,"\n" );
This will return 1 instead of the actuall string length. I'd rewrite this as
buf[len] = "\n" return len;
This will replace the last space in the string with a newline, so you won't end up with a trailing space.
Quoted 11 lines
drivers/iio/accel/ST_accel_core.c new file +11
-0+} + +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,
drivers/iio/accel/ST_accel_core.c line 1219
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+ sysfs_set_sampling_frequency); + +static struct attribute *acc_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_fullscale_available.dev_attr.attr, + &iio_dev_attr_fullscale.dev_attr.attr, + &iio_dev_attr_enable.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, +NULL , +};
Non standard attributes need to be documented.
Quoted 19 lines
drivers/iio/accel/ST_accel_core.c new file +19
-0+ +static int init_sensor (struct iio_dev *indio_dev) +{ +int err; +struct odr_available *odr_out; +struct fullscale_available *fs_out; +struct st_acc_data *adata =iio_priv (indio_dev); + + odr_out =kzalloc (sizeof (*odr_out),GFP_KERNEL ); +if (odr_out ==NULL ) + goto odr_out_allocate_memory_error; + fs_out =kzalloc (sizeof (*fs_out),GFP_KERNEL ); +if (fs_out ==NULL ) + goto fs_out_allocate_memory_error; +set_enable (indio_dev,ST_ACCEL_OFF ); +match_fs (&st_accel_sensors[adata->index], adata->fullscale, fs_out); + err =set_fullscale (indio_dev, fs_out); +if (err <0 ) + goto init_error;
drivers/iio/accel/ST_accel_core.c line 1258
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+match_odr (&st_accel_sensors[adata->index], adata->odr, odr_out); + err =set_odr (indio_dev, odr_out); +if (err <0 ) + goto init_error; + err =set_bdu (indio_dev, + &st_accel_sensors[adata->index].bdu, (u8 )ST_ACCEL_ON ); +if (err <0 ) + goto init_error; +kfree (odr_out); +kfree (fs_out);
Allocate both odr_out and fs_out on the stack.
Also this function could use a few newlines.
Quoted 14 lines
drivers/iio/accel/ST_accel_core.c new file +14
-0+ +return 0 ; + +init_error: +kfree (fs_out); +fs_out_allocate_memory_error: +kfree (odr_out); +odr_out_allocate_memory_error: +return -EIO ; +} + +int acc_iio_probe (struct iio_dev *indio_dev) +{ +int err;
drivers/iio/accel/ST_accel_core.c line 1282
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+u8 wai; +struct st_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,ST_DEFAULT_WAI_ADDRESS , &wai); +if (err <0 ) + goto get_wai_error;
Missing newline
drivers/iio/accel/ST_accel_core.c line 1285
Quoted 3 lines
drivers/iio/accel/ST_accel_core.c new file +3
-0+ err =check_device_list (indio_dev, wai); +if (err <0 ) + goto check_device_list_error;
Missing newline
drivers/iio/accel/ST_accel_core.c line 1287
Quoted 2 lines
drivers/iio/accel/ST_accel_core.c new file +2
-0+set_sensor_parameters (indio_dev); +register_channels (indio_dev);
Missing newline
drivers/iio/accel/ST_accel_core.c line 1294
Quoted 7 lines
drivers/iio/accel/ST_accel_core.c new file +7
-0+ err =init_sensor (indio_dev); +if (err <0 ) + goto init_sensor_error; + + err =acc_check_irq (indio_dev); +if (err <0 ) + goto gpio_check_error;
Missing newline
drivers/iio/accel/ST_accel_core.c line 1307
Quoted 13 lines
drivers/iio/accel/ST_accel_core.c new file +13
-0+ 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);
This is just noise. Imagine every driver doing this you'd end up with quite a few lines of "Drivers has probed correctly" in your bootlog.
drivers/iio/accel/ST_accel_core.c line 1319
Quoted 12 lines
drivers/iio/accel/ST_accel_core.c new file +12
-0+ +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: +check_device_list_error: +get_wai_error:
No need for all these labels
drivers/iio/accel/ST_accel_core.c line 1329
Quoted 10 lines
drivers/iio/accel/ST_accel_core.c new file +10
-0+return err; +} +EXPORT_SYMBOL (acc_iio_probe); + +void acc_iio_remove (struct iio_dev *indio_dev) +{ +acc_remove_trigger (indio_dev); +acc_deallocate_ring (indio_dev); +iio_device_unregister (indio_dev); +iio_device_free (indio_dev);
Rule of thumb: First iio_device_unregister then everything else and iio_device_free last
Quoted 27 lines
drivers/iio/accel/ST_accel_core.c new file +6
-0+} +EXPORT_SYMBOL (acc_iio_remove); + +MODULE_AUTHOR ("Denis Ciocca <[email protected]>" ); +MODULE_DESCRIPTION ("STMicroelectronics accelerometers driver" ); +MODULE_LICENSE ("GPL v2" );drivers/iio/accel/ST_accel_i2c.c new file +15
-0@@ -0,0 +1,165 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* 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>
drivers/iio/accel/ST_accel_i2c.c line 25
Quoted 10 lines
drivers/iio/accel/ST_accel_i2c.c new file +10
-0+#include <linux/iio/trigger.h> + +#include <linux/iio/accel/ST_accel.h> +#include <linux/platform_data/ST_accel_pdata.h> + + +#define ST_ACCEL_I2C_MULTIREAD 0x80 + +static inline s32 acc_i2c_read_byte (struct st_acc_data *adata,u8 reg_addr, +u8 *res_byte)
This function obviously can not be inlined since it is used as a callback and use int for the return type
Quoted 5 lines
drivers/iio/accel/ST_accel_i2c.c new file +5
-0+{ +s32 err; + + err =i2c_smbus_read_byte_data (to_i2c_client (adata->dev), reg_addr); +if (err <0 )
drivers/iio/accel/ST_accel_i2c.c line 40
Quoted 10 lines
drivers/iio/accel/ST_accel_i2c.c new file +10
-0+ goto acc_i2c_read_byte_error; + *res_byte = err &0xff ; +return err; + +acc_i2c_read_byte_error: +return -EIO ; +} + +static inline s32 acc_i2c_read_multiple_byte (struct st_acc_data *adata, +u8 reg_addr,int len,u8 *data)
same here
Quoted 9 lines
drivers/iio/accel/ST_accel_i2c.c new file +9
-0+{ +s32 err; + +if (adata->multiread_bit == true) + reg_addr |=ST_ACCEL_I2C_MULTIREAD ; + + err =i2c_smbus_read_i2c_block_data (to_i2c_client (adata->dev), + reg_addr, len, data); +if (err <0 )
drivers/iio/accel/ST_accel_i2c.c line 59
Quoted 10 lines
drivers/iio/accel/ST_accel_i2c.c new file +10
-0+ goto acc_i2c_read_multiple_byte_error; + +return err; + +acc_i2c_read_multiple_byte_error: +return -EIO ; +} + +static inline s32 acc_i2c_write_byte (struct st_acc_data *adata, +u8 reg_addr,u8 data)
same here
Quoted 5 lines
drivers/iio/accel/ST_accel_i2c.c new file +5
-0+{ +return i2c_smbus_write_byte_data (to_i2c_client (adata->dev), + reg_addr, data); +} +
[...]
Quoted 24 lines
drivers/iio/accel/ST_accel_i2c.c new file +2
-0+MODULE_DESCRIPTION ("STMicroelectronics accelerometers i2c driver" ); +MODULE_LICENSE ("GPL v2" );drivers/iio/accel/ST_accel_spi.c new file +16
-0@@ -0,0 +1,236 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h>
drivers/iio/accel/ST_accel_spi.c line 26
Quoted 10 lines
drivers/iio/accel/ST_accel_spi.c new file +10
-0+ +#include <linux/iio/accel/ST_accel.h> +#include <linux/platform_data/ST_accel_pdata.h> + + +#define ACC_SPI_READ 0x80; +#define ACC_SPI_MULTIREAD 0xc0 + +static inline s32 acc_spi_read_byte (struct st_acc_data *adata,u8 reg_addr, +u8 *res_byte)
This function obviously can not be inlined since it is used as a callback and use int for the return type
Quoted 23 lines
drivers/iio/accel/ST_accel_spi.c new file +23
-0+{ +struct spi_message msg; +int err; +u8 tx; + +struct spi_transfer xfers[] = { + { + .tx_buf = &tx, + .bits_per_word =8 , + .len =1 , + .cs_change =0 , + .delay_usecs =10 , + }, + { + .rx_buf = res_byte, + .bits_per_word =8 , + .len =1 , + .cs_change =0 , + .delay_usecs =10 , + } + }; + +mutex_lock (&adata->slock);
drivers/iio/accel/ST_accel_spi.c line 59
Quoted 10 lines
drivers/iio/accel/ST_accel_spi.c new file +10
-0+ tx = reg_addr |ACC_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 (adata->dev), &msg); +mutex_unlock (&adata->slock); +if (err) + goto acc_spi_read_byte_error; + +
Rule of thumb: You should never have multiple consecutive newlines
drivers/iio/accel/ST_accel_spi.c line 67
Quoted 8 lines
drivers/iio/accel/ST_accel_spi.c new file +8
-0+return err; + +acc_spi_read_byte_error: +return -EIO ; +} + +static inline s32 acc_spi_read_multiple_byte (struct st_acc_data *adata, +u8 reg_addr,int len,u8 *data)
same here
Quoted 33 lines
drivers/iio/accel/ST_accel_spi.c new file +33
-0+{ +struct spi_message msg; +int err; +u8 tx; + +struct spi_transfer xfers[] = { + { + .tx_buf = &tx, + .bits_per_word =8 , + .len =1 , + .cs_change =0 , + .delay_usecs =10 , + }, + { + .rx_buf = data, + .bits_per_word =8 , + .len = len, + .cs_change =0 , + .delay_usecs =10 , + } + }; + +mutex_lock (&adata->slock); +if (adata->multiread_bit == true) + tx = reg_addr |ACC_SPI_MULTIREAD ; +else + tx = reg_addr |ACC_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 (adata->dev), &msg); +mutex_unlock (&adata->slock);
drivers/iio/accel/ST_accel_spi.c line 110
Quoted 10 lines
drivers/iio/accel/ST_accel_spi.c new file +10
-0+if (err) + goto acc_spi_read_multiple_byte_error; +return len; + +acc_spi_read_multiple_byte_error: +return -EIO ; +} + +static inline s32 acc_spi_write_byte (struct st_acc_data *adata, +u8 reg_addr,u8 data)
same here
drivers/iio/accel/ST_accel_spi.c line 121
Quoted 11 lines
drivers/iio/accel/ST_accel_spi.c new file +11
-0+{ +struct spi_message msg; +int err; +u8 tx[2 ]; + +struct spi_transfer xfers[] = { + { + .tx_buf = tx, + .bits_per_word =8 , + .len =2 , + .cs_change =0 ,
Usually if cs_change is 0 it is not explicitly initalized.
Quoted 17 lines
drivers/iio/accel/ST_accel_spi.c new file +17
-0+ .delay_usecs = 10, + } + }; + +mutex_lock (&adata->slock); + tx[0 ] = reg_addr; + tx[1 ] = data; +spi_message_init (&msg); +spi_message_add_tail (&xfers[0 ], &msg); + err =spi_sync (to_spi_device (adata->dev), &msg); +mutex_unlock (&adata->slock); + +return err; +} + +void set_platform_data (struct spi_device *client,struct st_acc_data *adata) +{
drivers/iio/accel/ST_accel_spi.c line 148
Quoted 10 lines
drivers/iio/accel/ST_accel_spi.c new file +10
-0+if (client->dev.platform_data ==NULL ) { + adata->fullscale = st_acc_default_pdata.fullscale; + adata->odr = st_acc_default_pdata.sampling_frequency; + }else { + adata->fullscale = ((struct st_acc_platform_data *) + (client->dev.platform_data))->fullscale; + adata->odr = ((struct st_acc_platform_data *) + (client->dev.platform_data))->sampling_frequency; + } +}
I'd move this to the generic part of the driver, it's the same for both SPI and I2C. Also instead of the casting create a helper variale and use that.
struct st_acc_platform_data *pdata = adata->dev->platform_data; adata->fullscale = pdata->fullscale;
...
drivers/iio/accel/ST_accel_spi.c line 157
Quoted 9 lines
drivers/iio/accel/ST_accel_spi.c new file +9
-0+ +void set_trigger_parent (struct iio_dev *indio_dev) +{ +struct spi_device *client; +struct st_acc_data *adata =iio_priv (indio_dev); + + client =to_spi_device (adata->dev); + adata->trig->dev.parent = &client->dev; +}
This should not be necessary. You convert your device to a SPI device and then read the device again from the spi device. So in the end adata->dev and &client->dev are the same. There is no need for this callback, just do adata->trig->dev.parent = ata->dev; in the generic code.
Quoted 52 lines
drivers/iio/accel/ST_accel_spi.c new file +52
-0+ +static int __devinit acc_spi_probe (struct spi_device *client) +{ +struct iio_dev *indio_dev; +struct st_acc_data *adata; +int err; + + indio_dev =iio_device_alloc (sizeof (*adata)); +if (indio_dev ==NULL ) { + err = -ENOMEM ; + goto iio_device_alloc_error; + } + + adata =iio_priv (indio_dev); + adata->dev = &client->dev; +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->set_trigger_parent = set_trigger_parent; + adata->name = client->modalias; + adata->irq_data_ready = &client->irq; + +set_platform_data (client, adata); + err =acc_iio_probe (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) +{ +struct iio_dev *indio_dev =spi_get_drvdata (spi); + +acc_iio_remove (indio_dev); +return 0 ; +} + +static const struct spi_device_id acc_id_table[] = { + {LSM303DLH_ACC_IIO_DEV_NAME ,0 }, + {LSM303DLHC_ACC_IIO_DEV_NAME ,0 }, + {LIS3DH_ACC_IIO_DEV_NAME ,0 },
drivers/iio/accel/ST_accel_spi.c line 219
Quoted 10 lines
drivers/iio/accel/ST_accel_spi.c new file +10
-0+ { LSM330D_ACC_IIO_DEV_NAME, 0 }, + { LSM330DL_ACC_IIO_DEV_NAME, 0 }, + { LSM330DLC_ACC_IIO_DEV_NAME, 0 }, + { LSM303D_ACC_IIO_DEV_NAME, 0 }, + { LSM9DS0_ACC_IIO_DEV_NAME, 0 }, + { LIS331DLH_ACC_IIO_DEV_NAME, 0 }, + { LSM303DL_ACC_IIO_DEV_NAME, 0 }, + { LSM303DLM_ACC_IIO_DEV_NAME, 0 }, + { LSM330_ACC_IIO_DEV_NAME, 0 }, + {},
Since the second field won't be used in the driver you can just leave the it blank,
e.g. { LSM330_ACC_IIO_DEV_NAME },
Quoted 150 lines
drivers/iio/accel/ST_accel_spi.c new file +17
-0+}; +MODULE_DEVICE_TABLE (spi, acc_id_table); + +static struct spi_driver acc_driver = { + .driver = { + .owner =THIS_MODULE , + .name ="ST-accel-spi" , + }, + .probe = acc_spi_probe, + .remove =__devexit_p (acc_spi_remove), + .id_table = acc_id_table, +}; +module_spi_driver (acc_driver); + +MODULE_AUTHOR ("Denis Ciocca <[email protected]>" ); +MODULE_DESCRIPTION ("STMicroelectronics accelerometers spi driver" ); +MODULE_LICENSE ("GPL v2" );drivers/iio/accel/ST_accel_trigger.c new file +87
-0@@ -0,0 +1,87 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> + +#include <linux/iio/accel/ST_accel.h> + + +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 st_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, + iio_trigger_generic_data_rdy_poll, +NULL , +IRQF_TRIGGER_RISING , + adata->trig->name, + adata->trig); +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; + + adata->set_trigger_parent (indio_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; +} +EXPORT_SYMBOL (acc_probe_trigger); + +void acc_remove_trigger (struct iio_dev *indio_dev) +{ +struct st_acc_data *adata =iio_priv (indio_dev); + +iio_trigger_unregister (adata->trig); +free_irq (*adata->irq_data_ready, adata->trig); +iio_trigger_free (adata->trig); +} +EXPORT_SYMBOL (acc_remove_trigger);include/linux/iio/accel/ST_accel.h new file +32
-0@@ -0,0 +1,120 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* v. 1.0.0 +* Licensed under the GPL-2. +*/ + +/* +* Supported sensors: +* LSM303DLH +* LSM303DLHC +* LIS3DH +* LSM330D +* LSM330DL +* LSM330DLC +* LSM303D +* LSM9DS0 +* LIS331DLH +* LSM303DL +* LSM303DLM +* LSM330 +*/ + + +#ifndef ST_ACCELEROMETERS_IIO_ACC_H +#define ST_ACCELEROMETERS_IIO_ACC_H + +#define LSM303DLH_ACC_IIO_DEV_NAME "lsm303dlh_acc_iio" +#define LSM303DLHC_ACC_IIO_DEV_NAME "lsm303dlhc_acc_iio"
include/linux/iio/accel/ST_accel.h line 42
Quoted 10 lines
include/linux/iio/accel/ST_accel.h new file +10
-0+#define LIS3DH_ACC_IIO_DEV_NAME "lis3dh_acc_iio" +#define LSM330D_ACC_IIO_DEV_NAME "lsm330d_acc_iio" +#define LSM330DL_ACC_IIO_DEV_NAME "lsm330dl_acc_iio" +#define LSM330DLC_ACC_IIO_DEV_NAME "lsm330dlc_acc_iio" +#define LSM303D_ACC_IIO_DEV_NAME "lsm303d_iio" +#define LSM9DS0_ACC_IIO_DEV_NAME "lsm9ds0_iio" +#define LIS331DLH_ACC_IIO_DEV_NAME "lis331dlh_acc_iio" +#define LSM303DL_ACC_IIO_DEV_NAME "lsm303dl_acc_iio" +#define LSM303DLM_ACC_IIO_DEV_NAME "lsm303dlm_acc_iio" +#define LSM330_ACC_IIO_DEV_NAME "lsm330_acc_iio"
The acc_iio prefixes on the driver names should not be necessary
Quoted 21 lines
include/linux/iio/accel/ST_accel.h new file +21
-0+ +#define ST_ACC_NUMBER_ALL_CHANNELS 4 +#define ST_ACC_BYTE_FOR_CHANNEL 2 +#define ST_ACC_SCAN_X 0 +#define ST_ACC_SCAN_Y 1 +#define ST_ACC_SCAN_Z 2 + +/** +* struct st_acc_data - ST accel device status +* @dev: Pointer to instance of struct device (I2C or SPI). +* @name: Name of the sensor in use. +* @enabled: Status of the sensor (0->off, 1->on). +* @index: Number used to point the sensor being used in the +* st_accel_sensors struct. +* @fullscale: Maximum range of measure by the sensor. +* @gain: Sensitivity of the sensor [ug/LSB]. +* @odr: Output data rate of the sensor. +* @multiread_bit: Use or not particular bit for [I2C/SPI] multiread. +* @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.
include/linux/iio/accel/ST_accel.h line 73
Quoted 10 lines
include/linux/iio/accel/ST_accel.h new file +10
-0+* @set_trigger_parent: Function used to set the trigger parent. +* @trig: The trigger in use by the core driver. +* @irq_data_ready: IRQ number for data ready on INT1 pin. +* @slock: mutex for read and write operation. +*/ + +struct st_acc_data { +struct device *dev; +char *name; +atomic_t enabled;
Why does this have to be atomic?
Quoted 5 lines
include/linux/iio/accel/ST_accel.h new file +5
-0+short index; + +int fullscale; +int gain; +int odr;
include/linux/iio/accel/ST_accel.h line 88
Quoted 10 lines
include/linux/iio/accel/ST_accel.h new file +10
-0+ +bool multiread_bit; +int (*read_byte) (struct st_acc_data *adata,u8 reg_addr,u8 *res_byte); +int (*write_byte) (struct st_acc_data *adata,u8 reg_addr,u8 data); +int (*read_multiple_byte) (struct st_acc_data *adata,u8 reg_addr, +int len,u8 *data); +void (*set_trigger_parent) (struct iio_dev *indio_dev); + +struct iio_trigger *trig; +int *irq_data_ready;
Why does this have to be a pointer?
Quoted 61 lines
include/linux/iio/accel/ST_accel.h new file +32
-0+struct mutex slock; +}; + +int acc_iio_probe (struct iio_dev *indio_dev); +void acc_iio_remove (struct iio_dev *indio_dev); +int acc_set_dataready_irq (struct iio_dev *indio_dev,bool enable); + +#ifdef CONFIG_IIO_BUFFER +int acc_probe_trigger (struct iio_dev *indio_dev); +void acc_remove_trigger (struct iio_dev *indio_dev); +int acc_allocate_ring (struct iio_dev *indio_dev); +void acc_deallocate_ring (struct iio_dev *indio_dev); +#else /* CONFIG_IIO_BUFFER */ +static inline int acc_probe_trigger (struct iio_dev *indio_dev) +{ +return 0 ; +} +static inline void acc_remove_trigger (struct iio_dev *indio_dev) +{ +return ; +} +static inline int acc_allocate_ring (struct iio_dev *indio_dev) +{ +return 0 ; +} +static inline void acc_deallocate_ring (struct iio_dev *indio_dev) +{ +return ; +} +#endif /* CONFIG_IIO_BUFFER */ + +#endif /* ST_ACCELEROMETERS_IIO_ACC_H */ include/linux/platform_data/ST_accel_pdata.h new file +22
-0@@ -0,0 +1,34 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + + +#ifndef ST_ACCELEROMETERS_PDATA_ACC_H +#define ST_ACCELEROMETERS_PDATA_ACC_H + +#define DEFAULT_ACCEL_ODR_AVL_100HZ 100 +#define DEFAULT_ACCEL_FS_AVL_2G 2 + +/** +* struct st_acc_platform_data - ST accel device platform data +* @fullscale: Value of fullscale used for the sensor. +* @sampling_frequency: Value of sampling frequency used for the sensor. +*/
include/linux/platform_data/ST_accel_pdata.h line 32
Quoted 10 lines
include/linux/platform_data/ST_accel_pdata.h new file +10
-0+ +struct st_acc_platform_data { +int fullscale; +int sampling_frequency; +}; + +static const struct st_acc_platform_data st_acc_default_pdata = { + .fullscale =DEFAULT_ACCEL_FS_AVL_2G , + .sampling_frequency =DEFAULT_ACCEL_ODR_AVL_100HZ , +};
This one should clearly not be defined in the header.
Quoted 2 lines
include/linux/platform_data/ST_accel_pdata.h new file +2
-0+ +#endif /* ST_ACCELEROMETERS_PDATA_ACC_H */
Hi guys,
according to Lars-Peter requests, I have modified my driver. (I hope I have checked everything).
I have only one things about read_all function on the buffer source file:
I think the best solution is reading every time of all output registers, these are three-axial accelerometers, the overhead for support the power on/off of single axis is too much big for the purpose (because if I don't power off the single axis the data-ready doesn't reset). What do you think about that?
Thanks
Denis
From f2f556001170e8698a1bbf0146ed2d11e75436b1 Mon Sep 17 00:00:00 2001
From: Denis Ciocca <[email protected]>
Date: Mon, 22 Oct 2012 11:17:27 +0200
Subject: [PATCH] iio:accel: Add STMicroelectronics accelerometers driver
This patch adds generic accelerometer driver for STMicroelectronics accelerometers, currently it supports:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D, LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330
--- drivers/iio/accel/Kconfig | 37 + drivers/iio/accel/Makefile | 6 + drivers/iio/accel/st_accel_buffer.c | 103 ++ drivers/iio/accel/st_accel_core.c | 1301 ++++++++++++++++++++++++++ drivers/iio/accel/st_accel_i2c.c | 139 +++ drivers/iio/accel/st_accel_spi.c | 199 ++++ drivers/iio/accel/st_accel_trigger.c | 84 ++ include/linux/iio/accel/st_accel.h | 119 +++ include/linux/platform_data/st_accel_pdata.h | 27 + 9 files changed, 2015 insertions(+), 0 deletions(-)
create mode 100644 drivers/iio/accel/st_accel_buffer.c create mode 100644 drivers/iio/accel/st_accel_core.c create mode 100644 drivers/iio/accel/st_accel_i2c.c create mode 100644 drivers/iio/accel/st_accel_spi.c create mode 100644 drivers/iio/accel/st_accel_trigger.c create mode 100644 include/linux/iio/accel/st_accel.h create mode 100644 include/linux/platform_data/st_accel_pdata.h
drivers/iio/accel/Kconfig +37 -0
@@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D Say yes here to build support for the HID SENSOR accelerometers 3D. +config ST_ACCEL_3AXIS + tristate "STMicroelectronics accelerometers 3-Axis Driver" + depends on (I2C || SPI) && SYSFS + help + Say yes here to build support for STMicroelectronics accelerometers: + LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D, + LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330. + + This driver can also be built as a module. If so, the module + will be called st_accel. + +config ST_ACCEL_3AXIS_I2C + tristate "support I2C bus connection" + depends on ST_ACCEL_3AXIS && I2C + help + Say yes here to build I2C support for STMicroelectronics accelerometers. + + To compile this driver as a module, choose M here: the + module will be called st_accel_i2c. + +config ST_ACCEL_3AXIS_SPI + tristate "support SPI bus connection" + depends on ST_ACCEL_3AXIS && SPI_MASTER + help + Say yes here to build SPI support for STMicroelectronics accelerometers. + + To compile this driver as a module, choose M here: the + module will be called st_accel_spi. + +config ST_ACCEL_3AXIS_TRIGGERED_BUFFER + tristate "support triggered buffer" + depends on ST_ACCEL_3AXIS + select IIO_TRIGGERED_BUFFER + select IIO_BUFFER + help + Default trigger and buffer for STMicroelectronics accelerometers driver. + endmenu
drivers/iio/accel/Makefile +5 -0
@@ -3,3 +3,9 @@ # obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o + +st_accel-y := st_accel_core.o +obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) += st_accel_i2c.o +obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) += st_accel_spi.o +obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) += st_accel_trigger.o
st_accel_buffer.o
+obj-$(CONFIG_ST_ACCEL_3AXIS) += st_accel.o
(quoted diff) +0 -0
b/drivers/iio/accel/st_accel_buffer.c
new file mode 100644
index 0000000..e6e4b0d
drivers/iio/accel/st_accel_buffer.c new file +103 -0
103 lines not on this page · raw message
(quoted diff) +0 -0
b/drivers/iio/accel/st_accel_core.c
new file mode 100644
index 0000000..6295970
drivers/iio/accel/st_accel_core.c new file +669 -0
669 lines not on this page · raw message
reg_addr,
+ u8 mask, short num_bit, u8 data)
+{
+ int err, j, pos; + u8 prev_data; + u8 new_data; + struct st_accel_data *adata = iio_priv(indio_dev);
+
+ pos = 8 - num_bit;
+ for (j = 128; j >= 0; j = j/2) {
+ if (mask / j > 0)
+ break;
+ else
+ pos--;
+ }
+
+ err = adata->read_byte(adata, reg_addr, &prev_data);
+ if (err < 0)
+ goto st_accel_write_data_with_mask_error;
+
+ new_data = ((prev_data & (~mask)) | ((data << pos) & mask));
+ err = adata->write_byte(adata, reg_addr, new_data);
+
+st_accel_write_data_with_mask_error:
+ return err;
+}
+
+static int st_accel_match_odr(const struct st_accel_sensors *sensor,
+ unsigned int odr, struct st_accel_odr_available *odr_out)
+{
+ int i, ret = -1;
+
+ for (i = 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
+ if (sensor->odr.odr_avl[i].hz == odr) {
+ odr_out->hz = sensor->odr.odr_avl[i].hz; + odr_out->value = sensor->odr.odr_avl[i].value; + ret = 0; + break;
+ }
+ }
+
+ return ret;
+}
+
+static int st_accel_match_fs(const struct st_accel_sensors *sensor,
+ unsigned int fs, struct st_accel_fullscale_available *fs_out)
+{
+ int i, ret = -1;
+
+ for (i = 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+ if (sensor->fs.fs_avl[i].num == fs) {
+ 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; + ret = 0; + break;
+ }
+ }
+
+ return ret;
+}
+
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable) +{
+ int err;
+ struct st_accel_data *adata;
+
+ adata = iio_priv(indio_dev);
+ if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
+ err = st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].drdy_irq.ig1.en_addr, + st_accel_sensors[adata->index].drdy_irq.ig1.en_mask, 1, + (int)enable);
+ if (err < 0)
+ goto st_accel_set_dataready_irq_error;
+ }
+
+ if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > 0) {
+ err = st_accel_write_data_with_mask(indio_dev, + st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr, + st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask, 1,
+ (int)enable);
+ if (err < 0)
+ goto st_accel_set_dataready_irq_error;
+ }
+
+ err = st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].drdy_irq.addr,
+ st_accel_sensors[adata->index].drdy_irq.mask, 1, (int)enable);
+ if (err < 0)
+ goto st_accel_set_dataready_irq_error;
+
+st_accel_set_dataready_irq_error:
+ return err;
+}
+EXPORT_SYMBOL(st_accel_set_dataready_irq);
+
+static int st_accel_set_bdu(struct iio_dev *indio_dev,
+ const struct st_accel_bdu *bdu, u8 value)
+{
+ return st_accel_write_data_with_mask(indio_dev, bdu->addr, bdu->mask,
+ 1, value);
+}
+
+static int st_accel_set_odr(struct iio_dev *indio_dev,
+ struct st_accel_odr_available *odr_available)
+{
+ int err;
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ if ((st_accel_sensors[adata->index].odr.addr ==
+ st_accel_sensors[adata->index].pw.addr) &&
+ (st_accel_sensors[adata->index].odr.mask ==
+ st_accel_sensors[adata->index].pw.mask)) {
+ if (adata->enabled == (bool)ST_ACCEL_ON) {
+ err = st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].odr.addr, + st_accel_sensors[adata->index].odr.mask, + st_accel_sensors[adata->index].odr.num_bit, + odr_available->value);
+ if (err < 0)
+ goto sc_accel_set_odr_error;
+ } else {
+ adata->odr = odr_available->hz;
+ err = 0;
+ }
+ } else {
+ err = st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].odr.addr, + st_accel_sensors[adata->index].odr.mask, + st_accel_sensors[adata->index].odr.num_bit, + odr_available->value);
+ if (err < 0)
+ goto sc_accel_set_odr_error;
+ }
+
+sc_accel_set_odr_error:
+ return err;
+}
+
+static int st_accel_set_enable(struct iio_dev *indio_dev, int enable) +{
+ int err;
+ bool found;
+ u8 tmp_value;
+ struct st_accel_odr_available odr_out;
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ switch (enable) {
+ case ST_ACCEL_ON:
+ found = false;
+ tmp_value = st_accel_sensors[adata->index].pw.value_on;
+ if ((st_accel_sensors[adata->index].odr.addr ==+ st_accel_sensors[adata->index].pw.addr) &&
+ (st_accel_sensors[adata->index].odr.mask ==
+ st_accel_sensors[adata->index].pw.mask)) {
+ err = st_accel_match_odr(
+ &st_accel_sensors[adata->index], adata->odr,
+ &odr_out);
+ if (err < 0)
+ goto set_enable_error;
+ tmp_value = odr_out.value;
+ found = true;
+ }
+ err = st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].pw.addr, + st_accel_sensors[adata->index].pw.mask, + st_accel_sensors[adata->index].pw.num_bit, + tmp_value);
+ if (err < 0)
+ goto set_enable_error;
+ adata->enabled = (bool)ST_ACCEL_ON;
+ if (found)
+ adata->odr = odr_out.hz;
+ break;
+ case ST_ACCEL_OFF:
+ err = st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].pw.addr, + st_accel_sensors[adata->index].pw.mask, + st_accel_sensors[adata->index].pw.num_bit, + st_accel_sensors[adata->index].pw.value_off);
+ if (err < 0)
+ goto set_enable_error;
+ adata->enabled = (bool)ST_ACCEL_OFF;
+ break;
+ default:
+ err = -EINVAL;
+ goto set_enable_error;
+ }
+
+set_enable_error:
+ return err;
+}
+
+static int st_accel_set_fullscale(struct iio_dev *indio_dev,
+ struct st_accel_fullscale_available *fs_avl)
+{
+ int err;
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ err = st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].fs.addr, + st_accel_sensors[adata->index].fs.mask, + st_accel_sensors[adata->index].fs.num_bit, + fs_avl->value); + if (err < 0) + goto st_accel_set_fullscale_error; + + adata->fullscale = fs_avl->num; + adata->gain = fs_avl->gain; + return err;
+
+st_accel_set_fullscale_error:
+ dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+ return err;
+}
+
+static int st_accel_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[ST_ACCEL_BYTE_FOR_CHANNEL]; + struct st_accel_data *adata = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
+ err = -EBUSY;
+ goto read_error;
+ } else {
+ if (!adata->enabled) {
+ err = -EIO;
+ goto read_error;
+ } else {
+ err = adata->read_multiple_byte(adata,
+ ch->address, ST_ACCEL_BYTE_FOR_CHANNEL,
+ outdata);
+ if (err < 0)
+ goto read_error;
+
+ *val = ((s16)(((s16)(outdata[1]) << 8)
+ | outdata[0])) >> ch->scan_type.shift;
+ }
+ } + mutex_unlock(&indio_dev->mlock); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + data_tmp = UG_TO_MS2(adata->gain); + *val = 0; + *val2 = data_tmp; + return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+
+read_error:
+ mutex_unlock(&indio_dev->mlock);
+ return err;
+}
+
+static int st_accel_check_device_list(struct iio_dev *indio_dev, u8 wai) +{
+ int i; + bool found; + struct st_accel_data *adata = iio_priv(indio_dev);
+
+ found = false;
+ for (i = 0; i < ARRAY_SIZE(st_accel_sensors); i++) {
+ if (st_accel_sensors[i].wai == wai) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ goto check_device_error;
+
+ adata->index = i;
+
+ return i;
+
+check_device_error:
+ dev_err(&indio_dev->dev, "device not supported -> wai (0x%x).\n", wai);
+ return -ENODEV;
+}
+
+static int st_accel_get_wai_device(struct iio_dev *indio_dev, u8 reg_addr,
+ u8 *value)
+{
+ int ret;
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ ret = adata->read_byte(adata, reg_addr, value);
+ if (ret < 0)
+ goto read_byte_wai_error;
+
+ return 0;
+
+read_byte_wai_error:
+ dev_err(&indio_dev->dev,
+ "failed to read WhoAmI (register 0x%x).\n", reg_addr);
+ return -EIO;
+}
+
+static ssize_t st_accel_sysfs_set_sampling_frequency(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err; + unsigned int freq; + struct st_accel_odr_available odr_out; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct st_accel_data *adata = iio_priv(indio_dev);
+
+ err = kstrtoint(buf, 10, &freq);
+ if (err < 0)
+ goto conversion_error;
+
+ mutex_lock(&indio_dev->mlock);
+ err = st_accel_match_odr(&st_accel_sensors[adata->index],
+ freq, &odr_out);
+ if (err < 0)
+ goto st_accel_sysfs_set_sampling_frequency_error;
+
+ err = st_accel_set_odr(indio_dev, &odr_out);
+ if (err < 0) {
+ dev_err(&indio_dev->dev,
+ "failed to set sampling frequency to %d.\n", freq);
+ goto st_accel_sysfs_set_sampling_frequency_error;
+ }
+ adata->odr = odr_out.hz;
+
+st_accel_sysfs_set_sampling_frequency_error:
+ mutex_unlock(&indio_dev->mlock);
+conversion_error:
+ return size;
+}
+
+static ssize_t st_accel_sysfs_get_sampling_frequency(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", adata->odr);
+}
+
+static ssize_t st_accel_sysfs_set_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err; + bool en; + struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+ err = strtobool(buf, &en);
+ if (err < 0)
+ goto set_enable_error;
+
+ mutex_lock(&indio_dev->mlock); + err = st_accel_set_enable(indio_dev, (int)en); + if (err < 0)
+ dev_err(&indio_dev->dev,
+ "failed to set enable to %d.\n", (int)en);
+ mutex_unlock(&indio_dev->mlock);
+
+set_enable_error:
+ return size;
+}
+
+static ssize_t st_accel_sysfs_get_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", (int)adata->enabled);
+}
+
+static ssize_t st_accel_sysfs_get_fullscale(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", adata->fullscale);
+}
+
+static ssize_t st_accel_sysfs_set_fullscale(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err; + unsigned int fs; + struct st_accel_fullscale_available fs_out; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct st_accel_data *adata = iio_priv(indio_dev);
+
+ err = kstrtoint(buf, 10, &fs);
+ if (err < 0)
+ goto conversion_error;
+
+ mutex_lock(&indio_dev->mlock); + err = st_accel_match_fs(&st_accel_sensors[adata->index], fs, &fs_out); + if (err < 0)
+ goto match_fullscale_error;
+
+ err = st_accel_set_fullscale(indio_dev, &fs_out);
+ if (err < 0) {
+ dev_err(&indio_dev->dev,
+ "failed to set new fullscale. (errn %d).\n", err);
+ }
+
+match_fullscale_error:
+ mutex_unlock(&indio_dev->mlock);
+conversion_error:
+ return size;
+}
+
+static ssize_t st_accel_sysfs_fullscale_available(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i, len = 0; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct st_accel_data *adata = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ for (i = 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].fs.fs_avl);
+ i++) {
+ if (st_accel_sensors[adata->index].fs.fs_avl[i].num == 0)
+ break;
+
+ len += sprintf(buf+len, "%d ",
+ st_accel_sensors[adata->index].fs.fs_avl[i].num);
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ len--; + len += sprintf(buf+len, "\n"); + return len;
+}
+
+static ssize_t st_accel_sysfs_sampling_frequency_available(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i, len = 0; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct st_accel_data *adata = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ for (i = 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].odr.odr_avl);
+ i++) {
+ if (st_accel_sensors[adata->index].odr.odr_avl[i].hz == 0)
+ break;
+
+ len += sprintf(buf+len, "%d ",
+ st_accel_sensors[adata->index].odr.odr_avl[i].hz);
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ len--; + len += sprintf(buf+len, "\n"); + return len;
+}
+
+/**
+ * IIO_DEVICE_ATTR - sampling_frequency_available
+ * @read: show all frequency available of the sensor.
+ *
+ */
+
+static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
+ st_accel_sysfs_sampling_frequency_available, NULL , 0);
+
+/**
+ * IIO_DEVICE_ATTR - fullscale_available
+ * @read: show all fullscale available of the sensor.
+ *
+ */
+
+static IIO_DEVICE_ATTR(fullscale_available, S_IRUGO,
+ st_accel_sysfs_fullscale_available, NULL , 0);
+
+/**
+ * IIO_DEVICE_ATTR - fullscale
+ * @read: show the current fullscale of the sensor.
+ * @write: store the current fullscale of the sensor.
+ *
+ */
+
+static IIO_DEVICE_ATTR(fullscale, S_IWUSR | S_IRUGO,
+ st_accel_sysfs_get_fullscale, st_accel_sysfs_set_fullscale , 0);
+
+/**
+ * IIO_DEVICE_ATTR - enable
+ * @read: show the current status of the sensor.
+ * @write: power on/off the sensor.
+ *
+ */
+
+static IIO_DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, st_accel_sysfs_get_enable,
+ st_accel_sysfs_set_enable , 0);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ st_accel_sysfs_get_sampling_frequency,
+ st_accel_sysfs_set_sampling_frequency);
+
+static struct attribute *st_accel_attributes[] = {
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_fullscale_available.dev_attr.attr, + &iio_dev_attr_fullscale.dev_attr.attr, + &iio_dev_attr_enable.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, + NULL,
+};
+
+static const struct attribute_group st_accel_attribute_group = {
+ .attrs = st_accel_attributes,
+};
+
+static const struct iio_info acc_info = {
+ .driver_module = THIS_MODULE, + .attrs = &st_accel_attribute_group, + .read_raw = &st_accel_read_raw,
+};
+
+static int st_accel_init_sensor(struct iio_dev *indio_dev) +{
+ int err; + struct st_accel_odr_available odr_out; + struct st_accel_fullscale_available fs_out; + struct st_accel_data *adata = iio_priv(indio_dev);
+
+ err = st_accel_set_enable(indio_dev, ST_ACCEL_OFF);
+ if (err < 0)
+ goto init_error;
+
+ st_accel_match_fs(&st_accel_sensors[adata->index],
+ adata->fullscale, &fs_out);
+ err = st_accel_set_fullscale(indio_dev, &fs_out);
+ if (err < 0)
+ goto init_error;
+
+ st_accel_match_odr(&st_accel_sensors[adata->index],
+ adata->odr, &odr_out);
+ err = st_accel_set_odr(indio_dev, &odr_out);
+ if (err < 0)
+ goto init_error;
+
+ err = st_accel_set_bdu(indio_dev,
+ &st_accel_sensors[adata->index].bdu, (u8)ST_ACCEL_ON);
+ if (err < 0)
+ goto init_error;
+
+ return 0;
+
+init_error:
+ dev_err(&indio_dev->dev, "failed to init sensor data.");
+ return -EIO;
+}
+
+int st_accel_iio_probe(struct iio_dev *indio_dev) +{
+ int err; + u8 wai; + struct st_accel_data *adata = iio_priv(indio_dev); + struct st_accel_platform_data *pdata; + + mutex_init(&adata->slock); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &acc_info;
+
+ err = st_accel_get_wai_device(indio_dev,
+ ST_ACCEL_DEFAULT_WAI_ADDRESS, &wai);
+ if (err < 0)
+ goto st_accel_iio_probe_error;
+
+ err = st_accel_check_device_list(indio_dev, wai);
+ if (err < 0)
+ goto st_accel_iio_probe_error;
+
+ adata->multiread_bit = st_accel_sensors[adata->index].multi_read_bit;
+ indio_dev->channels = st_accel_sensors[adata->index].ch;
+ indio_dev->num_channels = ST_ACCEL_NUMBER_ALL_CHANNELS;
+ pdata = adata->dev->platform_data;
+ if (pdata == NULL) {+ adata->fullscale = st_accel_default_pdata.fullscale;
+ adata->odr = st_accel_default_pdata.sampling_frequency;
+ } else {
+ adata->fullscale = pdata->fullscale;
+ adata->odr = pdata->sampling_frequency;
+ }
+
+ err = st_accel_init_sensor(indio_dev);
+ if (err < 0)
+ goto st_accel_iio_probe_error;
+
+ err = st_accel_allocate_ring(indio_dev);
+ if (err < 0)
+ goto st_accel_iio_probe_error;
+
+ if (*adata->irq_data_ready > 0) {
+ err = st_accel_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;
+
+ return err;
+
+iio_device_register_error:
+ st_accel_remove_trigger(indio_dev);
+acc_probe_trigger_error:
+ st_accel_deallocate_ring(indio_dev);
+st_accel_iio_probe_error:
+ return err;
+}
+EXPORT_SYMBOL(st_accel_iio_probe);
+
+void st_accel_iio_remove(struct iio_dev *indio_dev) +{
+ iio_device_unregister(indio_dev); + st_accel_remove_trigger(indio_dev); + st_accel_deallocate_ring(indio_dev); + iio_device_free(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_iio_remove);
+
+MODULE_AUTHOR("Denis Ciocca <[email protected]>"); +MODULE_DESCRIPTION("STMicroelectronics accelerometers driver"); +MODULE_LICENSE("GPL v2");
(quoted diff) +0 -0
b/drivers/iio/accel/st_accel_i2c.c
new file mode 100644
index 0000000..7cfeb4c
drivers/iio/accel/st_accel_i2c.c new file +139 -0
139 lines not on this page · raw message
(quoted diff) +0 -0
b/drivers/iio/accel/st_accel_spi.c
new file mode 100644
index 0000000..40279bd
drivers/iio/accel/st_accel_spi.c new file +199 -0
199 lines not on this page · raw message
(quoted diff) +0 -0
b/drivers/iio/accel/st_accel_trigger.c
new file mode 100644
index 0000000..7b57b7f
drivers/iio/accel/st_accel_trigger.c new file +22 -0
@@ -0,0 +1,84 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> + +#include <linux/iio/accel/st_accel.h> + + +static int st_accel_trig_acc_set_state(struct iio_trigger *trig, bool
state)
+{
+ struct iio_dev *indio_dev = trig->private_data;
+ return st_accel_set_dataready_irq(indio_dev, state);
+}
+
+static const struct iio_trigger_ops st_accel_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = &st_accel_trig_acc_set_state,
+};
+
+int st_accel_probe_trigger(struct iio_dev *indio_dev) +{
+ int err;
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ adata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
+ if (adata->trig == NULL) {
+ err = -ENOMEM; + dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n"); + goto iio_trigger_alloc_error; + } + + err = request_threaded_irq(*adata->irq_data_ready, + iio_trigger_generic_data_rdy_poll, + NULL, + IRQF_TRIGGER_RISING, + adata->trig->name, + adata->trig); + if (err) + goto request_irq_error; + + adata->trig->private_data = indio_dev; + adata->trig->ops = &st_accel_trigger_ops; + adata->trig->dev.parent = adata->dev;
+
+ err = iio_trigger_register(adata->trig);
+ if (err < 0) {
+ dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
+ goto iio_trigger_register_error;
+ }
+ indio_dev->trig = adata->trig;
+
+ 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;
+}
+EXPORT_SYMBOL(st_accel_probe_trigger);
+
+void st_accel_remove_trigger(struct iio_dev *indio_dev) +{
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ iio_trigger_unregister(adata->trig); + free_irq(*adata->irq_data_ready, adata->trig); + iio_trigger_free(adata->trig);
+}
+EXPORT_SYMBOL(st_accel_remove_trigger);
(quoted diff) +0 -0
b/include/linux/iio/accel/st_accel.h
new file mode 100644
index 0000000..1386488
include/linux/iio/accel/st_accel.h new file +119 -0
119 lines not on this page · raw message
(quoted diff) +0 -0
b/include/linux/platform_data/st_accel_pdata.h
new file mode 100644
index 0000000..416489b
include/linux/platform_data/st_accel_pdata.h new file +27 -0
@@ -0,0 +1,27 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + + +#ifndef ST_ACCEL_PDATA_H +#define ST_ACCEL_PDATA_H + + +/** +* struct st_accel_platform_data - ST accel device platform data +* @fullscale: Value of fullscale used for the sensor. +* @sampling_frequency: Value of sampling frequency used for the sensor. +*/ + +struct st_accel_platform_data { +int fullscale; +int sampling_frequency; +}; + +#endif /* ST_ACCEL_PDATA_H */
--
1.7.0.4
Jonathan Cameron
maintainerQuoted 10 lines · Denis CIOCCA
Hi guys,
according to Lars-Peter requests, I have modified my driver. (I hope I have checked everything).
I have only one things aboutread_allfunction on the buffer source file:
I think the best solution is reading every time of all output registers, these are three-axial accelerometers, the overhead for support the power on/off of single axis is too much big for the purpose (because if I don't power off the single axis the data-ready doesn't reset). What do you think about that?
For the non buffered case - reading is slow anyway so if anyone cares they will be doing buffered reads. For buffered reads this isn't going to change often (and there is a lot of cost associated with bringing the buffer up and down anyway) so a little cost in disabling the channel is minor compared to the cost of hammering the bus unecessarily - particularly with good old slow i2c.
So I'd be inclined to turn on only channels we care about. Do you have an estimate of how long it will take to turn one on for a single read?
Quoted 2,132 lines
Thanks
Denis
From
f2f556001170e8698a1bbf0146ed2d11e75436b1Mon Sep 17 00:00:00 2001
From: Denis Ciocca <[email protected]>
Date: Mon, 22 Oct 2012 11:17:27 +0200
Subject: [PATCH] iio:accel: Add STMicroelectronics accelerometers driverThis patch adds generic accelerometer driver for STMicroelectronics accelerometers, currently it supports:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D, LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330Signed-off-by Denis Ciocca <[email protected]>--- drivers/iio/accel/Kconfig | 37 + drivers/iio/accel/Makefile | 6 + drivers/iio/accel/st_accel_buffer.c | 103 ++ drivers/iio/accel/st_accel_core.c | 1301 ++++++++++++++++++++++++++ drivers/iio/accel/st_accel_i2c.c | 139 +++ drivers/iio/accel/st_accel_spi.c | 199 ++++ drivers/iio/accel/st_accel_trigger.c | 84 ++ include/linux/iio/accel/st_accel.h | 119 +++ include/linux/platform_data/st_accel_pdata.h | 27 + 9 files changed, 2015 insertions(+), 0 deletions(-)create mode 100644
drivers/iio/accel/st_accel_buffer.ccreate mode 100644drivers/iio/accel/st_accel_core.ccreate mode 100644drivers/iio/accel/st_accel_i2c.ccreate mode 100644drivers/iio/accel/st_accel_spi.ccreate mode 100644drivers/iio/accel/st_accel_trigger.ccreate mode 100644include/linux/iio/accel/st_accel.hcreate mode 100644include/linux/platform_data/st_accel_pdata.hdrivers/iio/accel/Kconfig +37
-0@@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D Say yes here to build support for the HID SENSOR accelerometers 3D. +config ST_ACCEL_3AXIS + tristate "STMicroelectronics accelerometers 3-Axis Driver" + depends on (I2C || SPI) && SYSFS + help + Say yes here to build support for STMicroelectronics accelerometers: + LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D, + LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330. + + This driver can also be built as a module. If so, the module + will be called st_accel. + +config ST_ACCEL_3AXIS_I2C + tristate "support I2C bus connection" + depends on ST_ACCEL_3AXIS && I2C + help + Say yes here to build I2C support for STMicroelectronics accelerometers. + + To compile this driver as a module, choose M here: the + module will be called st_accel_i2c. + +config ST_ACCEL_3AXIS_SPI + tristate "support SPI bus connection" + depends on ST_ACCEL_3AXIS && SPI_MASTER + help + Say yes here to build SPI support for STMicroelectronics accelerometers. + + To compile this driver as a module, choose M here: the + module will be called st_accel_spi. + +config ST_ACCEL_3AXIS_TRIGGERED_BUFFER + tristate "support triggered buffer" + depends on ST_ACCEL_3AXIS + select IIO_TRIGGERED_BUFFER + select IIO_BUFFER + help + Default trigger and buffer for STMicroelectronics accelerometers driver. + endmenudrivers/iio/accel/Makefile +5
-0@@ -3,3 +3,9 @@ # obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o + +st_accel-y := st_accel_core.o +obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) += st_accel_i2c.o +obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) += st_accel_spi.o +obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) += st_accel_trigger.ost_accel_buffer.o
drivers/iio/accel/Makefile +1
-0+obj-$(CONFIG_ST_ACCEL_3AXIS) += st_accel.odrivers/iio/accel/st_accel_buffer.c new file +103
-0@@ -0,0 +1,103 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/module.h> +#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/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#include <linux/iio/accel/st_accel.h> + + +#define ST_ACCEL_NUMBER_DATA_CHANNELS 3 + +static int st_accel_read_all (struct iio_dev *indio_dev,u8 *rx_array) +{ +int len; +struct st_accel_data *adata =iio_priv (indio_dev); + + len = adata->read_multiple_byte (adata, + indio_dev->channels[ST_ACCEL_SCAN_X ].address, +ST_ACCEL_BYTE_FOR_CHANNEL *ST_ACCEL_NUMBER_DATA_CHANNELS , + rx_array); +if (len <0 ) + goto read_error; + +return len; + +read_error: +return -EIO ; +} + +static int st_accel_get_buffer_element (struct iio_dev *indio_dev,u8 *buf) +{ +int ret, i, n =0 ; +u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL *ST_ACCEL_NUMBER_DATA_CHANNELS ]; +s16 *data = (s16 *)buf; + + ret =st_accel_read_all (indio_dev, rx_array); +if (ret <0 ) +return ret; + +for (i =0 ; i <ST_ACCEL_NUMBER_DATA_CHANNELS ; i++) { +if (test_bit (i, indio_dev->active_scan_mask)) { + data[n] = (s16)(((s16)(rx_array[2 *i+1 ]) <<8 ) + | rx_array[2 *i]); + n++; + } + } + +return n*sizeof (data[0 ]); +} + +static irqreturn_t st_accel_trigger_handler (int irq,void *p) +{ +struct iio_poll_func *pf = p; +struct iio_dev *indio_dev = pf->indio_dev; +int len =0 ; +char *data; + + data =kmalloc (indio_dev->scan_bytes,GFP_KERNEL ); +if (data ==NULL ) + goto done; +if (!bitmap_empty (indio_dev->active_scan_mask, indio_dev->masklength)) + len =st_accel_get_buffer_element (indio_dev, data); +else + goto done; +if (indio_dev->scan_timestamp) + *(s64 *)((u8 *)data +ALIGN (len,sizeof (s64))) = pf->timestamp; +iio_push_to_buffer (indio_dev->buffer, data); +kfree (data); + +done: +iio_trigger_notify_done (indio_dev->trig); +return IRQ_HANDLED ; +} + +int st_accel_allocate_ring (struct iio_dev *indio_dev) +{ + indio_dev->scan_timestamp = true; +return iio_triggered_buffer_setup (indio_dev, &iio_pollfunc_store_time, + &st_accel_trigger_handler,NULL ); +} +EXPORT_SYMBOL (st_accel_allocate_ring); + +void st_accel_deallocate_ring (struct iio_dev *indio_dev) +{ +iio_triggered_buffer_cleanup (indio_dev); +} +EXPORT_SYMBOL (st_accel_deallocate_ring);drivers/iio/accel/st_accel_core.c new file +669
-0@@ -0,0 +1,1301 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/mutex.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/irq.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/buffer.h> + +#include <linux/iio/accel/st_accel.h> +#include <linux/platform_data/st_accel_pdata.h> + + +#define UG_TO_MS2 (x) (x*9800) +#define ST_ACCEL_FULLSCALE_AVL_MAX 5 +#define ST_ACCEL_ODR_LIST_MAX 10 +#define ST_ACCEL_ON 1 +#define ST_ACCEL_OFF 0 + +/* DEFAULT VALUE FOR SENSORS */ +#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR 0x28 +#define ST_ACCEL_DEFAULT_OUT_X_H_ADDR 0x29 +#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR 0x2a +#define ST_ACCEL_DEFAULT_OUT_Y_H_ADDR 0x2b +#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR 0x2c +#define ST_ACCEL_DEFAULT_OUT_Z_H_ADDR 0x2d +#define ST_ACCEL_DEFAULT_WAI_ADDRESS 0x0f +#define ST_ACCEL_DEFAULT_POWER_ON_VALUE 0x01 +#define ST_ACCEL_DEFAULT_POWER_OFF_VALUE 0x00 +#define ST_ACCEL_DEFAULT_12_REALBITS 12 +#define ST_ACCEL_DEFAULT_16_REALBITS 16 + +/* ODR */ +#define ST_ACCEL_ODR_AVL_1HZ 1 +#define ST_ACCEL_ODR_AVL_3HZ 3 +#define ST_ACCEL_ODR_AVL_6HZ 6 +#define ST_ACCEL_ODR_AVL_10HZ 10 +#define ST_ACCEL_ODR_AVL_12HZ 12 +#define ST_ACCEL_ODR_AVL_25HZ 25 +#define ST_ACCEL_ODR_AVL_50HZ 50 +#define ST_ACCEL_ODR_AVL_100HZ 100 +#define ST_ACCEL_ODR_AVL_200HZ 200 +#define ST_ACCEL_ODR_AVL_400HZ 400 +#define ST_ACCEL_ODR_AVL_800HZ 800 +#define ST_ACCEL_ODR_AVL_1000HZ 1000 +#define ST_ACCEL_ODR_AVL_1600HZ 1600 + +/* FULLSCALE */ +#define ST_ACCEL_FS_AVL_2G 2 +#define ST_ACCEL_FS_AVL_4G 4 +#define ST_ACCEL_FS_AVL_6G 6 +#define ST_ACCEL_FS_AVL_8G 8 +#define ST_ACCEL_FS_AVL_16G 16 + +/* CUSTOM VALUES FOR SENSOR 1 */ +#define ST_ACCEL_1_WAI_EXP 0x33 +#define ST_ACCEL_1_ODR_ADDR 0x20 +#define ST_ACCEL_1_ODR_MASK 0xf0 +#define ST_ACCEL_1_ODR_N_BIT 4 +#define ST_ACCEL_1_ODR_AVL_1HZ_VAL 0x01 +#define ST_ACCEL_1_ODR_AVL_10HZ_VAL 0x02 +#define ST_ACCEL_1_ODR_AVL_25HZ_VAL 0x03 +#define ST_ACCEL_1_ODR_AVL_50HZ_VAL 0x04 +#define ST_ACCEL_1_ODR_AVL_100HZ_VAL 0x05 +#define ST_ACCEL_1_ODR_AVL_200HZ_VAL 0x06 +#define ST_ACCEL_1_ODR_AVL_400HZ_VAL 0x07 +#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL 0x08 +#define ST_ACCEL_1_FS_N_BIT 2 +#define ST_ACCEL_1_FS_ADDR 0x23 +#define ST_ACCEL_1_FS_MASK 0x30 +#define ST_ACCEL_1_FS_AVL_2_VAL 0x00 +#define ST_ACCEL_1_FS_AVL_4_VAL 0x01 +#define ST_ACCEL_1_FS_AVL_8_VAL 0x02 +#define ST_ACCEL_1_FS_AVL_16_VAL 0x03 +#define ST_ACCEL_1_FS_AVL_2_GAIN 1000 +#define ST_ACCEL_1_FS_AVL_4_GAIN 2000 +#define ST_ACCEL_1_FS_AVL_8_GAIN 4000 +#define ST_ACCEL_1_FS_AVL_16_GAIN 12000 +#define ST_ACCEL_1_BDU_ADDR 0x23 +#define ST_ACCEL_1_BDU_MASK 0x80 +#define ST_ACCEL_1_DRDY_IRQ_ADDR 0x22 +#define ST_ACCEL_1_DRDY_IRQ_MASK 0x10 +#define ST_ACCEL_1_MULTIREAD_BIT true + +/* CUSTOM VALUES FOR SENSOR 2 */ +#define ST_ACCEL_2_WAI_EXP 0x49 +#define ST_ACCEL_2_ODR_ADDR 0x20 +#define ST_ACCEL_2_ODR_MASK 0xf0 +#define ST_ACCEL_2_ODR_N_BIT 4 +#define ST_ACCEL_2_ODR_AVL_3HZ_VAL 0x01 +#define ST_ACCEL_2_ODR_AVL_6HZ_VAL 0x02 +#define ST_ACCEL_2_ODR_AVL_12HZ_VAL 0x03 +#define ST_ACCEL_2_ODR_AVL_25HZ_VAL 0x04 +#define ST_ACCEL_2_ODR_AVL_50HZ_VAL 0x05 +#define ST_ACCEL_2_ODR_AVL_100HZ_VAL 0x06 +#define ST_ACCEL_2_ODR_AVL_200HZ_VAL 0x07 +#define ST_ACCEL_2_ODR_AVL_400HZ_VAL 0x08 +#define ST_ACCEL_2_ODR_AVL_800HZ_VAL 0x09 +#define ST_ACCEL_2_ODR_AVL_1600HZ_VAL 0x0a +#define ST_ACCEL_2_FS_N_BIT 3 +#define ST_ACCEL_2_FS_ADDR 0x21 +#define ST_ACCEL_2_FS_MASK 0x38 +#define ST_ACCEL_2_FS_AVL_2_VAL 0X00 +#define ST_ACCEL_2_FS_AVL_4_VAL 0X01 +#define ST_ACCEL_2_FS_AVL_6_VAL 0x02 +#define ST_ACCEL_2_FS_AVL_8_VAL 0x03 +#define ST_ACCEL_2_FS_AVL_16_VAL 0x04 +#define ST_ACCEL_2_FS_AVL_2_GAIN 61 +#define ST_ACCEL_2_FS_AVL_4_GAIN 122 +#define ST_ACCEL_2_FS_AVL_6_GAIN 183 +#define ST_ACCEL_2_FS_AVL_8_GAIN 244 +#define ST_ACCEL_2_FS_AVL_16_GAIN 732 +#define ST_ACCEL_2_BDU_ADDR 0x20 +#define ST_ACCEL_2_BDU_MASK 0x08 +#define ST_ACCEL_2_DRDY_IRQ_ADDR 0x22 +#define ST_ACCEL_2_DRDY_IRQ_MASK 0x04 +#define ST_ACCEL_2_MULTIREAD_BIT true + +/* CUSTOM VALUES FOR SENSOR 3 */ +#define ST_ACCEL_3_WAI_EXP 0x32 +#define ST_ACCEL_3_ODR_ADDR 0x20 +#define ST_ACCEL_3_ODR_MASK 0x18 +#define ST_ACCEL_3_ODR_N_BIT 2 +#define ST_ACCEL_3_ODR_AVL_50HZ_VAL 0x00 +#define ST_ACCEL_3_ODR_AVL_100HZ_VAL 0x01 +#define ST_ACCEL_3_ODR_AVL_400HZ_VAL 0x02 +#define ST_ACCEL_3_ODR_AVL_1000HZ_VAL 0x03 +#define ST_ACCEL_3_PW_ADDR 0x20 +#define ST_ACCEL_3_PW_MASK 0xe0 +#define ST_ACCEL_3_PW_N_BIT 3 +#define ST_ACCEL_3_PW_VALUE_ON 0x01 +#define ST_ACCEL_3_PW_VALUE_OFF 0x00 +#define ST_ACCEL_3_FS_N_BIT 2 +#define ST_ACCEL_3_FS_ADDR 0x23 +#define ST_ACCEL_3_FS_MASK 0x30 +#define ST_ACCEL_3_FS_AVL_2_VAL 0X00 +#define ST_ACCEL_3_FS_AVL_4_VAL 0X01 +#define ST_ACCEL_3_FS_AVL_8_VAL 0x03 +#define ST_ACCEL_3_FS_AVL_2_GAIN 1000 +#define ST_ACCEL_3_FS_AVL_4_GAIN 2000 +#define ST_ACCEL_3_FS_AVL_8_GAIN 3900 +#define ST_ACCEL_3_BDU_ADDR 0x23 +#define ST_ACCEL_3_BDU_MASK 0x80 +#define ST_ACCEL_3_DRDY_IRQ_ADDR 0x22 +#define ST_ACCEL_3_DRDY_IRQ_MASK 0x02 +#define ST_ACCEL_3_MULTIREAD_BIT true + +/* CUSTOM VALUES FOR SENSOR 4 */ +#define ST_ACCEL_4_WAI_EXP 0x40 +#define ST_ACCEL_4_ODR_ADDR 0x20 +#define ST_ACCEL_4_ODR_MASK 0xf0 +#define ST_ACCEL_4_ODR_N_BIT 4 +#define ST_ACCEL_4_ODR_AVL_3HZ_VAL 0x01 +#define ST_ACCEL_4_ODR_AVL_6HZ_VAL 0x02 +#define ST_ACCEL_4_ODR_AVL_12HZ_VAL 0x03 +#define ST_ACCEL_4_ODR_AVL_25HZ_VAL 0x04 +#define ST_ACCEL_4_ODR_AVL_50HZ_VAL 0x05 +#define ST_ACCEL_4_ODR_AVL_100HZ_VAL 0x06 +#define ST_ACCEL_4_ODR_AVL_200HZ_VAL 0x07 +#define ST_ACCEL_4_ODR_AVL_400HZ_VAL 0x08 +#define ST_ACCEL_4_ODR_AVL_800HZ_VAL 0x09 +#define ST_ACCEL_4_ODR_AVL_1600HZ_VAL 0x0a +#define ST_ACCEL_4_FS_N_BIT 3 +#define ST_ACCEL_4_FS_ADDR 0x24 +#define ST_ACCEL_4_FS_MASK 0x38 +#define ST_ACCEL_4_FS_AVL_2_VAL 0X00 +#define ST_ACCEL_4_FS_AVL_4_VAL 0X01 +#define ST_ACCEL_4_FS_AVL_6_VAL 0x02 +#define ST_ACCEL_4_FS_AVL_8_VAL 0x03 +#define ST_ACCEL_4_FS_AVL_16_VAL 0x04 +#define ST_ACCEL_4_FS_AVL_2_GAIN 61 +#define ST_ACCEL_4_FS_AVL_4_GAIN 122 +#define ST_ACCEL_4_FS_AVL_6_GAIN 183 +#define ST_ACCEL_4_FS_AVL_8_GAIN 244 +#define ST_ACCEL_4_FS_AVL_16_GAIN 732 +#define ST_ACCEL_4_BDU_ADDR 0x20 +#define ST_ACCEL_4_BDU_MASK 0x08 +#define ST_ACCEL_4_DRDY_IRQ_ADDR 0x23 +#define ST_ACCEL_4_DRDY_IRQ_MASK 0x80 +#define ST_ACCEL_4_IG1_EN_ADDR 0x23 +#define ST_ACCEL_4_IG1_EN_MASK 0x08 +#define ST_ACCEL_4_MULTIREAD_BIT false + +struct st_accel_odr_available { +unsigned int hz; +u8 value; +}; + +struct st_accel_odr { +u8 addr; +u8 mask; +short num_bit; +struct st_accel_odr_available odr_avl[ST_ACCEL_ODR_LIST_MAX ]; +}; + +struct st_accel_power { +u8 addr; +u8 mask; +unsigned short num_bit; +u8 value_off; +u8 value_on; +}; + +struct st_accel_fullscale_available { +unsigned int num; +u8 value; +unsigned int gain; +}; + +struct st_accel_fullscale { +u8 addr; +u8 mask; +unsigned short num_bit; +struct st_accel_fullscale_available fs_avl[ST_ACCEL_FULLSCALE_AVL_MAX ]; +}; + +struct st_accel_bdu { +u8 addr; +u8 mask; +}; + +struct st_accel_interrupt_generator { +u8 en_addr; +u8 latch_mask_addr; +u8 en_mask; +u8 latching_mask; +}; + +struct st_accel_data_ready_irq { +u8 addr; +u8 mask; +struct st_accel_interrupt_generator ig1; +}; + +#define ST_ACCEL_LSM_CHANNELS (index, mod, endian, bits, addr) \ +{ \ + .type = IIO_ACCEL, \ + .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, \ + }, \ +} + +static const struct iio_chan_spec st_accel_12bit_channels[] = { +ST_ACCEL_LSM_CHANNELS (ST_ACCEL_SCAN_X ,IIO_MOD_X ,IIO_LE , +ST_ACCEL_DEFAULT_12_REALBITS ,ST_ACCEL_DEFAULT_OUT_X_L_ADDR ), +ST_ACCEL_LSM_CHANNELS (ST_ACCEL_SCAN_Y ,IIO_MOD_Y ,IIO_LE , +ST_ACCEL_DEFAULT_12_REALBITS ,ST_ACCEL_DEFAULT_OUT_Y_L_ADDR ), +ST_ACCEL_LSM_CHANNELS (ST_ACCEL_SCAN_Z ,IIO_MOD_Z ,IIO_LE , +ST_ACCEL_DEFAULT_12_REALBITS ,ST_ACCEL_DEFAULT_OUT_Z_L_ADDR ), +IIO_CHAN_SOFT_TIMESTAMP (3 ) +}; + +static const struct iio_chan_spec st_accel_16bit_channels[] = { +ST_ACCEL_LSM_CHANNELS (ST_ACCEL_SCAN_X ,IIO_MOD_X ,IIO_LE , +ST_ACCEL_DEFAULT_16_REALBITS ,ST_ACCEL_DEFAULT_OUT_X_L_ADDR ), +ST_ACCEL_LSM_CHANNELS (ST_ACCEL_SCAN_Y ,IIO_MOD_Y ,IIO_LE , +ST_ACCEL_DEFAULT_16_REALBITS ,ST_ACCEL_DEFAULT_OUT_Y_L_ADDR ), +ST_ACCEL_LSM_CHANNELS (ST_ACCEL_SCAN_Z ,IIO_MOD_Z ,IIO_LE , +ST_ACCEL_DEFAULT_16_REALBITS ,ST_ACCEL_DEFAULT_OUT_Z_L_ADDR ), +IIO_CHAN_SOFT_TIMESTAMP (3 ) +}; + +static const struct st_accel_platform_data st_accel_default_pdata = { + .fullscale =ST_ACCEL_FS_AVL_2G , + .sampling_frequency =ST_ACCEL_ODR_AVL_100HZ , +}; + +/** +* struct st_accel_sensors - ST accel 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. +* @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. +* +*/ + +static const struct st_accel_sensors { +u8 wai; +struct iio_chan_spec *ch; +struct st_accel_odr odr; +struct st_accel_power pw; +struct st_accel_fullscale fs; +struct st_accel_bdu bdu; +struct st_accel_data_ready_irq drdy_irq; +bool multi_read_bit; +} st_accel_sensors[] = { + { + .wai =ST_ACCEL_1_WAI_EXP , + .ch = (struct iio_chan_spec *)st_accel_12bit_channels, + .odr = { + .addr =ST_ACCEL_1_ODR_ADDR , + .mask =ST_ACCEL_1_ODR_MASK , + .num_bit =ST_ACCEL_1_ODR_N_BIT , + .odr_avl = { + [0 ] = { + .hz =ST_ACCEL_ODR_AVL_1HZ , + .value =ST_ACCEL_1_ODR_AVL_1HZ_VAL , + }, + [1 ] = { + .hz =ST_ACCEL_ODR_AVL_10HZ , + .value =ST_ACCEL_1_ODR_AVL_10HZ_VAL , + }, + [2 ] = { + .hz =ST_ACCEL_ODR_AVL_25HZ , + .value =ST_ACCEL_1_ODR_AVL_25HZ_VAL , + }, + [3 ] = { + .hz =ST_ACCEL_ODR_AVL_50HZ , + .value =ST_ACCEL_1_ODR_AVL_50HZ_VAL , + }, + [4 ] = { + .hz =ST_ACCEL_ODR_AVL_100HZ , + .value =ST_ACCEL_1_ODR_AVL_100HZ_VAL , + }, + [5 ] = { + .hz =ST_ACCEL_ODR_AVL_200HZ , + .value =ST_ACCEL_1_ODR_AVL_200HZ_VAL , + }, + [6 ] = { + .hz =ST_ACCEL_ODR_AVL_400HZ , + .value =ST_ACCEL_1_ODR_AVL_400HZ_VAL , + }, + [7 ] = { + .hz =ST_ACCEL_ODR_AVL_1600HZ , + .value =ST_ACCEL_1_ODR_AVL_1600HZ_VAL , + }, + }, + }, + .pw = { + .addr =ST_ACCEL_1_ODR_ADDR , + .mask =ST_ACCEL_1_ODR_MASK , + .num_bit =ST_ACCEL_1_ODR_N_BIT , + .value_off =ST_ACCEL_DEFAULT_POWER_OFF_VALUE , + }, + .fs = { + .addr =ST_ACCEL_1_FS_ADDR , + .mask =ST_ACCEL_1_FS_MASK , + .num_bit =ST_ACCEL_1_FS_N_BIT , + .fs_avl = { + [0 ] = { + .num =ST_ACCEL_FS_AVL_2G , + .value =ST_ACCEL_1_FS_AVL_2_VAL , + .gain =ST_ACCEL_1_FS_AVL_2_GAIN , + }, + [1 ] = { + .num =ST_ACCEL_FS_AVL_4G , + .value =ST_ACCEL_1_FS_AVL_4_VAL , + .gain =ST_ACCEL_1_FS_AVL_4_GAIN , + }, + [2 ] = { + .num =ST_ACCEL_FS_AVL_8G , + .value =ST_ACCEL_1_FS_AVL_8_VAL , + .gain =ST_ACCEL_1_FS_AVL_8_GAIN , + }, + [3 ] = { + .num =ST_ACCEL_FS_AVL_16G , + .value =ST_ACCEL_1_FS_AVL_16_VAL , + .gain =ST_ACCEL_1_FS_AVL_16_GAIN , + }, + }, + }, + .bdu = { + .addr =ST_ACCEL_1_BDU_ADDR , + .mask =ST_ACCEL_1_BDU_MASK , + }, + .drdy_irq = { + .addr =ST_ACCEL_1_DRDY_IRQ_ADDR , + .mask =ST_ACCEL_1_DRDY_IRQ_MASK , + }, + .multi_read_bit =ST_ACCEL_1_MULTIREAD_BIT , + }, + { + .wai =ST_ACCEL_2_WAI_EXP , + .ch = (struct iio_chan_spec *)st_accel_16bit_channels, + .odr = { + .addr =ST_ACCEL_2_ODR_ADDR , + .mask =ST_ACCEL_2_ODR_MASK , + .num_bit =ST_ACCEL_2_ODR_N_BIT , + .odr_avl = { + [0 ] = { + .hz =ST_ACCEL_ODR_AVL_3HZ , + .value =ST_ACCEL_2_ODR_AVL_3HZ_VAL , + }, + [1 ] = { + .hz =ST_ACCEL_ODR_AVL_6HZ , + .value =ST_ACCEL_2_ODR_AVL_6HZ_VAL , + }, + [2 ] = { + .hz =ST_ACCEL_ODR_AVL_12HZ , + .value =ST_ACCEL_2_ODR_AVL_12HZ_VAL , + }, + [3 ] = { + .hz =ST_ACCEL_ODR_AVL_25HZ , + .value =ST_ACCEL_2_ODR_AVL_25HZ_VAL , + }, + [4 ] = { + .hz =ST_ACCEL_ODR_AVL_50HZ , + .value =ST_ACCEL_2_ODR_AVL_50HZ_VAL , + }, + [5 ] = { + .hz =ST_ACCEL_ODR_AVL_100HZ , + .value =ST_ACCEL_2_ODR_AVL_100HZ_VAL , + }, + [6 ] = { + .hz =ST_ACCEL_ODR_AVL_200HZ , + .value =ST_ACCEL_2_ODR_AVL_200HZ_VAL , + }, + [7 ] = { + .hz =ST_ACCEL_ODR_AVL_400HZ , + .value =ST_ACCEL_2_ODR_AVL_400HZ_VAL , + }, + [8 ] = { + .hz =ST_ACCEL_ODR_AVL_800HZ , + .value =ST_ACCEL_2_ODR_AVL_800HZ_VAL , + }, + [9 ] = { + .hz =ST_ACCEL_ODR_AVL_1600HZ , + .value =ST_ACCEL_2_ODR_AVL_1600HZ_VAL , + }, + }, + }, + .pw = { + .addr =ST_ACCEL_2_ODR_ADDR , + .mask =ST_ACCEL_2_ODR_MASK , + .num_bit =ST_ACCEL_2_ODR_N_BIT , + .value_off =ST_ACCEL_DEFAULT_POWER_OFF_VALUE , + }, + .fs = { + .addr =ST_ACCEL_2_FS_ADDR , + .mask =ST_ACCEL_2_FS_MASK , + .num_bit =ST_ACCEL_2_FS_N_BIT , + .fs_avl = { + [0 ] = { + .num =ST_ACCEL_FS_AVL_2G , + .value =ST_ACCEL_2_FS_AVL_2_VAL , + .gain =ST_ACCEL_2_FS_AVL_2_GAIN , + }, + [1 ] = { + .num =ST_ACCEL_FS_AVL_4G , + .value =ST_ACCEL_2_FS_AVL_4_VAL , + .gain =ST_ACCEL_2_FS_AVL_4_GAIN , + }, + [2 ] = { + .num =ST_ACCEL_FS_AVL_6G , + .value =ST_ACCEL_2_FS_AVL_6_VAL , + .gain =ST_ACCEL_2_FS_AVL_6_GAIN , + }, + [3 ] = { + .num =ST_ACCEL_FS_AVL_8G , + .value =ST_ACCEL_2_FS_AVL_8_VAL , + .gain =ST_ACCEL_2_FS_AVL_8_GAIN , + }, + [4 ] = { + .num =ST_ACCEL_FS_AVL_16G , + .value =ST_ACCEL_2_FS_AVL_16_VAL , + .gain =ST_ACCEL_2_FS_AVL_16_GAIN , + }, + }, + }, + .drdy_irq = { + .addr =ST_ACCEL_2_DRDY_IRQ_ADDR , + .mask =ST_ACCEL_2_DRDY_IRQ_MASK , + }, + .bdu = { + .addr =ST_ACCEL_2_BDU_ADDR , + .mask =ST_ACCEL_2_BDU_MASK , + }, + .multi_read_bit =ST_ACCEL_2_MULTIREAD_BIT , + }, + { + .wai =ST_ACCEL_3_WAI_EXP , + .ch = (struct iio_chan_spec *)st_accel_12bit_channels, + .odr = { + .addr =ST_ACCEL_3_ODR_ADDR , + .mask =ST_ACCEL_3_ODR_MASK , + .num_bit =ST_ACCEL_3_ODR_N_BIT , + .odr_avl = { + [0 ] = { + .hz =ST_ACCEL_ODR_AVL_50HZ , + .value =ST_ACCEL_3_ODR_AVL_50HZ_VAL , + }, + [1 ] = { + .hz =ST_ACCEL_ODR_AVL_100HZ , + .value =ST_ACCEL_3_ODR_AVL_100HZ_VAL , + }, + [2 ] = { + .hz =ST_ACCEL_ODR_AVL_400HZ , + .value =ST_ACCEL_3_ODR_AVL_400HZ_VAL , + }, + [3 ] = { + .hz =ST_ACCEL_ODR_AVL_1000HZ , + .value =ST_ACCEL_3_ODR_AVL_1000HZ_VAL , + }, + }, + }, + .pw = { + .addr =ST_ACCEL_3_PW_ADDR , + .mask =ST_ACCEL_3_PW_MASK , + .num_bit =ST_ACCEL_3_PW_N_BIT , + .value_on =ST_ACCEL_3_PW_VALUE_ON , + .value_off =ST_ACCEL_3_PW_VALUE_OFF , + }, + .fs = { + .addr =ST_ACCEL_3_FS_ADDR , + .mask =ST_ACCEL_3_FS_MASK , + .num_bit =ST_ACCEL_3_FS_N_BIT , + .fs_avl = { + [0 ] = { + .num =ST_ACCEL_FS_AVL_2G , + .value =ST_ACCEL_3_FS_AVL_2_VAL , + .gain =ST_ACCEL_3_FS_AVL_2_GAIN , + }, + [1 ] = { + .num =ST_ACCEL_FS_AVL_4G , + .value =ST_ACCEL_3_FS_AVL_4_VAL , + .gain =ST_ACCEL_3_FS_AVL_4_GAIN , + }, + [2 ] = { + .num =ST_ACCEL_FS_AVL_8G , + .value =ST_ACCEL_3_FS_AVL_8_VAL , + .gain =ST_ACCEL_3_FS_AVL_8_GAIN , + }, + }, + }, + .bdu = { + .addr =ST_ACCEL_3_BDU_ADDR , + .mask =ST_ACCEL_3_BDU_MASK , + }, + .drdy_irq = { + .addr =ST_ACCEL_3_DRDY_IRQ_ADDR , + .mask =ST_ACCEL_3_DRDY_IRQ_MASK , + }, + .multi_read_bit =ST_ACCEL_3_MULTIREAD_BIT , + }, + { + .wai =ST_ACCEL_4_WAI_EXP , + .ch = (struct iio_chan_spec *)st_accel_16bit_channels, + .odr = { + .addr =ST_ACCEL_4_ODR_ADDR , + .mask =ST_ACCEL_4_ODR_MASK , + .num_bit =ST_ACCEL_4_ODR_N_BIT , + .odr_avl = { + [0 ] = { + .hz =ST_ACCEL_ODR_AVL_3HZ , + .value =ST_ACCEL_4_ODR_AVL_3HZ_VAL , + }, + [1 ] = { + .hz =ST_ACCEL_ODR_AVL_6HZ , + .value =ST_ACCEL_4_ODR_AVL_6HZ_VAL , + }, + [2 ] = { + .hz =ST_ACCEL_ODR_AVL_12HZ , + .value =ST_ACCEL_4_ODR_AVL_12HZ_VAL , + }, + [3 ] = { + .hz =ST_ACCEL_ODR_AVL_25HZ , + .value =ST_ACCEL_4_ODR_AVL_25HZ_VAL , + }, + [4 ] = { + .hz =ST_ACCEL_ODR_AVL_50HZ , + .value =ST_ACCEL_4_ODR_AVL_50HZ_VAL , + }, + [5 ] = { + .hz =ST_ACCEL_ODR_AVL_100HZ , + .value =ST_ACCEL_4_ODR_AVL_100HZ_VAL , + }, + [6 ] = { + .hz =ST_ACCEL_ODR_AVL_200HZ , + .value =ST_ACCEL_4_ODR_AVL_200HZ_VAL , + }, + [7 ] = { + .hz =ST_ACCEL_ODR_AVL_400HZ , + .value =ST_ACCEL_4_ODR_AVL_400HZ_VAL , + }, + [8 ] = { + .hz =ST_ACCEL_ODR_AVL_800HZ , + .value =ST_ACCEL_4_ODR_AVL_800HZ_VAL , + }, + [9 ] = { + .hz =ST_ACCEL_ODR_AVL_1600HZ , + .value =ST_ACCEL_4_ODR_AVL_1600HZ_VAL , + }, + }, + }, + .pw = { + .addr =ST_ACCEL_4_ODR_ADDR , + .mask =ST_ACCEL_4_ODR_MASK , + .num_bit =ST_ACCEL_4_ODR_N_BIT , + .value_off =ST_ACCEL_DEFAULT_POWER_OFF_VALUE , + }, + .fs = { + .addr =ST_ACCEL_4_FS_ADDR , + .mask =ST_ACCEL_4_FS_MASK , + .num_bit =ST_ACCEL_4_FS_N_BIT , + .fs_avl = { + [0 ] = { + .num =ST_ACCEL_FS_AVL_2G , + .value =ST_ACCEL_4_FS_AVL_2_VAL , + .gain =ST_ACCEL_4_FS_AVL_2_GAIN , + }, + [1 ] = { + .num =ST_ACCEL_FS_AVL_4G , + .value =ST_ACCEL_4_FS_AVL_4_VAL , + .gain =ST_ACCEL_4_FS_AVL_4_GAIN , + }, + [2 ] = { + .num =ST_ACCEL_FS_AVL_6G , + .value =ST_ACCEL_4_FS_AVL_6_VAL , + .gain =ST_ACCEL_4_FS_AVL_6_GAIN , + }, + [3 ] = { + .num =ST_ACCEL_FS_AVL_8G , + .value =ST_ACCEL_4_FS_AVL_8_VAL , + .gain =ST_ACCEL_4_FS_AVL_8_GAIN , + }, + [4 ] = { + .num =ST_ACCEL_FS_AVL_16G , + .value =ST_ACCEL_4_FS_AVL_16_VAL , + .gain =ST_ACCEL_4_FS_AVL_16_GAIN , + }, + }, + }, + .bdu = { + .addr =ST_ACCEL_4_BDU_ADDR , + .mask =ST_ACCEL_4_BDU_MASK , + }, + .drdy_irq = { + .addr =ST_ACCEL_4_DRDY_IRQ_ADDR , + .mask =ST_ACCEL_4_DRDY_IRQ_MASK , + .ig1 = { + .en_addr =ST_ACCEL_4_IG1_EN_ADDR , + .en_mask =ST_ACCEL_4_IG1_EN_MASK , + }, + }, + .multi_read_bit =ST_ACCEL_4_MULTIREAD_BIT , + }, +}; + +static int st_accel_write_data_with_mask(struct iio_dev *indio_dev, u8
reg_addr,drivers/iio/accel/st_accel_core.c new file +441
-0+ u8 mask, short num_bit, u8 data) +{ + int err, j, pos; +u8 prev_data; +u8 new_data; +struct st_accel_data *adata =iio_priv (indio_dev); + + pos =8 - num_bit; +for (j =128 ; j >=0 ; j = j/2 ) { +if (mask / j >0 ) +break ; +else + pos--; + } + + err = adata->read_byte (adata, reg_addr, &prev_data); +if (err <0 ) + goto st_accel_write_data_with_mask_error; + + new_data = ((prev_data & (~mask)) | ((data << pos) & mask)); + err = adata->write_byte (adata, reg_addr, new_data); + +st_accel_write_data_with_mask_error: +return err; +} + +static int st_accel_match_odr (const struct st_accel_sensors *sensor, +unsigned int odr,struct st_accel_odr_available *odr_out) +{ +int i, ret =-1 ; + +for (i =0 ; i <ARRAY_SIZE (sensor->odr.odr_avl); i++) { +if (sensor->odr.odr_avl[i].hz == odr) { + odr_out->hz = sensor->odr.odr_avl[i].hz; + odr_out->value = sensor->odr.odr_avl[i].value; + ret =0 ; +break ; + } + } + +return ret; +} + +static int st_accel_match_fs (const struct st_accel_sensors *sensor, +unsigned int fs,struct st_accel_fullscale_available *fs_out) +{ +int i, ret =-1 ; + +for (i =0 ; i <ARRAY_SIZE (sensor->fs.fs_avl); i++) { +if (sensor->fs.fs_avl[i].num == fs) { + 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; + ret =0 ; +break ; + } + } + +return ret; +} + +int st_accel_set_dataready_irq (struct iio_dev *indio_dev,bool enable) +{ +int err; +struct st_accel_data *adata; + + adata =iio_priv (indio_dev); +if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr >0 ) { + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].drdy_irq.ig1.en_addr, + st_accel_sensors[adata->index].drdy_irq.ig1.en_mask,1 , + (int )enable); +if (err <0 ) + goto st_accel_set_dataready_irq_error; + } + +if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr >0 ) { + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr, + st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask,1 , + (int )enable); +if (err <0 ) + goto st_accel_set_dataready_irq_error; + } + + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].drdy_irq.addr, + st_accel_sensors[adata->index].drdy_irq.mask,1 , (int )enable); +if (err <0 ) + goto st_accel_set_dataready_irq_error; + +st_accel_set_dataready_irq_error: +return err; +} +EXPORT_SYMBOL (st_accel_set_dataready_irq); + +static int st_accel_set_bdu (struct iio_dev *indio_dev, +const struct st_accel_bdu *bdu,u8 value) +{ +return st_accel_write_data_with_mask (indio_dev, bdu->addr, bdu->mask, +1 , value); +} + +static int st_accel_set_odr (struct iio_dev *indio_dev, +struct st_accel_odr_available *odr_available) +{ +int err; +struct st_accel_data *adata =iio_priv (indio_dev); + +if ((st_accel_sensors[adata->index].odr.addr == + st_accel_sensors[adata->index].pw.addr) && + (st_accel_sensors[adata->index].odr.mask == + st_accel_sensors[adata->index].pw.mask)) { +if (adata->enabled == (bool )ST_ACCEL_ON ) { + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].odr.addr, + st_accel_sensors[adata->index].odr.mask, + st_accel_sensors[adata->index].odr.num_bit, + odr_available->value); +if (err <0 ) + goto sc_accel_set_odr_error; + }else { + adata->odr = odr_available->hz; + err =0 ; + } + }else { + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].odr.addr, + st_accel_sensors[adata->index].odr.mask, + st_accel_sensors[adata->index].odr.num_bit, + odr_available->value); +if (err <0 ) + goto sc_accel_set_odr_error; + } + +sc_accel_set_odr_error: +return err; +} + +static int st_accel_set_enable (struct iio_dev *indio_dev,int enable) +{ +int err; +bool found; +u8 tmp_value; +struct st_accel_odr_available odr_out; +struct st_accel_data *adata =iio_priv (indio_dev); + +switch (enable) { +case ST_ACCEL_ON : + found = false; + tmp_value = st_accel_sensors[adata->index].pw.value_on; +if ((st_accel_sensors[adata->index].odr.addr == + st_accel_sensors[adata->index].pw.addr) && + (st_accel_sensors[adata->index].odr.mask == + st_accel_sensors[adata->index].pw.mask)) { + err =st_accel_match_odr ( + &st_accel_sensors[adata->index], adata->odr, + &odr_out); +if (err <0 ) + goto set_enable_error; + tmp_value = odr_out.value; + found = true; + } + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].pw.addr, + st_accel_sensors[adata->index].pw.mask, + st_accel_sensors[adata->index].pw.num_bit, + tmp_value); +if (err <0 ) + goto set_enable_error; + adata->enabled = (bool )ST_ACCEL_ON ; +if (found) + adata->odr = odr_out.hz; +break ; +case ST_ACCEL_OFF : + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].pw.addr, + st_accel_sensors[adata->index].pw.mask, + st_accel_sensors[adata->index].pw.num_bit, + st_accel_sensors[adata->index].pw.value_off); +if (err <0 ) + goto set_enable_error; + adata->enabled = (bool )ST_ACCEL_OFF ; +break ; +default : + err = -EINVAL ; + goto set_enable_error; + } + +set_enable_error: +return err; +} + +static int st_accel_set_fullscale (struct iio_dev *indio_dev, +struct st_accel_fullscale_available *fs_avl) +{ +int err; +struct st_accel_data *adata =iio_priv (indio_dev); + + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].fs.addr, + st_accel_sensors[adata->index].fs.mask, + st_accel_sensors[adata->index].fs.num_bit, + fs_avl->value); +if (err <0 ) + goto st_accel_set_fullscale_error; + + adata->fullscale = fs_avl->num; + adata->gain = fs_avl->gain; +return err; + +st_accel_set_fullscale_error: +dev_err (&indio_dev->dev,"failed to set new fullscale.\n" ); +return err; +} + +static int st_accel_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[ST_ACCEL_BYTE_FOR_CHANNEL ]; +struct st_accel_data *adata =iio_priv (indio_dev); + +switch (mask) { +case IIO_CHAN_INFO_RAW : +mutex_lock (&indio_dev->mlock); +if (indio_dev->currentmode ==INDIO_BUFFER_TRIGGERED ) { + err = -EBUSY ; + goto read_error; + }else { +if (!adata->enabled) { + err = -EIO ; + goto read_error; + }else { + err = adata->read_multiple_byte (adata, + ch->address,ST_ACCEL_BYTE_FOR_CHANNEL , + outdata); +if (err <0 ) + goto read_error; + + *val = ((s16)(((s16)(outdata[1 ]) <<8 ) + | outdata[0 ])) >> ch->scan_type.shift; + } + } +mutex_unlock (&indio_dev->mlock); +return IIO_VAL_INT ; +case IIO_CHAN_INFO_SCALE : + data_tmp =UG_TO_MS2 (adata->gain); + *val =0 ; + *val2 = data_tmp; +return IIO_VAL_INT_PLUS_NANO ; +default : +return -EINVAL ; + } + +read_error: +mutex_unlock (&indio_dev->mlock); +return err; +} + +static int st_accel_check_device_list (struct iio_dev *indio_dev,u8 wai) +{ +int i; +bool found; +struct st_accel_data *adata =iio_priv (indio_dev); + + found = false; +for (i =0 ; i <ARRAY_SIZE (st_accel_sensors); i++) { +if (st_accel_sensors[i].wai == wai) { + found = true; +break ; + } + } +if (!found) + goto check_device_error; + + adata->index = i; + +return i; + +check_device_error: +dev_err (&indio_dev->dev,"device not supported -> wai (0x%x).\n" , wai); +return -ENODEV ; +} + +static int st_accel_get_wai_device (struct iio_dev *indio_dev,u8 reg_addr, +u8 *value) +{ +int ret; +struct st_accel_data *adata =iio_priv (indio_dev); + + ret = adata->read_byte (adata, reg_addr, value); +if (ret <0 ) + goto read_byte_wai_error; + +return 0 ; + +read_byte_wai_error: +dev_err (&indio_dev->dev, +"failed to read WhoAmI (register 0x%x).\n" , reg_addr); +return -EIO ; +} + +static ssize_t st_accel_sysfs_set_sampling_frequency (struct device *dev, +struct device_attribute *attr,const char *buf,size_t size) +{ +int err; +unsigned int freq; +struct st_accel_odr_available odr_out; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + + err =kstrtoint (buf,10 , &freq); +if (err <0 ) + goto conversion_error; + +mutex_lock (&indio_dev->mlock); + err =st_accel_match_odr (&st_accel_sensors[adata->index], + freq, &odr_out); +if (err <0 ) + goto st_accel_sysfs_set_sampling_frequency_error; + + err =st_accel_set_odr (indio_dev, &odr_out); +if (err <0 ) { +dev_err (&indio_dev->dev, +"failed to set sampling frequency to %d.\n" , freq); + goto st_accel_sysfs_set_sampling_frequency_error; + } + adata->odr = odr_out.hz; + +st_accel_sysfs_set_sampling_frequency_error: +mutex_unlock (&indio_dev->mlock); +conversion_error: +return size; +} + +static ssize_t st_accel_sysfs_get_sampling_frequency (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + +return sprintf (buf,"%d\n" , adata->odr); +} + +static ssize_t st_accel_sysfs_set_enable (struct device *dev, +struct device_attribute *attr,const char *buf,size_t size) +{ +int err; +bool en; +struct iio_dev *indio_dev =dev_get_drvdata (dev); + + err =strtobool (buf, &en); +if (err <0 ) + goto set_enable_error; + +mutex_lock (&indio_dev->mlock); + err =st_accel_set_enable (indio_dev, (int )en); +if (err <0 ) +dev_err (&indio_dev->dev, +"failed to set enable to %d.\n" , (int )en); +mutex_unlock (&indio_dev->mlock); + +set_enable_error: +return size; +} + +static ssize_t st_accel_sysfs_get_enable (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + +return sprintf (buf,"%d\n" , (int )adata->enabled); +} + +static ssize_t st_accel_sysfs_get_fullscale (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + +return sprintf (buf,"%d\n" , adata->fullscale); +} + +static ssize_t st_accel_sysfs_set_fullscale (struct device *dev, +struct device_attribute *attr,const char *buf,size_t size) +{ +int err; +unsigned int fs; +struct st_accel_fullscale_available fs_out; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + + err =kstrtoint (buf,10 , &fs); +if (err <0 ) + goto conversion_error; + +mutex_lock (&indio_dev->mlock); + err =st_accel_match_fs (&st_accel_sensors[adata->index], fs, &fs_out); +if (err <0 ) + goto match_fullscale_error; + + err =st_accel_set_fullscale (indio_dev, &fs_out); +if (err <0 ) { +dev_err (&indio_dev->dev, +"failed to set new fullscale. (errn %d).\n" , err); + } + +match_fullscale_error: +mutex_unlock (&indio_dev->mlock); +conversion_error: +return size; +} + +static ssize_t st_accel_sysfs_fullscale_available (struct device *dev, +struct device_attribute *attr,char *buf) +{ +int i, len =0 ; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + +mutex_lock (&indio_dev->mlock); +for (i =0 ; i <ARRAY_SIZE (st_accel_sensors[adata->index].fs.fs_avl); + i++) { +if (st_accel_sensors[adata->index].fs.fs_avl[i].num ==0 ) +break ; + + len +=sprintf (buf+len,"%d " , + st_accel_sensors[adata->index].fs.fs_avl[i].num); + } +mutex_unlock (&indio_dev->mlock); + + len--; + len +=sprintf (buf+len,"\n" ); +return len; +} + +static ssize_t st_accel_sysfs_sampling_frequency_available(structdevice *dev,
drivers/iio/accel/st_accel_core.c new file +58
-0+ struct device_attribute *attr, char *buf) +{ + int i, len = 0; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + +mutex_lock (&indio_dev->mlock); +for (i =0 ; i <ARRAY_SIZE (st_accel_sensors[adata->index].odr.odr_avl); + i++) { +if (st_accel_sensors[adata->index].odr.odr_avl[i].hz ==0 ) +break ; + + len +=sprintf (buf+len,"%d " , + st_accel_sensors[adata->index].odr.odr_avl[i].hz); + } +mutex_unlock (&indio_dev->mlock); + + len--; + len +=sprintf (buf+len,"\n" ); +return len; +} + +/** +* IIO_DEVICE_ATTR - sampling_frequency_available +* @read: show all frequency available of the sensor. +* +*/ + +static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO, + st_accel_sysfs_sampling_frequency_available, NULL , 0); + +/** +* IIO_DEVICE_ATTR - fullscale_available +* @read: show all fullscale available of the sensor. +* +*/ + +static IIO_DEVICE_ATTR(fullscale_available, S_IRUGO, + st_accel_sysfs_fullscale_available, NULL , 0); + +/** +* IIO_DEVICE_ATTR - fullscale +* @read: show the current fullscale of the sensor. +* @write: store the current fullscale of the sensor. +* +*/ + +static IIO_DEVICE_ATTR(fullscale, S_IWUSR | S_IRUGO, + st_accel_sysfs_get_fullscale, st_accel_sysfs_set_fullscale , 0); + +/** +* IIO_DEVICE_ATTR - enable +* @read: show the current status of the sensor. +* @write: power on/off the sensor. +* +*/ + +static IIO_DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
st_accel_sysfs_get_enable,drivers/iio/accel/st_accel_core.c new file +133
-0+ st_accel_sysfs_set_enable , 0); + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + st_accel_sysfs_get_sampling_frequency, + st_accel_sysfs_set_sampling_frequency); + +static struct attribute *st_accel_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_fullscale_available.dev_attr.attr, + &iio_dev_attr_fullscale.dev_attr.attr, + &iio_dev_attr_enable.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, +NULL , +}; + +static const struct attribute_group st_accel_attribute_group = { + .attrs = st_accel_attributes, +}; + +static const struct iio_info acc_info = { + .driver_module =THIS_MODULE , + .attrs = &st_accel_attribute_group, + .read_raw = &st_accel_read_raw, +}; + +static int st_accel_init_sensor (struct iio_dev *indio_dev) +{ +int err; +struct st_accel_odr_available odr_out; +struct st_accel_fullscale_available fs_out; +struct st_accel_data *adata =iio_priv (indio_dev); + + err =st_accel_set_enable (indio_dev,ST_ACCEL_OFF ); +if (err <0 ) + goto init_error; + +st_accel_match_fs (&st_accel_sensors[adata->index], + adata->fullscale, &fs_out); + err =st_accel_set_fullscale (indio_dev, &fs_out); +if (err <0 ) + goto init_error; + +st_accel_match_odr (&st_accel_sensors[adata->index], + adata->odr, &odr_out); + err =st_accel_set_odr (indio_dev, &odr_out); +if (err <0 ) + goto init_error; + + err =st_accel_set_bdu (indio_dev, + &st_accel_sensors[adata->index].bdu, (u8 )ST_ACCEL_ON ); +if (err <0 ) + goto init_error; + +return 0 ; + +init_error: +dev_err (&indio_dev->dev,"failed to init sensor data." ); +return -EIO ; +} + +int st_accel_iio_probe (struct iio_dev *indio_dev) +{ +int err; +u8 wai; +struct st_accel_data *adata =iio_priv (indio_dev); +struct st_accel_platform_data *pdata; + +mutex_init (&adata->slock); + indio_dev->modes =INDIO_DIRECT_MODE ; + indio_dev->info = &acc_info; + + err =st_accel_get_wai_device (indio_dev, +ST_ACCEL_DEFAULT_WAI_ADDRESS , &wai); +if (err <0 ) + goto st_accel_iio_probe_error; + + err =st_accel_check_device_list (indio_dev, wai); +if (err <0 ) + goto st_accel_iio_probe_error; + + adata->multiread_bit = st_accel_sensors[adata->index].multi_read_bit; + indio_dev->channels = st_accel_sensors[adata->index].ch; + indio_dev->num_channels =ST_ACCEL_NUMBER_ALL_CHANNELS ; + pdata = adata->dev->platform_data; +if (pdata ==NULL ) { + adata->fullscale = st_accel_default_pdata.fullscale; + adata->odr = st_accel_default_pdata.sampling_frequency; + }else { + adata->fullscale = pdata->fullscale; + adata->odr = pdata->sampling_frequency; + } + + err =st_accel_init_sensor (indio_dev); +if (err <0 ) + goto st_accel_iio_probe_error; + + err =st_accel_allocate_ring (indio_dev); +if (err <0 ) + goto st_accel_iio_probe_error; + +if (*adata->irq_data_ready >0 ) { + err =st_accel_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; + +return err; + +iio_device_register_error: +st_accel_remove_trigger (indio_dev); +acc_probe_trigger_error: +st_accel_deallocate_ring (indio_dev); +st_accel_iio_probe_error: +return err; +} +EXPORT_SYMBOL (st_accel_iio_probe); + +void st_accel_iio_remove (struct iio_dev *indio_dev) +{ +iio_device_unregister (indio_dev); +st_accel_remove_trigger (indio_dev); +st_accel_deallocate_ring (indio_dev); +iio_device_free (indio_dev); +} +EXPORT_SYMBOL (st_accel_iio_remove); + +MODULE_AUTHOR ("Denis Ciocca <[email protected]>" ); +MODULE_DESCRIPTION ("STMicroelectronics accelerometers driver" ); +MODULE_LICENSE ("GPL v2" );drivers/iio/accel/st_accel_i2c.c new file +139
-0@@ -0,0 +1,139 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* 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/trigger.h> + +#include <linux/iio/accel/st_accel.h> + + +#define ST_ACCEL_I2C_MULTIREAD 0x80 + +static int st_accel_i2c_read_byte (struct st_accel_data *adata, +u8 reg_addr,u8 *res_byte) +{ +int err; + + err =i2c_smbus_read_byte_data (to_i2c_client (adata->dev), reg_addr); +if (err <0 ) + goto st_accel_i2c_read_byte_error; + *res_byte = err &0xff ; +return err; + +st_accel_i2c_read_byte_error: +return -EIO ; +} + +static int st_accel_i2c_read_multiple_byte (struct st_accel_data *adata, +u8 reg_addr,int len,u8 *data) +{ +int err; + +if (adata->multiread_bit == true) + reg_addr |=ST_ACCEL_I2C_MULTIREAD ; + + err =i2c_smbus_read_i2c_block_data (to_i2c_client (adata->dev), + reg_addr, len, data); +if (err <0 ) + goto st_accel_i2c_read_multiple_byte_error; + +return err; + +st_accel_i2c_read_multiple_byte_error: +return -EIO ; +} + +static int st_accel_i2c_write_byte (struct st_accel_data *adata, +u8 reg_addr,u8 data) +{ +return i2c_smbus_write_byte_data (to_i2c_client (adata->dev), + reg_addr, data); +} + +static int __devinit st_accel_i2c_probe (struct i2c_client *client, +const struct i2c_device_id *id) +{ +struct iio_dev *indio_dev; +struct st_accel_data *adata; +int err; + + indio_dev =iio_device_alloc (sizeof (*adata)); +if (indio_dev ==NULL ) { + err = -ENOMEM ; + goto iio_device_alloc_error; + } + + adata =iio_priv (indio_dev); + adata->dev = &client->dev; +i2c_set_clientdata (client, indio_dev); + + indio_dev->dev.parent = &client->dev; + indio_dev->name = client->name; + + adata->read_byte = st_accel_i2c_read_byte; + adata->write_byte = st_accel_i2c_write_byte; + adata->read_multiple_byte = st_accel_i2c_read_multiple_byte; + adata->irq_data_ready = &client->irq; + + err =st_accel_iio_probe (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 st_accel_i2c_remove(struct i2c_client *client) +{ +struct iio_dev *indio_dev =i2c_get_clientdata (client); + +st_accel_iio_remove (indio_dev); +return 0 ; +} + +static const struct i2c_device_id st_accel_id_table[] = { + {LSM303DLH_ACCEL_DEV_NAME }, + {LSM303DLHC_ACCEL_DEV_NAME }, + {LIS3DH_ACCEL_DEV_NAME }, + {LSM330D_ACCEL_DEV_NAME }, + {LSM330DL_ACCEL_DEV_NAME }, + {LSM330DLC_ACCEL_DEV_NAME }, + {LSM303D_ACCEL_DEV_NAME }, + {LSM9DS0_ACCEL_DEV_NAME }, + {LIS331DLH_ACCEL_DEV_NAME }, + {LSM303DL_ACCEL_DEV_NAME }, + {LSM303DLM_ACCEL_DEV_NAME }, + {LSM330_ACCEL_DEV_NAME }, + {}, +}; +MODULE_DEVICE_TABLE (i2c, st_accel_id_table); + +static struct i2c_driver st_accel_driver = { + .driver = { + .owner =THIS_MODULE , + .name ="st-accel-i2c" , + }, + .probe = st_accel_i2c_probe, + .remove =__devexit_p (st_accel_i2c_remove), + .id_table = st_accel_id_table, +}; +module_i2c_driver (st_accel_driver); + +MODULE_AUTHOR ("Denis Ciocca <[email protected]>" ); +MODULE_DESCRIPTION ("STMicroelectronics accelerometers i2c driver" ); +MODULE_LICENSE ("GPL v2" );drivers/iio/accel/st_accel_spi.c new file +199
-0@@ -0,0 +1,199 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> + +#include <linux/iio/accel/st_accel.h> + + +#define ACC_SPI_READ 0x80; +#define ACC_SPI_MULTIREAD 0xc0 + +static int st_accel_spi_read_byte (struct st_accel_data *adata, +u8 reg_addr,u8 *res_byte) +{ +struct spi_message msg; +int err; +u8 tx; + +struct spi_transfer xfers[] = { + { + .tx_buf = &tx, + .bits_per_word =8 , + .len =1 , + }, + { + .rx_buf = res_byte, + .bits_per_word =8 , + .len =1 , + } + }; + +mutex_lock (&adata->slock); + tx = reg_addr |ACC_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 (adata->dev), &msg); +mutex_unlock (&adata->slock); +if (err) + goto acc_spi_read_byte_error; + +return err; + +acc_spi_read_byte_error: +return -EIO ; +} + +static int st_accel_spi_read_multiple_byte (struct st_accel_data *adata, +u8 reg_addr,int len,u8 *data) +{ +struct spi_message msg; +int err; +u8 tx; + +struct spi_transfer xfers[] = { + { + .tx_buf = &tx, + .bits_per_word =8 , + .len =1 , + }, + { + .rx_buf = data, + .bits_per_word =8 , + .len = len, + } + }; + +mutex_lock (&adata->slock); +if (adata->multiread_bit == true) + tx = reg_addr |ACC_SPI_MULTIREAD ; +else + tx = reg_addr |ACC_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 (adata->dev), &msg); +mutex_unlock (&adata->slock); +if (err) + goto acc_spi_read_multiple_byte_error; +return len; + +acc_spi_read_multiple_byte_error: +return -EIO ; +} + +static int st_accel_spi_write_byte (struct st_accel_data *adata, +u8 reg_addr,u8 data) +{ +struct spi_message msg; +int err; +u8 tx[2 ]; + +struct spi_transfer xfers[] = { + { + .tx_buf = tx, + .bits_per_word =8 , + .len =2 , + } + }; + +mutex_lock (&adata->slock); + tx[0 ] = reg_addr; + tx[1 ] = data; +spi_message_init (&msg); +spi_message_add_tail (&xfers[0 ], &msg); + err =spi_sync (to_spi_device (adata->dev), &msg); +mutex_unlock (&adata->slock); + +return err; +} + +static int __devinit st_accel_spi_probe (struct spi_device *client) +{ +struct iio_dev *indio_dev; +struct st_accel_data *adata; +int err; + + indio_dev =iio_device_alloc (sizeof (*adata)); +if (indio_dev ==NULL ) { + err = -ENOMEM ; + goto iio_device_alloc_error; + } + + adata =iio_priv (indio_dev); + adata->dev = &client->dev; +spi_set_drvdata (client, indio_dev); + + indio_dev->dev.parent = &client->dev; + indio_dev->name = client->modalias; + + adata->read_byte = st_accel_spi_read_byte; + adata->write_byte = st_accel_spi_write_byte; + adata->read_multiple_byte = st_accel_spi_read_multiple_byte; + adata->irq_data_ready = &client->irq; + + err =st_accel_iio_probe (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 st_accel_spi_remove(struct spi_device *spi) +{ +struct iio_dev *indio_dev =spi_get_drvdata (spi); + +st_accel_iio_remove (indio_dev); +return 0 ; +} + +static const struct spi_device_id st_accel_id_table[] = { + {LSM303DLH_ACCEL_DEV_NAME }, + {LSM303DLHC_ACCEL_DEV_NAME }, + {LIS3DH_ACCEL_DEV_NAME }, + {LSM330D_ACCEL_DEV_NAME }, + {LSM330DL_ACCEL_DEV_NAME }, + {LSM330DLC_ACCEL_DEV_NAME }, + {LSM303D_ACCEL_DEV_NAME }, + {LSM9DS0_ACCEL_DEV_NAME }, + {LIS331DLH_ACCEL_DEV_NAME }, + {LSM303DL_ACCEL_DEV_NAME }, + {LSM303DLM_ACCEL_DEV_NAME }, + {LSM330_ACCEL_DEV_NAME }, + {}, +}; +MODULE_DEVICE_TABLE (spi, st_accel_id_table); + +static struct spi_driver st_accel_driver = { + .driver = { + .owner =THIS_MODULE , + .name ="st-accel-spi" , + }, + .probe = st_accel_spi_probe, + .remove =__devexit_p (st_accel_spi_remove), + .id_table = st_accel_id_table, +}; +module_spi_driver (st_accel_driver); + +MODULE_AUTHOR ("Denis Ciocca <[email protected]>" ); +MODULE_DESCRIPTION ("STMicroelectronics accelerometers spi driver" ); +MODULE_LICENSE ("GPL v2" );drivers/iio/accel/st_accel_trigger.c new file +22
-0@@ -0,0 +1,84 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> + +#include <linux/iio/accel/st_accel.h> + + +static int st_accel_trig_acc_set_state(struct iio_trigger *trig, boolstate)
drivers/iio/accel/st_accel_trigger.c new file +62
-0+{ +struct iio_dev *indio_dev = trig->private_data; +return st_accel_set_dataready_irq (indio_dev, state); +} + +static const struct iio_trigger_ops st_accel_trigger_ops = { + .owner =THIS_MODULE , + .set_trigger_state = &st_accel_trig_acc_set_state, +}; + +int st_accel_probe_trigger (struct iio_dev *indio_dev) +{ +int err; +struct st_accel_data *adata =iio_priv (indio_dev); + + adata->trig =iio_trigger_alloc ("%s-trigger" , indio_dev->name); +if (adata->trig ==NULL ) { + err = -ENOMEM ; +dev_err (&indio_dev->dev,"failed to allocate iio trigger.\n" ); + goto iio_trigger_alloc_error; + } + + err =request_threaded_irq (*adata->irq_data_ready, + iio_trigger_generic_data_rdy_poll, +NULL , +IRQF_TRIGGER_RISING , + adata->trig->name, + adata->trig); +if (err) + goto request_irq_error; + + adata->trig->private_data = indio_dev; + adata->trig->ops = &st_accel_trigger_ops; + adata->trig->dev.parent = adata->dev; + + err =iio_trigger_register (adata->trig); +if (err <0 ) { +dev_err (&indio_dev->dev,"failed to register iio trigger.\n" ); + goto iio_trigger_register_error; + } + indio_dev->trig = adata->trig; + +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; +} +EXPORT_SYMBOL (st_accel_probe_trigger); + +void st_accel_remove_trigger (struct iio_dev *indio_dev) +{ +struct st_accel_data *adata =iio_priv (indio_dev); + +iio_trigger_unregister (adata->trig); +free_irq (*adata->irq_data_ready, adata->trig); +iio_trigger_free (adata->trig); +} +EXPORT_SYMBOL (st_accel_remove_trigger);include/linux/iio/accel/st_accel.h new file +119
-0@@ -0,0 +1,119 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* v. 1.0.0 +* Licensed under the GPL-2. +*/ + +/* +* Supported sensors: +* LSM303DLH +* LSM303DLHC +* LIS3DH +* LSM330D +* LSM330DL +* LSM330DLC +* LSM303D +* LSM9DS0 +* LIS331DLH +* LSM303DL +* LSM303DLM +* LSM330 +*/ + + +#ifndef ST_ACCEL_H +#define ST_ACCEL_H + +#define LSM303DLH_ACCEL_DEV_NAME "lsm303dlh_accel" +#define LSM303DLHC_ACCEL_DEV_NAME "lsm303dlhc_accel" +#define LIS3DH_ACCEL_DEV_NAME "lis3dh" +#define LSM330D_ACCEL_DEV_NAME "lsm330d_accel" +#define LSM330DL_ACCEL_DEV_NAME "lsm330dl_accel" +#define LSM330DLC_ACCEL_DEV_NAME "lsm330dlc_accel" +#define LSM303D_ACCEL_DEV_NAME "lsm303d" +#define LSM9DS0_ACCEL_DEV_NAME "lsm9ds0" +#define LIS331DLH_ACCEL_DEV_NAME "lis331dlh" +#define LSM303DL_ACCEL_DEV_NAME "lsm303dl_accel" +#define LSM303DLM_ACCEL_DEV_NAME "lsm303dlm_accel" +#define LSM330_ACCEL_DEV_NAME "lsm330_accel" + +#define ST_ACCEL_NUMBER_ALL_CHANNELS 4 +#define ST_ACCEL_BYTE_FOR_CHANNEL 2 +#define ST_ACCEL_SCAN_X 0 +#define ST_ACCEL_SCAN_Y 1 +#define ST_ACCEL_SCAN_Z 2 + +/** +* struct st_accel_data - ST accel device status +* @dev: Pointer to instance of struct device (I2C or SPI). +* @name: Name of the sensor in use. +* @enabled: Status of the sensor (0->off, 1->on). +* @index: Number used to point the sensor being used in the +* st_accel_sensors struct. +* @fullscale: Maximum range of measure by the sensor. +* @gain: Sensitivity of the sensor [ug/LSB]. +* @odr: Output data rate of the sensor. +* @multiread_bit: Use or not particular bit for [I2C/SPI] multiread. +* @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. +* @set_trigger_parent: Function used to set the trigger parent. +* @trig: The trigger in use by the core driver. +* @irq_data_ready: IRQ number for data ready on INT1 pin. +* @slock: mutex for read and write operation. +*/ + +struct st_accel_data { +struct device *dev; +bool enabled; +short index; + +unsigned int fullscale; +unsigned int gain; +unsigned int odr; + +bool multiread_bit; +int (*read_byte) (struct st_accel_data *adata,u8 reg_addr, +u8 *res_byte); +int (*write_byte) (struct st_accel_data *adata,u8 reg_addr,u8 data); +int (*read_multiple_byte) (struct st_accel_data *adata,u8 reg_addr, +int len,u8 *data); + +struct iio_trigger *trig; +int *irq_data_ready; +struct mutex slock; +}; + +int st_accel_iio_probe (struct iio_dev *indio_dev); +void st_accel_iio_remove (struct iio_dev *indio_dev); +int st_accel_set_dataready_irq (struct iio_dev *indio_dev,bool enable); + +#ifdef CONFIG_IIO_BUFFER +int st_accel_probe_trigger (struct iio_dev *indio_dev); +void st_accel_remove_trigger (struct iio_dev *indio_dev); +int st_accel_allocate_ring (struct iio_dev *indio_dev); +void st_accel_deallocate_ring (struct iio_dev *indio_dev); +#else /* CONFIG_IIO_BUFFER */ +static inline int st_accel_probe_trigger (struct iio_dev *indio_dev) +{ +return 0 ; +} +static inline void st_accel_remove_trigger (struct iio_dev *indio_dev) +{ +return ; +} +static inline int st_accel_allocate_ring (struct iio_dev *indio_dev) +{ +return 0 ; +} +static inline void st_accel_deallocate_ring (struct iio_dev *indio_dev) +{ +return ; +} +#endif /* CONFIG_IIO_BUFFER */ + +#endif /* ST_ACCEL_H */ include/linux/platform_data/st_accel_pdata.h new file +27
-1@@ -0,0 +1,27 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + + +#ifndef ST_ACCEL_PDATA_H +#define ST_ACCEL_PDATA_H + + +/** +* struct st_accel_platform_data - ST accel device platform data +* @fullscale: Value of fullscale used for the sensor. +* @sampling_frequency: Value of sampling frequency used for the sensor. +*/ + +struct st_accel_platform_data { +int fullscale; +int sampling_frequency; +}; + +#endif /* ST_ACCEL_PDATA_H */ --1.7.0.4
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 11 lines · Jonathan Cameron
Quoted 10 lines · Denis CIOCCA
Hi guys,
according to Lars-Peter requests, I have modified my driver. (I hope I have checked everything).
I have only one things aboutread_allfunction on the buffer source file:
I think the best solution is reading every time of all output registers, these are three-axial accelerometers, the overhead for support the power on/off of single axis is too much big for the purpose (because if I don't power off the single axis the data-ready doesn't reset). What do you think about that?
Quoted 2 lines
For the non buffered case - reading is slow anyway so if anyone cares they will be doing buffered reads.
In the non buffered case is done by reading only the selected registers.
Quoted 5 lines
For buffered reads this isn't going
to change often (and there is a lot of cost associated with bringing the buffer up and down anyway) so a little cost in disabling the channel is minor compared to the cost of hammering the bus unecessarily - particularly with good old slow i2c.
For the buffered case you are right.
Quoted 3 lines
So I'd be inclined to turn on only channels we care about. Do you have an estimate of how long it will take to turn one on for a single read?
For a single read, I think is not the best solution activate one channel and then deactivate it when the reading is ended. The time of turn on the channel depends on device, tomorrow morning I check these details on our datasheets. The only benefit in this kind of approach is the power consumption of the devices.
Denis
Quoted 2,128 lines
Quoted 2,128 lines
From
f2f556001170e8698a1bbf0146ed2d11e75436b1Mon Sep 17 00:00:00 2001
From: Denis Ciocca <[email protected]>
Date: Mon, 22 Oct 2012 11:17:27 +0200
Subject: [PATCH] iio:accel: Add STMicroelectronics accelerometers driverThis patch adds generic accelerometer driver for STMicroelectronics accelerometers, currently it supports:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D, LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330Signed-off-by Denis Ciocca <[email protected]>--- drivers/iio/accel/Kconfig | 37 + drivers/iio/accel/Makefile | 6 + drivers/iio/accel/st_accel_buffer.c | 103 ++ drivers/iio/accel/st_accel_core.c | 1301 ++++++++++++++++++++++++++ drivers/iio/accel/st_accel_i2c.c | 139 +++ drivers/iio/accel/st_accel_spi.c | 199 ++++ drivers/iio/accel/st_accel_trigger.c | 84 ++ include/linux/iio/accel/st_accel.h | 119 +++ include/linux/platform_data/st_accel_pdata.h | 27 + 9 files changed, 2015 insertions(+), 0 deletions(-)create mode 100644
drivers/iio/accel/st_accel_buffer.ccreate mode 100644drivers/iio/accel/st_accel_core.ccreate mode 100644drivers/iio/accel/st_accel_i2c.ccreate mode 100644drivers/iio/accel/st_accel_spi.ccreate mode 100644drivers/iio/accel/st_accel_trigger.ccreate mode 100644include/linux/iio/accel/st_accel.hcreate mode 100644include/linux/platform_data/st_accel_pdata.hdrivers/iio/accel/Kconfig +37
-0@@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D Say yes here to build support for the HID SENSOR accelerometers 3D. +config ST_ACCEL_3AXIS + tristate "STMicroelectronics accelerometers 3-Axis Driver" + depends on (I2C || SPI) && SYSFS + help + Say yes here to build support for STMicroelectronics accelerometers: + LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D, + LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330. + + This driver can also be built as a module. If so, the module + will be called st_accel. + +config ST_ACCEL_3AXIS_I2C + tristate "support I2C bus connection" + depends on ST_ACCEL_3AXIS && I2C + help + Say yes here to build I2C support for STMicroelectronics accelerometers. + + To compile this driver as a module, choose M here: the + module will be called st_accel_i2c. + +config ST_ACCEL_3AXIS_SPI + tristate "support SPI bus connection" + depends on ST_ACCEL_3AXIS && SPI_MASTER + help + Say yes here to build SPI support for STMicroelectronics accelerometers. + + To compile this driver as a module, choose M here: the + module will be called st_accel_spi. + +config ST_ACCEL_3AXIS_TRIGGERED_BUFFER + tristate "support triggered buffer" + depends on ST_ACCEL_3AXIS + select IIO_TRIGGERED_BUFFER + select IIO_BUFFER + help + Default trigger and buffer for STMicroelectronics accelerometers driver. + endmenudrivers/iio/accel/Makefile +5
-0@@ -3,3 +3,9 @@ # obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o + +st_accel-y := st_accel_core.o +obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) += st_accel_i2c.o +obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) += st_accel_spi.o +obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) += st_accel_trigger.ost_accel_buffer.o
drivers/iio/accel/Makefile +1
-0+obj-$(CONFIG_ST_ACCEL_3AXIS) += st_accel.odrivers/iio/accel/st_accel_buffer.c new file +103
-0@@ -0,0 +1,103 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/module.h> +#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/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#include <linux/iio/accel/st_accel.h> + + +#define ST_ACCEL_NUMBER_DATA_CHANNELS 3 + +static int st_accel_read_all (struct iio_dev *indio_dev,u8 *rx_array) +{ +int len; +struct st_accel_data *adata =iio_priv (indio_dev); + + len = adata->read_multiple_byte (adata, + indio_dev->channels[ST_ACCEL_SCAN_X ].address, +ST_ACCEL_BYTE_FOR_CHANNEL *ST_ACCEL_NUMBER_DATA_CHANNELS , + rx_array); +if (len <0 ) + goto read_error; + +return len; + +read_error: +return -EIO ; +} + +static int st_accel_get_buffer_element (struct iio_dev *indio_dev,u8 *buf) +{ +int ret, i, n =0 ; +u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL *ST_ACCEL_NUMBER_DATA_CHANNELS ]; +s16 *data = (s16 *)buf; + + ret =st_accel_read_all (indio_dev, rx_array); +if (ret <0 ) +return ret; + +for (i =0 ; i <ST_ACCEL_NUMBER_DATA_CHANNELS ; i++) { +if (test_bit (i, indio_dev->active_scan_mask)) { + data[n] = (s16)(((s16)(rx_array[2 *i+1 ]) <<8 ) + | rx_array[2 *i]); + n++; + } + } + +return n*sizeof (data[0 ]); +} + +static irqreturn_t st_accel_trigger_handler (int irq,void *p) +{ +struct iio_poll_func *pf = p; +struct iio_dev *indio_dev = pf->indio_dev; +int len =0 ; +char *data; + + data =kmalloc (indio_dev->scan_bytes,GFP_KERNEL ); +if (data ==NULL ) + goto done; +if (!bitmap_empty (indio_dev->active_scan_mask, indio_dev->masklength)) + len =st_accel_get_buffer_element (indio_dev, data); +else + goto done; +if (indio_dev->scan_timestamp) + *(s64 *)((u8 *)data +ALIGN (len,sizeof (s64))) = pf->timestamp; +iio_push_to_buffer (indio_dev->buffer, data); +kfree (data); + +done: +iio_trigger_notify_done (indio_dev->trig); +return IRQ_HANDLED ; +} + +int st_accel_allocate_ring (struct iio_dev *indio_dev) +{ + indio_dev->scan_timestamp = true; +return iio_triggered_buffer_setup (indio_dev, &iio_pollfunc_store_time, + &st_accel_trigger_handler,NULL ); +} +EXPORT_SYMBOL (st_accel_allocate_ring); + +void st_accel_deallocate_ring (struct iio_dev *indio_dev) +{ +iio_triggered_buffer_cleanup (indio_dev); +} +EXPORT_SYMBOL (st_accel_deallocate_ring);drivers/iio/accel/st_accel_core.c new file +669
-0@@ -0,0 +1,1301 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/mutex.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/irq.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/buffer.h> + +#include <linux/iio/accel/st_accel.h> +#include <linux/platform_data/st_accel_pdata.h> + + +#define UG_TO_MS2 (x) (x*9800) +#define ST_ACCEL_FULLSCALE_AVL_MAX 5 +#define ST_ACCEL_ODR_LIST_MAX 10 +#define ST_ACCEL_ON 1 +#define ST_ACCEL_OFF 0 + +/* DEFAULT VALUE FOR SENSORS */ +#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR 0x28 +#define ST_ACCEL_DEFAULT_OUT_X_H_ADDR 0x29 +#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR 0x2a +#define ST_ACCEL_DEFAULT_OUT_Y_H_ADDR 0x2b +#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR 0x2c +#define ST_ACCEL_DEFAULT_OUT_Z_H_ADDR 0x2d +#define ST_ACCEL_DEFAULT_WAI_ADDRESS 0x0f +#define ST_ACCEL_DEFAULT_POWER_ON_VALUE 0x01 +#define ST_ACCEL_DEFAULT_POWER_OFF_VALUE 0x00 +#define ST_ACCEL_DEFAULT_12_REALBITS 12 +#define ST_ACCEL_DEFAULT_16_REALBITS 16 + +/* ODR */ +#define ST_ACCEL_ODR_AVL_1HZ 1 +#define ST_ACCEL_ODR_AVL_3HZ 3 +#define ST_ACCEL_ODR_AVL_6HZ 6 +#define ST_ACCEL_ODR_AVL_10HZ 10 +#define ST_ACCEL_ODR_AVL_12HZ 12 +#define ST_ACCEL_ODR_AVL_25HZ 25 +#define ST_ACCEL_ODR_AVL_50HZ 50 +#define ST_ACCEL_ODR_AVL_100HZ 100 +#define ST_ACCEL_ODR_AVL_200HZ 200 +#define ST_ACCEL_ODR_AVL_400HZ 400 +#define ST_ACCEL_ODR_AVL_800HZ 800 +#define ST_ACCEL_ODR_AVL_1000HZ 1000 +#define ST_ACCEL_ODR_AVL_1600HZ 1600 + +/* FULLSCALE */ +#define ST_ACCEL_FS_AVL_2G 2 +#define ST_ACCEL_FS_AVL_4G 4 +#define ST_ACCEL_FS_AVL_6G 6 +#define ST_ACCEL_FS_AVL_8G 8 +#define ST_ACCEL_FS_AVL_16G 16 + +/* CUSTOM VALUES FOR SENSOR 1 */ +#define ST_ACCEL_1_WAI_EXP 0x33 +#define ST_ACCEL_1_ODR_ADDR 0x20 +#define ST_ACCEL_1_ODR_MASK 0xf0 +#define ST_ACCEL_1_ODR_N_BIT 4 +#define ST_ACCEL_1_ODR_AVL_1HZ_VAL 0x01 +#define ST_ACCEL_1_ODR_AVL_10HZ_VAL 0x02 +#define ST_ACCEL_1_ODR_AVL_25HZ_VAL 0x03 +#define ST_ACCEL_1_ODR_AVL_50HZ_VAL 0x04 +#define ST_ACCEL_1_ODR_AVL_100HZ_VAL 0x05 +#define ST_ACCEL_1_ODR_AVL_200HZ_VAL 0x06 +#define ST_ACCEL_1_ODR_AVL_400HZ_VAL 0x07 +#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL 0x08 +#define ST_ACCEL_1_FS_N_BIT 2 +#define ST_ACCEL_1_FS_ADDR 0x23 +#define ST_ACCEL_1_FS_MASK 0x30 +#define ST_ACCEL_1_FS_AVL_2_VAL 0x00 +#define ST_ACCEL_1_FS_AVL_4_VAL 0x01 +#define ST_ACCEL_1_FS_AVL_8_VAL 0x02 +#define ST_ACCEL_1_FS_AVL_16_VAL 0x03 +#define ST_ACCEL_1_FS_AVL_2_GAIN 1000 +#define ST_ACCEL_1_FS_AVL_4_GAIN 2000 +#define ST_ACCEL_1_FS_AVL_8_GAIN 4000 +#define ST_ACCEL_1_FS_AVL_16_GAIN 12000 +#define ST_ACCEL_1_BDU_ADDR 0x23 +#define ST_ACCEL_1_BDU_MASK 0x80 +#define ST_ACCEL_1_DRDY_IRQ_ADDR 0x22 +#define ST_ACCEL_1_DRDY_IRQ_MASK 0x10 +#define ST_ACCEL_1_MULTIREAD_BIT true + +/* CUSTOM VALUES FOR SENSOR 2 */ +#define ST_ACCEL_2_WAI_EXP 0x49 +#define ST_ACCEL_2_ODR_ADDR 0x20 +#define ST_ACCEL_2_ODR_MASK 0xf0 +#define ST_ACCEL_2_ODR_N_BIT 4 +#define ST_ACCEL_2_ODR_AVL_3HZ_VAL 0x01 +#define ST_ACCEL_2_ODR_AVL_6HZ_VAL 0x02 +#define ST_ACCEL_2_ODR_AVL_12HZ_VAL 0x03 +#define ST_ACCEL_2_ODR_AVL_25HZ_VAL 0x04 +#define ST_ACCEL_2_ODR_AVL_50HZ_VAL 0x05 +#define ST_ACCEL_2_ODR_AVL_100HZ_VAL 0x06 +#define ST_ACCEL_2_ODR_AVL_200HZ_VAL 0x07 +#define ST_ACCEL_2_ODR_AVL_400HZ_VAL 0x08 +#define ST_ACCEL_2_ODR_AVL_800HZ_VAL 0x09 +#define ST_ACCEL_2_ODR_AVL_1600HZ_VAL 0x0a +#define ST_ACCEL_2_FS_N_BIT 3 +#define ST_ACCEL_2_FS_ADDR 0x21 +#define ST_ACCEL_2_FS_MASK 0x38 +#define ST_ACCEL_2_FS_AVL_2_VAL 0X00 +#define ST_ACCEL_2_FS_AVL_4_VAL 0X01 +#define ST_ACCEL_2_FS_AVL_6_VAL 0x02 +#define ST_ACCEL_2_FS_AVL_8_VAL 0x03 +#define ST_ACCEL_2_FS_AVL_16_VAL 0x04 +#define ST_ACCEL_2_FS_AVL_2_GAIN 61 +#define ST_ACCEL_2_FS_AVL_4_GAIN 122 +#define ST_ACCEL_2_FS_AVL_6_GAIN 183 +#define ST_ACCEL_2_FS_AVL_8_GAIN 244 +#define ST_ACCEL_2_FS_AVL_16_GAIN 732 +#define ST_ACCEL_2_BDU_ADDR 0x20 +#define ST_ACCEL_2_BDU_MASK 0x08 +#define ST_ACCEL_2_DRDY_IRQ_ADDR 0x22 +#define ST_ACCEL_2_DRDY_IRQ_MASK 0x04 +#define ST_ACCEL_2_MULTIREAD_BIT true + +/* CUSTOM VALUES FOR SENSOR 3 */ +#define ST_ACCEL_3_WAI_EXP 0x32 +#define ST_ACCEL_3_ODR_ADDR 0x20 +#define ST_ACCEL_3_ODR_MASK 0x18 +#define ST_ACCEL_3_ODR_N_BIT 2 +#define ST_ACCEL_3_ODR_AVL_50HZ_VAL 0x00 +#define ST_ACCEL_3_ODR_AVL_100HZ_VAL 0x01 +#define ST_ACCEL_3_ODR_AVL_400HZ_VAL 0x02 +#define ST_ACCEL_3_ODR_AVL_1000HZ_VAL 0x03 +#define ST_ACCEL_3_PW_ADDR 0x20 +#define ST_ACCEL_3_PW_MASK 0xe0 +#define ST_ACCEL_3_PW_N_BIT 3 +#define ST_ACCEL_3_PW_VALUE_ON 0x01 +#define ST_ACCEL_3_PW_VALUE_OFF 0x00 +#define ST_ACCEL_3_FS_N_BIT 2 +#define ST_ACCEL_3_FS_ADDR 0x23 +#define ST_ACCEL_3_FS_MASK 0x30 +#define ST_ACCEL_3_FS_AVL_2_VAL 0X00 +#define ST_ACCEL_3_FS_AVL_4_VAL 0X01 +#define ST_ACCEL_3_FS_AVL_8_VAL 0x03 +#define ST_ACCEL_3_FS_AVL_2_GAIN 1000 +#define ST_ACCEL_3_FS_AVL_4_GAIN 2000 +#define ST_ACCEL_3_FS_AVL_8_GAIN 3900 +#define ST_ACCEL_3_BDU_ADDR 0x23 +#define ST_ACCEL_3_BDU_MASK 0x80 +#define ST_ACCEL_3_DRDY_IRQ_ADDR 0x22 +#define ST_ACCEL_3_DRDY_IRQ_MASK 0x02 +#define ST_ACCEL_3_MULTIREAD_BIT true + +/* CUSTOM VALUES FOR SENSOR 4 */ +#define ST_ACCEL_4_WAI_EXP 0x40 +#define ST_ACCEL_4_ODR_ADDR 0x20 +#define ST_ACCEL_4_ODR_MASK 0xf0 +#define ST_ACCEL_4_ODR_N_BIT 4 +#define ST_ACCEL_4_ODR_AVL_3HZ_VAL 0x01 +#define ST_ACCEL_4_ODR_AVL_6HZ_VAL 0x02 +#define ST_ACCEL_4_ODR_AVL_12HZ_VAL 0x03 +#define ST_ACCEL_4_ODR_AVL_25HZ_VAL 0x04 +#define ST_ACCEL_4_ODR_AVL_50HZ_VAL 0x05 +#define ST_ACCEL_4_ODR_AVL_100HZ_VAL 0x06 +#define ST_ACCEL_4_ODR_AVL_200HZ_VAL 0x07 +#define ST_ACCEL_4_ODR_AVL_400HZ_VAL 0x08 +#define ST_ACCEL_4_ODR_AVL_800HZ_VAL 0x09 +#define ST_ACCEL_4_ODR_AVL_1600HZ_VAL 0x0a +#define ST_ACCEL_4_FS_N_BIT 3 +#define ST_ACCEL_4_FS_ADDR 0x24 +#define ST_ACCEL_4_FS_MASK 0x38 +#define ST_ACCEL_4_FS_AVL_2_VAL 0X00 +#define ST_ACCEL_4_FS_AVL_4_VAL 0X01 +#define ST_ACCEL_4_FS_AVL_6_VAL 0x02 +#define ST_ACCEL_4_FS_AVL_8_VAL 0x03 +#define ST_ACCEL_4_FS_AVL_16_VAL 0x04 +#define ST_ACCEL_4_FS_AVL_2_GAIN 61 +#define ST_ACCEL_4_FS_AVL_4_GAIN 122 +#define ST_ACCEL_4_FS_AVL_6_GAIN 183 +#define ST_ACCEL_4_FS_AVL_8_GAIN 244 +#define ST_ACCEL_4_FS_AVL_16_GAIN 732 +#define ST_ACCEL_4_BDU_ADDR 0x20 +#define ST_ACCEL_4_BDU_MASK 0x08 +#define ST_ACCEL_4_DRDY_IRQ_ADDR 0x23 +#define ST_ACCEL_4_DRDY_IRQ_MASK 0x80 +#define ST_ACCEL_4_IG1_EN_ADDR 0x23 +#define ST_ACCEL_4_IG1_EN_MASK 0x08 +#define ST_ACCEL_4_MULTIREAD_BIT false + +struct st_accel_odr_available { +unsigned int hz; +u8 value; +}; + +struct st_accel_odr { +u8 addr; +u8 mask; +short num_bit; +struct st_accel_odr_available odr_avl[ST_ACCEL_ODR_LIST_MAX ]; +}; + +struct st_accel_power { +u8 addr; +u8 mask; +unsigned short num_bit; +u8 value_off; +u8 value_on; +}; + +struct st_accel_fullscale_available { +unsigned int num; +u8 value; +unsigned int gain; +}; + +struct st_accel_fullscale { +u8 addr; +u8 mask; +unsigned short num_bit; +struct st_accel_fullscale_available fs_avl[ST_ACCEL_FULLSCALE_AVL_MAX ]; +}; + +struct st_accel_bdu { +u8 addr; +u8 mask; +}; + +struct st_accel_interrupt_generator { +u8 en_addr; +u8 latch_mask_addr; +u8 en_mask; +u8 latching_mask; +}; + +struct st_accel_data_ready_irq { +u8 addr; +u8 mask; +struct st_accel_interrupt_generator ig1; +}; + +#define ST_ACCEL_LSM_CHANNELS (index, mod, endian, bits, addr) \ +{ \ + .type = IIO_ACCEL, \ + .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, \ + }, \ +} + +static const struct iio_chan_spec st_accel_12bit_channels[] = { +ST_ACCEL_LSM_CHANNELS (ST_ACCEL_SCAN_X ,IIO_MOD_X ,IIO_LE , +ST_ACCEL_DEFAULT_12_REALBITS ,ST_ACCEL_DEFAULT_OUT_X_L_ADDR ), +ST_ACCEL_LSM_CHANNELS (ST_ACCEL_SCAN_Y ,IIO_MOD_Y ,IIO_LE , +ST_ACCEL_DEFAULT_12_REALBITS ,ST_ACCEL_DEFAULT_OUT_Y_L_ADDR ), +ST_ACCEL_LSM_CHANNELS (ST_ACCEL_SCAN_Z ,IIO_MOD_Z ,IIO_LE , +ST_ACCEL_DEFAULT_12_REALBITS ,ST_ACCEL_DEFAULT_OUT_Z_L_ADDR ), +IIO_CHAN_SOFT_TIMESTAMP (3 ) +}; + +static const struct iio_chan_spec st_accel_16bit_channels[] = { +ST_ACCEL_LSM_CHANNELS (ST_ACCEL_SCAN_X ,IIO_MOD_X ,IIO_LE , +ST_ACCEL_DEFAULT_16_REALBITS ,ST_ACCEL_DEFAULT_OUT_X_L_ADDR ), +ST_ACCEL_LSM_CHANNELS (ST_ACCEL_SCAN_Y ,IIO_MOD_Y ,IIO_LE , +ST_ACCEL_DEFAULT_16_REALBITS ,ST_ACCEL_DEFAULT_OUT_Y_L_ADDR ), +ST_ACCEL_LSM_CHANNELS (ST_ACCEL_SCAN_Z ,IIO_MOD_Z ,IIO_LE , +ST_ACCEL_DEFAULT_16_REALBITS ,ST_ACCEL_DEFAULT_OUT_Z_L_ADDR ), +IIO_CHAN_SOFT_TIMESTAMP (3 ) +}; + +static const struct st_accel_platform_data st_accel_default_pdata = { + .fullscale =ST_ACCEL_FS_AVL_2G , + .sampling_frequency =ST_ACCEL_ODR_AVL_100HZ , +}; + +/** +* struct st_accel_sensors - ST accel 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. +* @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. +* +*/ + +static const struct st_accel_sensors { +u8 wai; +struct iio_chan_spec *ch; +struct st_accel_odr odr; +struct st_accel_power pw; +struct st_accel_fullscale fs; +struct st_accel_bdu bdu; +struct st_accel_data_ready_irq drdy_irq; +bool multi_read_bit; +} st_accel_sensors[] = { + { + .wai =ST_ACCEL_1_WAI_EXP , + .ch = (struct iio_chan_spec *)st_accel_12bit_channels, + .odr = { + .addr =ST_ACCEL_1_ODR_ADDR , + .mask =ST_ACCEL_1_ODR_MASK , + .num_bit =ST_ACCEL_1_ODR_N_BIT , + .odr_avl = { + [0 ] = { + .hz =ST_ACCEL_ODR_AVL_1HZ , + .value =ST_ACCEL_1_ODR_AVL_1HZ_VAL , + }, + [1 ] = { + .hz =ST_ACCEL_ODR_AVL_10HZ , + .value =ST_ACCEL_1_ODR_AVL_10HZ_VAL , + }, + [2 ] = { + .hz =ST_ACCEL_ODR_AVL_25HZ , + .value =ST_ACCEL_1_ODR_AVL_25HZ_VAL , + }, + [3 ] = { + .hz =ST_ACCEL_ODR_AVL_50HZ , + .value =ST_ACCEL_1_ODR_AVL_50HZ_VAL , + }, + [4 ] = { + .hz =ST_ACCEL_ODR_AVL_100HZ , + .value =ST_ACCEL_1_ODR_AVL_100HZ_VAL , + }, + [5 ] = { + .hz =ST_ACCEL_ODR_AVL_200HZ , + .value =ST_ACCEL_1_ODR_AVL_200HZ_VAL , + }, + [6 ] = { + .hz =ST_ACCEL_ODR_AVL_400HZ , + .value =ST_ACCEL_1_ODR_AVL_400HZ_VAL , + }, + [7 ] = { + .hz =ST_ACCEL_ODR_AVL_1600HZ , + .value =ST_ACCEL_1_ODR_AVL_1600HZ_VAL , + }, + }, + }, + .pw = { + .addr =ST_ACCEL_1_ODR_ADDR , + .mask =ST_ACCEL_1_ODR_MASK , + .num_bit =ST_ACCEL_1_ODR_N_BIT , + .value_off =ST_ACCEL_DEFAULT_POWER_OFF_VALUE , + }, + .fs = { + .addr =ST_ACCEL_1_FS_ADDR , + .mask =ST_ACCEL_1_FS_MASK , + .num_bit =ST_ACCEL_1_FS_N_BIT , + .fs_avl = { + [0 ] = { + .num =ST_ACCEL_FS_AVL_2G , + .value =ST_ACCEL_1_FS_AVL_2_VAL , + .gain =ST_ACCEL_1_FS_AVL_2_GAIN , + }, + [1 ] = { + .num =ST_ACCEL_FS_AVL_4G , + .value =ST_ACCEL_1_FS_AVL_4_VAL , + .gain =ST_ACCEL_1_FS_AVL_4_GAIN , + }, + [2 ] = { + .num =ST_ACCEL_FS_AVL_8G , + .value =ST_ACCEL_1_FS_AVL_8_VAL , + .gain =ST_ACCEL_1_FS_AVL_8_GAIN , + }, + [3 ] = { + .num =ST_ACCEL_FS_AVL_16G , + .value =ST_ACCEL_1_FS_AVL_16_VAL , + .gain =ST_ACCEL_1_FS_AVL_16_GAIN , + }, + }, + }, + .bdu = { + .addr =ST_ACCEL_1_BDU_ADDR , + .mask =ST_ACCEL_1_BDU_MASK , + }, + .drdy_irq = { + .addr =ST_ACCEL_1_DRDY_IRQ_ADDR , + .mask =ST_ACCEL_1_DRDY_IRQ_MASK , + }, + .multi_read_bit =ST_ACCEL_1_MULTIREAD_BIT , + }, + { + .wai =ST_ACCEL_2_WAI_EXP , + .ch = (struct iio_chan_spec *)st_accel_16bit_channels, + .odr = { + .addr =ST_ACCEL_2_ODR_ADDR , + .mask =ST_ACCEL_2_ODR_MASK , + .num_bit =ST_ACCEL_2_ODR_N_BIT , + .odr_avl = { + [0 ] = { + .hz =ST_ACCEL_ODR_AVL_3HZ , + .value =ST_ACCEL_2_ODR_AVL_3HZ_VAL , + }, + [1 ] = { + .hz =ST_ACCEL_ODR_AVL_6HZ , + .value =ST_ACCEL_2_ODR_AVL_6HZ_VAL , + }, + [2 ] = { + .hz =ST_ACCEL_ODR_AVL_12HZ , + .value =ST_ACCEL_2_ODR_AVL_12HZ_VAL , + }, + [3 ] = { + .hz =ST_ACCEL_ODR_AVL_25HZ , + .value =ST_ACCEL_2_ODR_AVL_25HZ_VAL , + }, + [4 ] = { + .hz =ST_ACCEL_ODR_AVL_50HZ , + .value =ST_ACCEL_2_ODR_AVL_50HZ_VAL , + }, + [5 ] = { + .hz =ST_ACCEL_ODR_AVL_100HZ , + .value =ST_ACCEL_2_ODR_AVL_100HZ_VAL , + }, + [6 ] = { + .hz =ST_ACCEL_ODR_AVL_200HZ , + .value =ST_ACCEL_2_ODR_AVL_200HZ_VAL , + }, + [7 ] = { + .hz =ST_ACCEL_ODR_AVL_400HZ , + .value =ST_ACCEL_2_ODR_AVL_400HZ_VAL , + }, + [8 ] = { + .hz =ST_ACCEL_ODR_AVL_800HZ , + .value =ST_ACCEL_2_ODR_AVL_800HZ_VAL , + }, + [9 ] = { + .hz =ST_ACCEL_ODR_AVL_1600HZ , + .value =ST_ACCEL_2_ODR_AVL_1600HZ_VAL , + }, + }, + }, + .pw = { + .addr =ST_ACCEL_2_ODR_ADDR , + .mask =ST_ACCEL_2_ODR_MASK , + .num_bit =ST_ACCEL_2_ODR_N_BIT , + .value_off =ST_ACCEL_DEFAULT_POWER_OFF_VALUE , + }, + .fs = { + .addr =ST_ACCEL_2_FS_ADDR , + .mask =ST_ACCEL_2_FS_MASK , + .num_bit =ST_ACCEL_2_FS_N_BIT , + .fs_avl = { + [0 ] = { + .num =ST_ACCEL_FS_AVL_2G , + .value =ST_ACCEL_2_FS_AVL_2_VAL , + .gain =ST_ACCEL_2_FS_AVL_2_GAIN , + }, + [1 ] = { + .num =ST_ACCEL_FS_AVL_4G , + .value =ST_ACCEL_2_FS_AVL_4_VAL , + .gain =ST_ACCEL_2_FS_AVL_4_GAIN , + }, + [2 ] = { + .num =ST_ACCEL_FS_AVL_6G , + .value =ST_ACCEL_2_FS_AVL_6_VAL , + .gain =ST_ACCEL_2_FS_AVL_6_GAIN , + }, + [3 ] = { + .num =ST_ACCEL_FS_AVL_8G , + .value =ST_ACCEL_2_FS_AVL_8_VAL , + .gain =ST_ACCEL_2_FS_AVL_8_GAIN , + }, + [4 ] = { + .num =ST_ACCEL_FS_AVL_16G , + .value =ST_ACCEL_2_FS_AVL_16_VAL , + .gain =ST_ACCEL_2_FS_AVL_16_GAIN , + }, + }, + }, + .drdy_irq = { + .addr =ST_ACCEL_2_DRDY_IRQ_ADDR , + .mask =ST_ACCEL_2_DRDY_IRQ_MASK , + }, + .bdu = { + .addr =ST_ACCEL_2_BDU_ADDR , + .mask =ST_ACCEL_2_BDU_MASK , + }, + .multi_read_bit =ST_ACCEL_2_MULTIREAD_BIT , + }, + { + .wai =ST_ACCEL_3_WAI_EXP , + .ch = (struct iio_chan_spec *)st_accel_12bit_channels, + .odr = { + .addr =ST_ACCEL_3_ODR_ADDR , + .mask =ST_ACCEL_3_ODR_MASK , + .num_bit =ST_ACCEL_3_ODR_N_BIT , + .odr_avl = { + [0 ] = { + .hz =ST_ACCEL_ODR_AVL_50HZ , + .value =ST_ACCEL_3_ODR_AVL_50HZ_VAL , + }, + [1 ] = { + .hz =ST_ACCEL_ODR_AVL_100HZ , + .value =ST_ACCEL_3_ODR_AVL_100HZ_VAL , + }, + [2 ] = { + .hz =ST_ACCEL_ODR_AVL_400HZ , + .value =ST_ACCEL_3_ODR_AVL_400HZ_VAL , + }, + [3 ] = { + .hz =ST_ACCEL_ODR_AVL_1000HZ , + .value =ST_ACCEL_3_ODR_AVL_1000HZ_VAL , + }, + }, + }, + .pw = { + .addr =ST_ACCEL_3_PW_ADDR , + .mask =ST_ACCEL_3_PW_MASK , + .num_bit =ST_ACCEL_3_PW_N_BIT , + .value_on =ST_ACCEL_3_PW_VALUE_ON , + .value_off =ST_ACCEL_3_PW_VALUE_OFF , + }, + .fs = { + .addr =ST_ACCEL_3_FS_ADDR , + .mask =ST_ACCEL_3_FS_MASK , + .num_bit =ST_ACCEL_3_FS_N_BIT , + .fs_avl = { + [0 ] = { + .num =ST_ACCEL_FS_AVL_2G , + .value =ST_ACCEL_3_FS_AVL_2_VAL , + .gain =ST_ACCEL_3_FS_AVL_2_GAIN , + }, + [1 ] = { + .num =ST_ACCEL_FS_AVL_4G , + .value =ST_ACCEL_3_FS_AVL_4_VAL , + .gain =ST_ACCEL_3_FS_AVL_4_GAIN , + }, + [2 ] = { + .num =ST_ACCEL_FS_AVL_8G , + .value =ST_ACCEL_3_FS_AVL_8_VAL , + .gain =ST_ACCEL_3_FS_AVL_8_GAIN , + }, + }, + }, + .bdu = { + .addr =ST_ACCEL_3_BDU_ADDR , + .mask =ST_ACCEL_3_BDU_MASK , + }, + .drdy_irq = { + .addr =ST_ACCEL_3_DRDY_IRQ_ADDR , + .mask =ST_ACCEL_3_DRDY_IRQ_MASK , + }, + .multi_read_bit =ST_ACCEL_3_MULTIREAD_BIT , + }, + { + .wai =ST_ACCEL_4_WAI_EXP , + .ch = (struct iio_chan_spec *)st_accel_16bit_channels, + .odr = { + .addr =ST_ACCEL_4_ODR_ADDR , + .mask =ST_ACCEL_4_ODR_MASK , + .num_bit =ST_ACCEL_4_ODR_N_BIT , + .odr_avl = { + [0 ] = { + .hz =ST_ACCEL_ODR_AVL_3HZ , + .value =ST_ACCEL_4_ODR_AVL_3HZ_VAL , + }, + [1 ] = { + .hz =ST_ACCEL_ODR_AVL_6HZ , + .value =ST_ACCEL_4_ODR_AVL_6HZ_VAL , + }, + [2 ] = { + .hz =ST_ACCEL_ODR_AVL_12HZ , + .value =ST_ACCEL_4_ODR_AVL_12HZ_VAL , + }, + [3 ] = { + .hz =ST_ACCEL_ODR_AVL_25HZ , + .value =ST_ACCEL_4_ODR_AVL_25HZ_VAL , + }, + [4 ] = { + .hz =ST_ACCEL_ODR_AVL_50HZ , + .value =ST_ACCEL_4_ODR_AVL_50HZ_VAL , + }, + [5 ] = { + .hz =ST_ACCEL_ODR_AVL_100HZ , + .value =ST_ACCEL_4_ODR_AVL_100HZ_VAL , + }, + [6 ] = { + .hz =ST_ACCEL_ODR_AVL_200HZ , + .value =ST_ACCEL_4_ODR_AVL_200HZ_VAL , + }, + [7 ] = { + .hz =ST_ACCEL_ODR_AVL_400HZ , + .value =ST_ACCEL_4_ODR_AVL_400HZ_VAL , + }, + [8 ] = { + .hz =ST_ACCEL_ODR_AVL_800HZ , + .value =ST_ACCEL_4_ODR_AVL_800HZ_VAL , + }, + [9 ] = { + .hz =ST_ACCEL_ODR_AVL_1600HZ , + .value =ST_ACCEL_4_ODR_AVL_1600HZ_VAL , + }, + }, + }, + .pw = { + .addr =ST_ACCEL_4_ODR_ADDR , + .mask =ST_ACCEL_4_ODR_MASK , + .num_bit =ST_ACCEL_4_ODR_N_BIT , + .value_off =ST_ACCEL_DEFAULT_POWER_OFF_VALUE , + }, + .fs = { + .addr =ST_ACCEL_4_FS_ADDR , + .mask =ST_ACCEL_4_FS_MASK , + .num_bit =ST_ACCEL_4_FS_N_BIT , + .fs_avl = { + [0 ] = { + .num =ST_ACCEL_FS_AVL_2G , + .value =ST_ACCEL_4_FS_AVL_2_VAL , + .gain =ST_ACCEL_4_FS_AVL_2_GAIN , + }, + [1 ] = { + .num =ST_ACCEL_FS_AVL_4G , + .value =ST_ACCEL_4_FS_AVL_4_VAL , + .gain =ST_ACCEL_4_FS_AVL_4_GAIN , + }, + [2 ] = { + .num =ST_ACCEL_FS_AVL_6G , + .value =ST_ACCEL_4_FS_AVL_6_VAL , + .gain =ST_ACCEL_4_FS_AVL_6_GAIN , + }, + [3 ] = { + .num =ST_ACCEL_FS_AVL_8G , + .value =ST_ACCEL_4_FS_AVL_8_VAL , + .gain =ST_ACCEL_4_FS_AVL_8_GAIN , + }, + [4 ] = { + .num =ST_ACCEL_FS_AVL_16G , + .value =ST_ACCEL_4_FS_AVL_16_VAL , + .gain =ST_ACCEL_4_FS_AVL_16_GAIN , + }, + }, + }, + .bdu = { + .addr =ST_ACCEL_4_BDU_ADDR , + .mask =ST_ACCEL_4_BDU_MASK , + }, + .drdy_irq = { + .addr =ST_ACCEL_4_DRDY_IRQ_ADDR , + .mask =ST_ACCEL_4_DRDY_IRQ_MASK , + .ig1 = { + .en_addr =ST_ACCEL_4_IG1_EN_ADDR , + .en_mask =ST_ACCEL_4_IG1_EN_MASK , + }, + }, + .multi_read_bit =ST_ACCEL_4_MULTIREAD_BIT , + }, +}; + +static int st_accel_write_data_with_mask(struct iio_dev *indio_dev, u8
reg_addr,drivers/iio/accel/st_accel_core.c new file +441
-0+ u8 mask, short num_bit, u8 data) +{ + int err, j, pos; +u8 prev_data; +u8 new_data; +struct st_accel_data *adata =iio_priv (indio_dev); + + pos =8 - num_bit; +for (j =128 ; j >=0 ; j = j/2 ) { +if (mask / j >0 ) +break ; +else + pos--; + } + + err = adata->read_byte (adata, reg_addr, &prev_data); +if (err <0 ) + goto st_accel_write_data_with_mask_error; + + new_data = ((prev_data & (~mask)) | ((data << pos) & mask)); + err = adata->write_byte (adata, reg_addr, new_data); + +st_accel_write_data_with_mask_error: +return err; +} + +static int st_accel_match_odr (const struct st_accel_sensors *sensor, +unsigned int odr,struct st_accel_odr_available *odr_out) +{ +int i, ret =-1 ; + +for (i =0 ; i <ARRAY_SIZE (sensor->odr.odr_avl); i++) { +if (sensor->odr.odr_avl[i].hz == odr) { + odr_out->hz = sensor->odr.odr_avl[i].hz; + odr_out->value = sensor->odr.odr_avl[i].value; + ret =0 ; +break ; + } + } + +return ret; +} + +static int st_accel_match_fs (const struct st_accel_sensors *sensor, +unsigned int fs,struct st_accel_fullscale_available *fs_out) +{ +int i, ret =-1 ; + +for (i =0 ; i <ARRAY_SIZE (sensor->fs.fs_avl); i++) { +if (sensor->fs.fs_avl[i].num == fs) { + 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; + ret =0 ; +break ; + } + } + +return ret; +} + +int st_accel_set_dataready_irq (struct iio_dev *indio_dev,bool enable) +{ +int err; +struct st_accel_data *adata; + + adata =iio_priv (indio_dev); +if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr >0 ) { + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].drdy_irq.ig1.en_addr, + st_accel_sensors[adata->index].drdy_irq.ig1.en_mask,1 , + (int )enable); +if (err <0 ) + goto st_accel_set_dataready_irq_error; + } + +if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr >0 ) { + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr, + st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask,1 , + (int )enable); +if (err <0 ) + goto st_accel_set_dataready_irq_error; + } + + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].drdy_irq.addr, + st_accel_sensors[adata->index].drdy_irq.mask,1 , (int )enable); +if (err <0 ) + goto st_accel_set_dataready_irq_error; + +st_accel_set_dataready_irq_error: +return err; +} +EXPORT_SYMBOL (st_accel_set_dataready_irq); + +static int st_accel_set_bdu (struct iio_dev *indio_dev, +const struct st_accel_bdu *bdu,u8 value) +{ +return st_accel_write_data_with_mask (indio_dev, bdu->addr, bdu->mask, +1 , value); +} + +static int st_accel_set_odr (struct iio_dev *indio_dev, +struct st_accel_odr_available *odr_available) +{ +int err; +struct st_accel_data *adata =iio_priv (indio_dev); + +if ((st_accel_sensors[adata->index].odr.addr == + st_accel_sensors[adata->index].pw.addr) && + (st_accel_sensors[adata->index].odr.mask == + st_accel_sensors[adata->index].pw.mask)) { +if (adata->enabled == (bool )ST_ACCEL_ON ) { + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].odr.addr, + st_accel_sensors[adata->index].odr.mask, + st_accel_sensors[adata->index].odr.num_bit, + odr_available->value); +if (err <0 ) + goto sc_accel_set_odr_error; + }else { + adata->odr = odr_available->hz; + err =0 ; + } + }else { + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].odr.addr, + st_accel_sensors[adata->index].odr.mask, + st_accel_sensors[adata->index].odr.num_bit, + odr_available->value); +if (err <0 ) + goto sc_accel_set_odr_error; + } + +sc_accel_set_odr_error: +return err; +} + +static int st_accel_set_enable (struct iio_dev *indio_dev,int enable) +{ +int err; +bool found; +u8 tmp_value; +struct st_accel_odr_available odr_out; +struct st_accel_data *adata =iio_priv (indio_dev); + +switch (enable) { +case ST_ACCEL_ON : + found = false; + tmp_value = st_accel_sensors[adata->index].pw.value_on; +if ((st_accel_sensors[adata->index].odr.addr == + st_accel_sensors[adata->index].pw.addr) && + (st_accel_sensors[adata->index].odr.mask == + st_accel_sensors[adata->index].pw.mask)) { + err =st_accel_match_odr ( + &st_accel_sensors[adata->index], adata->odr, + &odr_out); +if (err <0 ) + goto set_enable_error; + tmp_value = odr_out.value; + found = true; + } + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].pw.addr, + st_accel_sensors[adata->index].pw.mask, + st_accel_sensors[adata->index].pw.num_bit, + tmp_value); +if (err <0 ) + goto set_enable_error; + adata->enabled = (bool )ST_ACCEL_ON ; +if (found) + adata->odr = odr_out.hz; +break ; +case ST_ACCEL_OFF : + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].pw.addr, + st_accel_sensors[adata->index].pw.mask, + st_accel_sensors[adata->index].pw.num_bit, + st_accel_sensors[adata->index].pw.value_off); +if (err <0 ) + goto set_enable_error; + adata->enabled = (bool )ST_ACCEL_OFF ; +break ; +default : + err = -EINVAL ; + goto set_enable_error; + } + +set_enable_error: +return err; +} + +static int st_accel_set_fullscale (struct iio_dev *indio_dev, +struct st_accel_fullscale_available *fs_avl) +{ +int err; +struct st_accel_data *adata =iio_priv (indio_dev); + + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].fs.addr, + st_accel_sensors[adata->index].fs.mask, + st_accel_sensors[adata->index].fs.num_bit, + fs_avl->value); +if (err <0 ) + goto st_accel_set_fullscale_error; + + adata->fullscale = fs_avl->num; + adata->gain = fs_avl->gain; +return err; + +st_accel_set_fullscale_error: +dev_err (&indio_dev->dev,"failed to set new fullscale.\n" ); +return err; +} + +static int st_accel_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[ST_ACCEL_BYTE_FOR_CHANNEL ]; +struct st_accel_data *adata =iio_priv (indio_dev); + +switch (mask) { +case IIO_CHAN_INFO_RAW : +mutex_lock (&indio_dev->mlock); +if (indio_dev->currentmode ==INDIO_BUFFER_TRIGGERED ) { + err = -EBUSY ; + goto read_error; + }else { +if (!adata->enabled) { + err = -EIO ; + goto read_error; + }else { + err = adata->read_multiple_byte (adata, + ch->address,ST_ACCEL_BYTE_FOR_CHANNEL , + outdata); +if (err <0 ) + goto read_error; + + *val = ((s16)(((s16)(outdata[1 ]) <<8 ) + | outdata[0 ])) >> ch->scan_type.shift; + } + } +mutex_unlock (&indio_dev->mlock); +return IIO_VAL_INT ; +case IIO_CHAN_INFO_SCALE : + data_tmp =UG_TO_MS2 (adata->gain); + *val =0 ; + *val2 = data_tmp; +return IIO_VAL_INT_PLUS_NANO ; +default : +return -EINVAL ; + } + +read_error: +mutex_unlock (&indio_dev->mlock); +return err; +} + +static int st_accel_check_device_list (struct iio_dev *indio_dev,u8 wai) +{ +int i; +bool found; +struct st_accel_data *adata =iio_priv (indio_dev); + + found = false; +for (i =0 ; i <ARRAY_SIZE (st_accel_sensors); i++) { +if (st_accel_sensors[i].wai == wai) { + found = true; +break ; + } + } +if (!found) + goto check_device_error; + + adata->index = i; + +return i; + +check_device_error: +dev_err (&indio_dev->dev,"device not supported -> wai (0x%x).\n" , wai); +return -ENODEV ; +} + +static int st_accel_get_wai_device (struct iio_dev *indio_dev,u8 reg_addr, +u8 *value) +{ +int ret; +struct st_accel_data *adata =iio_priv (indio_dev); + + ret = adata->read_byte (adata, reg_addr, value); +if (ret <0 ) + goto read_byte_wai_error; + +return 0 ; + +read_byte_wai_error: +dev_err (&indio_dev->dev, +"failed to read WhoAmI (register 0x%x).\n" , reg_addr); +return -EIO ; +} + +static ssize_t st_accel_sysfs_set_sampling_frequency (struct device *dev, +struct device_attribute *attr,const char *buf,size_t size) +{ +int err; +unsigned int freq; +struct st_accel_odr_available odr_out; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + + err =kstrtoint (buf,10 , &freq); +if (err <0 ) + goto conversion_error; + +mutex_lock (&indio_dev->mlock); + err =st_accel_match_odr (&st_accel_sensors[adata->index], + freq, &odr_out); +if (err <0 ) + goto st_accel_sysfs_set_sampling_frequency_error; + + err =st_accel_set_odr (indio_dev, &odr_out); +if (err <0 ) { +dev_err (&indio_dev->dev, +"failed to set sampling frequency to %d.\n" , freq); + goto st_accel_sysfs_set_sampling_frequency_error; + } + adata->odr = odr_out.hz; + +st_accel_sysfs_set_sampling_frequency_error: +mutex_unlock (&indio_dev->mlock); +conversion_error: +return size; +} + +static ssize_t st_accel_sysfs_get_sampling_frequency (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + +return sprintf (buf,"%d\n" , adata->odr); +} + +static ssize_t st_accel_sysfs_set_enable (struct device *dev, +struct device_attribute *attr,const char *buf,size_t size) +{ +int err; +bool en; +struct iio_dev *indio_dev =dev_get_drvdata (dev); + + err =strtobool (buf, &en); +if (err <0 ) + goto set_enable_error; + +mutex_lock (&indio_dev->mlock); + err =st_accel_set_enable (indio_dev, (int )en); +if (err <0 ) +dev_err (&indio_dev->dev, +"failed to set enable to %d.\n" , (int )en); +mutex_unlock (&indio_dev->mlock); + +set_enable_error: +return size; +} + +static ssize_t st_accel_sysfs_get_enable (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + +return sprintf (buf,"%d\n" , (int )adata->enabled); +} + +static ssize_t st_accel_sysfs_get_fullscale (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + +return sprintf (buf,"%d\n" , adata->fullscale); +} + +static ssize_t st_accel_sysfs_set_fullscale (struct device *dev, +struct device_attribute *attr,const char *buf,size_t size) +{ +int err; +unsigned int fs; +struct st_accel_fullscale_available fs_out; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + + err =kstrtoint (buf,10 , &fs); +if (err <0 ) + goto conversion_error; + +mutex_lock (&indio_dev->mlock); + err =st_accel_match_fs (&st_accel_sensors[adata->index], fs, &fs_out); +if (err <0 ) + goto match_fullscale_error; + + err =st_accel_set_fullscale (indio_dev, &fs_out); +if (err <0 ) { +dev_err (&indio_dev->dev, +"failed to set new fullscale. (errn %d).\n" , err); + } + +match_fullscale_error: +mutex_unlock (&indio_dev->mlock); +conversion_error: +return size; +} + +static ssize_t st_accel_sysfs_fullscale_available (struct device *dev, +struct device_attribute *attr,char *buf) +{ +int i, len =0 ; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + +mutex_lock (&indio_dev->mlock); +for (i =0 ; i <ARRAY_SIZE (st_accel_sensors[adata->index].fs.fs_avl); + i++) { +if (st_accel_sensors[adata->index].fs.fs_avl[i].num ==0 ) +break ; + + len +=sprintf (buf+len,"%d " , + st_accel_sensors[adata->index].fs.fs_avl[i].num); + } +mutex_unlock (&indio_dev->mlock); + + len--; + len +=sprintf (buf+len,"\n" ); +return len; +} + +static ssize_t st_accel_sysfs_sampling_frequency_available(structdevice *dev,
drivers/iio/accel/st_accel_core.c new file +58
-0+ struct device_attribute *attr, char *buf) +{ + int i, len = 0; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + +mutex_lock (&indio_dev->mlock); +for (i =0 ; i <ARRAY_SIZE (st_accel_sensors[adata->index].odr.odr_avl); + i++) { +if (st_accel_sensors[adata->index].odr.odr_avl[i].hz ==0 ) +break ; + + len +=sprintf (buf+len,"%d " , + st_accel_sensors[adata->index].odr.odr_avl[i].hz); + } +mutex_unlock (&indio_dev->mlock); + + len--; + len +=sprintf (buf+len,"\n" ); +return len; +} + +/** +* IIO_DEVICE_ATTR - sampling_frequency_available +* @read: show all frequency available of the sensor. +* +*/ + +static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO, + st_accel_sysfs_sampling_frequency_available, NULL , 0); + +/** +* IIO_DEVICE_ATTR - fullscale_available +* @read: show all fullscale available of the sensor. +* +*/ + +static IIO_DEVICE_ATTR(fullscale_available, S_IRUGO, + st_accel_sysfs_fullscale_available, NULL , 0); + +/** +* IIO_DEVICE_ATTR - fullscale +* @read: show the current fullscale of the sensor. +* @write: store the current fullscale of the sensor. +* +*/ + +static IIO_DEVICE_ATTR(fullscale, S_IWUSR | S_IRUGO, + st_accel_sysfs_get_fullscale, st_accel_sysfs_set_fullscale , 0); + +/** +* IIO_DEVICE_ATTR - enable +* @read: show the current status of the sensor. +* @write: power on/off the sensor. +* +*/ + +static IIO_DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
st_accel_sysfs_get_enable,drivers/iio/accel/st_accel_core.c new file +133
-0+ st_accel_sysfs_set_enable , 0); + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + st_accel_sysfs_get_sampling_frequency, + st_accel_sysfs_set_sampling_frequency); + +static struct attribute *st_accel_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_fullscale_available.dev_attr.attr, + &iio_dev_attr_fullscale.dev_attr.attr, + &iio_dev_attr_enable.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, +NULL , +}; + +static const struct attribute_group st_accel_attribute_group = { + .attrs = st_accel_attributes, +}; + +static const struct iio_info acc_info = { + .driver_module =THIS_MODULE , + .attrs = &st_accel_attribute_group, + .read_raw = &st_accel_read_raw, +}; + +static int st_accel_init_sensor (struct iio_dev *indio_dev) +{ +int err; +struct st_accel_odr_available odr_out; +struct st_accel_fullscale_available fs_out; +struct st_accel_data *adata =iio_priv (indio_dev); + + err =st_accel_set_enable (indio_dev,ST_ACCEL_OFF ); +if (err <0 ) + goto init_error; + +st_accel_match_fs (&st_accel_sensors[adata->index], + adata->fullscale, &fs_out); + err =st_accel_set_fullscale (indio_dev, &fs_out); +if (err <0 ) + goto init_error; + +st_accel_match_odr (&st_accel_sensors[adata->index], + adata->odr, &odr_out); + err =st_accel_set_odr (indio_dev, &odr_out); +if (err <0 ) + goto init_error; + + err =st_accel_set_bdu (indio_dev, + &st_accel_sensors[adata->index].bdu, (u8 )ST_ACCEL_ON ); +if (err <0 ) + goto init_error; + +return 0 ; + +init_error: +dev_err (&indio_dev->dev,"failed to init sensor data." ); +return -EIO ; +} + +int st_accel_iio_probe (struct iio_dev *indio_dev) +{ +int err; +u8 wai; +struct st_accel_data *adata =iio_priv (indio_dev); +struct st_accel_platform_data *pdata; + +mutex_init (&adata->slock); + indio_dev->modes =INDIO_DIRECT_MODE ; + indio_dev->info = &acc_info; + + err =st_accel_get_wai_device (indio_dev, +ST_ACCEL_DEFAULT_WAI_ADDRESS , &wai); +if (err <0 ) + goto st_accel_iio_probe_error; + + err =st_accel_check_device_list (indio_dev, wai); +if (err <0 ) + goto st_accel_iio_probe_error; + + adata->multiread_bit = st_accel_sensors[adata->index].multi_read_bit; + indio_dev->channels = st_accel_sensors[adata->index].ch; + indio_dev->num_channels =ST_ACCEL_NUMBER_ALL_CHANNELS ; + pdata = adata->dev->platform_data; +if (pdata ==NULL ) { + adata->fullscale = st_accel_default_pdata.fullscale; + adata->odr = st_accel_default_pdata.sampling_frequency; + }else { + adata->fullscale = pdata->fullscale; + adata->odr = pdata->sampling_frequency; + } + + err =st_accel_init_sensor (indio_dev); +if (err <0 ) + goto st_accel_iio_probe_error; + + err =st_accel_allocate_ring (indio_dev); +if (err <0 ) + goto st_accel_iio_probe_error; + +if (*adata->irq_data_ready >0 ) { + err =st_accel_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; + +return err; + +iio_device_register_error: +st_accel_remove_trigger (indio_dev); +acc_probe_trigger_error: +st_accel_deallocate_ring (indio_dev); +st_accel_iio_probe_error: +return err; +} +EXPORT_SYMBOL (st_accel_iio_probe); + +void st_accel_iio_remove (struct iio_dev *indio_dev) +{ +iio_device_unregister (indio_dev); +st_accel_remove_trigger (indio_dev); +st_accel_deallocate_ring (indio_dev); +iio_device_free (indio_dev); +} +EXPORT_SYMBOL (st_accel_iio_remove); + +MODULE_AUTHOR ("Denis Ciocca <[email protected]>" ); +MODULE_DESCRIPTION ("STMicroelectronics accelerometers driver" ); +MODULE_LICENSE ("GPL v2" );drivers/iio/accel/st_accel_i2c.c new file +139
-0@@ -0,0 +1,139 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* 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/trigger.h> + +#include <linux/iio/accel/st_accel.h> + + +#define ST_ACCEL_I2C_MULTIREAD 0x80 + +static int st_accel_i2c_read_byte (struct st_accel_data *adata, +u8 reg_addr,u8 *res_byte) +{ +int err; + + err =i2c_smbus_read_byte_data (to_i2c_client (adata->dev), reg_addr); +if (err <0 ) + goto st_accel_i2c_read_byte_error; + *res_byte = err &0xff ; +return err; + +st_accel_i2c_read_byte_error: +return -EIO ; +} + +static int st_accel_i2c_read_multiple_byte (struct st_accel_data *adata, +u8 reg_addr,int len,u8 *data) +{ +int err; + +if (adata->multiread_bit == true) + reg_addr |=ST_ACCEL_I2C_MULTIREAD ; + + err =i2c_smbus_read_i2c_block_data (to_i2c_client (adata->dev), + reg_addr, len, data); +if (err <0 ) + goto st_accel_i2c_read_multiple_byte_error; + +return err; + +st_accel_i2c_read_multiple_byte_error: +return -EIO ; +} + +static int st_accel_i2c_write_byte (struct st_accel_data *adata, +u8 reg_addr,u8 data) +{ +return i2c_smbus_write_byte_data (to_i2c_client (adata->dev), + reg_addr, data); +} + +static int __devinit st_accel_i2c_probe (struct i2c_client *client, +const struct i2c_device_id *id) +{ +struct iio_dev *indio_dev; +struct st_accel_data *adata; +int err; + + indio_dev =iio_device_alloc (sizeof (*adata)); +if (indio_dev ==NULL ) { + err = -ENOMEM ; + goto iio_device_alloc_error; + } + + adata =iio_priv (indio_dev); + adata->dev = &client->dev; +i2c_set_clientdata (client, indio_dev); + + indio_dev->dev.parent = &client->dev; + indio_dev->name = client->name; + + adata->read_byte = st_accel_i2c_read_byte; + adata->write_byte = st_accel_i2c_write_byte; + adata->read_multiple_byte = st_accel_i2c_read_multiple_byte; + adata->irq_data_ready = &client->irq; + + err =st_accel_iio_probe (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 st_accel_i2c_remove(struct i2c_client *client) +{ +struct iio_dev *indio_dev =i2c_get_clientdata (client); + +st_accel_iio_remove (indio_dev); +return 0 ; +} + +static const struct i2c_device_id st_accel_id_table[] = { + {LSM303DLH_ACCEL_DEV_NAME }, + {LSM303DLHC_ACCEL_DEV_NAME }, + {LIS3DH_ACCEL_DEV_NAME }, + {LSM330D_ACCEL_DEV_NAME }, + {LSM330DL_ACCEL_DEV_NAME }, + {LSM330DLC_ACCEL_DEV_NAME }, + {LSM303D_ACCEL_DEV_NAME }, + {LSM9DS0_ACCEL_DEV_NAME }, + {LIS331DLH_ACCEL_DEV_NAME }, + {LSM303DL_ACCEL_DEV_NAME }, + {LSM303DLM_ACCEL_DEV_NAME }, + {LSM330_ACCEL_DEV_NAME }, + {}, +}; +MODULE_DEVICE_TABLE (i2c, st_accel_id_table); + +static struct i2c_driver st_accel_driver = { + .driver = { + .owner =THIS_MODULE , + .name ="st-accel-i2c" , + }, + .probe = st_accel_i2c_probe, + .remove =__devexit_p (st_accel_i2c_remove), + .id_table = st_accel_id_table, +}; +module_i2c_driver (st_accel_driver); + +MODULE_AUTHOR ("Denis Ciocca <[email protected]>" ); +MODULE_DESCRIPTION ("STMicroelectronics accelerometers i2c driver" ); +MODULE_LICENSE ("GPL v2" );drivers/iio/accel/st_accel_spi.c new file +199
-0@@ -0,0 +1,199 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> + +#include <linux/iio/accel/st_accel.h> + + +#define ACC_SPI_READ 0x80; +#define ACC_SPI_MULTIREAD 0xc0 + +static int st_accel_spi_read_byte (struct st_accel_data *adata, +u8 reg_addr,u8 *res_byte) +{ +struct spi_message msg; +int err; +u8 tx; + +struct spi_transfer xfers[] = { + { + .tx_buf = &tx, + .bits_per_word =8 , + .len =1 , + }, + { + .rx_buf = res_byte, + .bits_per_word =8 , + .len =1 , + } + }; + +mutex_lock (&adata->slock); + tx = reg_addr |ACC_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 (adata->dev), &msg); +mutex_unlock (&adata->slock); +if (err) + goto acc_spi_read_byte_error; + +return err; + +acc_spi_read_byte_error: +return -EIO ; +} + +static int st_accel_spi_read_multiple_byte (struct st_accel_data *adata, +u8 reg_addr,int len,u8 *data) +{ +struct spi_message msg; +int err; +u8 tx; + +struct spi_transfer xfers[] = { + { + .tx_buf = &tx, + .bits_per_word =8 , + .len =1 , + }, + { + .rx_buf = data, + .bits_per_word =8 , + .len = len, + } + }; + +mutex_lock (&adata->slock); +if (adata->multiread_bit == true) + tx = reg_addr |ACC_SPI_MULTIREAD ; +else + tx = reg_addr |ACC_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 (adata->dev), &msg); +mutex_unlock (&adata->slock); +if (err) + goto acc_spi_read_multiple_byte_error; +return len; + +acc_spi_read_multiple_byte_error: +return -EIO ; +} + +static int st_accel_spi_write_byte (struct st_accel_data *adata, +u8 reg_addr,u8 data) +{ +struct spi_message msg; +int err; +u8 tx[2 ]; + +struct spi_transfer xfers[] = { + { + .tx_buf = tx, + .bits_per_word =8 , + .len =2 , + } + }; + +mutex_lock (&adata->slock); + tx[0 ] = reg_addr; + tx[1 ] = data; +spi_message_init (&msg); +spi_message_add_tail (&xfers[0 ], &msg); + err =spi_sync (to_spi_device (adata->dev), &msg); +mutex_unlock (&adata->slock); + +return err; +} + +static int __devinit st_accel_spi_probe (struct spi_device *client) +{ +struct iio_dev *indio_dev; +struct st_accel_data *adata; +int err; + + indio_dev =iio_device_alloc (sizeof (*adata)); +if (indio_dev ==NULL ) { + err = -ENOMEM ; + goto iio_device_alloc_error; + } + + adata =iio_priv (indio_dev); + adata->dev = &client->dev; +spi_set_drvdata (client, indio_dev); + + indio_dev->dev.parent = &client->dev; + indio_dev->name = client->modalias; + + adata->read_byte = st_accel_spi_read_byte; + adata->write_byte = st_accel_spi_write_byte; + adata->read_multiple_byte = st_accel_spi_read_multiple_byte; + adata->irq_data_ready = &client->irq; + + err =st_accel_iio_probe (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 st_accel_spi_remove(struct spi_device *spi) +{ +struct iio_dev *indio_dev =spi_get_drvdata (spi); + +st_accel_iio_remove (indio_dev); +return 0 ; +} + +static const struct spi_device_id st_accel_id_table[] = { + {LSM303DLH_ACCEL_DEV_NAME }, + {LSM303DLHC_ACCEL_DEV_NAME }, + {LIS3DH_ACCEL_DEV_NAME }, + {LSM330D_ACCEL_DEV_NAME }, + {LSM330DL_ACCEL_DEV_NAME }, + {LSM330DLC_ACCEL_DEV_NAME }, + {LSM303D_ACCEL_DEV_NAME }, + {LSM9DS0_ACCEL_DEV_NAME }, + {LIS331DLH_ACCEL_DEV_NAME }, + {LSM303DL_ACCEL_DEV_NAME }, + {LSM303DLM_ACCEL_DEV_NAME }, + {LSM330_ACCEL_DEV_NAME }, + {}, +}; +MODULE_DEVICE_TABLE (spi, st_accel_id_table); + +static struct spi_driver st_accel_driver = { + .driver = { + .owner =THIS_MODULE , + .name ="st-accel-spi" , + }, + .probe = st_accel_spi_probe, + .remove =__devexit_p (st_accel_spi_remove), + .id_table = st_accel_id_table, +}; +module_spi_driver (st_accel_driver); + +MODULE_AUTHOR ("Denis Ciocca <[email protected]>" ); +MODULE_DESCRIPTION ("STMicroelectronics accelerometers spi driver" ); +MODULE_LICENSE ("GPL v2" );drivers/iio/accel/st_accel_trigger.c new file +22
-0@@ -0,0 +1,84 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> + +#include <linux/iio/accel/st_accel.h> + + +static int st_accel_trig_acc_set_state(struct iio_trigger *trig, boolstate)
drivers/iio/accel/st_accel_trigger.c new file +62
-0+{ +struct iio_dev *indio_dev = trig->private_data; +return st_accel_set_dataready_irq (indio_dev, state); +} + +static const struct iio_trigger_ops st_accel_trigger_ops = { + .owner =THIS_MODULE , + .set_trigger_state = &st_accel_trig_acc_set_state, +}; + +int st_accel_probe_trigger (struct iio_dev *indio_dev) +{ +int err; +struct st_accel_data *adata =iio_priv (indio_dev); + + adata->trig =iio_trigger_alloc ("%s-trigger" , indio_dev->name); +if (adata->trig ==NULL ) { + err = -ENOMEM ; +dev_err (&indio_dev->dev,"failed to allocate iio trigger.\n" ); + goto iio_trigger_alloc_error; + } + + err =request_threaded_irq (*adata->irq_data_ready, + iio_trigger_generic_data_rdy_poll, +NULL , +IRQF_TRIGGER_RISING , + adata->trig->name, + adata->trig); +if (err) + goto request_irq_error; + + adata->trig->private_data = indio_dev; + adata->trig->ops = &st_accel_trigger_ops; + adata->trig->dev.parent = adata->dev; + + err =iio_trigger_register (adata->trig); +if (err <0 ) { +dev_err (&indio_dev->dev,"failed to register iio trigger.\n" ); + goto iio_trigger_register_error; + } + indio_dev->trig = adata->trig; + +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; +} +EXPORT_SYMBOL (st_accel_probe_trigger); + +void st_accel_remove_trigger (struct iio_dev *indio_dev) +{ +struct st_accel_data *adata =iio_priv (indio_dev); + +iio_trigger_unregister (adata->trig); +free_irq (*adata->irq_data_ready, adata->trig); +iio_trigger_free (adata->trig); +} +EXPORT_SYMBOL (st_accel_remove_trigger);include/linux/iio/accel/st_accel.h new file +119
-0@@ -0,0 +1,119 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* v. 1.0.0 +* Licensed under the GPL-2. +*/ + +/* +* Supported sensors: +* LSM303DLH +* LSM303DLHC +* LIS3DH +* LSM330D +* LSM330DL +* LSM330DLC +* LSM303D +* LSM9DS0 +* LIS331DLH +* LSM303DL +* LSM303DLM +* LSM330 +*/ + + +#ifndef ST_ACCEL_H +#define ST_ACCEL_H + +#define LSM303DLH_ACCEL_DEV_NAME "lsm303dlh_accel" +#define LSM303DLHC_ACCEL_DEV_NAME "lsm303dlhc_accel" +#define LIS3DH_ACCEL_DEV_NAME "lis3dh" +#define LSM330D_ACCEL_DEV_NAME "lsm330d_accel" +#define LSM330DL_ACCEL_DEV_NAME "lsm330dl_accel" +#define LSM330DLC_ACCEL_DEV_NAME "lsm330dlc_accel" +#define LSM303D_ACCEL_DEV_NAME "lsm303d" +#define LSM9DS0_ACCEL_DEV_NAME "lsm9ds0" +#define LIS331DLH_ACCEL_DEV_NAME "lis331dlh" +#define LSM303DL_ACCEL_DEV_NAME "lsm303dl_accel" +#define LSM303DLM_ACCEL_DEV_NAME "lsm303dlm_accel" +#define LSM330_ACCEL_DEV_NAME "lsm330_accel" + +#define ST_ACCEL_NUMBER_ALL_CHANNELS 4 +#define ST_ACCEL_BYTE_FOR_CHANNEL 2 +#define ST_ACCEL_SCAN_X 0 +#define ST_ACCEL_SCAN_Y 1 +#define ST_ACCEL_SCAN_Z 2 + +/** +* struct st_accel_data - ST accel device status +* @dev: Pointer to instance of struct device (I2C or SPI). +* @name: Name of the sensor in use. +* @enabled: Status of the sensor (0->off, 1->on). +* @index: Number used to point the sensor being used in the +* st_accel_sensors struct. +* @fullscale: Maximum range of measure by the sensor. +* @gain: Sensitivity of the sensor [ug/LSB]. +* @odr: Output data rate of the sensor. +* @multiread_bit: Use or not particular bit for [I2C/SPI] multiread. +* @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. +* @set_trigger_parent: Function used to set the trigger parent. +* @trig: The trigger in use by the core driver. +* @irq_data_ready: IRQ number for data ready on INT1 pin. +* @slock: mutex for read and write operation. +*/ + +struct st_accel_data { +struct device *dev; +bool enabled; +short index; + +unsigned int fullscale; +unsigned int gain; +unsigned int odr; + +bool multiread_bit; +int (*read_byte) (struct st_accel_data *adata,u8 reg_addr, +u8 *res_byte); +int (*write_byte) (struct st_accel_data *adata,u8 reg_addr,u8 data); +int (*read_multiple_byte) (struct st_accel_data *adata,u8 reg_addr, +int len,u8 *data); + +struct iio_trigger *trig; +int *irq_data_ready; +struct mutex slock; +}; + +int st_accel_iio_probe (struct iio_dev *indio_dev); +void st_accel_iio_remove (struct iio_dev *indio_dev); +int st_accel_set_dataready_irq (struct iio_dev *indio_dev,bool enable); + +#ifdef CONFIG_IIO_BUFFER +int st_accel_probe_trigger (struct iio_dev *indio_dev); +void st_accel_remove_trigger (struct iio_dev *indio_dev); +int st_accel_allocate_ring (struct iio_dev *indio_dev); +void st_accel_deallocate_ring (struct iio_dev *indio_dev); +#else /* CONFIG_IIO_BUFFER */ +static inline int st_accel_probe_trigger (struct iio_dev *indio_dev) +{ +return 0 ; +} +static inline void st_accel_remove_trigger (struct iio_dev *indio_dev) +{ +return ; +} +static inline int st_accel_allocate_ring (struct iio_dev *indio_dev) +{ +return 0 ; +} +static inline void st_accel_deallocate_ring (struct iio_dev *indio_dev) +{ +return ; +} +#endif /* CONFIG_IIO_BUFFER */ + +#endif /* ST_ACCEL_H */ include/linux/platform_data/st_accel_pdata.h new file +27
-1@@ -0,0 +1,27 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + + +#ifndef ST_ACCEL_PDATA_H +#define ST_ACCEL_PDATA_H + + +/** +* struct st_accel_platform_data - ST accel device platform data +* @fullscale: Value of fullscale used for the sensor. +* @sampling_frequency: Value of sampling frequency used for the sensor. +*/ + +struct st_accel_platform_data { +int fullscale; +int sampling_frequency; +}; + +#endif /* ST_ACCEL_PDATA_H */ --1.7.0.4
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Jonathan,
Quoted 3 lines
For the non buffered case - reading is slow anyway so if anyone cares they will be doing buffered reads. For buffered reads this isn't going to change often (and there is a lot of cost associated with bringing the =
buffer
Quoted 2 lines
up and down anyway) so a little cost in disabling the channel is minor compared to the cost of hammering the bus unecessarily - particularly wit=
h
Quoted 4 lines
good old slow i2c.
So I'd be inclined to turn on only channels we care about. Do you have an estimate of how long it will take to turn one on for a single read?
I have checked what you tell me about power on/off of the axis. For the buffered case I have modify the driver to power on and off the single axis. What you think about my driver now? It is necessary other modify? Thanks
Denis
(If you want I send this patch by git because I don't know if this is the better way)
From b34e061a37655da5ab9bacde4d9a38bb9d3cf69f Mon Sep 17 00:00:00 2001
From: Denis Ciocca <[email protected]>
Date: Mon, 22 Oct 2012 11:17:27 +0200
Subject: [PATCH 1/2] iio:accel: Add STMicroelectronics accelerometers drive= r
This patch adds generic accelerometer driver for STMicroelectronics accelerometers, currently it supports:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D, LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330
--- drivers/iio/accel/Kconfig | 37 + drivers/iio/accel/Makefile | 6 + drivers/iio/accel/st_accel_buffer.c | 166 ++++ drivers/iio/accel/st_accel_core.c | 1342 ++++++++++++++++++++++++++ drivers/iio/accel/st_accel_i2c.c | 139 +++ drivers/iio/accel/st_accel_spi.c | 199 ++++ drivers/iio/accel/st_accel_trigger.c | 84 ++ include/linux/iio/accel/st_accel.h | 122 +++ include/linux/platform_data/st_accel_pdata.h | 27 + 9 files changed, 2122 insertions(+), 0 deletions(-)
create mode 100644 drivers/iio/accel/st_accel_buffer.c create mode 100644 drivers/iio/accel/st_accel_core.c create mode 100644 drivers/iio/accel/st_accel_i2c.c create mode 100644 drivers/iio/accel/st_accel_spi.c create mode 100644 drivers/iio/accel/st_accel_trigger.c create mode 100644 include/linux/iio/accel/st_accel.h create mode 100644 include/linux/platform_data/st_accel_pdata.h
drivers/iio/accel/Kconfig +5 -0
@@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D Say yes here to build support for the HID SENSOR accelerometers 3D. +config ST_ACCEL_3AXIS + tristate "STMicroelectronics accelerometers 3-Axis Driver" + depends on (I2C || SPI) && SYSFS + help + Say yes here to build support for STMicroelectronics acceleromete=
rs:
+ LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM3=
03D,
+ LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330.
+
+ This driver can also be built as a module. If so, the module
+ will be called st_accel.
+
+config ST_ACCEL_3AXIS_I2C
+ tristate "support I2C bus connection" + depends on ST_ACCEL_3AXIS && I2C + help
+ Say yes here to build I2C support for STMicroelectronics accelero=
meters.
+
+ To compile this driver as a module, choose M here: the
+ module will be called st_accel_i2c.
+
+config ST_ACCEL_3AXIS_SPI
+ tristate "support SPI bus connection" + depends on ST_ACCEL_3AXIS && SPI_MASTER + help
+ Say yes here to build SPI support for STMicroelectronics accelero=
meters.
+
+ To compile this driver as a module, choose M here: the
+ module will be called st_accel_spi.
+
+config ST_ACCEL_3AXIS_TRIGGERED_BUFFER
+ tristate "support triggered buffer" + depends on ST_ACCEL_3AXIS + select IIO_TRIGGERED_BUFFER + select IIO_BUFFER + help
+ Default trigger and buffer for STMicroelectronics accelerometers =
driver.
+
endmenu
drivers/iio/accel/Makefile +5 -0
@@ -3,3 +3,9 @@ # obj-$(CONFIG_HID_SENSOR_ACCEL_3D) +=3D hid-sensor-accel-3d.o + +st_accel-y :=3D st_accel_core.o +obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) +=3D st_accel_i2c.o +obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) +=3D st_accel_spi.o +obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) +=3D st_accel_trigger.o
st_accel_buffer.o
+obj-$(CONFIG_ST_ACCEL_3AXIS) +=3D st_accel.o
(quoted diff) +0 -0
b/drivers/iio/accel/st_accel_buffer.c
new file mode 100644
index 0000000..600ecc6
drivers/iio/accel/st_accel_buffer.c new file +43 -0
43 lines not on this page · raw message
);
+ break;
+ case 2:
+ if ((reg_addr[1] - reg_addr[0]) =3D=3D ST_ACCEL_BYTE_FOR_CH=
ANNEL) {
+ len =3D adata->read_multiple_byte(adata, reg_addr[0=
],
+ ST_ACCEL_BYTE_FOR_CHANNEL*n,
+ rx_array);
+ } else {
+ len =3D adata->read_multiple_byte(adata, reg_addr[0=
],
+ ST_ACCEL_BYTE_FOR_CHANNEL* + ST_ACCEL_NUMBER_DATA_CHANNELS, + rx_array); + rx_array[2] =3D rx_array[4]; + rx_array[3] =3D rx_array[5]; + len =3D ST_ACCEL_BYTE_FOR_CHANNEL*n;
+ }
+ break;
+ case 3:
+ len =3D adata->read_multiple_byte(adata, reg_addr[0],
+ ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHAN=
NELS,
+ rx_array);
+ break;
+ default:
+ break;
+ }
+
+ return len;
+}
+
+static int st_accel_get_buffer_element(struct iio_dev *indio_dev, u8 *buf) +{
+ int ret, i, scan_count;
+ u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL*ST_ACCEL_NUMBER_DATA_CHANNELS=
];
+ s16 *data =3D (s16 *)buf;
+
+ ret =3D st_accel_read_all(indio_dev, rx_array);
+ if (ret < 0)
+ return ret;
+
+ scan_count =3D bitmap_weight(indio_dev->active_scan_mask,
+ indio_dev->maskleng=
th);
+
+ for (i =3D 0; i < scan_count; i++)
+ data[i] =3D (s16)(((s16)(rx_array[2*i+1]) << 8) | rx_array[=
2*i]);
+
+ return i*sizeof(data[0]);
+}
+
+static irqreturn_t st_accel_trigger_handler(int irq, void *p) +{
+ struct iio_poll_func *pf =3D p; + struct iio_dev *indio_dev =3D pf->indio_dev; + int len =3D 0; + char *data;
+
+ data =3D kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (data =3D=3D NULL)
+ goto done;
+ if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklengt=
h))
+ len =3D st_accel_get_buffer_element(indio_dev, data);
+ else
+ goto done;
+ if (indio_dev->scan_timestamp)
+ *(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) =3D pf->time=
stamp;
+ iio_push_to_buffer(indio_dev->buffer, data);
+ kfree(data);
+
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+static int st_accel_buffer_postenable(struct iio_dev *indio_dev) +{
+ int err, i;
+ u8 active_bit =3D 0x00;
+
+ for (i =3D 0; i < ST_ACCEL_NUMBER_DATA_CHANNELS; i++)
+ if (test_bit(i, indio_dev->active_scan_mask))
+ active_bit |=3D (1 << i);
+
+ err =3D st_accel_set_axis_enable(indio_dev, active_bit);
+ if (err < 0)
+ goto st_accel_buffer_postenable_error;
+
+ err =3D iio_triggered_buffer_postenable(indio_dev);
+
+st_accel_buffer_postenable_error:
+ return err;
+}
+
+static int st_accel_buffer_predisable(struct iio_dev *indio_dev) +{
+ int err;
+
+ err =3D iio_triggered_buffer_predisable(indio_dev);
+ if (err < 0)
+ goto st_accel_buffer_predisable_error;
+
+ err =3D st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHA=
NNELS);
+
+st_accel_buffer_predisable_error:
+ return err;
+}
+
+static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops =3D {
+ .preenable =3D &iio_sw_buffer_preenable, + .postenable =3D &st_accel_buffer_postenable, + .predisable =3D &st_accel_buffer_predisable,
+};
+
+int st_accel_allocate_ring(struct iio_dev *indio_dev) +{
+ indio_dev->scan_timestamp =3D true;
+ return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_ti=
me,
+ &st_accel_trigger_handler, &st_accel_buffer_setup_o=
ps);
+}
+EXPORT_SYMBOL(st_accel_allocate_ring);
+
+void st_accel_deallocate_ring(struct iio_dev *indio_dev) +{
+ iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_deallocate_ring);
(quoted diff) +0 -0
b/drivers/iio/accel/st_accel_core.c
new file mode 100644
index 0000000..97ea1b7
drivers/iio/accel/st_accel_core.c new file +239 -0
239 lines not on this page · raw message
AX];
+};
+
+struct st_accel_bdu {
+ u8 addr;
+ u8 mask;
+};
+
+struct st_accel_interrupt_generator {
+ u8 en_addr;
+ u8 latch_mask_addr;
+ u8 en_mask;
+ u8 latching_mask;
+};
+
+struct st_accel_data_ready_irq {
+ u8 addr;
+ u8 mask;
+ struct st_accel_interrupt_generator ig1;+};
+
+#define ST_ACCEL_LSM_CHANNELS(index, mod, endian, bits, addr) \
+{ \
+ .type =3D IIO_ACCEL, \
+ .modified =3D 1, \
+ .info_mask =3D IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+ IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+ .scan_index =3D index, \
+ .channel2 =3D mod, \
+ .address =3D addr, \
+ .scan_type =3D { \
+ .sign =3D 's', \
+ .realbits =3D bits, \
+ .shift =3D 16 - bits, \
+ .storagebits =3D 16, \
+ .endianness =3D endian, \+ }, \
+}
+
+static const struct iio_chan_spec st_accel_12bit_channels[] =3D {
+ ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
+ ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR=
),
+ ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
+ ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR=
),
+ ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
+ ST_ACCEL_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR=
),
+ IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct iio_chan_spec st_accel_16bit_channels[] =3D {
+ ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_X, IIO_MOD_X, IIO_LE,
+ ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR=
),
+ ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Y, IIO_MOD_Y, IIO_LE,
+ ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR=
),
+ ST_ACCEL_LSM_CHANNELS(ST_ACCEL_SCAN_Z, IIO_MOD_Z, IIO_LE,
+ ST_ACCEL_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR=
),
+ IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static const struct st_accel_platform_data st_accel_default_pdata =3D {
+ .fullscale =3D ST_ACCEL_FS_AVL_2G,
+ .sampling_frequency =3D ST_ACCEL_ODR_AVL_100HZ,
+};
+
+/**
+ * struct st_accel_sensors - ST accel 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.
+ * @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.
+ *
+ */
+
+static const struct st_accel_sensors {
+ u8 wai;
+ struct iio_chan_spec *ch;
+ struct st_accel_odr odr;
+ struct st_accel_power pw;
+ struct st_accel_axis enable_axis;
+ struct st_accel_fullscale fs;
+ struct st_accel_bdu bdu;
+ struct st_accel_data_ready_irq drdy_irq;
+ bool multi_read_bit;
+} st_accel_sensors[] =3D {
+ {
+ .wai =3D ST_ACCEL_1_WAI_EXP,
+ .ch =3D (struct iio_chan_spec *)st_accel_12bit_channels,
+ .odr =3D {
+ .addr =3D ST_ACCEL_1_ODR_ADDR,
+ .mask =3D ST_ACCEL_1_ODR_MASK,
+ .num_bit =3D ST_ACCEL_1_ODR_N_BIT,
+ .odr_avl =3D {+ [0] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_1HZ,
+ .value =3D ST_ACCEL_1_ODR_AVL_1HZ_V=
AL,
+ },
+ [1] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_10HZ,
+ .value =3D ST_ACCEL_1_ODR_AVL_10HZ_=
VAL,
+ },
+ [2] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_25HZ,
+ .value =3D ST_ACCEL_1_ODR_AVL_25HZ_=
VAL,
+ },
+ [3] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_50HZ,
+ .value =3D ST_ACCEL_1_ODR_AVL_50HZ_=
VAL,
+ },
+ [4] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_100HZ,
+ .value =3D ST_ACCEL_1_ODR_AVL_100HZ=_VAL,
+ },
+ [5] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_200HZ,
+ .value =3D ST_ACCEL_1_ODR_AVL_200HZ=_VAL,
+ },
+ [6] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_400HZ,
+ .value =3D ST_ACCEL_1_ODR_AVL_400HZ=_VAL,
+ },
+ [7] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_1600HZ,
+ .value =3D ST_ACCEL_1_ODR_AVL_1600H=Z_VAL,
+ },
+ },
+ },
+ .pw =3D {
+ .addr =3D ST_ACCEL_1_ODR_ADDR, + .mask =3D ST_ACCEL_1_ODR_MASK, + .num_bit =3D ST_ACCEL_1_ODR_N_BIT, + .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+ },
+ .enable_axis =3D {
+ .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+ .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+ },
+ .fs =3D {
+ .addr =3D ST_ACCEL_1_FS_ADDR,
+ .mask =3D ST_ACCEL_1_FS_MASK,
+ .num_bit =3D ST_ACCEL_1_FS_N_BIT,
+ .fs_avl =3D {
+ [0] =3D {
+ .num =3D ST_ACCEL_FS_AVL_2G,
+ .value =3D ST_ACCEL_1_FS_AVL_2_VAL,
+ .gain =3D ST_ACCEL_1_FS_AVL_2_GAIN,
+ },
+ [1] =3D {
+ .num =3D ST_ACCEL_FS_AVL_4G,
+ .value =3D ST_ACCEL_1_FS_AVL_4_VAL,
+ .gain =3D ST_ACCEL_1_FS_AVL_4_GAIN,
+ },
+ [2] =3D {
+ .num =3D ST_ACCEL_FS_AVL_8G,
+ .value =3D ST_ACCEL_1_FS_AVL_8_VAL,
+ .gain =3D ST_ACCEL_1_FS_AVL_8_GAIN,+ },
+ [3] =3D {
+ .num =3D ST_ACCEL_FS_AVL_16G,
+ .value =3D ST_ACCEL_1_FS_AVL_16_VAL=
,
+ .gain =3D ST_ACCEL_1_FS_AVL_16_GAIN=
,
+ },
+ },
+ },
+ .bdu =3D {
+ .addr =3D ST_ACCEL_1_BDU_ADDR,
+ .mask =3D ST_ACCEL_1_BDU_MASK,
+ },
+ .drdy_irq =3D {
+ .addr =3D ST_ACCEL_1_DRDY_IRQ_ADDR,
+ .mask =3D ST_ACCEL_1_DRDY_IRQ_MASK,
+ },
+ .multi_read_bit =3D ST_ACCEL_1_MULTIREAD_BIT,
+ },
+ {
+ .wai =3D ST_ACCEL_2_WAI_EXP,
+ .ch =3D (struct iio_chan_spec *)st_accel_16bit_channels,
+ .odr =3D {
+ .addr =3D ST_ACCEL_2_ODR_ADDR,
+ .mask =3D ST_ACCEL_2_ODR_MASK,
+ .num_bit =3D ST_ACCEL_2_ODR_N_BIT,
+ .odr_avl =3D {+ [0] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_3HZ,
+ .value =3D ST_ACCEL_2_ODR_AVL_3HZ_V=
AL,
+ },
+ [1] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_6HZ,
+ .value =3D ST_ACCEL_2_ODR_AVL_6HZ_V=
AL,
+ },
+ [2] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_12HZ,
+ .value =3D ST_ACCEL_2_ODR_AVL_12HZ_=
VAL,
+ },
+ [3] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_25HZ,
+ .value =3D ST_ACCEL_2_ODR_AVL_25HZ_=
VAL,
+ },
+ [4] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_50HZ,
+ .value =3D ST_ACCEL_2_ODR_AVL_50HZ_=
VAL,
+ },
+ [5] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_100HZ,
+ .value =3D ST_ACCEL_2_ODR_AVL_100HZ=_VAL,
+ },
+ [6] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_200HZ,
+ .value =3D ST_ACCEL_2_ODR_AVL_200HZ=_VAL,
+ },
+ [7] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_400HZ,
+ .value =3D ST_ACCEL_2_ODR_AVL_400HZ=_VAL,
+ },
+ [8] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_800HZ,
+ .value =3D ST_ACCEL_2_ODR_AVL_800HZ=_VAL,
+ },
+ [9] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_1600HZ,
+ .value =3D ST_ACCEL_2_ODR_AVL_1600H=Z_VAL,
+ },
+ },
+ },
+ .pw =3D {
+ .addr =3D ST_ACCEL_2_ODR_ADDR, + .mask =3D ST_ACCEL_2_ODR_MASK, + .num_bit =3D ST_ACCEL_2_ODR_N_BIT, + .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+ },
+ .enable_axis =3D {
+ .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+ .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+ },
+ .fs =3D {
+ .addr =3D ST_ACCEL_2_FS_ADDR,
+ .mask =3D ST_ACCEL_2_FS_MASK,
+ .num_bit =3D ST_ACCEL_2_FS_N_BIT,
+ .fs_avl =3D {
+ [0] =3D {
+ .num =3D ST_ACCEL_FS_AVL_2G,
+ .value =3D ST_ACCEL_2_FS_AVL_2_VAL,
+ .gain =3D ST_ACCEL_2_FS_AVL_2_GAIN,
+ },
+ [1] =3D {
+ .num =3D ST_ACCEL_FS_AVL_4G,
+ .value =3D ST_ACCEL_2_FS_AVL_4_VAL,
+ .gain =3D ST_ACCEL_2_FS_AVL_4_GAIN,
+ },
+ [2] =3D {
+ .num =3D ST_ACCEL_FS_AVL_6G,
+ .value =3D ST_ACCEL_2_FS_AVL_6_VAL,
+ .gain =3D ST_ACCEL_2_FS_AVL_6_GAIN,
+ },
+ [3] =3D {
+ .num =3D ST_ACCEL_FS_AVL_8G,
+ .value =3D ST_ACCEL_2_FS_AVL_8_VAL,
+ .gain =3D ST_ACCEL_2_FS_AVL_8_GAIN,+ },
+ [4] =3D {
+ .num =3D ST_ACCEL_FS_AVL_16G,
+ .value =3D ST_ACCEL_2_FS_AVL_16_VAL=
,
+ .gain =3D ST_ACCEL_2_FS_AVL_16_GAIN=
,
+ },
+ },
+ },
+ .drdy_irq =3D {
+ .addr =3D ST_ACCEL_2_DRDY_IRQ_ADDR,
+ .mask =3D ST_ACCEL_2_DRDY_IRQ_MASK,
+ },
+ .bdu =3D {
+ .addr =3D ST_ACCEL_2_BDU_ADDR,
+ .mask =3D ST_ACCEL_2_BDU_MASK,
+ },
+ .multi_read_bit =3D ST_ACCEL_2_MULTIREAD_BIT,
+ },
+ {
+ .wai =3D ST_ACCEL_3_WAI_EXP,
+ .ch =3D (struct iio_chan_spec *)st_accel_12bit_channels,
+ .odr =3D {
+ .addr =3D ST_ACCEL_3_ODR_ADDR,
+ .mask =3D ST_ACCEL_3_ODR_MASK,
+ .num_bit =3D ST_ACCEL_3_ODR_N_BIT,
+ .odr_avl =3D {+ [0] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_50HZ,
+ .value =3D ST_ACCEL_3_ODR_AVL_50HZ_=
VAL,
+ },
+ [1] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_100HZ,
+ .value =3D ST_ACCEL_3_ODR_AVL_100HZ=_VAL,
+ },
+ [2] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_400HZ,
+ .value =3D ST_ACCEL_3_ODR_AVL_400HZ=_VAL,
+ },
+ [3] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_1000HZ,
+ .value =3D ST_ACCEL_3_ODR_AVL_1000H=Z_VAL,
+ },
+ },
+ },
+ .pw =3D {
+ .addr =3D ST_ACCEL_3_PW_ADDR, + .mask =3D ST_ACCEL_3_PW_MASK, + .num_bit =3D ST_ACCEL_3_PW_N_BIT, + .value_on =3D ST_ACCEL_3_PW_VALUE_ON, + .value_off =3D ST_ACCEL_3_PW_VALUE_OFF,
+ },
+ .enable_axis =3D {
+ .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+ .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+ },
+ .fs =3D {
+ .addr =3D ST_ACCEL_3_FS_ADDR,
+ .mask =3D ST_ACCEL_3_FS_MASK,
+ .num_bit =3D ST_ACCEL_3_FS_N_BIT,
+ .fs_avl =3D {
+ [0] =3D {
+ .num =3D ST_ACCEL_FS_AVL_2G,
+ .value =3D ST_ACCEL_3_FS_AVL_2_VAL,
+ .gain =3D ST_ACCEL_3_FS_AVL_2_GAIN,
+ },
+ [1] =3D {
+ .num =3D ST_ACCEL_FS_AVL_4G,
+ .value =3D ST_ACCEL_3_FS_AVL_4_VAL,
+ .gain =3D ST_ACCEL_3_FS_AVL_4_GAIN,
+ },
+ [2] =3D {
+ .num =3D ST_ACCEL_FS_AVL_8G,
+ .value =3D ST_ACCEL_3_FS_AVL_8_VAL,
+ .gain =3D ST_ACCEL_3_FS_AVL_8_GAIN,+ },
+ },
+ },
+ .bdu =3D {
+ .addr =3D ST_ACCEL_3_BDU_ADDR,
+ .mask =3D ST_ACCEL_3_BDU_MASK,
+ },
+ .drdy_irq =3D {
+ .addr =3D ST_ACCEL_3_DRDY_IRQ_ADDR,
+ .mask =3D ST_ACCEL_3_DRDY_IRQ_MASK,
+ },
+ .multi_read_bit =3D ST_ACCEL_3_MULTIREAD_BIT,
+ },
+ {
+ .wai =3D ST_ACCEL_4_WAI_EXP,
+ .ch =3D (struct iio_chan_spec *)st_accel_16bit_channels,
+ .odr =3D {
+ .addr =3D ST_ACCEL_4_ODR_ADDR,
+ .mask =3D ST_ACCEL_4_ODR_MASK,
+ .num_bit =3D ST_ACCEL_4_ODR_N_BIT,
+ .odr_avl =3D {+ [0] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_3HZ,
+ .value =3D ST_ACCEL_4_ODR_AVL_3HZ_V=
AL,
+ },
+ [1] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_6HZ,
+ .value =3D ST_ACCEL_4_ODR_AVL_6HZ_V=
AL,
+ },
+ [2] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_12HZ,
+ .value =3D ST_ACCEL_4_ODR_AVL_12HZ_=
VAL,
+ },
+ [3] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_25HZ,
+ .value =3D ST_ACCEL_4_ODR_AVL_25HZ_=
VAL,
+ },
+ [4] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_50HZ,
+ .value =3D ST_ACCEL_4_ODR_AVL_50HZ_=
VAL,
+ },
+ [5] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_100HZ,
+ .value =3D ST_ACCEL_4_ODR_AVL_100HZ=_VAL,
+ },
+ [6] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_200HZ,
+ .value =3D ST_ACCEL_4_ODR_AVL_200HZ=_VAL,
+ },
+ [7] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_400HZ,
+ .value =3D ST_ACCEL_4_ODR_AVL_400HZ=_VAL,
+ },
+ [8] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_800HZ,
+ .value =3D ST_ACCEL_4_ODR_AVL_800HZ=_VAL,
+ },
+ [9] =3D {
+ .hz =3D ST_ACCEL_ODR_AVL_1600HZ,
+ .value =3D ST_ACCEL_4_ODR_AVL_1600H=Z_VAL,
+ },
+ },
+ },
+ .pw =3D {
+ .addr =3D ST_ACCEL_4_ODR_ADDR, + .mask =3D ST_ACCEL_4_ODR_MASK, + .num_bit =3D ST_ACCEL_4_ODR_N_BIT, + .value_off =3D ST_ACCEL_DEFAULT_POWER_OFF_VALUE,
+ },
+ .enable_axis =3D {
+ .addr =3D ST_ACCEL_DEFAULT_AXIS_ADDR,
+ .mask =3D ST_ACCEL_DEFAULT_AXIS_MASK,
+ },
+ .fs =3D {
+ .addr =3D ST_ACCEL_4_FS_ADDR,
+ .mask =3D ST_ACCEL_4_FS_MASK,
+ .num_bit =3D ST_ACCEL_4_FS_N_BIT,
+ .fs_avl =3D {
+ [0] =3D {
+ .num =3D ST_ACCEL_FS_AVL_2G,
+ .value =3D ST_ACCEL_4_FS_AVL_2_VAL,
+ .gain =3D ST_ACCEL_4_FS_AVL_2_GAIN,
+ },
+ [1] =3D {
+ .num =3D ST_ACCEL_FS_AVL_4G,
+ .value =3D ST_ACCEL_4_FS_AVL_4_VAL,
+ .gain =3D ST_ACCEL_4_FS_AVL_4_GAIN,
+ },
+ [2] =3D {
+ .num =3D ST_ACCEL_FS_AVL_6G,
+ .value =3D ST_ACCEL_4_FS_AVL_6_VAL,
+ .gain =3D ST_ACCEL_4_FS_AVL_6_GAIN,
+ },
+ [3] =3D {
+ .num =3D ST_ACCEL_FS_AVL_8G,
+ .value =3D ST_ACCEL_4_FS_AVL_8_VAL,
+ .gain =3D ST_ACCEL_4_FS_AVL_8_GAIN,+ },
+ [4] =3D {
+ .num =3D ST_ACCEL_FS_AVL_16G,
+ .value =3D ST_ACCEL_4_FS_AVL_16_VAL=
,
+ .gain =3D ST_ACCEL_4_FS_AVL_16_GAIN=
,
+ },
+ },
+ },
+ .bdu =3D {
+ .addr =3D ST_ACCEL_4_BDU_ADDR,
+ .mask =3D ST_ACCEL_4_BDU_MASK,
+ },
+ .drdy_irq =3D {
+ .addr =3D ST_ACCEL_4_DRDY_IRQ_ADDR,
+ .mask =3D ST_ACCEL_4_DRDY_IRQ_MASK,
+ .ig1 =3D {
+ .en_addr =3D ST_ACCEL_4_IG1_EN_ADDR,
+ .en_mask =3D ST_ACCEL_4_IG1_EN_MASK,
+ },
+ },
+ .multi_read_bit =3D ST_ACCEL_4_MULTIREAD_BIT,
+ },+};
+
+static int st_accel_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 st_accel_data *adata =3D iio_priv(indio_dev);
+
+ pos =3D 8 - num_bit;
+ for (j =3D 128; j >=3D 0; j =3D j/2) {
+ if (mask / j > 0)
+ break;
+ else
+ pos--;
+ }
+
+ err =3D adata->read_byte(adata, reg_addr, &prev_data);
+ if (err < 0)
+ goto st_accel_write_data_with_mask_error;
+
+ new_data =3D ((prev_data & (~mask)) | ((data << pos) & mask));
+ err =3D adata->write_byte(adata, reg_addr, new_data);
+
+st_accel_write_data_with_mask_error:
+ return err;
+}
+
+static int st_accel_match_odr(const struct st_accel_sensors *sensor,
+ unsigned int odr, struct st_accel_odr_available *odr_out)
+{
+ int i, ret =3D -1;
+
+ for (i =3D 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
+ if (sensor->odr.odr_avl[i].hz =3D=3D odr) {
+ odr_out->hz =3D sensor->odr.odr_avl[i].hz; + odr_out->value =3D sensor->odr.odr_avl[i].value; + ret =3D 0; + break;
+ }
+ }
+
+ return ret;
+}
+
+static int st_accel_match_fs(const struct st_accel_sensors *sensor,
+ unsigned int fs, struct st_accel_fullscale_available *fs_ou=
t)
+{
+ int i, ret =3D -1;
+
+ for (i =3D 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+ if (sensor->fs.fs_avl[i].num =3D=3D fs) {
+ fs_out->num =3D sensor->fs.fs_avl[i].num; + fs_out->gain =3D sensor->fs.fs_avl[i].gain; + fs_out->value =3D sensor->fs.fs_avl[i].value; + ret =3D 0; + break;
+ }
+ }
+
+ return ret;
+}
+
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable) +{
+ int err;
+ struct st_accel_data *adata;
+
+ adata =3D iio_priv(indio_dev);
+ if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
+ err =3D st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].drdy_irq.ig1.en_addr=
,
+ st_accel_sensors[adata->index].drdy_irq.ig1.en_mask=
, 1,
+ (int)enable);
+ if (err < 0)
+ goto st_accel_set_dataready_irq_error;
+ }
+
+ if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > 0=
) {
+ err =3D st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr=
,
+ st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask, =
1,
+ (int)enable);
+ if (err < 0)
+ goto st_accel_set_dataready_irq_error;
+ }
+
+ err =3D st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].drdy_irq.addr,
+ st_accel_sensors[adata->index].drdy_irq.mask, 1, (int)enabl=
e);
+ if (err < 0)
+ goto st_accel_set_dataready_irq_error;
+
+st_accel_set_dataready_irq_error:
+ return err;
+}
+EXPORT_SYMBOL(st_accel_set_dataready_irq);
+
+static int st_accel_set_bdu(struct iio_dev *indio_dev,
+ const struct st_accel_bdu *bdu, u8 value)
+{
+ return st_accel_write_data_with_mask(indio_dev, bdu->addr, bdu->mas=
k,
+ 1, value);
+}
+
+static int st_accel_set_odr(struct iio_dev *indio_dev,
+ struct st_accel_odr_available *odr_availabl=
e)
+{
+ int err;
+ struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+ if ((st_accel_sensors[adata->index].odr.addr =3D=3D
+ st_accel_sensors[adata->index].pw.addr) &&
+ (st_accel_sensors[adata->index].odr.mask =3D=3D
+ st_accel_sensors[adata->index].pw.mask)) {
+ if (adata->enabled =3D=3D (bool)ST_ACCEL_ON) {
+ err =3D st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].odr.addr, + st_accel_sensors[adata->index].odr.mask, + st_accel_sensors[adata->index].odr.num_bit, + odr_available->value);
+ if (err < 0)
+ goto sc_accel_set_odr_error;
+ } else {
+ adata->odr =3D odr_available->hz;
+ err =3D 0;
+ }
+ } else {
+ err =3D st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].odr.addr, + st_accel_sensors[adata->index].odr.mask, + st_accel_sensors[adata->index].odr.num_bit, + odr_available->value);
+ if (err < 0)
+ goto sc_accel_set_odr_error;
+ }
+
+sc_accel_set_odr_error:
+ return err;
+}
+
+int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable) +{
+ int err;
+ struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+ err =3D st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].enable_axis.addr, + st_accel_sensors[adata->index].enable_axis.mask, + ST_ACCEL_DEFAULT_AXIS_N_BIT, axis_enable);
+
+ return err;
+}
+
+static int st_accel_set_enable(struct iio_dev *indio_dev, int enable) +{
+ int err;
+ bool found;
+ u8 tmp_value;
+ struct st_accel_odr_available odr_out;
+ struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+ switch (enable) {
+ case ST_ACCEL_ON:
+ found =3D false;
+ tmp_value =3D st_accel_sensors[adata->index].pw.value_on;
+ if ((st_accel_sensors[adata->index].odr.addr =3D=3D+ st_accel_sensors[adata->index].pw.addr) &&
+ (st_accel_sensors[adata->index].odr.mask =3D=3D
+ st_accel_sensors[adata->index].pw.mask)) {
+ err =3D st_accel_match_odr(
+ &st_accel_sensors[adata->index], adata->odr=
,
+ &odr_out);
+ if (err < 0)
+ goto set_enable_error;
+ tmp_value =3D odr_out.value;
+ found =3D true;
+ }
+ err =3D st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].pw.addr, + st_accel_sensors[adata->index].pw.mask, + st_accel_sensors[adata->index].pw.num_bit, + tmp_value);
+ if (err < 0)
+ goto set_enable_error;
+ adata->enabled =3D (bool)ST_ACCEL_ON;
+ if (found)
+ adata->odr =3D odr_out.hz;
+ break;
+ case ST_ACCEL_OFF:
+ err =3D st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].pw.addr, + st_accel_sensors[adata->index].pw.mask, + st_accel_sensors[adata->index].pw.num_bit, + st_accel_sensors[adata->index].pw.value_off=
);
+ if (err < 0)
+ goto set_enable_error;
+ adata->enabled =3D (bool)ST_ACCEL_OFF;
+ break;
+ default:
+ err =3D -EINVAL;
+ goto set_enable_error;
+ }
+
+set_enable_error:
+ return err;
+}
+
+static int st_accel_set_fullscale(struct iio_dev *indio_dev,
+ struct st_accel_fullscale_available *fs_avl=
)
+{
+ int err;
+ struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+ err =3D st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].fs.addr, + st_accel_sensors[adata->index].fs.mask, + st_accel_sensors[adata->index].fs.num_bit, + fs_avl->value); + if (err < 0) + goto st_accel_set_fullscale_error; + + adata->fullscale =3D fs_avl->num; + adata->gain =3D fs_avl->gain; + return err;
+
+st_accel_set_fullscale_error:
+ dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+ return err;
+}
+
+static int st_accel_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *ch, int *val,
+ int *val2, long mas=
k)
+{
+ int err; + u8 outdata[ST_ACCEL_BYTE_FOR_CHANNEL]; + struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ if (indio_dev->currentmode =3D=3D INDIO_BUFFER_TRIGGERED) {
+ err =3D -EBUSY;
+ goto read_error;
+ } else {
+ if (!adata->enabled) {
+ err =3D -EIO;
+ goto read_error;
+ } else {
+ err =3D adata->read_multiple_byte(adata,
+ ch->address, ST_ACCEL_BYTE_FOR_CHAN=
NEL,
+ outdata);
+ if (err < 0)
+ goto read_error;
+
+ *val =3D ((s16)(((s16)(outdata[1]) << 8)
+ | outdata[0])) >> ch->scan_type.shi=
ft;
+ }
+ } + mutex_unlock(&indio_dev->mlock); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val =3D 0; + *val2 =3D UG_TO_MS2(adata->gain); + return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+
+read_error:
+ mutex_unlock(&indio_dev->mlock);
+ return err;
+}
+
+static int st_accel_check_device_list(struct iio_dev *indio_dev, u8 wai) +{
+ int i; + bool found; + struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+ found =3D false;
+ for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors); i++) {
+ if (st_accel_sensors[i].wai =3D=3D wai) {
+ found =3D true;
+ break;
+ }
+ }
+ if (!found)
+ goto check_device_error;
+
+ adata->index =3D i;
+
+ return i;
+
+check_device_error:
+ dev_err(&indio_dev->dev, "device not supported -> wai (0x%x).\n", w=
ai);
+ return -ENODEV;
+}
+
+static int st_accel_get_wai_device(struct iio_dev *indio_dev, u8 reg_addr,
+ u8 *value)
+{
+ int ret;
+ struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+ ret =3D adata->read_byte(adata, reg_addr, value);
+ if (ret < 0)
+ goto read_byte_wai_error;
+
+ return 0;
+
+read_byte_wai_error:
+ dev_err(&indio_dev->dev,
+ "failed to read WhoAmI (register 0x%x).\n", reg_add=
r);
+ return -EIO;
+}
+
+static ssize_t st_accel_sysfs_set_sampling_frequency(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size=
)
+{
+ int err; + unsigned int freq; + struct st_accel_odr_available odr_out; + struct iio_dev *indio_dev =3D dev_get_drvdata(dev); + struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+ err =3D kstrtoint(buf, 10, &freq);
+ if (err < 0)
+ goto conversion_error;
+
+ mutex_lock(&indio_dev->mlock);
+ err =3D st_accel_match_odr(&st_accel_sensors[adata->index],
+ freq, &odr_=
out);
+ if (err < 0)
+ goto st_accel_sysfs_set_sampling_frequency_error;
+
+ err =3D st_accel_set_odr(indio_dev, &odr_out);
+ if (err < 0) {
+ dev_err(&indio_dev->dev,
+ "failed to set sampling frequency to %d.\n", freq);
+ goto st_accel_sysfs_set_sampling_frequency_error;
+ }
+ adata->odr =3D odr_out.hz;
+
+st_accel_sysfs_set_sampling_frequency_error:
+ mutex_unlock(&indio_dev->mlock);
+conversion_error:
+ return size;
+}
+
+static ssize_t st_accel_sysfs_get_sampling_frequency(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+ struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", adata->odr);
+}
+
+static ssize_t st_accel_sysfs_set_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size=
)
+{
+ int err; + bool en; + struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+
+ err =3D strtobool(buf, &en);
+ if (err < 0)
+ goto set_enable_error;
+
+ mutex_lock(&indio_dev->mlock); + err =3D st_accel_set_enable(indio_dev, (int)en); + if (err < 0)
+ dev_err(&indio_dev->dev,
+ "failed to set enable to %d.\n", (int)en);
+ mutex_unlock(&indio_dev->mlock);
+
+set_enable_error:
+ return size;
+}
+
+static ssize_t st_accel_sysfs_get_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+ struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", (int)adata->enabled);
+}
+
+static ssize_t st_accel_sysfs_get_fullscale(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+ struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", adata->fullscale);
+}
+
+static ssize_t st_accel_sysfs_set_fullscale(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size=
)
+{
+ int err; + unsigned int fs; + struct st_accel_fullscale_available fs_out; + struct iio_dev *indio_dev =3D dev_get_drvdata(dev); + struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+ err =3D kstrtoint(buf, 10, &fs);
+ if (err < 0)
+ goto conversion_error;
+
+ mutex_lock(&indio_dev->mlock);
+ err =3D st_accel_match_fs(&st_accel_sensors[adata->index], fs, &fs_=
out);
+ if (err < 0)
+ goto match_fullscale_error;
+
+ err =3D st_accel_set_fullscale(indio_dev, &fs_out);
+ if (err < 0) {
+ dev_err(&indio_dev->dev,
+ "failed to set new fullscale. (errn %d).\n", err);
+ }
+
+match_fullscale_error:
+ mutex_unlock(&indio_dev->mlock);
+conversion_error:
+ return size;
+}
+
+static ssize_t st_accel_sysfs_fullscale_available(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i, len =3D 0; + struct iio_dev *indio_dev =3D dev_get_drvdata(dev); + struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].fs.fs_a=
vl);
+ i++=
) {
+ if (st_accel_sensors[adata->index].fs.fs_avl[i].num =3D=3D =
0)
+ break;
+
+ len +=3D sprintf(buf+len, "%d ",
+ st_accel_sensors[adata->index].fs.fs_avl[i].num);
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ len--; + len +=3D sprintf(buf+len, "\n"); + return len;
+}
+
+static ssize_t st_accel_sysfs_sampling_frequency_available(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i, len =3D 0; + struct iio_dev *indio_dev =3D dev_get_drvdata(dev); + struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ for (i =3D 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].odr.odr=_avl);
+ i++=
) {
+ if (st_accel_sensors[adata->index].odr.odr_avl[i].hz =3D=3D=
0)
+ break;
+
+ len +=3D sprintf(buf+len, "%d ",
+ st_accel_sensors[adata->index].odr.odr_avl[i].hz);
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ len--; + len +=3D sprintf(buf+len, "\n"); + return len;
+}
+
+/**
+ * IIO_DEVICE_ATTR - sampling_frequency_available
+ * @read: show all frequency available of the sensor.
+ *
+ */
+
+static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
+ st_accel_sysfs_sampling_frequency_available, NULL , 0);
+
+/**
+ * IIO_DEVICE_ATTR - fullscale_available
+ * @read: show all fullscale available of the sensor.
+ *
+ */
+
+static IIO_DEVICE_ATTR(fullscale_available, S_IRUGO,
+ st_accel_sysfs_fullscale_available, NULL , 0);
+
+/**
+ * IIO_DEVICE_ATTR - fullscale
+ * @read: show the current fullscale of the sensor.
+ * @write: store the current fullscale of the sensor.
+ *
+ */
+
+static IIO_DEVICE_ATTR(fullscale, S_IWUSR | S_IRUGO,
+ st_accel_sysfs_get_fullscale, st_accel_sysfs_set_fullscale =
, 0);
+
+/**
+ * IIO_DEVICE_ATTR - enable
+ * @read: show the current status of the sensor.
+ * @write: power on/off the sensor.
+ *
+ */
+
+static IIO_DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, st_accel_sysfs_get_enable,
+ st_accel_sysfs_set_enable , 0);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ st_accel_sysfs_get_sampling_frequency,
+ st_accel_sysfs_set_sampling_frequen=
cy);
+
+static struct attribute *st_accel_attributes[] =3D {
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_fullscale_available.dev_attr.attr, + &iio_dev_attr_fullscale.dev_attr.attr, + &iio_dev_attr_enable.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, + NULL,
+};
+
+static const struct attribute_group st_accel_attribute_group =3D {
+ .attrs =3D st_accel_attributes,
+};
+
+static const struct iio_info acc_info =3D {
+ .driver_module =3D THIS_MODULE, + .attrs =3D &st_accel_attribute_group, + .read_raw =3D &st_accel_read_raw,
+};
+
+static int st_accel_init_sensor(struct iio_dev *indio_dev) +{
+ int err; + struct st_accel_odr_available odr_out; + struct st_accel_fullscale_available fs_out; + struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+ err =3D st_accel_set_enable(indio_dev, ST_ACCEL_OFF);
+ if (err < 0)
+ goto init_error;
+
+ err =3D st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHA=
NNELS);
+ if (err < 0)
+ goto init_error;
+
+ st_accel_match_fs(&st_accel_sensors[adata->index],
+ adata->fullscale, &fs_out);
+ err =3D st_accel_set_fullscale(indio_dev, &fs_out);
+ if (err < 0)
+ goto init_error;
+
+ st_accel_match_odr(&st_accel_sensors[adata->index],
+ adata->odr, &odr_ou=
t);
+ err =3D st_accel_set_odr(indio_dev, &odr_out);
+ if (err < 0)
+ goto init_error;
+
+ err =3D st_accel_set_bdu(indio_dev,
+ &st_accel_sensors[adata->index].bdu, (u8)ST_ACCEL_O=
N);
+ if (err < 0)
+ goto init_error;
+
+ return 0;
+
+init_error:
+ dev_err(&indio_dev->dev, "failed to init sensor data.");
+ return -EIO;
+}
+
+int st_accel_iio_probe(struct iio_dev *indio_dev) +{
+ int err; + u8 wai; + struct st_accel_data *adata =3D iio_priv(indio_dev); + struct st_accel_platform_data *pdata; + + mutex_init(&adata->slock); + indio_dev->modes =3D INDIO_DIRECT_MODE; + indio_dev->info =3D &acc_info;
+
+ err =3D st_accel_get_wai_device(indio_dev,
+ ST_ACCEL_DEFAULT_WAI_ADDRESS, &wai)=
;
+ if (err < 0)
+ goto st_accel_iio_probe_error;
+
+ err =3D st_accel_check_device_list(indio_dev, wai);
+ if (err < 0)
+ goto st_accel_iio_probe_error;
+
+ adata->multiread_bit =3D st_accel_sensors[adata->index].multi_read_=
bit;
+ indio_dev->channels =3D st_accel_sensors[adata->index].ch;
+ indio_dev->num_channels =3D ST_ACCEL_NUMBER_ALL_CHANNELS;
+ pdata =3D adata->dev->platform_data;
+ if (pdata =3D=3D NULL) {+ adata->fullscale =3D st_accel_default_pdata.fullscale;
+ adata->odr =3D st_accel_default_pdata.sampling_frequency;
+ } else {
+ adata->fullscale =3D pdata->fullscale;
+ adata->odr =3D pdata->sampling_frequency;
+ }
+
+ err =3D st_accel_init_sensor(indio_dev);
+ if (err < 0)
+ goto st_accel_iio_probe_error;
+
+ err =3D st_accel_allocate_ring(indio_dev);
+ if (err < 0)
+ goto st_accel_iio_probe_error;
+
+ if (*adata->irq_data_ready > 0) {
+ err =3D st_accel_probe_trigger(indio_dev);
+ if (err < 0)
+ goto acc_probe_trigger_error;
+ }
+
+ err =3D iio_device_register(indio_dev);
+ if (err)
+ goto iio_device_register_error;
+
+ return err;
+
+iio_device_register_error:
+ st_accel_remove_trigger(indio_dev);
+acc_probe_trigger_error:
+ st_accel_deallocate_ring(indio_dev);
+st_accel_iio_probe_error:
+ return err;
+}
+EXPORT_SYMBOL(st_accel_iio_probe);
+
+void st_accel_iio_remove(struct iio_dev *indio_dev) +{
+ iio_device_unregister(indio_dev); + st_accel_remove_trigger(indio_dev); + st_accel_deallocate_ring(indio_dev); + iio_device_free(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_iio_remove);
+
+MODULE_AUTHOR("Denis Ciocca <[email protected]>"); +MODULE_DESCRIPTION("STMicroelectronics accelerometers driver"); +MODULE_LICENSE("GPL v2");
(quoted diff) +0 -0
b/drivers/iio/accel/st_accel_i2c.c
new file mode 100644
index 0000000..7cfeb4c
drivers/iio/accel/st_accel_i2c.c new file +28 -0
@@ -0,0 +1,139 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* 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/trigger.h> + +#include <linux/iio/accel/st_accel.h> + + +#define ST_ACCEL_I2C_MULTIREAD 0x80 + +static int st_accel_i2c_read_byte(struct st_accel_data *adata, + u8 reg_addr, u8 *res_byte) +{ + int err; + + err =3D i2c_smbus_read_byte_data(to_i2c_client(adata->dev), reg_add=
r);
+ if (err < 0)
+ goto st_accel_i2c_read_byte_error;
+ *res_byte =3D err & 0xff;
+ return err;
+
+st_accel_i2c_read_byte_error:
+ return -EIO;
+}
+
+static int st_accel_i2c_read_multiple_byte(struct st_accel_data *adata,
+ u8 reg_addr, int len, u8 *d=
ata)
+{
+ int err;
+
+ if (adata->multiread_bit =3D=3D true)
+ reg_addr |=3D ST_ACCEL_I2C_MULTIREAD;
+
+ err =3D i2c_smbus_read_i2c_block_data(to_i2c_client(adata->dev),
+ reg_addr, len, data=
);
+ if (err < 0)
+ goto st_accel_i2c_read_multiple_byte_error;
+
+ return err;
+
+st_accel_i2c_read_multiple_byte_error:
+ return -EIO;
+}
+
+static int st_accel_i2c_write_byte(struct st_accel_data *adata,
+ u8 reg_addr, u8 dat=
a)
+{
+ return i2c_smbus_write_byte_data(to_i2c_client(adata->dev),
+ reg_addr, d=
ata);
+}
+
+static int __devinit st_accel_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *indio_dev; + struct st_accel_data *adata; + int err;
+
+ indio_dev =3D iio_device_alloc(sizeof(*adata));
+ if (indio_dev =3D=3D NULL) {
+ err =3D -ENOMEM;
+ goto iio_device_alloc_error;
+ }
+
+ adata =3D iio_priv(indio_dev); + adata->dev =3D &client->dev; + i2c_set_clientdata(client, indio_dev);
+
+ indio_dev->dev.parent =3D &client->dev;
+ indio_dev->name =3D client->name;
+
+ adata->read_byte =3D st_accel_i2c_read_byte; + adata->write_byte =3D st_accel_i2c_write_byte; + adata->read_multiple_byte =3D st_accel_i2c_read_multiple_byte; + adata->irq_data_ready =3D &client->irq;
+
+ err =3D st_accel_iio_probe(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 st_accel_i2c_remove(struct i2c_client *client) +{
+ struct iio_dev *indio_dev =3D i2c_get_clientdata(client);
+
+ st_accel_iio_remove(indio_dev);
+ return 0;
+}
+
+static const struct i2c_device_id st_accel_id_table[] =3D {
+ { LSM303DLH_ACCEL_DEV_NAME },
+ { LSM303DLHC_ACCEL_DEV_NAME },
+ { LIS3DH_ACCEL_DEV_NAME },
+ { LSM330D_ACCEL_DEV_NAME },
+ { LSM330DL_ACCEL_DEV_NAME },
+ { LSM330DLC_ACCEL_DEV_NAME },
+ { LSM303D_ACCEL_DEV_NAME },
+ { LSM9DS0_ACCEL_DEV_NAME },
+ { LIS331DLH_ACCEL_DEV_NAME },
+ { LSM303DL_ACCEL_DEV_NAME },
+ { LSM303DLM_ACCEL_DEV_NAME },
+ { LSM330_ACCEL_DEV_NAME },
+ {},+};
+MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
+
+static struct i2c_driver st_accel_driver =3D {
+ .driver =3D {
+ .owner =3D THIS_MODULE,
+ .name =3D "st-accel-i2c",
+ }, + .probe =3D st_accel_i2c_probe, + .remove =3D __devexit_p(st_accel_i2c_remove), + .id_table =3D st_accel_id_table,
+};
+module_i2c_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <[email protected]>"); +MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver"); +MODULE_LICENSE("GPL v2");
(quoted diff) +0 -0
b/drivers/iio/accel/st_accel_spi.c
new file mode 100644
index 0000000..40279bd
drivers/iio/accel/st_accel_spi.c new file +61 -0
61 lines not on this page · raw message
ata)
+{
+ struct spi_message msg;
+ int err;
+ u8 tx;
+
+ struct spi_transfer xfers[] =3D {
+ {
+ .tx_buf =3D &tx,
+ .bits_per_word =3D 8,
+ .len =3D 1,
+ },
+ {
+ .rx_buf =3D data,
+ .bits_per_word =3D 8,
+ .len =3D len,+ }
+ };
+
+ mutex_lock(&adata->slock);
+ if (adata->multiread_bit =3D=3D true)
+ tx =3D reg_addr | ACC_SPI_MULTIREAD;
+ else
+ tx =3D reg_addr | ACC_SPI_READ;
+
+ spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + err =3D spi_sync(to_spi_device(adata->dev), &msg); + mutex_unlock(&adata->slock); + if (err)
+ goto acc_spi_read_multiple_byte_error;
+ return len;
+
+acc_spi_read_multiple_byte_error:
+ return -EIO;
+}
+
+static int st_accel_spi_write_byte(struct st_accel_data *adata,
+ u8 reg_addr, u8 dat=
a)
+{
+ struct spi_message msg;
+ int err;
+ u8 tx[2];
+
+ struct spi_transfer xfers[] =3D {
+ {
+ .tx_buf =3D tx,
+ .bits_per_word =3D 8,
+ .len =3D 2,
+ }
+ };
+
+ mutex_lock(&adata->slock);
+ tx[0] =3D reg_addr;
+ tx[1] =3D data;
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfers[0], &msg);
+ err =3D spi_sync(to_spi_device(adata->dev), &msg);
+ mutex_unlock(&adata->slock);+
+ return err;
+}
+
+static int __devinit st_accel_spi_probe(struct spi_device *client) +{
+ struct iio_dev *indio_dev; + struct st_accel_data *adata; + int err;
+
+ indio_dev =3D iio_device_alloc(sizeof(*adata));
+ if (indio_dev =3D=3D NULL) {
+ err =3D -ENOMEM;
+ goto iio_device_alloc_error;
+ }
+
+ adata =3D iio_priv(indio_dev); + adata->dev =3D &client->dev; + spi_set_drvdata(client, indio_dev);
+
+ indio_dev->dev.parent =3D &client->dev;
+ indio_dev->name =3D client->modalias;
+
+ adata->read_byte =3D st_accel_spi_read_byte; + adata->write_byte =3D st_accel_spi_write_byte; + adata->read_multiple_byte =3D st_accel_spi_read_multiple_byte; + adata->irq_data_ready =3D &client->irq;
+
+ err =3D st_accel_iio_probe(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 st_accel_spi_remove(struct spi_device *spi) +{
+ struct iio_dev *indio_dev =3D spi_get_drvdata(spi);
+
+ st_accel_iio_remove(indio_dev);
+ return 0;
+}
+
+static const struct spi_device_id st_accel_id_table[] =3D {
+ { LSM303DLH_ACCEL_DEV_NAME },
+ { LSM303DLHC_ACCEL_DEV_NAME },
+ { LIS3DH_ACCEL_DEV_NAME },
+ { LSM330D_ACCEL_DEV_NAME },
+ { LSM330DL_ACCEL_DEV_NAME },
+ { LSM330DLC_ACCEL_DEV_NAME },
+ { LSM303D_ACCEL_DEV_NAME },
+ { LSM9DS0_ACCEL_DEV_NAME },
+ { LIS331DLH_ACCEL_DEV_NAME },
+ { LSM303DL_ACCEL_DEV_NAME },
+ { LSM303DLM_ACCEL_DEV_NAME },
+ { LSM330_ACCEL_DEV_NAME },
+ {},+};
+MODULE_DEVICE_TABLE(spi, st_accel_id_table);
+
+static struct spi_driver st_accel_driver =3D {
+ .driver =3D {
+ .owner =3D THIS_MODULE,
+ .name =3D "st-accel-spi",
+ }, + .probe =3D st_accel_spi_probe, + .remove =3D __devexit_p(st_accel_spi_remove), + .id_table =3D st_accel_id_table,
+};
+module_spi_driver(st_accel_driver);
+
+MODULE_AUTHOR("Denis Ciocca <[email protected]>"); +MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver"); +MODULE_LICENSE("GPL v2");
(quoted diff) +0 -0
b/drivers/iio/accel/st_accel_trigger.c
new file mode 100644
index 0000000..7b57b7f
drivers/iio/accel/st_accel_trigger.c new file +22 -0
@@ -0,0 +1,84 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> + +#include <linux/iio/accel/st_accel.h> + + +static int st_accel_trig_acc_set_state(struct iio_trigger *trig, bool
state)
+{
+ struct iio_dev *indio_dev =3D trig->private_data;
+ return st_accel_set_dataready_irq(indio_dev, state);
+}
+
+static const struct iio_trigger_ops st_accel_trigger_ops =3D {
+ .owner =3D THIS_MODULE,
+ .set_trigger_state =3D &st_accel_trig_acc_set_state,
+};
+
+int st_accel_probe_trigger(struct iio_dev *indio_dev) +{
+ int err;
+ struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+ adata->trig =3D iio_trigger_alloc("%s-trigger", indio_dev->name);
+ if (adata->trig =3D=3D NULL) {
+ err =3D -ENOMEM;
+ dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n=
");
+ goto iio_trigger_alloc_error;
+ }
+
+ err =3D request_threaded_irq(*adata->irq_data_ready,
+ iio_trigger_generic_data_rdy_poll, + NULL, + IRQF_TRIGGER_RISING, + adata->trig->name, + adata->trig); + if (err) + goto request_irq_error; + + adata->trig->private_data =3D indio_dev; + adata->trig->ops =3D &st_accel_trigger_ops; + adata->trig->dev.parent =3D adata->dev;
+
+ err =3D iio_trigger_register(adata->trig);
+ if (err < 0) {
+ dev_err(&indio_dev->dev, "failed to register iio trigger.\n=
");
+ goto iio_trigger_register_error;
+ }
+ indio_dev->trig =3D adata->trig;
+
+ 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;
+}
+EXPORT_SYMBOL(st_accel_probe_trigger);
+
+void st_accel_remove_trigger(struct iio_dev *indio_dev) +{
+ struct st_accel_data *adata =3D iio_priv(indio_dev);
+
+ iio_trigger_unregister(adata->trig); + free_irq(*adata->irq_data_ready, adata->trig); + iio_trigger_free(adata->trig);
+}
+EXPORT_SYMBOL(st_accel_remove_trigger);
(quoted diff) +0 -0
b/include/linux/iio/accel/st_accel.h
new file mode 100644
index 0000000..557d8cf
include/linux/iio/accel/st_accel.h new file +83 -0
83 lines not on this page · raw message
e);
+ int (*write_byte) (struct st_accel_data *adata, u8 reg_addr, u8 dat=
a);
+ int (*read_multiple_byte) (struct st_accel_data *adata, u8 reg_addr=
,
+ int len, u8 *data);
+
+ struct iio_trigger *trig; + int *irq_data_ready; + struct mutex slock;
+};
+
+int st_accel_iio_probe(struct iio_dev *indio_dev);
+void st_accel_iio_remove(struct iio_dev *indio_dev);
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable); +int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable); +
+#ifdef CONFIG_IIO_BUFFER
+int st_accel_probe_trigger(struct iio_dev *indio_dev);
+void st_accel_remove_trigger(struct iio_dev *indio_dev); +int st_accel_allocate_ring(struct iio_dev *indio_dev);
+void st_accel_deallocate_ring(struct iio_dev *indio_dev); +#else /* CONFIG_IIO_BUFFER */
+static inline int st_accel_probe_trigger(struct iio_dev *indio_dev) +{
+ return 0;
+}
+static inline void st_accel_remove_trigger(struct iio_dev *indio_dev) +{
+ return;
+}
+static inline int st_accel_allocate_ring(struct iio_dev *indio_dev) +{
+ return 0;
+}
+static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev) +{
+ return;
+}
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* ST_ACCEL_H */
(quoted diff) +0 -0
b/include/linux/platform_data/st_accel_pdata.h
new file mode 100644
index 0000000..416489b
include/linux/platform_data/st_accel_pdata.h new file +27 -0
@@ -0,0 +1,27 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + + +#ifndef ST_ACCEL_PDATA_H +#define ST_ACCEL_PDATA_H + + +/** +* struct st_accel_platform_data - ST accel device platform data +* @fullscale: Value of fullscale used for the sensor. +* @sampling_frequency: Value of sampling frequency used for the sensor. +*/ + +struct st_accel_platform_data { +int fullscale; +int sampling_frequency; +}; + +#endif /* ST_ACCEL_PDATA_H */
--
1.7.0.4
Quoted 10 lines · Denis CIOCCA
Hi Jonathan,
Quoted 7 lines
For the non buffered case - reading is slow anyway so if anyone cares they will be doing buffered reads. For buffered reads this isn't going to change often (and there is a lot of cost associated with bringing the buffer up and down anyway) so a little cost in disabling the channel is minor compared to the cost of hammering the bus unecessarily - particularly with good old slow i2c.
Quoted 6 lines
Quoted 2 lines
So I'd be inclined to turn on only channels we care about. Do you have an estimate of how long it will take to turn one on for a single read?
I have checked what you tell me about power on/off of the axis. For the buffered case I have modify the driver to power on and off the single axis. What you think about my driver now? It is necessary other modify?
We'll need at least another revision, since you did not address all comments from my last review. But I guess 2-3 more revision are more realistic.
Quoted 7 lines
Thanks
Denis
(If you want I send this patch by git because I don't know if this is the better way)
For the final version it's better to use git send-email. Your mailclient for example seems to replace tabs with spaces and also insert a extra newline occasionally. This is not nice, but, well, does not really impact review.
Quoted 178 lines
From
b34e061a37655da5ab9bacde4d9a38bb9d3cf69fMon Sep 17 00:00:00 2001
From: Denis Ciocca <[email protected]>
Date: Mon, 22 Oct 2012 11:17:27 +0200
Subject: [PATCH 1/2] iio:accel: Add STMicroelectronics accelerometers driverThis patch adds generic accelerometer driver for STMicroelectronics accelerometers, currently it supports:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D, LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330Signed-off-by Denis Ciocca <[email protected]>--- drivers/iio/accel/Kconfig | 37 + drivers/iio/accel/Makefile | 6 + drivers/iio/accel/st_accel_buffer.c | 166 ++++ drivers/iio/accel/st_accel_core.c | 1342 ++++++++++++++++++++++++++ drivers/iio/accel/st_accel_i2c.c | 139 +++ drivers/iio/accel/st_accel_spi.c | 199 ++++ drivers/iio/accel/st_accel_trigger.c | 84 ++ include/linux/iio/accel/st_accel.h | 122 +++ include/linux/platform_data/st_accel_pdata.h | 27 + 9 files changed, 2122 insertions(+), 0 deletions(-)create mode 100644
drivers/iio/accel/st_accel_buffer.ccreate mode 100644drivers/iio/accel/st_accel_core.ccreate mode 100644drivers/iio/accel/st_accel_i2c.ccreate mode 100644drivers/iio/accel/st_accel_spi.ccreate mode 100644drivers/iio/accel/st_accel_trigger.ccreate mode 100644include/linux/iio/accel/st_accel.hcreate mode 100644include/linux/platform_data/st_accel_pdata.hdrivers/iio/accel/Kconfig +37
-0@@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D Say yes here to build support for the HID SENSOR accelerometers 3D. +config ST_ACCEL_3AXIS + tristate "STMicroelectronics accelerometers 3-Axis Driver" + depends on (I2C || SPI) && SYSFS + help + Say yes here to build support for STMicroelectronics accelerometers: + LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D, + LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330. + + This driver can also be built as a module. If so, the module + will be called st_accel. + +config ST_ACCEL_3AXIS_I2C + tristate "support I2C bus connection" + depends on ST_ACCEL_3AXIS && I2C + help + Say yes here to build I2C support for STMicroelectronics accelerometers. + + To compile this driver as a module, choose M here: the + module will be called st_accel_i2c. + +config ST_ACCEL_3AXIS_SPI + tristate "support SPI bus connection" + depends on ST_ACCEL_3AXIS && SPI_MASTER + help + Say yes here to build SPI support for STMicroelectronics accelerometers. + + To compile this driver as a module, choose M here: the + module will be called st_accel_spi. + +config ST_ACCEL_3AXIS_TRIGGERED_BUFFER + tristate "support triggered buffer" + depends on ST_ACCEL_3AXIS + select IIO_TRIGGERED_BUFFER + select IIO_BUFFER + help + Default trigger and buffer for STMicroelectronics accelerometers driver. + endmenudrivers/iio/accel/Makefile +5
-0@@ -3,3 +3,9 @@ # obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o + +st_accel-y := st_accel_core.o +obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) += st_accel_i2c.o +obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) += st_accel_spi.o +obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) += st_accel_trigger.ost_accel_buffer.o
drivers/iio/accel/Makefile +1
-0+obj-$(CONFIG_ST_ACCEL_3AXIS) += st_accel.odrivers/iio/accel/st_accel_buffer.c new file +76
-0@@ -0,0 +1,166 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/module.h> +#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/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#include <linux/iio/accel/st_accel.h> + +#define ST_ACCEL_ENABLE_ALL_CHANNELS 0x07 + + +static int st_accel_read_all (struct iio_dev *indio_dev,u8 *rx_array) +{ +int len =0 , i, n =0 ; +u8 reg_addr[ST_ACCEL_NUMBER_DATA_CHANNELS ]; +struct st_accel_data *adata =iio_priv (indio_dev); + +for (i =0 ; i <ST_ACCEL_NUMBER_DATA_CHANNELS ; i++) { +if (test_bit (i, indio_dev->active_scan_mask)) { + reg_addr[n] = indio_dev->channels[i].address; + n++; + } + } +switch (n) { +case 1 : + len = adata->read_multiple_byte (adata, reg_addr[0 ], +ST_ACCEL_BYTE_FOR_CHANNEL , rx_array); +break ; +case 2 : +if ((reg_addr[1 ] - reg_addr[0 ]) ==ST_ACCEL_BYTE_FOR_CHANNEL ) { + len = adata->read_multiple_byte (adata, reg_addr[0 ], +ST_ACCEL_BYTE_FOR_CHANNEL *n, + rx_array); + }else { + len = adata->read_multiple_byte (adata, reg_addr[0 ], +ST_ACCEL_BYTE_FOR_CHANNEL * +ST_ACCEL_NUMBER_DATA_CHANNELS , + rx_array); + rx_array[2 ] = rx_array[4 ]; + rx_array[3 ] = rx_array[5 ]; + len =ST_ACCEL_BYTE_FOR_CHANNEL *n; + } +break ; +case 3 : + len = adata->read_multiple_byte (adata, reg_addr[0 ], +ST_ACCEL_BYTE_FOR_CHANNEL *ST_ACCEL_NUMBER_DATA_CHANNELS , + rx_array); +break ; +default : +break ; + } + +return len; +} + +static int st_accel_get_buffer_element (struct iio_dev *indio_dev,u8 *buf) +{ +int ret, i, scan_count; +u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL *ST_ACCEL_NUMBER_DATA_CHANNELS ]; +s16 *data = (s16 *)buf;
drivers/iio/accel/st_accel_buffer.c line 86
Quoted 10 lines
drivers/iio/accel/st_accel_buffer.c new file +10
-0+ + ret =st_accel_read_all (indio_dev, rx_array); +if (ret <0 ) +return ret; + + scan_count =bitmap_weight (indio_dev->active_scan_mask, + indio_dev->masklength); + +for (i =0 ; i < scan_count; i++) + data[i] = (s16)(((s16)(rx_array[2 *i+1 ]) <<8 ) | rx_array[2 *i]);
There is no need to perform endian conversion
Quoted 4 lines
drivers/iio/accel/st_accel_buffer.c new file +4
-0+ +return i*sizeof (data[0 ]); +} +
drivers/iio/accel/st_accel_buffer.c line 100
Quoted 10 lines
drivers/iio/accel/st_accel_buffer.c new file +10
-0+static irqreturn_t st_accel_trigger_handler (int irq,void *p) +{ +struct iio_poll_func *pf = p; +struct iio_dev *indio_dev = pf->indio_dev; +int len =0 ; +char *data; + + data =kmalloc (indio_dev->scan_bytes,GFP_KERNEL ); +if (data ==NULL ) + goto done;
You could preallocate data in your postenable callback, so you don't have to do this each time the trigger handler is called.
drivers/iio/accel/st_accel_buffer.c line 101
Quoted 1 line
drivers/iio/accel/st_accel_buffer.c new file +1
-0+if (!bitmap_empty (indio_dev->active_scan_mask, indio_dev->masklength))
The bitmap will never be empty at this point
Quoted 100 lines
drivers/iio/accel/st_accel_buffer.c new file +65
-0+ len =st_accel_get_buffer_element (indio_dev, data); + else + goto done; +if (indio_dev->scan_timestamp) + *(s64 *)((u8 *)data +ALIGN (len,sizeof (s64))) = pf->timestamp; +iio_push_to_buffer (indio_dev->buffer, data); +kfree (data); + +done: +iio_trigger_notify_done (indio_dev->trig); +return IRQ_HANDLED ; +} + +static int st_accel_buffer_postenable (struct iio_dev *indio_dev) +{ +int err, i; +u8 active_bit =0x00 ; + +for (i =0 ; i <ST_ACCEL_NUMBER_DATA_CHANNELS ; i++) +if (test_bit (i, indio_dev->active_scan_mask)) + active_bit |= (1 << i); + + err =st_accel_set_axis_enable (indio_dev, active_bit); +if (err <0 ) + goto st_accel_buffer_postenable_error; + + err =iio_triggered_buffer_postenable (indio_dev); + +st_accel_buffer_postenable_error: +return err; +} + +static int st_accel_buffer_predisable (struct iio_dev *indio_dev) +{ +int err; + + err =iio_triggered_buffer_predisable (indio_dev); +if (err <0 ) + goto st_accel_buffer_predisable_error; + + err =st_accel_set_axis_enable (indio_dev,ST_ACCEL_ENABLE_ALL_CHANNELS ); + +st_accel_buffer_predisable_error: +return err; +} + +static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops = { + .preenable = &iio_sw_buffer_preenable, + .postenable = &st_accel_buffer_postenable, + .predisable = &st_accel_buffer_predisable, +}; + +int st_accel_allocate_ring (struct iio_dev *indio_dev) +{ + indio_dev->scan_timestamp = true; +return iio_triggered_buffer_setup (indio_dev, &iio_pollfunc_store_time, + &st_accel_trigger_handler, &st_accel_buffer_setup_ops); +} +EXPORT_SYMBOL (st_accel_allocate_ring); + +void st_accel_deallocate_ring (struct iio_dev *indio_dev) +{ +iio_triggered_buffer_cleanup (indio_dev); +} +EXPORT_SYMBOL (st_accel_deallocate_ring);drivers/iio/accel/st_accel_core.c new file +28
-0@@ -0,0 +1,1342 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/mutex.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/irq.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/buffer.h> + +#include <linux/iio/accel/st_accel.h> +#include <linux/platform_data/st_accel_pdata.h> +
[...]
Quoted 6 lines
+static int st_accel_write_data_with_mask(struct
iio_dev*indio_dev, u8reg_addr,drivers/iio/accel/st_accel_core.c new file +4
-0+ u8 mask, short num_bit, u8 data) +{ + int err, j, pos; +u8 prev_data;
drivers/iio/accel/st_accel_core.c
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+u8 new_data; +struct st_accel_data *adata =iio_priv (indio_dev); + + pos =8 - num_bit; +for (j =128 ; j >=0 ; j = j/2 ) { +if (mask / j >0 ) +break ; +else + pos--; + }
is this pos = __ffs(mask)? Or can't you just pass in pos directly instead of num_bit? I think that would make the code much more straight forward.
Quoted 6 lines
drivers/iio/accel/st_accel_core.c new file +6
-0+ + err = adata->read_byte (adata, reg_addr, &prev_data); +if (err <0 ) + goto st_accel_write_data_with_mask_error; + + new_data = ((prev_data & (~mask)) | ((data << pos) & mask));
drivers/iio/accel/st_accel_core.c
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+ err = adata->write_byte (adata, reg_addr, new_data); + +st_accel_write_data_with_mask_error: +return err; +} + +static int st_accel_match_odr (const struct st_accel_sensors *sensor, +unsigned int odr,struct st_accel_odr_available *odr_out) +{ +int i, ret =-1 ;
Since you pass it on to userspace this needs to be a proper error code, e.g. EINVAL
Quoted 7 lines
drivers/iio/accel/st_accel_core.c new file +7
-0+ +for (i =0 ; i <ARRAY_SIZE (sensor->odr.odr_avl); i++) { +if (sensor->odr.odr_avl[i].hz == odr) { + odr_out->hz = sensor->odr.odr_avl[i].hz; + odr_out->value = sensor->odr.odr_avl[i].value; + ret =0 ; +break ;
drivers/iio/accel/st_accel_core.c
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+ } + } + +return ret; +} + +static int st_accel_match_fs (const struct st_accel_sensors *sensor, +unsigned int fs,struct st_accel_fullscale_available *fs_out) +{ +int i, ret =-1 ;
Same here
Quoted 14 lines
drivers/iio/accel/st_accel_core.c new file +14
-0+ +for (i =0 ; i <ARRAY_SIZE (sensor->fs.fs_avl); i++) { +if (sensor->fs.fs_avl[i].num == fs) { + 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; + ret =0 ; +break ; + } + } + +return ret; +} +
[...]
drivers/iio/accel/st_accel_core.c
Quoted 2 lines
drivers/iio/accel/st_accel_core.c new file +2
-0+ +static int st_accel_set_enable (struct iio_dev *indio_dev,int enable)
I'd just let this take a bool instead of an int, lets you avoid some casts. ST_ACCEL_{ON,OFF} is kind of your custom bool, just use true and false instead
Quoted 1 line
+{
[...]
Quoted 25 lines
drivers/iio/accel/st_accel_core.c new file +25
-0+ +static int st_accel_read_raw (struct iio_dev *indio_dev, +struct iio_chan_spec const *ch,int *val, +int *val2,long mask) +{ +int err; +u8 outdata[ST_ACCEL_BYTE_FOR_CHANNEL ]; +struct st_accel_data *adata =iio_priv (indio_dev); + +switch (mask) { +case IIO_CHAN_INFO_RAW : +mutex_lock (&indio_dev->mlock); +if (indio_dev->currentmode ==INDIO_BUFFER_TRIGGERED ) { + err = -EBUSY ; + goto read_error; + }else { +if (!adata->enabled) { + err = -EIO ; + goto read_error; + }else { + err = adata->read_multiple_byte (adata, + ch->address,ST_ACCEL_BYTE_FOR_CHANNEL , + outdata); +if (err <0 ) + goto read_error;
drivers/iio/accel/st_accel_core.c
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+ + *val = ((s16)(((s16)(outdata[1 ]) <<8 ) + | outdata[0 ])) >> ch->scan_type.shift; + } + } +mutex_unlock (&indio_dev->mlock); +return IIO_VAL_INT ; +case IIO_CHAN_INFO_SCALE : + *val =0 ; + *val2 =UG_TO_MS2 (adata->gain);
There is no a generic macro for G to MS2 in iio called IIO_G_TO_M_S_2. I think you can also do the conversion of G to m/s**2 at compile time when you initilize the gain attributes of the st_accel_fullscale strutcs
Quoted 14 lines
drivers/iio/accel/st_accel_core.c new file +14
-0+return IIO_VAL_INT_PLUS_NANO ; +default : +return -EINVAL ; + } + +read_error: +mutex_unlock (&indio_dev->mlock); +return err; +} + +static int st_accel_check_device_list (struct iio_dev *indio_dev,u8 wai) +{ +int i; +bool found;
drivers/iio/accel/st_accel_core.c
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+struct st_accel_data *adata =iio_priv (indio_dev); + + found = false; +for (i =0 ; i <ARRAY_SIZE (st_accel_sensors); i++) { +if (st_accel_sensors[i].wai == wai) { + found = true; +break ; + } + } +if (!found)
You can just do 'i != ARRAY_SIZE(st_accel_sensors)' instead of 'found'.
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+ goto check_device_error; + + adata->index = i; + +return i; + +check_device_error: +dev_err (&indio_dev->dev,"device not supported -> wai (0x%x).\n" , wai); +return -ENODEV ; +}
[...]
[...]
Quoted 28 lines
drivers/iio/accel/st_accel_core.c new file +28
-0+static ssize_t st_accel_sysfs_get_fullscale (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + +return sprintf (buf,"%d\n" , adata->fullscale); +} + +static ssize_t st_accel_sysfs_set_fullscale (struct device *dev, +struct device_attribute *attr,const char *buf,size_t size) +{ +int err; +unsigned int fs; +struct st_accel_fullscale_available fs_out; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + + err =kstrtoint (buf,10 , &fs); +if (err <0 ) + goto conversion_error; + +mutex_lock (&indio_dev->mlock); + err =st_accel_match_fs (&st_accel_sensors[adata->index], fs, &fs_out); +if (err <0 ) + goto match_fullscale_error; + + err =st_accel_set_fullscale (indio_dev, &fs_out);
drivers/iio/accel/st_accel_core.c
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+if (err <0 ) { +dev_err (&indio_dev->dev, +"failed to set new fullscale. (errn %d).\n" , err); + } + +match_fullscale_error: +mutex_unlock (&indio_dev->mlock); +conversion_error: +return size; +}
I don't think you need the fullscale attribute. This is just the same as the scale attribute just in a different representation, as far as I can see.
Quoted 11 lines
drivers/iio/accel/st_accel_core.c new file +11
-0+ +static ssize_t st_accel_sysfs_fullscale_available (struct device *dev, +struct device_attribute *attr,char *buf) +{ +int i, len =0 ; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + +mutex_lock (&indio_dev->mlock); +for (i =0 ; i <ARRAY_SIZE (st_accel_sensors[adata->index].fs.fs_avl); + i++) {
drivers/iio/accel/st_accel_core.c
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+if (st_accel_sensors[adata->index].fs.fs_avl[i].num ==0 ) +break ; + + len +=sprintf (buf+len,"%d " , + st_accel_sensors[adata->index].fs.fs_avl[i].num); + } +mutex_unlock (&indio_dev->mlock); + + len--; + len +=sprintf (buf+len,"\n" );
just buf[len-1] = '\n'; would be simpler
Quoted 14 lines
drivers/iio/accel/st_accel_core.c new file +4
-0+return len; +} + +static ssize_t st_accel_sysfs_sampling_frequency_available(structdevice *dev,
drivers/iio/accel/st_accel_core.c new file +9
-0+ struct device_attribute *attr, char *buf) +{ + int i, len = 0; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + +mutex_lock (&indio_dev->mlock); +for (i =0 ; i <ARRAY_SIZE (st_accel_sensors[adata->index].odr.odr_avl); + i++) {
drivers/iio/accel/st_accel_core.c
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+if (st_accel_sensors[adata->index].odr.odr_avl[i].hz ==0 ) +break ; + + len +=sprintf (buf+len,"%d " , + st_accel_sensors[adata->index].odr.odr_avl[i].hz); + } +mutex_unlock (&indio_dev->mlock); + + len--; + len +=sprintf (buf+len,"\n" );
just buf[len-1] = '\n'; would be simpler
Quoted 21 lines
drivers/iio/accel/st_accel_core.c new file +21
-0+return len; +} + +/** +* IIO_DEVICE_ATTR - sampling_frequency_available +* @read: show all frequency available of the sensor. +* +*/ + +static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO, + st_accel_sysfs_sampling_frequency_available, NULL , 0); + +/** +* IIO_DEVICE_ATTR - fullscale_available +* @read: show all fullscale available of the sensor. +* +*/ + +static IIO_DEVICE_ATTR(fullscale_available, S_IRUGO, + st_accel_sysfs_fullscale_available, NULL , 0); +
drivers/iio/accel/st_accel_core.c
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+/** +* IIO_DEVICE_ATTR - fullscale +* @read: show the current fullscale of the sensor. +* @write: store the current fullscale of the sensor. +* +*/ + +static IIO_DEVICE_ATTR(fullscale, S_IWUSR | S_IRUGO, + st_accel_sysfs_get_fullscale, st_accel_sysfs_set_fullscale , 0); +
You need to document the non-standard sysfs files in a text document in Documentation/ABI/testing/sysfs-bus-iio-accel-st. See the other files in that folder for an example
Quoted 9 lines
drivers/iio/accel/st_accel_core.c new file +8
-0+/** +* IIO_DEVICE_ATTR - enable +* @read: show the current status of the sensor. +* @write: power on/off the sensor. +* +*/ + +static IIO_DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
st_accel_sysfs_get_enable,
drivers/iio/accel/st_accel_core.c
Quoted 6 lines
drivers/iio/accel/st_accel_core.c new file +6
-0+ st_accel_sysfs_set_enable , 0); + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + st_accel_sysfs_get_sampling_frequency, + st_accel_sysfs_set_sampling_frequency); +
I still don't get why you need this. Can't you just power-up and down the device on demand?
Quoted 1 line
+
[...]
Quoted 14 lines
drivers/iio/accel/st_accel_core.c new file +14
-0+int st_accel_iio_probe (struct iio_dev *indio_dev) +{ +int err; +u8 wai; +struct st_accel_data *adata =iio_priv (indio_dev); +struct st_accel_platform_data *pdata; + +mutex_init (&adata->slock); + indio_dev->modes =INDIO_DIRECT_MODE ; + indio_dev->info = &acc_info; + + err =st_accel_get_wai_device (indio_dev, +ST_ACCEL_DEFAULT_WAI_ADDRESS , &wai); +if (err <0 )
drivers/iio/accel/st_accel_core.c
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+ goto st_accel_iio_probe_error; + + err =st_accel_check_device_list (indio_dev, wai); +if (err <0 ) + goto st_accel_iio_probe_error; + + adata->multiread_bit = st_accel_sensors[adata->index].multi_read_bit; + indio_dev->channels = st_accel_sensors[adata->index].ch; + indio_dev->num_channels =ST_ACCEL_NUMBER_ALL_CHANNELS ; + pdata = adata->dev->platform_data;
How about if (!pdata)
pdata = &st_accel_default_pdata;
Quoted 78 lines
drivers/iio/accel/st_accel_core.c new file +49
-0+if (pdata ==NULL ) { + adata->fullscale = st_accel_default_pdata.fullscale; + adata->odr = st_accel_default_pdata.sampling_frequency; + }else { + adata->fullscale = pdata->fullscale; + adata->odr = pdata->sampling_frequency; + } + + err =st_accel_init_sensor (indio_dev); +if (err <0 ) + goto st_accel_iio_probe_error; + + err =st_accel_allocate_ring (indio_dev); +if (err <0 ) + goto st_accel_iio_probe_error; + +if (*adata->irq_data_ready >0 ) { + err =st_accel_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; + +return err; + +iio_device_register_error: +st_accel_remove_trigger (indio_dev); +acc_probe_trigger_error: +st_accel_deallocate_ring (indio_dev); +st_accel_iio_probe_error: +return err; +} +EXPORT_SYMBOL (st_accel_iio_probe); + +void st_accel_iio_remove (struct iio_dev *indio_dev) +{ +iio_device_unregister (indio_dev); +st_accel_remove_trigger (indio_dev); +st_accel_deallocate_ring (indio_dev); +iio_device_free (indio_dev); +} +EXPORT_SYMBOL (st_accel_iio_remove); + +MODULE_AUTHOR ("Denis Ciocca <[email protected]>" ); +MODULE_DESCRIPTION ("STMicroelectronics accelerometers driver" ); +MODULE_LICENSE ("GPL v2" );drivers/iio/accel/st_accel_i2c.c new file +22
-0@@ -0,0 +1,139 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* 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/trigger.h> + +#include <linux/iio/accel/st_accel.h> + + +#define ST_ACCEL_I2C_MULTIREAD 0x80 +
drivers/iio/accel/st_accel_i2c.c line 32
Quoted 10 lines
drivers/iio/accel/st_accel_i2c.c new file +10
-0+static int st_accel_i2c_read_byte (struct st_accel_data *adata, +u8 reg_addr,u8 *res_byte) +{ +int err; + + err =i2c_smbus_read_byte_data (to_i2c_client (adata->dev), reg_addr); +if (err <0 ) + goto st_accel_i2c_read_byte_error; + *res_byte = err &0xff ; +return err;
Shouldn't this be return 0?
drivers/iio/accel/st_accel_i2c.c line 35
Quoted 3 lines
drivers/iio/accel/st_accel_i2c.c new file +3
-0+ +st_accel_i2c_read_byte_error: +return -EIO ;
Just return err
Quoted 9 lines
drivers/iio/accel/st_accel_i2c.c new file +9
-0+} + +static int st_accel_i2c_read_multiple_byte (struct st_accel_data *adata, +u8 reg_addr,int len,u8 *data) +{ +int err; + +if (adata->multiread_bit == true) + reg_addr |=ST_ACCEL_I2C_MULTIREAD ;
drivers/iio/accel/st_accel_i2c.c line 54
Quoted 10 lines
drivers/iio/accel/st_accel_i2c.c new file +10
-0+ + err =i2c_smbus_read_i2c_block_data (to_i2c_client (adata->dev), + reg_addr, len, data); +if (err <0 ) + goto st_accel_i2c_read_multiple_byte_error; + +return err; + +st_accel_i2c_read_multiple_byte_error: +return -EIO ;
Just return err. Also makes the code much simpler since you don't need the goto and could just do a return i2c_smbus_read_i2c_block_data(...)
Quoted 2 lines
drivers/iio/accel/st_accel_i2c.c new file +2
-0+} +
[...]
Quoted 47 lines
drivers/iio/accel/st_accel_spi.c new file +40
-0@@ -0,0 +1,199 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> + +#include <linux/iio/accel/st_accel.h> + + +#define ACC_SPI_READ 0x80; +#define ACC_SPI_MULTIREAD 0xc0 + +static int st_accel_spi_read_byte (struct st_accel_data *adata, +u8 reg_addr,u8 *res_byte) +{ +struct spi_message msg; +int err; +u8 tx; + +struct spi_transfer xfers[] = { + { + .tx_buf = &tx, + .bits_per_word =8 , + .len =1 , + }, + { + .rx_buf = res_byte, + .bits_per_word =8 , + .len =1 ,
drivers/iio/accel/st_accel_spi.c line 50
Quoted 10 lines
drivers/iio/accel/st_accel_spi.c new file +10
-0+ } + }; + +mutex_lock (&adata->slock); + tx = reg_addr |ACC_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 (adata->dev), &msg); +mutex_unlock (&adata->slock);
Why does the spi code require locking and the i2c does not? Everything here seems to be on the stack, and the SPI bus has internal locking, so I don't think it is necessary to take slock.
drivers/iio/accel/st_accel_spi.c line 57
Quoted 7 lines
drivers/iio/accel/st_accel_spi.c new file +7
-0+if (err) + goto acc_spi_read_byte_error; + +return err; + +acc_spi_read_byte_error: +return -EIO ;
Just returne err.
Quoted 23 lines
drivers/iio/accel/st_accel_spi.c new file +23
-0+} + +static int st_accel_spi_read_multiple_byte (struct st_accel_data *adata, +u8 reg_addr,int len,u8 *data) +{ +struct spi_message msg; +int err; +u8 tx; + +struct spi_transfer xfers[] = { + { + .tx_buf = &tx, + .bits_per_word =8 , + .len =1 , + }, + { + .rx_buf = data, + .bits_per_word =8 , + .len = len, + } + }; + +mutex_lock (&adata->slock);
drivers/iio/accel/st_accel_spi.c line 90
Quoted 10 lines
drivers/iio/accel/st_accel_spi.c new file +10
-0+if (adata->multiread_bit == true) + tx = reg_addr |ACC_SPI_MULTIREAD ; +else + tx = reg_addr |ACC_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 (adata->dev), &msg); +mutex_unlock (&adata->slock);
Same thing about locking as above
drivers/iio/accel/st_accel_spi.c line 57
Quoted 6 lines
drivers/iio/accel/st_accel_spi.c new file +6
-0+if (err) + goto acc_spi_read_multiple_byte_error; +return len; + +acc_spi_read_multiple_byte_error: +return -EIO ;
Just return err
Quoted 14 lines
drivers/iio/accel/st_accel_spi.c new file +14
-0+} + +static int st_accel_spi_write_byte (struct st_accel_data *adata, +u8 reg_addr,u8 data) +{ +struct spi_message msg; +int err; +u8 tx[2 ]; + +struct spi_transfer xfers[] = { + { + .tx_buf = tx, + .bits_per_word =8 , + .len =2 ,
drivers/iio/accel/st_accel_spi.c line 50
Quoted 10 lines
drivers/iio/accel/st_accel_spi.c new file +10
-0+ } + }; + +mutex_lock (&adata->slock); + tx[0 ] = reg_addr; + tx[1 ] = data; +spi_message_init (&msg); +spi_message_add_tail (&xfers[0 ], &msg); + err =spi_sync (to_spi_device (adata->dev), &msg); +mutex_unlock (&adata->slock);
Again, is the lock necessary?
Quoted 4 lines
drivers/iio/accel/st_accel_spi.c new file +4
-0+ +return err; +} +
[...]
Quoted 2 lines
drivers/iio/accel/st_accel_trigger.c +0
-0
[...]
Quoted 86 lines
include/linux/iio/accel/st_accel.h new file +79
-0@@ -0,0 +1,122 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* v. 1.0.0 +* Licensed under the GPL-2. +*/ + +/* +* Supported sensors: +* LSM303DLH +* LSM303DLHC +* LIS3DH +* LSM330D +* LSM330DL +* LSM330DLC +* LSM303D +* LSM9DS0 +* LIS331DLH +* LSM303DL +* LSM303DLM +* LSM330 +* +*/ + + +#ifndef ST_ACCEL_H +#define ST_ACCEL_H + +#define LSM303DLH_ACCEL_DEV_NAME "lsm303dlh_accel" +#define LSM303DLHC_ACCEL_DEV_NAME "lsm303dlhc_accel" +#define LIS3DH_ACCEL_DEV_NAME "lis3dh" +#define LSM330D_ACCEL_DEV_NAME "lsm330d_accel" +#define LSM330DL_ACCEL_DEV_NAME "lsm330dl_accel" +#define LSM330DLC_ACCEL_DEV_NAME "lsm330dlc_accel" +#define LSM303D_ACCEL_DEV_NAME "lsm303d" +#define LSM9DS0_ACCEL_DEV_NAME "lsm9ds0" +#define LIS331DLH_ACCEL_DEV_NAME "lis331dlh" +#define LSM303DL_ACCEL_DEV_NAME "lsm303dl_accel" +#define LSM303DLM_ACCEL_DEV_NAME "lsm303dlm_accel" +#define LSM330_ACCEL_DEV_NAME "lsm330_accel" + +#define ST_ACCEL_NUMBER_ALL_CHANNELS 4 +#define ST_ACCEL_NUMBER_DATA_CHANNELS 3 +#define ST_ACCEL_BYTE_FOR_CHANNEL 2 +#define ST_ACCEL_SCAN_X 0 +#define ST_ACCEL_SCAN_Y 1 +#define ST_ACCEL_SCAN_Z 2 + +/** + * struct st_accel_data - ST accel device status + * @dev: Pointer to instance of struct device (I2C or SPI). + * @name: Name of the sensor in use. + * @enabled: Status of the sensor (0->off, 1->on). + * @index: Number used to point the sensor being used in the + * st_accel_sensors struct. + * @fullscale: Maximum range of measure by the sensor. + * @gain: Sensitivity of the sensor [ug/LSB]. + * @odr: Output data rate of the sensor. + * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread. + * @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. + * @trig: The trigger in use by the core driver. + * @irq_data_ready: IRQ number for data ready on INT1 pin. + * @slock: mutex for read and write operation. + * + */ + +struct st_accel_data { + struct device *dev; + bool enabled; + short index; + + unsigned int fullscale; + unsigned int gain; + unsigned int odr;
include/linux/iio/accel/st_accel.h line 89
Quoted 10 lines
include/linux/iio/accel/st_accel.h new file +10
-0+ +bool multiread_bit; +int (*read_byte) (struct st_accel_data *adata,u8 reg_addr, +u8 *res_byte); +int (*write_byte) (struct st_accel_data *adata,u8 reg_addr,u8 data); +int (*read_multiple_byte) (struct st_accel_data *adata,u8 reg_addr, +int len,u8 *data); + +struct iio_trigger *trig; +int *irq_data_ready;
I still don't get why this has to be a pointer...
Quoted 74 lines
include/linux/iio/accel/st_accel.h new file +33
-0+struct mutex slock; +}; + +int st_accel_iio_probe (struct iio_dev *indio_dev); +void st_accel_iio_remove (struct iio_dev *indio_dev); +int st_accel_set_dataready_irq (struct iio_dev *indio_dev,bool enable); +int st_accel_set_axis_enable (struct iio_dev *indio_dev,u8 axis_enable); + +#ifdef CONFIG_IIO_BUFFER +int st_accel_probe_trigger (struct iio_dev *indio_dev); +void st_accel_remove_trigger (struct iio_dev *indio_dev); +int st_accel_allocate_ring (struct iio_dev *indio_dev); +void st_accel_deallocate_ring (struct iio_dev *indio_dev); +#else /* CONFIG_IIO_BUFFER */ +static inline int st_accel_probe_trigger (struct iio_dev *indio_dev) +{ +return 0 ; +} +static inline void st_accel_remove_trigger (struct iio_dev *indio_dev) +{ +return ; +} +static inline int st_accel_allocate_ring (struct iio_dev *indio_dev) +{ +return 0 ; +} +static inline void st_accel_deallocate_ring (struct iio_dev *indio_dev) +{ +return ; +} +#endif /* CONFIG_IIO_BUFFER */ + +#endif /* ST_ACCEL_H */ include/linux/platform_data/st_accel_pdata.h new file +27
-1@@ -0,0 +1,27 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + + +#ifndef ST_ACCEL_PDATA_H +#define ST_ACCEL_PDATA_H + + +/** +* struct st_accel_platform_data - ST accel device platform data +* @fullscale: Value of fullscale used for the sensor. +* @sampling_frequency: Value of sampling frequency used for the sensor. +*/ + +struct st_accel_platform_data { +int fullscale; +int sampling_frequency; +}; + +#endif /* ST_ACCEL_PDATA_H */ --1.7.0.4
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Lars-Peter,
I have modified the source code to apply your code review, now I would tell you if I attach the only new patch or the full code, as you wish.
Also I have some question for you:
Quoted 4 lines
There is no a generic macro for G to MS2 in iio called
IIO_G_TO_M_S_2. I think you can also do the conversion of G to m/s**2 at compile time when you initilize the gain attributes of thest_accel_fullscalestrutcs
I don't find IIO_G_TO_M_S_2 in the framework code, but I added this macro in my source code. It is exatly?
Quoted 3 lines
I don't think you need the fullscale attribute. This is just the same as the scale attribute just in a different representation, as far as I can see.
I think this is more useful to user because if you want change the fullscale you can see immediately the current fullscale without apply conversion manually.
Quoted 2 lines
I still don't get why you need this. Can't you just power-up and down the device on demand?
The boot time is too different from one sensor to another, this introduce much delay in the single read.
Quoted 1 line
I still don't get why this has to be a pointer...
I don't understand if your point is use a pointer or use a variable or it is not necessary.
Thanks and best regards
Denis
Hi,
Quoted 4 lines · Denis CIOCCA
Hi Lars-Peter,
I have modified the source code to apply your code review, now I would tell you if I attach the only new patch or the full code, as you wish.
There is no attachment attached to this mail.
Quoted 10 lines
Also I have some question for you:
Quoted 4 lines
There is no a generic macro for G to MS2 in iio called
IIO_G_TO_M_S_2. I think you can also do the conversion of G to m/s**2 at compile time when you initilize the gain attributes of thest_accel_fullscalestrutcsI don't find
IIO_G_TO_M_S_2in the framework code, but I added this macro in my source code. It is exatly?
It's in the latest IIO tree and also in staging/staging-next. The definition is
+#define IIO_DEGREE_TO_RAD(deg) (((deg) * 314159ULL + 9000000ULL) \ / 18000000ULL)
Quoted 9 lines
Quoted 5 lines
I don't think you need the fullscale attribute. This is just the same as the scale attribute just in a different representation, as far as I can see.
I think this is more useful to user because if you want change the fullscale you can see immediately the current fullscale without apply conversion manually.
I understand your reasoning and I also think that changing the scale factor by entering the floating point lsb resolution is rather tedious and don't like this. On the other hand having two attributes for the same information and more importantly doing things different from every other driver in the IIO framework is kind of a no-go. So we have two possible solutions, both are far from optimal, but in my opinion the first one is the lesser of the two evils.
And hopefully the user or end-user will not have to manually navigate sysfs but will rather use some nice abstraction which hides these details.
Quoted 5 lines
Quoted 3 lines
I still don't get why you need this. Can't you just power-up and down the device on demand?
The boot time is too different from one sensor to another, this introduce much delay in the single read.
Ok, fair enough. But what power-up times are we talking about exactly. In the order of ms or more in the order of up to seconds?
Secondly we have similar attributes for other drivers, primarily DACs. But the attribute is called 'powerdown' instead of 'enable'. For consistency within the IIO framework it would be good if you could use that name (and its semantics) as well.
Quoted 6 lines
Quoted 3 lines
I still don't get why this has to be a pointer...
I don't understand if your point is use a pointer or use a variable or it is not necessary.
A plain integer should be enough. But you could also get rid of it completely since it is only used in the probe and remove callbacks, by adding a parameter to these functions and pass client->irq from the I2C and SPI probe and remove functions/
- Lars
Quoted 2 lines
There is no attachment attached to this mail.
my was a question. You prefer I attach all source code or the last modified?
Quoted 7 lines
Quoted 2 lines
I don't find
IIO_G_TO_M_S_2in the framework code, but I added this macro in my source code. It is exatly?It's in the latest IIO tree and also in staging/staging-next. The definition is
+#define IIO_DEGREE_TO_RAD(deg) (((deg) * 314159ULL + 9000000ULL) \ / 18000000ULL)
Sorry but I'm lost. The relation from IIO_DEGREE_TO_RAD and IIO_G_TO_M_S_2?
Quoted 16 lines
Quoted 7 lines
Quoted 3 lines
I don't think you need the fullscale attribute. This is just the same as the scale attribute just in a different representation, as far as I can see.
I think this is more useful to user because if you want change the fullscale you can see immediately the current fullscale without apply conversion manually.
I understand your reasoning and I also think that changing the scale factor by entering the floating point lsb resolution is rather tedious and don't like this. On the other hand having two attributes for the same information and more importantly doing things different from every other driver in the IIO framework is kind of a no-go. So we have two possible solutions, both are far from optimal, but in my opinion the first one is the lesser of the two evils.
Quoted 2 lines
And hopefully the user or end-user will not have to manually navigate sysfs but will rather use some nice abstraction which hides these details.
I check other drivers for that and then do cross-check with you.
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
I still don't get why you need this. Can't you just power-up and down the device on demand?
The boot time is too different from one sensor to another, this introduce much delay in the single read.
Ok, fair enough. But what power-up times are we talking about exactly. In the order of ms or more in the order of up to seconds?
The power-up time depends by ODR and by device. For example in some devices, you can lose the first 3-4 samples before it is ready, for most application this is too big.
Quoted 4 lines
Secondly we have similar attributes for other drivers, primarily DACs. But the attribute is called 'powerdown' instead of 'enable'. For consistency within the IIO framework it would be good if you could use that name (and its semantics) as well.
Ok this is not a problem.
Quoted 9 lines
Quoted 4 lines
Quoted 1 line
I still don't get why this has to be a pointer...
I don't understand if your point is use a pointer or use a variable or it is not necessary.
A plain integer should be enough. But you could also get rid of it completely since it is only used in the probe and remove callbacks, by adding a parameter to these functions and pass
client->irqfrom the I2C and SPI probe and remove functions
Ok the second one is very good solution.
Quoted 8 lines · Denis CIOCCA
Quoted 2 lines
There is no attachment attached to this mail.
my was a question. You prefer I attach all source code or the last modified?
Quoted 3 lines
Quoted 2 lines
I don't find
IIO_G_TO_M_S_2in the framework code, but I added this macro in my source code. It is exatly?
Quoted 7 lines
Quoted 4 lines
It's in the latest IIO tree and also in staging/staging-next. The definition is
+#define IIO_DEGREE_TO_RAD(deg) (((deg) * 314159ULL + 9000000ULL) \ / 18000000ULL)Sorry but I'm lost. The relation from
IIO_DEGREE_TO_RADandIIO_G_TO_M_S_2?
Sorry, too early in the morning, copied the wrong line.
+#define IIO_G_TO_M_S_2(g) ((g) * 980665ULL / 100000ULL)
Btw. the full patch can be found here:
http://comments.gmane.org/gmane.linux.kernel.iio/5777
- Lars
Quoted 17 lines · Lars-Peter Clausen
Quoted 8 lines · Denis CIOCCA
Quoted 2 lines
There is no attachment attached to this mail.
my was a question. You prefer I attach all source code or the last modified?
Quoted 3 lines
Quoted 2 lines
I don't find
IIO_G_TO_M_S_2in the framework code, but I added this macro in my source code. It is exatly?Quoted 7 lines
Quoted 4 lines
It's in the latest IIO tree and also in staging/staging-next. The definition is
+#define IIO_DEGREE_TO_RAD(deg) (((deg) * 314159ULL + 9000000ULL) \ / 18000000ULL)Sorry but I'm lost. The relation from
IIO_DEGREE_TO_RADandIIO_G_TO_M_S_2?
Quoted 9 lines
Sorry, too early in the morning, copied the wrong line.
+#define
IIO_G_TO_M_S_2(g)((g) * 980665ULL / 100000ULL)Btw. the full patch can be found here:
http://comments.gmane.org/gmane.linux.kernel.iio/5777- Lars
Ok thanks.
;)
Denis
Hi Lars-Peter,
I'm trying modified the driver to change the fullscale attribute and use the in_accel_*_scale attribute.
My question is that:
- the in_accel_*_scale attribute rappresent the conversion value from raw data sensor to m/s2, if I use only this value, how can I know what is the maximum full scale associated to the value?
- how can I view a list of available values? I have to create scale attribute on sysfs? If the last point is true, it is so redundant what I did before?
Thanks,
Denis
Quoted 26 lines · Lars-Peter Clausen
Quoted 8 lines · Denis CIOCCA
Quoted 2 lines
There is no attachment attached to this mail.
my was a question. You prefer I attach all source code or the last modified?
Quoted 3 lines
Quoted 2 lines
I don't find
IIO_G_TO_M_S_2in the framework code, but I added this macro in my source code. It is exatly?Quoted 7 lines
Quoted 4 lines
It's in the latest IIO tree and also in staging/staging-next. The definition is
+#define IIO_DEGREE_TO_RAD(deg) (((deg) * 314159ULL + 9000000ULL) \ / 18000000ULL)Sorry but I'm lost. The relation from
IIO_DEGREE_TO_RADandIIO_G_TO_M_S_2?Sorry, too early in the morning, copied the wrong line.
+#define
IIO_G_TO_M_S_2(g)((g) * 980665ULL / 100000ULL)Btw. the full patch can be found here:
http://comments.gmane.org/gmane.linux.kernel.iio/5777- Lars
Hi,
Quoted 9 lines · Denis CIOCCA
Hi Lars-Peter,
I'm trying modified the driver to change the fullscale attribute and use the in_accel_*_scale attribute.
My question is that:- the in_accel_*_scale attribute rappresent the conversion value from raw data sensor to m/s2, if I use only this value, how can I know what is the maximum full scale associated to the value?
Unfortunately there is currently no way in IIO to specify minimum or maximum values for a given channel. But you are welcome to come up with one, I think this will be useful for other drivers as well.
Quoted 4 lines
- how can I view a list of available values? I have to create scale attribute on sysfs? If the last point is true, it is so redundant what I did before?
You should add a scale_available attribute. Some ADC drivers implement this, you could use them as an example.
- Lars
Quoted 34 lines
Thanks,
Denis
Quoted 26 lines · Lars-Peter Clausen
Quoted 8 lines · Denis CIOCCA
Quoted 2 lines
There is no attachment attached to this mail.
my was a question. You prefer I attach all source code or the last modified?
Quoted 3 lines
Quoted 2 lines
I don't find
IIO_G_TO_M_S_2in the framework code, but I added this macro in my source code. It is exatly?Quoted 7 lines
Quoted 4 lines
It's in the latest IIO tree and also in staging/staging-next. The definition is
+#define IIO_DEGREE_TO_RAD(deg) (((deg) * 314159ULL + 9000000ULL) \ / 18000000ULL)Sorry but I'm lost. The relation from
IIO_DEGREE_TO_RADandIIO_G_TO_M_S_2?Sorry, too early in the morning, copied the wrong line.
+#define
IIO_G_TO_M_S_2(g)((g) * 980665ULL / 100000ULL)Btw. the full patch can be found here:
http://comments.gmane.org/gmane.linux.kernel.iio/5777- Lars
Jonathan Cameron
maintainerQuoted 9 lines · Lars-Peter Clausen
Hi,
Quoted 6 lines · Denis CIOCCA
Hi Lars-Peter,
I'm trying modified the driver to change the fullscale attribute and use the in_accel_*_scale attribute.
My question is that:
Quoted 7 lines
Quoted 3 lines
- the in_accel_*_scale attribute rappresent the conversion value from raw data sensor to m/s2, if I use only this value, how can I know what is the maximum full scale associated to the value?
Unfortunately there is currently no way in IIO to specify minimum or maximum values for a given channel. But you are welcome to come up with one, I think this will be useful for other drivers as well.
My personal preference would be an additional (for now optional) callback alongside read_raw and write_raw called something like available. This would be used by additional xxxxx_available attributes to describe the possible values for info_mask element. Exactly how to do this cleanly is a small 'implemention detail' which means this will probably be rather more complex than this implies :)
Interdependent info_mask elements will be 'interesting' (say a scale attribute for which the possible values change with the sampling frequency or visa-versa).
I've been meaning to look at this for a while, but if anyone else fancies giving it a go, feel free!
Quoted 46 lines
Quoted 5 lines
- how can I view a list of available values? I have to create scale attribute on sysfs? If the last point is true, it is so redundant what I did before?
You should add a
scale_availableattribute. Some ADC drivers implement this, you could use them as an example.- Lars
Quoted 34 lines
Thanks,
Denis
Quoted 26 lines · Lars-Peter Clausen
Quoted 8 lines · Denis CIOCCA
Quoted 2 lines
There is no attachment attached to this mail.
my was a question. You prefer I attach all source code or the last modified?
Quoted 3 lines
Quoted 2 lines
I don't find
IIO_G_TO_M_S_2in the framework code, but I added this macro in my source code. It is exatly?Quoted 7 lines
Quoted 4 lines
It's in the latest IIO tree and also in staging/staging-next. The definition is
+#define IIO_DEGREE_TO_RAD(deg) (((deg) * 314159ULL + 9000000ULL) \ / 18000000ULL)Sorry but I'm lost. The relation from
IIO_DEGREE_TO_RADandIIO_G_TO_M_S_2?Sorry, too early in the morning, copied the wrong line.
+#define
IIO_G_TO_M_S_2(g)((g) * 980665ULL / 100000ULL)Btw. the full patch can be found here:
http://comments.gmane.org/gmane.linux.kernel.iio/5777- Lars
Hi everybody,
I have modify the driver in according to your previous arrangements. I don't know if I use the best solution (I think not), but now I don't have time to read and write code for the framework because my work doesn't allow me.
Thanks
Denis
Quoted 1 line
From
348bcf19b2bb400bae0af5a5e444f772bb5c4f97Mon Sep 17 00:00:00 2001
From: Denis Ciocca <[email protected]>
Date: Mon, 22 Oct 2012 11:17:27 +0200
Subject: [PATCH] iio:accel: Add STMicroelectronics accelerometers driver
This patch adds generic accelerometer driver for STMicroelectronics accelerometers, currently it supports:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D, LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330
--- Documentation/ABI/testing/sysfs-bus-iio-accel-st | 26 + drivers/iio/accel/Kconfig | 37 + drivers/iio/accel/Makefile | 6 + drivers/iio/accel/st_accel_buffer.c | 176 +++ drivers/iio/accel/st_accel_core.c | 1291 ++++++++++++++++++++++ drivers/iio/accel/st_accel_i2c.c | 129 +++ drivers/iio/accel/st_accel_spi.c | 188 ++++ drivers/iio/accel/st_accel_trigger.c | 84 ++ include/linux/iio/accel/st_accel.h | 121 ++ include/linux/platform_data/st_accel_pdata.h | 27 + 10 files changed, 2085 insertions(+), 0 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-accel-st create mode 100644 drivers/iio/accel/st_accel_buffer.c create mode 100644 drivers/iio/accel/st_accel_core.c create mode 100644 drivers/iio/accel/st_accel_i2c.c create mode 100644 drivers/iio/accel/st_accel_spi.c create mode 100644 drivers/iio/accel/st_accel_trigger.c create mode 100644 include/linux/iio/accel/st_accel.h create mode 100644 include/linux/platform_data/st_accel_pdata.h
(quoted diff) +0 -0
b/Documentation/ABI/testing/sysfs-bus-iio-accel-st new file mode 100644
index 0000000..e86c1b4
Documentation/ABI/testing/sysfs-bus-iio-accel-st new file +26 -0
@@ -0,0 +1,26 @@ +What: /sys/bus/iio/devices/iio:deviceX/powerdown +KernelVersion: 3.7.0 +Contact: [email protected] +Description: + Reading returns either '1' or '0'. + '1' means that the device in question is off. + '0' means that the devices in question is on. + +What: /sys/bus/iio/devices/iio:deviceX/sampling_frequency_available +KernelVersion: 3.7.0 +Contact: [email protected] +Description: + Reading returns the sampling frequency available for the + device in question. This values are known in datasheet with + ODR [output data rate]. The units measurement is Hz. + +What: /sys/bus/iio/devices/iio:deviceX/in_accel_scale_available +KernelVersion: 3.7.0 +Contact: [email protected] +Description: + Reading returns the scale values available for the device in + question. + Writing one of this values to one of in_accel_*_scale attribute, + the full scale of the sensor in question change, the + value is related by this relation: + [in_accel_scale_available] = [sensitivity]*[conversion_to_ms^2].
drivers/iio/accel/Kconfig +37 -0
@@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D Say yes here to build support for the HID SENSOR accelerometers 3D. +config ST_ACCEL_3AXIS + tristate "STMicroelectronics accelerometers 3-Axis Driver" + depends on (I2C || SPI) && SYSFS + help + Say yes here to build support for STMicroelectronics accelerometers: + LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D, + LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330. + + This driver can also be built as a module. If so, the module + will be called st_accel. + +config ST_ACCEL_3AXIS_I2C + tristate "support I2C bus connection" + depends on ST_ACCEL_3AXIS && I2C + help + Say yes here to build I2C support for STMicroelectronics accelerometers. + + To compile this driver as a module, choose M here: the + module will be called st_accel_i2c. + +config ST_ACCEL_3AXIS_SPI + tristate "support SPI bus connection" + depends on ST_ACCEL_3AXIS && SPI_MASTER + help + Say yes here to build SPI support for STMicroelectronics accelerometers. + + To compile this driver as a module, choose M here: the + module will be called st_accel_spi. + +config ST_ACCEL_3AXIS_TRIGGERED_BUFFER + tristate "support triggered buffer" + depends on ST_ACCEL_3AXIS + select IIO_TRIGGERED_BUFFER + select IIO_BUFFER + help + Default trigger and buffer for STMicroelectronics accelerometers driver. + endmenu
drivers/iio/accel/Makefile +5 -0
@@ -3,3 +3,9 @@ # obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o + +st_accel-y := st_accel_core.o +obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) += st_accel_i2c.o +obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) += st_accel_spi.o +obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) += st_accel_trigger.o
st_accel_buffer.o
+obj-$(CONFIG_ST_ACCEL_3AXIS) += st_accel.o
(quoted diff) +0 -0
b/drivers/iio/accel/st_accel_buffer.c
new file mode 100644
index 0000000..bcf0fe2
drivers/iio/accel/st_accel_buffer.c new file +176 -0
176 lines not on this page · raw message
(quoted diff) +0 -0
b/drivers/iio/accel/st_accel_core.c
new file mode 100644
index 0000000..9697a2b
drivers/iio/accel/st_accel_core.c new file +691 -0
691 lines not on this page · raw message
u8 reg_addr,
+ u8 mask, short num_bit, u8 data)
+{
+ int err;
+ u8 prev_data;
+ u8 new_data;
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ err = adata->read_byte(adata, reg_addr, &prev_data);
+ if (err < 0)
+ goto st_accel_write_data_with_mask_error;
+
+ new_data = ((prev_data & (~mask)) | ((data << __ffs(mask)) & mask));
+ err = adata->write_byte(adata, reg_addr, new_data);
+
+st_accel_write_data_with_mask_error:
+ return err;
+}
+
+static int st_accel_match_odr(const struct st_accel_sensors *sensor,
+ unsigned int odr, struct st_accel_odr_available *odr_out)
+{
+ int i, ret = -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(sensor->odr.odr_avl); i++) {
+ if (sensor->odr.odr_avl[i].hz == odr) {
+ odr_out->hz = sensor->odr.odr_avl[i].hz;
+ odr_out->value = sensor->odr.odr_avl[i].value;
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int st_accel_match_fs(const struct st_accel_sensors *sensor,
+ unsigned int fs, struct st_accel_fullscale_available *fs_out)
+{
+ int i, ret = -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+ if (sensor->fs.fs_avl[i].num == fs) {
+ 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;
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int st_accel_match_scale(const struct st_accel_sensors *sensor,
+ int scale, struct st_accel_fullscale_available *fs_out)
+{
+ int i, ret = -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(sensor->fs.fs_avl); i++) {
+ if (sensor->fs.fs_avl[i].gain == scale) {
+ 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;
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int st_accel_set_dataready_irq(struct iio_dev *indio_dev, bool enable) +{
+ int err;
+ struct st_accel_data *adata;
+
+ adata = iio_priv(indio_dev);
+ if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr > 0) {
+ err = st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].drdy_irq.ig1.en_addr,
+ st_accel_sensors[adata->index].drdy_irq.ig1.en_mask, 1,
+ (int)enable);
+ if (err < 0)
+ goto st_accel_set_dataready_irq_error;
+ }
+
+ if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr > 0) {
+ err = st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr,
+ st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask, 1,
+ (int)enable);
+ if (err < 0)
+ goto st_accel_set_dataready_irq_error;
+ }
+
+ err = st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].drdy_irq.addr,
+ st_accel_sensors[adata->index].drdy_irq.mask, 1, (int)enable);
+ if (err < 0)
+ goto st_accel_set_dataready_irq_error;
+
+st_accel_set_dataready_irq_error:
+ return err;
+}
+EXPORT_SYMBOL(st_accel_set_dataready_irq);
+
+static int st_accel_set_bdu(struct iio_dev *indio_dev,
+ const struct st_accel_bdu *bdu, bool value)
+{
+ return st_accel_write_data_with_mask(indio_dev, bdu->addr, bdu->mask,
+ 1, (u8)value);
+}
+
+static int st_accel_set_odr(struct iio_dev *indio_dev,
+ struct st_accel_odr_available *odr_available)
+{
+ int err;
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ if ((st_accel_sensors[adata->index].odr.addr ==
+ st_accel_sensors[adata->index].pw.addr) &&
+ (st_accel_sensors[adata->index].odr.mask ==
+ st_accel_sensors[adata->index].pw.mask)) {
+ if (adata->enabled == true) {
+ err = st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].odr.addr,
+ st_accel_sensors[adata->index].odr.mask,
+ st_accel_sensors[adata->index].odr.num_bit,
+ odr_available->value);
+ if (err < 0)
+ goto sc_accel_set_odr_error;
+ } else {
+ adata->odr = odr_available->hz;
+ err = 0;
+ }
+ } else {
+ err = st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].odr.addr,
+ st_accel_sensors[adata->index].odr.mask,
+ st_accel_sensors[adata->index].odr.num_bit,
+ odr_available->value);
+ if (err < 0)
+ goto sc_accel_set_odr_error;
+ }
+
+sc_accel_set_odr_error:
+ return err;
+}
+
+int st_accel_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable) +{
+ int err;
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ err = st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].enable_axis.addr,
+ st_accel_sensors[adata->index].enable_axis.mask,
+ ST_ACCEL_DEFAULT_AXIS_N_BIT, axis_enable);
+
+ return err;
+}
+
+static int st_accel_set_enable(struct iio_dev *indio_dev, bool enable) +{
+ int err;
+ bool found;
+ u8 tmp_value;
+ struct st_accel_odr_available odr_out;
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ switch (enable) {
+ case true:
+ found = false;
+ tmp_value = st_accel_sensors[adata->index].pw.value_on;
+ if ((st_accel_sensors[adata->index].odr.addr ==
+ st_accel_sensors[adata->index].pw.addr) &&
+ (st_accel_sensors[adata->index].odr.mask ==
+ st_accel_sensors[adata->index].pw.mask)) {
+ err = st_accel_match_odr(
+ &st_accel_sensors[adata->index], adata->odr,
+ &odr_out);
+ if (err < 0)
+ goto set_enable_error;
+ tmp_value = odr_out.value;
+ found = true;
+ }
+ err = st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].pw.addr,
+ st_accel_sensors[adata->index].pw.mask,
+ st_accel_sensors[adata->index].pw.num_bit,
+ tmp_value);
+ if (err < 0)
+ goto set_enable_error;
+ adata->enabled = true;
+ if (found)
+ adata->odr = odr_out.hz;
+ break;
+ case false:
+ err = st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].pw.addr,
+ st_accel_sensors[adata->index].pw.mask,
+ st_accel_sensors[adata->index].pw.num_bit,
+ st_accel_sensors[adata->index].pw.value_off);
+ if (err < 0)
+ goto set_enable_error;
+ adata->enabled = false;
+ break;
+ default:
+ err = -EINVAL;
+ goto set_enable_error;
+ }
+
+set_enable_error:
+ return err;
+}
+
+static int st_accel_set_fullscale(struct iio_dev *indio_dev,
+ struct st_accel_fullscale_available *fs_avl)
+{
+ int err;
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ err = st_accel_write_data_with_mask(indio_dev,
+ st_accel_sensors[adata->index].fs.addr,
+ st_accel_sensors[adata->index].fs.mask,
+ st_accel_sensors[adata->index].fs.num_bit,
+ fs_avl->value);
+ if (err < 0)
+ goto st_accel_set_fullscale_error;
+
+ adata->fullscale = fs_avl->num;
+ adata->gain = fs_avl->gain;
+ return err;
+
+st_accel_set_fullscale_error:
+ dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
+ return err;
+}
+
+static int st_accel_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *ch, int *val,
+ int *val2, long mask)
+{
+ int err;
+ u8 outdata[ST_ACCEL_BYTE_FOR_CHANNEL];
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
+ err = -EBUSY;
+ goto read_error;
+ } else {
+ if (!adata->enabled) {
+ err = -EIO;
+ goto read_error;
+ } else {
+ err = adata->read_multiple_byte(adata,
+ ch->address, ST_ACCEL_BYTE_FOR_CHANNEL,
+ outdata);
+ if (err < 0)
+ goto read_error;
+
+ *val = ((s16)(((s16)(outdata[1]) << 8)
+ | outdata[0])) >> ch->scan_type.shift;
+ }
+ }
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = adata->gain;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+
+read_error:
+ mutex_unlock(&indio_dev->mlock);
+ return err;
+}
+
+static int st_accel_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ int err;
+ struct st_accel_fullscale_available fs_out;
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ err = st_accel_match_scale(&st_accel_sensors[adata->index],
+ val2, &fs_out);
+ if (err < 0)
+ goto write_error;
+
+ err = st_accel_set_fullscale(indio_dev, &fs_out);
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+write_error:
+ return err;
+}
+
+static int st_accel_check_device_list(struct iio_dev *indio_dev, u8 wai) +{
+ int i;
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ for (i = 0; i < ARRAY_SIZE(st_accel_sensors); i++) {
+ if (st_accel_sensors[i].wai == wai)
+ break;
+ }
+ if (i == ARRAY_SIZE(st_accel_sensors))
+ goto check_device_error;
+
+ adata->index = i;
+
+ return i;
+
+check_device_error:
+ dev_err(&indio_dev->dev, "device not supported -> wai (0x%x).\n", wai);
+ return -ENODEV;
+}
+
+static int st_accel_get_wai_device(struct iio_dev *indio_dev, u8 reg_addr,
+ u8 *value)
+{
+ int ret;
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ ret = adata->read_byte(adata, reg_addr, value);
+ if (ret < 0)
+ goto read_byte_wai_error;
+
+ return 0;
+
+read_byte_wai_error:
+ dev_err(&indio_dev->dev,
+ "failed to read WhoAmI (register 0x%x).\n", reg_addr);
+ return -EIO;
+}
+
+static ssize_t st_accel_sysfs_set_sampling_frequency(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err;
+ unsigned int freq;
+ struct st_accel_odr_available odr_out;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ err = kstrtoint(buf, 10, &freq);
+ if (err < 0)
+ goto conversion_error;
+
+ mutex_lock(&indio_dev->mlock);
+ err = st_accel_match_odr(&st_accel_sensors[adata->index],
+ freq, &odr_out);
+ if (err < 0)
+ goto st_accel_sysfs_set_sampling_frequency_error;
+
+ err = st_accel_set_odr(indio_dev, &odr_out);
+ if (err < 0) {
+ dev_err(&indio_dev->dev,
+ "failed to set sampling frequency to %d.\n", freq);
+ goto st_accel_sysfs_set_sampling_frequency_error;
+ }
+ adata->odr = odr_out.hz;
+
+st_accel_sysfs_set_sampling_frequency_error:
+ mutex_unlock(&indio_dev->mlock);
+conversion_error:
+ return size;
+}
+
+static ssize_t st_accel_sysfs_get_sampling_frequency(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", adata->odr);
+}
+
+static ssize_t st_accel_sysfs_set_powerdown(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err;
+ bool powerdown;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+ err = strtobool(buf, &powerdown);
+ if (err < 0)
+ goto set_enable_error;
+
+ mutex_lock(&indio_dev->mlock);
+ err = st_accel_set_enable(indio_dev, ~powerdown);
+ if (err < 0)
+ dev_err(&indio_dev->dev,
+ "failed to set powerdown to %d.\n", (int)(powerdown));
+ mutex_unlock(&indio_dev->mlock);
+
+set_enable_error:
+ return size;
+}
+
+static ssize_t st_accel_sysfs_get_powerdown(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", (int)(!adata->enabled));
+}
+
+static ssize_t st_accel_sysfs_scale_available(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i, len = 0;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ for (i = 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].fs.fs_avl);
+ i++) {
+ if (st_accel_sensors[adata->index].fs.fs_avl[i].num == 0)
+ break;
+
+ len += sprintf(buf+len, "0.%06u ",
+ st_accel_sensors[adata->index].fs.fs_avl[i].gain);
+ }
+ mutex_unlock(&indio_dev->mlock);
+ buf[len-1] = '\n';
+
+ return len;
+}
+
+static ssize_t st_accel_sysfs_sampling_frequency_available(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i, len = 0;
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ for (i = 0; i < ARRAY_SIZE(st_accel_sensors[adata->index].odr.odr_avl);
+ i++) {
+ if (st_accel_sensors[adata->index].odr.odr_avl[i].hz == 0)
+ break;
+
+ len += sprintf(buf+len, "%d ",
+ st_accel_sensors[adata->index].odr.odr_avl[i].hz);
+ }
+ mutex_unlock(&indio_dev->mlock);
+ buf[len-1] = '\n';
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
+ st_accel_sysfs_sampling_frequency_available, NULL , 0);
+
+static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
+ st_accel_sysfs_scale_available, NULL , 0);
+
+static IIO_DEVICE_ATTR(powerdown, S_IWUSR | S_IRUGO,
+ st_accel_sysfs_get_powerdown, st_accel_sysfs_set_powerdown , 0);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ st_accel_sysfs_get_sampling_frequency,
+ st_accel_sysfs_set_sampling_frequency);
+
+static struct attribute *st_accel_attributes[] = {
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_in_accel_scale_available.dev_attr.attr,
+ &iio_dev_attr_powerdown.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group st_accel_attribute_group = {
+ .attrs = st_accel_attributes,
+};
+
+static const struct iio_info acc_info = {
+ .driver_module = THIS_MODULE,
+ .attrs = &st_accel_attribute_group,
+ .read_raw = &st_accel_read_raw,
+ .write_raw = &st_accel_write_raw,
+};
+
+static int st_accel_init_sensor(struct iio_dev *indio_dev)
+{
+ int err;
+ struct st_accel_odr_available odr_out;
+ struct st_accel_fullscale_available fs_out;
+ struct st_accel_data *adata = iio_priv(indio_dev);
+
+ err = st_accel_set_enable(indio_dev, false);
+ if (err < 0)
+ goto init_error;
+
+ err = st_accel_set_axis_enable(indio_dev, ST_ACCEL_ENABLE_ALL_CHANNELS);
+ if (err < 0)
+ goto init_error;
+
+ st_accel_match_fs(&st_accel_sensors[adata->index],
+ adata->fullscale, &fs_out);
+ err = st_accel_set_fullscale(indio_dev, &fs_out);
+ if (err < 0)
+ goto init_error;
+
+ st_accel_match_odr(&st_accel_sensors[adata->index],
+ adata->odr, &odr_out);
+ err = st_accel_set_odr(indio_dev, &odr_out);
+ if (err < 0)
+ goto init_error;
+
+ err = st_accel_set_bdu(indio_dev,
+ &st_accel_sensors[adata->index].bdu, true);
+
+init_error:
+ return err;
+}
+
+int st_accel_iio_probe(struct iio_dev *indio_dev, int irq)
+{
+ int err;
+ u8 wai;
+ struct st_accel_data *adata = iio_priv(indio_dev);
+ struct st_accel_platform_data *pdata;
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &acc_info;
+
+ err = st_accel_get_wai_device(indio_dev,
+ ST_ACCEL_DEFAULT_WAI_ADDRESS, &wai);
+ if (err < 0)
+ goto st_accel_iio_probe_error;
+
+ err = st_accel_check_device_list(indio_dev, wai);
+ if (err < 0)
+ goto st_accel_iio_probe_error;
+
+ adata->multiread_bit = st_accel_sensors[adata->index].multi_read_bit;
+ indio_dev->channels = st_accel_sensors[adata->index].ch;
+ indio_dev->num_channels = ST_ACCEL_NUMBER_ALL_CHANNELS;
+ pdata = adata->dev->platform_data;
+ if (!pdata)
+ pdata = (struct st_accel_platform_data *)
+ &st_accel_default_pdata;
+
+ adata->fullscale = pdata->fullscale;
+ adata->odr = pdata->sampling_frequency;
+
+ err = st_accel_init_sensor(indio_dev);
+ if (err < 0)
+ goto st_accel_iio_probe_error;
+
+ err = st_accel_allocate_ring(indio_dev);
+ if (err < 0)
+ goto st_accel_iio_probe_error;
+
+ if (irq > 0) {
+ err = st_accel_probe_trigger(indio_dev, irq);
+ if (err < 0)
+ goto acc_probe_trigger_error;
+ }
+
+ err = iio_device_register(indio_dev);
+ if (err)
+ goto iio_device_register_error;
+
+ return err;
+
+iio_device_register_error:
+ st_accel_remove_trigger(indio_dev, irq);
+acc_probe_trigger_error:
+ st_accel_deallocate_ring(indio_dev);
+st_accel_iio_probe_error:
+ return err;
+}
+EXPORT_SYMBOL(st_accel_iio_probe);
+
+void st_accel_iio_remove(struct iio_dev *indio_dev, int irq)
+{
+ iio_device_unregister(indio_dev);
+ st_accel_remove_trigger(indio_dev, irq);
+ st_accel_deallocate_ring(indio_dev);
+ iio_device_free(indio_dev);
+}
+EXPORT_SYMBOL(st_accel_iio_remove);
+
+MODULE_AUTHOR("Denis Ciocca <[email protected]>"); +MODULE_DESCRIPTION("STMicroelectronics accelerometers driver"); +MODULE_LICENSE("GPL v2");
drivers/iio/accel/st_accel_i2c.c new file +129 -0
129 lines not on this page · raw message
drivers/iio/accel/st_accel_spi.c new file +188 -0
188 lines not on this page · raw message
(quoted diff) +0 -0
b/drivers/iio/accel/st_accel_trigger.c
new file mode 100644
index 0000000..8bc970c
drivers/iio/accel/st_accel_trigger.c new file +84 -0
84 lines not on this page · raw message
(quoted diff) +0 -0
b/include/linux/iio/accel/st_accel.h
new file mode 100644
index 0000000..1efd29e
include/linux/iio/accel/st_accel.h new file +121 -0
121 lines not on this page · raw message
(quoted diff) +0 -0
b/include/linux/platform_data/st_accel_pdata.h
new file mode 100644
index 0000000..416489b
include/linux/platform_data/st_accel_pdata.h new file +27 -0
@@ -0,0 +1,27 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + + +#ifndef ST_ACCEL_PDATA_H +#define ST_ACCEL_PDATA_H + + +/** +* struct st_accel_platform_data - ST accel device platform data +* @fullscale: Value of fullscale used for the sensor. +* @sampling_frequency: Value of sampling frequency used for the sensor. +*/ + +struct st_accel_platform_data { +int fullscale; +int sampling_frequency; +}; + +#endif /* ST_ACCEL_PDATA_H */
Signature · 1 line
1.7.0.4
Jonathan Cameron
maintainerQuoted 6 lines · Denis Ciocca
Hi everybody,
I have modify the driver in according to your previous arrangements. I don't know if I use the best solution (I think not), but now I don't have time to read and write code for the framework because my work doesn't allow me.
Thanks for your work so far on this and I fully understand that work can and often takes one away from contributing.
I have reviewed the code as it stands so that if you or anyone else wants to continue in the future the pointers are there.
It's a pretty clean driver now with all the big stuff sorted. I hope we do manage to get the last few bits cleaned up.
Quoted 51 lines
Thanks
Denis
From
348bcf19b2bb400bae0af5a5e444f772bb5c4f97Mon Sep 17 00:00:00 2001
From: Denis Ciocca <[email protected]>
Date: Mon, 22 Oct 2012 11:17:27 +0200
Subject: [PATCH] iio:accel: Add STMicroelectronics accelerometers driverThis patch adds generic accelerometer driver for STMicroelectronics accelerometers, currently it supports:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D, LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330Signed-off-by Denis Ciocca <[email protected]>--- Documentation/ABI/testing/sysfs-bus-iio-accel-st | 26 + drivers/iio/accel/Kconfig | 37 + drivers/iio/accel/Makefile | 6 + drivers/iio/accel/st_accel_buffer.c | 176 +++ drivers/iio/accel/st_accel_core.c | 1291 ++++++++++++++++++++++ drivers/iio/accel/st_accel_i2c.c | 129 +++ drivers/iio/accel/st_accel_spi.c | 188 ++++ drivers/iio/accel/st_accel_trigger.c | 84 ++ include/linux/iio/accel/st_accel.h | 121 ++ include/linux/platform_data/st_accel_pdata.h | 27 + 10 files changed, 2085 insertions(+), 0 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-accel-st create mode 100644 drivers/iio/accel/st_accel_buffer.c create mode 100644 drivers/iio/accel/st_accel_core.c create mode 100644 drivers/iio/accel/st_accel_i2c.c create mode 100644 drivers/iio/accel/st_accel_spi.c create mode 100644 drivers/iio/accel/st_accel_trigger.c create mode 100644 include/linux/iio/accel/st_accel.h create mode 100644 include/linux/platform_data/st_accel_pdata.hDocumentation/ABI/testing/sysfs-bus-iio-accel-st new file +6
-0@@ -0,0 +1,26 @@ +What: /sys/bus/iio/devices/iio:deviceX/powerdown +KernelVersion: 3.7.0 +Contact: [email protected] +Description: + Reading returns either '1' or '0'. + '1' means that the device in question is off.
Documentation/ABI/testing/sysfs-bus-iio-accel-st line 16
Quoted 10 lines
Documentation/ABI/testing/sysfs-bus-iio-accel-st new file +10
-0+ '0' means that the devices in question is on. + +What: /sys/bus/iio/devices/iio:deviceX/sampling_frequency_available +KernelVersion: 3.7.0 +Contact: [email protected] +Description: + Reading returns the sampling frequency available for the + device in question. This values are known in datasheet with + ODR [output data rate]. The units measurement is Hz. +
This is standard (even if it's not used anywhere else) so should be in sysfs-bus-iio. (same for sampling_frequency_available)
Quoted 155 lines
Documentation/ABI/testing/sysfs-bus-iio-accel-st new file +10
-0+What: /sys/bus/iio/devices/iio:deviceX/in_accel_scale_available +KernelVersion: 3.7.0 +Contact: [email protected] +Description: + Reading returns the scale values available for the device in + question. + Writing one of this values to one of in_accel_*_scale attribute, + the full scale of the sensor in question change, the + value is related by this relation: + [in_accel_scale_available] = [sensitivity]*[conversion_to_ms^2].drivers/iio/accel/Kconfig +37
-0@@ -13,4 +13,41 @@ config HID_SENSOR_ACCEL_3D Say yes here to build support for the HID SENSOR accelerometers 3D. +config ST_ACCEL_3AXIS + tristate "STMicroelectronics accelerometers 3-Axis Driver" + depends on (I2C || SPI) && SYSFS + help + Say yes here to build support for STMicroelectronics accelerometers: + LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LSM303D, + LSM9DS0, LIS331DLH, LSM303DL, LSM303DLM, LSM330. + + This driver can also be built as a module. If so, the module + will be called st_accel. + +config ST_ACCEL_3AXIS_I2C + tristate "support I2C bus connection" + depends on ST_ACCEL_3AXIS && I2C + help + Say yes here to build I2C support for STMicroelectronics accelerometers. + + To compile this driver as a module, choose M here: the + module will be called st_accel_i2c. + +config ST_ACCEL_3AXIS_SPI + tristate "support SPI bus connection" + depends on ST_ACCEL_3AXIS && SPI_MASTER + help + Say yes here to build SPI support for STMicroelectronics accelerometers. + + To compile this driver as a module, choose M here: the + module will be called st_accel_spi. + +config ST_ACCEL_3AXIS_TRIGGERED_BUFFER + tristate "support triggered buffer" + depends on ST_ACCEL_3AXIS + select IIO_TRIGGERED_BUFFER + select IIO_BUFFER + help + Default trigger and buffer for STMicroelectronics accelerometers driver. +endmenu
drivers/iio/accel/Makefile +5
-0@@ -3,3 +3,9 @@ # obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o + +st_accel-y := st_accel_core.o +obj-$(CONFIG_ST_ACCEL_3AXIS_I2C) += st_accel_i2c.o +obj-$(CONFIG_ST_ACCEL_3AXIS_SPI) += st_accel_spi.o +obj-$(CONFIG_ST_ACCEL_3AXIS_TRIGGERED_BUFFER) += st_accel_trigger.ost_accel_buffer.o
drivers/iio/accel/Makefile +1
-0+obj-$(CONFIG_ST_ACCEL_3AXIS) += st_accel.odrivers/iio/accel/st_accel_buffer.c new file +76
-0@@ -0,0 +1,176 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/module.h> +#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/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#include <linux/iio/accel/st_accel.h> + + +#define ST_ACCEL_ENABLE_ALL_CHANNELS 0x07 + +static int st_accel_read_all (struct iio_dev *indio_dev,u8 *rx_array) +{ +int len =0 , i, n =0 ; +u8 reg_addr[ST_ACCEL_NUMBER_DATA_CHANNELS ]; +struct st_accel_data *adata =iio_priv (indio_dev); + +for (i =0 ; i <ST_ACCEL_NUMBER_DATA_CHANNELS ; i++) { +if (test_bit (i, indio_dev->active_scan_mask)) { + reg_addr[n] = indio_dev->channels[i].address; + n++; + } + } +switch (n) { +case 1 : + len = adata->read_multiple_byte (adata, reg_addr[0 ], +ST_ACCEL_BYTE_FOR_CHANNEL , rx_array); +break ; +case 2 : +if ((reg_addr[1 ] - reg_addr[0 ]) ==ST_ACCEL_BYTE_FOR_CHANNEL ) { + len = adata->read_multiple_byte (adata, reg_addr[0 ], +ST_ACCEL_BYTE_FOR_CHANNEL *n, + rx_array); + }else { + len = adata->read_multiple_byte (adata, reg_addr[0 ], +ST_ACCEL_BYTE_FOR_CHANNEL * +ST_ACCEL_NUMBER_DATA_CHANNELS , + rx_array); + rx_array[2 ] = rx_array[4 ]; + rx_array[3 ] = rx_array[5 ]; + len =ST_ACCEL_BYTE_FOR_CHANNEL *n; + } +break ; +case 3 : + len = adata->read_multiple_byte (adata, reg_addr[0 ], +ST_ACCEL_BYTE_FOR_CHANNEL *ST_ACCEL_NUMBER_DATA_CHANNELS , + rx_array); +break ; +default : +break ; + } + +return len; +} + +static int st_accel_get_buffer_element (struct iio_dev *indio_dev,u8 *buf) +{ +int ret, i, scan_count; +u8 rx_array[ST_ACCEL_BYTE_FOR_CHANNEL *ST_ACCEL_NUMBER_DATA_CHANNELS ]; +s16 *data = (s16 *)buf;
drivers/iio/accel/st_accel_buffer.c line 86
Quoted 10 lines
drivers/iio/accel/st_accel_buffer.c new file +10
-0+ + ret =st_accel_read_all (indio_dev, rx_array); +if (ret <0 ) +return ret; + + scan_count =bitmap_weight (indio_dev->active_scan_mask, + indio_dev->masklength); + +for (i =0 ; i < scan_count; i++) + data[i] =le16_to_cpup ((__le16 *)&(rx_array[i*2 ]));
As Lars pointed out there is no need to do this. Just specify these as little endian channels and let userspace handle it.
Quoted 397 lines
drivers/iio/accel/st_accel_buffer.c new file +90
-0+ +return i*sizeof (data[0 ]); +} + +static irqreturn_t st_accel_trigger_handler (int irq,void *p) +{ +int len =0 ; +struct iio_poll_func *pf = p; +struct iio_dev *indio_dev = pf->indio_dev; +struct st_accel_data *adata =iio_priv (indio_dev); + + len =st_accel_get_buffer_element (indio_dev, adata->buffer_data); +if (indio_dev->scan_timestamp) + *(s64 *)((u8 *)adata->buffer_data + +ALIGN (len,sizeof (s64))) = pf->timestamp; + +iio_push_to_buffer (indio_dev->buffer, adata->buffer_data); +iio_trigger_notify_done (indio_dev->trig); + +return IRQ_HANDLED ; +} + +static int st_accel_buffer_postenable (struct iio_dev *indio_dev) +{ +int err, i; +u8 active_bit =0x00 ; +struct st_accel_data *adata =iio_priv (indio_dev); + + adata->buffer_data =kmalloc (indio_dev->scan_bytes,GFP_KERNEL ); +if (adata->buffer_data ==NULL ) { + err = -ENOMEM ; + goto allocate_memory_error; + } + +for (i =0 ; i <ST_ACCEL_NUMBER_DATA_CHANNELS ; i++) +if (test_bit (i, indio_dev->active_scan_mask)) + active_bit |= (1 << i); + + err =st_accel_set_axis_enable (indio_dev, active_bit); +if (err <0 ) + goto st_accel_buffer_postenable_error; + + err =iio_triggered_buffer_postenable (indio_dev); + +return err; + +allocate_memory_error: +kfree (adata->buffer_data); +st_accel_buffer_postenable_error: +return err; +} + +static int st_accel_buffer_predisable (struct iio_dev *indio_dev) +{ +int err; +struct st_accel_data *adata =iio_priv (indio_dev); + + err =iio_triggered_buffer_predisable (indio_dev); +if (err <0 ) + goto st_accel_buffer_predisable_error; + + err =st_accel_set_axis_enable (indio_dev,ST_ACCEL_ENABLE_ALL_CHANNELS ); +if (err <0 ) + goto st_accel_buffer_predisable_error; + +kfree (adata->buffer_data); + +st_accel_buffer_predisable_error: +return err; +} + +static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops = { + .preenable = &iio_sw_buffer_preenable, + .postenable = &st_accel_buffer_postenable, + .predisable = &st_accel_buffer_predisable, +}; + +int st_accel_allocate_ring (struct iio_dev *indio_dev) +{ + indio_dev->scan_timestamp = true; +return iio_triggered_buffer_setup (indio_dev, &iio_pollfunc_store_time, + &st_accel_trigger_handler, &st_accel_buffer_setup_ops); +} +EXPORT_SYMBOL (st_accel_allocate_ring); + +void st_accel_deallocate_ring (struct iio_dev *indio_dev) +{ +iio_triggered_buffer_cleanup (indio_dev); +} +EXPORT_SYMBOL (st_accel_deallocate_ring);drivers/iio/accel/st_accel_core.c new file +300
-0@@ -0,0 +1,1291 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/mutex.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/irq.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/buffer.h> + +#include <linux/iio/accel/st_accel.h> +#include <linux/platform_data/st_accel_pdata.h> + + +#define ST_ACCEL_FULLSCALE_AVL_MAX 5 +#define ST_ACCEL_ODR_LIST_MAX 10 +#define ST_ACCEL_ENABLE_ALL_CHANNELS 0x07 + +/* DEFAULT VALUE FOR SENSORS */ +#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR 0x28 +#define ST_ACCEL_DEFAULT_OUT_X_H_ADDR 0x29 +#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR 0x2a +#define ST_ACCEL_DEFAULT_OUT_Y_H_ADDR 0x2b +#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR 0x2c +#define ST_ACCEL_DEFAULT_OUT_Z_H_ADDR 0x2d +#define ST_ACCEL_DEFAULT_WAI_ADDRESS 0x0f +#define ST_ACCEL_DEFAULT_POWER_ON_VALUE 0x01 +#define ST_ACCEL_DEFAULT_POWER_OFF_VALUE 0x00 +#define ST_ACCEL_DEFAULT_12_REALBITS 12 +#define ST_ACCEL_DEFAULT_16_REALBITS 16 +#define ST_ACCEL_DEFAULT_AXIS_ADDR 0x20 +#define ST_ACCEL_DEFAULT_AXIS_MASK 0x07 +#define ST_ACCEL_DEFAULT_AXIS_N_BIT 3 + +/* ODR */ +#define ST_ACCEL_ODR_AVL_1HZ 1 +#define ST_ACCEL_ODR_AVL_3HZ 3 +#define ST_ACCEL_ODR_AVL_6HZ 6 +#define ST_ACCEL_ODR_AVL_10HZ 10 +#define ST_ACCEL_ODR_AVL_12HZ 12 +#define ST_ACCEL_ODR_AVL_25HZ 25 +#define ST_ACCEL_ODR_AVL_50HZ 50 +#define ST_ACCEL_ODR_AVL_100HZ 100 +#define ST_ACCEL_ODR_AVL_200HZ 200 +#define ST_ACCEL_ODR_AVL_400HZ 400 +#define ST_ACCEL_ODR_AVL_800HZ 800 +#define ST_ACCEL_ODR_AVL_1000HZ 1000 +#define ST_ACCEL_ODR_AVL_1600HZ 1600 + +/* FULLSCALE */ +#define ST_ACCEL_FS_AVL_2G 2 +#define ST_ACCEL_FS_AVL_4G 4 +#define ST_ACCEL_FS_AVL_6G 6 +#define ST_ACCEL_FS_AVL_8G 8 +#define ST_ACCEL_FS_AVL_16G 16 + +/* CUSTOM VALUES FOR SENSOR 1 */ +#define ST_ACCEL_1_WAI_EXP 0x33 +#define ST_ACCEL_1_ODR_ADDR 0x20 +#define ST_ACCEL_1_ODR_MASK 0xf0 +#define ST_ACCEL_1_ODR_N_BIT 4 +#define ST_ACCEL_1_ODR_AVL_1HZ_VAL 0x01 +#define ST_ACCEL_1_ODR_AVL_10HZ_VAL 0x02 +#define ST_ACCEL_1_ODR_AVL_25HZ_VAL 0x03 +#define ST_ACCEL_1_ODR_AVL_50HZ_VAL 0x04 +#define ST_ACCEL_1_ODR_AVL_100HZ_VAL 0x05 +#define ST_ACCEL_1_ODR_AVL_200HZ_VAL 0x06 +#define ST_ACCEL_1_ODR_AVL_400HZ_VAL 0x07 +#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL 0x08 +#define ST_ACCEL_1_FS_N_BIT 2 +#define ST_ACCEL_1_FS_ADDR 0x23 +#define ST_ACCEL_1_FS_MASK 0x30 +#define ST_ACCEL_1_FS_AVL_2_VAL 0x00 +#define ST_ACCEL_1_FS_AVL_4_VAL 0x01 +#define ST_ACCEL_1_FS_AVL_8_VAL 0x02 +#define ST_ACCEL_1_FS_AVL_16_VAL 0x03 +#define ST_ACCEL_1_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1000) +#define ST_ACCEL_1_FS_AVL_4_GAIN IIO_G_TO_M_S_2(2000) +#define ST_ACCEL_1_FS_AVL_8_GAIN IIO_G_TO_M_S_2(4000) +#define ST_ACCEL_1_FS_AVL_16_GAIN IIO_G_TO_M_S_2(12000) +#define ST_ACCEL_1_BDU_ADDR 0x23 +#define ST_ACCEL_1_BDU_MASK 0x80 +#define ST_ACCEL_1_DRDY_IRQ_ADDR 0x22 +#define ST_ACCEL_1_DRDY_IRQ_MASK 0x10 +#define ST_ACCEL_1_MULTIREAD_BIT true + +/* CUSTOM VALUES FOR SENSOR 2 */ +#define ST_ACCEL_2_WAI_EXP 0x49 +#define ST_ACCEL_2_ODR_ADDR 0x20 +#define ST_ACCEL_2_ODR_MASK 0xf0 +#define ST_ACCEL_2_ODR_N_BIT 4 +#define ST_ACCEL_2_ODR_AVL_3HZ_VAL 0x01 +#define ST_ACCEL_2_ODR_AVL_6HZ_VAL 0x02 +#define ST_ACCEL_2_ODR_AVL_12HZ_VAL 0x03 +#define ST_ACCEL_2_ODR_AVL_25HZ_VAL 0x04 +#define ST_ACCEL_2_ODR_AVL_50HZ_VAL 0x05 +#define ST_ACCEL_2_ODR_AVL_100HZ_VAL 0x06 +#define ST_ACCEL_2_ODR_AVL_200HZ_VAL 0x07 +#define ST_ACCEL_2_ODR_AVL_400HZ_VAL 0x08 +#define ST_ACCEL_2_ODR_AVL_800HZ_VAL 0x09 +#define ST_ACCEL_2_ODR_AVL_1600HZ_VAL 0x0a +#define ST_ACCEL_2_FS_N_BIT 3 +#define ST_ACCEL_2_FS_ADDR 0x21 +#define ST_ACCEL_2_FS_MASK 0x38 +#define ST_ACCEL_2_FS_AVL_2_VAL 0X00 +#define ST_ACCEL_2_FS_AVL_4_VAL 0X01 +#define ST_ACCEL_2_FS_AVL_6_VAL 0x02 +#define ST_ACCEL_2_FS_AVL_8_VAL 0x03 +#define ST_ACCEL_2_FS_AVL_16_VAL 0x04 +#define ST_ACCEL_2_FS_AVL_2_GAIN IIO_G_TO_M_S_2(61) +#define ST_ACCEL_2_FS_AVL_4_GAIN IIO_G_TO_M_S_2(122) +#define ST_ACCEL_2_FS_AVL_6_GAIN IIO_G_TO_M_S_2(183) +#define ST_ACCEL_2_FS_AVL_8_GAIN IIO_G_TO_M_S_2(244) +#define ST_ACCEL_2_FS_AVL_16_GAIN IIO_G_TO_M_S_2(732) +#define ST_ACCEL_2_BDU_ADDR 0x20 +#define ST_ACCEL_2_BDU_MASK 0x08 +#define ST_ACCEL_2_DRDY_IRQ_ADDR 0x22 +#define ST_ACCEL_2_DRDY_IRQ_MASK 0x04 +#define ST_ACCEL_2_MULTIREAD_BIT true + +/* CUSTOM VALUES FOR SENSOR 3 */ +#define ST_ACCEL_3_WAI_EXP 0x32 +#define ST_ACCEL_3_ODR_ADDR 0x20 +#define ST_ACCEL_3_ODR_MASK 0x18 +#define ST_ACCEL_3_ODR_N_BIT 2 +#define ST_ACCEL_3_ODR_AVL_50HZ_VAL 0x00 +#define ST_ACCEL_3_ODR_AVL_100HZ_VAL 0x01 +#define ST_ACCEL_3_ODR_AVL_400HZ_VAL 0x02 +#define ST_ACCEL_3_ODR_AVL_1000HZ_VAL 0x03 +#define ST_ACCEL_3_PW_ADDR 0x20 +#define ST_ACCEL_3_PW_MASK 0xe0 +#define ST_ACCEL_3_PW_N_BIT 3 +#define ST_ACCEL_3_FS_N_BIT 2 +#define ST_ACCEL_3_FS_ADDR 0x23 +#define ST_ACCEL_3_FS_MASK 0x30 +#define ST_ACCEL_3_FS_AVL_2_VAL 0X00 +#define ST_ACCEL_3_FS_AVL_4_VAL 0X01 +#define ST_ACCEL_3_FS_AVL_8_VAL 0x03 +#define ST_ACCEL_3_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1000) +#define ST_ACCEL_3_FS_AVL_4_GAIN IIO_G_TO_M_S_2(2000) +#define ST_ACCEL_3_FS_AVL_8_GAIN IIO_G_TO_M_S_2(3900) +#define ST_ACCEL_3_BDU_ADDR 0x23 +#define ST_ACCEL_3_BDU_MASK 0x80 +#define ST_ACCEL_3_DRDY_IRQ_ADDR 0x22 +#define ST_ACCEL_3_DRDY_IRQ_MASK 0x02 +#define ST_ACCEL_3_MULTIREAD_BIT true + +/* CUSTOM VALUES FOR SENSOR 4 */ +#define ST_ACCEL_4_WAI_EXP 0x40 +#define ST_ACCEL_4_ODR_ADDR 0x20 +#define ST_ACCEL_4_ODR_MASK 0xf0 +#define ST_ACCEL_4_ODR_N_BIT 4 +#define ST_ACCEL_4_ODR_AVL_3HZ_VAL 0x01 +#define ST_ACCEL_4_ODR_AVL_6HZ_VAL 0x02 +#define ST_ACCEL_4_ODR_AVL_12HZ_VAL 0x03 +#define ST_ACCEL_4_ODR_AVL_25HZ_VAL 0x04 +#define ST_ACCEL_4_ODR_AVL_50HZ_VAL 0x05 +#define ST_ACCEL_4_ODR_AVL_100HZ_VAL 0x06 +#define ST_ACCEL_4_ODR_AVL_200HZ_VAL 0x07 +#define ST_ACCEL_4_ODR_AVL_400HZ_VAL 0x08 +#define ST_ACCEL_4_ODR_AVL_800HZ_VAL 0x09 +#define ST_ACCEL_4_ODR_AVL_1600HZ_VAL 0x0a +#define ST_ACCEL_4_FS_N_BIT 3 +#define ST_ACCEL_4_FS_ADDR 0x24 +#define ST_ACCEL_4_FS_MASK 0x38 +#define ST_ACCEL_4_FS_AVL_2_VAL 0X00 +#define ST_ACCEL_4_FS_AVL_4_VAL 0X01 +#define ST_ACCEL_4_FS_AVL_6_VAL 0x02 +#define ST_ACCEL_4_FS_AVL_8_VAL 0x03 +#define ST_ACCEL_4_FS_AVL_16_VAL 0x04 +#define ST_ACCEL_4_FS_AVL_2_GAIN IIO_G_TO_M_S_2(61) +#define ST_ACCEL_4_FS_AVL_4_GAIN IIO_G_TO_M_S_2(122) +#define ST_ACCEL_4_FS_AVL_6_GAIN IIO_G_TO_M_S_2(183) +#define ST_ACCEL_4_FS_AVL_8_GAIN IIO_G_TO_M_S_2(244) +#define ST_ACCEL_4_FS_AVL_16_GAIN IIO_G_TO_M_S_2(732) +#define ST_ACCEL_4_BDU_ADDR 0x20 +#define ST_ACCEL_4_BDU_MASK 0x08 +#define ST_ACCEL_4_DRDY_IRQ_ADDR 0x23 +#define ST_ACCEL_4_DRDY_IRQ_MASK 0x80 +#define ST_ACCEL_4_IG1_EN_ADDR 0x23 +#define ST_ACCEL_4_IG1_EN_MASK 0x08 +#define ST_ACCEL_4_MULTIREAD_BIT false + +struct st_accel_odr_available { +unsigned int hz; +u8 value; +}; + +struct st_accel_odr { +u8 addr; +u8 mask; +short num_bit; +struct st_accel_odr_available odr_avl[ST_ACCEL_ODR_LIST_MAX ]; +}; + +struct st_accel_power { +u8 addr; +u8 mask; +unsigned short num_bit; +u8 value_off; +u8 value_on; +}; + +struct st_accel_axis { +u8 addr; +u8 mask; +}; + +struct st_accel_fullscale_available { +unsigned int num; +u8 value; +unsigned int gain; +}; + +struct st_accel_fullscale { +u8 addr; +u8 mask; +unsigned short num_bit; +struct st_accel_fullscale_available fs_avl[ST_ACCEL_FULLSCALE_AVL_MAX ]; +}; + +struct st_accel_bdu { +u8 addr; +u8 mask; +}; + +struct st_accel_interrupt_generator { +u8 en_addr; +u8 latch_mask_addr; +u8 en_mask; +u8 latching_mask; +}; + +struct st_accel_data_ready_irq { +u8 addr; +u8 mask; +struct st_accel_interrupt_generator ig1; +}; + +#define ST_ACCEL_LSM_CHANNELS (index, mod, endian, bits, addr) \ +{ \ + .type = IIO_ACCEL, \ + .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, \ + }, \ +} + +static const struct iio_chan_spec st_accel_12bit_channels[] = { +ST_ACCEL_LSM_CHANNELS (ST_ACCEL_SCAN_X ,IIO_MOD_X ,IIO_LE , +ST_ACCEL_DEFAULT_12_REALBITS ,ST_ACCEL_DEFAULT_OUT_X_L_ADDR ), +ST_ACCEL_LSM_CHANNELS (ST_ACCEL_SCAN_Y ,IIO_MOD_Y ,IIO_LE , +ST_ACCEL_DEFAULT_12_REALBITS ,ST_ACCEL_DEFAULT_OUT_Y_L_ADDR ), +ST_ACCEL_LSM_CHANNELS (ST_ACCEL_SCAN_Z ,IIO_MOD_Z ,IIO_LE , +ST_ACCEL_DEFAULT_12_REALBITS ,ST_ACCEL_DEFAULT_OUT_Z_L_ADDR ), +IIO_CHAN_SOFT_TIMESTAMP (3 ) +}; + +static const struct iio_chan_spec st_accel_16bit_channels[] = { +ST_ACCEL_LSM_CHANNELS (ST_ACCEL_SCAN_X ,IIO_MOD_X ,IIO_LE , +ST_ACCEL_DEFAULT_16_REALBITS ,ST_ACCEL_DEFAULT_OUT_X_L_ADDR ), +ST_ACCEL_LSM_CHANNELS (ST_ACCEL_SCAN_Y ,IIO_MOD_Y ,IIO_LE , +ST_ACCEL_DEFAULT_16_REALBITS ,ST_ACCEL_DEFAULT_OUT_Y_L_ADDR ), +ST_ACCEL_LSM_CHANNELS (ST_ACCEL_SCAN_Z ,IIO_MOD_Z ,IIO_LE , +ST_ACCEL_DEFAULT_16_REALBITS ,ST_ACCEL_DEFAULT_OUT_Z_L_ADDR ), +IIO_CHAN_SOFT_TIMESTAMP (3 ) +}; + +static const struct st_accel_platform_data st_accel_default_pdata = { + .fullscale =ST_ACCEL_FS_AVL_2G , + .sampling_frequency =ST_ACCEL_ODR_AVL_100HZ , +}; + +/** +* struct st_accel_sensors - ST accel sensors list +* @wai: Contents of WhoAmI register.
drivers/iio/accel/st_accel_core.c line 310
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+* @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. +* +*/
Excess white line here and above the */
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+ +static const struct st_accel_sensors { + u8 wai; + struct iio_chan_spec *ch; + struct st_accel_odr odr; + struct st_accel_power pw; + struct st_accel_axis enable_axis; + struct st_accel_fullscale fs; + struct st_accel_bdu bdu; + struct st_accel_data_ready_irq drdy_irq;
drivers/iio/accel/st_accel_core.c line 330
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+bool multi_read_bit; +} st_accel_sensors[] = { + { + .wai = ST_ACCEL_1_WAI_EXP, + .ch = (struct iio_chan_spec *)st_accel_12bit_channels, + .odr = { + .addr = ST_ACCEL_1_ODR_ADDR, + .mask = ST_ACCEL_1_ODR_MASK, + .num_bit = ST_ACCEL_1_ODR_N_BIT, + .odr_avl = {
While sometimes c99 assignment helps with clarity - sometimes it just bloats the code. Personally I'd do these as
.odr_avl = {{ST_ACCEL_ODR_AVL_1HZ, ST_ACCEL_1_ODR_AVL_1HZ_VAL},
{...}
etc
or for that matter take the view that the defines are largely pointless as they are only used here and
just do
.odr_avl = {{1, 0x01},
{10, 0x02},etc.
Or maybe if it fits on the line
.odr_avl = {{ .hz = 1, .value = 0x01 } might be the clearest... Not sure.
Quoted 473 lines
drivers/iio/accel/st_accel_core.c new file +361
-0+ [0] = { + .hz = ST_ACCEL_ODR_AVL_1HZ, + .value = ST_ACCEL_1_ODR_AVL_1HZ_VAL, + }, + [1] = { + .hz = ST_ACCEL_ODR_AVL_10HZ, + .value = ST_ACCEL_1_ODR_AVL_10HZ_VAL, + }, + [2] = { + .hz = ST_ACCEL_ODR_AVL_25HZ, + .value = ST_ACCEL_1_ODR_AVL_25HZ_VAL, + }, + [3] = { + .hz = ST_ACCEL_ODR_AVL_50HZ, + .value = ST_ACCEL_1_ODR_AVL_50HZ_VAL, + }, + [4] = { + .hz = ST_ACCEL_ODR_AVL_100HZ, + .value = ST_ACCEL_1_ODR_AVL_100HZ_VAL, + }, + [5] = { + .hz = ST_ACCEL_ODR_AVL_200HZ, + .value = ST_ACCEL_1_ODR_AVL_200HZ_VAL, + }, + [6] = { + .hz = ST_ACCEL_ODR_AVL_400HZ, + .value = ST_ACCEL_1_ODR_AVL_400HZ_VAL, + }, + [7] = { + .hz = ST_ACCEL_ODR_AVL_1600HZ, + .value = ST_ACCEL_1_ODR_AVL_1600HZ_VAL, + }, + }, + }, + .pw = { + .addr = ST_ACCEL_1_ODR_ADDR, + .mask = ST_ACCEL_1_ODR_MASK, + .num_bit = ST_ACCEL_1_ODR_N_BIT, + .value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_ACCEL_DEFAULT_AXIS_ADDR, + .mask = ST_ACCEL_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = ST_ACCEL_1_FS_ADDR, + .mask = ST_ACCEL_1_FS_MASK, + .num_bit = ST_ACCEL_1_FS_N_BIT, + .fs_avl = { + [0] = { + .num = ST_ACCEL_FS_AVL_2G, + .value = ST_ACCEL_1_FS_AVL_2_VAL, + .gain = ST_ACCEL_1_FS_AVL_2_GAIN, + }, + [1] = { + .num = ST_ACCEL_FS_AVL_4G, + .value = ST_ACCEL_1_FS_AVL_4_VAL, + .gain = ST_ACCEL_1_FS_AVL_4_GAIN, + }, + [2] = { + .num = ST_ACCEL_FS_AVL_8G, + .value = ST_ACCEL_1_FS_AVL_8_VAL, + .gain = ST_ACCEL_1_FS_AVL_8_GAIN, + }, + [3] = { + .num = ST_ACCEL_FS_AVL_16G, + .value = ST_ACCEL_1_FS_AVL_16_VAL, + .gain = ST_ACCEL_1_FS_AVL_16_GAIN, + }, + }, + }, + .bdu = { + .addr = ST_ACCEL_1_BDU_ADDR, + .mask = ST_ACCEL_1_BDU_MASK, + }, + .drdy_irq = { + .addr = ST_ACCEL_1_DRDY_IRQ_ADDR, + .mask = ST_ACCEL_1_DRDY_IRQ_MASK, + }, + .multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT, + }, + { + .wai = ST_ACCEL_2_WAI_EXP, + .ch = (struct iio_chan_spec *)st_accel_16bit_channels, + .odr = { + .addr = ST_ACCEL_2_ODR_ADDR, + .mask = ST_ACCEL_2_ODR_MASK, + .num_bit = ST_ACCEL_2_ODR_N_BIT, + .odr_avl = { + [0] = { + .hz = ST_ACCEL_ODR_AVL_3HZ, + .value = ST_ACCEL_2_ODR_AVL_3HZ_VAL, + }, + [1] = { + .hz = ST_ACCEL_ODR_AVL_6HZ, + .value = ST_ACCEL_2_ODR_AVL_6HZ_VAL, + }, + [2] = { + .hz = ST_ACCEL_ODR_AVL_12HZ, + .value = ST_ACCEL_2_ODR_AVL_12HZ_VAL, + }, + [3] = { + .hz = ST_ACCEL_ODR_AVL_25HZ, + .value = ST_ACCEL_2_ODR_AVL_25HZ_VAL, + }, + [4] = { + .hz = ST_ACCEL_ODR_AVL_50HZ, + .value = ST_ACCEL_2_ODR_AVL_50HZ_VAL, + }, + [5] = { + .hz = ST_ACCEL_ODR_AVL_100HZ, + .value = ST_ACCEL_2_ODR_AVL_100HZ_VAL, + }, + [6] = { + .hz = ST_ACCEL_ODR_AVL_200HZ, + .value = ST_ACCEL_2_ODR_AVL_200HZ_VAL, + }, + [7] = { + .hz = ST_ACCEL_ODR_AVL_400HZ, + .value = ST_ACCEL_2_ODR_AVL_400HZ_VAL, + }, + [8] = { + .hz = ST_ACCEL_ODR_AVL_800HZ, + .value = ST_ACCEL_2_ODR_AVL_800HZ_VAL, + }, + [9] = { + .hz = ST_ACCEL_ODR_AVL_1600HZ, + .value = ST_ACCEL_2_ODR_AVL_1600HZ_VAL, + }, + }, + }, + .pw = { + .addr = ST_ACCEL_2_ODR_ADDR, + .mask = ST_ACCEL_2_ODR_MASK, + .num_bit = ST_ACCEL_2_ODR_N_BIT, + .value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_ACCEL_DEFAULT_AXIS_ADDR, + .mask = ST_ACCEL_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = ST_ACCEL_2_FS_ADDR, + .mask = ST_ACCEL_2_FS_MASK, + .num_bit = ST_ACCEL_2_FS_N_BIT, + .fs_avl = { + [0] = { + .num = ST_ACCEL_FS_AVL_2G, + .value = ST_ACCEL_2_FS_AVL_2_VAL, + .gain = ST_ACCEL_2_FS_AVL_2_GAIN, + }, + [1] = { + .num = ST_ACCEL_FS_AVL_4G, + .value = ST_ACCEL_2_FS_AVL_4_VAL, + .gain = ST_ACCEL_2_FS_AVL_4_GAIN, + }, + [2] = { + .num = ST_ACCEL_FS_AVL_6G, + .value = ST_ACCEL_2_FS_AVL_6_VAL, + .gain = ST_ACCEL_2_FS_AVL_6_GAIN, + }, + [3] = { + .num = ST_ACCEL_FS_AVL_8G, + .value = ST_ACCEL_2_FS_AVL_8_VAL, + .gain = ST_ACCEL_2_FS_AVL_8_GAIN, + }, + [4] = { + .num = ST_ACCEL_FS_AVL_16G, + .value = ST_ACCEL_2_FS_AVL_16_VAL, + .gain = ST_ACCEL_2_FS_AVL_16_GAIN, + }, + }, + }, + .drdy_irq = { + .addr = ST_ACCEL_2_DRDY_IRQ_ADDR, + .mask = ST_ACCEL_2_DRDY_IRQ_MASK, + }, + .bdu = { + .addr = ST_ACCEL_2_BDU_ADDR, + .mask = ST_ACCEL_2_BDU_MASK, + }, + .multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT, + }, + { + .wai = ST_ACCEL_3_WAI_EXP, + .ch = (struct iio_chan_spec *)st_accel_12bit_channels, + .odr = { + .addr = ST_ACCEL_3_ODR_ADDR, + .mask = ST_ACCEL_3_ODR_MASK, + .num_bit = ST_ACCEL_3_ODR_N_BIT, + .odr_avl = { + [0] = { + .hz = ST_ACCEL_ODR_AVL_50HZ, + .value = ST_ACCEL_3_ODR_AVL_50HZ_VAL, + }, + [1] = { + .hz = ST_ACCEL_ODR_AVL_100HZ, + .value = ST_ACCEL_3_ODR_AVL_100HZ_VAL, + }, + [2] = { + .hz = ST_ACCEL_ODR_AVL_400HZ, + .value = ST_ACCEL_3_ODR_AVL_400HZ_VAL, + }, + [3] = { + .hz = ST_ACCEL_ODR_AVL_1000HZ, + .value = ST_ACCEL_3_ODR_AVL_1000HZ_VAL, + }, + }, + }, + .pw = { + .addr = ST_ACCEL_3_PW_ADDR, + .mask = ST_ACCEL_3_PW_MASK, + .num_bit = ST_ACCEL_3_PW_N_BIT, + .value_on = ST_ACCEL_DEFAULT_POWER_ON_VALUE, + .value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_ACCEL_DEFAULT_AXIS_ADDR, + .mask = ST_ACCEL_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = ST_ACCEL_3_FS_ADDR, + .mask = ST_ACCEL_3_FS_MASK, + .num_bit = ST_ACCEL_3_FS_N_BIT, + .fs_avl = { + [0] = { + .num = ST_ACCEL_FS_AVL_2G, + .value = ST_ACCEL_3_FS_AVL_2_VAL, + .gain = ST_ACCEL_3_FS_AVL_2_GAIN, + }, + [1] = { + .num = ST_ACCEL_FS_AVL_4G, + .value = ST_ACCEL_3_FS_AVL_4_VAL, + .gain = ST_ACCEL_3_FS_AVL_4_GAIN, + }, + [2] = { + .num = ST_ACCEL_FS_AVL_8G, + .value = ST_ACCEL_3_FS_AVL_8_VAL, + .gain = ST_ACCEL_3_FS_AVL_8_GAIN, + }, + }, + }, + .bdu = { + .addr = ST_ACCEL_3_BDU_ADDR, + .mask = ST_ACCEL_3_BDU_MASK, + }, + .drdy_irq = { + .addr = ST_ACCEL_3_DRDY_IRQ_ADDR, + .mask = ST_ACCEL_3_DRDY_IRQ_MASK, + }, + .multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT, + }, + { + .wai = ST_ACCEL_4_WAI_EXP, + .ch = (struct iio_chan_spec *)st_accel_16bit_channels, + .odr = { + .addr = ST_ACCEL_4_ODR_ADDR, + .mask = ST_ACCEL_4_ODR_MASK, + .num_bit = ST_ACCEL_4_ODR_N_BIT, + .odr_avl = { + [0] = { + .hz = ST_ACCEL_ODR_AVL_3HZ, + .value = ST_ACCEL_4_ODR_AVL_3HZ_VAL, + }, + [1] = { + .hz = ST_ACCEL_ODR_AVL_6HZ, + .value = ST_ACCEL_4_ODR_AVL_6HZ_VAL, + }, + [2] = { + .hz = ST_ACCEL_ODR_AVL_12HZ, + .value = ST_ACCEL_4_ODR_AVL_12HZ_VAL, + }, + [3] = { + .hz = ST_ACCEL_ODR_AVL_25HZ, + .value = ST_ACCEL_4_ODR_AVL_25HZ_VAL, + }, + [4] = { + .hz = ST_ACCEL_ODR_AVL_50HZ, + .value = ST_ACCEL_4_ODR_AVL_50HZ_VAL, + }, + [5] = { + .hz = ST_ACCEL_ODR_AVL_100HZ, + .value = ST_ACCEL_4_ODR_AVL_100HZ_VAL, + }, + [6] = { + .hz = ST_ACCEL_ODR_AVL_200HZ, + .value = ST_ACCEL_4_ODR_AVL_200HZ_VAL, + }, + [7] = { + .hz = ST_ACCEL_ODR_AVL_400HZ, + .value = ST_ACCEL_4_ODR_AVL_400HZ_VAL, + }, + [8] = { + .hz = ST_ACCEL_ODR_AVL_800HZ, + .value = ST_ACCEL_4_ODR_AVL_800HZ_VAL, + }, + [9] = { + .hz = ST_ACCEL_ODR_AVL_1600HZ, + .value = ST_ACCEL_4_ODR_AVL_1600HZ_VAL, + }, + }, + }, + .pw = { + .addr = ST_ACCEL_4_ODR_ADDR, + .mask = ST_ACCEL_4_ODR_MASK, + .num_bit = ST_ACCEL_4_ODR_N_BIT, + .value_off = ST_ACCEL_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_ACCEL_DEFAULT_AXIS_ADDR, + .mask = ST_ACCEL_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = ST_ACCEL_4_FS_ADDR, + .mask = ST_ACCEL_4_FS_MASK, + .num_bit = ST_ACCEL_4_FS_N_BIT, + .fs_avl = { + [0] = { + .num = ST_ACCEL_FS_AVL_2G, + .value = ST_ACCEL_4_FS_AVL_2_VAL, + .gain = ST_ACCEL_4_FS_AVL_2_GAIN, + }, + [1] = { + .num = ST_ACCEL_FS_AVL_4G, + .value = ST_ACCEL_4_FS_AVL_4_VAL, + .gain = ST_ACCEL_4_FS_AVL_4_GAIN, + }, + [2] = { + .num = ST_ACCEL_FS_AVL_6G, + .value = ST_ACCEL_4_FS_AVL_6_VAL, + .gain = ST_ACCEL_4_FS_AVL_6_GAIN, + }, + [3] = { + .num = ST_ACCEL_FS_AVL_8G, + .value = ST_ACCEL_4_FS_AVL_8_VAL, + .gain = ST_ACCEL_4_FS_AVL_8_GAIN, + }, + [4] = { + .num = ST_ACCEL_FS_AVL_16G, + .value = ST_ACCEL_4_FS_AVL_16_VAL, + .gain = ST_ACCEL_4_FS_AVL_16_GAIN, + }, + }, + }, + .bdu = { + .addr = ST_ACCEL_4_BDU_ADDR, + .mask = ST_ACCEL_4_BDU_MASK, + }, + .drdy_irq = { + .addr = ST_ACCEL_4_DRDY_IRQ_ADDR, + .mask = ST_ACCEL_4_DRDY_IRQ_MASK, + .ig1 = { + .en_addr = ST_ACCEL_4_IG1_EN_ADDR, + .en_mask = ST_ACCEL_4_IG1_EN_MASK, + }, + }, + .multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT, + }, +}; + +static int st_accel_write_data_with_mask(struct iio_dev *indio_dev,u8
reg_addr,drivers/iio/accel/st_accel_core.c new file +111
-0+ u8 mask, short num_bit, u8 data) +{ + int err; +u8 prev_data; +u8 new_data; +struct st_accel_data *adata =iio_priv (indio_dev); + + err = adata->read_byte (adata, reg_addr, &prev_data); +if (err <0 ) + goto st_accel_write_data_with_mask_error; + + new_data = ((prev_data & (~mask)) | ((data <<__ffs (mask)) & mask)); + err = adata->write_byte (adata, reg_addr, new_data); + +st_accel_write_data_with_mask_error: +return err; +} + +static int st_accel_match_odr (const struct st_accel_sensors *sensor, +unsigned int odr,struct st_accel_odr_available *odr_out) +{ +int i, ret = -EINVAL ; + +for (i =0 ; i <ARRAY_SIZE (sensor->odr.odr_avl); i++) { +if (sensor->odr.odr_avl[i].hz == odr) { + odr_out->hz = sensor->odr.odr_avl[i].hz; + odr_out->value = sensor->odr.odr_avl[i].value; + ret =0 ; +break ; + } + } + +return ret; +} + +static int st_accel_match_fs (const struct st_accel_sensors *sensor, +unsigned int fs,struct st_accel_fullscale_available *fs_out) +{ +int i, ret = -EINVAL ; + +for (i =0 ; i <ARRAY_SIZE (sensor->fs.fs_avl); i++) { +if (sensor->fs.fs_avl[i].num == fs) { + 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; + ret =0 ; +break ; + } + } + +return ret; +} + +static int st_accel_match_scale (const struct st_accel_sensors *sensor, +int scale,struct st_accel_fullscale_available *fs_out) +{ +int i, ret = -EINVAL ; + +for (i =0 ; i <ARRAY_SIZE (sensor->fs.fs_avl); i++) { +if (sensor->fs.fs_avl[i].gain == scale) { + 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; + ret =0 ; +break ; + } + } + +return ret; +} + +int st_accel_set_dataready_irq (struct iio_dev *indio_dev,bool enable) +{ +int err; +struct st_accel_data *adata; + + adata =iio_priv (indio_dev); +if (st_accel_sensors[adata->index].drdy_irq.ig1.en_addr >0 ) { + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].drdy_irq.ig1.en_addr, + st_accel_sensors[adata->index].drdy_irq.ig1.en_mask,1 , + (int )enable); +if (err <0 ) + goto st_accel_set_dataready_irq_error; + } + +if (st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr >0 ) { + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].drdy_irq.ig1.latch_mask_addr, + st_accel_sensors[adata->index].drdy_irq.ig1.latching_mask,1 , + (int )enable); +if (err <0 ) + goto st_accel_set_dataready_irq_error; + } + + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].drdy_irq.addr, + st_accel_sensors[adata->index].drdy_irq.mask,1 , (int )enable); +if (err <0 ) + goto st_accel_set_dataready_irq_error; + +st_accel_set_dataready_irq_error: +return err; +} +EXPORT_SYMBOL (st_accel_set_dataready_irq); + +static int st_accel_set_bdu (struct iio_dev *indio_dev, +const struct st_accel_bdu *bdu,bool value) +{ +return st_accel_write_data_with_mask (indio_dev, bdu->addr, bdu->mask, +1 , (u8 )value);
drivers/iio/accel/st_accel_core.c
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+} + +static int st_accel_set_odr (struct iio_dev *indio_dev, +struct st_accel_odr_available *odr_available) +{ +int err; +struct st_accel_data *adata =iio_priv (indio_dev); + + if ((st_accel_sensors[adata->index].odr.addr == + st_accel_sensors[adata->index].pw.addr) &&
Alignment is rather messed up here I think...
Quoted 39 lines
drivers/iio/accel/st_accel_core.c new file +39
-0+ (st_accel_sensors[adata->index].odr.mask == + st_accel_sensors[adata->index].pw.mask)) { +if (adata->enabled == true) { + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].odr.addr, + st_accel_sensors[adata->index].odr.mask, + st_accel_sensors[adata->index].odr.num_bit, + odr_available->value); +if (err <0 ) + goto sc_accel_set_odr_error; + }else { + adata->odr = odr_available->hz; + err =0 ; + } + } else { + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].odr.addr, + st_accel_sensors[adata->index].odr.mask, + st_accel_sensors[adata->index].odr.num_bit, + odr_available->value); +if (err <0 ) + goto sc_accel_set_odr_error; + } + +sc_accel_set_odr_error: +return err; +} + +int st_accel_set_axis_enable (struct iio_dev *indio_dev,u8 axis_enable) +{ +int err; +struct st_accel_data *adata =iio_priv (indio_dev); + + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].enable_axis.addr, + st_accel_sensors[adata->index].enable_axis.mask, +ST_ACCEL_DEFAULT_AXIS_N_BIT , axis_enable); + +return err;
drivers/iio/accel/st_accel_core.c
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+} + +static int st_accel_set_enable (struct iio_dev *indio_dev,bool enable) +{ +int err; +bool found; +u8 tmp_value; +struct st_accel_odr_available odr_out; +struct st_accel_data *adata =iio_priv (indio_dev); +
switch on a boolean seems overly complex. I'd just do and if / else pair
Quoted 28 lines
drivers/iio/accel/st_accel_core.c new file +28
-0+switch (enable) { +case true: + found = false; + tmp_value = st_accel_sensors[adata->index].pw.value_on; +if ((st_accel_sensors[adata->index].odr.addr == + st_accel_sensors[adata->index].pw.addr) && + (st_accel_sensors[adata->index].odr.mask == + st_accel_sensors[adata->index].pw.mask)) { + err =st_accel_match_odr ( + &st_accel_sensors[adata->index], adata->odr, + &odr_out); +if (err <0 ) + goto set_enable_error; + tmp_value = odr_out.value; + found = true; + } + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].pw.addr, + st_accel_sensors[adata->index].pw.mask, + st_accel_sensors[adata->index].pw.num_bit, + tmp_value); +if (err <0 ) + goto set_enable_error; + adata->enabled = true; +if (found) + adata->odr = odr_out.hz; +break ; +case false:
drivers/iio/accel/st_accel_core.c
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+ err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].pw.addr, + st_accel_sensors[adata->index].pw.mask, + st_accel_sensors[adata->index].pw.num_bit, + st_accel_sensors[adata->index].pw.value_off); +if (err <0 ) + goto set_enable_error; + adata->enabled = false; +break ; +default :
default on a boolean switch?
Quoted 48 lines
drivers/iio/accel/st_accel_core.c new file +48
-0+ err = -EINVAL ; + goto set_enable_error; + } + +set_enable_error: +return err; +} + +static int st_accel_set_fullscale (struct iio_dev *indio_dev, +struct st_accel_fullscale_available *fs_avl) +{ +int err; +struct st_accel_data *adata =iio_priv (indio_dev); + + err =st_accel_write_data_with_mask (indio_dev, + st_accel_sensors[adata->index].fs.addr, + st_accel_sensors[adata->index].fs.mask, + st_accel_sensors[adata->index].fs.num_bit, + fs_avl->value); +if (err <0 ) + goto st_accel_set_fullscale_error; + + adata->fullscale = fs_avl->num; + adata->gain = fs_avl->gain; +return err; + +st_accel_set_fullscale_error: +dev_err (&indio_dev->dev,"failed to set new fullscale.\n" ); +return err; +} + +static int st_accel_read_raw (struct iio_dev *indio_dev, +struct iio_chan_spec const *ch,int *val, +int *val2,long mask) +{ +int err; +u8 outdata[ST_ACCEL_BYTE_FOR_CHANNEL ]; +struct st_accel_data *adata =iio_priv (indio_dev); + +switch (mask) { +case IIO_CHAN_INFO_RAW : +mutex_lock (&indio_dev->mlock); +if (indio_dev->currentmode ==INDIO_BUFFER_TRIGGERED ) { + err = -EBUSY ; + goto read_error; + }else { +if (!adata->enabled) { + err = -EIO ;
drivers/iio/accel/st_accel_core.c
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+ goto read_error; + }else { + err = adata->read_multiple_byte (adata, + ch->address,ST_ACCEL_BYTE_FOR_CHANNEL , + outdata); +if (err <0 ) + goto read_error; + + *val = ((s16)(((s16)(outdata[1 ]) <<8 ) + | outdata[0 ])) >> ch->scan_type.shift;
Why not standard endian conversion functions?
Quoted 182 lines
drivers/iio/accel/st_accel_core.c new file +182
-0+ } + } +mutex_unlock (&indio_dev->mlock); +return IIO_VAL_INT ; +case IIO_CHAN_INFO_SCALE : + *val =0 ; + *val2 = adata->gain; +return IIO_VAL_INT_PLUS_MICRO ; +default : +return -EINVAL ; + } + +read_error: +mutex_unlock (&indio_dev->mlock); +return err; +} + +static int st_accel_write_raw (struct iio_dev *indio_dev, +struct iio_chan_spec const *chan,int val,int val2,long mask) +{ +int err; +struct st_accel_fullscale_available fs_out; +struct st_accel_data *adata =iio_priv (indio_dev); + +switch (mask) { +case IIO_CHAN_INFO_SCALE : + err =st_accel_match_scale (&st_accel_sensors[adata->index], + val2, &fs_out); +if (err <0 ) + goto write_error; + + err =st_accel_set_fullscale (indio_dev, &fs_out); +break ; +default : + err = -EINVAL ; + } + +write_error: +return err; +} + +static int st_accel_check_device_list (struct iio_dev *indio_dev,u8 wai) +{ +int i; +struct st_accel_data *adata =iio_priv (indio_dev); + +for (i =0 ; i <ARRAY_SIZE (st_accel_sensors); i++) { +if (st_accel_sensors[i].wai == wai) +break ; + } +if (i ==ARRAY_SIZE (st_accel_sensors)) + goto check_device_error; + + adata->index = i; + +return i; + +check_device_error: +dev_err (&indio_dev->dev,"device not supported -> wai (0x%x).\n" , wai); +return -ENODEV ; +} + +static int st_accel_get_wai_device (struct iio_dev *indio_dev,u8 reg_addr, +u8 *value) +{ +int ret; +struct st_accel_data *adata =iio_priv (indio_dev); + + ret = adata->read_byte (adata, reg_addr, value); +if (ret <0 ) + goto read_byte_wai_error; + +return 0 ; + +read_byte_wai_error: +dev_err (&indio_dev->dev, +"failed to read WhoAmI (register 0x%x).\n" , reg_addr); +return -EIO ; +} + +static ssize_t st_accel_sysfs_set_sampling_frequency (struct device *dev, +struct device_attribute *attr,const char *buf,size_t size) +{ +int err; +unsigned int freq; +struct st_accel_odr_available odr_out; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + + err =kstrtoint (buf,10 , &freq); +if (err <0 ) + goto conversion_error; + +mutex_lock (&indio_dev->mlock); + err =st_accel_match_odr (&st_accel_sensors[adata->index], + freq, &odr_out); +if (err <0 ) + goto st_accel_sysfs_set_sampling_frequency_error; + + err =st_accel_set_odr (indio_dev, &odr_out); +if (err <0 ) { +dev_err (&indio_dev->dev, +"failed to set sampling frequency to %d.\n" , freq); + goto st_accel_sysfs_set_sampling_frequency_error; + } + adata->odr = odr_out.hz; + +st_accel_sysfs_set_sampling_frequency_error: +mutex_unlock (&indio_dev->mlock); +conversion_error: +return size; +} + +static ssize_t st_accel_sysfs_get_sampling_frequency (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + +return sprintf (buf,"%d\n" , adata->odr); +} + +static ssize_t st_accel_sysfs_set_powerdown (struct device *dev, +struct device_attribute *attr,const char *buf,size_t size) +{ +int err; +bool powerdown; +struct iio_dev *indio_dev =dev_get_drvdata (dev); + + err =strtobool (buf, &powerdown); +if (err <0 ) + goto set_enable_error; + +mutex_lock (&indio_dev->mlock); + err =st_accel_set_enable (indio_dev, ~powerdown); +if (err <0 ) +dev_err (&indio_dev->dev, +"failed to set powerdown to %d.\n" , (int )(powerdown)); +mutex_unlock (&indio_dev->mlock); + +set_enable_error: +return size; +} + +static ssize_t st_accel_sysfs_get_powerdown (struct device *dev, +struct device_attribute *attr,char *buf) +{ +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + +return sprintf (buf,"%d\n" , (int )(!adata->enabled)); +} + +static ssize_t st_accel_sysfs_scale_available (struct device *dev, +struct device_attribute *attr,char *buf) +{ +int i, len =0 ; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); + +mutex_lock (&indio_dev->mlock); +for (i =0 ; i <ARRAY_SIZE (st_accel_sensors[adata->index].fs.fs_avl); + i++) { +if (st_accel_sensors[adata->index].fs.fs_avl[i].num ==0 ) +break ; + + len +=sprintf (buf+len,"0.%06u " , + st_accel_sensors[adata->index].fs.fs_avl[i].gain); + } +mutex_unlock (&indio_dev->mlock); + buf[len-1 ] ='\n' ; + +return len; +} + +static ssize_t st_accel_sysfs_sampling_frequency_available (struct device *dev, +struct device_attribute *attr,char *buf) +{ +int i, len =0 ; +struct iio_dev *indio_dev =dev_get_drvdata (dev); +struct st_accel_data *adata =iio_priv (indio_dev); +
drivers/iio/accel/st_accel_core.c
Quoted 10 lines
drivers/iio/accel/st_accel_core.c new file +10
-0+mutex_lock (&indio_dev->mlock); +for (i =0 ; i <ARRAY_SIZE (st_accel_sensors[adata->index].odr.odr_avl); + i++) { +if (st_accel_sensors[adata->index].odr.odr_avl[i].hz ==0 ) +break ; + + len +=sprintf (buf+len,"%d " , + st_accel_sensors[adata->index].odr.odr_avl[i].hz); + } +mutex_unlock (&indio_dev->mlock);
spaces around -
Quoted 157 lines
drivers/iio/accel/st_accel_core.c new file +142
-0+ buf[len-1 ] ='\n' ; + +return len; +} + +static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO, + st_accel_sysfs_sampling_frequency_available, NULL , 0); + +static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO, + st_accel_sysfs_scale_available, NULL , 0); + +static IIO_DEVICE_ATTR(powerdown, S_IWUSR | S_IRUGO, + st_accel_sysfs_get_powerdown, st_accel_sysfs_set_powerdown , 0); + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + st_accel_sysfs_get_sampling_frequency, + st_accel_sysfs_set_sampling_frequency); + +static struct attribute *st_accel_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_accel_scale_available.dev_attr.attr, + &iio_dev_attr_powerdown.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, +NULL , +}; + +static const struct attribute_group st_accel_attribute_group = { + .attrs = st_accel_attributes, +}; + +static const struct iio_info acc_info = { + .driver_module =THIS_MODULE , + .attrs = &st_accel_attribute_group, + .read_raw = &st_accel_read_raw, + .write_raw = &st_accel_write_raw, +}; + +static int st_accel_init_sensor (struct iio_dev *indio_dev) +{ +int err; +struct st_accel_odr_available odr_out; +struct st_accel_fullscale_available fs_out; +struct st_accel_data *adata =iio_priv (indio_dev); + + err =st_accel_set_enable (indio_dev, false); +if (err <0 ) + goto init_error; + + err =st_accel_set_axis_enable (indio_dev,ST_ACCEL_ENABLE_ALL_CHANNELS ); +if (err <0 ) + goto init_error; + +st_accel_match_fs (&st_accel_sensors[adata->index], + adata->fullscale, &fs_out); + err =st_accel_set_fullscale (indio_dev, &fs_out); +if (err <0 ) + goto init_error; + +st_accel_match_odr (&st_accel_sensors[adata->index], + adata->odr, &odr_out); + err =st_accel_set_odr (indio_dev, &odr_out); +if (err <0 ) + goto init_error; + + err =st_accel_set_bdu (indio_dev, + &st_accel_sensors[adata->index].bdu, true); + +init_error: +return err; +} + +int st_accel_iio_probe (struct iio_dev *indio_dev,int irq) +{ +int err; +u8 wai; +struct st_accel_data *adata =iio_priv (indio_dev); +struct st_accel_platform_data *pdata; + + indio_dev->modes =INDIO_DIRECT_MODE ; + indio_dev->info = &acc_info; + + err =st_accel_get_wai_device (indio_dev, +ST_ACCEL_DEFAULT_WAI_ADDRESS , &wai); +if (err <0 ) + goto st_accel_iio_probe_error; + + err =st_accel_check_device_list (indio_dev, wai); +if (err <0 ) + goto st_accel_iio_probe_error; + + adata->multiread_bit = st_accel_sensors[adata->index].multi_read_bit; + indio_dev->channels = st_accel_sensors[adata->index].ch; + indio_dev->num_channels =ST_ACCEL_NUMBER_ALL_CHANNELS ; + pdata = adata->dev->platform_data; +if (!pdata) + pdata = (struct st_accel_platform_data *) + &st_accel_default_pdata; + + adata->fullscale = pdata->fullscale; + adata->odr = pdata->sampling_frequency; + + err =st_accel_init_sensor (indio_dev); +if (err <0 ) + goto st_accel_iio_probe_error; + + err =st_accel_allocate_ring (indio_dev); +if (err <0 ) + goto st_accel_iio_probe_error; + +if (irq >0 ) { + err =st_accel_probe_trigger (indio_dev, irq); +if (err <0 ) + goto acc_probe_trigger_error; + } + + err =iio_device_register (indio_dev); +if (err) + goto iio_device_register_error; + +return err; + +iio_device_register_error: +st_accel_remove_trigger (indio_dev, irq); +acc_probe_trigger_error: +st_accel_deallocate_ring (indio_dev); +st_accel_iio_probe_error: +return err; +} +EXPORT_SYMBOL (st_accel_iio_probe); + +void st_accel_iio_remove (struct iio_dev *indio_dev,int irq) +{ +iio_device_unregister (indio_dev); +st_accel_remove_trigger (indio_dev, irq); +st_accel_deallocate_ring (indio_dev); +iio_device_free (indio_dev); +} +EXPORT_SYMBOL (st_accel_iio_remove); + +MODULE_AUTHOR ("Denis Ciocca <[email protected]>" ); +MODULE_DESCRIPTION ("STMicroelectronics accelerometers driver" ); +MODULE_LICENSE ("GPL v2" );drivers/iio/accel/st_accel_i2c.c new file +9
-0@@ -0,0 +1,129 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/
drivers/iio/accel/st_accel_i2c.c line 19
Quoted 10 lines
drivers/iio/accel/st_accel_i2c.c new file +10
-0+ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> + +#include <linux/iio/accel/st_accel.h> +
1 line is enought here.
Quoted 66 lines
drivers/iio/accel/st_accel_i2c.c new file +66
-0+ +#define ST_ACCEL_I2C_MULTIREAD 0x80 + +static int st_accel_i2c_read_byte (struct st_accel_data *adata, +u8 reg_addr,u8 *res_byte) +{ +int err; + + err =i2c_smbus_read_byte_data (to_i2c_client (adata->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; +} + +static int st_accel_i2c_read_multiple_byte (struct st_accel_data *adata, +u8 reg_addr,int len,u8 *data) +{ +if (adata->multiread_bit == true) + reg_addr |=ST_ACCEL_I2C_MULTIREAD ; + +return i2c_smbus_read_i2c_block_data (to_i2c_client (adata->dev), + reg_addr, len, data); +} + +static int st_accel_i2c_write_byte (struct st_accel_data *adata, +u8 reg_addr,u8 data) +{ +return i2c_smbus_write_byte_data (to_i2c_client (adata->dev), + reg_addr, data); +} + +static int __devinit st_accel_i2c_probe (struct i2c_client *client, +const struct i2c_device_id *id) +{ +struct iio_dev *indio_dev; +struct st_accel_data *adata; +int err; + + indio_dev =iio_device_alloc (sizeof (*adata)); +if (indio_dev ==NULL ) { + err = -ENOMEM ; + goto iio_device_alloc_error; + } + + adata =iio_priv (indio_dev); + adata->dev = &client->dev; +i2c_set_clientdata (client, indio_dev); + + indio_dev->dev.parent = &client->dev; + indio_dev->name = client->name; + + adata->read_byte = st_accel_i2c_read_byte; + adata->write_byte = st_accel_i2c_write_byte; + adata->read_multiple_byte = st_accel_i2c_read_multiple_byte; + + err =st_accel_iio_probe (indio_dev, client->irq); +if (err <0 ) + goto acc_iio_default_error; + +return 0 ; + +acc_iio_default_error:
drivers/iio/accel/st_accel_i2c.c line 95
Quoted 10 lines
drivers/iio/accel/st_accel_i2c.c new file +10
-0+iio_device_free (indio_dev); +iio_device_alloc_error: +return err; +} + +static int __devexit st_accel_i2c_remove(struct i2c_client *client) +{ +struct iio_dev *indio_dev =i2c_get_clientdata (client); + +st_accel_iio_remove (indio_dev, client->irq);
blank line here would be good.
Quoted 66 lines
drivers/iio/accel/st_accel_i2c.c new file +34
-0+return 0 ; +} + +static const struct i2c_device_id st_accel_id_table[] = { + {LSM303DLH_ACCEL_DEV_NAME }, + {LSM303DLHC_ACCEL_DEV_NAME }, + {LIS3DH_ACCEL_DEV_NAME }, + {LSM330D_ACCEL_DEV_NAME }, + {LSM330DL_ACCEL_DEV_NAME }, + {LSM330DLC_ACCEL_DEV_NAME }, + {LSM303D_ACCEL_DEV_NAME }, + {LSM9DS0_ACCEL_DEV_NAME }, + {LIS331DLH_ACCEL_DEV_NAME }, + {LSM303DL_ACCEL_DEV_NAME }, + {LSM303DLM_ACCEL_DEV_NAME }, + {LSM330_ACCEL_DEV_NAME }, + {}, +}; +MODULE_DEVICE_TABLE (i2c, st_accel_id_table); + +static struct i2c_driver st_accel_driver = { + .driver = { + .owner =THIS_MODULE , + .name ="st-accel-i2c" , + }, + .probe = st_accel_i2c_probe, + .remove =__devexit_p (st_accel_i2c_remove), + .id_table = st_accel_id_table, +}; +module_i2c_driver (st_accel_driver); + +MODULE_AUTHOR ("Denis Ciocca <[email protected]>" ); +MODULE_DESCRIPTION ("STMicroelectronics accelerometers i2c driver" ); +MODULE_LICENSE ("GPL v2" );drivers/iio/accel/st_accel_spi.c new file +26
-0@@ -0,0 +1,188 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> + +#include <linux/iio/accel/st_accel.h> + + +#define ACC_SPI_READ 0x80; +#define ACC_SPI_MULTIREAD 0xc0 + +static int st_accel_spi_read_byte (struct st_accel_data *adata, +u8 reg_addr,u8 *res_byte) +{
drivers/iio/accel/st_accel_spi.c line 36
Quoted 10 lines
drivers/iio/accel/st_accel_spi.c new file +10
-0+struct spi_message msg; +int err; +u8 tx; + +struct spi_transfer xfers[] = { + { + .tx_buf = &tx, + .bits_per_word =8 , + .len =1 , + },
}, {
Quoted 19 lines
drivers/iio/accel/st_accel_spi.c new file +19
-0+ { + .rx_buf = res_byte, + .bits_per_word = 8, + .len = 1, + } + }; + + tx = reg_addr |ACC_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 (adata->dev), &msg); + +return err; +} + +static int st_accel_spi_read_multiple_byte (struct st_accel_data *adata, +u8 reg_addr,int len,u8 *data) +{
drivers/iio/accel/st_accel_spi.c line 65
Quoted 10 lines
drivers/iio/accel/st_accel_spi.c new file +10
-0+struct spi_message msg; +int err; +u8 tx; + +struct spi_transfer xfers[] = { + { + .tx_buf = &tx, + .bits_per_word =8 , + .len =1 , + },
}, {
Quoted 22 lines
drivers/iio/accel/st_accel_spi.c new file +22
-0+ { + .rx_buf = data, + .bits_per_word = 8, + .len = len, + } + }; + +if (adata->multiread_bit == true) + tx = reg_addr |ACC_SPI_MULTIREAD ; +else + tx = reg_addr |ACC_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 (adata->dev), &msg); +if (err) + goto acc_spi_read_multiple_byte_error; + +return len; + +acc_spi_read_multiple_byte_error:
drivers/iio/accel/st_accel_spi.c line 97
Quoted 10 lines
drivers/iio/accel/st_accel_spi.c new file +10
-0+return err; +} + +static int st_accel_spi_write_byte (struct st_accel_data *adata, +u8 reg_addr,u8 data) +{ +struct spi_message msg; +int err; +u8 tx[2 ]; +
No benefit to array of structures so just have one of them.
Quoted 47 lines
drivers/iio/accel/st_accel_spi.c new file +47
-0+struct spi_transfer xfers[] = { + { + .tx_buf = tx, + .bits_per_word =8 , + .len =2 , + } + }; + + tx[0 ] = reg_addr; + tx[1 ] = data; +spi_message_init (&msg); +spi_message_add_tail (&xfers[0 ], &msg); + err =spi_sync (to_spi_device (adata->dev), &msg); + +return err; +} + +static int __devinit st_accel_spi_probe (struct spi_device *spi) +{ +struct iio_dev *indio_dev; +struct st_accel_data *adata; +int err; + + indio_dev =iio_device_alloc (sizeof (*adata)); +if (indio_dev ==NULL ) { + err = -ENOMEM ; + goto iio_device_alloc_error; + } + + adata =iio_priv (indio_dev); + adata->dev = &spi->dev; +spi_set_drvdata (spi, indio_dev); + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi->modalias; + + adata->read_byte = st_accel_spi_read_byte; + adata->write_byte = st_accel_spi_write_byte; + adata->read_multiple_byte = st_accel_spi_read_multiple_byte; + + err =st_accel_iio_probe (indio_dev, spi->irq); +if (err <0 ) + goto acc_iio_default_error; + +return 0 ; + +acc_iio_default_error:
drivers/iio/accel/st_accel_spi.c line 154
Quoted 10 lines
drivers/iio/accel/st_accel_spi.c new file +10
-0+iio_device_free (indio_dev); +iio_device_alloc_error: +return err; +} + +static int __devexit st_accel_spi_remove(struct spi_device *spi) +{ +struct iio_dev *indio_dev =spi_get_drvdata (spi); + +st_accel_iio_remove (indio_dev, spi->irq);
Additional blank line would be good here.
Quoted 51 lines
drivers/iio/accel/st_accel_spi.c new file +34
-0+return 0 ; +} + +static const struct spi_device_id st_accel_id_table[] = { + {LSM303DLH_ACCEL_DEV_NAME }, + {LSM303DLHC_ACCEL_DEV_NAME }, + {LIS3DH_ACCEL_DEV_NAME }, + {LSM330D_ACCEL_DEV_NAME }, + {LSM330DL_ACCEL_DEV_NAME }, + {LSM330DLC_ACCEL_DEV_NAME }, + {LSM303D_ACCEL_DEV_NAME }, + {LSM9DS0_ACCEL_DEV_NAME }, + {LIS331DLH_ACCEL_DEV_NAME }, + {LSM303DL_ACCEL_DEV_NAME }, + {LSM303DLM_ACCEL_DEV_NAME }, + {LSM330_ACCEL_DEV_NAME }, + {}, +}; +MODULE_DEVICE_TABLE (spi, st_accel_id_table); + +static struct spi_driver st_accel_driver = { + .driver = { + .owner =THIS_MODULE , + .name ="st-accel-spi" , + }, + .probe = st_accel_spi_probe, + .remove =__devexit_p (st_accel_spi_remove), + .id_table = st_accel_id_table, +}; +module_spi_driver (st_accel_driver); + +MODULE_AUTHOR ("Denis Ciocca <[email protected]>" ); +MODULE_DESCRIPTION ("STMicroelectronics accelerometers spi driver" ); +MODULE_LICENSE ("GPL v2" );drivers/iio/accel/st_accel_trigger.c new file +10
-0@@ -0,0 +1,84 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/ +
drivers/iio/accel/st_accel_trigger.c line 20
Quoted 10 lines
drivers/iio/accel/st_accel_trigger.c new file +10
-0+#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> + +#include <linux/iio/accel/st_accel.h> +
Excess blank lines.
Quoted 71 lines
drivers/iio/accel/st_accel_trigger.c new file +64
-0+ +static int st_accel_trig_acc_set_state (struct iio_trigger *trig,bool state) +{ +struct iio_dev *indio_dev = trig->private_data; +return st_accel_set_dataready_irq (indio_dev, state); +} + +static const struct iio_trigger_ops st_accel_trigger_ops = { + .owner =THIS_MODULE , + .set_trigger_state = &st_accel_trig_acc_set_state, +}; + +int st_accel_probe_trigger (struct iio_dev *indio_dev,int irq) +{ +int err; +struct st_accel_data *adata =iio_priv (indio_dev); + + adata->trig =iio_trigger_alloc ("%s-trigger" , indio_dev->name); +if (adata->trig ==NULL ) { + err = -ENOMEM ; +dev_err (&indio_dev->dev,"failed to allocate iio trigger.\n" ); + goto iio_trigger_alloc_error; + } + + err =request_threaded_irq (irq, + iio_trigger_generic_data_rdy_poll, +NULL , +IRQF_TRIGGER_RISING , + adata->trig->name, + adata->trig); +if (err) + goto request_irq_error; + + adata->trig->private_data = indio_dev; + adata->trig->ops = &st_accel_trigger_ops; + adata->trig->dev.parent = adata->dev; + + err =iio_trigger_register (adata->trig); +if (err <0 ) { +dev_err (&indio_dev->dev,"failed to register iio trigger.\n" ); + goto iio_trigger_register_error; + } + indio_dev->trig = adata->trig; + +return 0 ; + +iio_trigger_register_error: +free_irq (irq, adata->trig); +request_irq_error: +iio_trigger_free (adata->trig); +iio_trigger_alloc_error: +return err; +} +EXPORT_SYMBOL (st_accel_probe_trigger); + +void st_accel_remove_trigger (struct iio_dev *indio_dev,int irq) +{ +struct st_accel_data *adata =iio_priv (indio_dev); + +iio_trigger_unregister (adata->trig); +free_irq (irq, adata->trig); +iio_trigger_free (adata->trig); +} +EXPORT_SYMBOL (st_accel_remove_trigger);include/linux/iio/accel/st_accel.h new file +0
-0
include/linux/iio/accel/st_accel.h line 10
Quoted 10 lines
include/linux/iio/accel/st_accel.h new file +10
-0@@ -0,0 +1,121 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* v. 1.0.0 +* Licensed under the GPL-2. +*/ +
The supported sensors don't really need to be listed here as they are apparent elsewhere in the driver. Documentation like this just tends to get out of date.
Quoted 47 lines
include/linux/iio/accel/st_accel.h new file +47
-0+/* +* Supported sensors: +* LSM303DLH +* LSM303DLHC +* LIS3DH +* LSM330D +* LSM330DL +* LSM330DLC +* LSM303D +* LSM9DS0 +* LIS331DLH +* LSM303DL +* LSM303DLM +* LSM330 +* +*/ + + +#ifndef ST_ACCEL_H +#define ST_ACCEL_H + +#define LSM303DLH_ACCEL_DEV_NAME "lsm303dlh_accel" +#define LSM303DLHC_ACCEL_DEV_NAME "lsm303dlhc_accel" +#define LIS3DH_ACCEL_DEV_NAME "lis3dh" +#define LSM330D_ACCEL_DEV_NAME "lsm330d_accel" +#define LSM330DL_ACCEL_DEV_NAME "lsm330dl_accel" +#define LSM330DLC_ACCEL_DEV_NAME "lsm330dlc_accel" +#define LSM303D_ACCEL_DEV_NAME "lsm303d" +#define LSM9DS0_ACCEL_DEV_NAME "lsm9ds0" +#define LIS331DLH_ACCEL_DEV_NAME "lis331dlh" +#define LSM303DL_ACCEL_DEV_NAME "lsm303dl_accel" +#define LSM303DLM_ACCEL_DEV_NAME "lsm303dlm_accel" +#define LSM330_ACCEL_DEV_NAME "lsm330_accel" + +#define ST_ACCEL_NUMBER_ALL_CHANNELS 4 +#define ST_ACCEL_NUMBER_DATA_CHANNELS 3 +#define ST_ACCEL_BYTE_FOR_CHANNEL 2 +#define ST_ACCEL_SCAN_X 0 +#define ST_ACCEL_SCAN_Y 1 +#define ST_ACCEL_SCAN_Z 2 + +/** +* struct st_accel_data - ST accel 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.
include/linux/iio/accel/st_accel.h line 67
Quoted 10 lines
include/linux/iio/accel/st_accel.h new file +10
-0+* @index: Number used to point the sensor being used in the +* st_accel_sensors struct. +* @buffer_data: Data used by buffer part. +* @fullscale: Maximum range of measure by the sensor. +* @gain: Sensitivity of the sensor [ms^2/LSB]. +* @odr: Output data rate of the sensor [Hz]. +* @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. +*
Unecessary blank line.
Quoted 61 lines
include/linux/iio/accel/st_accel.h new file +54
-0+*/ + +struct st_accel_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 odr; + +int (*read_byte) (struct st_accel_data *adata,u8 reg_addr, +u8 *res_byte); +int (*write_byte) (struct st_accel_data *adata,u8 reg_addr,u8 data); +int (*read_multiple_byte) (struct st_accel_data *adata,u8 reg_addr, +int len,u8 *data); +}; + +int st_accel_iio_probe (struct iio_dev *indio_dev,int irq); +void st_accel_iio_remove (struct iio_dev *indio_dev,int irq); +int st_accel_set_dataready_irq (struct iio_dev *indio_dev,bool enable); +int st_accel_set_axis_enable (struct iio_dev *indio_dev,u8 axis_enable); + +#ifdef CONFIG_IIO_BUFFER +int st_accel_probe_trigger (struct iio_dev *indio_dev,int irq); +void st_accel_remove_trigger (struct iio_dev *indio_dev,int irq); +int st_accel_allocate_ring (struct iio_dev *indio_dev); +void st_accel_deallocate_ring (struct iio_dev *indio_dev); +#else /* CONFIG_IIO_BUFFER */ +static inline int st_accel_probe_trigger (struct iio_dev *indio_dev,int irq) +{ +return 0 ; +} +static inline void st_accel_remove_trigger (struct iio_dev *indio_dev,int irq) +{ +return ; +} +static inline int st_accel_allocate_ring (struct iio_dev *indio_dev) +{ +return 0 ; +} +static inline void st_accel_deallocate_ring (struct iio_dev *indio_dev) +{ +return ; +} +#endif /* CONFIG_IIO_BUFFER */ + +#endif /* ST_ACCEL_H */ include/linux/platform_data/st_accel_pdata.h new file +0
-0
include/linux/platform_data/st_accel_pdata.h line 9
Quoted 9 lines
include/linux/platform_data/st_accel_pdata.h new file +9
-0@@ -0,0 +1,27 @@ +/* +* STMicroelectronics accelerometers driver +* +* Copyright 2012 STMicroelectronics Inc. +* +* Denis Ciocca <[email protected]> +* +* Licensed under the GPL-2. +*/
The general principal that it rarely makes sense to have more than one blank line applies here.
Quoted 6 lines
include/linux/platform_data/st_accel_pdata.h new file +6
-0+ + +#ifndef ST_ACCEL_PDATA_H +#define ST_ACCEL_PDATA_H + +
include/linux/platform_data/st_accel_pdata.h line 25
Quoted 10 lines
include/linux/platform_data/st_accel_pdata.h new file +10
-0+/** +* struct st_accel_platform_data - ST accel device platform data +* @fullscale: Value of fullscale used for the sensor. +* @sampling_frequency: Value of sampling frequency used for the sensor. +*/ + +struct st_accel_platform_data { +int fullscale; +int sampling_frequency; +};
What is the purpose of having these as platform data?
Quoted 3 lines
include/linux/platform_data/st_accel_pdata.h new file +2
-0+ +#endif /* ST_ACCEL_PDATA_H */
Hi Jonathan,
Thanks for your support!
I have applied your comments, only two point I would check better.
Quoted 15 lines
drivers/iio/accel/st_accel_core.c line 330
Quoted 9 lines
drivers/iio/accel/st_accel_core.c new file +9
-0+} st_accel_sensors[] = { + { + .wai = ST_ACCEL_1_WAI_EXP, + .ch = (struct iio_chan_spec *)st_accel_12bit_channels, + .odr = { + .addr = ST_ACCEL_1_ODR_ADDR, + .mask = ST_ACCEL_1_ODR_MASK, + .num_bit = ST_ACCEL_1_ODR_N_BIT, + .odr_avl = {While sometimes c99 assignment helps with clarity - sometimes it just bloats the code. Personally I'd do these as
.odr_avl = {{ST_ACCEL_ODR_AVL_1HZ,ST_ACCEL_1_ODR_AVL_1HZ_VAL},{...}etc
Quoted 9 lines
or for that matter take the view that the defines are largely pointless as they are only used here and
just do.odr_avl = {{1, 0x01}, {10, 0x02}, etc. Or maybe if it fits on the line .odr_avl = {{ .hz = 1, .value = 0x01 } might be the clearest... Not sure.
In my first version I have used about this compact approach but Lars-Peter tell to me to use this style code:
Quoted 9 lines
I'd use
.odr_avl = { [0] = { ... }, [1] = { ...Makes things a bit more readable in my opinion.
Ok for me it is not a problem but you must tell me one definitive style please. I think this style is more cleanly, though are necessary more rows.
Quoted 4 lines
Quoted 4 lines
+/**
+ * structst_accel_platform_data- ST accel device platform data
+ * @fullscale: Value of fullscale used for the sensor.
+ * @sampling_frequency: Value of sampling frequency used for the sensor.
Quoted 9 lines
include/linux/platform_data/st_accel_pdata.h line 25
Quoted 6 lines
+ */
include/linux/platform_data/st_accel_pdata.h new file +5
-0+ +struct st_accel_platform_data { +int fullscale; +int sampling_frequency; +};What is the purpose of having these as platform data?
For now the purpose is to set only the initial values for fullscale and sampling_frequency but I think it's good thing include the mapping of axis. You think it's necessary to remove this for now?
Thanks,
Denis
Hi Jonathan,
can you please provide me one direction to modify the part in question=20 about the style of struct, in such a way I can do some modifications.
Thanks,
Denis Ciocca
Quoted 32 lines · Denis Ciocca
Hi Jonathan,
Thanks for your support!
I have applied your comments, only two point I would check better.Quoted 25 lines
Review
Quoted 9 lines
(quoted diff) +9
-0+} st_accel_sensors[] =3D { + { + .wai =3D ST_ACCEL_1_WAI_EXP, + .ch =3D (struct iio_chan_spec *)st_accel_12bit_channels, + .odr =3D { + .addr =3D ST_ACCEL_1_ODR_ADDR, + .mask =3D ST_ACCEL_1_ODR_MASK, + .num_bit =3D ST_ACCEL_1_ODR_N_BIT, + .odr_avl =3D {While sometimes c99 assignment helps with clarity - sometimes it
just bloats the code.
Personally I'd do these as
.odr_avl =3D {{ST_ACCEL_ODR_AVL_1HZ,ST_ACCEL_1_ODR_AVL_1HZ_VAL},{...}etc
or for that matter take the view that the defines are largely pointless as they
are only used here and
just do.odr_avl =3D {{1, 0x01}, {10, 0x02}, etc. Or maybe if it fits on the line .odr_avl =3D {{ .hz =3D 1, .value =3D 0x01 } might be the clearest..=
.
Quoted 13 lines
Quoted 2 lines
Not sure.
In my first version I have used about this compact approach but Lars-Peter tell to me to use this style code:
Quoted 8 lines
I'd use
.odr_avl =3D { [0] =3D { ... }, [1] =3D { ...
Quoted 4 lines
Quoted 1 line
Makes things a bit more readable in my opinion.
Ok for me it is not a problem but you must tell me one definitive style please. I think this style is more cleanly, though are necessary more row=
s.
Quoted 37 lines
Quoted 17 lines
Quoted 9 lines
+/**
+ * structst_accel_platform_data- ST accel device platform data
+ * @fullscale: Value of fullscale used for the sensor.
+ * @sampling_frequency: Value of sampling frequency used for the sensor.
+ */include/linux/platform_data/st_accel_pdata.h line 25
Quoted 5 lines
include/linux/platform_data/st_accel_pdata.h new file +5
-0+ +struct st_accel_platform_data { +int fullscale; +int sampling_frequency; +};What is the purpose of having these as platform data?
For now the purpose is to set only the initial values for fullscale and
sampling_frequencybut I think it's good thing include the mapping of axis. You think it's necessary to remove this for now?Thanks,
Denis
Jonathan Cameron
maintainerQuoted 5 lines · Denis CIOCCA
Hi Jonathan,
can you please provide me one direction to modify the part in question about the style of struct, in such a way I can do some modifications.
Sorry, I haven't caught up with my emails for a few days!
Anyhow, quick reply below.
Quoted 56 lines
Thanks,
Denis Ciocca
Quoted 49 lines · Denis Ciocca
Hi Jonathan,
Thanks for your support!
I have applied your comments, only two point I would check better.Quoted 20 lines
drivers/iio/accel/st_accel_core.c line 330
Quoted 9 lines
drivers/iio/accel/st_accel_core.c new file +9
-0+} st_accel_sensors[] = { + { + .wai = ST_ACCEL_1_WAI_EXP, + .ch = (struct iio_chan_spec *)st_accel_12bit_channels, + .odr = { + .addr = ST_ACCEL_1_ODR_ADDR, + .mask = ST_ACCEL_1_ODR_MASK, + .num_bit = ST_ACCEL_1_ODR_N_BIT, + .odr_avl = {While sometimes c99 assignment helps with clarity - sometimes it
just bloats the code.
Personally I'd do these as
.odr_avl = {{ST_ACCEL_ODR_AVL_1HZ,ST_ACCEL_1_ODR_AVL_1HZ_VAL},{...}etc
or for that matter take the view that the defines are largely pointless as they
are only used here and
just doQuoted 7 lines
.odr_avl = {{1, 0x01}, {10, 0x02}, etc. Or maybe if it fits on the line .odr_avl = {{ .hz = 1, .value = 0x01 } might be the clearest...Not sure.
In my first version I have used about this compact approach but Lars-Peter tell to me to use this style code:
Quoted 9 lines
I'd use
.odr_avl = { [0] = { ... }, [1] = { ...Makes things a bit more readable in my opinion.
Ok for me it is not a problem but you must tell me one definitive style please. I think this style is more cleanly, though are necessary more rows.
The indexing gives no benefit - it's not relevant to the use of this array as far as I can tell. Now we had index value pairs, then it would make sense, but we don't.
My personal view is go for whatever gives the best combination of readability to code compactness. So here, where what we have is effectively key value pairs, I'd just have them as an array. Elsewhere, when you have general elements of a structure then c99 style assignment is clearest and allows the structure to sometimes be extended without needing to be careful about the ordering etc.
To a certain extent I don't care, I just thought for key value pairs there was an awful lot of code here and it actually damaged readability rather than enhancing it.
Hence have a think about it and go with whatever you think is clearest.
Quoted 19 lines
Quoted 19 lines
Quoted 18 lines
Quoted 10 lines
+/**
+ * structst_accel_platform_data- ST accel device platform data
+ * @fullscale: Value of fullscale used for the sensor.
+ * @sampling_frequency: Value of sampling frequency used for the sensor.
+ */include/linux/platform_data/st_accel_pdata.h line 25
Quoted 5 lines
include/linux/platform_data/st_accel_pdata.h new file +5
-0+ +struct st_accel_platform_data { +int fullscale; +int sampling_frequency; +};What is the purpose of having these as platform data?
Quoted 3 lines
Quoted 3 lines
For now the purpose is to set only the initial values for fullscale and
sampling_frequencybut I think it's good thing include the mapping of axis. You think it's necessary to remove this for now?
I'd not bother with it. Just put a sensible default in the driver and if anything else is wanted leave it up to userspace once the driver is loaded.
Axis alignment is an interesting one. So far we have it for just one driver and that was to maintain feature parity with an existing driver when we pulled it into the kernel.
It is often rather arbitary. The case where it matters is usually for human input in which case it can be handled in the mapping code (if using iio and the input bridge driver).