This patch add generic library for STMicroelectronics 3-axis sensors. --- drivers/iio/common/Kconfig | 1 + drivers/iio/common/Makefile | 1 + drivers/iio/common/st_sensors/Kconfig | 30 ++ drivers/iio/common/st_sensors/Makefile | 9 + drivers/iio/common/st_sensors/st_sensors_buffer.c | 115 +++++++ drivers/iio/common/st_sensors/st_sensors_core.c | 335 ++++++++++++++++++++ drivers/iio/common/st_sensors/st_sensors_i2c.c | 78 +++++ drivers/iio/common/st_sensors/st_sensors_spi.c | 124 +++++++ drivers/iio/common/st_sensors/st_sensors_trigger.c | 76 +++++ include/linux/iio/common/st_sensors.h | 246 ++++++++++++++ 10 files changed, 1015 insertions(+), 0 deletions(-) create mode 100644 drivers/iio/common/st_sensors/Kconfig create mode 100644 drivers/iio/common/st_sensors/Makefile create mode 100644 drivers/iio/common/st_sensors/st_sensors_buffer.c create mode 100644 drivers/iio/common/st_sensors/st_sensors_core.c create mode 100644 drivers/iio/common/st_sensors/st_sensors_i2c.c create mode 100644 drivers/iio/common/st_sensors/st_sensors_spi.c create mode 100644 drivers/iio/common/st_sensors/st_sensors_trigger.c create mode 100644 include/linux/iio/common/st_sensors.h diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig index ed45ee5..0b6e97d 100644 --- a/drivers/iio/common/Kconfig +++ b/drivers/iio/common/Kconfig @@ -3,3 +3,4 @@ # source "drivers/iio/common/hid-sensors/Kconfig" +source "drivers/iio/common/st_sensors/Kconfig" diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile index 8158400..c2352be 100644 --- a/drivers/iio/common/Makefile +++ b/drivers/iio/common/Makefile @@ -7,3 +7,4 @@ # obj-y += hid-sensors/ +obj-y += st_sensors/ diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig new file mode 100644 index 0000000..ddcfcc6 --- /dev/null +++ b/drivers/iio/common/st_sensors/Kconfig @@ -0,0 +1,30 @@ +# +# Hid Sensor common modules +# +menu "STMicroelectronics sensors library" + +config ST_SENSORS_CORE + tristate "Common modules for all ST sensors IIO drivers" + help + Say yes here to build support for ST sensors to use + common core functions. + +config ST_SENSORS_I2C + tristate "I2C common library for ST Sensor IIO drivers" + help + Say yes here to build support for ST sensors to use + common i2c functions. + +config ST_SENSORS_SPI + tristate "SPI common library for ST Sensor IIO drivers" + help + Say yes here to build support for ST sensors to use + common spi functions. + +config ST_SENSORS_TRIGGERED_BUFFER + tristate "trigger and buffer common library for ST Sensor IIO drivers" + help + Say yes here to build support for ST sensors to use + common trigger and buffer functions. + +endmenu diff --git a/drivers/iio/common/st_sensors/Makefile b/drivers/iio/common/st_sensors/Makefile new file mode 100644 index 0000000..a191e65 --- /dev/null +++ b/drivers/iio/common/st_sensors/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the STMicroelectronics sensor common modules. +# + +obj-$(CONFIG_ST_SENSORS_CORE) += st_sensors_core.o +obj-$(CONFIG_ST_SENSORS_I2C) += st_sensors_i2c.o +obj-$(CONFIG_ST_SENSORS_SPI) += st_sensors_spi.o +obj-$(CONFIG_ST_SENSORS_TRIGGERED_BUFFER) += st_sensors_triggered_buffer.o +st_sensors_triggered_buffer-y := st_sensors_trigger.o st_sensors_buffer.o diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c new file mode 100644 index 0000000..b636751 --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c @@ -0,0 +1,115 @@ +/* + * STMicroelectronics sensors buffer library driver + * + * Copyright 2012 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@xxxxxx> + * + * Licensed under the GPL-2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> +#include <linux/interrupt.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/irqreturn.h> + +#include <linux/iio/common/st_sensors.h> + +int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf) +{ + int i, n = 0, len; + u8 addr[ST_SENSORS_NUMBER_DATA_CHANNELS]; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) { + if (test_bit(i, indio_dev->active_scan_mask)) { + addr[n] = indio_dev->channels[i].address; + n++; + } + } + switch (n) { + case 1: + len = sdata->tf.read_multiple_byte(&sdata->tf, sdata->dev, + addr[0], ST_SENSORS_BYTE_FOR_CHANNEL, buf, + sdata->multiread_bit); + break; + case 2: + if ((addr[1] - addr[0]) == ST_SENSORS_BYTE_FOR_CHANNEL) { + len = sdata->tf.read_multiple_byte(&sdata->tf, + sdata->dev, addr[0], + ST_SENSORS_BYTE_FOR_CHANNEL*n, + buf, sdata->multiread_bit); + } else { + u8 rx_array[ST_SENSORS_BYTE_FOR_CHANNEL* + ST_SENSORS_NUMBER_DATA_CHANNELS]; + len = sdata->tf.read_multiple_byte(&sdata->tf, + sdata->dev, addr[0], + ST_SENSORS_BYTE_FOR_CHANNEL* + ST_SENSORS_NUMBER_DATA_CHANNELS, + rx_array, sdata->multiread_bit); + if (len < 0) + goto read_data_channels_error; + + for (i = 0; i < n * ST_SENSORS_NUMBER_DATA_CHANNELS; + i++) { + if (i < n) + buf[i] = rx_array[i]; + else + buf[i] = rx_array[n + i]; + } + len = ST_SENSORS_BYTE_FOR_CHANNEL*n; + } + break; + case 3: + len = sdata->tf.read_multiple_byte(&sdata->tf, sdata->dev, + addr[0], ST_SENSORS_BYTE_FOR_CHANNEL* + ST_SENSORS_NUMBER_DATA_CHANNELS, + buf, sdata->multiread_bit); + break; + default: + len = -EINVAL; + goto read_data_channels_error; + } + if (len != ST_SENSORS_BYTE_FOR_CHANNEL*n) { + len = -EIO; + goto read_data_channels_error; + } + +read_data_channels_error: + return len; +} +EXPORT_SYMBOL(st_sensors_get_buffer_element); + +irqreturn_t st_sensors_trigger_handler(int irq, void *p) +{ + int len; + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data); + if (len < 0) + goto st_sensors_get_buffer_element_error; + + if (indio_dev->scan_timestamp) + *(s64 *)((u8 *)sdata->buffer_data + + ALIGN(len, sizeof(s64))) = pf->timestamp; + + iio_push_to_buffers(indio_dev, sdata->buffer_data); + +st_sensors_get_buffer_element_error: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL(st_sensors_trigger_handler); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@xxxxxx>"); +MODULE_DESCRIPTION("STMicroelectronics ST-sensors buffer"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c new file mode 100644 index 0000000..2b00ed7 --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -0,0 +1,335 @@ +/* + * STMicroelectronics sensors core library driver + * + * Copyright 2012 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@xxxxxx> + * + * Licensed under the GPL-2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/iio/iio.h> + +#include <linux/iio/common/st_sensors.h> + + +#define ST_SENSORS_WAI_ADDRESS 0x0f + +int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, u8 reg_addr, + u8 mask, short num_bit, u8 data) +{ + int err; + u8 new_data; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + err = sdata->tf.read_byte(&sdata->tf, sdata->dev, reg_addr, &new_data); + if (err < 0) + goto st_sensors_write_data_with_mask_error; + + new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask)); + err = sdata->tf.write_byte(&sdata->tf, sdata->dev, reg_addr, new_data); + +st_sensors_write_data_with_mask_error: + return err; +} +EXPORT_SYMBOL(st_sensors_write_data_with_mask); + +int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev, + const struct st_sensor_odr_avl *odr_avl, char *buf) +{ + int i, len = 0; + + mutex_lock(&indio_dev->mlock); + for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) { + if (odr_avl[i].hz == 0) + break; + + len += sprintf(buf + len, "%d ", odr_avl[i].hz); + } + mutex_unlock(&indio_dev->mlock); + buf[len - 1] = '\n'; + + return len; +} +EXPORT_SYMBOL(st_sensors_get_sampling_frequency_avl); + +int st_sensors_get_fullscale_avl(struct iio_dev *indio_dev, + const struct st_sensor_fullscale_avl *fullscale_avl, char *buf) +{ + int i, len = 0; + + mutex_lock(&indio_dev->mlock); + for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) { + if (fullscale_avl[i].num == 0) + break; + + len += sprintf(buf + len, "0.%06u ", fullscale_avl[i].gain); + } + mutex_unlock(&indio_dev->mlock); + buf[len - 1] = '\n'; + + return len; +} +EXPORT_SYMBOL(st_sensors_get_fullscale_avl); + +static int st_sensors_match_odr(const struct st_sensors *sensor, + unsigned int odr, struct st_sensor_odr_avl *odr_out) +{ + int i, ret = -EINVAL; + + for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) { + if ((sensor->odr.odr_avl[i].hz == odr) && + (sensor->odr.odr_avl[i].hz != 0)) { + odr_out->hz = sensor->odr.odr_avl[i].hz; + odr_out->value = sensor->odr.odr_avl[i].value; + ret = 0; + break; + } + } + + return ret; +} + +int st_sensors_set_odr(struct iio_dev *indio_dev, + const struct st_sensors *sensor, unsigned int odr) +{ + int err; + struct st_sensor_data *sdata = iio_priv(indio_dev); + struct st_sensor_odr_avl odr_out; + + err = st_sensors_match_odr(sensor, odr, &odr_out); + if (err < 0) + goto st_sensors_match_odr_error; + + if ((sensor->odr.addr == sensor->pw.addr) && + (sensor->odr.mask == sensor->pw.mask)) { + if (sdata->enabled == true) { + err = st_sensors_write_data_with_mask(indio_dev, + sensor->odr.addr, sensor->odr.mask, + sensor->odr.num_bit, + odr_out.value); + } else { + err = 0; + } + } else { + err = st_sensors_write_data_with_mask(indio_dev, + sensor->odr.addr, sensor->odr.mask, + sensor->odr.num_bit, odr_out.value); + } + if (err >= 0) + sdata->odr = odr_out.hz; + +st_sensors_match_odr_error: + return err; +} +EXPORT_SYMBOL(st_sensors_set_odr); + +static int st_sensors_match_fs(const struct st_sensors *sensor, + unsigned int fs, struct st_sensor_fullscale_avl *fs_out) +{ + int i, ret = -EINVAL; + + for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) { + if ((sensor->fs.fs_avl[i].num == fs) && + (sensor->fs.fs_avl[i].num != 0)) { + fs_out->num = sensor->fs.fs_avl[i].num; + fs_out->gain = sensor->fs.fs_avl[i].gain; + fs_out->value = sensor->fs.fs_avl[i].value; + ret = 0; + break; + } + } + + return ret; +} + +int st_sensors_set_fullscale(struct iio_dev *indio_dev, + const struct st_sensors *sensor, unsigned int fs) +{ + int err; + struct st_sensor_data *sdata = iio_priv(indio_dev); + struct st_sensor_fullscale_avl fs_out; + + err = st_sensors_match_fs(sensor, fs, &fs_out); + if (err < 0) + goto st_accel_set_fullscale_error; + + err = st_sensors_write_data_with_mask(indio_dev, sensor->fs.addr, + sensor->fs.mask, sensor->fs.num_bit, sensor->fs.fs_avl->value); + if (err < 0) + goto st_accel_set_fullscale_error; + + sdata->fullscale = sensor->fs.fs_avl->num; + sdata->gain = sensor->fs.fs_avl->gain; + sdata->gain2 = sensor->fs.fs_avl->gain2; + return err; + +st_accel_set_fullscale_error: + dev_err(&indio_dev->dev, "failed to set new fullscale.\n"); + return err; +} +EXPORT_SYMBOL(st_sensors_set_fullscale); + +int st_sensors_set_enable(struct iio_dev *indio_dev, + const struct st_sensors *sensor, bool enable) +{ + int err = -EINVAL; + bool found; + u8 tmp_value; + struct st_sensor_odr_avl odr_out; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + if (enable) { + found = false; + tmp_value = sensor->pw.value_on; + if ((sensor->odr.addr == sensor->pw.addr) && + (sensor->odr.mask == sensor->pw.mask)) { + err = st_sensors_match_odr(sensor, + sdata->odr, &odr_out); + if (err < 0) + goto set_enable_error; + tmp_value = odr_out.value; + found = true; + } + err = st_sensors_write_data_with_mask(indio_dev, + sensor->pw.addr, sensor->pw.mask, + sensor->pw.num_bit, tmp_value); + if (err < 0) + goto set_enable_error; + sdata->enabled = true; + if (found) + sdata->odr = odr_out.hz; + } else { + err = st_sensors_write_data_with_mask(indio_dev, + sensor->pw.addr, sensor->pw.mask, + sensor->pw.num_bit, sensor->pw.value_off); + if (err < 0) + goto set_enable_error; + sdata->enabled = false; + } + +set_enable_error: + return err; +} +EXPORT_SYMBOL(st_sensors_set_enable); + +int st_sensors_set_axis_enable(struct iio_dev *indio_dev, + const struct st_sensors *sensor, u8 axis_enable) +{ + return st_sensors_write_data_with_mask(indio_dev, + sensor->enable_axis.addr, sensor->enable_axis.mask, + 3, axis_enable); +} +EXPORT_SYMBOL(st_sensors_set_axis_enable); + +int st_sensors_init_sensor(struct iio_dev *indio_dev, + const struct st_sensors *sensor) +{ + int err; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + err = st_sensors_set_enable(indio_dev, sensor, false); + if (err < 0) + goto init_error; + + err = st_sensors_set_fullscale(indio_dev, sensor, sdata->fullscale); + if (err < 0) + goto init_error; + + err = st_sensors_set_odr(indio_dev, sensor, sdata->odr); + if (err < 0) + goto init_error; + + /* set BDU */ + err = st_sensors_write_data_with_mask(indio_dev, sensor->bdu.addr, + sensor->bdu.mask, 1, true); + if (err < 0) + goto init_error; + + err = st_sensors_set_axis_enable(indio_dev, sensor, + ST_SENSORS_ENABLE_ALL_CHANNELS); + +init_error: + return err; +} +EXPORT_SYMBOL(st_sensors_init_sensor); + +int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, + const struct st_sensors *sensor, bool enable) +{ + int err; + + if (sensor->drdy_irq.ig1.en_addr > 0) { + err = st_sensors_write_data_with_mask(indio_dev, + sensor->drdy_irq.ig1.en_addr, + sensor->drdy_irq.ig1.en_mask, 1, (int)enable); + if (err < 0) + goto st_accel_set_dataready_irq_error; + } + + if (sensor->drdy_irq.ig1.latch_mask_addr > 0) { + err = st_sensors_write_data_with_mask(indio_dev, + sensor->drdy_irq.ig1.latch_mask_addr, + sensor->drdy_irq.ig1.latching_mask, 1, + (int)enable); + if (err < 0) + goto st_accel_set_dataready_irq_error; + } + + err = st_sensors_write_data_with_mask(indio_dev, + sensor->drdy_irq.addr, + sensor->drdy_irq.mask, 1, (int)enable); + +st_accel_set_dataready_irq_error: + return err; +} +EXPORT_SYMBOL(st_sensors_set_dataready_irq); + +int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, + const struct st_sensors *sensor, int scale) +{ + int err = -EINVAL, i; + + for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) { + if ((sensor->fs.fs_avl[i].gain == scale) && + (sensor->fs.fs_avl[i].gain != 0)) { + err = 0; + break; + } + } + if (err < 0) + goto st_sensors_match_scale_error; + + err = st_sensors_set_fullscale(indio_dev, + sensor, sensor->fs.fs_avl[i].num); + +st_sensors_match_scale_error: + return err; +} +EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain); + +int st_sensors_read_axis_data(struct iio_dev *indio_dev, u8 ch_addr, int *data) +{ + int err; + u8 outdata[ST_SENSORS_BYTE_FOR_CHANNEL]; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + err = sdata->tf.read_multiple_byte(&sdata->tf, sdata->dev, + ch_addr, ST_SENSORS_BYTE_FOR_CHANNEL, + outdata, sdata->multiread_bit); + if (err < 0) + goto read_error; + + *data = ((s16)le16_to_cpup((u16 *)outdata)); + +read_error: + return err; +} +EXPORT_SYMBOL(st_sensors_read_axis_data); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@xxxxxx>"); +MODULE_DESCRIPTION("STMicroelectronics ST-sensors core"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c new file mode 100644 index 0000000..fa4bc7d --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c @@ -0,0 +1,78 @@ +/* + * STMicroelectronics sensors i2c library driver + * + * Copyright 2012 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@xxxxxx> + * + * Licensed under the GPL-2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/iio/iio.h> + +#include <linux/iio/common/st_sensors.h> + + +#define ST_SENSORS_I2C_MULTIREAD 0x80 + +unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev) +{ + struct st_sensor_data *sdata = iio_priv(indio_dev); + struct i2c_client *client = to_i2c_client(sdata->dev); + + return client->irq; +} + +static int st_sensors_i2c_read_byte(struct st_sensor_transfer *st, + struct device *dev, u8 reg_addr, u8 *res_byte) +{ + int err; + + err = i2c_smbus_read_byte_data(to_i2c_client(dev), reg_addr); + if (err < 0) + goto st_accel_i2c_read_byte_error; + + *res_byte = err & 0xff; + +st_accel_i2c_read_byte_error: + return err < 0 ? err : 0; +} + +static int st_sensors_i2c_read_multiple_byte(struct st_sensor_transfer *st, + struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit) +{ + if (multiread_bit == true) + reg_addr |= ST_SENSORS_I2C_MULTIREAD; + + return i2c_smbus_read_i2c_block_data(to_i2c_client(dev), + reg_addr, len, data); +} + +static int st_sensors_i2c_write_byte(struct st_sensor_transfer *st, + struct device *dev, u8 reg_addr, u8 data) +{ + return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data); +} + +void st_sensors_i2c_configure(struct iio_dev *indio_dev, + struct i2c_client *client, struct st_sensor_data *sdata) +{ + i2c_set_clientdata(client, indio_dev); + + indio_dev->dev.parent = &client->dev; + indio_dev->name = client->name; + + mutex_init(&sdata->tf.buf_lock); + sdata->tf.read_byte = st_sensors_i2c_read_byte; + sdata->tf.write_byte = st_sensors_i2c_write_byte; + sdata->tf.read_multiple_byte = st_sensors_i2c_read_multiple_byte; + sdata->get_irq_data_ready = st_sensors_i2c_get_irq; +} +EXPORT_SYMBOL(st_sensors_i2c_configure); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@xxxxxx>"); +MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c new file mode 100644 index 0000000..82fa633 --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_spi.c @@ -0,0 +1,124 @@ +/* + * STMicroelectronics sensors spi library driver + * + * Copyright 2012 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@xxxxxx> + * + * Licensed under the GPL-2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/iio/iio.h> + +#include <linux/iio/common/st_sensors.h> + + +#define ST_SENSORS_SPI_MULTIREAD 0xc0 +#define ST_SENSORS_SPI_READ 0x80 + +unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev) +{ + struct st_sensor_data *sdata = iio_priv(indio_dev); + struct spi_device *spi = to_spi_device(sdata->dev); + + return spi->irq; +} + +static int st_sensors_spi_read(struct st_sensor_transfer *st, + struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit) +{ + struct spi_message msg; + int err; + + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx_buf, + .bits_per_word = 8, + .len = 1, + }, + { + .rx_buf = st->rx_buf, + .bits_per_word = 8, + .len = len, + } + }; + + mutex_lock(&st->buf_lock); + if ((multiread_bit == true) && (len > 1)) + st->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD; + else + st->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + err = spi_sync(to_spi_device(dev), &msg); + if (err) + goto acc_spi_read_error; + + memcpy(data, st->rx_buf, len*sizeof(u8)); + mutex_unlock(&st->buf_lock); + return len; + +acc_spi_read_error: + return err; +} + +static int st_sensors_spi_read_byte(struct st_sensor_transfer *st, + struct device *dev, u8 reg_addr, u8 *res_byte) +{ + return st_sensors_spi_read(st, dev, reg_addr, 1, res_byte, false); +} + +static int st_sensors_spi_read_multiple_byte(struct st_sensor_transfer *st, + struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit) +{ + return st_sensors_spi_read(st, dev, reg_addr, len, data, multiread_bit); +} + +static int st_sensors_spi_write_byte(struct st_sensor_transfer *st, + struct device *dev, u8 reg_addr, u8 data) +{ + struct spi_message msg; + int err; + + struct spi_transfer xfers = { + .tx_buf = st->tx_buf, + .bits_per_word = 8, + .len = 2, + }; + + mutex_lock(&st->buf_lock); + st->tx_buf[0] = reg_addr; + st->tx_buf[1] = data; + + spi_message_init(&msg); + spi_message_add_tail(&xfers, &msg); + err = spi_sync(to_spi_device(dev), &msg); + mutex_unlock(&st->buf_lock); + + return err; +} + +void st_sensors_spi_configure(struct iio_dev *indio_dev, + struct spi_device *spi, struct st_sensor_data *sdata) +{ + spi_set_drvdata(spi, indio_dev); + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi->modalias; + + mutex_init(&sdata->tf.buf_lock); + sdata->tf.read_byte = st_sensors_spi_read_byte; + sdata->tf.write_byte = st_sensors_spi_write_byte; + sdata->tf.read_multiple_byte = st_sensors_spi_read_multiple_byte; + sdata->get_irq_data_ready = st_sensors_spi_get_irq; +} +EXPORT_SYMBOL(st_sensors_spi_configure); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@xxxxxx>"); +MODULE_DESCRIPTION("STMicroelectronics ST-sensors spi driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c new file mode 100644 index 0000000..6d43b13 --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -0,0 +1,76 @@ +/* + * STMicroelectronics sensors trigger library driver + * + * Copyright 2012 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@xxxxxx> + * + * Licensed under the GPL-2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> +#include <linux/interrupt.h> + +#include <linux/iio/common/st_sensors.h> + +int st_sensors_allocate_trigger(struct iio_dev *indio_dev, + const struct iio_trigger_ops *trig_ops) +{ + int err; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name); + if (sdata->trig == NULL) { + err = -ENOMEM; + dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n"); + goto iio_trigger_alloc_error; + } + + err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev), + iio_trigger_generic_data_rdy_poll, + NULL, + IRQF_TRIGGER_RISING, + sdata->trig->name, + sdata->trig); + if (err) + goto request_irq_error; + + sdata->trig->private_data = indio_dev; + sdata->trig->ops = trig_ops; + sdata->trig->dev.parent = sdata->dev; + + err = iio_trigger_register(sdata->trig); + if (err < 0) { + dev_err(&indio_dev->dev, "failed to register iio trigger.\n"); + goto iio_trigger_register_error; + } + indio_dev->trig = sdata->trig; + + return 0; + +iio_trigger_register_error: + free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig); +request_irq_error: + iio_trigger_free(sdata->trig); +iio_trigger_alloc_error: + return err; +} +EXPORT_SYMBOL(st_sensors_allocate_trigger); + +void st_sensors_deallocate_trigger(struct iio_dev *indio_dev) +{ + struct st_sensor_data *sdata = iio_priv(indio_dev); + + iio_trigger_unregister(sdata->trig); + free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig); + iio_trigger_free(sdata->trig); +} +EXPORT_SYMBOL(st_sensors_deallocate_trigger); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@xxxxxx>"); +MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h new file mode 100644 index 0000000..c0f2acc --- /dev/null +++ b/include/linux/iio/common/st_sensors.h @@ -0,0 +1,246 @@ +/* + * STMicroelectronics sensors library driver + * + * Copyright 2012 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@xxxxxx> + * + * Licensed under the GPL-2. + */ + +#ifndef ST_SENSORS_H +#define ST_SENSORS_H + +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <linux/irqreturn.h> +#include <linux/iio/trigger.h> + + +#define ST_SENSORS_TX_MAX_LENGHT 2 +#define ST_SENSORS_RX_MAX_LENGHT 6 + +#define ST_SENSORS_ODR_LIST_MAX 10 +#define ST_SENSORS_FULLSCALE_AVL_MAX 10 + +#define ST_SENSORS_NUMBER_ALL_CHANNELS 4 +#define ST_SENSORS_NUMBER_DATA_CHANNELS 3 +#define ST_SENSORS_ENABLE_ALL_CHANNELS 0x07 +#define ST_SENSORS_BYTE_FOR_CHANNEL 2 +#define ST_SENSORS_SCAN_X 0 +#define ST_SENSORS_SCAN_Y 1 +#define ST_SENSORS_SCAN_Z 2 +#define ST_SENSORS_DEFAULT_12_REALBITS 12 +#define ST_SENSORS_DEFAULT_16_REALBITS 16 +#define ST_SENSORS_DEFAULT_POWER_ON_VALUE 0x01 +#define ST_SENSORS_DEFAULT_POWER_OFF_VALUE 0x00 +#define ST_SENSORS_DEFAULT_WAI_ADDRESS 0x0f +#define ST_SENSORS_DEFAULT_AXIS_ADDR 0x20 +#define ST_SENSORS_DEFAULT_AXIS_MASK 0x07 +#define ST_SENSORS_DEFAULT_AXIS_N_BIT 3 + +#define ST_SENSORS_LSM_CHANNELS(device_type, index, mod, endian, bits, addr) \ +{ \ + .type = device_type, \ + .modified = 1, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ + .scan_index = index, \ + .channel2 = mod, \ + .address = addr, \ + .scan_type = { \ + .sign = 's', \ + .realbits = bits, \ + .shift = 16 - bits, \ + .storagebits = 16, \ + .endianness = endian, \ + }, \ +} + +struct st_sensor_odr_avl { + unsigned int hz; + u8 value; +}; + +struct st_sensor_odr { + u8 addr; + u8 mask; + short num_bit; + struct st_sensor_odr_avl odr_avl[ST_SENSORS_ODR_LIST_MAX]; +}; + +struct st_sensor_power { + u8 addr; + u8 mask; + unsigned short num_bit; + u8 value_off; + u8 value_on; +}; + +struct st_sensor_axis { + u8 addr; + u8 mask; +}; + +struct st_sensor_fullscale_avl { + unsigned int num; + u8 value; + unsigned int gain; + unsigned int gain2; +}; + +struct st_sensor_fullscale { + u8 addr; + u8 mask; + unsigned short num_bit; + struct st_sensor_fullscale_avl fs_avl[ST_SENSORS_FULLSCALE_AVL_MAX]; +}; + +struct st_sensor_bdu { + u8 addr; + u8 mask; +}; + +struct st_sensor_interrupt_generator { + u8 en_addr; + u8 latch_mask_addr; + u8 en_mask; + u8 latching_mask; +}; + +struct st_sensor_data_ready_irq { + u8 addr; + u8 mask; + struct st_sensor_interrupt_generator ig1; +}; + +/** + * struct st_sensor_transfer - ST sensor device I/O struct + * @buf_lock: Mutex to protect rx and tx buffers. + * @tx_buf: Buffer used by SPI transfer function to send data to the sensors. + * This buffer is used to avoid DMA not-aligned issue. + * @rx_buf: Buffer used by SPI transfer to receive data from sensors. + * This buffer is used to avoid DMA not-aligned issue. + * @read_byte: Function used to read one byte. + * @write_byte: Function used to write one byte. + * @read_multiple_byte: Function used to read multiple byte. + */ +struct st_sensor_transfer { + struct mutex buf_lock; + u8 tx_buf[ST_SENSORS_TX_MAX_LENGHT] ____cacheline_aligned; + u8 rx_buf[ST_SENSORS_RX_MAX_LENGHT] ____cacheline_aligned; + int (*read_byte) (struct st_sensor_transfer *st, struct device *dev, + u8 reg_addr, u8 *res_byte); + int (*write_byte) (struct st_sensor_transfer *st, struct device *dev, + u8 reg_addr, u8 data); + int (*read_multiple_byte) (struct st_sensor_transfer *st, + struct device *dev, u8 reg_addr, int len, u8 *data, + bool multiread_bit); +}; + +/** + * struct st_sensor_data - ST sensor device status + * @dev: Pointer to instance of struct device (I2C or SPI). + * @trig: The trigger in use by the core driver. + * @enabled: Status of the sensor (false->off, true->on). + * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread. + * @index: Number used to point the sensor being used in the st_sensors struct. + * @buffer_data: Data used by buffer part. + * @fullscale: Maximum range of measure by the sensor. + * @gain: Sensitivity of the sensor [m/s^2/LSB]. + * @odr: Output data rate of the sensor [Hz]. + * @tf: Transfer function structure used by I/O operations. + */ +struct st_sensor_data { + struct device *dev; + struct iio_trigger *trig; + + bool enabled; + bool multiread_bit; + + short index; + + char *buffer_data; + + unsigned int fullscale; + unsigned int gain; + unsigned int gain2; + unsigned int odr; + + struct st_sensor_transfer tf; + + unsigned int (*get_irq_data_ready) (struct iio_dev *indio_dev); +}; + +/** + * struct st_sensors - ST sensors list + * @wai: Contents of WhoAmI register. + * @ch: IIO channels for the sensor. + * @odr: Output data rate register and odr list available. + * @pw: Power register of the sensor. + * @enable_axis: Enable one or more axis of the sensor. + * @fs: Full scale register and fs list available. + * @bdu: Block data update register. + * @drdy_irq: Data ready register of the sensor. + * @multi_read_bit: Use or not particular bit for [I2C/SPI] multiread. + */ +struct st_sensors { + u8 wai; + struct iio_chan_spec *ch; + struct st_sensor_odr odr; + struct st_sensor_power pw; + struct st_sensor_axis enable_axis; + struct st_sensor_fullscale fs; + struct st_sensor_bdu bdu; + struct st_sensor_data_ready_irq drdy_irq; + bool multi_read_bit; +}; + +void st_sensors_spi_configure(struct iio_dev *indio_dev, + struct spi_device *spi, struct st_sensor_data *sdata); + +void st_sensors_i2c_configure(struct iio_dev *indio_dev, + struct i2c_client *client, struct st_sensor_data *sdata); + +int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, u8 reg_addr, + u8 mask, short num_bit, u8 data); + +int st_sensors_get_sampling_frequency_avl(struct iio_dev *indio_dev, + const struct st_sensor_odr_avl *odr_avl, char *buf); + +int st_sensors_get_fullscale_avl(struct iio_dev *indio_dev, + const struct st_sensor_fullscale_avl *fullscale_avl, char *buf); + +int st_sensors_set_odr(struct iio_dev *indio_dev, + const struct st_sensors *sensor, unsigned int odr); + +int st_sensors_set_fullscale(struct iio_dev *indio_dev, + const struct st_sensors *sensor, unsigned int fs); + +int st_sensors_set_enable(struct iio_dev *indio_dev, + const struct st_sensors *sensor, bool enable); + +int st_sensors_set_axis_enable(struct iio_dev *indio_dev, + const struct st_sensors *sensor, u8 axis_enable); + +int st_sensors_init_sensor(struct iio_dev *indio_dev, + const struct st_sensors *sensor); + +int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, + const struct st_sensors *sensor, bool enable); + +int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, + const struct st_sensors *sensor, int scale); + +int st_sensors_read_axis_data(struct iio_dev *indio_dev, u8 ch_addr, int *data); + +int st_sensors_allocate_trigger(struct iio_dev *indio_dev, + const struct iio_trigger_ops *trig_ops); + +void st_sensors_deallocate_trigger(struct iio_dev *indio_dev); + +int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf); + +irqreturn_t st_sensors_trigger_handler(int irq, void *p); + +#endif /* ST_SENSORS_H */ -- 1.7.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html