This patch add generic library for STMicroelectronics 3-axis sensors. Signed-off-by: Denis Ciocca <denis.ciocca@xxxxxx> --- 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 | 329 ++++++++++++++++++++ drivers/iio/common/st_sensors/st_sensors_i2c.c | 81 +++++ drivers/iio/common/st_sensors/st_sensors_spi.c | 128 ++++++++ drivers/iio/common/st_sensors/st_sensors_trigger.c | 83 +++++ include/linux/iio/common/st_sensors.h | 278 +++++++++++++++++ 10 files changed, 1055 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..1cf41fd --- /dev/null +++ b/drivers/iio/common/st_sensors/Kconfig @@ -0,0 +1,30 @@ +# +# Hid Sensor common modules +# +menu "STMicroelectronics sensors library" + +config IIO_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 IIO_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 IIO_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 IIO_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..8418b6b --- /dev/null +++ b/drivers/iio/common/st_sensors/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the STMicroelectronics sensor common modules. +# + +obj-$(CONFIG_IIO_ST_SENSORS_CORE) += st_sensors_core.o +obj-$(CONFIG_IIO_ST_SENSORS_I2C) += st_sensors_i2c.o +obj-$(CONFIG_IIO_ST_SENSORS_SPI) += st_sensors_spi.o +obj-$(CONFIG_IIO_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..2170b1d --- /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->tb, 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->tb, + 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->tb, + 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->tb, 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..af3eb1f --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -0,0 +1,329 @@ +/* + * 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 +#define ST_SENSORS_G_TO_MG(x) (x*1000000ULL) + +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->tb, 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->tb, 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_scale_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_scale_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, int *index_fs_avl) +{ + 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)) { + *index_fs_avl = i; + 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, i; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + err = st_sensors_match_fs(sensor, fs, &i); + 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[i].value); + if (err < 0) + goto st_accel_set_fullscale_error; + + sdata->current_fullscale = (struct st_sensor_fullscale_avl *) + &sensor->fs.fs_avl[i]; + 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); + + mutex_init(&sdata->tb.buf_lock); + + err = st_sensors_set_enable(indio_dev, sensor, false); + if (err < 0) + goto init_error; + + err = st_sensors_set_fullscale(indio_dev, sensor, + sdata->current_fullscale->num); + 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; + + /* Enable/Disable the interrupt generator 1. */ + 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; + } + + /* Enable/Disable the interrupt generator for data ready. */ + 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->tb, sdata->dev, + ch_addr, ST_SENSORS_BYTE_FOR_CHANNEL, + outdata, sdata->multiread_bit); + if (err < 0) + goto read_error; + + *data = ((s16)le16_to_cpup((__le16 *)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..1498831 --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c @@ -0,0 +1,81 @@ +/* + * 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); + + return to_i2c_client(sdata->dev)->irq; +} + +static int st_sensors_i2c_read_byte(struct st_sensor_transfer_buffer *tb, + 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_buffer *tb, 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_buffer *tb, + struct device *dev, u8 reg_addr, u8 data) +{ + return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data); +} + +static const struct st_sensor_transfer_function st_sensors_tf_i2c = { + .read_byte = st_sensors_i2c_read_byte, + .write_byte = st_sensors_i2c_write_byte, + .read_multiple_byte = st_sensors_i2c_read_multiple_byte, +}; + +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; + + sdata->tf = (struct st_sensor_transfer_function *)&st_sensors_tf_i2c; + 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..2079f4b --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_spi.c @@ -0,0 +1,128 @@ +/* + * 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); + + return to_spi_device(sdata->dev)->irq; +} + +static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb, + 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 = tb->tx_buf, + .bits_per_word = 8, + .len = 1, + }, + { + .rx_buf = tb->rx_buf, + .bits_per_word = 8, + .len = len, + } + }; + + mutex_lock(&tb->buf_lock); + if ((multiread_bit == true) && (len > 1)) + tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD; + else + tb->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, tb->rx_buf, len*sizeof(u8)); + mutex_unlock(&tb->buf_lock); + return len; + +acc_spi_read_error: + mutex_unlock(&tb->buf_lock); + return err; +} + +static int st_sensors_spi_read_byte(struct st_sensor_transfer_buffer *tb, + struct device *dev, u8 reg_addr, u8 *res_byte) +{ + return st_sensors_spi_read(tb, dev, reg_addr, 1, res_byte, false); +} + +static int st_sensors_spi_read_multiple_byte( + struct st_sensor_transfer_buffer *tb, struct device *dev, + u8 reg_addr, int len, u8 *data, bool multiread_bit) +{ + return st_sensors_spi_read(tb, dev, reg_addr, len, data, multiread_bit); +} + +static int st_sensors_spi_write_byte(struct st_sensor_transfer_buffer *tb, + struct device *dev, u8 reg_addr, u8 data) +{ + struct spi_message msg; + int err; + + struct spi_transfer xfers = { + .tx_buf = tb->tx_buf, + .bits_per_word = 8, + .len = 2, + }; + + mutex_lock(&tb->buf_lock); + tb->tx_buf[0] = reg_addr; + tb->tx_buf[1] = data; + + spi_message_init(&msg); + spi_message_add_tail(&xfers, &msg); + err = spi_sync(to_spi_device(dev), &msg); + mutex_unlock(&tb->buf_lock); + + return err; +} + +static const struct st_sensor_transfer_function st_sensors_tf_spi = { + .read_byte = st_sensors_spi_read_byte, + .write_byte = st_sensors_spi_write_byte, + .read_multiple_byte = st_sensors_spi_read_multiple_byte, +}; + +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; + + sdata->tf = (struct st_sensor_transfer_function *)&st_sensors_tf_spi; + 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..1c69c63 --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -0,0 +1,83 @@ +/* + * 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> + +static struct iio_trigger_ops st_sensors_trigger_ops = { + .owner = THIS_MODULE, +}; + +int st_sensors_allocate_trigger(struct iio_dev *indio_dev, + int (*set_trigger_state)(struct iio_trigger *trig, bool state), + int (*try_reenable)(struct iio_trigger *trig)) +{ + 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; + + st_sensors_trigger_ops.set_trigger_state = set_trigger_state; + st_sensors_trigger_ops.try_reenable = try_reenable; + sdata->trig->private_data = indio_dev; + sdata->trig->ops = &st_sensors_trigger_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..f396bc5 --- /dev/null +++ b/include/linux/iio/common/st_sensors.h @@ -0,0 +1,278 @@ +/* + * 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_MAX_NAME 17 +#define ST_SENSORS_MAX_4WAI 7 + +#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 - ST sensor device block data update + * @addr: address of the register. + * @mask: mask to write the block data update flag. + */ +struct st_sensor_bdu { + u8 addr; + u8 mask; +}; + +/** + * struct st_sensor_data_ready_irq - ST sensor device data-ready interrupt + * @addr: address of the register. + * @mask: mask to write the on/off value. + * struct ig1 - represents the Interrupt Generator 1 of sensors. + * @en_addr: address of the enable ig1 register. + * @en_mask: mask to write the on/off value for enable. + */ +struct st_sensor_data_ready_irq { + u8 addr; + u8 mask; + struct { + u8 en_addr; + u8 en_mask; + } ig1; +}; + +/** + * struct st_sensor_transfer_buffer - ST sensor device I/O buffer + * @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. + */ +struct st_sensor_transfer_buffer { + struct mutex buf_lock; + u8 rx_buf[ST_SENSORS_RX_MAX_LENGHT]; + u8 tx_buf[ST_SENSORS_TX_MAX_LENGHT] ____cacheline_aligned; +}; + +/** + * struct st_sensor_transfer_function - ST sensor device I/O function + * @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_function { + int (*read_byte) (struct st_sensor_transfer_buffer *tb, + struct device *dev, u8 reg_addr, u8 *res_byte); + int (*write_byte) (struct st_sensor_transfer_buffer *tb, + struct device *dev, u8 reg_addr, u8 data); + int (*read_multiple_byte) (struct st_sensor_transfer_buffer *tb, + 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. + * @current_fullscale: Maximum range of measure by the sensor. + * @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. + * @odr: Output data rate of the sensor [Hz]. + * @get_irq_data_ready: Function to get the IRQ used for data ready signal. + * @tf: Transfer function structure used by I/O operations. + * @tb: Transfer buffers and mutex used by I/O operations. + */ +struct st_sensor_data { + struct device *dev; + struct iio_trigger *trig; + struct st_sensor_fullscale_avl *current_fullscale; + + bool enabled; + bool multiread_bit; + + short index; + + char *buffer_data; + + unsigned int odr; + + unsigned int (*get_irq_data_ready) (struct iio_dev *indio_dev); + + struct st_sensor_transfer_function *tf; + struct st_sensor_transfer_buffer tb; +}; + +/** + * 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 full scale 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] multi-read. + * @bootime: samples to discard when sensor passing from power-down to power-up. + */ +struct st_sensors { + u8 wai; + char sensors_supported[ST_SENSORS_MAX_4WAI][ST_SENSORS_MAX_NAME]; + 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; + unsigned int bootime; +}; + +#ifdef CONFIG_IIO_ST_SENSORS_SPI +void st_sensors_spi_configure(struct iio_dev *indio_dev, + struct spi_device *spi, struct st_sensor_data *sdata); + +unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev); +#endif + +#ifdef CONFIG_IIO_ST_SENSORS_I2C +void st_sensors_i2c_configure(struct iio_dev *indio_dev, + struct i2c_client *client, struct st_sensor_data *sdata); + +unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev); +#endif + +#ifdef CONFIG_IIO_ST_SENSORS_TRIGGERED_BUFFER +int st_sensors_allocate_trigger(struct iio_dev *indio_dev, + int (*set_trigger_state)(struct iio_trigger *trig, bool state), + int (*try_reenable)(struct iio_trigger *trig)); + +void st_sensors_deallocate_trigger(struct iio_dev *indio_dev); + +irqreturn_t st_sensors_trigger_handler(int irq, void *p); + +int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf); +#endif + +int st_sensors_init_sensor(struct iio_dev *indio_dev, + const struct st_sensors *sensor); + +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_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_scale_avl(struct iio_dev *indio_dev, + const struct st_sensor_fullscale_avl *fullscale_avl, char *buf); + +int st_sensors_set_fullscale(struct iio_dev *indio_dev, + const struct st_sensors *sensor, unsigned int fs); + +int st_sensors_set_odr(struct iio_dev *indio_dev, + const struct st_sensors *sensor, unsigned int odr); + +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); + +#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