diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-02-05 11:25:37 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-02-05 11:25:37 -0800 |
commit | 4e98fcfb95dfd9c40976dcfa2f3b878eeec4dbaa (patch) | |
tree | 8afe7ff2f2a3bf960dfd637bfa1ae56838f9f0bb | |
parent | cc400e185c07c15a42d2635995f422de5b94b696 (diff) | |
parent | 6cb2afd7c0abb93bd9dc6d36b858b1e312e2407d (diff) |
Merge tag 'iio-for-3.9c' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next
Jonathan writes:
"Third set of IIO new drivers, cleanups and fixes for the 3.9 cycle
New drivers
1) A driver for ST microelectronics sensors. This driver already covers
a large set of new parts (20 gyros, accelerometer and magnetometers)
not currently covered by the existing drivers. The intent moving forward
is to merge this with the other drivers for similar parts already in tree.
The lis3l02dq driver currently in staging/iio will be trivial, the lis3
driver in misc more complex as it has a number of additional interfaces.
Any merging in of the lis3 driver will rely on the not currently
merged iio_input bridge driver and handling of freefall notifications
etc.
2) A driver for the itg3200 gyroscope.
Graduations from staging
1) Cleanup and move out of staging of the adxrs450 gyroscope driver. The
cleanup required was all minor but there were a couple of fixes hidden in
there.
Core and driver additions
1) Initial work from Guenter Roeck on device tree support for IIO's provider/
consumer code. Focuses on the iio_hwmon driver and the max1363 adc driver.
The full device tree syntax is currently under discussion but should
follow shortly.
Cleanups and fixes
1) Remove a noop function __iio_update_buffer
2) Couple of small fixes and cleanups for the max1363
"
49 files changed, 4455 insertions, 259 deletions
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index d6003531bd6..bb594963f91 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -21,4 +21,35 @@ config KXSD9 Say yes here to build support for the Kionix KXSD9 accelerometer. Currently this only supports the device via an SPI interface. +config IIO_ST_ACCEL_3AXIS + tristate "STMicroelectronics accelerometers 3-Axis Driver" + depends on (I2C || SPI_MASTER) && SYSFS + select IIO_ST_SENSORS_CORE + select IIO_ST_ACCEL_I2C_3AXIS if (I2C) + select IIO_ST_ACCEL_SPI_3AXIS if (SPI_MASTER) + select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) + select IIO_ST_ACCEL_BUFFER if (IIO_TRIGGERED_BUFFER) + help + Say yes here to build support for STMicroelectronics accelerometers: + LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, + LIS331DLH, LSM303DL, LSM303DLM, LSM330. + + This driver can also be built as a module. If so, will be created + these modules: + - st_accel (core functions for the driver [it is mandatory]); + - st_accel_i2c (necessary for the I2C devices [optional*]); + - st_accel_spi (necessary for the SPI devices [optional*]); + + (*) one of these is necessary to do something. + +config IIO_ST_ACCEL_I2C_3AXIS + tristate + depends on IIO_ST_ACCEL_3AXIS + depends on IIO_ST_SENSORS_I2C + +config IIO_ST_ACCEL_SPI_3AXIS + tristate + depends on IIO_ST_ACCEL_3AXIS + depends on IIO_ST_SENSORS_SPI + endmenu diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 4e1c85987d1..87d8fa26489 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -3,4 +3,12 @@ # obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o + +obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o +st_accel-y := st_accel_core.o +st_accel-$(CONFIG_IIO_BUFFER) += st_accel_buffer.o + +obj-$(CONFIG_IIO_ST_ACCEL_I2C_3AXIS) += st_accel_i2c.o +obj-$(CONFIG_IIO_ST_ACCEL_SPI_3AXIS) += st_accel_spi.o + obj-$(CONFIG_KXSD9) += kxsd9.o diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h new file mode 100644 index 00000000000..37949b94377 --- /dev/null +++ b/drivers/iio/accel/st_accel.h @@ -0,0 +1,47 @@ +/* + * STMicroelectronics accelerometers driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * v. 1.0.0 + * Licensed under the GPL-2. + */ + +#ifndef ST_ACCEL_H +#define ST_ACCEL_H + +#include <linux/types.h> +#include <linux/iio/common/st_sensors.h> + +#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 LIS331DLH_ACCEL_DEV_NAME "lis331dlh" +#define LSM303DL_ACCEL_DEV_NAME "lsm303dl_accel" +#define LSM303DLH_ACCEL_DEV_NAME "lsm303dlh_accel" +#define LSM303DLM_ACCEL_DEV_NAME "lsm303dlm_accel" +#define LSM330_ACCEL_DEV_NAME "lsm330_accel" + +int st_accel_common_probe(struct iio_dev *indio_dev); +void st_accel_common_remove(struct iio_dev *indio_dev); + +#ifdef CONFIG_IIO_BUFFER +int st_accel_allocate_ring(struct iio_dev *indio_dev); +void st_accel_deallocate_ring(struct iio_dev *indio_dev); +int st_accel_trig_set_state(struct iio_trigger *trig, bool state); +#define ST_ACCEL_TRIGGER_SET_STATE (&st_accel_trig_set_state) +#else /* CONFIG_IIO_BUFFER */ +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) +{ +} +#define ST_ACCEL_TRIGGER_SET_STATE NULL +#endif /* CONFIG_IIO_BUFFER */ + +#endif /* ST_ACCEL_H */ diff --git a/drivers/iio/accel/st_accel_buffer.c b/drivers/iio/accel/st_accel_buffer.c new file mode 100644 index 00000000000..6bd82c7f769 --- /dev/null +++ b/drivers/iio/accel/st_accel_buffer.c @@ -0,0 +1,114 @@ +/* + * STMicroelectronics accelerometers driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * 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/i2c.h> +#include <linux/delay.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/common/st_sensors.h> +#include "st_accel.h" + +int st_accel_trig_set_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = trig->private_data; + + return st_sensors_set_dataready_irq(indio_dev, state); +} + +static int st_accel_buffer_preenable(struct iio_dev *indio_dev) +{ + int err; + + err = st_sensors_set_enable(indio_dev, true); + if (err < 0) + goto st_accel_set_enable_error; + + err = iio_sw_buffer_preenable(indio_dev); + +st_accel_set_enable_error: + return err; +} + +static int st_accel_buffer_postenable(struct iio_dev *indio_dev) +{ + int err; + struct st_sensor_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; + } + + err = st_sensors_set_axis_enable(indio_dev, + (u8)indio_dev->active_scan_mask[0]); + if (err < 0) + goto st_accel_buffer_postenable_error; + + err = iio_triggered_buffer_postenable(indio_dev); + if (err < 0) + goto st_accel_buffer_postenable_error; + + return err; + +st_accel_buffer_postenable_error: + kfree(adata->buffer_data); +allocate_memory_error: + return err; +} + +static int st_accel_buffer_predisable(struct iio_dev *indio_dev) +{ + int err; + struct st_sensor_data *adata = iio_priv(indio_dev); + + err = iio_triggered_buffer_predisable(indio_dev); + if (err < 0) + goto st_accel_buffer_predisable_error; + + err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS); + if (err < 0) + goto st_accel_buffer_predisable_error; + + err = st_sensors_set_enable(indio_dev, false); + +st_accel_buffer_predisable_error: + kfree(adata->buffer_data); + return err; +} + +static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops = { + .preenable = &st_accel_buffer_preenable, + .postenable = &st_accel_buffer_postenable, + .predisable = &st_accel_buffer_predisable, +}; + +int st_accel_allocate_ring(struct iio_dev *indio_dev) +{ + return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + &st_sensors_trigger_handler, &st_accel_buffer_setup_ops); +} + +void st_accel_deallocate_ring(struct iio_dev *indio_dev) +{ + iio_triggered_buffer_cleanup(indio_dev); +} + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics accelerometers buffer"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c new file mode 100644 index 00000000000..a235de252a9 --- /dev/null +++ b/drivers/iio/accel/st_accel_core.c @@ -0,0 +1,495 @@ +/* + * STMicroelectronics accelerometers driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * 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/common/st_sensors.h> +#include "st_accel.h" + +/* DEFAULT VALUE FOR SENSORS */ +#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR 0x28 +#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR 0x2a +#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR 0x2c + +/* 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_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_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 0x32 +#define ST_ACCEL_2_ODR_ADDR 0x20 +#define ST_ACCEL_2_ODR_MASK 0x18 +#define ST_ACCEL_2_ODR_AVL_50HZ_VAL 0x00 +#define ST_ACCEL_2_ODR_AVL_100HZ_VAL 0x01 +#define ST_ACCEL_2_ODR_AVL_400HZ_VAL 0x02 +#define ST_ACCEL_2_ODR_AVL_1000HZ_VAL 0x03 +#define ST_ACCEL_2_PW_ADDR 0x20 +#define ST_ACCEL_2_PW_MASK 0xe0 +#define ST_ACCEL_2_FS_ADDR 0x23 +#define ST_ACCEL_2_FS_MASK 0x30 +#define ST_ACCEL_2_FS_AVL_2_VAL 0X00 +#define ST_ACCEL_2_FS_AVL_4_VAL 0X01 +#define ST_ACCEL_2_FS_AVL_8_VAL 0x03 +#define ST_ACCEL_2_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1000) +#define ST_ACCEL_2_FS_AVL_4_GAIN IIO_G_TO_M_S_2(2000) +#define ST_ACCEL_2_FS_AVL_8_GAIN IIO_G_TO_M_S_2(3900) +#define ST_ACCEL_2_BDU_ADDR 0x23 +#define ST_ACCEL_2_BDU_MASK 0x80 +#define ST_ACCEL_2_DRDY_IRQ_ADDR 0x22 +#define ST_ACCEL_2_DRDY_IRQ_MASK 0x02 +#define ST_ACCEL_2_MULTIREAD_BIT true + +/* CUSTOM VALUES FOR SENSOR 3 */ +#define ST_ACCEL_3_WAI_EXP 0x40 +#define ST_ACCEL_3_ODR_ADDR 0x20 +#define ST_ACCEL_3_ODR_MASK 0xf0 +#define ST_ACCEL_3_ODR_AVL_3HZ_VAL 0x01 +#define ST_ACCEL_3_ODR_AVL_6HZ_VAL 0x02 +#define ST_ACCEL_3_ODR_AVL_12HZ_VAL 0x03 +#define ST_ACCEL_3_ODR_AVL_25HZ_VAL 0x04 +#define ST_ACCEL_3_ODR_AVL_50HZ_VAL 0x05 +#define ST_ACCEL_3_ODR_AVL_100HZ_VAL 0x06 +#define ST_ACCEL_3_ODR_AVL_200HZ_VAL 0x07 +#define ST_ACCEL_3_ODR_AVL_400HZ_VAL 0x08 +#define ST_ACCEL_3_ODR_AVL_800HZ_VAL 0x09 +#define ST_ACCEL_3_ODR_AVL_1600HZ_VAL 0x0a +#define ST_ACCEL_3_FS_ADDR 0x24 +#define ST_ACCEL_3_FS_MASK 0x38 +#define ST_ACCEL_3_FS_AVL_2_VAL 0X00 +#define ST_ACCEL_3_FS_AVL_4_VAL 0X01 +#define ST_ACCEL_3_FS_AVL_6_VAL 0x02 +#define ST_ACCEL_3_FS_AVL_8_VAL 0x03 +#define ST_ACCEL_3_FS_AVL_16_VAL 0x04 +#define ST_ACCEL_3_FS_AVL_2_GAIN IIO_G_TO_M_S_2(61) +#define ST_ACCEL_3_FS_AVL_4_GAIN IIO_G_TO_M_S_2(122) +#define ST_ACCEL_3_FS_AVL_6_GAIN IIO_G_TO_M_S_2(183) +#define ST_ACCEL_3_FS_AVL_8_GAIN IIO_G_TO_M_S_2(244) +#define ST_ACCEL_3_FS_AVL_16_GAIN IIO_G_TO_M_S_2(732) +#define ST_ACCEL_3_BDU_ADDR 0x20 +#define ST_ACCEL_3_BDU_MASK 0x08 +#define ST_ACCEL_3_DRDY_IRQ_ADDR 0x23 +#define ST_ACCEL_3_DRDY_IRQ_MASK 0x80 +#define ST_ACCEL_3_IG1_EN_ADDR 0x23 +#define ST_ACCEL_3_IG1_EN_MASK 0x08 +#define ST_ACCEL_3_MULTIREAD_BIT false + +static const struct iio_chan_spec st_accel_12bit_channels[] = { + ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE, + ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE, + ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE, + ST_SENSORS_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_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE, + ST_SENSORS_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE, + ST_SENSORS_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE, + ST_SENSORS_DEFAULT_16_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR), + IIO_CHAN_SOFT_TIMESTAMP(3) +}; + +static const struct st_sensors st_accel_sensors[] = { + { + .wai = ST_ACCEL_1_WAI_EXP, + .sensors_supported = { + [0] = LIS3DH_ACCEL_DEV_NAME, + [1] = LSM303DLHC_ACCEL_DEV_NAME, + [2] = LSM330D_ACCEL_DEV_NAME, + [3] = LSM330DL_ACCEL_DEV_NAME, + [4] = LSM330DLC_ACCEL_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_accel_12bit_channels, + .odr = { + .addr = ST_ACCEL_1_ODR_ADDR, + .mask = ST_ACCEL_1_ODR_MASK, + .odr_avl = { + { 1, ST_ACCEL_1_ODR_AVL_1HZ_VAL, }, + { 10, ST_ACCEL_1_ODR_AVL_10HZ_VAL, }, + { 25, ST_ACCEL_1_ODR_AVL_25HZ_VAL, }, + { 50, ST_ACCEL_1_ODR_AVL_50HZ_VAL, }, + { 100, ST_ACCEL_1_ODR_AVL_100HZ_VAL, }, + { 200, ST_ACCEL_1_ODR_AVL_200HZ_VAL, }, + { 400, ST_ACCEL_1_ODR_AVL_400HZ_VAL, }, + { 1600, ST_ACCEL_1_ODR_AVL_1600HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_ACCEL_1_ODR_ADDR, + .mask = ST_ACCEL_1_ODR_MASK, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, + .mask = ST_SENSORS_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = ST_ACCEL_1_FS_ADDR, + .mask = ST_ACCEL_1_FS_MASK, + .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, + .bootime = 2, + }, + { + .wai = ST_ACCEL_2_WAI_EXP, + .sensors_supported = { + [0] = LIS331DLH_ACCEL_DEV_NAME, + [1] = LSM303DL_ACCEL_DEV_NAME, + [2] = LSM303DLH_ACCEL_DEV_NAME, + [3] = LSM303DLM_ACCEL_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_accel_12bit_channels, + .odr = { + .addr = ST_ACCEL_2_ODR_ADDR, + .mask = ST_ACCEL_2_ODR_MASK, + .odr_avl = { + { 50, ST_ACCEL_2_ODR_AVL_50HZ_VAL, }, + { 100, ST_ACCEL_2_ODR_AVL_100HZ_VAL, }, + { 400, ST_ACCEL_2_ODR_AVL_400HZ_VAL, }, + { 1000, ST_ACCEL_2_ODR_AVL_1000HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_ACCEL_2_PW_ADDR, + .mask = ST_ACCEL_2_PW_MASK, + .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, + .mask = ST_SENSORS_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = ST_ACCEL_2_FS_ADDR, + .mask = ST_ACCEL_2_FS_MASK, + .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_8G, + .value = ST_ACCEL_2_FS_AVL_8_VAL, + .gain = ST_ACCEL_2_FS_AVL_8_GAIN, + }, + }, + }, + .bdu = { + .addr = ST_ACCEL_2_BDU_ADDR, + .mask = ST_ACCEL_2_BDU_MASK, + }, + .drdy_irq = { + .addr = ST_ACCEL_2_DRDY_IRQ_ADDR, + .mask = ST_ACCEL_2_DRDY_IRQ_MASK, + }, + .multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT, + .bootime = 2, + }, + { + .wai = ST_ACCEL_3_WAI_EXP, + .sensors_supported = { + [0] = LSM330_ACCEL_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_accel_16bit_channels, + .odr = { + .addr = ST_ACCEL_3_ODR_ADDR, + .mask = ST_ACCEL_3_ODR_MASK, + .odr_avl = { + { 3, ST_ACCEL_3_ODR_AVL_3HZ_VAL }, + { 6, ST_ACCEL_3_ODR_AVL_6HZ_VAL, }, + { 12, ST_ACCEL_3_ODR_AVL_12HZ_VAL, }, + { 25, ST_ACCEL_3_ODR_AVL_25HZ_VAL, }, + { 50, ST_ACCEL_3_ODR_AVL_50HZ_VAL, }, + { 100, ST_ACCEL_3_ODR_AVL_100HZ_VAL, }, + { 200, ST_ACCEL_3_ODR_AVL_200HZ_VAL, }, + { 400, ST_ACCEL_3_ODR_AVL_400HZ_VAL, }, + { 800, ST_ACCEL_3_ODR_AVL_800HZ_VAL, }, + { 1600, ST_ACCEL_3_ODR_AVL_1600HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_ACCEL_3_ODR_ADDR, + .mask = ST_ACCEL_3_ODR_MASK, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, + .mask = ST_SENSORS_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = ST_ACCEL_3_FS_ADDR, + .mask = ST_ACCEL_3_FS_MASK, + .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_6G, + .value = ST_ACCEL_3_FS_AVL_6_VAL, + .gain = ST_ACCEL_3_FS_AVL_6_GAIN, + }, + [3] = { + .num = ST_ACCEL_FS_AVL_8G, + .value = ST_ACCEL_3_FS_AVL_8_VAL, + .gain = ST_ACCEL_3_FS_AVL_8_GAIN, + }, + [4] = { + .num = ST_ACCEL_FS_AVL_16G, + .value = ST_ACCEL_3_FS_AVL_16_VAL, + .gain = ST_ACCEL_3_FS_AVL_16_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, + .ig1 = { + .en_addr = ST_ACCEL_3_IG1_EN_ADDR, + .en_mask = ST_ACCEL_3_IG1_EN_MASK, + }, + }, + .multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT, + .bootime = 2, + }, +}; + +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; + struct st_sensor_data *adata = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + err = st_sensors_read_info_raw(indio_dev, ch, val); + if (err < 0) + goto read_error; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = adata->current_fullscale->gain; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + +read_error: + 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; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + err = st_sensors_set_fullscale_by_gain(indio_dev, val2); + break; + default: + return -EINVAL; + } + + return err; +} + +static ST_SENSOR_DEV_ATTR_SAMP_FREQ(); +static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL(); +static ST_SENSORS_DEV_ATTR_SCALE_AVAIL(in_accel_scale_available); + +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_sampling_frequency.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_accel_attribute_group = { + .attrs = st_accel_attributes, +}; + +static const struct iio_info accel_info = { + .driver_module = THIS_MODULE, + .attrs = &st_accel_attribute_group, + .read_raw = &st_accel_read_raw, + .write_raw = &st_accel_write_raw, +}; + +static const struct iio_trigger_ops st_accel_trigger_ops = { + .owner = THIS_MODULE, + .set_trigger_state = ST_ACCEL_TRIGGER_SET_STATE, +}; + +int st_accel_common_probe(struct iio_dev *indio_dev) +{ + int err; + struct st_sensor_data *adata = iio_priv(indio_dev); + + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &accel_info; + + err = st_sensors_check_device_support(indio_dev, + ARRAY_SIZE(st_accel_sensors), st_accel_sensors); + if (err < 0) + goto st_accel_common_probe_error; + + adata->multiread_bit = adata->sensor->multi_read_bit; + indio_dev->channels = adata->sensor->ch; + indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; + + adata->current_fullscale = (struct st_sensor_fullscale_avl *) + &adata->sensor->fs.fs_avl[0]; + adata->odr = adata->sensor->odr.odr_avl[0].hz; + + err = st_sensors_init_sensor(indio_dev); + if (err < 0) + goto st_accel_common_probe_error; + + if (adata->get_irq_data_ready(indio_dev) > 0) { + err = st_accel_allocate_ring(indio_dev); + if (err < 0) + goto st_accel_common_probe_error; + + err = st_sensors_allocate_trigger(indio_dev, + &st_accel_trigger_ops); + if (err < 0) + goto st_accel_probe_trigger_error; + } + + err = iio_device_register(indio_dev); + if (err) + goto st_accel_device_register_error; + + return err; + +st_accel_device_register_error: + if (adata->get_irq_data_ready(indio_dev) > 0) + st_sensors_deallocate_trigger(indio_dev); +st_accel_probe_trigger_error: + if (adata->get_irq_data_ready(indio_dev) > 0) + st_accel_deallocate_ring(indio_dev); +st_accel_common_probe_error: + return err; +} +EXPORT_SYMBOL(st_accel_common_probe); + +void st_accel_common_remove(struct iio_dev *indio_dev) +{ + struct st_sensor_data *adata = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + if (adata->get_irq_data_ready(indio_dev) > 0) { + st_sensors_deallocate_trigger(indio_dev); + st_accel_deallocate_ring(indio_dev); + } + iio_device_free(indio_dev); +} +EXPORT_SYMBOL(st_accel_common_remove); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics accelerometers driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c new file mode 100644 index 00000000000..90b8ddfb61e --- /dev/null +++ b/drivers/iio/accel/st_accel_i2c.c @@ -0,0 +1,87 @@ +/* + * STMicroelectronics accelerometers driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * 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/common/st_sensors.h> +#include <linux/iio/common/st_sensors_i2c.h> +#include "st_accel.h" + +static int st_accel_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct st_sensor_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; + + st_sensors_i2c_configure(indio_dev, client, adata); + + err = st_accel_common_probe(indio_dev); + if (err < 0) + goto st_accel_common_probe_error; + + return 0; + +st_accel_common_probe_error: + iio_device_free(indio_dev); +iio_device_alloc_error: + return err; +} + +static int st_accel_i2c_remove(struct i2c_client *client) +{ + st_accel_common_remove(i2c_get_clientdata(client)); + + 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 }, + { 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 = st_accel_i2c_remove, + .id_table = st_accel_id_table, +}; +module_i2c_driver(st_accel_driver); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c new file mode 100644 index 00000000000..dbd45c08711 --- /dev/null +++ b/drivers/iio/accel/st_accel_spi.c @@ -0,0 +1,86 @@ +/* + * STMicroelectronics accelerometers driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * 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/common/st_sensors.h> +#include <linux/iio/common/st_sensors_spi.h> +#include "st_accel.h" + +static int st_accel_spi_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct st_sensor_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; + + st_sensors_spi_configure(indio_dev, spi, adata); + + err = st_accel_common_probe(indio_dev); + if (err < 0) + goto st_accel_common_probe_error; + + return 0; + +st_accel_common_probe_error: + iio_device_free(indio_dev); +iio_device_alloc_error: + return err; +} + +static int st_accel_spi_remove(struct spi_device *spi) +{ + st_accel_common_remove(spi_get_drvdata(spi)); + + 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 }, + { 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 = st_accel_spi_remove, + .id_table = st_accel_id_table, +}; +module_spi_driver(st_accel_driver); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/lp8788_adc.c b/drivers/iio/adc/lp8788_adc.c index f8bcb1f5892..763f57565ee 100644 --- a/drivers/iio/adc/lp8788_adc.c +++ b/drivers/iio/adc/lp8788_adc.c @@ -187,12 +187,6 @@ static int lp8788_iio_map_register(struct iio_dev *indio_dev, return 0; } -static inline void lp8788_iio_map_unregister(struct iio_dev *indio_dev, - struct lp8788_adc *adc) -{ - iio_map_array_unregister(indio_dev, adc->map); -} - static int lp8788_adc_probe(struct platform_device *pdev) { struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); @@ -208,6 +202,7 @@ static int lp8788_adc_probe(struct platform_device *pdev) adc->lp = lp; platform_set_drvdata(pdev, indio_dev); + indio_dev->dev.of_node = pdev->dev.of_node; ret = lp8788_iio_map_register(indio_dev, lp->pdata, adc); if (ret) goto err_iio_map; @@ -230,7 +225,7 @@ static int lp8788_adc_probe(struct platform_device *pdev) return 0; err_iio_device: - lp8788_iio_map_unregister(indio_dev, adc); + iio_map_array_unregister(indio_dev); err_iio_map: iio_device_free(indio_dev); return ret; @@ -239,10 +234,9 @@ err_iio_map: static int lp8788_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct lp8788_adc *adc = iio_priv(indio_dev); iio_device_unregister(indio_dev); - lp8788_iio_map_unregister(indio_dev, adc); + iio_map_array_unregister(indio_dev); iio_device_free(indio_dev); return 0; diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 5db56f5ce7d..08e4feb4f6e 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -335,7 +335,7 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev, { int ret = 0; s32 data; - char rxbuf[2]; + u8 rxbuf[2]; struct max1363_state *st = iio_priv(indio_dev); struct i2c_client *client = st->client; @@ -367,7 +367,8 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev, ret = data; goto error_ret; } - data = (s32)(rxbuf[1]) | ((s32)(rxbuf[0] & 0x0F)) << 8; + data = (rxbuf[1] | rxbuf[0] << 8) & + ((1 << st->chip_info->bits) - 1); } else { /* Get reading */ data = i2c_master_recv(client, rxbuf, 1); @@ -1496,6 +1497,7 @@ static int max1363_probe(struct i2c_client *client, goto error_out; } + indio_dev->dev.of_node = client->dev.of_node; ret = iio_map_array_register(indio_dev, client->dev.platform_data); if (ret < 0) goto error_free_device; @@ -1529,8 +1531,6 @@ static int max1363_probe(struct i2c_client *client, indio_dev->num_channels = st->chip_info->num_channels; indio_dev->info = st->chip_info->info; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = st->chip_info->channels; - indio_dev->num_channels = st->chip_info->num_channels; ret = max1363_initial_setup(st); if (ret < 0) goto error_free_available_scan_masks; @@ -1569,7 +1569,7 @@ error_disable_reg: error_put_reg: regulator_put(st->reg); error_unregister_map: - iio_map_array_unregister(indio_dev, client->dev.platform_data); + iio_map_array_unregister(indio_dev); error_free_device: iio_device_free(indio_dev); error_out: @@ -1588,7 +1588,7 @@ static int max1363_remove(struct i2c_client *client) kfree(indio_dev->available_scan_masks); regulator_disable(st->reg); regulator_put(st->reg); - iio_map_array_unregister(indio_dev, client->dev.platform_data); + iio_map_array_unregister(indio_dev); iio_device_free(indio_dev); return 0; diff --git a/drivers/iio/buffer_cb.c b/drivers/iio/buffer_cb.c index 4d40e24f372..9201022945e 100644 --- a/drivers/iio/buffer_cb.c +++ b/drivers/iio/buffer_cb.c @@ -25,7 +25,7 @@ static struct iio_buffer_access_funcs iio_cb_access = { .store_to = &iio_buffer_cb_store_to, }; -struct iio_cb_buffer *iio_channel_get_all_cb(const char *name, +struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev, int (*cb)(u8 *data, void *private), void *private) @@ -46,7 +46,7 @@ struct iio_cb_buffer *iio_channel_get_all_cb(const char *name, cb_buff->buffer.access = &iio_cb_access; INIT_LIST_HEAD(&cb_buff->buffer.demux_list); - cb_buff->channels = iio_channel_get_all(name); + cb_buff->channels = iio_channel_get_all(dev); if (IS_ERR(cb_buff->channels)) { ret = PTR_ERR(cb_buff->channels); goto error_free_cb_buff; diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig index ed45ee54500..0b6e97d18fa 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 81584009b21..c2352beb5d9 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 00000000000..865f1ca33eb --- /dev/null +++ b/drivers/iio/common/st_sensors/Kconfig @@ -0,0 +1,14 @@ +# +# STMicroelectronics sensors common library +# + +config IIO_ST_SENSORS_I2C + tristate + +config IIO_ST_SENSORS_SPI + tristate + +config IIO_ST_SENSORS_CORE + tristate + select IIO_ST_SENSORS_I2C if I2C + select IIO_ST_SENSORS_SPI if SPI_MASTER diff --git a/drivers/iio/common/st_sensors/Makefile b/drivers/iio/common/st_sensors/Makefile new file mode 100644 index 00000000000..9f3e24f3024 --- /dev/null +++ b/drivers/iio/common/st_sensors/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the STMicroelectronics sensor common modules. +# + +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_CORE) += st_sensors.o +st_sensors-y := st_sensors_core.o +st_sensors-$(CONFIG_IIO_BUFFER) += st_sensors_buffer.o +st_sensors-$(CONFIG_IIO_TRIGGER) += st_sensors_trigger.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 00000000000..09b236d6ee8 --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c @@ -0,0 +1,116 @@ +/* + * STMicroelectronics sensors buffer library driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * 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@st.com>"); +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 00000000000..0198324a8b0 --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -0,0 +1,446 @@ +/* + * STMicroelectronics sensors core library driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * Licensed under the GPL-2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/iio/iio.h> +#include <asm/unaligned.h> + +#include <linux/iio/common/st_sensors.h> + + +#define ST_SENSORS_WAI_ADDRESS 0x0f + +static int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, + u8 reg_addr, u8 mask, 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; +} + +static int st_sensors_match_odr(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 == 0) + goto st_sensors_match_odr_error; + + 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; + } + } + +st_sensors_match_odr_error: + return ret; +} + +int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr) +{ + int err; + struct st_sensor_odr_avl odr_out; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + err = st_sensors_match_odr(sdata->sensor, odr, &odr_out); + if (err < 0) + goto st_sensors_match_odr_error; + + if ((sdata->sensor->odr.addr == sdata->sensor->pw.addr) && + (sdata->sensor->odr.mask == sdata->sensor->pw.mask)) { + if (sdata->enabled == true) { + err = st_sensors_write_data_with_mask(indio_dev, + sdata->sensor->odr.addr, + sdata->sensor->odr.mask, + odr_out.value); + } else { + err = 0; + } + } else { + err = st_sensors_write_data_with_mask(indio_dev, + sdata->sensor->odr.addr, sdata->sensor->odr.mask, + 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(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 == 0) + goto st_sensors_match_odr_error; + + if (sensor->fs.fs_avl[i].num == fs) { + *index_fs_avl = i; + ret = 0; + break; + } + } + +st_sensors_match_odr_error: + return ret; +} + +static int st_sensors_set_fullscale(struct iio_dev *indio_dev, unsigned int fs) +{ + int err, i; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + err = st_sensors_match_fs(sdata->sensor, fs, &i); + if (err < 0) + goto st_accel_set_fullscale_error; + + err = st_sensors_write_data_with_mask(indio_dev, + sdata->sensor->fs.addr, + sdata->sensor->fs.mask, + sdata->sensor->fs.fs_avl[i].value); + if (err < 0) + goto st_accel_set_fullscale_error; + + sdata->current_fullscale = (struct st_sensor_fullscale_avl *) + &sdata->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; +} + +int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable) +{ + bool found; + u8 tmp_value; + int err = -EINVAL; + struct st_sensor_odr_avl odr_out; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + if (enable) { + found = false; + tmp_value = sdata->sensor->pw.value_on; + if ((sdata->sensor->odr.addr == sdata->sensor->pw.addr) && + (sdata->sensor->odr.mask == sdata->sensor->pw.mask)) { + err = st_sensors_match_odr(sdata->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, + sdata->sensor->pw.addr, + sdata->sensor->pw.mask, 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, + sdata->sensor->pw.addr, + sdata->sensor->pw.mask, + sdata->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, u8 axis_enable) +{ + struct st_sensor_data *sdata = iio_priv(indio_dev); + + return st_sensors_write_data_with_mask(indio_dev, + sdata->sensor->enable_axis.addr, + sdata->sensor->enable_axis.mask, axis_enable); +} +EXPORT_SYMBOL(st_sensors_set_axis_enable); + +int st_sensors_init_sensor(struct iio_dev *indio_dev) +{ + int err; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + mutex_init(&sdata->tb.buf_lock); + + err = st_sensors_set_enable(indio_dev, false); + if (err < 0) + goto init_error; + + err = st_sensors_set_fullscale(indio_dev, + sdata->current_fullscale->num); + if (err < 0) + goto init_error; + + err = st_sensors_set_odr(indio_dev, sdata->odr); + if (err < 0) + goto init_error; + + /* set BDU */ + err = st_sensors_write_data_with_mask(indio_dev, + sdata->sensor->bdu.addr, sdata->sensor->bdu.mask, true); + if (err < 0) + goto init_error; + + err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS); + +init_error: + return err; +} +EXPORT_SYMBOL(st_sensors_init_sensor); + +int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable) +{ + int err; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + /* Enable/Disable the interrupt generator 1. */ + if (sdata->sensor->drdy_irq.ig1.en_addr > 0) { + err = st_sensors_write_data_with_mask(indio_dev, + sdata->sensor->drdy_irq.ig1.en_addr, + sdata->sensor->drdy_irq.ig1.en_mask, (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, + sdata->sensor->drdy_irq.addr, + sdata->sensor->drdy_irq.mask, (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, int scale) +{ + int err = -EINVAL, i; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) { + if ((sdata->sensor->fs.fs_avl[i].gain == scale) && + (sdata->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, + sdata->sensor->fs.fs_avl[i].num); + +st_sensors_match_scale_error: + return err; +} +EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain); + +static 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)get_unaligned_le16(outdata); + +read_error: + return err; +} + +int st_sensors_read_info_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *ch, int *val) +{ + int err; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + mutex_lock(&indio_dev->mlock); + if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) { + err = -EBUSY; + goto read_error; + } else { + err = st_sensors_set_enable(indio_dev, true); + if (err < 0) + goto read_error; + + msleep((sdata->sensor->bootime * 1000) / sdata->odr); + err = st_sensors_read_axis_data(indio_dev, ch->address, val); + if (err < 0) + goto read_error; + + *val = *val >> ch->scan_type.shift; + } + mutex_unlock(&indio_dev->mlock); + + return err; + +read_error: + mutex_unlock(&indio_dev->mlock); + return err; +} +EXPORT_SYMBOL(st_sensors_read_info_raw); + +int st_sensors_check_device_support(struct iio_dev *indio_dev, + int num_sensors_list, const struct st_sensors *sensors) +{ + u8 wai; + int i, n, err; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + err = sdata->tf->read_byte(&sdata->tb, sdata->dev, + ST_SENSORS_DEFAULT_WAI_ADDRESS, &wai); + if (err < 0) { + dev_err(&indio_dev->dev, "failed to read Who-Am-I register.\n"); + goto read_wai_error; + } + + for (i = 0; i < num_sensors_list; i++) { + if (sensors[i].wai == wai) + break; + } + if (i == num_sensors_list) + goto device_not_supported; + + for (n = 0; n < ARRAY_SIZE(sensors[i].sensors_supported); n++) { + if (strcmp(indio_dev->name, + &sensors[i].sensors_supported[n][0]) == 0) + break; + } + if (n == ARRAY_SIZE(sensors[i].sensors_supported)) { + dev_err(&indio_dev->dev, "device name and WhoAmI mismatch.\n"); + goto sensor_name_mismatch; + } + + sdata->sensor = (struct st_sensors *)&sensors[i]; + + return i; + +device_not_supported: + dev_err(&indio_dev->dev, "device not supported: WhoAmI (0x%x).\n", wai); +sensor_name_mismatch: + err = -ENODEV; +read_wai_error: + return err; +} +EXPORT_SYMBOL(st_sensors_check_device_support); + +ssize_t st_sensors_sysfs_get_sampling_frequency(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct st_sensor_data *adata = iio_priv(dev_get_drvdata(dev)); + + return sprintf(buf, "%d\n", adata->odr); +} +EXPORT_SYMBOL(st_sensors_sysfs_get_sampling_frequency); + +ssize_t st_sensors_sysfs_set_sampling_frequency(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int err; + unsigned int odr; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + err = kstrtoint(buf, 10, &odr); + if (err < 0) + goto conversion_error; + + mutex_lock(&indio_dev->mlock); + err = st_sensors_set_odr(indio_dev, odr); + mutex_unlock(&indio_dev->mlock); + +conversion_error: + return err < 0 ? err : size; +} +EXPORT_SYMBOL(st_sensors_sysfs_set_sampling_frequency); + +ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i, len = 0; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct st_sensor_data *sdata = iio_priv(indio_dev); + + mutex_lock(&indio_dev->mlock); + for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) { + if (sdata->sensor->odr.odr_avl[i].hz == 0) + break; + + len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", + sdata->sensor->odr.odr_avl[i].hz); + } + mutex_unlock(&indio_dev->mlock); + buf[len - 1] = '\n'; + + return len; +} +EXPORT_SYMBOL(st_sensors_sysfs_sampling_frequency_avail); + +ssize_t st_sensors_sysfs_scale_avail(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i, len = 0; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct st_sensor_data *sdata = iio_priv(indio_dev); + + mutex_lock(&indio_dev->mlock); + for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) { + if (sdata->sensor->fs.fs_avl[i].num == 0) + break; + + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ", + sdata->sensor->fs.fs_avl[i].gain); + } + mutex_unlock(&indio_dev->mlock); + buf[len - 1] = '\n'; + + return len; +} +EXPORT_SYMBOL(st_sensors_sysfs_scale_avail); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +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 00000000000..38af9440c10 --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c @@ -0,0 +1,81 @@ +/* + * STMicroelectronics sensors i2c library driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * 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_i2c.h> + + +#define ST_SENSORS_I2C_MULTIREAD 0x80 + +static 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) + 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 = &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@st.com>"); +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 00000000000..f0aa2f10522 --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_spi.c @@ -0,0 +1,128 @@ +/* + * STMicroelectronics sensors spi library driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * 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_spi.h> + + +#define ST_SENSORS_SPI_MULTIREAD 0xc0 +#define ST_SENSORS_SPI_READ 0x80 + +static 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) && (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 = &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@st.com>"); +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 00000000000..139ed030abb --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -0,0 +1,77 @@ +/* + * STMicroelectronics sensors trigger library driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * 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 *trigger_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 = 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@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig index 752ac8a3448..6be4628faff 100644 --- a/drivers/iio/gyro/Kconfig +++ b/drivers/iio/gyro/Kconfig @@ -19,6 +19,16 @@ config ADIS16136 Say yes here to build support for the Analog Devices ADIS16133, ADIS16135, ADIS16136 gyroscope devices. +config ADXRS450 + tristate "Analog Devices ADXRS450/3 Digital Output Gyroscope SPI driver" + depends on SPI + help + Say yes here to build support for Analog Devices ADXRS450 and ADXRS453 + programmable digital output gyroscope. + + This driver can also be built as a module. If so, the module + will be called adxrs450. + config HID_SENSOR_GYRO_3D depends on HID_SENSOR_HUB select IIO_BUFFER @@ -30,4 +40,42 @@ config HID_SENSOR_GYRO_3D Say yes here to build support for the HID SENSOR Gyroscope 3D. +config IIO_ST_GYRO_3AXIS + tristate "STMicroelectronics gyroscopes 3-Axis Driver" + depends on (I2C || SPI_MASTER) && SYSFS + select IIO_ST_SENSORS_CORE + select IIO_ST_GYRO_I2C_3AXIS if (I2C) + select IIO_ST_GYRO_SPI_3AXIS if (SPI_MASTER) + select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) + select IIO_ST_GYRO_BUFFER if (IIO_TRIGGERED_BUFFER) + help + Say yes here to build support for STMicroelectronics gyroscopes: + L3G4200D, LSM330DL, L3GD20, L3GD20H, LSM330DLC, L3G4IS, LSM330. + + This driver can also be built as a module. If so, will be created + these modules: + - st_gyro (core functions for the driver [it is mandatory]); + - st_gyro_i2c (necessary for the I2C devices [optional*]); + - st_gyro_spi (necessary for the SPI devices [optional*]); + + (*) one of these is necessary to do something. + +config IIO_ST_GYRO_I2C_3AXIS + tristate + depends on IIO_ST_GYRO_3AXIS + depends on IIO_ST_SENSORS_I2C + +config IIO_ST_GYRO_SPI_3AXIS + tristate + depends on IIO_ST_GYRO_3AXIS + depends on IIO_ST_SENSORS_SPI + +config ITG3200 + tristate "InvenSense ITG3200 Digital 3-Axis Gyroscope I2C driver" + depends on I2C + select IIO_TRIGGERED_BUFFER if IIO_BUFFER + help + Say yes here to add support for the InvenSense ITG3200 digital + 3-axis gyroscope sensor. + endmenu diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile index 9b090ee084d..225d289082e 100644 --- a/drivers/iio/gyro/Makefile +++ b/drivers/iio/gyro/Makefile @@ -4,4 +4,17 @@ obj-$(CONFIG_ADIS16080) += adis16080.o obj-$(CONFIG_ADIS16136) += adis16136.o +obj-$(CONFIG_ADXRS450) += adxrs450.o + obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o + +itg3200-y := itg3200_core.o +itg3200-$(CONFIG_IIO_BUFFER) += itg3200_buffer.o +obj-$(CONFIG_ITG3200) += itg3200.o + +obj-$(CONFIG_IIO_ST_GYRO_3AXIS) += st_gyro.o +st_gyro-y := st_gyro_core.o +st_gyro-$(CONFIG_IIO_BUFFER) += st_gyro_buffer.o + +obj-$(CONFIG_IIO_ST_GYRO_I2C_3AXIS) += st_gyro_i2c.o +obj-$(CONFIG_IIO_ST_GYRO_SPI_3AXIS) += st_gyro_spi.o diff --git a/drivers/staging/iio/gyro/adxrs450_core.c b/drivers/iio/gyro/adxrs450.c index f0ce81da8ac..d9d43831c38 100644 --- a/drivers/staging/iio/gyro/adxrs450_core.c +++ b/drivers/iio/gyro/adxrs450.c @@ -21,45 +21,110 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> -#include "adxrs450.h" +#define ADXRS450_STARTUP_DELAY 50 /* ms */ + +/* The MSB for the spi commands */ +#define ADXRS450_SENSOR_DATA (0x20 << 24) +#define ADXRS450_WRITE_DATA (0x40 << 24) +#define ADXRS450_READ_DATA (0x80 << 24) + +#define ADXRS450_RATE1 0x00 /* Rate Registers */ +#define ADXRS450_TEMP1 0x02 /* Temperature Registers */ +#define ADXRS450_LOCST1 0x04 /* Low CST Memory Registers */ +#define ADXRS450_HICST1 0x06 /* High CST Memory Registers */ +#define ADXRS450_QUAD1 0x08 /* Quad Memory Registers */ +#define ADXRS450_FAULT1 0x0A /* Fault Registers */ +#define ADXRS450_PID1 0x0C /* Part ID Register 1 */ +#define ADXRS450_SNH 0x0E /* Serial Number Registers, 4 bytes */ +#define ADXRS450_SNL 0x10 +#define ADXRS450_DNC1 0x12 /* Dynamic Null Correction Registers */ +/* Check bits */ +#define ADXRS450_P 0x01 +#define ADXRS450_CHK 0x02 +#define ADXRS450_CST 0x04 +#define ADXRS450_PWR 0x08 +#define ADXRS450_POR 0x10 +#define ADXRS450_NVM 0x20 +#define ADXRS450_Q 0x40 +#define ADXRS450_PLL 0x80 +#define ADXRS450_UV 0x100 +#define ADXRS450_OV 0x200 +#define ADXRS450_AMP 0x400 +#define ADXRS450_FAIL 0x800 + +#define ADXRS450_WRERR_MASK (0x7 << 29) + +#define ADXRS450_MAX_RX 4 +#define ADXRS450_MAX_TX 4 + +#define ADXRS450_GET_ST(a) ((a >> 26) & 0x3) + +enum { + ID_ADXRS450, + ID_ADXRS453, +}; + +/** + * struct adxrs450_state - device instance specific data + * @us: actual spi_device + * @buf_lock: mutex to protect tx and rx + * @tx: transmit buffer + * @rx: receive buffer + **/ +struct adxrs450_state { + struct spi_device *us; + struct mutex buf_lock; + __be32 tx ____cacheline_aligned; + __be32 rx; + +}; /** * adxrs450_spi_read_reg_16() - read 2 bytes from a register pair - * @dev: device associated with child of actual iio_dev - * @reg_address: the address of the lower of the two registers,which should be an even address, - * Second register's address is reg_address + 1. + * @indio_dev: device associated with child of actual iio_dev + * @reg_address: the address of the lower of the two registers, which should be + * an even address, the second register's address is reg_address + 1. * @val: somewhere to pass back the value read **/ static int adxrs450_spi_read_reg_16(struct iio_dev *indio_dev, u8 reg_address, u16 *val) { + struct spi_message msg; struct adxrs450_state *st = iio_priv(indio_dev); + u32 tx; int ret; + struct spi_transfer xfers[] = { + { + .tx_buf = &st->tx, + .bits_per_word = 8, + .len = sizeof(st->tx), + .cs_change = 1, + }, { + .rx_buf = &st->rx, + .bits_per_word = 8, + .len = sizeof(st->rx), + }, + }; mutex_lock(&st->buf_lock); - st->tx[0] = ADXRS450_READ_DATA | (reg_address >> 7); - st->tx[1] = reg_address << 1; - st->tx[2] = 0; - st->tx[3] = 0; + tx = ADXRS450_READ_DATA | (reg_address << 17); - if (!(hweight32(be32_to_cpu(*(u32 *)st->tx)) & 1)) - st->tx[3] |= ADXRS450_P; + if (!(hweight32(tx) & 1)) + tx |= ADXRS450_P; - ret = spi_write(st->us, st->tx, 4); - if (ret) { - dev_err(&st->us->dev, "problem while reading 16 bit register 0x%02x\n", - reg_address); - goto error_ret; - } - ret = spi_read(st->us, st->rx, 4); + st->tx = cpu_to_be32(tx); + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); if (ret) { dev_err(&st->us->dev, "problem while reading 16 bit register 0x%02x\n", reg_address); goto error_ret; } - *val = (be32_to_cpu(*(u32 *)st->rx) >> 5) & 0xFFFF; + *val = (be32_to_cpu(st->rx) >> 5) & 0xFFFF; error_ret: mutex_unlock(&st->buf_lock); @@ -68,9 +133,9 @@ error_ret: /** * adxrs450_spi_write_reg_16() - write 2 bytes data to a register pair - * @dev: device associated with child of actual actual iio_dev - * @reg_address: the address of the lower of the two registers,which should be an even address, - * Second register's address is reg_address + 1. + * @indio_dev: device associated with child of actual actual iio_dev + * @reg_address: the address of the lower of the two registers,which should be + * an even address, the second register's address is reg_address + 1. * @val: value to be written. **/ static int adxrs450_spi_write_reg_16(struct iio_dev *indio_dev, @@ -78,55 +143,61 @@ static int adxrs450_spi_write_reg_16(struct iio_dev *indio_dev, u16 val) { struct adxrs450_state *st = iio_priv(indio_dev); + u32 tx; int ret; mutex_lock(&st->buf_lock); - st->tx[0] = ADXRS450_WRITE_DATA | reg_address >> 7; - st->tx[1] = reg_address << 1 | val >> 15; - st->tx[2] = val >> 7; - st->tx[3] = val << 1; + tx = ADXRS450_WRITE_DATA | (reg_address << 17) | (val << 1); - if (!(hweight32(be32_to_cpu(*(u32 *)st->tx)) & 1)) - st->tx[3] |= ADXRS450_P; + if (!(hweight32(tx) & 1)) + tx |= ADXRS450_P; - ret = spi_write(st->us, st->tx, 4); + st->tx = cpu_to_be32(tx); + ret = spi_write(st->us, &st->tx, sizeof(st->tx)); if (ret) dev_err(&st->us->dev, "problem while writing 16 bit register 0x%02x\n", reg_address); - msleep(1); /* enforce sequential transfer delay 0.1ms */ + usleep_range(100, 1000); /* enforce sequential transfer delay 0.1ms */ mutex_unlock(&st->buf_lock); return ret; } /** * adxrs450_spi_sensor_data() - read 2 bytes sensor data - * @dev: device associated with child of actual iio_dev + * @indio_dev: device associated with child of actual iio_dev * @val: somewhere to pass back the value read **/ static int adxrs450_spi_sensor_data(struct iio_dev *indio_dev, s16 *val) { + struct spi_message msg; struct adxrs450_state *st = iio_priv(indio_dev); int ret; + struct spi_transfer xfers[] = { + { + .tx_buf = &st->tx, + .bits_per_word = 8, + .len = sizeof(st->tx), + .cs_change = 1, + }, { + .rx_buf = &st->rx, + .bits_per_word = 8, + .len = sizeof(st->rx), + }, + }; mutex_lock(&st->buf_lock); - st->tx[0] = ADXRS450_SENSOR_DATA; - st->tx[1] = 0; - st->tx[2] = 0; - st->tx[3] = 0; + st->tx = cpu_to_be32(ADXRS450_SENSOR_DATA); - ret = spi_write(st->us, st->tx, 4); - if (ret) { - dev_err(&st->us->dev, "Problem while reading sensor data\n"); - goto error_ret; - } - - ret = spi_read(st->us, st->rx, 4); + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + ret = spi_sync(st->us, &msg); if (ret) { dev_err(&st->us->dev, "Problem while reading sensor data\n"); goto error_ret; } - *val = (be32_to_cpu(*(u32 *)st->rx) >> 10) & 0xFFFF; + *val = (be32_to_cpu(st->rx) >> 10) & 0xFFFF; error_ret: mutex_unlock(&st->buf_lock); @@ -137,26 +208,26 @@ error_ret: * adxrs450_spi_initial() - use for initializing procedure. * @st: device instance specific data * @val: somewhere to pass back the value read + * @chk: Whether to perform fault check **/ static int adxrs450_spi_initial(struct adxrs450_state *st, u32 *val, char chk) { struct spi_message msg; int ret; + u32 tx; struct spi_transfer xfers = { - .tx_buf = st->tx, - .rx_buf = st->rx, + .tx_buf = &st->tx, + .rx_buf = &st->rx, .bits_per_word = 8, - .len = 4, + .len = sizeof(st->tx), }; mutex_lock(&st->buf_lock); - st->tx[0] = ADXRS450_SENSOR_DATA; - st->tx[1] = 0; - st->tx[2] = 0; - st->tx[3] = 0; + tx = ADXRS450_SENSOR_DATA; if (chk) - st->tx[3] |= (ADXRS450_CHK | ADXRS450_P); + tx |= (ADXRS450_CHK | ADXRS450_P); + st->tx = cpu_to_be32(tx); spi_message_init(&msg); spi_message_add_tail(&xfers, &msg); ret = spi_sync(st->us, &msg); @@ -165,7 +236,7 @@ static int adxrs450_spi_initial(struct adxrs450_state *st, goto error_ret; } - *val = be32_to_cpu(*(u32 *)st->rx); + *val = be32_to_cpu(st->rx); error_ret: mutex_unlock(&st->buf_lock); @@ -185,8 +256,7 @@ static int adxrs450_initial_setup(struct iio_dev *indio_dev) if (ret) return ret; if (t != 0x01) - dev_warn(&st->us->dev, "The initial power on response " - "is not correct! Restart without reset?\n"); + dev_warn(&st->us->dev, "The initial power on response is not correct! Restart without reset?\n"); msleep(ADXRS450_STARTUP_DELAY); ret = adxrs450_spi_initial(st, &t, 0); @@ -217,20 +287,6 @@ static int adxrs450_initial_setup(struct iio_dev *indio_dev) dev_err(&st->us->dev, "The device is not in normal status!\n"); return -EINVAL; } - ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_PID1, &data); - if (ret) - return ret; - dev_info(&st->us->dev, "The Part ID is 0x%x\n", data); - - ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_SNL, &data); - if (ret) - return ret; - t = data; - ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_SNH, &data); - if (ret) - return ret; - t |= data << 16; - dev_info(&st->us->dev, "The Serial Number is 0x%x\n", t); return 0; } @@ -244,9 +300,10 @@ static int adxrs450_write_raw(struct iio_dev *indio_dev, int ret; switch (mask) { case IIO_CHAN_INFO_CALIBBIAS: + if (val < -0x400 || val >= 0x400) + return -EINVAL; ret = adxrs450_spi_write_reg_16(indio_dev, - ADXRS450_DNC1, - val & 0x3FF); + ADXRS450_DNC1, val); break; default: ret = -EINVAL; @@ -312,7 +369,7 @@ static int adxrs450_read_raw(struct iio_dev *indio_dev, ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_DNC1, &t); if (ret) break; - *val = t; + *val = sign_extend32(t, 9); ret = IIO_VAL_INT; break; default: diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c new file mode 100644 index 00000000000..f667d2c8c00 --- /dev/null +++ b/drivers/iio/gyro/itg3200_buffer.c @@ -0,0 +1,156 @@ +/* + * itg3200_buffer.c -- support InvenSense ITG3200 + * Digital 3-Axis Gyroscope driver + * + * Copyright (c) 2011 Christian Strobel <christian.strobel@iis.fraunhofer.de> + * Copyright (c) 2011 Manuel Stahl <manuel.stahl@iis.fraunhofer.de> + * Copyright (c) 2012 Thorsten Nowak <thorsten.nowak@iis.fraunhofer.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> + +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/gyro/itg3200.h> + + +static int itg3200_read_all_channels(struct i2c_client *i2c, __be16 *buf) +{ + u8 tx = 0x80 | ITG3200_REG_TEMP_OUT_H; + struct i2c_msg msg[2] = { + { + .addr = i2c->addr, + .flags = i2c->flags, + .len = 1, + .buf = &tx, + }, + { + .addr = i2c->addr, + .flags = i2c->flags | I2C_M_RD, + .len = ITG3200_SCAN_ELEMENTS * sizeof(s16), + .buf = (char *)&buf, + }, + }; + + return i2c_transfer(i2c->adapter, msg, 2); +} + +static irqreturn_t itg3200_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct itg3200 *st = iio_priv(indio_dev); + __be16 buf[ITG3200_SCAN_ELEMENTS + sizeof(s64)/sizeof(u16)]; + + int ret = itg3200_read_all_channels(st->i2c, buf); + if (ret < 0) + goto error_ret; + + if (indio_dev->scan_timestamp) + memcpy(buf + indio_dev->scan_bytes - sizeof(s64), + &pf->timestamp, sizeof(pf->timestamp)); + + iio_push_to_buffers(indio_dev, (u8 *)buf); + iio_trigger_notify_done(indio_dev->trig); + +error_ret: + return IRQ_HANDLED; +} + +int itg3200_buffer_configure(struct iio_dev *indio_dev) +{ + return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + itg3200_trigger_handler, NULL); +} + +void itg3200_buffer_unconfigure(struct iio_dev *indio_dev) +{ + iio_triggered_buffer_cleanup(indio_dev); +} + + +static int itg3200_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct iio_dev *indio_dev = trig->private_data; + int ret; + u8 msc; + + ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_IRQ_CONFIG, &msc); + if (ret) + goto error_ret; + + if (state) + msc |= ITG3200_IRQ_DATA_RDY_ENABLE; + else + msc &= ~ITG3200_IRQ_DATA_RDY_ENABLE; + + ret = itg3200_write_reg_8(indio_dev, ITG3200_REG_IRQ_CONFIG, msc); + if (ret) + goto error_ret; + +error_ret: + return ret; + +} + +static const struct iio_trigger_ops itg3200_trigger_ops = { + .owner = THIS_MODULE, + .set_trigger_state = &itg3200_data_rdy_trigger_set_state, +}; + +int itg3200_probe_trigger(struct iio_dev *indio_dev) +{ + int ret; + struct itg3200 *st = iio_priv(indio_dev); + + st->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name, + indio_dev->id); + if (!st->trig) + return -ENOMEM; + + ret = request_irq(st->i2c->irq, + &iio_trigger_generic_data_rdy_poll, + IRQF_TRIGGER_RISING, + "itg3200_data_rdy", + st->trig); + if (ret) + goto error_free_trig; + + + st->trig->dev.parent = &st->i2c->dev; + st->trig->ops = &itg3200_trigger_ops; + st->trig->private_data = indio_dev; + ret = iio_trigger_register(st->trig); + if (ret) + goto error_free_irq; + + /* select default trigger */ + indio_dev->trig = st->trig; + + return 0; + +error_free_irq: + free_irq(st->i2c->irq, st->trig); +error_free_trig: + iio_trigger_free(st->trig); + return ret; +} + +void itg3200_remove_trigger(struct iio_dev *indio_dev) +{ + struct itg3200 *st = iio_priv(indio_dev); + + iio_trigger_unregister(st->trig); + free_irq(st->i2c->irq, st->trig); + iio_trigger_free(st->trig); +} diff --git a/drivers/iio/gyro/itg3200_core.c b/drivers/iio/gyro/itg3200_core.c new file mode 100644 index 00000000000..df2e6aa5d73 --- /dev/null +++ b/drivers/iio/gyro/itg3200_core.c @@ -0,0 +1,401 @@ +/* + * itg3200_core.c -- support InvenSense ITG3200 + * Digital 3-Axis Gyroscope driver + * + * Copyright (c) 2011 Christian Strobel <christian.strobel@iis.fraunhofer.de> + * Copyright (c) 2011 Manuel Stahl <manuel.stahl@iis.fraunhofer.de> + * Copyright (c) 2012 Thorsten Nowak <thorsten.nowak@iis.fraunhofer.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * TODO: + * - Support digital low pass filter + * - Support power management + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/module.h> +#include <linux/delay.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> +#include <linux/iio/buffer.h> + +#include <linux/iio/gyro/itg3200.h> + + +int itg3200_write_reg_8(struct iio_dev *indio_dev, + u8 reg_address, u8 val) +{ + struct itg3200 *st = iio_priv(indio_dev); + + return i2c_smbus_write_byte_data(st->i2c, 0x80 | reg_address, val); +} + +int itg3200_read_reg_8(struct iio_dev *indio_dev, + u8 reg_address, u8 *val) +{ + struct itg3200 *st = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_read_byte_data(st->i2c, reg_address); + if (ret < 0) + return ret; + *val = ret; + return 0; +} + +static int itg3200_read_reg_s16(struct iio_dev *indio_dev, u8 lower_reg_address, + int *val) +{ + struct itg3200 *st = iio_priv(indio_dev); + struct i2c_client *client = st->i2c; + int ret; + s16 out; + + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = client->flags, + .len = 1, + .buf = (char *)&lower_reg_address, + }, + { + .addr = client->addr, + .flags = client->flags | I2C_M_RD, + .len = 2, + .buf = (char *)&out, + }, + }; + + lower_reg_address |= 0x80; + ret = i2c_transfer(client->adapter, msg, 2); + be16_to_cpus(&out); + *val = out; + + return (ret == 2) ? 0 : ret; +} + +static int itg3200_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long info) +{ + int ret = 0; + u8 reg; + + switch (info) { + case IIO_CHAN_INFO_RAW: + reg = (u8)chan->address; + ret = itg3200_read_reg_s16(indio_dev, reg, val); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + if (chan->type == IIO_TEMP) + *val2 = 1000000000/280; + else + *val2 = 1214142; /* (1 / 14,375) * (PI / 180) */ + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + /* Only the temperature channel has an offset */ + *val = 23000; + return IIO_VAL_INT; + default: + return -EINVAL; + } + + return ret; +} + +static ssize_t itg3200_read_frequency(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + int ret, sps; + u8 val; + + ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_DLPF, &val); + if (ret) + return ret; + + sps = (val & ITG3200_DLPF_CFG_MASK) ? 1000 : 8000; + + ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_SAMPLE_RATE_DIV, &val); + if (ret) + return ret; + + sps /= val + 1; + + return sprintf(buf, "%d\n", sps); +} + +static ssize_t itg3200_write_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + unsigned val; + int ret; + u8 t; + + ret = kstrtouint(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + + ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_DLPF, &t); + if (ret) + goto err_ret; + + if (val == 0) { + ret = -EINVAL; + goto err_ret; + } + t = ((t & ITG3200_DLPF_CFG_MASK) ? 1000u : 8000u) / val - 1; + + ret = itg3200_write_reg_8(indio_dev, ITG3200_REG_SAMPLE_RATE_DIV, t); + +err_ret: + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +/* + * Reset device and internal registers to the power-up-default settings + * Use the gyro clock as reference, as suggested by the datasheet + */ +static int itg3200_reset(struct iio_dev *indio_dev) +{ + struct itg3200 *st = iio_priv(indio_dev); + int ret; + + dev_dbg(&st->i2c->dev, "reset device"); + + ret = itg3200_write_reg_8(indio_dev, + ITG3200_REG_POWER_MANAGEMENT, + ITG3200_RESET); + if (ret) { + dev_err(&st->i2c->dev, "error resetting device"); + goto error_ret; + } + + /* Wait for PLL (1ms according to datasheet) */ + udelay(1500); + + ret = itg3200_write_reg_8(indio_dev, + ITG3200_REG_IRQ_CONFIG, + ITG3200_IRQ_ACTIVE_HIGH | + ITG3200_IRQ_PUSH_PULL | + ITG3200_IRQ_LATCH_50US_PULSE | + ITG3200_IRQ_LATCH_CLEAR_ANY); + + if (ret) + dev_err(&st->i2c->dev, "error init device"); + +error_ret: + return ret; +} + +/* itg3200_enable_full_scale() - Disables the digital low pass filter */ +static int itg3200_enable_full_scale(struct iio_dev *indio_dev) +{ + u8 val; + int ret; + + ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_DLPF, &val); + if (ret) + goto err_ret; + + val |= ITG3200_DLPF_FS_SEL_2000; + return itg3200_write_reg_8(indio_dev, ITG3200_REG_DLPF, val); + +err_ret: + return ret; +} + +static int itg3200_initial_setup(struct iio_dev *indio_dev) +{ + struct itg3200 *st = iio_priv(indio_dev); + int ret; + u8 val; + + ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_ADDRESS, &val); + if (ret) + goto err_ret; + + if (((val >> 1) & 0x3f) != 0x34) { + dev_err(&st->i2c->dev, "invalid reg value 0x%02x", val); + ret = -ENXIO; + goto err_ret; + } + + ret = itg3200_reset(indio_dev); + if (ret) + goto err_ret; + + ret = itg3200_enable_full_scale(indio_dev); +err_ret: + return ret; +} + +#define ITG3200_TEMP_INFO_MASK (IIO_CHAN_INFO_OFFSET_SHARED_BIT | \ + IIO_CHAN_INFO_SCALE_SHARED_BIT | \ + IIO_CHAN_INFO_RAW_SEPARATE_BIT) +#define ITG3200_GYRO_INFO_MASK (IIO_CHAN_INFO_SCALE_SHARED_BIT | \ + IIO_CHAN_INFO_RAW_SEPARATE_BIT) + +#define ITG3200_ST \ + { .sign = 's', .realbits = 16, .storagebits = 16, .endianness = IIO_BE } + +#define ITG3200_GYRO_CHAN(_mod) { \ + .type = IIO_ANGL_VEL, \ + .modified = 1, \ + .channel2 = IIO_MOD_ ## _mod, \ + .info_mask = ITG3200_GYRO_INFO_MASK, \ + .address = ITG3200_REG_GYRO_ ## _mod ## OUT_H, \ + .scan_index = ITG3200_SCAN_GYRO_ ## _mod, \ + .scan_type = ITG3200_ST, \ +} + +static const struct iio_chan_spec itg3200_channels[] = { + { + .type = IIO_TEMP, + .channel2 = IIO_NO_MOD, + .info_mask = ITG3200_TEMP_INFO_MASK, + .address = ITG3200_REG_TEMP_OUT_H, + .scan_index = ITG3200_SCAN_TEMP, + .scan_type = ITG3200_ST, + }, + ITG3200_GYRO_CHAN(X), + ITG3200_GYRO_CHAN(Y), + ITG3200_GYRO_CHAN(Z), + IIO_CHAN_SOFT_TIMESTAMP(ITG3200_SCAN_ELEMENTS), +}; + +/* IIO device attributes */ +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, itg3200_read_frequency, + itg3200_write_frequency); + +static struct attribute *itg3200_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + NULL +}; + +static const struct attribute_group itg3200_attribute_group = { + .attrs = itg3200_attributes, +}; + +static const struct iio_info itg3200_info = { + .attrs = &itg3200_attribute_group, + .read_raw = &itg3200_read_raw, + .driver_module = THIS_MODULE, +}; + +static const unsigned long itg3200_available_scan_masks[] = { 0xffffffff, 0x0 }; + +static int itg3200_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct itg3200 *st; + struct iio_dev *indio_dev; + + dev_dbg(&client->dev, "probe I2C dev with IRQ %i", client->irq); + + indio_dev = iio_device_alloc(sizeof(*st)); + if (indio_dev == NULL) { + ret = -ENOMEM; + goto error_ret; + } + + st = iio_priv(indio_dev); + + i2c_set_clientdata(client, indio_dev); + st->i2c = client; + + indio_dev->dev.parent = &client->dev; + indio_dev->name = client->dev.driver->name; + indio_dev->channels = itg3200_channels; + indio_dev->num_channels = ARRAY_SIZE(itg3200_channels); + indio_dev->available_scan_masks = itg3200_available_scan_masks; + indio_dev->info = &itg3200_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = itg3200_buffer_configure(indio_dev); + if (ret) + goto error_free_dev; + + if (client->irq) { + ret = itg3200_probe_trigger(indio_dev); + if (ret) + goto error_unconfigure_buffer; + } + + ret = itg3200_initial_setup(indio_dev); + if (ret) + goto error_remove_trigger; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_remove_trigger; + + return 0; + +error_remove_trigger: + if (client->irq) + itg3200_remove_trigger(indio_dev); +error_unconfigure_buffer: + itg3200_buffer_unconfigure(indio_dev); +error_free_dev: + iio_device_free(indio_dev); +error_ret: + return ret; +} + +static int itg3200_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + iio_device_unregister(indio_dev); + + if (client->irq) + itg3200_remove_trigger(indio_dev); + + itg3200_buffer_unconfigure(indio_dev); + + iio_device_free(indio_dev); + + return 0; +} + +static const struct i2c_device_id itg3200_id[] = { + { "itg3200", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, itg3200_id); + +static struct i2c_driver itg3200_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "itg3200", + }, + .id_table = itg3200_id, + .probe = itg3200_probe, + .remove = itg3200_remove, +}; + +module_i2c_driver(itg3200_driver); + +MODULE_AUTHOR("Christian Strobel <christian.strobel@iis.fraunhofer.de>"); +MODULE_DESCRIPTION("ITG3200 Gyroscope I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/st_gyro.h b/drivers/iio/gyro/st_gyro.h new file mode 100644 index 00000000000..3ad9907bb15 --- /dev/null +++ b/drivers/iio/gyro/st_gyro.h @@ -0,0 +1,45 @@ +/* + * STMicroelectronics gyroscopes driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * v. 1.0.0 + * Licensed under the GPL-2. + */ + +#ifndef ST_GYRO_H +#define ST_GYRO_H + +#include <linux/types.h> +#include <linux/iio/common/st_sensors.h> + +#define L3G4200D_GYRO_DEV_NAME "l3g4200d" +#define LSM330D_GYRO_DEV_NAME "lsm330d_gyro" +#define LSM330DL_GYRO_DEV_NAME "lsm330dl_gyro" +#define LSM330DLC_GYRO_DEV_NAME "lsm330dlc_gyro" +#define L3GD20_GYRO_DEV_NAME "l3gd20" +#define L3GD20H_GYRO_DEV_NAME "l3gd20h" +#define L3G4IS_GYRO_DEV_NAME "l3g4is_ui" +#define LSM330_GYRO_DEV_NAME "lsm330_gyro" + +int st_gyro_common_probe(struct iio_dev *indio_dev); +void st_gyro_common_remove(struct iio_dev *indio_dev); + +#ifdef CONFIG_IIO_BUFFER +int st_gyro_allocate_ring(struct iio_dev *indio_dev); +void st_gyro_deallocate_ring(struct iio_dev *indio_dev); +int st_gyro_trig_set_state(struct iio_trigger *trig, bool state); +#define ST_GYRO_TRIGGER_SET_STATE (&st_gyro_trig_set_state) +#else /* CONFIG_IIO_BUFFER */ +static inline int st_gyro_allocate_ring(struct iio_dev *indio_dev) +{ + return 0; +} +static inline void st_gyro_deallocate_ring(struct iio_dev *indio_dev) +{ +} +#define ST_GYRO_TRIGGER_SET_STATE NULL +#endif /* CONFIG_IIO_BUFFER */ + +#endif /* ST_GYRO_H */ diff --git a/drivers/iio/gyro/st_gyro_buffer.c b/drivers/iio/gyro/st_gyro_buffer.c new file mode 100644 index 00000000000..da4d122ec7d --- /dev/null +++ b/drivers/iio/gyro/st_gyro_buffer.c @@ -0,0 +1,114 @@ +/* + * STMicroelectronics gyroscopes driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * 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/i2c.h> +#include <linux/delay.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/common/st_sensors.h> +#include "st_gyro.h" + +int st_gyro_trig_set_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = trig->private_data; + + return st_sensors_set_dataready_irq(indio_dev, state); +} + +static int st_gyro_buffer_preenable(struct iio_dev *indio_dev) +{ + int err; + + err = st_sensors_set_enable(indio_dev, true); + if (err < 0) + goto st_gyro_set_enable_error; + + err = iio_sw_buffer_preenable(indio_dev); + +st_gyro_set_enable_error: + return err; +} + +static int st_gyro_buffer_postenable(struct iio_dev *indio_dev) +{ + int err; + struct st_sensor_data *gdata = iio_priv(indio_dev); + + gdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); + if (gdata->buffer_data == NULL) { + err = -ENOMEM; + goto allocate_memory_error; + } + + err = st_sensors_set_axis_enable(indio_dev, + (u8)indio_dev->active_scan_mask[0]); + if (err < 0) + goto st_gyro_buffer_postenable_error; + + err = iio_triggered_buffer_postenable(indio_dev); + if (err < 0) + goto st_gyro_buffer_postenable_error; + + return err; + +st_gyro_buffer_postenable_error: + kfree(gdata->buffer_data); +allocate_memory_error: + return err; +} + +static int st_gyro_buffer_predisable(struct iio_dev *indio_dev) +{ + int err; + struct st_sensor_data *gdata = iio_priv(indio_dev); + + err = iio_triggered_buffer_predisable(indio_dev); + if (err < 0) + goto st_gyro_buffer_predisable_error; + + err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS); + if (err < 0) + goto st_gyro_buffer_predisable_error; + + err = st_sensors_set_enable(indio_dev, false); + +st_gyro_buffer_predisable_error: + kfree(gdata->buffer_data); + return err; +} + +static const struct iio_buffer_setup_ops st_gyro_buffer_setup_ops = { + .preenable = &st_gyro_buffer_preenable, + .postenable = &st_gyro_buffer_postenable, + .predisable = &st_gyro_buffer_predisable, +}; + +int st_gyro_allocate_ring(struct iio_dev *indio_dev) +{ + return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + &st_sensors_trigger_handler, &st_gyro_buffer_setup_ops); +} + +void st_gyro_deallocate_ring(struct iio_dev *indio_dev) +{ + iio_triggered_buffer_cleanup(indio_dev); +} + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics gyroscopes buffer"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c new file mode 100644 index 00000000000..0a09998276c --- /dev/null +++ b/drivers/iio/gyro/st_gyro_core.c @@ -0,0 +1,363 @@ +/* + * STMicroelectronics gyroscopes driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * 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/delay.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/common/st_sensors.h> +#include "st_gyro.h" + +/* DEFAULT VALUE FOR SENSORS */ +#define ST_GYRO_DEFAULT_OUT_X_L_ADDR 0x28 +#define ST_GYRO_DEFAULT_OUT_Y_L_ADDR 0x2a +#define ST_GYRO_DEFAULT_OUT_Z_L_ADDR 0x2c + +/* FULLSCALE */ +#define ST_GYRO_FS_AVL_250DPS 250 +#define ST_GYRO_FS_AVL_500DPS 500 +#define ST_GYRO_FS_AVL_2000DPS 2000 + +/* CUSTOM VALUES FOR SENSOR 1 */ +#define ST_GYRO_1_WAI_EXP 0xd3 +#define ST_GYRO_1_ODR_ADDR 0x20 +#define ST_GYRO_1_ODR_MASK 0xc0 +#define ST_GYRO_1_ODR_AVL_100HZ_VAL 0x00 +#define ST_GYRO_1_ODR_AVL_200HZ_VAL 0x01 +#define ST_GYRO_1_ODR_AVL_400HZ_VAL 0x02 +#define ST_GYRO_1_ODR_AVL_800HZ_VAL 0x03 +#define ST_GYRO_1_PW_ADDR 0x20 +#define ST_GYRO_1_PW_MASK 0x08 +#define ST_GYRO_1_FS_ADDR 0x23 +#define ST_GYRO_1_FS_MASK 0x30 +#define ST_GYRO_1_FS_AVL_250_VAL 0x00 +#define ST_GYRO_1_FS_AVL_500_VAL 0x01 +#define ST_GYRO_1_FS_AVL_2000_VAL 0x02 +#define ST_GYRO_1_FS_AVL_250_GAIN IIO_DEGREE_TO_RAD(8750) +#define ST_GYRO_1_FS_AVL_500_GAIN IIO_DEGREE_TO_RAD(17500) +#define ST_GYRO_1_FS_AVL_2000_GAIN IIO_DEGREE_TO_RAD(70000) +#define ST_GYRO_1_BDU_ADDR 0x23 +#define ST_GYRO_1_BDU_MASK 0x80 +#define ST_GYRO_1_DRDY_IRQ_ADDR 0x22 +#define ST_GYRO_1_DRDY_IRQ_MASK 0x08 +#define ST_GYRO_1_MULTIREAD_BIT true + +/* CUSTOM VALUES FOR SENSOR 2 */ +#define ST_GYRO_2_WAI_EXP 0xd4 +#define ST_GYRO_2_ODR_ADDR 0x20 +#define ST_GYRO_2_ODR_MASK 0xc0 +#define ST_GYRO_2_ODR_AVL_95HZ_VAL 0x00 +#define ST_GYRO_2_ODR_AVL_190HZ_VAL 0x01 +#define ST_GYRO_2_ODR_AVL_380HZ_VAL 0x02 +#define ST_GYRO_2_ODR_AVL_760HZ_VAL 0x03 +#define ST_GYRO_2_PW_ADDR 0x20 +#define ST_GYRO_2_PW_MASK 0x08 +#define ST_GYRO_2_FS_ADDR 0x23 +#define ST_GYRO_2_FS_MASK 0x30 +#define ST_GYRO_2_FS_AVL_250_VAL 0x00 +#define ST_GYRO_2_FS_AVL_500_VAL 0x01 +#define ST_GYRO_2_FS_AVL_2000_VAL 0x02 +#define ST_GYRO_2_FS_AVL_250_GAIN IIO_DEGREE_TO_RAD(8750) +#define ST_GYRO_2_FS_AVL_500_GAIN IIO_DEGREE_TO_RAD(17500) +#define ST_GYRO_2_FS_AVL_2000_GAIN IIO_DEGREE_TO_RAD(70000) +#define ST_GYRO_2_BDU_ADDR 0x23 +#define ST_GYRO_2_BDU_MASK 0x80 +#define ST_GYRO_2_DRDY_IRQ_ADDR 0x22 +#define ST_GYRO_2_DRDY_IRQ_MASK 0x08 +#define ST_GYRO_2_MULTIREAD_BIT true + +static const struct iio_chan_spec st_gyro_16bit_channels[] = { + ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_X, + IIO_MOD_X, IIO_LE, ST_SENSORS_DEFAULT_16_REALBITS, + ST_GYRO_DEFAULT_OUT_X_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_Y, + IIO_MOD_Y, IIO_LE, ST_SENSORS_DEFAULT_16_REALBITS, + ST_GYRO_DEFAULT_OUT_Y_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, ST_SENSORS_SCAN_Z, + IIO_MOD_Z, IIO_LE, ST_SENSORS_DEFAULT_16_REALBITS, + ST_GYRO_DEFAULT_OUT_Z_L_ADDR), + IIO_CHAN_SOFT_TIMESTAMP(3) +}; + +static const struct st_sensors st_gyro_sensors[] = { + { + .wai = ST_GYRO_1_WAI_EXP, + .sensors_supported = { + [0] = L3G4200D_GYRO_DEV_NAME, + [1] = LSM330DL_GYRO_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_gyro_16bit_channels, + .odr = { + .addr = ST_GYRO_1_ODR_ADDR, + .mask = ST_GYRO_1_ODR_MASK, + .odr_avl = { + { 100, ST_GYRO_1_ODR_AVL_100HZ_VAL, }, + { 200, ST_GYRO_1_ODR_AVL_200HZ_VAL, }, + { 400, ST_GYRO_1_ODR_AVL_400HZ_VAL, }, + { 800, ST_GYRO_1_ODR_AVL_800HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_GYRO_1_PW_ADDR, + .mask = ST_GYRO_1_PW_MASK, + .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, + .mask = ST_SENSORS_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = ST_GYRO_1_FS_ADDR, + .mask = ST_GYRO_1_FS_MASK, + .fs_avl = { + [0] = { + .num = ST_GYRO_FS_AVL_250DPS, + .value = ST_GYRO_1_FS_AVL_250_VAL, + .gain = ST_GYRO_1_FS_AVL_250_GAIN, + }, + [1] = { + .num = ST_GYRO_FS_AVL_500DPS, + .value = ST_GYRO_1_FS_AVL_500_VAL, + .gain = ST_GYRO_1_FS_AVL_500_GAIN, + }, + [2] = { + .num = ST_GYRO_FS_AVL_2000DPS, + .value = ST_GYRO_1_FS_AVL_2000_VAL, + .gain = ST_GYRO_1_FS_AVL_2000_GAIN, + }, + }, + }, + .bdu = { + .addr = ST_GYRO_1_BDU_ADDR, + .mask = ST_GYRO_1_BDU_MASK, + }, + .drdy_irq = { + .addr = ST_GYRO_1_DRDY_IRQ_ADDR, + .mask = ST_GYRO_1_DRDY_IRQ_MASK, + }, + .multi_read_bit = ST_GYRO_1_MULTIREAD_BIT, + .bootime = 2, + }, + { + .wai = ST_GYRO_2_WAI_EXP, + .sensors_supported = { + [0] = L3GD20_GYRO_DEV_NAME, + [1] = L3GD20H_GYRO_DEV_NAME, + [2] = LSM330D_GYRO_DEV_NAME, + [3] = LSM330DLC_GYRO_DEV_NAME, + [4] = L3G4IS_GYRO_DEV_NAME, + [5] = LSM330_GYRO_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_gyro_16bit_channels, + .odr = { + .addr = ST_GYRO_2_ODR_ADDR, + .mask = ST_GYRO_2_ODR_MASK, + .odr_avl = { + { 95, ST_GYRO_2_ODR_AVL_95HZ_VAL, }, + { 190, ST_GYRO_2_ODR_AVL_190HZ_VAL, }, + { 380, ST_GYRO_2_ODR_AVL_380HZ_VAL, }, + { 760, ST_GYRO_2_ODR_AVL_760HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_GYRO_2_PW_ADDR, + .mask = ST_GYRO_2_PW_MASK, + .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, + .mask = ST_SENSORS_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = ST_GYRO_2_FS_ADDR, + .mask = ST_GYRO_2_FS_MASK, + .fs_avl = { + [0] = { + .num = ST_GYRO_FS_AVL_250DPS, + .value = ST_GYRO_2_FS_AVL_250_VAL, + .gain = ST_GYRO_2_FS_AVL_250_GAIN, + }, + [1] = { + .num = ST_GYRO_FS_AVL_500DPS, + .value = ST_GYRO_2_FS_AVL_500_VAL, + .gain = ST_GYRO_2_FS_AVL_500_GAIN, + }, + [2] = { + .num = ST_GYRO_FS_AVL_2000DPS, + .value = ST_GYRO_2_FS_AVL_2000_VAL, + .gain = ST_GYRO_2_FS_AVL_2000_GAIN, + }, + }, + }, + .bdu = { + .addr = ST_GYRO_2_BDU_ADDR, + .mask = ST_GYRO_2_BDU_MASK, + }, + .drdy_irq = { + .addr = ST_GYRO_2_DRDY_IRQ_ADDR, + .mask = ST_GYRO_2_DRDY_IRQ_MASK, + }, + .multi_read_bit = ST_GYRO_2_MULTIREAD_BIT, + .bootime = 2, + }, +}; + +static int st_gyro_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *ch, int *val, + int *val2, long mask) +{ + int err; + struct st_sensor_data *gdata = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + err = st_sensors_read_info_raw(indio_dev, ch, val); + if (err < 0) + goto read_error; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = gdata->current_fullscale->gain; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + +read_error: + return err; +} + +static int st_gyro_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + int err; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + err = st_sensors_set_fullscale_by_gain(indio_dev, val2); + break; + default: + err = -EINVAL; + } + + return err; +} + +static ST_SENSOR_DEV_ATTR_SAMP_FREQ(); +static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL(); +static ST_SENSORS_DEV_ATTR_SCALE_AVAIL(in_anglvel_scale_available); + +static struct attribute *st_gyro_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_anglvel_scale_available.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_gyro_attribute_group = { + .attrs = st_gyro_attributes, +}; + +static const struct iio_info gyro_info = { + .driver_module = THIS_MODULE, + .attrs = &st_gyro_attribute_group, + .read_raw = &st_gyro_read_raw, + .write_raw = &st_gyro_write_raw, +}; + +static const struct iio_trigger_ops st_gyro_trigger_ops = { + .owner = THIS_MODULE, + .set_trigger_state = ST_GYRO_TRIGGER_SET_STATE, +}; + +int st_gyro_common_probe(struct iio_dev *indio_dev) +{ + int err; + struct st_sensor_data *gdata = iio_priv(indio_dev); + + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &gyro_info; + + err = st_sensors_check_device_support(indio_dev, + ARRAY_SIZE(st_gyro_sensors), st_gyro_sensors); + if (err < 0) + goto st_gyro_common_probe_error; + + gdata->multiread_bit = gdata->sensor->multi_read_bit; + indio_dev->channels = gdata->sensor->ch; + indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; + + gdata->current_fullscale = (struct st_sensor_fullscale_avl *) + &gdata->sensor->fs.fs_avl[0]; + gdata->odr = gdata->sensor->odr.odr_avl[0].hz; + + err = st_sensors_init_sensor(indio_dev); + if (err < 0) + goto st_gyro_common_probe_error; + + if (gdata->get_irq_data_ready(indio_dev) > 0) { + err = st_gyro_allocate_ring(indio_dev); + if (err < 0) + goto st_gyro_common_probe_error; + + err = st_sensors_allocate_trigger(indio_dev, + &st_gyro_trigger_ops); + if (err < 0) + goto st_gyro_probe_trigger_error; + } + + err = iio_device_register(indio_dev); + if (err) + goto st_gyro_device_register_error; + + return err; + +st_gyro_device_register_error: + if (gdata->get_irq_data_ready(indio_dev) > 0) + st_sensors_deallocate_trigger(indio_dev); +st_gyro_probe_trigger_error: + if (gdata->get_irq_data_ready(indio_dev) > 0) + st_gyro_deallocate_ring(indio_dev); +st_gyro_common_probe_error: + return err; +} +EXPORT_SYMBOL(st_gyro_common_probe); + +void st_gyro_common_remove(struct iio_dev *indio_dev) +{ + struct st_sensor_data *gdata = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + if (gdata->get_irq_data_ready(indio_dev) > 0) { + st_sensors_deallocate_trigger(indio_dev); + st_gyro_deallocate_ring(indio_dev); + } + iio_device_free(indio_dev); +} +EXPORT_SYMBOL(st_gyro_common_remove); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics gyroscopes driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c new file mode 100644 index 00000000000..a44b5b4a201 --- /dev/null +++ b/drivers/iio/gyro/st_gyro_i2c.c @@ -0,0 +1,85 @@ +/* + * STMicroelectronics gyroscopes driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * 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/common/st_sensors.h> +#include <linux/iio/common/st_sensors_i2c.h> +#include "st_gyro.h" + +static int st_gyro_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct st_sensor_data *gdata; + int err; + + indio_dev = iio_device_alloc(sizeof(*gdata)); + if (indio_dev == NULL) { + err = -ENOMEM; + goto iio_device_alloc_error; + } + + gdata = iio_priv(indio_dev); + gdata->dev = &client->dev; + + st_sensors_i2c_configure(indio_dev, client, gdata); + + err = st_gyro_common_probe(indio_dev); + if (err < 0) + goto st_gyro_common_probe_error; + + return 0; + +st_gyro_common_probe_error: + iio_device_free(indio_dev); +iio_device_alloc_error: + return err; +} + +static int st_gyro_i2c_remove(struct i2c_client *client) +{ + st_gyro_common_remove(i2c_get_clientdata(client)); + + return 0; +} + +static const struct i2c_device_id st_gyro_id_table[] = { + { L3G4200D_GYRO_DEV_NAME }, + { LSM330D_GYRO_DEV_NAME }, + { LSM330DL_GYRO_DEV_NAME }, + { LSM330DLC_GYRO_DEV_NAME }, + { L3GD20_GYRO_DEV_NAME }, + { L3GD20H_GYRO_DEV_NAME }, + { L3G4IS_GYRO_DEV_NAME }, + { LSM330_GYRO_DEV_NAME }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, st_gyro_id_table); + +static struct i2c_driver st_gyro_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "st-gyro-i2c", + }, + .probe = st_gyro_i2c_probe, + .remove = st_gyro_i2c_remove, + .id_table = st_gyro_id_table, +}; +module_i2c_driver(st_gyro_driver); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics gyroscopes i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c new file mode 100644 index 00000000000..8b4dcc5eab0 --- /dev/null +++ b/drivers/iio/gyro/st_gyro_spi.c @@ -0,0 +1,84 @@ +/* + * STMicroelectronics gyroscopes driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * 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/common/st_sensors.h> +#include <linux/iio/common/st_sensors_spi.h> +#include "st_gyro.h" + +static int st_gyro_spi_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct st_sensor_data *gdata; + int err; + + indio_dev = iio_device_alloc(sizeof(*gdata)); + if (indio_dev == NULL) { + err = -ENOMEM; + goto iio_device_alloc_error; + } + + gdata = iio_priv(indio_dev); + gdata->dev = &spi->dev; + + st_sensors_spi_configure(indio_dev, spi, gdata); + + err = st_gyro_common_probe(indio_dev); + if (err < 0) + goto st_gyro_common_probe_error; + + return 0; + +st_gyro_common_probe_error: + iio_device_free(indio_dev); +iio_device_alloc_error: + return err; +} + +static int st_gyro_spi_remove(struct spi_device *spi) +{ + st_gyro_common_remove(spi_get_drvdata(spi)); + + return 0; +} + +static const struct spi_device_id st_gyro_id_table[] = { + { L3G4200D_GYRO_DEV_NAME }, + { LSM330D_GYRO_DEV_NAME }, + { LSM330DL_GYRO_DEV_NAME }, + { LSM330DLC_GYRO_DEV_NAME }, + { L3GD20_GYRO_DEV_NAME }, + { L3GD20H_GYRO_DEV_NAME }, + { L3G4IS_GYRO_DEV_NAME }, + { LSM330_GYRO_DEV_NAME }, + {}, +}; +MODULE_DEVICE_TABLE(spi, st_gyro_id_table); + +static struct spi_driver st_gyro_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "st-gyro-spi", + }, + .probe = st_gyro_spi_probe, + .remove = st_gyro_spi_remove, + .id_table = st_gyro_id_table, +}; +module_spi_driver(st_gyro_driver); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics gyroscopes spi driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index d55e98fb300..c42aba6817e 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -54,39 +54,25 @@ error_ret: EXPORT_SYMBOL_GPL(iio_map_array_register); -/* Assumes the exact same array (e.g. memory locations) - * used at unregistration as used at registration rather than - * more complex checking of contents. +/* + * Remove all map entries associated with the given iio device */ -int iio_map_array_unregister(struct iio_dev *indio_dev, - struct iio_map *maps) +int iio_map_array_unregister(struct iio_dev *indio_dev) { - int i = 0, ret = 0; - bool found_it; + int ret = -ENODEV; struct iio_map_internal *mapi; - - if (maps == NULL) - return 0; + struct list_head *pos, *tmp; mutex_lock(&iio_map_list_lock); - while (maps[i].consumer_dev_name != NULL) { - found_it = false; - list_for_each_entry(mapi, &iio_map_list, l) - if (&maps[i] == mapi->map) { - list_del(&mapi->l); - kfree(mapi); - found_it = true; - break; - } - if (!found_it) { - ret = -ENODEV; - goto error_ret; + list_for_each_safe(pos, tmp, &iio_map_list) { + mapi = list_entry(pos, struct iio_map_internal, l); + if (indio_dev == mapi->indio_dev) { + list_del(&mapi->l); + kfree(mapi); + ret = 0; } - i++; } -error_ret: mutex_unlock(&iio_map_list_lock); - return ret; } EXPORT_SYMBOL_GPL(iio_map_array_unregister); @@ -167,16 +153,18 @@ void iio_channel_release(struct iio_channel *channel) } EXPORT_SYMBOL_GPL(iio_channel_release); -struct iio_channel *iio_channel_get_all(const char *name) +struct iio_channel *iio_channel_get_all(struct device *dev) { + const char *name; struct iio_channel *chans; struct iio_map_internal *c = NULL; int nummaps = 0; int mapind = 0; int i, ret; - if (name == NULL) + if (dev == NULL) return ERR_PTR(-EINVAL); + name = dev_name(dev); mutex_lock(&iio_map_list_lock); /* first count the matching maps */ diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c index 5bc5c860e9c..a923c78d5cb 100644 --- a/drivers/iio/kfifo_buf.c +++ b/drivers/iio/kfifo_buf.c @@ -22,7 +22,6 @@ static inline int __iio_allocate_kfifo(struct iio_kfifo *buf, if ((length == 0) || (bytes_per_datum == 0)) return -EINVAL; - __iio_update_buffer(&buf->buffer, bytes_per_datum, length); return __kfifo_alloc((struct __kfifo *)&buf->kf, length, bytes_per_datum, GFP_KERNEL); } diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index ff11d68225c..cd29be54f64 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -14,4 +14,34 @@ config HID_SENSOR_MAGNETOMETER_3D Say yes here to build support for the HID SENSOR Magnetometer 3D. +config IIO_ST_MAGN_3AXIS + tristate "STMicroelectronics magnetometers 3-Axis Driver" + depends on (I2C || SPI_MASTER) && SYSFS + select IIO_ST_SENSORS_CORE + select IIO_ST_MAGN_I2C_3AXIS if (I2C) + select IIO_ST_MAGN_SPI_3AXIS if (SPI_MASTER) + select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) + select IIO_ST_MAGN_BUFFER if (IIO_TRIGGERED_BUFFER) + help + Say yes here to build support for STMicroelectronics magnetometers: + LSM303DLHC, LSM303DLM, LIS3MDL. + + This driver can also be built as a module. If so, will be created + these modules: + - st_magn (core functions for the driver [it is mandatory]); + - st_magn_i2c (necessary for the I2C devices [optional*]); + - st_magn_spi (necessary for the SPI devices [optional*]); + + (*) one of these is necessary to do something. + +config IIO_ST_MAGN_I2C_3AXIS + tristate + depends on IIO_ST_MAGN_3AXIS + depends on IIO_ST_SENSORS_I2C + +config IIO_ST_MAGN_SPI_3AXIS + tristate + depends on IIO_ST_MAGN_3AXIS + depends on IIO_ST_SENSORS_SPI + endmenu diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index 60dc4f2b196..e78672876dc 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -3,3 +3,10 @@ # obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o + +obj-$(CONFIG_IIO_ST_MAGN_3AXIS) += st_magn.o +st_magn-y := st_magn_core.o +st_magn-$(CONFIG_IIO_BUFFER) += st_magn_buffer.o + +obj-$(CONFIG_IIO_ST_MAGN_I2C_3AXIS) += st_magn_i2c.o +obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o diff --git a/drivers/iio/magnetometer/st_magn.h b/drivers/iio/magnetometer/st_magn.h new file mode 100644 index 00000000000..7e81d00ef0c --- /dev/null +++ b/drivers/iio/magnetometer/st_magn.h @@ -0,0 +1,45 @@ +/* + * STMicroelectronics magnetometers driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * v. 1.0.0 + * Licensed under the GPL-2. + */ + +#ifndef ST_MAGN_H +#define ST_MAGN_H + +#include <linux/types.h> +#include <linux/iio/common/st_sensors.h> + +#define LSM303DLHC_MAGN_DEV_NAME "lsm303dlhc_magn" +#define LSM303DLM_MAGN_DEV_NAME "lsm303dlm_magn" +#define LIS3MDL_MAGN_DEV_NAME "lis3mdl" + +int st_magn_common_probe(struct iio_dev *indio_dev); +void st_magn_common_remove(struct iio_dev *indio_dev); + +#ifdef CONFIG_IIO_BUFFER +int st_magn_allocate_ring(struct iio_dev *indio_dev); +void st_magn_deallocate_ring(struct iio_dev *indio_dev); +#else /* CONFIG_IIO_BUFFER */ +static inline int st_magn_probe_trigger(struct iio_dev *indio_dev, int irq) +{ + return 0; +} +static inline void st_magn_remove_trigger(struct iio_dev *indio_dev, int irq) +{ + return; +} +static inline int st_magn_allocate_ring(struct iio_dev *indio_dev) +{ + return 0; +} +static inline void st_magn_deallocate_ring(struct iio_dev *indio_dev) +{ +} +#endif /* CONFIG_IIO_BUFFER */ + +#endif /* ST_MAGN_H */ diff --git a/drivers/iio/magnetometer/st_magn_buffer.c b/drivers/iio/magnetometer/st_magn_buffer.c new file mode 100644 index 00000000000..708857bdb47 --- /dev/null +++ b/drivers/iio/magnetometer/st_magn_buffer.c @@ -0,0 +1,98 @@ +/* + * STMicroelectronics magnetometers driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * 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/i2c.h> +#include <linux/delay.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/common/st_sensors.h> +#include "st_magn.h" + +static int st_magn_buffer_preenable(struct iio_dev *indio_dev) +{ + int err; + + err = st_sensors_set_enable(indio_dev, true); + if (err < 0) + goto st_magn_set_enable_error; + + err = iio_sw_buffer_preenable(indio_dev); + +st_magn_set_enable_error: + return err; +} + +static int st_magn_buffer_postenable(struct iio_dev *indio_dev) +{ + int err; + struct st_sensor_data *mdata = iio_priv(indio_dev); + + mdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); + if (mdata->buffer_data == NULL) { + err = -ENOMEM; + goto allocate_memory_error; + } + + err = iio_triggered_buffer_postenable(indio_dev); + if (err < 0) + goto st_magn_buffer_postenable_error; + + return err; + +st_magn_buffer_postenable_error: + kfree(mdata->buffer_data); +allocate_memory_error: + return err; +} + +static int st_magn_buffer_predisable(struct iio_dev *indio_dev) +{ + int err; + struct st_sensor_data *mdata = iio_priv(indio_dev); + + err = iio_triggered_buffer_predisable(indio_dev); + if (err < 0) + goto st_magn_buffer_predisable_error; + + err = st_sensors_set_enable(indio_dev, false); + +st_magn_buffer_predisable_error: + kfree(mdata->buffer_data); + return err; +} + +static const struct iio_buffer_setup_ops st_magn_buffer_setup_ops = { + .preenable = &st_magn_buffer_preenable, + .postenable = &st_magn_buffer_postenable, + .predisable = &st_magn_buffer_predisable, +}; + +int st_magn_allocate_ring(struct iio_dev *indio_dev) +{ + return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + &st_sensors_trigger_handler, &st_magn_buffer_setup_ops); +} + +void st_magn_deallocate_ring(struct iio_dev *indio_dev) +{ + iio_triggered_buffer_cleanup(indio_dev); +} + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics magnetometers buffer"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c new file mode 100644 index 00000000000..a69fbe19fc8 --- /dev/null +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -0,0 +1,401 @@ +/* + * STMicroelectronics magnetometers driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * 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/delay.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/common/st_sensors.h> +#include "st_magn.h" + +/* DEFAULT VALUE FOR SENSORS */ +#define ST_MAGN_DEFAULT_OUT_X_L_ADDR 0X04 +#define ST_MAGN_DEFAULT_OUT_Y_L_ADDR 0X08 +#define ST_MAGN_DEFAULT_OUT_Z_L_ADDR 0X06 + +/* FULLSCALE */ +#define ST_MAGN_FS_AVL_1300MG 1300 +#define ST_MAGN_FS_AVL_1900MG 1900 +#define ST_MAGN_FS_AVL_2500MG 2500 +#define ST_MAGN_FS_AVL_4000MG 4000 +#define ST_MAGN_FS_AVL_4700MG 4700 +#define ST_MAGN_FS_AVL_5600MG 5600 +#define ST_MAGN_FS_AVL_8000MG 8000 +#define ST_MAGN_FS_AVL_8100MG 8100 +#define ST_MAGN_FS_AVL_10000MG 10000 + +/* CUSTOM VALUES FOR SENSOR 1 */ +#define ST_MAGN_1_WAI_EXP 0x3c +#define ST_MAGN_1_ODR_ADDR 0x00 +#define ST_MAGN_1_ODR_MASK 0x1c +#define ST_MAGN_1_ODR_AVL_1HZ_VAL 0x00 +#define ST_MAGN_1_ODR_AVL_2HZ_VAL 0x01 +#define ST_MAGN_1_ODR_AVL_3HZ_VAL 0x02 +#define ST_MAGN_1_ODR_AVL_8HZ_VAL 0x03 +#define ST_MAGN_1_ODR_AVL_15HZ_VAL 0x04 +#define ST_MAGN_1_ODR_AVL_30HZ_VAL 0x05 +#define ST_MAGN_1_ODR_AVL_75HZ_VAL 0x06 +#define ST_MAGN_1_ODR_AVL_220HZ_VAL 0x07 +#define ST_MAGN_1_PW_ADDR 0x02 +#define ST_MAGN_1_PW_MASK 0x03 +#define ST_MAGN_1_PW_ON 0x00 +#define ST_MAGN_1_PW_OFF 0x03 +#define ST_MAGN_1_FS_ADDR 0x01 +#define ST_MAGN_1_FS_MASK 0xe0 +#define ST_MAGN_1_FS_AVL_1300_VAL 0x01 +#define ST_MAGN_1_FS_AVL_1900_VAL 0x02 +#define ST_MAGN_1_FS_AVL_2500_VAL 0x03 +#define ST_MAGN_1_FS_AVL_4000_VAL 0x04 +#define ST_MAGN_1_FS_AVL_4700_VAL 0x05 +#define ST_MAGN_1_FS_AVL_5600_VAL 0x06 +#define ST_MAGN_1_FS_AVL_8100_VAL 0x07 +#define ST_MAGN_1_FS_AVL_1300_GAIN_XY 1100 +#define ST_MAGN_1_FS_AVL_1900_GAIN_XY 855 +#define ST_MAGN_1_FS_AVL_2500_GAIN_XY 670 +#define ST_MAGN_1_FS_AVL_4000_GAIN_XY 450 +#define ST_MAGN_1_FS_AVL_4700_GAIN_XY 400 +#define ST_MAGN_1_FS_AVL_5600_GAIN_XY 330 +#define ST_MAGN_1_FS_AVL_8100_GAIN_XY 230 +#define ST_MAGN_1_FS_AVL_1300_GAIN_Z 980 +#define ST_MAGN_1_FS_AVL_1900_GAIN_Z 760 +#define ST_MAGN_1_FS_AVL_2500_GAIN_Z 600 +#define ST_MAGN_1_FS_AVL_4000_GAIN_Z 400 +#define ST_MAGN_1_FS_AVL_4700_GAIN_Z 355 +#define ST_MAGN_1_FS_AVL_5600_GAIN_Z 295 +#define ST_MAGN_1_FS_AVL_8100_GAIN_Z 205 +#define ST_MAGN_1_MULTIREAD_BIT false + +/* CUSTOM VALUES FOR SENSOR 2 */ +#define ST_MAGN_2_WAI_EXP 0x3d +#define ST_MAGN_2_ODR_ADDR 0x20 +#define ST_MAGN_2_ODR_MASK 0x1c +#define ST_MAGN_2_ODR_AVL_1HZ_VAL 0x00 +#define ST_MAGN_2_ODR_AVL_2HZ_VAL 0x01 +#define ST_MAGN_2_ODR_AVL_3HZ_VAL 0x02 +#define ST_MAGN_2_ODR_AVL_5HZ_VAL 0x03 +#define ST_MAGN_2_ODR_AVL_10HZ_VAL 0x04 +#define ST_MAGN_2_ODR_AVL_20HZ_VAL 0x05 +#define ST_MAGN_2_ODR_AVL_40HZ_VAL 0x06 +#define ST_MAGN_2_ODR_AVL_80HZ_VAL 0x07 +#define ST_MAGN_2_PW_ADDR 0x22 +#define ST_MAGN_2_PW_MASK 0x03 +#define ST_MAGN_2_PW_ON 0x00 +#define ST_MAGN_2_PW_OFF 0x03 +#define ST_MAGN_2_FS_ADDR 0x21 +#define ST_MAGN_2_FS_MASK 0x60 +#define ST_MAGN_2_FS_AVL_4000_VAL 0x00 +#define ST_MAGN_2_FS_AVL_8000_VAL 0x01 +#define ST_MAGN_2_FS_AVL_10000_VAL 0x02 +#define ST_MAGN_2_FS_AVL_4000_GAIN 430 +#define ST_MAGN_2_FS_AVL_8000_GAIN 230 +#define ST_MAGN_2_FS_AVL_10000_GAIN 230 +#define ST_MAGN_2_MULTIREAD_BIT false +#define ST_MAGN_2_OUT_X_L_ADDR 0x28 +#define ST_MAGN_2_OUT_Y_L_ADDR 0x2a +#define ST_MAGN_2_OUT_Z_L_ADDR 0x2c + +static const struct iio_chan_spec st_magn_16bit_channels[] = { + ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE, + ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_DEFAULT_OUT_X_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE, + ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_DEFAULT_OUT_Y_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE, + ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_DEFAULT_OUT_Z_L_ADDR), + IIO_CHAN_SOFT_TIMESTAMP(3) +}; + +static const struct iio_chan_spec st_magn_2_16bit_channels[] = { + ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE, + ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_2_OUT_X_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE, + ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_2_OUT_Y_L_ADDR), + ST_SENSORS_LSM_CHANNELS(IIO_MAGN, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE, + ST_SENSORS_DEFAULT_16_REALBITS, ST_MAGN_2_OUT_Z_L_ADDR), + IIO_CHAN_SOFT_TIMESTAMP(3) +}; + +static const struct st_sensors st_magn_sensors[] = { + { + .wai = ST_MAGN_1_WAI_EXP, + .sensors_supported = { + [0] = LSM303DLHC_MAGN_DEV_NAME, + [1] = LSM303DLM_MAGN_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_magn_16bit_channels, + .odr = { + .addr = ST_MAGN_1_ODR_ADDR, + .mask = ST_MAGN_1_ODR_MASK, + .odr_avl = { + { 1, ST_MAGN_1_ODR_AVL_1HZ_VAL, }, + { 2, ST_MAGN_1_ODR_AVL_2HZ_VAL, }, + { 3, ST_MAGN_1_ODR_AVL_3HZ_VAL, }, + { 8, ST_MAGN_1_ODR_AVL_8HZ_VAL, }, + { 15, ST_MAGN_1_ODR_AVL_15HZ_VAL, }, + { 30, ST_MAGN_1_ODR_AVL_30HZ_VAL, }, + { 75, ST_MAGN_1_ODR_AVL_75HZ_VAL, }, + { 220, ST_MAGN_1_ODR_AVL_220HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_MAGN_1_PW_ADDR, + .mask = ST_MAGN_1_PW_MASK, + .value_on = ST_MAGN_1_PW_ON, + .value_off = ST_MAGN_1_PW_OFF, + }, + .fs = { + .addr = ST_MAGN_1_FS_ADDR, + .mask = ST_MAGN_1_FS_MASK, + .fs_avl = { + [0] = { + .num = ST_MAGN_FS_AVL_1300MG, + .value = ST_MAGN_1_FS_AVL_1300_VAL, + .gain = ST_MAGN_1_FS_AVL_1300_GAIN_XY, + .gain2 = ST_MAGN_1_FS_AVL_1300_GAIN_Z, + }, + [1] = { + .num = ST_MAGN_FS_AVL_1900MG, + .value = ST_MAGN_1_FS_AVL_1900_VAL, + .gain = ST_MAGN_1_FS_AVL_1900_GAIN_XY, + .gain2 = ST_MAGN_1_FS_AVL_1900_GAIN_Z, + }, + [2] = { + .num = ST_MAGN_FS_AVL_2500MG, + .value = ST_MAGN_1_FS_AVL_2500_VAL, + .gain = ST_MAGN_1_FS_AVL_2500_GAIN_XY, + .gain2 = ST_MAGN_1_FS_AVL_2500_GAIN_Z, + }, + [3] = { + .num = ST_MAGN_FS_AVL_4000MG, + .value = ST_MAGN_1_FS_AVL_4000_VAL, + .gain = ST_MAGN_1_FS_AVL_4000_GAIN_XY, + .gain2 = ST_MAGN_1_FS_AVL_4000_GAIN_Z, + }, + [4] = { + .num = ST_MAGN_FS_AVL_4700MG, + .value = ST_MAGN_1_FS_AVL_4700_VAL, + .gain = ST_MAGN_1_FS_AVL_4700_GAIN_XY, + .gain2 = ST_MAGN_1_FS_AVL_4700_GAIN_Z, + }, + [5] = { + .num = ST_MAGN_FS_AVL_5600MG, + .value = ST_MAGN_1_FS_AVL_5600_VAL, + .gain = ST_MAGN_1_FS_AVL_5600_GAIN_XY, + .gain2 = ST_MAGN_1_FS_AVL_5600_GAIN_Z, + }, + [6] = { + .num = ST_MAGN_FS_AVL_8100MG, + .value = ST_MAGN_1_FS_AVL_8100_VAL, + .gain = ST_MAGN_1_FS_AVL_8100_GAIN_XY, + .gain2 = ST_MAGN_1_FS_AVL_8100_GAIN_Z, + }, + }, + }, + .multi_read_bit = ST_MAGN_1_MULTIREAD_BIT, + .bootime = 2, + }, + { + .wai = ST_MAGN_2_WAI_EXP, + .sensors_supported = { + [0] = LIS3MDL_MAGN_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_magn_2_16bit_channels, + .odr = { + .addr = ST_MAGN_2_ODR_ADDR, + .mask = ST_MAGN_2_ODR_MASK, + .odr_avl = { + { 1, ST_MAGN_2_ODR_AVL_1HZ_VAL, }, + { 2, ST_MAGN_2_ODR_AVL_2HZ_VAL, }, + { 3, ST_MAGN_2_ODR_AVL_3HZ_VAL, }, + { 5, ST_MAGN_2_ODR_AVL_5HZ_VAL, }, + { 10, ST_MAGN_2_ODR_AVL_10HZ_VAL, }, + { 20, ST_MAGN_2_ODR_AVL_20HZ_VAL, }, + { 40, ST_MAGN_2_ODR_AVL_40HZ_VAL, }, + { 80, ST_MAGN_2_ODR_AVL_80HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_MAGN_2_PW_ADDR, + .mask = ST_MAGN_2_PW_MASK, + .value_on = ST_MAGN_2_PW_ON, + .value_off = ST_MAGN_2_PW_OFF, + }, + .fs = { + .addr = ST_MAGN_2_FS_ADDR, + .mask = ST_MAGN_2_FS_MASK, + .fs_avl = { + [0] = { + .num = ST_MAGN_FS_AVL_4000MG, + .value = ST_MAGN_2_FS_AVL_4000_VAL, + .gain = ST_MAGN_2_FS_AVL_4000_GAIN, + }, + [1] = { + .num = ST_MAGN_FS_AVL_8000MG, + .value = ST_MAGN_2_FS_AVL_8000_VAL, + .gain = ST_MAGN_2_FS_AVL_8000_GAIN, + }, + [2] = { + .num = ST_MAGN_FS_AVL_10000MG, + .value = ST_MAGN_2_FS_AVL_10000_VAL, + .gain = ST_MAGN_2_FS_AVL_10000_GAIN, + }, + }, + }, + .multi_read_bit = ST_MAGN_2_MULTIREAD_BIT, + .bootime = 2, + }, +}; + +static int st_magn_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *ch, int *val, + int *val2, long mask) +{ + int err; + struct st_sensor_data *mdata = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + err = st_sensors_read_info_raw(indio_dev, ch, val); + if (err < 0) + goto read_error; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + if ((ch->scan_index == ST_SENSORS_SCAN_Z) && + (mdata->current_fullscale->gain2 != 0)) + *val2 = mdata->current_fullscale->gain2; + else + *val2 = mdata->current_fullscale->gain; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + +read_error: + return err; +} + +static int st_magn_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + int err; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + err = st_sensors_set_fullscale_by_gain(indio_dev, val2); + break; + default: + err = -EINVAL; + } + + return err; +} + +static ST_SENSOR_DEV_ATTR_SAMP_FREQ(); +static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL(); +static ST_SENSORS_DEV_ATTR_SCALE_AVAIL(in_magn_scale_available); + +static struct attribute *st_magn_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_magn_scale_available.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_magn_attribute_group = { + .attrs = st_magn_attributes, +}; + +static const struct iio_info magn_info = { + .driver_module = THIS_MODULE, + .attrs = &st_magn_attribute_group, + .read_raw = &st_magn_read_raw, + .write_raw = &st_magn_write_raw, +}; + +int st_magn_common_probe(struct iio_dev *indio_dev) +{ + int err; + struct st_sensor_data *mdata = iio_priv(indio_dev); + + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &magn_info; + + err = st_sensors_check_device_support(indio_dev, + ARRAY_SIZE(st_magn_sensors), st_magn_sensors); + if (err < 0) + goto st_magn_common_probe_error; + + mdata->multiread_bit = mdata->sensor->multi_read_bit; + indio_dev->channels = mdata->sensor->ch; + indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; + + mdata->current_fullscale = (struct st_sensor_fullscale_avl *) + &mdata->sensor->fs.fs_avl[0]; + mdata->odr = mdata->sensor->odr.odr_avl[0].hz; + + err = st_sensors_init_sensor(indio_dev); + if (err < 0) + goto st_magn_common_probe_error; + + if (mdata->get_irq_data_ready(indio_dev) > 0) { + err = st_magn_allocate_ring(indio_dev); + if (err < 0) + goto st_magn_common_probe_error; + err = st_sensors_allocate_trigger(indio_dev, NULL); + if (err < 0) + goto st_magn_probe_trigger_error; + } + + err = iio_device_register(indio_dev); + if (err) + goto st_magn_device_register_error; + + return err; + +st_magn_device_register_error: + if (mdata->get_irq_data_ready(indio_dev) > 0) + st_sensors_deallocate_trigger(indio_dev); +st_magn_probe_trigger_error: + if (mdata->get_irq_data_ready(indio_dev) > 0) + st_magn_deallocate_ring(indio_dev); +st_magn_common_probe_error: + return err; +} +EXPORT_SYMBOL(st_magn_common_probe); + +void st_magn_common_remove(struct iio_dev *indio_dev) +{ + struct st_sensor_data *mdata = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + if (mdata->get_irq_data_ready(indio_dev) > 0) { + st_sensors_deallocate_trigger(indio_dev); + st_magn_deallocate_ring(indio_dev); + } + iio_device_free(indio_dev); +} +EXPORT_SYMBOL(st_magn_common_remove); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics magnetometers driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c new file mode 100644 index 00000000000..710b256a5a0 --- /dev/null +++ b/drivers/iio/magnetometer/st_magn_i2c.c @@ -0,0 +1,81 @@ +/* + * STMicroelectronics magnetometers driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * 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/common/st_sensors.h> +#include <linux/iio/common/st_sensors_i2c.h> +#include "st_magn.h" + +static int st_magn_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct st_sensor_data *mdata; + int err; + + indio_dev = iio_device_alloc(sizeof(*mdata)); + if (indio_dev == NULL) { + err = -ENOMEM; + goto iio_device_alloc_error; + } + + mdata = iio_priv(indio_dev); + mdata->dev = &client->dev; + + st_sensors_i2c_configure(indio_dev, client, mdata); + + err = st_magn_common_probe(indio_dev); + if (err < 0) + goto st_magn_common_probe_error; + + return 0; + +st_magn_common_probe_error: + iio_device_free(indio_dev); +iio_device_alloc_error: + return err; +} + +static int st_magn_i2c_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + st_magn_common_remove(indio_dev); + + return 0; +} + +static const struct i2c_device_id st_magn_id_table[] = { + { LSM303DLHC_MAGN_DEV_NAME }, + { LSM303DLM_MAGN_DEV_NAME }, + { LIS3MDL_MAGN_DEV_NAME }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, st_magn_id_table); + +static struct i2c_driver st_magn_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "st-magn-i2c", + }, + .probe = st_magn_i2c_probe, + .remove = st_magn_i2c_remove, + .id_table = st_magn_id_table, +}; +module_i2c_driver(st_magn_driver); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics magnetometers i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c new file mode 100644 index 00000000000..94547e7d658 --- /dev/null +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -0,0 +1,80 @@ +/* + * STMicroelectronics magnetometers driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * 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/common/st_sensors.h> +#include <linux/iio/common/st_sensors_spi.h> +#include "st_magn.h" + +static int st_magn_spi_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct st_sensor_data *mdata; + int err; + + indio_dev = iio_device_alloc(sizeof(*mdata)); + if (indio_dev == NULL) { + err = -ENOMEM; + goto iio_device_alloc_error; + } + + mdata = iio_priv(indio_dev); + mdata->dev = &spi->dev; + + st_sensors_spi_configure(indio_dev, spi, mdata); + + err = st_magn_common_probe(indio_dev); + if (err < 0) + goto st_magn_common_probe_error; + + return 0; + +st_magn_common_probe_error: + iio_device_free(indio_dev); +iio_device_alloc_error: + return err; +} + +static int st_magn_spi_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + st_magn_common_remove(indio_dev); + + return 0; +} + +static const struct spi_device_id st_magn_id_table[] = { + { LSM303DLHC_MAGN_DEV_NAME }, + { LSM303DLM_MAGN_DEV_NAME }, + { LIS3MDL_MAGN_DEV_NAME }, + {}, +}; +MODULE_DEVICE_TABLE(spi, st_magn_id_table); + +static struct spi_driver st_magn_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "st-magn-spi", + }, + .probe = st_magn_spi_probe, + .remove = st_magn_spi_remove, + .id_table = st_magn_id_table, +}; +module_spi_driver(st_magn_driver); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics magnetometers spi driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/gyro/Kconfig b/drivers/staging/iio/gyro/Kconfig index 658b3673d05..83606628719 100644 --- a/drivers/staging/iio/gyro/Kconfig +++ b/drivers/staging/iio/gyro/Kconfig @@ -29,14 +29,4 @@ config ADIS16260 This driver can also be built as a module. If so, the module will be called adis16260. -config ADXRS450 - tristate "Analog Devices ADXRS450/3 Digital Output Gyroscope SPI driver" - depends on SPI - help - Say yes here to build support for Analog Devices ADXRS450 and ADXRS453 - programmable digital output gyroscope. - - This driver can also be built as a module. If so, the module - will be called adxrs450. - endmenu diff --git a/drivers/staging/iio/gyro/Makefile b/drivers/staging/iio/gyro/Makefile index ef331617129..98e650061a3 100644 --- a/drivers/staging/iio/gyro/Makefile +++ b/drivers/staging/iio/gyro/Makefile @@ -10,6 +10,3 @@ obj-$(CONFIG_ADIS16130) += adis16130.o adis16260-y := adis16260_core.o obj-$(CONFIG_ADIS16260) += adis16260.o - -adxrs450-y := adxrs450_core.o -obj-$(CONFIG_ADXRS450) += adxrs450.o diff --git a/drivers/staging/iio/gyro/adxrs450.h b/drivers/staging/iio/gyro/adxrs450.h deleted file mode 100644 index f8cf21f0294..00000000000 --- a/drivers/staging/iio/gyro/adxrs450.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef SPI_ADXRS450_H_ -#define SPI_ADXRS450_H_ - -#define ADXRS450_STARTUP_DELAY 50 /* ms */ - -/* The MSB for the spi commands */ -#define ADXRS450_SENSOR_DATA 0x20 -#define ADXRS450_WRITE_DATA 0x40 -#define ADXRS450_READ_DATA 0x80 - -#define ADXRS450_RATE1 0x00 /* Rate Registers */ -#define ADXRS450_TEMP1 0x02 /* Temperature Registers */ -#define ADXRS450_LOCST1 0x04 /* Low CST Memory Registers */ -#define ADXRS450_HICST1 0x06 /* High CST Memory Registers */ -#define ADXRS450_QUAD1 0x08 /* Quad Memory Registers */ -#define ADXRS450_FAULT1 0x0A /* Fault Registers */ -#define ADXRS450_PID1 0x0C /* Part ID Register 1 */ -#define ADXRS450_SNH 0x0E /* Serial Number Registers, 4 bytes */ -#define ADXRS450_SNL 0x10 -#define ADXRS450_DNC1 0x12 /* Dynamic Null Correction Registers */ -/* Check bits */ -#define ADXRS450_P 0x01 -#define ADXRS450_CHK 0x02 -#define ADXRS450_CST 0x04 -#define ADXRS450_PWR 0x08 -#define ADXRS450_POR 0x10 -#define ADXRS450_NVM 0x20 -#define ADXRS450_Q 0x40 -#define ADXRS450_PLL 0x80 -#define ADXRS450_UV 0x100 -#define ADXRS450_OV 0x200 -#define ADXRS450_AMP 0x400 -#define ADXRS450_FAIL 0x800 - -#define ADXRS450_WRERR_MASK (0x7 << 29) - -#define ADXRS450_MAX_RX 4 -#define ADXRS450_MAX_TX 4 - -#define ADXRS450_GET_ST(a) ((a >> 26) & 0x3) - -enum { - ID_ADXRS450, - ID_ADXRS453, -}; - -/** - * struct adxrs450_state - device instance specific data - * @us: actual spi_device - * @buf_lock: mutex to protect tx and rx - * @tx: transmit buffer - * @rx: receive buffer - **/ -struct adxrs450_state { - struct spi_device *us; - struct mutex buf_lock; - u8 tx[ADXRS450_MAX_RX] ____cacheline_aligned; - u8 rx[ADXRS450_MAX_TX]; - -}; - -#endif /* SPI_ADXRS450_H_ */ diff --git a/drivers/staging/iio/iio_hwmon.c b/drivers/staging/iio/iio_hwmon.c index 97ad645bb15..93af756ba48 100644 --- a/drivers/staging/iio/iio_hwmon.c +++ b/drivers/staging/iio/iio_hwmon.c @@ -55,63 +55,58 @@ static ssize_t iio_hwmon_read_val(struct device *dev, return sprintf(buf, "%d\n", result); } -static void iio_hwmon_free_attrs(struct iio_hwmon_state *st) +static ssize_t show_name(struct device *dev, struct device_attribute *attr, + char *buf) { - int i; - struct sensor_device_attribute *a; - for (i = 0; i < st->num_channels; i++) - if (st->attrs[i]) { - a = to_sensor_dev_attr( - container_of(st->attrs[i], - struct device_attribute, - attr)); - kfree(a); - } + return sprintf(buf, "iio_hwmon\n"); } +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + static int iio_hwmon_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct iio_hwmon_state *st; struct sensor_device_attribute *a; int ret, i; int in_i = 1, temp_i = 1, curr_i = 1; enum iio_chan_type type; + struct iio_channel *channels; - st = kzalloc(sizeof(*st), GFP_KERNEL); - if (st == NULL) { - ret = -ENOMEM; - goto error_ret; - } + channels = iio_channel_get_all(dev); + if (IS_ERR(channels)) + return PTR_ERR(channels); - st->channels = iio_channel_get_all(dev_name(&pdev->dev)); - if (IS_ERR(st->channels)) { - ret = PTR_ERR(st->channels); - goto error_free_state; - } + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (st == NULL) + return -ENOMEM; + + st->channels = channels; /* count how many attributes we have */ while (st->channels[st->num_channels].indio_dev) st->num_channels++; - st->attrs = kzalloc(sizeof(*st->attrs) * (st->num_channels + 1), - GFP_KERNEL); + st->attrs = devm_kzalloc(dev, + sizeof(*st->attrs) * (st->num_channels + 2), + GFP_KERNEL); if (st->attrs == NULL) { ret = -ENOMEM; goto error_release_channels; } + for (i = 0; i < st->num_channels; i++) { - a = kzalloc(sizeof(*a), GFP_KERNEL); + a = devm_kzalloc(dev, sizeof(*a), GFP_KERNEL); if (a == NULL) { ret = -ENOMEM; - goto error_free_attrs; + goto error_release_channels; } sysfs_attr_init(&a->dev_attr.attr); ret = iio_get_channel_type(&st->channels[i], &type); - if (ret < 0) { - kfree(a); - goto error_free_attrs; - } + if (ret < 0) + goto error_release_channels; + switch (type) { case IIO_VOLTAGE: a->dev_attr.attr.name = kasprintf(GFP_KERNEL, @@ -130,27 +125,25 @@ static int iio_hwmon_probe(struct platform_device *pdev) break; default: ret = -EINVAL; - kfree(a); - goto error_free_attrs; + goto error_release_channels; } if (a->dev_attr.attr.name == NULL) { - kfree(a); ret = -ENOMEM; - goto error_free_attrs; + goto error_release_channels; } a->dev_attr.show = iio_hwmon_read_val; a->dev_attr.attr.mode = S_IRUGO; a->index = i; st->attrs[i] = &a->dev_attr.attr; } - + st->attrs[st->num_channels] = &dev_attr_name.attr; st->attr_group.attrs = st->attrs; platform_set_drvdata(pdev, st); - ret = sysfs_create_group(&pdev->dev.kobj, &st->attr_group); + ret = sysfs_create_group(&dev->kobj, &st->attr_group); if (ret < 0) - goto error_free_attrs; + goto error_release_channels; - st->hwmon_dev = hwmon_device_register(&pdev->dev); + st->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(st->hwmon_dev)) { ret = PTR_ERR(st->hwmon_dev); goto error_remove_group; @@ -158,15 +151,9 @@ static int iio_hwmon_probe(struct platform_device *pdev) return 0; error_remove_group: - sysfs_remove_group(&pdev->dev.kobj, &st->attr_group); -error_free_attrs: - iio_hwmon_free_attrs(st); - kfree(st->attrs); + sysfs_remove_group(&dev->kobj, &st->attr_group); error_release_channels: iio_channel_release_all(st->channels); -error_free_state: - kfree(st); -error_ret: return ret; } @@ -176,17 +163,21 @@ static int iio_hwmon_remove(struct platform_device *pdev) hwmon_device_unregister(st->hwmon_dev); sysfs_remove_group(&pdev->dev.kobj, &st->attr_group); - iio_hwmon_free_attrs(st); - kfree(st->attrs); iio_channel_release_all(st->channels); return 0; } +static struct of_device_id iio_hwmon_of_match[] = { + { .compatible = "iio-hwmon", }, + { } +}; + static struct platform_driver __refdata iio_hwmon_driver = { .driver = { .name = "iio_hwmon", .owner = THIS_MODULE, + .of_match_table = iio_hwmon_of_match, }, .probe = iio_hwmon_probe, .remove = iio_hwmon_remove, diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index f3eea18fdf4..2bac0eb8948 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -103,19 +103,6 @@ int iio_update_buffers(struct iio_dev *indio_dev, **/ void iio_buffer_init(struct iio_buffer *buffer); -/** - * __iio_update_buffer() - update common elements of buffers - * @buffer: buffer that is the event source - * @bytes_per_datum: size of individual datum including timestamp - * @length: number of datums in buffer - **/ -static inline void __iio_update_buffer(struct iio_buffer *buffer, - int bytes_per_datum, int length) -{ - buffer->bytes_per_datum = bytes_per_datum; - buffer->length = length; -} - int iio_scan_mask_query(struct iio_dev *indio_dev, struct iio_buffer *buffer, int bit); diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h new file mode 100644 index 00000000000..c40fdf537f6 --- /dev/null +++ b/include/linux/iio/common/st_sensors.h @@ -0,0 +1,270 @@ +/* + * STMicroelectronics sensors library driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * 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_LENGTH 2 +#define ST_SENSORS_RX_MAX_LENGTH 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_AXIS 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, \ + }, \ +} + +#define ST_SENSOR_DEV_ATTR_SAMP_FREQ() \ + IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, \ + st_sensors_sysfs_get_sampling_frequency, \ + st_sensors_sysfs_set_sampling_frequency) + +#define ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL() \ + IIO_DEV_ATTR_SAMP_FREQ_AVAIL( \ + st_sensors_sysfs_sampling_frequency_avail) + +#define ST_SENSORS_DEV_ATTR_SCALE_AVAIL(name) \ + IIO_DEVICE_ATTR(name, S_IRUGO, \ + st_sensors_sysfs_scale_avail, NULL , 0); + +struct st_sensor_odr_avl { + unsigned int hz; + u8 value; +}; + +struct st_sensor_odr { + u8 addr; + u8 mask; + struct st_sensor_odr_avl odr_avl[ST_SENSORS_ODR_LIST_MAX]; +}; + +struct st_sensor_power { + u8 addr; + u8 mask; + 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; + 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_LENGTH]; + u8 tx_buf[ST_SENSORS_TX_MAX_LENGTH] ____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_sensors - ST sensors list + * @wai: Contents of WhoAmI register. + * @sensors_supported: List of supported sensors by struct itself. + * @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; +}; + +/** + * 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. + * @sensor: Pointer to the current sensor struct in use. + * @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. + * @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_sensors *sensor; + struct st_sensor_fullscale_avl *current_fullscale; + + bool enabled; + bool multiread_bit; + + char *buffer_data; + + unsigned int odr; + + unsigned int (*get_irq_data_ready) (struct iio_dev *indio_dev); + + const struct st_sensor_transfer_function *tf; + struct st_sensor_transfer_buffer tb; +}; + +#ifdef CONFIG_IIO_BUFFER +int st_sensors_allocate_trigger(struct iio_dev *indio_dev, + const struct iio_trigger_ops *trigger_ops); + +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); + +int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable); + +int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable); + +int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr); + +int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable); + +int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, int scale); + +int st_sensors_read_info_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *ch, int *val); + +int st_sensors_check_device_support(struct iio_dev *indio_dev, + int num_sensors_list, const struct st_sensors *sensors); + +ssize_t st_sensors_sysfs_get_sampling_frequency(struct device *dev, + struct device_attribute *attr, char *buf); + +ssize_t st_sensors_sysfs_set_sampling_frequency(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); + +ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev, + struct device_attribute *attr, char *buf); + +ssize_t st_sensors_sysfs_scale_avail(struct device *dev, + struct device_attribute *attr, char *buf); + +#endif /* ST_SENSORS_H */ diff --git a/include/linux/iio/common/st_sensors_i2c.h b/include/linux/iio/common/st_sensors_i2c.h new file mode 100644 index 00000000000..67d845385ae --- /dev/null +++ b/include/linux/iio/common/st_sensors_i2c.h @@ -0,0 +1,20 @@ +/* + * STMicroelectronics sensors i2c library driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * Licensed under the GPL-2. + */ + +#ifndef ST_SENSORS_I2C_H +#define ST_SENSORS_I2C_H + +#include <linux/i2c.h> +#include <linux/iio/common/st_sensors.h> + +void st_sensors_i2c_configure(struct iio_dev *indio_dev, + struct i2c_client *client, struct st_sensor_data *sdata); + +#endif /* ST_SENSORS_I2C_H */ diff --git a/include/linux/iio/common/st_sensors_spi.h b/include/linux/iio/common/st_sensors_spi.h new file mode 100644 index 00000000000..d964a3563dc --- /dev/null +++ b/include/linux/iio/common/st_sensors_spi.h @@ -0,0 +1,20 @@ +/* + * STMicroelectronics sensors spi library driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * Licensed under the GPL-2. + */ + +#ifndef ST_SENSORS_SPI_H +#define ST_SENSORS_SPI_H + +#include <linux/spi/spi.h> +#include <linux/iio/common/st_sensors.h> + +void st_sensors_spi_configure(struct iio_dev *indio_dev, + struct spi_device *spi, struct st_sensor_data *sdata); + +#endif /* ST_SENSORS_SPI_H */ diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index 16c35ac045b..a85787ac66a 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -15,6 +15,7 @@ struct iio_dev; struct iio_chan_spec; +struct device; /** * struct iio_channel - everything needed for a consumer to use a channel @@ -48,14 +49,14 @@ void iio_channel_release(struct iio_channel *chan); /** * iio_channel_get_all() - get all channels associated with a client - * @name: name of consumer device. + * @dev: Pointer to consumer device. * * Returns an array of iio_channel structures terminated with one with * null iio_dev pointer. * This function is used by fairly generic consumers to get all the * channels registered as having this consumer. */ -struct iio_channel *iio_channel_get_all(const char *name); +struct iio_channel *iio_channel_get_all(struct device *dev); /** * iio_channel_release_all() - reverse iio_channel_get_all @@ -66,7 +67,7 @@ void iio_channel_release_all(struct iio_channel *chan); struct iio_cb_buffer; /** * iio_channel_get_all_cb() - register callback for triggered capture - * @name: Name of client device. + * @dev: Pointer to client device. * @cb: Callback function. * @private: Private data passed to callback. * @@ -74,7 +75,7 @@ struct iio_cb_buffer; * So if the channels requested come from different devices this will * fail. */ -struct iio_cb_buffer *iio_channel_get_all_cb(const char *name, +struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev, int (*cb)(u8 *data, void *private), void *private); diff --git a/include/linux/iio/driver.h b/include/linux/iio/driver.h index a4f8b2e05af..7dfb10ee266 100644 --- a/include/linux/iio/driver.h +++ b/include/linux/iio/driver.h @@ -22,13 +22,10 @@ int iio_map_array_register(struct iio_dev *indio_dev, struct iio_map *map); /** - * iio_map_array_unregister() - tell the core to remove consumer mappings + * iio_map_array_unregister() - tell the core to remove consumer mappings for + * the given provider device * @indio_dev: provider device - * @map: array of mappings to remove. Note these must have same memory - * addresses as those originally added not just equal parameter - * values. */ -int iio_map_array_unregister(struct iio_dev *indio_dev, - struct iio_map *map); +int iio_map_array_unregister(struct iio_dev *indio_dev); #endif diff --git a/include/linux/iio/gyro/itg3200.h b/include/linux/iio/gyro/itg3200.h new file mode 100644 index 00000000000..c53f16914b7 --- /dev/null +++ b/include/linux/iio/gyro/itg3200.h @@ -0,0 +1,154 @@ +/* + * itg3200.h -- support InvenSense ITG3200 + * Digital 3-Axis Gyroscope driver + * + * Copyright (c) 2011 Christian Strobel <christian.strobel@iis.fraunhofer.de> + * Copyright (c) 2011 Manuel Stahl <manuel.stahl@iis.fraunhofer.de> + * Copyright (c) 2012 Thorsten Nowak <thorsten.nowak@iis.fraunhofer.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef I2C_ITG3200_H_ +#define I2C_ITG3200_H_ + +#include <linux/iio/iio.h> + +/* Register with I2C address (34h) */ +#define ITG3200_REG_ADDRESS 0x00 + +/* Sample rate divider + * Range: 0 to 255 + * Default value: 0x00 */ +#define ITG3200_REG_SAMPLE_RATE_DIV 0x15 + +/* Digital low pass filter settings */ +#define ITG3200_REG_DLPF 0x16 +/* DLPF full scale range */ +#define ITG3200_DLPF_FS_SEL_2000 0x18 +/* Bandwidth (Hz) and internal sample rate + * (kHz) of DLPF */ +#define ITG3200_DLPF_256_8 0x00 +#define ITG3200_DLPF_188_1 0x01 +#define ITG3200_DLPF_98_1 0x02 +#define ITG3200_DLPF_42_1 0x03 +#define ITG3200_DLPF_20_1 0x04 +#define ITG3200_DLPF_10_1 0x05 +#define ITG3200_DLPF_5_1 0x06 + +#define ITG3200_DLPF_CFG_MASK 0x07 + +/* Configuration for interrupt operations */ +#define ITG3200_REG_IRQ_CONFIG 0x17 +/* Logic level */ +#define ITG3200_IRQ_ACTIVE_LOW 0x80 +#define ITG3200_IRQ_ACTIVE_HIGH 0x00 +/* Drive type */ +#define ITG3200_IRQ_OPEN_DRAIN 0x40 +#define ITG3200_IRQ_PUSH_PULL 0x00 +/* Latch mode */ +#define ITG3200_IRQ_LATCH_UNTIL_CLEARED 0x20 +#define ITG3200_IRQ_LATCH_50US_PULSE 0x00 +/* Latch clear method */ +#define ITG3200_IRQ_LATCH_CLEAR_ANY 0x10 +#define ITG3200_IRQ_LATCH_CLEAR_STATUS 0x00 +/* Enable interrupt when device is ready */ +#define ITG3200_IRQ_DEVICE_RDY_ENABLE 0x04 +/* Enable interrupt when data is available */ +#define ITG3200_IRQ_DATA_RDY_ENABLE 0x01 + +/* Determine the status of ITG-3200 interrupts */ +#define ITG3200_REG_IRQ_STATUS 0x1A +/* Status of 'device is ready'-interrupt */ +#define ITG3200_IRQ_DEVICE_RDY_STATUS 0x04 +/* Status of 'data is available'-interrupt */ +#define ITG3200_IRQ_DATA_RDY_STATUS 0x01 + +/* Sensor registers */ +#define ITG3200_REG_TEMP_OUT_H 0x1B +#define ITG3200_REG_TEMP_OUT_L 0x1C +#define ITG3200_REG_GYRO_XOUT_H 0x1D +#define ITG3200_REG_GYRO_XOUT_L 0x1E +#define ITG3200_REG_GYRO_YOUT_H 0x1F +#define ITG3200_REG_GYRO_YOUT_L 0x20 +#define ITG3200_REG_GYRO_ZOUT_H 0x21 +#define ITG3200_REG_GYRO_ZOUT_L 0x22 + +/* Power management */ +#define ITG3200_REG_POWER_MANAGEMENT 0x3E +/* Reset device and internal registers to the + * power-up-default settings */ +#define ITG3200_RESET 0x80 +/* Enable low power sleep mode */ +#define ITG3200_SLEEP 0x40 +/* Put according gyroscope in standby mode */ +#define ITG3200_STANDBY_GYRO_X 0x20 +#define ITG3200_STANDBY_GYRO_Y 0x10 +#define ITG3200_STANDBY_GYRO_Z 0x08 +/* Determine the device clock source */ +#define ITG3200_CLK_INTERNAL 0x00 +#define ITG3200_CLK_GYRO_X 0x01 +#define ITG3200_CLK_GYRO_Y 0x02 +#define ITG3200_CLK_GYRO_Z 0x03 +#define ITG3200_CLK_EXT_32K 0x04 +#define ITG3200_CLK_EXT_19M 0x05 + + +/** + * struct itg3200 - device instance specific data + * @i2c: actual i2c_client + * @trig: data ready trigger from itg3200 pin + **/ +struct itg3200 { + struct i2c_client *i2c; + struct iio_trigger *trig; +}; + +enum ITG3200_SCAN_INDEX { + ITG3200_SCAN_TEMP, + ITG3200_SCAN_GYRO_X, + ITG3200_SCAN_GYRO_Y, + ITG3200_SCAN_GYRO_Z, + ITG3200_SCAN_ELEMENTS, +}; + +int itg3200_write_reg_8(struct iio_dev *indio_dev, + u8 reg_address, u8 val); + +int itg3200_read_reg_8(struct iio_dev *indio_dev, + u8 reg_address, u8 *val); + + +#ifdef CONFIG_IIO_BUFFER + +void itg3200_remove_trigger(struct iio_dev *indio_dev); +int itg3200_probe_trigger(struct iio_dev *indio_dev); + +int itg3200_buffer_configure(struct iio_dev *indio_dev); +void itg3200_buffer_unconfigure(struct iio_dev *indio_dev); + +#else /* CONFIG_IIO_BUFFER */ + +static inline void itg3200_remove_trigger(struct iio_dev *indio_dev) +{ +} + +static inline int itg3200_probe_trigger(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline int itg3200_buffer_configure(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline void itg3200_buffer_unconfigure(struct iio_dev *indio_dev) +{ +} + +#endif /* CONFIG_IIO_RING_BUFFER */ + +#endif /* ITG3200_H_ */ |